Code Vonc

Géométrie algorithmique

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
Interpolations
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
Opérations sur les vecteurs
Sélections
Débug
Code source

# coding=utf-8
import c4d
import math

class VoncUtils :

    def __init__(self) :
        pass

    """
        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 ProjetteVecteur(a, bNor) :

        """
            Projette un vecteur A sur un vecteur B. Le vecteur B doit être normalisée.

            Paramètres :
                a (Vector) - Vecteur initial
                b (Vector) - Vecteur de projection (normalisé)

            Renvoie :
                (Vector) - Vecteur projeté C
        """

        return a.Dot(bNor) * bNor


    @staticmethod
    def RejetteVecteur(a, bNor) :

        """
            Calcule la réjection d'un vecteur A sur un vecteur B. Le vecteur B doit être normalisée.

            Paramètres :
                a (Vector) - Vecteur initial
                b (Vector) - Vecteur de réjection (normalisée)

            Renvoie :
                (Vector) - Vecteur rejeté C
        """

        return a - a.Dot(bNor) * bNor


    @staticmethod
    def TransformeVecteur(a, b, c, transformeTaille = True) :

        """
            Calcule la différence entre deux vecteurs et applique cette même déformation à un troisième vecteur.

            Paramètres :
                a (Vector) - Vecteur initial
                b (Vector) - Vecteur initial transformé
                c (Vector) - Vecteur à transformer
                transformeTaille (Bool) - Si vrai, transforme également la taille du vecteur

            Renvoie :
                (Vector) - Vecteur transformé D
        """

        aLon = a.GetLength()

        if aLon == 0.0 :
            return c4d.Vector()

        bLon = b.GetLength()

        if bLon == 0.0 :
            return c4d.Vector()

        cLon = c.GetLength()

        if cLon == 0.0 :
            return c4d.Vector()

        facLon = bLon / aLon

        b2 = b.GetNormalized() * cLon

        n = (b2 + c) * 0.5
        n.Normalize()

        d = c4d.Vector()

        # Cas où la normale est nulle
        if n == c4d.Vector() :
            d = -b

        else :
            d = (2. * (a.Dot(n)) * n) - a

        d.Normalize()

        if transformeTaille :
            d *= cLon * facLon

        return d


    @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. and qpxr == 0. :
            return None

        t = CrossScal((q - p), s) / rxs
        u = qpxr / rxs

        # if (0 <= t and t <= 1) and (0 <= u and u <= 1) :
        if (0 <= t <= 1) and (0 <= 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 PointEstDansTriangle2D(pt, v1, v2, v3) :

        """
            Détermine si un point se trouve dans un triangle 2D.

            Paramètres :
                pt (Vector) - Point à déterminer
                v1 (Vector) - Point du triangle
                v2 (Vector) - Point du triangle
                v3 (Vector) - Point du triangle

            Renvoie :
                (bool) - Booléen, si le point est contenu dans le triangle ou non
        """

        def signe(p1, p2, p3) :
            return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y)

        b1 = signe(pt, v1, v2) < 0.0
        b2 = signe(pt, v2, v3) < 0.0
        b3 = signe(pt, v3, v1) < 0.0

        return (b1 == b2) and (b2 == b3)


    @staticmethod
    def InterpoleDansTab(nb, c) :

        """
            Soit un tableau de longueur NB et C un curseur sur le tableau.
            Cette fonction renvoie l'indices des cases autour du curseur et le facteur de mélange entre les deux cases.

            Paramètres :
                nb (int) - Taille du tableau
                c (float) - Curseur, entre 0.0 et 1.0

            Renvoie :
                (int) - Case à gauche du curseur
                (int) - Case à droite du curseur
                (float) - Facteur de mélange entre les deux cases, entre 0.0 et 1.0
        """

        a = 0
        b = 0
        f = 0.0
        nbMU = nb - 1

        if c > 0 :
            bf = c * nbMU
            b = int(math.ceil(bf))
            a = b - 1
            f = 1.0 - (b - bf)

        if a >= nb : a = nbMU
        if b >= nb : b = nbMU
        if a < 0 : a = 0
        if b < 0 : b = 0

        return a, b, f


    @staticmethod
    def InterpoleLineaire(pts, nb, t) :

        """
            Interpole de façon linéaire une valeur dans un tableau de données

            Paramètres :
                pts (liste de Vector ou de float) - Liste des points de contrôles
                t (float) - Facteur d'interpolation, entre 0.0 et 1.0

            Renvoie :
                (Vector ou float) - La valeur interpolée dans le tableau
        """

        # nbMU = nb - 1.0
        # res = Vector()
        #
        # for i in xrange(nb) :
        #     fac = 1.0 - utils.ClampValue( abs(t * nbMU - i), 0.0, 1.0 )
        #     res += pts[i] * fac
        # return res

        a, b, f = Utils.InterpoleDansTab(nb, t)

        return (1.0 - f) * pts[a] + f * pts[b]


    @staticmethod
    def InterpoleDoux(pts, nb, t) :

        """
            Interpole de façon adoucie une valeur dans un tableau de données

            Paramètres :
                pts (liste de Vector ou de float) - Liste des points de contrôles
                t (float) - Facteur d'interpolation, entre 0.0 et 1.0

            Renvoie :
                (Vector ou float) - La valeur interpolée dans le tableau
        """

        a, b, f = Utils.InterpoleDansTab(nb, t)

        f = c4d.utils.Smoothstep(0.0, 1.0, f)

        return (1.0 - f) * pts[a] + f * pts[b]


    @staticmethod
    def InterpoleBezierQuadratique(pts, nb, t) :

        """
            Interpole selon la courbe de Bézier Quadratique une valeur dans un tableau de données

            Paramètres :
                pts (liste de 3 Vector ou de 3 float) - Tableau de 3 points de contrôles
                t (float) - Facteur d'interpolation, entre 0.0 et 1.0

            Renvoie :
                (Vector ou float) - La valeur interpolée dans le tableau
        """

        if nb <= 2 :
            return VoncUtils.InterpoleLineaire(pts, nb, t)

        nbU = nb - 1
        nbT = nb - 3
        nbU2Inv = 1. / (nbU * 2)

        tf = (t - nbU2Inv) * nbU

        i0 = int(tf)
        i1 = i0 + 1
        i2 = i1 + 1

        if nb == 3 :  # 3 points
            f = t
            p0 = pts[0]
            p1 = pts[1]
            p2 = pts[2]

        elif i0 == 0 :  # Début
            f = (tf + 0.5) / 1.5
            p0 = pts[i0]
            p1 = pts[i1]
            p2 = (pts[i1] + pts[i2]) * .5

        elif i0 >= nbT :  # Fin
            f = (tf - nbT) / 1.5
            i0 = nbT
            i1 = i0 + 1
            i2 = i1 + 1
            p0 = (pts[i0] + pts[i1]) * .5
            p1 = pts[i1]
            p2 = pts[i2]

        else :  # Milieu
            f = tf - i0
            p0 = (pts[i0] + pts[i1]) * .5
            p1 = pts[i1]
            p2 = (pts[i1] + pts[i2]) * .5

        return VoncUtils.CourbeBezierQuadratique(p0, p1, p2, f)


    @staticmethod
    def CourbeBezierQuadratique(p0, p1, p2, t) :

        """
            Courbe de Bézier Quadratique

            Paramètres :
                p0 (Vector ou float) - Premier point
                p1 (Vector ou float) - Deuxième point
                p2 (Vector ou float) - Troisième point
                t (float) - Facteur d'interpolation entre 0.0 et 1.0

            Renvoie :
                (Vector ou float) - Le point sur la courbe
        """

        t1 = 1.0 - t
        return t1 * t1 * p0 + 2.0 * t * t1 * p1 + t * t * p2


    @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()

        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

        for i in xrange(nbPts) :

            bruit[i] = noise(pts[i] * echelle, temps) * intensite

        return bruit


    @staticmethod
    def SubdiviseLissePoints(pts, nbPts, ferme) :

        """
            Subdivise et lisse une liste de points qui se suivent.

            Paramètres :
                pts (liste de Vector) - Liste des points à subdiviser
                nbPts (int) - Nombre de points de la liste
                ferme (bool) - Définit si la liste de points forme une boucle

            Renvoie :
                (liste de Vector) - Nouvelle liste de points subdivisés et lissés
                (int) - Nombre de points de la nouvelle liste
        """

        nbPtsNouv = nbPts * 2
        if not ferme : nbPtsNouv -= 1

        ptsNouv = [c4d.Vector()] * nbPtsNouv

        if nbPts == 0 :
            return [], 0

        if nbPts == 1 :
            return [pts[0]], 1

        j = 0
        suiv = c4d.Vector()
        pos = c4d.Vector()
        nouv = c4d.Vector()
        mil = c4d.Vector()
        milPrec = c4d.Vector()
        nbPtsMU = nbPts - 1

        # i = 0
        pos = pts[0]
        suiv = pts[1]
        mil = (pos + suiv) * 0.5
        ptsNouv[j] = pos + 0.0 ; j += 1
        ptsNouv[j] = mil ; j += 1
        milPrec = mil

        # 1 à nbPts-2
        for i in xrange(1, nbPtsMU) :

            pos = pts[i]
            suiv = pts[i + 1]
            mil = (pos + suiv) * 0.5
            nouv = ((mil + milPrec) * 0.5 + pos) * 0.5

            ptsNouv[j] = nouv ; j += 1
            ptsNouv[j] = mil ; j += 1

            milPrec = mil

        # i = nbPts-1
        pos = pts[nbPtsMU]
        if not ferme :
            ptsNouv[j] = pos + 0.0 ; j += 1
        else :
            suiv = pts[0]
            mil = (pos + suiv) * 0.5
            nouv = ((mil + milPrec) * 0.5 + pos) * 0.5
            ptsNouv[j] = nouv ; j += 1
            ptsNouv[j] = mil ; j += 1

            milPrec = mil
            mil = ptsNouv[1]
            nouv = ((mil + milPrec) * 0.5 + pts[0]) * 0.5
            ptsNouv[0] = nouv

        return ptsNouv, nbPtsNouv


    @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

            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)

        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

            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

                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]

                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

        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()