Code Vonc

Utilitaires

CommentairesComments
Sommaire
Calculs sur un objet
Calculs géométriques
Modification d'un objet
Opérations sur les polygones
Opérations sur les matrices
Opérations sur les vecteurs
Sélections
Débug
 
Calculs sur un objet
Calculs géométriques
Modification d'un objet
Opérations sur les polygones
Opérations sur les matrices
Opérations sur les vecteurs
Sélections
Débug
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") :
        
        """
            Ajoute un vecteur au document.
            
            Paramètres :
                doc (BaseDocument) - Document
                a (Vector) - Position de début
                b (Vector) - Position de fin
                coul (Vector) - Couleur du vecteur
                nom (string) - Nom du 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.)) :
        
        """
            Ajoute un texte au document.
            
            Paramètres :
                doc (BaseDocument) - Document
                pos (Vector) - Position
                texte (string) - Texte à afficher
                taille (float) - Taille de la police
                couleur (Vector) - Couleur de la police
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de Vector) - Liste des normales
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bsPolys (BaseSelect) - Sélection de polygones pour limiter le calcul des normales des points
            
            Renvoie :
                (liste de Vector) - Liste des normales
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bsPolys (BaseSelect) - Sélection de polygones pour limiter le calcul des normales
            
            Renvoie :
                (liste de Vector) - Liste des normales des points
                (liste de Vector) - Liste des normales des polys
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de Vector) - Liste des positions
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                intensite (float) - Intensité du bruit
                temps (float) - Temps
                echelle (float) - Échelle du bruit
            
            Renvoie :
                (liste de Vector) - Liste des positions
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de int) - Liste d'entiers
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de float) - Liste des aires
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de float) - Liste de flottants de 0.0 à 1.0
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de float) - Liste des périmètres
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de Vector) - Liste des positions
        """
        
        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.
            
            Paramètres :
                p0 (int) - ID du premier point de l'arête
                p1 (int) - ID du second point de l'arête
                poly (CPolygon) - Polygone concerné
            
            Renvoie :
                (tuple(int, int)) - ID du premier et du second point de l'arête opposée
        """
        
        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.
            
            Paramètres :
                p0 (int) - ID du premier point de l'arête
                p1 (int) - ID du second point de l'arête
                poly (CPolygon) - Polygone concerné
            
            Renvoie :
                (tuple(int, int)) - ID du premier et du second point de l'arête adjacente, trié par ID
        """
        
        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.
            
            Paramètres :
                poly (CPolygon) - Polygone
            
            Renvoie :
                (tuple de tuple(int, int)) - Liste des arêtes, une arête étant un tuple trié de deux ID de points
        """
        
        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 ici n'ont pas leurs indices triés.
            
            Paramètres :
                poly (CPolygon) - Polygone
            
            Renvoie :
                (tuple de tuple(int, int)) - Liste des arêtes, une arête étant ici un tuple non trié de deux ID de points
        """
        
        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.
            
            Paramètres :
                m (Matrix) - 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()
    
    
    @staticmethod
    def RefleteVecteur(v, n) :
    
        """
            Reflète un vecteur par rapport à une normale. La normale doit être normalisée.
            
            Paramètres :
                v (Vector) - Vecteur initial
                n (Vector) - Vecteur normale (normalisée)
            
            Renvoie :
                (Vector) - Vecteur réfléchi
        """
        
        return (2. * (v.Dot(n)) * n) - v
    
    
    @staticmethod
    def IntersectionSegments2DBool(p, p2, q, q2) :
        
        """
            Détermine si deux segments 2D se croisent.
            
            Paramètres :
                p (Vector) - Point de départ du premier segment
                p2 (Vector) - Point d'arrivée du premier segment
                q (Vector) - Point de départ du second segment
                q2 (Vector) - Point d'arrivée du second segment
            
            Renvoie :
                (bool) - Booléen
        """
        
        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.
            
            Paramètres :
                p (Vector) - Point de départ du premier segment
                p2 (Vector) - Point d'arrivée du premier segment
                q (Vector) - Point de départ du second segment
                q2 (Vector) - Point d'arrivée du second segment
            
            Renvoie :
                (Vector ou None) - Point d'intersection
        """
        
        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.
            
            Paramètres :
                p1 (Vector) - Point sur la première droite
                p2 (Vector) - Point sur la première droite
                q1 (Vector) - Point sur la seconde droite
                q2 (Vector) - Point sur la seconde droite
            
            Renvoie :
                (bool) - Booléen
        """
        
        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.
            
            Paramètres :
                p1 (Vector) - Point sur la première droite
                p2 (Vector) - Point sur la première droite
                q1 (Vector) - Point sur la seconde droite
                q2 (Vector) - Point sur la seconde droite
            
            Renvoie :
                (Vector ou None) - Point d'intersection
        """
        
        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.
            
            Paramètres :
                p1 (Vector) - Position du premier point
                p2 (Vector) - Position du second point
                p3 (Vector) - Position du troisième point
            
            Renvoie :
                (float) - Volume
        """
        
        return p1.Dot(p2.Cross(p3)) / 6.0
        
    
    @staticmethod
    def AirePolygone(pol, pts) :
        
        """
            Calcule l'aire d'un polygone.
            
            Paramètres :
                pol (CPolygon) - Polygone
                pts (liste de Vector) - Points de l'objet
            
            Renvoie :
                (float) - Aire
        """
        
        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.
            
            Paramètres :
                pol (CPolygon) - Polygone
                pts (liste de Vector) - Points de l'objet
            
            Renvoie :
                (float) - Périmètre
        """
        
        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.
            
            Paramètres :
                a (Vector) - Premier point
                b (Vector) - Second point
                c (Vector) - Troisième point
            
            Renvoie :
                (Vector ou None) - Orthocentre
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bsPolys (BaseSelect) - Sélection de polygones pour limiter le calcul
            
            Renvoie :
                (liste de int) - Liste d'entiers
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bsPolys (BaseSelect) - Sélection de polygones pour limiter le calcul
            
            Renvoie :
                (liste de int) - Liste d'indices des points de bordure
        """
        
        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 CalculePointsBordureMarquage(obj, bsPolys = None) :
        
        """
            Calcule le tableau de marquage des points qui constituent la bordure de l'objet.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bsPolys (BaseSelect) - Sélection de polygones pour limiter le calcul
            
            Renvoie :
                (liste de bool) - Liste de booléens marquants les points de bordure
        """
        
        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 CalculePointsBordureSelectMarquage(obj, bs, n = None) :
        
        """
            Calcule le tableau de marquage des points qui constituent la bordure de la sélection du BaseSelect.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bs (BaseSelect) - Sélection de polygones pour limiter le calcul
                n (Neighbor) - Neighbor initialisé avec le BaseSelect
            
            Renvoie :
                (liste de bool) - Liste de booléens marquants les points de bordure
        """
        
        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. Une arête est un couple de deux indices de points, le premier inférieur au second.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de tuple(int, int)) - Liste d'arêtes, une arête étant un tuple de deux ID de points, trié.
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (dictionnaire de tuple(int, int)) - Dictionnaire avec les arêtes comme clef et None comme valeur, une arête étant un tuple de deux ID de points, trié.
        """
        
        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, n = None) :
        
        """
            Calcule la liste des arêtes de bordure de l'objet.
            
            Paramètres :
                obj (PolygonObject) - Objet
                n (Neighbor) - Neighbor initialisé
            
            Renvoie :
                (liste de tuple(int, int)) - Liste d'arêtes de bordure, une arête étant un tuple de deux ID de points, trié.
        """
        
        aretes = []
        
        polys = obj.GetAllPolygons()
        
        if n is None :
            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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                n (Neighbor) - Neighbor initialisé avec le BaseSelect
                bsPolys (BaseSelect) - Sélection de polygones pour délimiter la zone
            
            Renvoie :
                (liste de tuple(int, int, int)) - Liste d'arêtes avec ID du polygone, une arête étant un tuple de deux ID de points, trié.
        """
        
        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 CalculePolysBordureMarquage(obj) :
        
        """
            Calcule le tableau de marquage des polys qui déterminent la bordure de l'objet.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (liste de bool) - Liste de booléens marquants les polys qui constituent 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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                nei (Neighbor) - Neighbor initialisé
            
            Renvoie :
                (liste de liste de int) - Liste des ID des polygones autour de 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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bsPolys (BaseSelect) - Sélection de polygones pour délimiter la zone
            
            Renvoie :
                (liste de liste de int) - Liste des ID des points autour 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
            
            Paramètres :
                (PolygonObject) - Objet
            
            Renvoie :
                (liste de int) - Liste d'ID de groupe pour chaque polygone
                (int) - 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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                intensite (float) - Intensité du bruit
                temps (float) - Temps
                echelle (float) - Échelle du bruit
            
            Renvoie :
                (liste de float) - Liste de flottants, de 0.0 à 1.0
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                intensite (float) - Intensité du bruit
                temps (float) - Temps
                echelle (float) - Échelle du bruit
        """
        
        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.
            
            Paramètres :
                intensite (float) - Facteur d'intensité du lissage (0.0 à 1.0).
                influence (liste de float) - Facteur d'intensité du lissage pour chaque point.
                nei (Neihbor) - Neighbor initialisé avec le BaseSelect.
                bsPolys (BaseSelect) - Sélection de polygones pour limiter l'effet.
        """
        
        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.
            
            Paramètres :
                src (PolygonObject) - Objet source
                dest (PolygonObject) - Objet de destination
            
            Renvoie :
                (bool) - Booléen, succès ou non
        """
        
        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_UPDATE)
        return True
        
    
    @staticmethod
    def Optimiser(obj, doc, polys = True, pts = True, ptsiso = True, tolerance = 0.01) :
        
        """
            Optimiser.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
                polys (bool) - Optimiser les polygones
                pts (bool) - Optimiser les points
                ptsiso (bool) - Optimiser les points isolés
                tolerance (float) - Tolérance d'optimisation des points
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
            
            Renvoie :
                (bool) - Succès ou non
        """
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_ALIGNNORMALS, list=[obj], doc=doc)
    
    
    @staticmethod
    def InverserNormales(obj, doc) :
        
        """
            Inverser les normales.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
            
            Renvoie :
                (bool) - Succès ou non
        """
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_REVERSENORMALS, list=[obj], doc=doc)
    
    
    @staticmethod
    def Trianguler(obj, doc) :
        
        """
            Convertit les polygones en triangles.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
            
            Renvoie :
                (bool) - Succès ou non
        """
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_TRIANGULATE, list=[obj], doc=doc)
    
    
    @staticmethod
    def Quadranguler(obj, doc) :
        
        """
            Convertit les triangles en quadrangles.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
            
            Renvoie :
                (bool) - Succès ou non
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
                iterations (int) - Nombre d'itération de la subdivision
                catmullClark (bool) - Active ou non la subdivision de type Catmull-Clark
                selectionActive (bool) - Utilise ou non la sélection de polygone courante
                bs (BaseSelect) - Sélection de polygones pour limiter la subdivision
            
            Renvoie :
                (bool) - Succès ou non
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
                rayon (float) - Rayon du biseau
                mode (string) - Mode du biseau ('chanfrein' ou 'solide')
                subdivision (int) - Subdivision
                limite (bool) - Limite le rayon du biseau
        """
        
        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.) :
        
        """
            !! EN CONSTRUCTION !!
            Biseaute une sélection d'arêtes.
            
            Paramètres :
                obj (PolygonObject) - Objet
                decalage (float) - Rayon du biseau
                nei (Neighbor) - Neighbor initialisé
                bs (BaseSelect) - Sélection d'arêtes
                selectionnerNouvPolys (bool) - Sélectionner ou non les polygones créés par le biseau
                aretesCentrales (liste vide de tuple(int, int)) - Liste vide, qui sera remplie par la liste des arêtes sélectionnées, mises à jour.
                facteurPolys (liste de float) - Intensité du biseau pour chaque polygone
                facteurPoints (liste de float) - Intensité du biseau pour chaque point
                limiter (bool) - 
                facInf (float) - Facteur d'intensité de la propriété Zone d'influence (Tvertexmap) de l'objet.
            
            Renvoie :
                (BaseSelect ou None) - None si echec, BaseSelect des nouveaux polygones créés si succès.
        """
        
        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 une sélection d'arêtes, en utilisant le déformateur Biseau. Renvoie l'objet biseauté.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
                bs (BaseSelect) - Sélection d'arêtes
                rayon (float) - Rayon du biseau
                mode (string) - Mode du biseau ('chanfrein' ou 'solide')
                subdivision (int) - Subdivision
                limite (bool) - Limite le rayon du biseau
            
            Renvoie :
                (BaseObject ou None) - 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. Renvoie le type de chaque polygone.
            
            Paramètres :
                obj (PolygonObject) - Objet
                decalage (float) - Décalage de l'extrusion
                capot (bool) - Créé ou non les polygones du capot
                subdivision (int) - Subdivision de l'extrusion
                bs (BaseSelect) - Sélection de polygones à extruder
                normalesPts (liste de Vector) - Direction de l'extrusion pour chaque point
                extrusionBi (bool) - Extrude vers l'avant et l'arriète
                facteurDec (liste de float) - Intensité de l'extrusion pour chaque point
            
            Renvoie :
                (liste de int, ou None) - Liste d'entiers. Pour chaque polygone :
                    0 : Polygone original
                    1 : Face avant
                    2 : Tranche
                    3 : Face arrière
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
                decalage (float) - Décalage de l'extrusion
                capot (bool) - Créé ou non le capot
                subdivision (int) - Subdivision de l'extrusion
                ngones (bool) - Créé ou non des n-gones
                angle (float) - Angle de limitation de cohésion des polygones extrudés
            
            Renvoie :
                (bool) - Succès ou non
        """
        
        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 polygones.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
                intensite (float) - Intensité de la réduction (de 0.0 à 1.0)
                courbe (bool) - Préserve la délimitation
            
            Renvoie :
                (None ou BaseObject) - Objet réduit
        """
        
        # 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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (BaseSelect) - Sélection 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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bsAretes (BaseSelect) - Sélection d'arêtes
            
            Renvoie :
                (BaseSelect) - Sélection de polygones
        """
        
        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 polygones sélectionnés.
            
            Paramètres :
                obj (PolygonObject) - Objet
            
            Renvoie :
                (BaseSelect) - Sélection de polygones
        """
        
        bs = c4d.BaseSelect()
        nbPolys = obj.GetPolygonCount()
        bs.SelectAll(nbPolys - 1)
        
        return bs
    
    
    @staticmethod
    def SelectionnePointsTous(obj) :
        
        """
            Sélectionne tous les points.
            
            Paramètres :
                obj (PolygonObject) - Objet
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                nei (Neighbor) - Neighbor initialisé
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
        """
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonS()
            bs.SelectAll(obj.GetPolygonCount() - 1)
    
    
    @staticmethod
    def SelectionnePolysInverse(obj) :
        
        """
            Inverse la sélection de polys courante.
            
            Paramètres :
                obj (PolygonObject) - Objet
        """
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonS()
            bs.ToggleAll(0, obj.GetPolygonCount() - 1)
            
            
    @staticmethod
    def SelectionnePolysAucun(obj) :
        
        """
            Désélectionne tous les polygones.
            
            Paramètres :
                obj (PolygonObject) - Objet
        """
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonS()
            bs.DeselectAll()
    
    
    @staticmethod
    def SelectionneAretesToutes(obj, doc) :
        
        """
            Sélectionne toutes les arêtes.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
                doc (BaseDocument) - Document
        """
        
        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.
            
            Paramètres :
                obj (PolygonObject) - Objet
        """
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetEdgeS()
            bs.DeselectAll()
    
    
    @staticmethod
    def SelectionneAretesContour(obj, n = None) :
    
        """
            Sélectionne les arêtes de contour.
            
            Paramètres :
                obj (PolygonObject) - Objet
                n (Neighbor) - Neighbor initialisé
        """
        
        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, bsAretesGetAll = None) :
    
        """
            Renvoie un tableau de marquage des points sélectionnés à partir d'une sélection d'arêtes.
            
            Paramètres :
                obj (PolygonObject) - Objet
                bsAretes (BaseSelect) - Sélection d'arêtes
                bsAretesGetAll (liste de int) - GetAll() du bsAretes
                
            Renvoie :
                (liste de bool) - Liste de booléens marquants les points sélectionnés des arêtes sélectionnées.
        """
        
        polys = obj.GetAllPolygons()
        nbPolys = obj.GetPolygonCount()
        nbPts = obj.GetPointCount()
        
        if bsAretes is None :
            bsAretes = obj.GetEdgeS()
        
        if bsAretesGetAll is None :
            bsAretesGetAll = bsAretes.GetAll(nbPolys * 4)
        
        ptsSel = [False] * nbPts
        
        for i, pol in enumerate(polys) :
            
            if not bsAretesGetAll[i*4] and not bsAretesGetAll[i*4+1] and not bsAretesGetAll[i*4+2] and not bsAretesGetAll[i*4+3] : continue
            
            aretesPolySensABCD = VoncUtils.ListeAretesPolygoneSensABCD(pol)
            
            for j, are in enumerate(aretesPolySensABCD) :
                if bsAretesGetAll[i*4 + j] :
                    ptsSel[are[0]] = True
                    ptsSel[are[1]] = True
            
            if pol.c == pol.d :
                if bsAretesGetAll[i*4 + 3] :
                    are = aretesPolySensABCD[2]
                    ptsSel[are[0]] = True
                    ptsSel[are[1]] = True
            
        return ptsSel
        
        
    @staticmethod
    def AffichePolysTous(obj) :
        
        """
            Affiche tous les polygones.
            
            Paramètres :
                obj (PolygonObject) - Objet
        """
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonH()
            bs.DeselectAll()
            
            
    @staticmethod
    def AffichePointsTous(obj) :
        
        """
            Affiche tous les polygones.
            
            Paramètres :
                obj (PolygonObject) - Objet
        """
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPointH()
            bs.DeselectAll()
            
            
    @staticmethod
    def AfficheAretesToutes(obj) :
        
        """
            Affiche toutes les arêtes.
            
            Paramètres :
                obj (PolygonObject) - Objet
        """
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetEdgeH()
            bs.DeselectAll()