Code Vonc

Utilitaires

CommentairesComments
Calculs géométriques sur un objet
Opérations logiques sur un polygone
Opérations sur les matrices
Opérations sur les vecteurs
Code source

import c4d
import math

class VoncUtils() :
    
    """
        Auteur : César Vonc
        http://code.vonc.fr
    """
    
    @staticmethod
    def DebugVecteur(doc, a, b = None, coul = c4d.Vector(1.), nom = "vec") :
        
        """Débug : affiche un vecteur."""
        
        groupe = doc.SearchObject("debug")
        
        if groupe is None :
            groupe = c4d.BaseObject(c4d.Onull)
            groupe.SetName("debug")
            doc.InsertObject(groupe)
            doc.AddUndo(c4d.UNDOTYPE_NEW, groupe)
        
        
        p = None
        
        if b is None :
            p = c4d.BaseObject(c4d.Onull)
            p[c4d.NULLOBJECT_DISPLAY] = 1
            p[c4d.NULLOBJECT_RADIUS] = 3.
            
        else :
            p = c4d.BaseObject(c4d.Ospline)
            p.ResizeObject(2)
            p.SetAllPoints([c4d.Vector(), b - a])
            p.Message(c4d.MSG_UPDATE)

        p.SetName(nom)
        p[c4d.ID_BASEOBJECT_USECOLOR] = 2
        p[c4d.ID_BASEOBJECT_COLOR] = coul
        p[c4d.ID_BASEOBJECT_REL_POSITION] = a
        p.InsertUnder(groupe)
        doc.AddUndo(c4d.UNDOTYPE_NEW, p)
        
        
    @staticmethod
    def DebugTexte(doc, pos = c4d.Vector(), texte = "texte", taille = 16., coul = c4d.Vector(1.)) :
        
        """Débug : affiche un texte."""
        
        groupe = doc.SearchObject("debug")
        
        if groupe is None :
            groupe = c4d.BaseObject(c4d.Onull)
            groupe.SetName("debug")
            doc.InsertObject(groupe)
            doc.AddUndo(c4d.UNDOTYPE_NEW, groupe)
        
        
        p = c4d.BaseObject(1019268)
        p.MakeTag(1001001)
        p[c4d.PRIM_TEXT_VSPACING] = - taille + (taille / 4.)
        p[c4d.ID_BASEOBJECT_USECOLOR] = 2
        p[c4d.ID_BASEOBJECT_COLOR] = coul
        p[c4d.PRIM_TEXT_HEIGHT] = taille
        p[c4d.ID_BASEOBJECT_REL_SCALE,c4d.VECTOR_X] = -1.
        p[c4d.PRIM_TEXT_ALIGN] = 1
        p[c4d.MGTEXTOBJECT_SPLINEMOVE] = 0.
        p[c4d.PRIM_TEXT_TEXT] = "\n" + texte
        p[c4d.ID_BASEOBJECT_REL_POSITION] = pos
        
        p.InsertUnder(groupe)
        doc.AddUndo(c4d.UNDOTYPE_NEW, p)
        
    
    @staticmethod
    def CalculeNormalesPolys(obj) :
    
        """Calcule les normales des polys de l'objet."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPolys = obj.GetPolygonCount()
        
        norPolys = [c4d.Vector()] * nbPolys
        nor = c4d.Vector()
        
        for i, poly in enumerate(polys) :
            nor = (pts[poly.a] - pts[poly.c]).Cross(pts[poly.b] - pts[poly.d])
            nor.Normalize()
            norPolys[i] = nor
        
        return norPolys
    
    
    @staticmethod
    def CalculeNormalesPoints(obj, bsPolys = None) :
    
        """Calcule les normales des points de l'objet."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPoints = obj.GetPointCount()
        
        norPts = [c4d.Vector()] * nbPoints
        
        if bsPolys is None :
        
            for i, poly in enumerate(polys) :
                
                normale = (pts[poly.a] - pts[poly.c]).Cross(pts[poly.b] - pts[poly.d])
                normale.Normalize()
                
                norPts[poly.a] += normale
                norPts[poly.b] += normale
                norPts[poly.c] += normale
                if (poly.c != poly.d) : norPts[poly.d] += normale
        
        else :
        
            nbPolys = obj.GetPolygonCount()
            bsTous = bsPolys.GetAll(nbPolys)
            
            for i, sel in enumerate(bsTous) :
                
                if not sel : continue
                
                poly = polys[i]
                
                normale = (pts[poly.a] - pts[poly.c]).Cross(pts[poly.b] - pts[poly.d])
                normale.Normalize()
                
                norPts[poly.a] += normale
                norPts[poly.b] += normale
                norPts[poly.c] += normale
                if (poly.c != poly.d) : norPts[poly.d] += normale
            
        
        for i in xrange(nbPoints) :
            norPts[i].Normalize()
        
        return norPts
        
        
    @staticmethod
    def CalculeNormalesPointsPolys(obj, bsPolys = None) :
    
        """Calcule les normales des points et des polys de l'objet."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPoints = obj.GetPointCount()
        nbPolys = obj.GetPolygonCount()
        
        norPts = [c4d.Vector()] * nbPoints
        norPolys = [c4d.Vector()] * nbPolys
        
        if bsPolys is None :
        
            for i, poly in enumerate(polys) :
                
                normale = (pts[poly.a] - pts[poly.c]).Cross(pts[poly.b] - pts[poly.d])
                normale.Normalize()
                
                norPolys[i] = normale
                
                norPts[poly.a] += normale
                norPts[poly.b] += normale
                norPts[poly.c] += normale
                if (poly.c != poly.d) : norPts[poly.d] += normale
        
        else :
        
            nbPolys = obj.GetPolygonCount()
            bsTous = bsPolys.GetAll(nbPolys)
            
            for i, sel in enumerate(bsTous) :
                
                if not sel : continue
                
                poly = polys[i]
                
                normale = (pts[poly.a] - pts[poly.c]).Cross(pts[poly.b] - pts[poly.d])
                normale.Normalize()
                
                norPolys[i] = normale
                
                norPts[poly.a] += normale
                norPts[poly.b] += normale
                norPts[poly.c] += normale
                if (poly.c != poly.d) : norPts[poly.d] += normale
            
        
        for i in xrange(nbPoints) :
            norPts[i].Normalize()
        
        return norPts, norPolys
    
    
    @staticmethod
    def CalculeCentrePolys(obj) :
        
        """Calcule le centre des polys de l'objet."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPolys = obj.GetPolygonCount()
        
        centrePolys = [c4d.Vector()] * nbPolys
        centre = c4d.Vector()
        
        for i, poly in enumerate(polys) :
            if poly.c != poly.d :
                centre = (pts[poly.a] + pts[poly.b] + pts[poly.c] + pts[poly.d]) / 4.
            else :
                centre = (pts[poly.a] + pts[poly.b] + pts[poly.c]) / 3.
            centrePolys[i] = centre
        
        return centrePolys
    
    
    @staticmethod
    def CalculeDecentreBruitPolys(obj, intensite = 1.0, temps = 0.0, echelle = 1.0) :
        
        """Calcule un point dans le polys de l'objet à partir d'un facteur de bruit."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPolys = obj.GetPolygonCount()
        bruit = c4d.utils.noise.Noise
        mixVec = c4d.utils.MixVec
        
        centrePolys = [c4d.Vector()] * nbPolys
        centre = c4d.Vector()
        decentre = c4d.Vector()
        pos = c4d.Vector()
        
        for i, poly in enumerate(polys) :
            
            a = pts[poly.a]
            b = pts[poly.b]
            c = pts[poly.c]
            d = pts[poly.d]
            
            if poly.c != poly.d :
                
                centre = (a + b + c + d) / 4.
                pos = centre * echelle
                
                n = bruit(pos, temps)
                m = bruit(pos + 9999., temps)
                
                decentre = a * (n * .5) + c * ((1. - n) * .5) + b * (m * .5) + d * ((1. - m) * .5)
                decentre = mixVec(centre, decentre, intensite)
                
            else :
            
                centre = (a + b + c) / 3.
                pos = centre * echelle
                
                n = bruit(pos, temps)
                m = bruit(pos + 9999., temps)
                
                d = (a + b) * .5
                
                decentre = a * (n * .5) + b * ((1. - n) * .5) + c * (m * .5) + d * ((1. - m) * .5)
                decentre = mixVec(centre, decentre, intensite)
                
                
            centrePolys[i] = decentre
        
        return centrePolys
    
    
    @staticmethod
    def CalculeNombreAretesPolys(obj) :
        
        """Calcule le nombre d'arête de chaque polys."""
        
        polys = obj.GetAllPolygons()
        nbPolys = obj.GetPolygonCount()
        nbAretesPolys = [0] * nbPolys
        
        for i, pol in enumerate(polys) :
            
            nbAretesPolys[i] = 3 if pol.c == pol.d else 4
        
        return nbAretesPolys
        
    
    @staticmethod
    def CalculeAirePolys(obj) :
        
        """Calcule l'aire de tous les polys."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPolys = obj.GetPolygonCount()
        aires = [0.] * nbPolys
        AirePolygone = VoncUtils.AirePolygone
        
        for i, pol in enumerate(polys) :
            
            aires[i] = AirePolygone(pol, pts)
        
        return aires
        
    
    @staticmethod
    def CalculeAirePolysFacteur(obj) :
        
        """Calcule un facteur de taille de 0 à 1 pour chaque poly par rapport à leur aire."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPolys = obj.GetPolygonCount()
        aires = [0.] * nbPolys
        mini = 0.
        maxi = 0.
        AirePolygone = VoncUtils.AirePolygone
        sqrt = math.sqrt
        
        for i, pol in enumerate(polys) :
            
            aire = sqrt(AirePolygone(pol, pts))
            aires[i] = aire
        
            if i == 0 :
                mini = aire
                maxi = aire
            else :
                if aire < mini : mini = aire
                if aire > maxi : maxi = aire
        
        diff = maxi - mini
        if abs(diff) <= 0.000001 : return [1.] * nbPolys
        
        for i, aire in enumerate(aires) :
            
            aires[i] = (aire - mini) / diff
            
        return aires
        
    
    @staticmethod
    def CalculePerimetrePolys(obj) :
        
        """Calcule le périmètre de tous les polys."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPolys = obj.GetPolygonCount()
        perims = [0.] * nbPolys
        PerimetrePolygone = VoncUtils.PerimetrePolygone
        
        for i, pol in enumerate(polys) :
            
            perims[i] = PerimetrePolygone(pol, pts)
        
        return perims
        
        
    @staticmethod
    def CalculeOrthocentreTriangles(obj) :
        
        """Calcule l'orthocentre des triangles de l'objet, à supposer que tous les polys sont des triangles."""
        
        nbTriangles = obj.GetPolygonCount()
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        
        orthoPolys = [c4d.Vector()] * nbTriangles
        
        for i, poly in enumerate(polys) :
            
            a = pts[poly.a]
            b = pts[poly.b]
            c = pts[poly.c]
            
            orthoPolys[i] = VoncUtils.OrthocentreTriangle(a, b, c)
        
        return orthoPolys
    
    
    @staticmethod
    def AreteOpposee(p0, p1, poly) :
    
        """Renvoie l'arête opposée à deux points, ordonnée de façon symétrique aux points en paramètre."""
        
        arete = poly.FindEdge(p0, p1)
        if arete == c4d.NOTOK :
            return (-1, -1)
        
        if poly.c == poly.d : # Triangle
        
            if p0 == poly.a :
                if p1 == poly.b : return (poly.c, p1)
                else : return (poly.b, p1)
                
            elif p0 == poly.b :
                if p1 == poly.a : return (poly.c, p1)
                else : return (poly.a, p1)
                
            else :
                if p1 == poly.a : return (poly.b, p1)
                else : return (poly.a, p1)
        
        
        areteOpp = (arete + 2) % 4
        
        ptsOpp = poly.EdgePoints(areteOpp)
        
        if poly.FindEdge(p0, ptsOpp[0]) != c4d.NOTOK :
            return (ptsOpp[0], ptsOpp[1])
        else :
            return (ptsOpp[1], ptsOpp[0])
    

    @staticmethod
    def AreteAdjacente(p0, p1, poly) :
    
        """Renvoie l'arête adjacente à partir de deux points d'une arête."""
        
        if p0 == p1 : return (-1, -1)
        
        if poly.IsTriangle() :
        
            if p0 == poly.a :
                if p1 == poly.c : return (poly.a, poly.b) if poly.a < poly.b else (poly.b, poly.a) # 0, 1
                else : return (poly.a, poly.c) if poly.a < poly.c else (poly.c, poly.a) # 0, 2
            
            elif p0 == poly.b : 
                if p1 == poly.a : return (poly.b, poly.c) if poly.b < poly.c else (poly.c, poly.b) # 1, 2
                else : return (poly.a, poly.b) if poly.a < poly.b else (poly.b, poly.a) # 1, 0
            
            else :
                if p1 == poly.b : return (poly.a, poly.c) if poly.a < poly.c else (poly.c, poly.a) # 2, 0
                else : return (poly.b, poly.c) if poly.b < poly.c else (poly.c, poly.b) # 2, 1
            
        else :
            if p0 == poly.a :
                if p1 == poly.d : return (poly.a, poly.b) if poly.a < poly.b else (poly.b, poly.a) # 0, 1
                else : return (poly.a, poly.d) if poly.a < poly.d else (poly.d, poly.a) # 0, 3
            
            elif p0 == poly.b : 
                if p1 == poly.a : return (poly.b, poly.c) if poly.b < poly.c else (poly.c, poly.b) # 1, 2
                else : return (poly.a, poly.b) if poly.a < poly.b else (poly.b, poly.a) # 1, 0
            
            elif p0 == poly.c : 
                if p1 == poly.b : return (poly.c, poly.d) if poly.c < poly.d else (poly.d, poly.c) # 2, 3
                else : return (poly.b, poly.c) if poly.b < poly.c else (poly.c, poly.b) # 2, 1
            
            else :
                if p1 == poly.c : return (poly.a, poly.d) if poly.a < poly.d else (poly.d, poly.a) # 3, 0
                else : return (poly.c, poly.d) if poly.c < poly.d else (poly.d, poly.c) # 3, 2
        
        
        return (-1, -1)
        
        
    @staticmethod
    def ListeAretesPolygone(poly) :
    
        """Liste les arêtes d'un polygone."""
        
        if poly.IsTriangle() :
        
            return (
                (poly.a, poly.b) if (poly.a < poly.b) else (poly.b, poly.a),
                (poly.b, poly.c) if (poly.b < poly.c) else (poly.c, poly.b),
                (poly.c, poly.a) if (poly.c < poly.a) else (poly.a, poly.c)
            )
        
        return (
            (poly.a, poly.b) if (poly.a < poly.b) else (poly.b, poly.a),
            (poly.b, poly.c) if (poly.b < poly.c) else (poly.c, poly.b),
            (poly.c, poly.d) if (poly.c < poly.d) else (poly.d, poly.c),
            (poly.d, poly.a) if (poly.d < poly.a) else (poly.a, poly.d)
        )
    
    
    @staticmethod
    def ListeAretesPolygoneSensABCD(poly) :
    
        """Liste les arêtes d'un polygone, les arêtes n'ont pas leurs indices triés."""
        
        if poly.IsTriangle() :
        
            return (
                (poly.a, poly.b),
                (poly.b, poly.c),
                (poly.c, poly.a)
            )
        
        return (
            (poly.a, poly.b),
            (poly.b, poly.c),
            (poly.c, poly.d),
            (poly.d, poly.a)
        )
    
    
    @staticmethod
    def TransposeMatrice(m) :
    
        """Transpose une matrice."""
        
        v1 = c4d.Vector(m.v1.x, m.v2.x, m.v3.x)
        v2 = c4d.Vector(m.v1.y, m.v2.y, m.v3.y)
        v3 = c4d.Vector(m.v1.z, m.v2.z, m.v3.z)
        
        m.v1 = v1
        m.v2 = v2
        m.v3 = v3
        m.off = c4d.Vector()
        
        return m
    
    
    @staticmethod
    def RefleteVecteur(v, n) :
    
        """Reflète un vecteur par une normale."""
        
        return (2. * (v.Dot(n)) * n) - v
    
    
    @staticmethod
    def IntersectionSegments2DBool(p, p2, q, q2) :
        
        """Détermine si deux segments 2D se croisent."""
        
        def ccw(p, p2, q) :
            return (q.y - p.y) * (p2.x - p.x) > (p2.y - p.y) * (q.x - p.x)
        
        return ccw(p, q, q2) != ccw(p2, q, q2) and ccw(p, p2, q) != ccw(p, p2, q2)
    
    
    @staticmethod
    def IntersectionSegments2DVector(p, p2, q, q2) :
        
        """Calcule le point d'intersection entre deux segments 2D."""
        
        def CrossScal(a, v) :
            return a.x * v.y - a.y * v.x
        
        r = p2 - p
        s = q2 - q
        rxs = CrossScal(r, s)
        qpxr = CrossScal((q - p), r)
    
        if rxs == 0. :
            return None
    
        t = CrossScal((q - p), s) / rxs
        u = CrossScal((q - p), r) / rxs
    
        if (0 <= t and t <= 1) and (0 <= u and u <= 1) :
            return p + t*r
    
        return None
    
    
    @staticmethod
    def IntersectionDroites2DBool(p1, p2, q1, q2) :
        
        """Détermine si deux droites 2D se croisent."""
        
        pa = p1.y - p2.y
        pb = p2.x - p1.x
        
        qa = q1.y - q2.y
        qb = q2.x - q1.x
        
        d  = pa * qb - pb * qa
        
        return d != 0.
    
    
    @staticmethod
    def IntersectionDroites2DVector(p1, p2, q1, q2) :
        
        """Calcule le point d'intersection entre deux droites 2D."""
        
        pa = p1.y - p2.y
        pb = p2.x - p1.x
        pc =  p2.x * p1.y - p1.x * p2.y
        
        qa = q1.y - q2.y
        qb = q2.x - q1.x
        qc = q2.x * q1.y - q1.x * q2.y
        
        d  = pa * qb - pb * qa
        dx = pc * qb - pb * qc
        dy = pa * qc - pc * qa
        
        if d != 0. :
            return c4d.Vector(dx / d, dy / d, 0.)
        
        else :
            return None
        
    
    @staticmethod
    def VolumeTetraedre(p1, p2, p3) :
        
        """Calcule le volume d'un tétraèdre."""
        
        return p1.Dot(p2.Cross(p3)) / 6.0
        
    
    @staticmethod
    def AirePolygone(pol, pts) :
        
        """Calcule l'aire d'un polygone."""
        
        a = pts[pol.a]
        b = pts[pol.b]
        c = pts[pol.c]
        
        if pol.IsTriangle() :
        
            return (a - b).Cross(a - c).GetLength() * 0.5
        
        d = pts[pol.d]
        
        ac = a - c
        return ((a - b).Cross(ac).GetLength() + (ac).Cross(a - d).GetLength()) * 0.5
        
        
    @staticmethod
    def PerimetrePolygone(pol, pts) :
        
        """Calcule le périmètre d'un polygone."""
        
        a = pts[pol.a]
        b = pts[pol.b]
        c = pts[pol.c]
        
        if pol.IsTriangle() :
            
            return (a - b).GetLength() + (b - c).GetLength() + (c - a).GetLength()
        
        d = pts[pol.d]
        
        return (a - b).GetLength() + (b - c).GetLength() + (c - d).GetLength() + (d - a).GetLength()
        
    
    @staticmethod
    def OrthocentreTriangle(a, b, c) :
        
        """Calcule l'orthocentre d'un triangle 3D."""
        
        ab = b - a
        bc = c - b
        cb = b - c
        abn = ab.GetNormalized()
        bcn = bc.GetNormalized()
        
        mab = abn.Dot(c - a) * abn + a
        mbc = bcn.Dot(a - b) * bcn + b
        
        n = (a - c).Cross(cb)
        
        hab = ab.Cross(n)
        hbc = (cb).Cross(n)
        
        m = c4d.utils.HPBToMatrix(c4d.utils.VectorToHPB(n))
        m.off = b
        mi = ~m
        
        hab = mi.Mul(hab)
        hbc = mi.Mul(hbc)
        
        mab = mi.Mul(mab)
        mbc = mi.Mul(mbc)
        
        inter = VoncUtils.IntersectionDroites2DVector(mab, mab + hab, mbc, mbc + hbc)
        
        if inter is None :
            return None
        
        inter = m.Mul(inter)
        
        return inter
    
    
    @staticmethod
    def CalculeNbPolysParPoint(obj, bsPolys = None) :
        
        """Calcule le nombre de polygones autour de chaque point."""
        
        polys = obj.GetAllPolygons()
        nbPts = obj.GetPointCount()
        
        nbPolysParPts = [0] * nbPts
        
        if bsPolys is None :
        
            for poly in polys :
                
                nbPolysParPts[poly.a] += 1
                nbPolysParPts[poly.b] += 1
                nbPolysParPts[poly.c] += 1
                
                if (poly.c != poly.d) : 
                    nbPolysParPts[poly.d] += 1

        else :
            
            nbPolys = obj.GetPolygonCount()
            bsTous = bsPolys.GetAll(nbPolys)
            
            for i, sel in enumerate(bsTous) :
                
                if not sel : continue
                
                poly = polys[i]
                
                nbPolysParPts[poly.a] += 1
                nbPolysParPts[poly.b] += 1
                nbPolysParPts[poly.c] += 1
                
                if (poly.c != poly.d) : 
                    nbPolysParPts[poly.d] += 1
        
        return nbPolysParPts
    
    
    @staticmethod
    def CalculePointsBordureListe(obj, bsPolys = None) :
        
        """Calcule la liste des points qui constituent la bordure de l'objet."""
        
        polys = obj.GetAllPolygons()
        
        ptsBordure = []
        
        liaisonPts = VoncUtils.CalculeLiaisonPoints(obj, bsPolys)
        nbPolysParPts = VoncUtils.CalculeNbPolysParPoint(obj, bsPolys)
        
        for i, npol in enumerate(nbPolysParPts) :
            
            npt = len(liaisonPts[i])
            if (npt != npol and npol != 0) :
                ptsBordure.append(i)
        
        return ptsBordure
    
    
    @staticmethod
    def CalculePointsBordureSelect(obj, bsPolys = None) :
        
        """Calcule la sélection des points qui constituent la bordure de l'objet."""
        
        polys = obj.GetAllPolygons()
        nbPts = obj.GetPointCount()
        
        ptsBordure = [False] * nbPts
        
        liaisonPts = VoncUtils.CalculeLiaisonPoints(obj, bsPolys)
        nbPolysParPts = VoncUtils.CalculeNbPolysParPoint(obj, bsPolys)
        
        for i, npol in enumerate(nbPolysParPts) :
            
            npt = len(liaisonPts[i])
            if (npt != npol) :
                ptsBordure[i] = True
        
        return ptsBordure
    
    
    @staticmethod
    def CalculePointsBordureNeighborSelect(obj, bs, n = None) :
        
        """Calcule la sélection des points qui constituent la bordure de la sélection du BaseSelect."""
        
        nbPolys = obj.GetPolygonCount()
        nbPts = obj.GetPointCount()
        polys = obj.GetAllPolygons()
        
        if n is None :
            n = c4d.utils.Neighbor()
            n.Init(obj, bs)
        
        ptsBords = [False] * nbPts
        
        for i, sel in enumerate(bs.GetAll(nbPolys)) :
        
            if sel :
            
                poly = polys[i]
            
                vois = n.GetNeighbor(poly.a, poly.b, i)
                if vois == c4d.NOTOK :
                    ptsBords[poly.a] = True
                    ptsBords[poly.b] = True
                
                vois = n.GetNeighbor(poly.b, poly.c, i)
                if vois == c4d.NOTOK :
                    ptsBords[poly.b] = True
                    ptsBords[poly.c] = True
                
                vois = n.GetNeighbor(poly.d, poly.a, i)
                if vois == c4d.NOTOK :
                    ptsBords[poly.d] = True
                    ptsBords[poly.a] = True
                
                if poly.c != poly.d :
                    vois = n.GetNeighbor(poly.c, poly.d, i)
                    if vois == c4d.NOTOK :
                        ptsBords[poly.c] = True
                        ptsBords[poly.d] = True
        
        return ptsBords
        
    
    @staticmethod
    def CalculeAretes(obj) :
        
        """Calcule la liste des arêtes de l'objet."""
        
        aretes = []
        aretesDico = {}
            
        polys = obj.GetAllPolygons()
        
        for pol in polys :
            
            areAB = (pol.a, pol.b)
            if (pol.b < pol.a) : areAB = (pol.b, pol.a)
            aretesDico[areAB] = True
            
            areBC = (pol.b, pol.c)
            if (pol.c < pol.b) : areBC = (pol.c, pol.b)
            aretesDico[areBC] = True
            
            if pol.c == pol.d :
                areCA = (pol.c, pol.a)
                if (pol.a < pol.c) : areCA = (pol.a, pol.c)
                aretesDico[areCA] = True
                
            else :
                areCD = (pol.c, pol.d)
                if (pol.d < pol.c) : areCD = (pol.d, pol.c)
                aretesDico[areCD] = True
                
                areDA = (pol.d, pol.a)
                if (pol.a < pol.d) : areDA = (pol.a, pol.d)
                aretesDico[areDA] = True
        
        aretes = aretesDico.keys()
        return aretes
    
    
    @staticmethod
    def DictionnaireAretes(obj) :
        
        """Renvoie le dictionnaire des arêtes de l'objet."""
        
        aretesDico = {}
            
        polys = obj.GetAllPolygons()
        
        for pol in polys :
            
            areAB = (pol.a, pol.b)
            if (pol.b < pol.a) : areAB = (pol.b, pol.a)
            aretesDico[areAB] = None
            
            areBC = (pol.b, pol.c)
            if (pol.c < pol.b) : areBC = (pol.c, pol.b)
            aretesDico[areBC] = None
            
            if pol.c == pol.d :
                areCA = (pol.c, pol.a)
                if (pol.a < pol.c) : areCA = (pol.a, pol.c)
                aretesDico[areCA] = None
                
            else :
                areCD = (pol.c, pol.d)
                if (pol.d < pol.c) : areCD = (pol.d, pol.c)
                aretesDico[areCD] = None
                
                areDA = (pol.d, pol.a)
                if (pol.a < pol.d) : areDA = (pol.a, pol.d)
                aretesDico[areDA] = None
        
        return aretesDico
    
    
    @staticmethod
    def CalculeAretesBordure(obj) :
        
        """Calcule la liste des arêtes de bordure de l'objet."""
        
        aretes = []
        
        polys = obj.GetAllPolygons()
        n = c4d.utils.Neighbor()
        n.Init(obj)
        
        for i, pol in enumerate(polys) :
            
            nab = n.GetNeighbor(pol.a, pol.b, i)
            nbc = n.GetNeighbor(pol.b, pol.c, i)
            nda = n.GetNeighbor(pol.d, pol.a, i)
            
            if nab == c4d.NOTOK :
                are = (pol.a, pol.b) if pol.a < pol.b else (pol.b, pol.a)
                aretes.append(are)
            
            if nbc == c4d.NOTOK :
                are = (pol.b, pol.c) if pol.b < pol.c else (pol.c, pol.b)
                aretes.append(are)
            
            if nda == c4d.NOTOK :
                are = (pol.d, pol.a) if pol.d < pol.a else (pol.a, pol.d)
                aretes.append(are)
                
            if pol.c != pol.d :
            
                ncd = n.GetNeighbor(pol.c, pol.d, i)
                
                if ncd == c4d.NOTOK :
                    are = (pol.c, pol.d) if pol.c < pol.d else (pol.d, pol.c)
                    aretes.append(are)
                
        return aretes
    
    
    @staticmethod
    def CalculeAretesBordureAvecPolys(obj, n = None, bsPolys = None) :
        
        """Calcule la liste des arêtes de bordure de l'objet, avec l'id du poly pour chaque arête."""
        
        aretes = []
        
        polys = obj.GetAllPolygons()
        nbPolys = obj.GetPolygonCount()
        
        if n is None :
            n = c4d.utils.Neighbor()
            n.Init(obj, bsPolys)
        
        def calculeBord(n, pol, i, aretes) :
        
            nab = n.GetNeighbor(pol.a, pol.b, i)
            nbc = n.GetNeighbor(pol.b, pol.c, i)
            nda = n.GetNeighbor(pol.d, pol.a, i)
            
            if nab == c4d.NOTOK :
                are = (pol.a, pol.b, i) if pol.a < pol.b else (pol.b, pol.a, i)
                aretes.append(are)
            
            if nbc == c4d.NOTOK :
                are = (pol.b, pol.c, i) if pol.b < pol.c else (pol.c, pol.b, i)
                aretes.append(are)
            
            if nda == c4d.NOTOK :
                are = (pol.d, pol.a, i) if pol.d < pol.a else (pol.a, pol.d, i)
                aretes.append(are)
                
            if pol.c != pol.d :
            
                ncd = n.GetNeighbor(pol.c, pol.d, i)
                    
                if ncd == c4d.NOTOK :
                    are = (pol.c, pol.d, i) if pol.c < pol.d else (pol.d, pol.c, i)
                    aretes.append(are)
        
        
        if bsPolys is None :
            for i, pol in enumerate(polys) :
                calculeBord(n, pol, i, aretes)
        
        else :
            bsTous = bsPolys.GetAll(nbPolys)
            for i, sel in enumerate(bsTous) :
                if sel : calculeBord(n, polys[i], i, aretes)
        
        return aretes
    
    
    @staticmethod
    def CalculePolysBordure(obj) :
        
        """Calcule les polys qui déterminent la bordure de l'objet."""
        
        polysBord = [False] * obj.GetPolygonCount()
        
        polys = obj.GetAllPolygons()
        n = c4d.utils.Neighbor()
        n.Init(obj)
        
        for i, pol in enumerate(polys) :
            
            nab = n.GetNeighbor(pol.a, pol.b, i)
            nbc = n.GetNeighbor(pol.b, pol.c, i)
            nda = n.GetNeighbor(pol.d, pol.a, i)
            
            if nab == c4d.NOTOK :
                polysBord[i] = True
            
            if nbc == c4d.NOTOK :
                polysBord[i] = True
            
            if nda == c4d.NOTOK :
                polysBord[i] = True
                
            if pol.c != pol.d :
            
                ncd = n.GetNeighbor(pol.c, pol.d, i)
                    
                if ncd == c4d.NOTOK :
                    polysBord[i] = True
                
        return polysBord
    
    
    @staticmethod
    def CalculePointPolysTries(obj, nei = None) :
        
        """Calcule les polygones attachés à chaque point, triés par sens horaire."""
        
        if nei is None :
            nei = c4d.utils.Neighbor()
            nei.Init(obj)
        
        nbPts = obj.GetPointCount()
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        
        pointsPolys = [None] * nbPts
        
        for i in xrange(nbPts) :
            
            polsDuPt = nei.GetPointPolys(i)
            
            if not polsDuPt :
                pointsPolys[i] = []
                continue
            
            nbVois = len(polsDuPt)
            polsDuPtTrie = []
            
            pol = polsDuPt[0]
            polDeb = pol
            arete = None
            inverser = False
            autreAreteDebut = False
            
            k = 0
            while k < nbVois + 1 :
                
                polsDuPtTrie.append(pol)
                
                poly = polys[pol]
                
                # icentre est le point i, iexts sont les deux autres points du poly, situés après et avant icentre (par ordre abcd)
                icentre = poly.a
                iexts = (poly.b, poly.d) # a, suivant : b, précédent : d (c = d pour triangle)
                if poly.b == i :
                    icentre = poly.b
                    iexts = (poly.c, poly.a) # b, suivant : c, précédent : a
                elif poly.c == i :
                    icentre = poly.c
                    if poly.c != poly.d :
                        iexts = (poly.d, poly.b) # (pour quadrangle) c, suivant : d, précédent : b
                    else :
                        iexts = (poly.a, poly.b) # (pour triangle) c, suivant : a, précédent : b
                elif poly.d == i :
                    icentre = poly.d
                    iexts = (poly.a, poly.c) # (pour quadrangle exclusivement) b, suivant : a, précédent : c
                
                if k == 0 or autreAreteDebut :
                    # Pour le premier poly, prendre l'arête du point qui suit l'ordre abcd du poly (nécessite les normales alignées)
                    arete = (icentre, iexts[0])
                    if autreAreteDebut :
                        arete = (icentre, iexts[1])
                    
                else :
                    # Prendre une arête du poly qui ne soit pas celle précédemment choisie
                    arete = (icentre, iexts[0]) if iexts[0] != arete[1] else (icentre, iexts[1])
                
                prem, sec = nei.GetEdgePolys(arete[0], arete[1])
                
                # Trouver le voisin qui ne soit pas égal au premier poly ni au poly précédemment ajouté
                if prem != c4d.NOTOK or sec != c4d.NOTOK :
                    
                    if prem != polDeb and prem != pol and prem != c4d.NOTOK :
                        
                        pol = prem
                        
                    elif sec != polDeb and sec != pol and sec != c4d.NOTOK :
                        
                        pol = sec
                    
                    else :
                        if k == 0 :
                            # Dans le cas du premier poly ajouté, si le sens choisi donne sur un bord, parcourir dans l'autre sens puis inverser le résultat et supprimer le doublon du premier poly.
                            pol = pol
                            polsDuPtTrie.pop()
                            inverser = True
                        else :
                            if len(polsDuPtTrie) != nbVois :
                                # S'il manque des polys, c'est qu'on a commencé par une arête d'un poly sans arête de bordure, et qu'on est tombé sur une bordure prématurément
                                # Dans ce cas, partir dans l'autre sens à partir du poly de départ, mais avec l'autre arête.
                                # Inverser le tableau actuel pour que l'ajout se fasse dans le bon ordre, puis réinverser le tout.
                                polsDuPtTrie.reverse()
                                _deb = polsDuPtTrie.pop()
                                inverser = True
                                autreAreteDebut = True
                                pol = polDeb
                                polDeb = _deb
                            else :
                                break
                    
                else :
                    break
                
                k += 1
            
            if not inverser :
                polsDuPtTrie.reverse()
            
            pointsPolys[i] = polsDuPtTrie
            
        return pointsPolys
    
    
    @staticmethod
    def CalculeLiaisonPoints(obj, bsPolys = None) :
        
        """Calcule la liste des points voisins de chaque point."""
        
        nbPts = obj.GetPointCount()
        polys = obj.GetAllPolygons()
        liaisonPts = [None] * nbPts
        
        for i in xrange(nbPts) :
            liaisonPts[i] = set()
        
        if bsPolys is None :
            for poly in polys :
                
                liaisonPts[poly.a].add(poly.d)
                liaisonPts[poly.a].add(poly.b)
            
                liaisonPts[poly.b].add(poly.a)
                liaisonPts[poly.b].add(poly.c)
            
                if (poly.c != poly.d) :
                    # Quadrangle
                    liaisonPts[poly.c].add(poly.b)
                    liaisonPts[poly.c].add(poly.d)
                    
                    liaisonPts[poly.d].add(poly.c)
                    liaisonPts[poly.d].add(poly.a)
                    
                else :
                    # Triangle
                    liaisonPts[poly.c].add(poly.b)
                    liaisonPts[poly.c].add(poly.a)
        
        else :
            nbPolys = obj.GetPolygonCount()
            bsTous = bsPolys.GetAll(nbPolys)
            
            for i, sel in enumerate(bsTous) :
                
                if not sel : continue
                poly = polys[i]
                
                liaisonPts[poly.a].add(poly.d)
                liaisonPts[poly.a].add(poly.b)
            
                liaisonPts[poly.b].add(poly.a)
                liaisonPts[poly.b].add(poly.c)
            
                if (poly.c != poly.d) :
                    # Quadrangle
                    liaisonPts[poly.c].add(poly.b)
                    liaisonPts[poly.c].add(poly.d)
                    
                    liaisonPts[poly.d].add(poly.c)
                    liaisonPts[poly.d].add(poly.a)
                    
                else :
                    # Triangle
                    liaisonPts[poly.c].add(poly.b)
                    liaisonPts[poly.c].add(poly.a)
        
        
        for i in xrange(nbPts) :
            liaisonPts[i] = tuple(liaisonPts[i])
        
        return liaisonPts
        
    
    @staticmethod
    def CalculeGroupesParPoly(obj):
    
        """
            Calcule pour chaque poly l'ID du groupe de polygones auquel il appartient
            
            Renvoie :
                polysGroupe : Liste d'ID de groupe pour chaque polygone
                nbGroupes : Nombre de groupes
        """
        
        if not obj : return None, None
        
        nbPolys = obj.GetPolygonCount()
        polys = obj.GetAllPolygons()
        
        polysGroupe = [-1] * nbPolys
        nbGroupes = 0
        
        n = c4d.utils.Neighbor()
        n.Init(obj)
        
        for i, g in enumerate(polysGroupe) :
            
            if g != -1 : continue
            
            polysATraiter = [i]
            
            while(polysATraiter) :
            
                polysATraiterSuivant = []
                
                for p in polysATraiter :
                    
                    polysGroupe[p] = nbGroupes
                    
                for p in polysATraiter :
                    
                    poly = polys[p]
                    
                    voisA = n.GetNeighbor(poly.a, poly.b, p)
                    voisB = n.GetNeighbor(poly.b, poly.c, p)
                    voisD = n.GetNeighbor(poly.d, poly.a, p)
                    voisC = c4d.NOTOK
                    
                    if poly.c != poly.d :
                        voisC = n.GetNeighbor(poly.c, poly.d, p)
                    
                    if voisA != c4d.NOTOK and polysGroupe[voisA] == -1 :
                        polysATraiterSuivant.append(voisA)
                    
                    if voisB != c4d.NOTOK and polysGroupe[voisB] == -1 :
                        polysATraiterSuivant.append(voisB)
                    
                    if voisC != c4d.NOTOK and polysGroupe[voisC] == -1 :
                        polysATraiterSuivant.append(voisC)
                    
                    if voisD != c4d.NOTOK and polysGroupe[voisD] == -1 :
                        polysATraiterSuivant.append(voisD)
                
                polysATraiter = list(set(polysATraiterSuivant))
            
            nbGroupes += 1
        
        return polysGroupe, nbGroupes
    
    
    @staticmethod
    def BruitPoints(obj, intensite = 1.0, temps = 0.0, echelle = 1.0) :
        
        """Renvoie une valeur aléatoire pour chaque point de l'objet."""
        
        nbPts = obj.GetPointCount()
        pts = obj.GetAllPoints()
        bruit = [0.] * nbPts
        
        if nbPts == 0 : return bruit
        
        noise = c4d.utils.noise.Noise
        mixVec = c4d.utils.MixVec
        
        for i in xrange(nbPts) :
            
            bruit[i] = noise(pts[i] * echelle, temps) * intensite
        
        return bruit
        
        
    @staticmethod
    def BruiteObjet(obj, intensite = 1.0, temps = 0.0, echelle = 1.0) :
        
        """Déforme les points d'un objet le long de ses arêtes de façon aléatoire."""
        
        nbPts = obj.GetPointCount()
        pts = obj.GetAllPoints()
        pts2 = [c4d.Vector()] * nbPts
        
        if nbPts == 0 : return
        
        liaisonPts = VoncUtils.CalculeLiaisonPoints(obj)
        bruit = c4d.utils.noise.Noise
        mixVec = c4d.utils.MixVec
        
        for i in xrange(nbPts) :
            
            ptsVois = liaisonPts[i] # Points autour du point
            nbPtsVois = len(ptsVois)
            
            pt = pts[i]
            
            pt2 = c4d.Vector()
            
            for vois in ptsVois :
                
                n = bruit(pts[vois] * echelle, temps) * 2. - 1.
                pt2 += mixVec(pt, pts[vois], n)
            
            pt2 /= float(nbPtsVois)
            
            pts2[i] = mixVec(pt, pt2, intensite)
        
        obj.SetAllPoints(pts2)
        obj.Message(c4d.MSG_UPDATE)
        
    
    @staticmethod
    def LissageCatmullClark(obj, intensite = 1.0, influence = None, nei = None, bsPolys = None) :
        
        """
            Lisse les points de l'objet basé sur la méthode Catmull-Clark.
            
            intensite : Facteur d'intensité du lissage (0.0 à 1.0).
            influence : Facteur d'intensité du lissage pour chaque point.
            nei : Neighbor initialisé de l'objet.
        """
        
        if nei is None :
            nei = c4d.utils.Neighbor()
            nei.Init(obj, bsPolys)
        
        nbPts = obj.GetPointCount()
        pts = obj.GetAllPoints()
        
        if nbPts == 0 : return
        
        ptsNouv = [c4d.Vector()] * nbPts
        
        liaisonPts = VoncUtils.CalculeLiaisonPoints(obj, bsPolys)
        centrePolys = VoncUtils.CalculeCentrePolys(obj)
        
        
        for i in xrange(nbPts) :
            
            polsVois = nei.GetPointPolys(i) # Polys autour du point
            nbPolsVois = len(polsVois)
            
            ptsVois = liaisonPts[i] # Points liés au point i
            nbPtsVois = len(ptsVois)
            
            if not nbPolsVois or not nbPtsVois :
                ptsNouv[i] = pts[i]
                continue
            
            pmaBord = [] # Point de Milieu d'Arête de Bordure
            po = pts[i] # Point Original
            pos = c4d.Vector() # Nouvelle position
            
            if nbPtsVois != nbPolsVois : # Bordure
                
                if nbPtsVois != nbPolsVois + 1 : # Point avec plus de 2 bordures
                    
                    pos = po
                    
                else :
                    
                    pmaMoy = c4d.Vector()
                    nbBord = 0
                    
                    for j in ptsVois :
                        
                        prem, sec = nei.GetEdgePolys(i, j)
                        
                        if prem == c4d.NOTOK or sec == c4d.NOTOK :
                            pmaMoy += (po + pts[j]) * .5
                            nbBord += 1
                    
                    pmaMoy /= float(nbBord)
                    
                    pos = (po + pmaMoy) * .5
                  
            else :
                
                nbPolsVois = float(nbPolsVois)
                nbPtsVois = float(nbPtsVois)
                
                # Moyenne des Point de Surface
                psMoy = c4d.Vector()
                for j in polsVois :
                    psMoy += centrePolys[j]
                psMoy /= nbPolsVois
                
                # Moyenne des Points d'Arête
                pmaMoy = c4d.Vector()
                for j in ptsVois :
                    pmaMoy += (po + pts[j]) * .5
                pmaMoy /= nbPtsVois
                
                # Catmull-Clark
                pos = psMoy + (2.0 * pmaMoy) + ((nbPolsVois - 3.0) * po);
                pos /= nbPolsVois
                
            
            ptsNouv[i] = pos
            
            
        for i, ptNouv in enumerate(ptsNouv) :
            
            if influence :
                pts[i] = ptNouv * intensite * influence[i] + (pts[i] * (1. - intensite * influence[i]))
            else :
                pts[i] = ptNouv * intensite + (pts[i] * (1. - intensite))
        
        
        obj.SetAllPoints(pts)
        obj.Message(c4d.MSG_UPDATE)
        
    
    @staticmethod
    def RecopieObjet(src, dest) :
    
        """Recopie les données polygonales d'un objet vers un autre"""
        
        nouvNbPts = src.GetPointCount()
        nouvNbPolys = src.GetPolygonCount()
        nouvPts = src.GetAllPoints()
        nouvPolys = src.GetAllPolygons()
        
        if nouvNbPts == 0 :
            return False
        
        dest.ResizeObject(nouvNbPts, nouvNbPolys)
        dest.Message(c4d.MSG_UPDATE)
        
        dest.SetAllPoints(nouvPts)
        for i, p in enumerate(nouvPolys) :
            dest.SetPolygon(i, p)
            
        dest.Message(c4d.MSG_SMALLUPDATE)
        return True
        
    
    @staticmethod
    def Optimiser(obj, doc, polys = True, pts = True, ptsiso = True, tolerance = 0.01) :
        
        """Optimiser."""
        
        params = c4d.BaseContainer()
        params[c4d.MDATA_OPTIMIZE_TOLERANCE] = tolerance
        params[c4d.MDATA_OPTIMIZE_POINTS] = pts
        params[c4d.MDATA_OPTIMIZE_POLYGONS] = polys
        params[c4d.MDATA_OPTIMIZE_UNUSEDPOINTS] = ptsiso
        c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_OPTIMIZE, list=[obj], bc=params, doc=doc)
        
    
    @staticmethod
    def AlignerNormales(obj, doc) :
        
        """Aligner les normales."""
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_ALIGNNORMALS, list=[obj], doc=doc)
    
    
    @staticmethod
    def InverserNormales(obj, doc) :
        
        """Inverser les normales."""
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_REVERSENORMALS, list=[obj], doc=doc)
    
    
    @staticmethod
    def Trianguler(obj, doc) :
        
        """Convertit les polygones en triangles."""
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_TRIANGULATE, list=[obj], doc=doc)
    
    
    @staticmethod
    def Quadranguler(obj, doc) :
        
        """Convertit les triangles en quadrangles."""
        
        bc = c4d.BaseContainer()
        bc[c4d.MDATA_UNTRIANGULATE_NGONS] = False
        bc[c4d.MDATA_UNTRIANGULATE_ANGLE_RAD] = .1 * (math.pi / 180.)
        bc[c4d.MDATA_UNTRIANGULATE_ANGLE] = False
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_UNTRIANGULATE, list=[obj], bc=bc, doc=doc)
    
    
    @staticmethod
    def Subdiviser(obj, doc, iterations = 1, catmullClark = True, selectionActive = False, bs = None) :
        
        """Subdivise les polygones d'un objet."""
        
        bc = c4d.BaseContainer()
        bc[c4d.MDATA_SUBDIVIDE_HYPER] = catmullClark
        bc[c4d.MDATA_SUBDIVIDE_ANGLE] = math.pi
        bc[c4d.MDATA_SUBDIVIDE_SUB] = iterations
        mode = c4d.MODELINGCOMMANDMODE_ALL
        
        if selectionActive :
            mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION
            
        if bs is not None :
            mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION
            bsc = bs.GetClone()
            bs = obj.GetPolygonS()
            bs.DeselectAll()
            bs.Merge(bsc)
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_SUBDIVIDE, list=[obj], mode=mode, bc=bc, doc=doc)
    
    
    @staticmethod
    def FermerTrous(obj, doc) :
        
        """Ferme tous les trous d'un objet."""
        
        polys = obj.GetAllPolygons()
        n = c4d.utils.Neighbor()
        n.Init(obj)
        params = c4d.BaseContainer()
        params[c4d.MDATA_CLOSEHOLE_INDEX] = obj
        params[c4d.MDATA_CLOSEHOLE_TRI] = True
        mode = c4d.MODELINGCOMMANDMODE_EDGESELECTION
        commande = c4d.ID_MODELING_CLOSEHOLE_TOOL
        
        SendModelingCommand = c4d.utils.SendModelingCommand
        
        for i, pol in enumerate(polys) :
            
            nab = n.GetNeighbor(pol.a, pol.b, i)
            nbc = n.GetNeighbor(pol.b, pol.c, i)
            ncd = n.GetNeighbor(pol.c, pol.d, i)
            nda = n.GetNeighbor(pol.d, pol.a, i)
            cotes = n.GetPolyInfo(i)["edge"]
            
            if nab == c4d.NOTOK :
                params[c4d.MDATA_CLOSEHOLE_EDGE] = cotes[0]
                SendModelingCommand(command=commande, list=[obj], mode=mode, bc=params, doc=doc)
                n.Init(obj)
                
            if nbc == c4d.NOTOK :
                params[c4d.MDATA_CLOSEHOLE_EDGE] = cotes[1]
                SendModelingCommand(command=commande, list=[obj], mode=mode, bc=params, doc=doc)
                n.Init(obj)
                
            if ((ncd == c4d.NOTOK) and (pol.c != pol.d)) :
                params[c4d.MDATA_CLOSEHOLE_EDGE] = cotes[2]
                SendModelingCommand(command=commande, list=[obj], mode=mode, bc=params, doc=doc)
                n.Init(obj)
                
            if nda == c4d.NOTOK :
                params[c4d.MDATA_CLOSEHOLE_EDGE] = cotes[3]
                SendModelingCommand(command=commande, list=[obj], mode=mode, bc=params, doc=doc)
                n.Init(obj)
        
        obj.Message(c4d.MSG_UPDATE)

        
    @staticmethod
    def BiseauterCommande(obj, doc, rayon = 0., mode = "chanfrein", subdivision = 0, limite = False) :
        
        """Biseaute la sélection d'arêtes courante, en utilisant CallCommand."""
        
        mmode = doc.GetMode()
        doc.SetMode(c4d.Medges)
        c4d.CallCommand(431000015, 431000015)
        
        tool = c4d.plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)
        
        if tool is not None :
            
            tool[c4d.MDATA_BEVEL_LIMIT] = limite
            tool[c4d.MDATA_BEVEL_SUB] = subdivision
            tool[c4d.MDATA_BEVEL_RADIUS] = rayon
            
            tool[c4d.MDATA_BEVEL_MASTER_MODE] = c4d.MDATA_BEVEL_MASTER_MODE_CHAMFER
            
            if mode == "solide" :
                tool[c4d.MDATA_BEVEL_MASTER_MODE] = c4d.MDATA_BEVEL_MASTER_MODE_SOLID
                
            c4d.CallButton(tool, c4d.MDATA_APPLY)
            
        doc.SetMode(mmode)
        
    
    @staticmethod
    def Biseauter(obj, decalage, nei = None, bs = None, selectionnerNouvPolys = False, aretesCentrales = None, facteurPolys = None, facteurPoints = None, limiter = True, facInf = 1.) :
        
        """Biseaute une sélection d'arêtes."""
        
        if obj is None or not obj.CheckType(c4d.Opolygon) : return None
        
        nbPts = obj.GetPointCount()
        nbPolys = obj.GetPolygonCount()
        pts = obj.GetAllPoints()
        polys = obj.GetAllPolygons()
        ipol = nbPolys
        
        nouvPolys = [None] * nbPolys # Copie des polys
        for i, pol in enumerate(polys) :
            nouvPolys[i] = c4d.CPolygon(pol.a, pol.b, pol.c, pol.d)
        
        if nbPolys == 0 : return None
        
        bsNouvPolys = c4d.BaseSelect()
        
        if bs is None :
            bs = obj.GetEdgeS()
        
        if nei is None :
            nei = c4d.utils.Neighbor()
            nei.Init(obj)
        
        bsTous = bs.GetAll(nbPolys * 4)
        
        mixNum = c4d.utils.MixNum
        
        IntersectionSegments2DVector = VoncUtils.IntersectionSegments2DVector
        
        # Zone d'influence
        vt = obj.GetTag(c4d.Tvertexmap)
        vtInf = None
        if vt is not None :
            vtInf = vt.GetAllHighlevelData()
        
        # Normales des polygones
        
        normalesPoly = VoncUtils.CalculeNormalesPolys(obj)
        
        
        # Dictionnaire des arêtes de l'objet, initialisé avec des futures infos d'arête
        
        aretesDico = VoncUtils.DictionnaireAretes(obj)
        for are in aretesDico.keys() :
            aretesDico[are] = [-1, -1] # Indice des futurs points à ajouter éventuellement à cette arête
        
        
        # Points d'arête sélectionnés
        
        ptsSel = VoncUtils.MarquagePointsDepuisAretes(obj, None, bsTous)
        
        # Calcul des nouveaux points et polys
        
        for i, pol in enumerate(polys) :
            
            
            # Passer les polys sans arête sélectionnée
            
            if not bsTous[i*4] and not bsTous[i*4+1] and not bsTous[i*4+2] and not bsTous[i*4+3] : continue
            
            estTri = pol.IsTriangle()
            
            # Facteur Poly
            
            aireFacPt = 1.
            aireFacPoly = 1.
            if facteurPolys is not None :
                aireFacPoly = facteurPolys[i]
            
            
            # Calcul des normales des arêtes du poly
            
            
            # Liste les arêtes du poly
            
            aretesPoly = VoncUtils.ListeAretesPolygone(pol)
            aretesPolySensABCD = VoncUtils.ListeAretesPolygoneSensABCD(pol)
            
            
            # Initialise les informations des points et des arêtes du poly
            
            nbAretes = 3 if estTri else 4
            
            aretesNormales = [c4d.Vector()] * nbAretes # Normales des arêtes
            pointsSel = [0] * nbAretes # Nombre de fois que le points est utilisé par une arête sélectionnée du poly
            pointsNormales = [c4d.Vector()] * nbAretes # Normales des points
            pointsNouveaux = [-1] * nbAretes # Id possible des nouveaux points
            pointsDot = [None] * nbAretes # Futur produit scalaire entre les normales des arêtes
            for j in xrange(nbAretes) :
                pointsDot[j] = []
            
            normalePoly = normalesPoly[i] # Normale du poly
            
            
            # Pour chaque arête, calculer sa normale
            
            for j in xrange(nbAretes) :
                
                # Si l'arête est sélectionnée...
                
                if bsTous[i*4 + j] :
                    
                    areABC = aretesPolySensABCD[j] # Arête du poly, sens horaire
                    
                    i0 = j # ABCD en mode 0123
                    i1 = j + 1
                    if i1 == nbAretes : i1 = 0
                    
                    areteNormale = (pts[areABC[0]] - pts[areABC[1]]).Cross(normalePoly).GetNormalized() # Normale de l'arête du poly
                    aretesNormales[j] = areteNormale
                    
                    pointsSel[i0] += 1 # Point utilisé une fois de plus par une arête sélectionnée
                    pointsSel[i1] += 1
                    
                    pointsNormales[i0] += areteNormale # Pour calcul de la normale du point
                    pointsNormales[i1] += areteNormale
                    pointsDot[i0].append(areteNormale)
                    pointsDot[i1].append(areteNormale)
            
            
            # Finalise la normale des points
            
            for j in xrange(nbAretes) :
                
                if pointsSel[j] > 1 : # Point de poly entre deux arête sel
                    pointsNormales[j].Normalize()
                    pointsDot[j] = pointsDot[j][0].Dot(pointsDot[j][1])
                    
            
            # Normale pour correction
            
            normalePoly = None
            matNormale = None
            matNormaleI = None
            
            if limiter :
                normalePoly = normalesPoly[i]
                matNormale = c4d.utils.HPBToMatrix(c4d.utils.VectorToHPB(normalePoly))
                matNormaleI = ~matNormale
            
            
            # Créé les nouveaux points
            
            for j in xrange(nbAretes) :
                
                if bsTous[i*4 + j] :
                    
                    areABC = aretesPolySensABCD[j]
                    
                    i0 = j # ABCD en mode 0123
                    i1 = j + 1
                    if i1 == nbAretes : i1 = 0
                    
                    p0 = areABC[0] # ABCD en mode 12, 15, 68, 20
                    p1 = areABC[1]
                    n0 = -1 # Nouvel id du point
                    n1 = -1
                    
                    # Définit les nouveaux id des points
                    
                    if pointsSel[i0] == 1 : # Si point utilisé par une seule arête, créer le point sur l'arête adjacente non sel
                        
                        are = VoncUtils.AreteAdjacente(i0, i1, pol) # Arete non sel, adjacente à l'arête sel
                        pDep, pFin = (0, 1) if are[0] == p0 else (1, 0)
                        areDico = aretesDico[are]
                        
                        if areDico[pDep] == -1 : # Indice du nouveau point sauvegardé pour ce côté de l'arête
                            areDico[pDep] = nbPts
                            
                            if facteurPoints is not None :
                                aireFacPt = facteurPoints[are[pDep]]
                                if vt is not None :
                                    vtInf.append(facInf * vtInf[are[pDep]])
                            
                            pts.append(pts[are[pDep]] + (pts[are[pFin]] - pts[are[pDep]]).GetNormalized() * decalage * aireFacPoly * aireFacPt)
                            nbPts += 1
                            
                        n0 = areDico[pDep]
                        pointsNouveaux[i0] = n0 # Sauvegarde l'id du nouveau point pour ce 0123 du poly
                    
                    elif pointsSel[i0] > 1 : # Sinon, créer le point à partir de la normale du point
                        
                        if pointsNouveaux[i0] == -1 :
                            
                            pointsNouveaux[i0] = nbPts # Sauvegarde l'id du nouveau point pour ce 0123 du poly
                            dotFac = (1.-pointsDot[i0])
                            dotFac *= 1.3
                            if dotFac < 1. : dotFac = 1.
                            
                            if facteurPoints is not None :
                                aireFacPt = facteurPoints[p0]
                                if vt is not None :
                                    vtInf.append(facInf * vtInf[p0])
                            
                            pts.append(pts[p0] + pointsNormales[i0] * decalage * dotFac * aireFacPoly * aireFacPt)
                            # pts.append(pts[p0] + pointsNormales[i0] * decalage)
                            
                            if aretesCentrales is not None : # Sauvegarde l'arête centrale, pour usage externe
                                aretesCentrales.append((p0, nbPts))
                        
                            nbPts += 1
                            
                        n0 = pointsNouveaux[i0]
                    
                    
                    # Idem que précédemment, mais avec l'autre point de l'arête
                    
                    if pointsSel[i1] == 1 : # Si point utilisé par une seule arête, créer le point sur l'arête adjacente non sel
                        
                        are = VoncUtils.AreteAdjacente(i1, i0, pol) # Arete non sel, adjacente à l'arête sel
                        pDep, pFin = (0, 1) if are[0] == p1 else (1, 0) # Si beugue, regarder ici
                        areDico = aretesDico[are]
                        
                        if areDico[pDep] == -1 :
                            areDico[pDep] = nbPts
                            
                            if facteurPoints is not None :
                                aireFacPt = facteurPoints[are[pDep]]
                                if vt is not None :
                                    vtInf.append(facInf * vtInf[are[pDep]])
                            
                            pts.append(pts[are[pDep]] + (pts[are[pFin]] - pts[are[pDep]]).GetNormalized() * decalage * aireFacPoly * aireFacPt)
                            nbPts += 1
                            
                        n1 = areDico[pDep]
                        pointsNouveaux[i1] = n1 # Sauvegarde l'id du nouveau point pour ce 0123 du poly
                    
                    elif pointsSel[i1] > 1 : # Sinon, créer le point à partir de la normale du point
                        
                        if pointsNouveaux[i1] == -1 :
                            
                            pointsNouveaux[i1] = nbPts # Sauvegarde l'id du nouveau point pour ce 0123 du poly
                            dotFac = (1.-pointsDot[i1])
                            dotFac *= 1.3
                            if dotFac < 1. : dotFac = 1.
                            
                            if facteurPoints is not None :
                                aireFacPt = facteurPoints[p1]
                                if vt is not None :
                                    vtInf.append(facInf * vtInf[p1])
                            
                            pts.append(pts[p1] + pointsNormales[i1] * decalage * dotFac * aireFacPoly * aireFacPt)
                            # pts.append(pts[p1] + pointsNormales[i1] * decalage)
                            
                            if aretesCentrales is not None : # Sauvegarde l'arête centrale, pour usage externe
                                aretesCentrales.append((p1, nbPts))
                        
                            nbPts += 1
                            
                        n1 = pointsNouveaux[i1]
                    
                    
                    # Créé le nouveau polygone pour cette arête
                    
                    if n0 != -1 and n1 != -1 :
                        
                        # Corrige le poly concave
                        if limiter :
                            _n0 = matNormale.MulV(pts[n0])
                            _p0 = matNormale.MulV(pts[p0])
                            _n1 = matNormale.MulV(pts[n1])
                            _p1 = matNormale.MulV(pts[p1])
                            _n0m = _p0 + (_n0 - _p0)
                            
                            inter = IntersectionSegments2DVector(_p0, _n0m, _p1, _n1)
                            if inter is not None :
                                _n1 = (_n1 + _n0) * .5
                                _n0 = _n1
                                pts[n1] = matNormaleI.MulV(_n1)
                                pts[n0] = matNormaleI.MulV(_n0)
                            else :
                                _n1m = _p1 + (_n1 - _p1)
                                inter = IntersectionSegments2DVector(_p0, _n0, _p1, _n1m)
                                if inter is not None :
                                    _n1 = (_n1 + _n0) * .5
                                    _n0 = _n1
                                    pts[n0] = matNormaleI.MulV(_n0)
                                    pts[n1] = matNormaleI.MulV(_n1)
                        
                        nouvPolys.append(c4d.CPolygon(p0, p1, n1, n0))
                        bsNouvPolys.Select(ipol)
                        ipol += 1
                    
            
            # Modifie le polygone original
            
            polNouv = nouvPolys[i]
            if pointsNouveaux[0] != -1 : polNouv.a = pointsNouveaux[0]
            if pointsNouveaux[1] != -1 : polNouv.b = pointsNouveaux[1]
            if pointsNouveaux[2] != -1 : polNouv.c = pointsNouveaux[2]
            if not estTri :
                if pointsNouveaux[3] != -1 : polNouv.d = pointsNouveaux[3]
            
        
        
        # Calcul des nouveaux points et polys des polygones qui ont juste un point d'arête sélectionné
        
        for i, pol in enumerate(polys) :
            
            # Passe les polys qui n'ont pas de point sélectionné
            
            nbPtsSel = sum([ptsSel[pol.a], ptsSel[pol.b], ptsSel[pol.c], ptsSel[pol.d]])
            if nbPtsSel == 0 : continue
            
            # Divers
            estTri = pol.IsTriangle()
            
            # Facteur Poly
            aireFacPt = 1.
            aireFacPoly = 1.
            if facteurPolys is not None :
                aireFacPoly = facteurPolys[i]
            
            # Liste des points du poly
            
            ptsPol = (pol.a, pol.b, pol.c, pol.d)
            nbPtsPol = 4
            if estTri :
                ptsPol = (pol.a, pol.b, pol.c)
                nbPtsPol = 3
            
            pointsNouveaux = [-1] * nbPtsPol
            
            # Traiter uniquement les points sélectionnés qui sont entre deux arêtes non sélectionnées
            
            for j, p in enumerate(ptsPol) :
                
                if not ptsSel[p] : continue
                
                jSuiv = (j + 1) % nbPtsPol
                jPrec = (j - 1) % nbPtsPol
                
                idArePrec = jPrec
                idAreSuiv = j
                
                # Passer si le point est entre au moins une arête sélectionnée dans le poly
                
                if bsTous[i*4 + idAreSuiv] or bsTous[i*4 + idArePrec] : continue
                
                # Calcul des points
                
                aretesPolySensABCD = VoncUtils.ListeAretesPolygoneSensABCD(pol)
                aretesPoly = VoncUtils.ListeAretesPolygone(pol)
                
                arePrecABC = aretesPolySensABCD[idArePrec] # Arête du poly, sens horaire
                areSuivABC = aretesPolySensABCD[idAreSuiv]
                arePrec = aretesPoly[idArePrec] # Arête du poly, sens horaire
                areSuiv = aretesPoly[idAreSuiv]
                
                nPrec = -1
                nSuiv = -1
                
                arePrecDico = aretesDico[arePrec]
                areSuivDico = aretesDico[areSuiv]
                
                
                # Arête précédente
                
                pDep, pFin = (0, 1)
                
                if arePrecDico[pDep] == -1 :
                    arePrecDico[pDep] = nbPts
                    
                    if facteurPoints is not None :
                        aireFacPt = facteurPoints[arePrecABC[pFin]]
                        if vt is not None :
                            vtInf.append(facInf * vtInf[arePrecABC[pFin]])
                    
                    pts.append(pts[arePrecABC[pFin]] + (pts[arePrecABC[pDep]] - pts[arePrecABC[pFin]]).GetNormalized() * decalage * aireFacPoly * aireFacPt)
                    nbPts += 1
                    
                nPrec = arePrecDico[pDep]
                
                
                # Arête suivante
                
                pDep, pFin = (1, 0) # Si bug, regarder ici
                
                if areSuivDico[pFin] == -1 :
                    areSuivDico[pFin] = nbPts
                    
                    if facteurPoints is not None :
                        aireFacPt = facteurPoints[areSuivABC[pFin]]
                        if vt is not None :
                            vtInf.append(facInf * vtInf[areSuivABC[pFin]])
                    
                    pts.append(pts[areSuivABC[pFin]] + (pts[areSuivABC[pDep]] - pts[areSuivABC[pFin]]).GetNormalized() * decalage * aireFacPoly * aireFacPt) # Si bug, regarder ici
                    # pts.append(pts[areSuivABC[pDep]] + (pts[areSuivABC[pFin]] - pts[areSuivABC[pDep]]).GetNormalized() * decalage)
                    nbPts += 1
                    
                nSuiv = areSuivDico[pFin]
                
                nSuivDep = areSuivABC[pDep]
                
                # Nouveaux polygones
                
                if nPrec != -1 and nSuiv != -1 :
                    
                    # Polygone de biseau
                    
                    nouvPolys.append(c4d.CPolygon(p, nSuiv, nPrec))
                    bsNouvPolys.Select(ipol)
                    ipol += 1
                    
                    # Modifie le polygone original et ajoute un petit triangle (car pas de ngon possible encore)
                    
                    polNouv = nouvPolys[i]
                    if j == 0 : polNouv.a = nPrec
                    elif j == 1 : polNouv.b = nPrec
                    elif j == 2 : polNouv.c = nPrec
                    elif j == 3 : polNouv.d = nPrec
                    
                    if pol.a == nSuivDep : nSuivDep = polNouv.a
                    elif pol.b == nSuivDep : nSuivDep = polNouv.b
                    elif pol.c == nSuivDep : nSuivDep = polNouv.c
                    elif pol.d == nSuivDep : nSuivDep = polNouv.d
                    
                    nouvPolys.append(c4d.CPolygon(nSuivDep, nPrec, nSuiv))
                    ipol += 1
        
        
        # Applique les nouvelles coordonnées
        
        obj.ResizeObject(nbPts, ipol)
        obj.SetAllPoints(pts)
        for i, p in enumerate(nouvPolys) :
            obj.SetPolygon(i, p)
            
        obj.Message(c4d.MSG_UPDATE)
        
        vt = obj.GetTag(c4d.Tvertexmap)
        if vt is not None :
            vt.SetAllHighlevelData(vtInf)
        
        
        # Sélectionne les nouveaux polys
        
        if selectionnerNouvPolys :
            bsPolys = obj.GetPolygonS()
            bsPolys.DeselectAll()
            bsNouvPolys.CopyTo(bsPolys)
            
        obj.Message(c4d.MSG_SMALLUPDATE)
        
        return bsNouvPolys
        
    
    @staticmethod
    def BiseauterC4D(obj, doc, bs, rayon = 0., mode = "chanfrein", subdivision = 0, limite = False) :
        
        """Biseaute la sélection d'arêtes courante, en utilisant le déformateur Biseau. Renvoie l'objet biseauté."""
        
        # Propriété Sélection d'arêtes
        propSelAretes = obj.MakeTag(c4d.Tedgeselection)
        propSelAretes.SetName("SelAreBiseau")
        bsAre = propSelAretes.GetBaseSelect()
        bsAre.Merge(bs)
        
        # Déformateur Biseau
        defBiseau = c4d.BaseObject(431000028)
        defBiseau[c4d.O_BEVEL_MODE_COMPONENT_TYPE] = c4d.O_BEVEL_MODE_COMPONENT_TYPE_EDGE
        defBiseau[c4d.O_BEVEL_RESTRICTION_START] = propSelAretes.GetName()
        if mode == "solide" : defBiseau[c4d.O_BEVEL_MASTER_MODE] = c4d.O_BEVEL_MASTER_MODE_SOLID
        else : defBiseau[c4d.O_BEVEL_MASTER_MODE] = c4d.O_BEVEL_MASTER_MODE_CHAMFER
        defBiseau[c4d.O_BEVEL_RADIUS] = rayon
        defBiseau[c4d.O_BEVEL_SUB] = subdivision
        defBiseau[c4d.O_BEVEL_LIMIT] = limite
        defBiseau.InsertUnder(obj)
        
        res = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[obj], doc=doc)
        
        if res : doc.InsertObject(res[0])
        
        propSelAretes.Remove()
        defBiseau.Remove()
        
        if res : return res[0]
        return None
    
    
    @staticmethod
    def Extruder(obj, decalage, capot = False, subdivision = 0, bs = None, normalesPts = None, extrusionBi = False, facteurDec = None) :
        
        """Extrude les polygones sélectionnés. Renvoie le type de chaque polygone."""
        
        if obj is None or not obj.CheckType(c4d.Opolygon) : return None
        
        nbPts = obj.GetPointCount()
        nbPolys = obj.GetPolygonCount()
        pts = obj.GetAllPoints()
        polys = obj.GetAllPolygons()
        if bs is None :
            bs = c4d.BaseSelect()
            bs.SelectAll(nbPolys-1)
        nbPolysSel = 0
        nouvNbPolys = nbPolys
        
        if not capot : extrusionBi = False
        if extrusionBi : decalage *= .5
        
        # Tableau pour les tableaux de marquage de retour
        tableauSel = [0] * nbPolys # 0 : Polygones originaux
        SEL_FACE_AVANT = 1 # Face avant
        SEL_FACE_TRANCHE = 2 # Tranche
        SEL_FACE_ARRIERE = 3 # Face arrière
        
        if decalage > 0 and capot :
            SEL_FACE_AVANT, SEL_FACE_ARRIERE = SEL_FACE_ARRIERE, SEL_FACE_AVANT
        
        if facteurDec is not None and len(facteurDec) != nbPts :
            facteurDec = None
        
        if normalesPts is not None and len(normalesPts) != nbPts :
            normalesPts = None
        
        if normalesPts is None :
            normalesPts = VoncUtils.CalculeNormalesPoints(obj, bs)
        
        bsTout = bs.GetAll(nbPolys)
        
        for i, sel in enumerate(bsTout) :
            if sel :
                nbPolysSel += 1
                tableauSel[i] = SEL_FACE_AVANT
        
        if nbPolysSel == 0 : return None
        
        
        # Récupère les arêtes et les points de bordure
        
        aretesBordPoly = VoncUtils.CalculeAretesBordureAvecPolys(obj, None, bs)
        nbBords = len(aretesBordPoly) * (subdivision+1)
        ptsBords = [False] * nbPts
        for are in aretesBordPoly :
            ptsBords[are[0]] = True
            ptsBords[are[1]] = True
        
        
        # Récupère les points à extruder et compte le nombre de polys attachés à eux
        
        ptsAEx = [0] * nbPts
        
        for i, sel in enumerate(bsTout) :
            
            if not sel : continue
            
            poly = polys[i]
            
            ptsAEx[poly.a] += 1
            ptsAEx[poly.b] += 1
            ptsAEx[poly.c] += 1
            if poly.c != poly.d : ptsAEx[poly.d] += 1
        
        
        # Compte le nouveau nombre de points et créé la correspondance des anciens points vers ceux extrudés
        
        nouvNbPts = nbPts
        ancPtVersNouv = [0] * nbPts
        
        for i, sel in enumerate(ptsAEx) :
            
            # Si le point fait parti du base select des polys et, dans le cas sans capot, s'il fait parti des bords, le copier
            if sel != 0 and (capot or ptsBords[i]) :
                
                ancPtVersNouv[i] = nouvNbPts
                nouvNbPts += 1
                
                if subdivision and ptsBords[i] : # Bordure
                
                    nouvNbPts += subdivision
        
        
        # Créé les nouveaux points
        
        nouvPts = [c4d.Vector()] * (nouvNbPts - nbPts)
        pts.extend(nouvPts)
        
        for i, sel in enumerate(ptsAEx) :
            
            if sel != 0 :
                
                # Si bordure, prendre le point clone correspondant, sinon prendre le même qu'on bouge (cas sans capot)
                j = i
                if (capot or ptsBords[i]) : j = ancPtVersNouv[i]
                
                facDec = decalage + 0.
                if facteurDec is not None :
                    facDec *= facteurDec[i]
                
                pts[j] = pts[i] + normalesPts[i] * facDec
                if extrusionBi :
                    pts[i] = pts[i] - normalesPts[i] * facDec
                
                if subdivision and ptsBords[i] : # Bordure
                    
                    if not extrusionBi :
                        for k in xrange(subdivision) :
                            pts[j + k + 1] = pts[i] + normalesPts[i] * facDec * (1. - (k + 1.) / (subdivision + 1.))
                    else :
                        for k in xrange(subdivision) :
                            pts[j + k + 1] = pts[i] + normalesPts[i] * facDec * (1. - (k + 1.) / (subdivision + 1.)) * 2.
                        
        
        # Créé les polygones de tranche
        
        j = nouvNbPolys
        nouvNbPolys += nbBords
        nouvPolys = [None] * nbBords
        polys.extend(nouvPolys)
        tableauSel.extend([SEL_FACE_TRANCHE] * nbBords)
        
        for are in aretesBordPoly :
            
            p0 = are[0]
            p1 = are[1]
            ipol = are[2]
            pol = polys[ipol]
            
            i0 = pol.Find(p0)
            i1 = pol.Find(p1)
            
            if pol.IsTriangle() :
                if i0 == 3 : i0 = 2
                if i1 == 3 : i1 = 2
                if decalage > 0. or not capot :
                    if i0 == (i1 + 1) % 3:
                        p0, p1 = p1, p0
                else :
                    if i1 == (i0 + 1) % 3:
                        p0, p1 = p1, p0
            
            else :
                if decalage > 0. or not capot :
                    if i0 == (i1 + 1) % 4:
                        p0, p1 = p1, p0
                else :
                    if i1 == (i0 + 1) % 4:
                        p0, p1 = p1, p0
            
            p2 = ancPtVersNouv[p1]
            p3 = ancPtVersNouv[p0]
            
            if subdivision :
            
                for i in xrange(subdivision + 1) :
                    
                    poly = c4d.CPolygon(
                        p0,
                        p1,
                        p2 + subdivision - i,
                        p3 + subdivision - i
                    )
                    polys[j] = poly
                    j += 1
                    
                    p0 = poly.d
                    p1 = poly.c
            
            else :
                poly = c4d.CPolygon(
                    p0,
                    p1,
                    p2,
                    p3
                )
                polys[j] = poly
                j += 1
            
        aretesBordPoly = None
        
        
        # Créé les nouveaux polygones extrudés
        
        if capot :
            
            j = nouvNbPolys
            nouvNbPolys += nbPolysSel
            nouvPolys = [None] * nbPolysSel
            polys.extend(nouvPolys)
            tableauSel.extend([SEL_FACE_ARRIERE] * nbPolysSel)
            
            for i, sel in enumerate(bsTout) :
                
                if not sel : continue
                
                poly = polys[i]
                
                nouvPoly = None
                
                if decalage > 0. :
                    nouvPoly = c4d.CPolygon(
                        ancPtVersNouv[poly.a],
                        ancPtVersNouv[poly.b],
                        ancPtVersNouv[poly.c],
                        ancPtVersNouv[poly.d]
                    )
                else :
                    nouvPoly = c4d.CPolygon(
                        ancPtVersNouv[poly.d],
                        ancPtVersNouv[poly.c],
                        ancPtVersNouv[poly.b],
                        ancPtVersNouv[poly.a]
                    )
                
                polys[j] = nouvPoly
                # tableauSel[j] = SEL_FACE_ARRIERE
                
                j += 1
        
        else :
            
            for i, sel in enumerate(bsTout) :
                
                if not sel : continue
                
                poly = polys[i]
                
                a = poly.a
                b = poly.b
                c = poly.c
                d = poly.d
                
                poly.a = ancPtVersNouv[a] if capot or ptsBords[a] else a
                poly.b = ancPtVersNouv[b] if capot or ptsBords[b] else b
                poly.c = ancPtVersNouv[c] if capot or ptsBords[c] else c
                if c != d :
                    poly.d = ancPtVersNouv[d] if capot or ptsBords[d] else d
                else :
                    poly.d = poly.c
        
        
        # Si capot et décalage positif renverser les normales des polys du capot
        
        if capot and decalage > 0. :
            
            for i, sel in enumerate(bsTout) :
                
                if not sel : continue
                
                poly = polys[i]
                
                a = poly.a
                b = poly.b
                c = poly.c
                d = poly.d
                
                if c != d :
                    poly.a = d
                    poly.b = c
                    poly.c = b
                    poly.d = a
                else :
                    poly.a = b
                    poly.b = a
        
        
        # Applique les nouvelles coordonnées
        
        obj.ResizeObject(nouvNbPts, nouvNbPolys)
        obj.SetAllPoints(pts)
        for i, p in enumerate(polys) :
            obj.SetPolygon(i, p)
            
        obj.Message(c4d.MSG_UPDATE)
        return tableauSel
        
    
    @staticmethod
    def ExtruderC4D(obj, doc, decalage, capot = False, subdivision = 0, ngones = False, angle = 3.141592653589793) :
        
        """Extrude les polygones sélectionnés avec la méthode de C4D."""
        
        bc = c4d.BaseContainer()
        bc[c4d.MDATA_EXTRUDE_PRESERVEGROUPS] = True
        bc[c4d.MDATA_EXTRUDE_OFFSET        ] = decalage
        bc[c4d.MDATA_EXTRUDE_VARIANCE      ] = 0.0
        bc[c4d.MDATA_EXTRUDE_ANGLE         ] = angle
        bc[c4d.MDATA_EXTRUDE_SUBDIVISION   ] = subdivision
        bc[c4d.MDATA_EXTRUDE_CREATENGONS   ] = ngones
        bc[c4d.MDATA_EXTRUDE_CREATECAPS    ] = capot
        
        return c4d.utils.SendModelingCommand(command = c4d.ID_MODELING_EXTRUDE_TOOL,
                                list = [obj],
                                bc = bc,
                                mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
                                doc = doc)
        
        
    @staticmethod
    def ReductionDePolys(obj, doc, intensite, courbe = False) :
        
        """Applique le déformateur Réduction de polys."""
        
        # Déformateur Biseau
        defReduc = c4d.BaseObject(c4d.Opolyreduction)
        defReduc[c4d.POLYREDUCTIONOBJECT_STRENGTH] = intensite
        defReduc[c4d.POLYREDUCTIONOBJECT_BOUNDARY] = courbe
        defReduc.InsertUnder(obj)
        
        res = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[obj], doc=doc)
        
        if res : doc.InsertObject(res[0])
        
        defReduc.Remove()
        
        if res : return res[0]
        return None
    
    
    @staticmethod
    def SupprimerPolygones(obj, doc) :
        
        """Supprime la sélection de polygones courante."""
        
        c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_DELETE,
                                list = [obj],
                                mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
                                doc = doc)
    
    
    @staticmethod
    def BaseSelectAretesToutes(obj) :
        
        """Renvoie le BaseSelect de toutes les arêtes sélectionnées."""
        
        bs = c4d.BaseSelect()
        nbPolys = obj.GetPolygonCount()
        bs.SelectAll(nbPolys * 4 - 1)
        
        return bs
    
    
    @staticmethod
    def BaseSelectPolysDepuisAretes(obj, bsAretes) :
        
        """Renvoie le BaseSelect des polys ayant au moins une arête sélectionnée."""
        
        bs = c4d.BaseSelect()
        nbAretes = obj.GetPolygonCount() * 4
        
        for i, sel in enumerate(bsAretes.GetAll(nbAretes)) :
            if sel :
                bs.Select(int(i / 4))
        
        return bs
    
    
    @staticmethod
    def BaseSelectPolysTous(obj) :
        
        """Renvoie le BaseSelect de tous les polys sélectionnées."""
        
        bs = c4d.BaseSelect()
        nbPolys = obj.GetPolygonCount()
        bs.SelectAll(nbPolys - 1)
        
        return bs
    
    
    @staticmethod
    def SelectionnePointsTous(obj) :
        
        """Sélectionne tous les points."""
        
        if obj.CheckType(c4d.Opoint) :
            bs = obj.GetPointS()
            bs.SelectAll(obj.GetPointCount() - 1)
    
    
    @staticmethod
    def SelectionnePointsPolysPoints(obj, nei = None) :
        
        """Sélectionne tous les points des polys des points sélectionnés."""
        
        if not obj.CheckType(c4d.Opolygon) : return
        
        if not nei :
            nei = c4d.utils.Neighbor()
            nei.Init(obj)
        
        nbPts = obj.GetPointCount()
        polys = obj.GetAllPolygons()
        bs = obj.GetPointS()
        bs2 = c4d.BaseSelect()
        bsTous = bs.GetAll(nbPts)
        
        for i, sel in enumerate(bsTous) :
            if not sel : continue
            vois = nei.GetPointPolys(i)
            for voi in vois :
                pol = polys[voi]
                bs2.Select(pol.a)
                bs2.Select(pol.b)
                bs2.Select(pol.c)
                if pol.c != pol.d : bs2.Select(pol.d)
        
        bs.Merge(bs2)
        
    
    @staticmethod
    def SelectionnePolysTous(obj) :
        
        """Sélectionne tous les polygones."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonS()
            bs.SelectAll(obj.GetPolygonCount() - 1)
    
    
    @staticmethod
    def SelectionnePolysInverse(obj) :
        
        """Inverse la sélection de polys courante."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonS()
            bs.ToggleAll(0, obj.GetPolygonCount() - 1)
            
            
    @staticmethod
    def SelectionnePolysAucun(obj) :
        
        """Désélectionne tous les polygones."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonS()
            bs.DeselectAll()
    
    
    @staticmethod
    def SelectionneAretesToutes(obj, doc) :
        
        """Sélectionne toutes les arêtes."""
        
        c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_SELECTALL,
                                list = [obj],
                                mode = c4d.MODELINGCOMMANDMODE_EDGESELECTION,
                                doc = doc)
    
    
    @staticmethod
    def SelectionneAretesInverse(obj, doc) :
        
        """Inverse la sélection d'arêtes courante."""
        
        c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_SELECTINVERSE,
                                list = [obj],
                                mode = c4d.MODELINGCOMMANDMODE_EDGESELECTION,
                                doc = doc)
    
    
    @staticmethod
    def SelectionneAretesAucune(obj) :
        
        """Désélectionne toutes les arêtes."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetEdgeS()
            bs.DeselectAll()
    
    
    @staticmethod
    def SelectionneAretesContour(obj, doc, n = None) :
    
        """Sélectionne les arêtes de contour."""
        
        if not obj : return
        if obj.GetType() != c4d.Opolygon: return
        
        nbPolys = obj.GetPolygonCount()
        polys = obj.GetAllPolygons()
        bs = c4d.BaseSelect()
        bsAre = obj.GetEdgeS().GetClone()
        
        if n is None :
            n = c4d.utils.Neighbor()
            n.Init(obj)
        
        for i, pol in enumerate(polys):
            
            pa = pol.a
            pb = pol.b
            pc = pol.c
            pd = pol.d
            
            nab = n.GetNeighbor(pa, pb, i)
            nbc = n.GetNeighbor(pb, pc, i)
            ncd = n.GetNeighbor(pc, pd, i)
            nda = n.GetNeighbor(pd, pa, i)
            
            cotes = n.GetPolyInfo(i)["edge"]
            
            if nab == -1:
                bs.Select(cotes[0])
            if nbc == -1:
                bs.Select(cotes[1])
            if ncd == -1:
                if pc != pd: bs.Select(cotes[2])
            if nda == -1:
                bs.Select(cotes[3])
        
        obj.SetSelectedEdges(n, bs, c4d.EDGESELECTIONTYPE_SELECTION)
        bs = obj.GetEdgeS()
        bs.Merge(bsAre)
    
    
    @staticmethod
    def MarquagePointsDepuisAretes(obj, bsAretes = None, bsTous = None) :
    
        """Renvoie un tableau de marquage des points sélectionnés à partir d'un BaseSelect d'arêtes."""
        
        polys = obj.GetAllPolygons()
        nbPolys = obj.GetPolygonCount()
        nbPts = obj.GetPointCount()
        
        if bsAretes is None :
            bsAretes = obj.GetEdgeS()
        
        if bsTous is None :
            bsTous = bsAretes.GetAll(nbPolys)
        
        ptsSel = [0] * nbPts
        
        for i, pol in enumerate(polys) :
        
            if not bsTous[i*4] and not bsTous[i*4+1] and not bsTous[i*4+2] and not bsTous[i*4+3] : continue
            
            aretesPolySensABCD = VoncUtils.ListeAretesPolygoneSensABCD(pol)
            for j, are in enumerate(aretesPolySensABCD) :
                if bsTous[i*4 + j] :
                    ptsSel[are[0]] = 1
                    ptsSel[are[1]] = 1
        
        return ptsSel
        
        
    @staticmethod
    def AffichePolysTous(obj) :
        
        """Affiche tous les polygones."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonH()
            bs.DeselectAll()
            
            
    @staticmethod
    def AffichePointsTous(obj) :
        
        """Affiche tous les polygones."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPointH()
            bs.DeselectAll()
            
            
    @staticmethod
    def AfficheAretesToutes(obj) :
        
        """Affiche toutes les arêtes."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetEdgeH()
            bs.DeselectAll()