Code Vonc

Utilitaires

CommentairesComments


Classe Utilitaires




class VoncUtils() :
    
    """
        Auteur : César Vonc
        http://code.vonc.fr
    """
    
    @staticmethod
    def CalculeNormalesPolys(obj) :
    
        """Calcule les normales des polys de l'objet."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPolys = obj.GetPolygonCount()
        
        norPolys = [c4d.Vector()] * nbPolys
        nor = c4d.Vector()
        
        for i, poly in enumerate(polys) :
            nor = (pts[poly.a] - pts[poly.c]).Cross(pts[poly.b] - pts[poly.d])
            nor.Normalize()
            norPolys[i] = nor
        
        return norPolys
    
    
    @staticmethod
    def CalculeNormalesPointsPolys(obj) :
    
        """Calcule les normales des points et des polys de l'objet."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPoints = obj.GetPointCount()
        nbPolys = obj.GetPolygonCount()
        
        norPts = [c4d.Vector()] * nbPoints
        norPolys = [c4d.Vector()] * nbPolys
        
        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
        
        for i in xrange(nbPoints) :
            norPts[i].Normalize()
        
        return norPts, norPolys
    
    
    @staticmethod
    def CalculeCentrePolys(obj) :
        
        """Calcule le centre des polys de l'objet."""
        
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        nbPolys = obj.GetPolygonCount()
        
        centrePolys = [c4d.Vector()] * nbPolys
        centre = c4d.Vector()
        
        for i, poly in enumerate(polys) :
            if poly.c != poly.d :
                centre = (pts[poly.a] + pts[poly.b] + pts[poly.c] + pts[poly.d]) / 4.
            else :
                centre = (pts[poly.a] + pts[poly.b] + pts[poly.c]) / 3.
            centrePolys[i] = centre
        
        return centrePolys
    
    
    @staticmethod
    def CalculeOrthocentreTriangles(obj) :
        
        """Calcule l'orthocentre des triangles de l'objet, à supposer que tous les polys soient des triangles."""
        
        nbTriangles = obj.GetPolygonCount()
        polys = obj.GetAllPolygons()
        pts = obj.GetAllPoints()
        
        orthoPolys = [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(poly, p0, p1) :
    
        """Renvoie l'arête opposée à deux points, ordonnée de façon symétrique aux points en paramètre."""
        
        arete = poly.FindEdge(p0, p1)
        if arete == c4d.NOTOK :
            return (-1, -1)
        
        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 RefleteVecteur(v, n) :
    
        """Reflète un vecteur par une normale."""
        
        return (2. * (v.Dot(n)) * n) - v
    
    
    @staticmethod
    def TransposeMatrice(m) :
    
        """Transpose une matrice."""
        
        v1 = c4d.Vector(m.v1.x, m.v2.x, m.v3.x)
        v2 = c4d.Vector(m.v1.y, m.v2.y, m.v3.y)
        v3 = c4d.Vector(m.v1.z, m.v2.z, m.v3.z)
        
        m.v1 = v1
        m.v2 = v2
        m.v3 = v3
        m.off = c4d.Vector()
        
        return m
    
    
    @staticmethod
    def IntersectionSegments2DBool(p, p2, q, q2) :
        
        """Détermine si deux segments 2D se croisent."""
        
        def ccw(p, p2, q) :
            return (q.y - p.y) * (p2.x - p.x) > (p2.y - p.y) * (q.x - p.x)
        
        return ccw(p, q, q2) != ccw(p2, q, q2) and ccw(p, p2, q) != ccw(p, p2, q2)
    
    
    @staticmethod
    def IntersectionSegments2DVector(p, p2, q, q2) :
        
        """Calcule le point d'intersection entre deux segments 2D."""
        
        def CrossScal(a, v) :
            return a.x * v.y - a.y * v.x
        
        r = p2 - p
        s = q2 - q
        rxs = CrossScal(r, s)
        qpxr = CrossScal((q - p), r)
    
        if rxs == 0. :
            return None
    
        t = CrossScal((q - p), s) / rxs
        u = CrossScal((q - p), r) / rxs
    
        if (0 <= t and t <= 1) and (0 <= u and u <= 1) :
            return p + t*r
    
        return None
    
    
    @staticmethod
    def IntersectionDroites2DBool(p1, p2, q1, q2) :
        
        """Détermine si deux droites 2D se croisent."""
        
        pa = p1.y - p2.y
        pb = p2.x - p1.x
        
        qa = q1.y - q2.y
        qb = q2.x - q1.x
        
        d  = pa * qb - pb * qa
        
        return d != 0.
    
    
    @staticmethod
    def IntersectionDroites2DVector(p1, p2, q1, q2) :
        
        """Calcule le point d'intersection entre deux droites 2D."""
        
        pa = p1.y - p2.y
        pb = p2.x - p1.x
        pc =  p2.x * p1.y - p1.x * p2.y
        
        qa = q1.y - q2.y
        qb = q2.x - q1.x
        qc = q2.x * q1.y - q1.x * q2.y
        
        d  = pa * qb - pb * qa
        dx = pc * qb - pb * qc
        dy = pa * qc - pc * qa
        
        if d != 0. :
            return c4d.Vector(dx / d, dy / d, 0.)
        
        else :
            return None
        
    
    @staticmethod
    def OrthocentreTriangle(a, b, c) :
        
        """Calcule l'orthocentre d'un triangle 3D."""
        
        mab = (a + b) * .5
        mbc = (b + c) * .5
        
        n = (a - c).Cross(b - c)
        
        hab = (b - a).Cross(n)
        hbc = (b - c).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) :
        
        """Calcule le nombre de polygones autour de chaque point."""
        
        polys = obj.GetAllPolygons()
        nbPts = obj.GetPointCount()
        
        nbPolysParPts = [0] * nbPts
        
        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
        
        return nbPolysParPts
    
    
    @staticmethod
    def CalculePointsBordureListe(obj) :
        
        """Calcule la liste des points qui constituent la bordure de l'objet."""
        
        polys = obj.GetAllPolygons()
        
        ptsBordure = []
        
        liaisonPts = VoncUtils.CalculeLiaisonPoints(obj)
        nbPolysParPts = VoncUtils.CalculeNbPolysParPoint(obj)
        
        for i, npol in enumerate(nbPolysParPts) :
            
            npt = len(liaisonPts[i])
            if (npt != npol) :
                ptsBordure.append(i)
        
        return ptsBordure
    
    
    @staticmethod
    def CalculePointsBordureSelect(obj) :
        
        """Calcule la sélection des points qui constituent la bordure de l'objet."""
        
        polys = obj.GetAllPolygons()
        nbPts = obj.GetPointCount()
        
        ptsBordure = [False] * nbPts
        
        liaisonPts = VoncUtils.CalculeLiaisonPoints(obj)
        nbPolysParPts = VoncUtils.CalculeNbPolysParPoint(obj)
        
        for i, npol in enumerate(nbPolysParPts) :
            
            npt = len(liaisonPts[i])
            if (npt != npol) :
                ptsBordure[i] = True
        
        return ptsBordure
    
    
    @staticmethod
    def CalculePointsBordureNeighborSelect(obj, bs, n = None) :
        
        """Calcule la sélection des points qui constituent la bordure de la sélection du BaseSelect."""
        
        nbPolys = obj.GetPolygonCount()
        nbPts = obj.GetPointCount()
        polys = obj.GetAllPolygons()
        
        if n is None :
            n = c4d.utils.Neighbor()
            n.Init(obj, bs)
        
        ptsBords = [False] * nbPts
        
        for i, sel in enumerate(bs.GetAll(nbPolys)) :
        
            if sel :
            
                poly = polys[i]
            
                vois = n.GetNeighbor(poly.a, poly.b, i)
                if vois == c4d.NOTOK :
                    ptsBords[poly.a] = True
                    ptsBords[poly.b] = True
                
                vois = n.GetNeighbor(poly.b, poly.c, i)
                if vois == c4d.NOTOK :
                    ptsBords[poly.b] = True
                    ptsBords[poly.c] = True
                
                vois = n.GetNeighbor(poly.d, poly.a, i)
                if vois == c4d.NOTOK :
                    ptsBords[poly.d] = True
                    ptsBords[poly.a] = True
                
                if poly.c != poly.d :
                    vois = n.GetNeighbor(poly.c, poly.d, i)
                    if vois == c4d.NOTOK :
                        ptsBords[poly.c] = True
                        ptsBords[poly.d] = True
        
        return ptsBords
        
    
    @staticmethod
    def CalculeAretes(obj) :
        
        """Calcule la liste des arêtes de l'objet."""
        
        aretes = []
        aretesDico = {}
            
        polys = obj.GetAllPolygons()
        
        for pol in polys :
            
            areAB = (pol.a, pol.b)
            if (pol.b < pol.a) : areAB = (pol.b, pol.a)
            aretesDico[areAB] = True
            
            areBC = (pol.b, pol.c)
            if (pol.c < pol.b) : areBC = (pol.c, pol.b)
            aretesDico[areBC] = True
            
            if pol.c == pol.d :
                areCA = (pol.c, pol.a)
                if (pol.a < pol.c) : areCA = (pol.a, pol.c)
                aretesDico[areCA] = True
                
            else :
                areCD = (pol.c, pol.d)
                if (pol.d < pol.c) : areCD = (pol.d, pol.c)
                aretesDico[areCD] = True
                
                areDA = (pol.d, pol.a)
                if (pol.a < pol.d) : areDA = (pol.a, pol.d)
                aretesDico[areDA] = True
        
        aretes = aretesDico.keys()
        return aretes
    
    
    @staticmethod
    def CalculeAretesBordure(obj) :
        
        """Calcule la liste des arêtes de bordure de l'objet."""
        
        aretes = []
        
        polys = obj.GetAllPolygons()
        n = c4d.utils.Neighbor()
        n.Init(obj)
        
        for i, pol in enumerate(polys) :
            
            nab = n.GetNeighbor(pol.a, pol.b, i)
            nbc = n.GetNeighbor(pol.b, pol.c, i)
            nda = n.GetNeighbor(pol.d, pol.a, i)
            
            if nab == c4d.NOTOK :
                are = (pol.a, pol.b) if pol.a < pol.b else (pol.b, pol.a)
                aretes.append(are)
            
            if nbc == c4d.NOTOK :
                are = (pol.b, pol.c) if pol.b < pol.c else (pol.c, pol.b)
                aretes.append(are)
            
            if nda == c4d.NOTOK :
                are = (pol.d, pol.a) if pol.d < pol.a else (pol.a, pol.d)
                aretes.append(are)
                
            if pol.c != pol.d :
            
                ncd = n.GetNeighbor(pol.c, pol.d, i)
                
                if ncd == c4d.NOTOK :
                    are = (pol.c, pol.d) if pol.c < pol.d else (pol.d, pol.c)
                    aretes.append(are)
                
        return aretes
    
    
    @staticmethod
    def CalculeAretesBordureAvecPolys(obj) :
        
        """Calcule la liste des arêtes de bordure de l'objet, avec l'id du poly pour chaque arête."""
        
        aretes = []
        
        polys = obj.GetAllPolygons()
        n = c4d.utils.Neighbor()
        n.Init(obj)
        
        for i, pol in enumerate(polys) :
            
            nab = n.GetNeighbor(pol.a, pol.b, i)
            nbc = n.GetNeighbor(pol.b, pol.c, i)
            nda = n.GetNeighbor(pol.d, pol.a, i)
            
            if nab == c4d.NOTOK :
                are = (pol.a, pol.b, 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)
                
        return aretes
    
    
    @staticmethod
    def CalculePolysBordure(obj) :
        
        """Calcule les polys qui déterminent la bordure de l'objet."""
        
        polysBord = [False] * obj.GetPolygonCount()
        
        polys = obj.GetAllPolygons()
        n = c4d.utils.Neighbor()
        n.Init(obj)
        
        for i, pol in enumerate(polys) :
            
            nab = n.GetNeighbor(pol.a, pol.b, i)
            nbc = n.GetNeighbor(pol.b, pol.c, i)
            nda = n.GetNeighbor(pol.d, pol.a, i)
            
            if nab == c4d.NOTOK :
                polysBord[i] = True
            
            if nbc == c4d.NOTOK :
                polysBord[i] = True
            
            if nda == c4d.NOTOK :
                polysBord[i] = True
                
            if pol.c != pol.d :
            
                ncd = n.GetNeighbor(pol.c, pol.d, i)
                    
                if ncd == c4d.NOTOK :
                    polysBord[i] = True
                
        return polysBord
    
    
    @staticmethod
    def CalculePointPolysTries(obj, nei = None) :
        
        """Calcule les polygones attachés à chaque point, triés par sens horaire."""
        
        if nei is None :
            nei = c4d.utils.Neighbor()
            nei.Init(obj)
        
        nbPts = obj.GetPointCount()
        polys = obj.GetAllPolygons()
        pts = op.GetAllPoints()
        
        pointsPolys = [None] * nbPts
        
        for i in xrange(nbPts) :
            
            polsDuPt = nei.GetPointPolys(i)
            
            if not polsDuPt :
                pointsPolys[i] = []
                continue
            
            nbVois = len(polsDuPt)
            polsDuPtTrie = []
            
            pol = polsDuPt[0]
            polDeb = pol
            arete = None
            inverser = False
            autreAreteDebut = False
            
            k = 0
            while k < nbVois + 1 :
                
                polsDuPtTrie.append(pol)
                
                poly = polys[pol]
                
                # icentre est le point i, iexts sont les deux autres points du poly, situés après et avant icentre (par ordre abcd)
                icentre = poly.a
                iexts = (poly.b, poly.d) # a, suivant : b, précédent : d (c = d pour triangle)
                if poly.b == i :
                    icentre = poly.b
                    iexts = (poly.c, poly.a) # b, suivant : c, précédent : a
                elif poly.c == i :
                    icentre = poly.c
                    if poly.c != poly.d :
                        iexts = (poly.d, poly.b) # (pour quadrangle) c, suivant : d, précédent : b
                    else :
                        iexts = (poly.a, poly.b) # (pour triangle) c, suivant : a, précédent : b
                elif poly.d == i :
                    icentre = poly.d
                    iexts = (poly.a, poly.c) # (pour quadrangle exclusivement) b, suivant : a, précédent : c
                
                if k == 0 or autreAreteDebut :
                    # Pour le premier poly, prendre l'arête du point qui suit l'ordre abcd du poly (nécessite les normales alignées)
                    arete = (icentre, iexts[0])
                    if autreAreteDebut :
                        arete = (icentre, iexts[1])
                    
                else :
                    # Prendre une arête du poly qui ne soit pas celle précédemment choisie
                    arete = (icentre, iexts[0]) if iexts[0] != arete[1] else (icentre, iexts[1])
                
                prem, sec = nei.GetEdgePolys(arete[0], arete[1])
                
                # Trouver le voisin qui ne soit pas égal au premier poly ni au poly précédemment ajouté
                if prem != c4d.NOTOK or sec != c4d.NOTOK :
                    
                    if prem != polDeb and prem != pol and prem != c4d.NOTOK :
                        
                        pol = prem
                        
                    elif sec != polDeb and sec != pol and sec != c4d.NOTOK :
                        
                        pol = sec
                    
                    else :
                        if k == 0 :
                            # Dans le cas du premier poly ajouté, si le sens choisi donne sur un bord, parcourir dans l'autre sens puis inverser le résultat et supprimer le doublon du premier poly.
                            pol = pol
                            polsDuPtTrie.pop()
                            inverser = True
                        else :
                            if len(polsDuPtTrie) != nbVois :
                                # S'il manque des polys, c'est qu'on a commencé par une arête d'un poly sans arête de bordure, et qu'on est tombé sur une bordure prématurément
                                # Dans ce cas, partir dans l'autre sens à partir du poly de départ, mais avec l'autre arête.
                                # Inverser le tableau actuel pour que l'ajout se fasse dans le bon ordre, puis réinverser le tout.
                                polsDuPtTrie.reverse()
                                _deb = polsDuPtTrie.pop()
                                inverser = True
                                autreAreteDebut = True
                                pol = polDeb
                                polDeb = _deb
                            else :
                                break
                    
                else :
                    break
                
                k += 1
            
            if not inverser :
                polsDuPtTrie.reverse()
            
            pointsPolys[i] = polsDuPtTrie
            
        return pointsPolys
    
    
    @staticmethod
    def CalculeLiaisonPoints(obj) :
        
        """Calcule la liste des points voisins de chaque point."""
        
        nbPts = obj.GetPointCount()
        polys = obj.GetAllPolygons()
        liaisonPts = [None] * nbPts
        
        for i in xrange(nbPts) :
            liaisonPts[i] = set()
        
        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)
        
        
        for i in xrange(nbPts) :
            liaisonPts[i] = tuple(liaisonPts[i])
        
        return liaisonPts
        
    
    @staticmethod
    def CalculeGroupesParPoly(obj):
    
        """
            Calcule pour chaque poly l'ID du groupe de polygones auquel il appartient
            
            Renvoie :
                polysGroupe : Liste d'ID de groupe pour chaque polygone
                nbGroupes : Nombre de groupes
        """
        
        if not obj : return None, None
        
        nbPolys = obj.GetPolygonCount()
        polys = obj.GetAllPolygons()
        
        polysGroupe = [-1] * nbPolys
        nbGroupes = 0
        
        n = c4d.utils.Neighbor()
        n.Init(obj)
        
        for i, g in enumerate(polysGroupe) :
            
            if g != -1 : continue
            
            polysATraiter = [i]
            
            while(polysATraiter) :
            
                polysATraiterSuivant = []
                
                for p in polysATraiter :
                    
                    polysGroupe[p] = nbGroupes
                    
                for p in polysATraiter :
                    
                    poly = polys[p]
                    
                    voisA = n.GetNeighbor(poly.a, poly.b, p)
                    voisB = n.GetNeighbor(poly.b, poly.c, p)
                    voisD = n.GetNeighbor(poly.d, poly.a, p)
                    voisC = c4d.NOTOK
                    
                    if poly.c != poly.d :
                        voisC = n.GetNeighbor(poly.c, poly.d, p)
                    
                    if voisA != c4d.NOTOK and polysGroupe[voisA] == -1 :
                        polysATraiterSuivant.append(voisA)
                    
                    if voisB != c4d.NOTOK and polysGroupe[voisB] == -1 :
                        polysATraiterSuivant.append(voisB)
                    
                    if voisC != c4d.NOTOK and polysGroupe[voisC] == -1 :
                        polysATraiterSuivant.append(voisC)
                    
                    if voisD != c4d.NOTOK and polysGroupe[voisD] == -1 :
                        polysATraiterSuivant.append(voisD)
                
                polysATraiter = list(set(polysATraiterSuivant))
            
            nbGroupes += 1
        
        return polysGroupe, nbGroupes
    
    
    @staticmethod
    def LissageCatmullClark(obj, intensite = 1.0, influence = None, nei = None) :
        
        """
            Lisse les points de l'objet basé sur la méthode Catmull-Clark.
            
            intensite : Facteur d'intensité du lissage (0.0 à 1.0).
            influence : Facteur d'intensité du lissage pour chaque point.
            nei : Neighbor initialisé de l'objet.
        """
        
        if nei is None :
            nei = c4d.utils.Neighbor()
            nei.Init(obj)
        
        nbPts = obj.GetPointCount()
        pts = obj.GetAllPoints()
        
        if nbPts == 0 : return
        
        ptsNouv = [c4d.Vector()] * nbPts
        
        liaisonPts = VoncUtils.CalculeLiaisonPoints(obj)
        centrePolys = VoncUtils.CalculeCentrePolys(obj)
        
        
        for i in xrange(nbPts) :
            
            polsVois = nei.GetPointPolys(i) # Polys autour du point
            nbPolsVois = len(polsVois)
            
            ptsVois = liaisonPts[i] # Points liés au point i
            nbPtsVois = len(ptsVois)
            
            if not nbPolsVois or not nbPtsVois :
                ptsNouv[i] = pts[i]
                continue
            
            pmaBord = [] # Point de Milieu d'Arête de Bordure
            po = pts[i] # Point Original
            pos = c4d.Vector() # Nouvelle position
            
            if nbPtsVois != nbPolsVois : # Bordure
                
                if nbPtsVois != nbPolsVois + 1 : # Point avec plus de 2 bordures
                    
                    pos = po
                    
                else :
                    
                    pmaMoy = c4d.Vector()
                    nbBord = 0
                    
                    for j in ptsVois :
                        
                        prem, sec = nei.GetEdgePolys(i, j)
                        
                        if prem == c4d.NOTOK or sec == c4d.NOTOK :
                            pmaMoy += (po + pts[j]) * .5
                            nbBord += 1
                    
                    pmaMoy /= float(nbBord)
                    
                    pos = (po + pmaMoy) * .5
                  
            else :
                
                nbPolsVois = float(nbPolsVois)
                nbPtsVois = float(nbPtsVois)
                
                # Moyenne des Point de Surface
                psMoy = c4d.Vector()
                for j in polsVois :
                    psMoy += centrePolys[j]
                psMoy /= nbPolsVois
                
                # Moyenne des Points d'Arête
                pmaMoy = c4d.Vector()
                for j in ptsVois :
                    pmaMoy += (po + pts[j]) * .5
                pmaMoy /= nbPtsVois
                
                # Catmull-Clark
                pos = psMoy + (2.0 * pmaMoy) + ((nbPolsVois - 3.0) * po);
                pos /= nbPolsVois
                
            
            ptsNouv[i] = pos
            
            
        for i, ptNouv in enumerate(ptsNouv) :
            
            if influence :
                pts[i] = ptNouv * intensite * influence[i] + (pts[i] * (1. - intensite * influence[i]))
            else :
                pts[i] = ptNouv * intensite + (pts[i] * (1. - intensite))
        
        
        obj.SetAllPoints(pts)
        obj.Message(c4d.MSG_UPDATE)
        
    
    @staticmethod
    def RecopieObjet(src, dest) :
    
        """Recopie les données polygonales d'un objet vers un autre"""
        
        nouvNbPts = src.GetPointCount()
        nouvNbPolys = src.GetPolygonCount()
        nouvPts = src.GetAllPoints()
        nouvPolys = src.GetAllPolygons()
        
        if nouvNbPts == 0 :
            return False
        
        dest.ResizeObject(nouvNbPts, nouvNbPolys)
        dest.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."""
        
        params = c4d.BaseContainer()
        params[c4d.MDATA_OPTIMIZE_TOLERANCE] = tolerance
        params[c4d.MDATA_OPTIMIZE_POINTS] = pts
        params[c4d.MDATA_OPTIMIZE_POLYGONS] = polys
        params[c4d.MDATA_OPTIMIZE_UNUSEDPOINTS] = ptsiso
        c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_OPTIMIZE, list=[obj], bc=params, doc=doc)
        
    
    @staticmethod
    def AlignerNormales(obj, doc) :
        
        """Aligner les normales."""
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_ALIGNNORMALS, list=[obj], doc=doc)
    
    
    @staticmethod
    def InverserNormales(obj, doc) :
        
        """Inverser les normales."""
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_REVERSENORMALS, list=[obj], doc=doc)
    
    
    @staticmethod
    def Trianguler(obj, doc) :
        
        """Convertit les polygones en triangles."""
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_TRIANGULATE, list=[obj], doc=doc)
    
    
    @staticmethod
    def Quadranguler(obj, doc) :
        
        """Convertit les triangles en quadrangles."""
        
        bc = c4d.BaseContainer()
        bc[c4d.MDATA_UNTRIANGULATE_NGONS] = False
        bc[c4d.MDATA_UNTRIANGULATE_ANGLE_RAD] = c4d.utils.DegToRad(0.1)
        bc[c4d.MDATA_UNTRIANGULATE_ANGLE] = False
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_UNTRIANGULATE, list=[obj], bc=bc, doc=doc)
    
    
    @staticmethod
    def Subdiviser(obj, doc, iterations = 1, catmullClark = True, selectionActive = False, bs = None) :
        
        """Subdivise les polygones d'un objet."""
        
        bc = c4d.BaseContainer()
        bc[c4d.MDATA_SUBDIVIDE_HYPER] = catmullClark
        bc[c4d.MDATA_SUBDIVIDE_ANGLE] = c4d.utils.DegToRad(180.)
        bc[c4d.MDATA_SUBDIVIDE_SUB] = iterations
        mode = c4d.MODELINGCOMMANDMODE_ALL
        
        if selectionActive :
            mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION
            
        if bs is not None :
            mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION
            bsc = bs.GetClone()
            bs = obj.GetPolygonS()
            bs.DeselectAll()
            bs.Merge(bsc)
        
        return c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_SUBDIVIDE, list=[obj], mode=mode, bc=bc, doc=doc)
    
    
    @staticmethod
    def FermerTrous(obj, doc) :
        
        """Ferme tous les trous d'un objet."""
        
        polys = obj.GetAllPolygons()
        n = c4d.utils.Neighbor()
        n.Init(obj)
        params = c4d.BaseContainer()
        params[c4d.MDATA_CLOSEHOLE_INDEX] = obj
        params[c4d.MDATA_CLOSEHOLE_TRI] = True
        mode = c4d.MODELINGCOMMANDMODE_EDGESELECTION
        commande = c4d.ID_MODELING_CLOSEHOLE_TOOL
        
        SendModelingCommand = c4d.utils.SendModelingCommand
        
        for i, pol in enumerate(polys) :
            
            nab = n.GetNeighbor(pol.a, pol.b, i)
            nbc = n.GetNeighbor(pol.b, pol.c, i)
            ncd = n.GetNeighbor(pol.c, pol.d, i)
            nda = n.GetNeighbor(pol.d, pol.a, i)
            cotes = n.GetPolyInfo(i)["edge"]
            
            if nab == c4d.NOTOK :
                params[c4d.MDATA_CLOSEHOLE_EDGE] = cotes[0]
                SendModelingCommand(command=commande, list=[obj], mode=mode, bc=params, doc=doc)
                n.Init(obj)
                
            if nbc == c4d.NOTOK :
                params[c4d.MDATA_CLOSEHOLE_EDGE] = cotes[1]
                SendModelingCommand(command=commande, list=[obj], mode=mode, bc=params, doc=doc)
                n.Init(obj)
                
            if ((ncd == c4d.NOTOK) and (pol.c != pol.d)) :
                params[c4d.MDATA_CLOSEHOLE_EDGE] = cotes[2]
                SendModelingCommand(command=commande, list=[obj], mode=mode, bc=params, doc=doc)
                n.Init(obj)
                
            if nda == c4d.NOTOK :
                params[c4d.MDATA_CLOSEHOLE_EDGE] = cotes[3]
                SendModelingCommand(command=commande, list=[obj], mode=mode, bc=params, doc=doc)
                n.Init(obj)
        
        obj.Message(c4d.MSG_UPDATE)

        
    @staticmethod
    def BiseauterCommande(obj, doc, rayon = 0., mode = "chanfrein", subdivision = 0, limite = False) :
        
        """Biseaute la sélection d'arêtes courante, en utilisant CallCommand."""
        
        mmode = doc.GetMode()
        doc.SetMode(c4d.Medges)
        c4d.CallCommand(431000015, 431000015)
        
        tool = c4d.plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)
        
        if tool is not None :
            
            tool[c4d.MDATA_BEVEL_LIMIT] = limite
            tool[c4d.MDATA_BEVEL_SUB] = subdivision
            tool[c4d.MDATA_BEVEL_RADIUS] = rayon
            
            tool[c4d.MDATA_BEVEL_MASTER_MODE] = c4d.MDATA_BEVEL_MASTER_MODE_CHAMFER
            
            if mode == "solide" :
                tool[c4d.MDATA_BEVEL_MASTER_MODE] = c4d.MDATA_BEVEL_MASTER_MODE_SOLID
                
            c4d.CallButton(tool, c4d.MDATA_APPLY)
            
        doc.SetMode(mmode)
        
        
    @staticmethod
    def Biseauter(obj, doc, bs, rayon = 0., mode = "chanfrein", subdivision = 0, limite = False) :
        
        """Biseaute la sélection d'arêtes courante, en utilisant le déformateur Biseau. Renvoie l'objet biseauté."""
        
        # Propriété Sélection d'arêtes
        propSelAretes = obj.MakeTag(c4d.Tedgeselection)
        propSelAretes.SetName("SelAreBiseau")
        bsAre = propSelAretes.GetBaseSelect()
        bsAre.Merge(bs)
        
        # Déformateur Biseau
        defBiseau = c4d.BaseObject(431000028)
        defBiseau[c4d.O_BEVEL_MODE_COMPONENT_TYPE] = c4d.O_BEVEL_MODE_COMPONENT_TYPE_EDGE
        defBiseau[c4d.O_BEVEL_RESTRICTION_START] = propSelAretes.GetName()
        if mode == "solide" : defBiseau[c4d.O_BEVEL_MASTER_MODE] = c4d.O_BEVEL_MASTER_MODE_SOLID
        else : defBiseau[c4d.O_BEVEL_MASTER_MODE] = c4d.O_BEVEL_MASTER_MODE_CHAMFER
        defBiseau[c4d.O_BEVEL_RADIUS] = rayon
        defBiseau[c4d.O_BEVEL_SUB] = subdivision
        defBiseau[c4d.O_BEVEL_LIMIT] = limite
        defBiseau.InsertUnder(obj)
        
        res = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[obj], doc=doc)
        
        if res : doc.InsertObject(res[0])
        
        propSelAretes.Remove()
        defBiseau.Remove()
        
        if res : return res[0]
        return None
    
    
    @staticmethod
    def Extruder(obj, doc, decalage, capot = False, subdivision = 0, ngones = False, angle = 3.141592653589793) :
        
        """Extrude les polygones sélectionnés."""
        
        bc = c4d.BaseContainer()
        bc[c4d.MDATA_EXTRUDE_PRESERVEGROUPS] = True
        bc[c4d.MDATA_EXTRUDE_OFFSET        ] = decalage
        bc[c4d.MDATA_EXTRUDE_VARIANCE      ] = 0.0
        bc[c4d.MDATA_EXTRUDE_ANGLE         ] = angle
        bc[c4d.MDATA_EXTRUDE_SUBDIVISION   ] = subdivision
        bc[c4d.MDATA_EXTRUDE_CREATENGONS   ] = ngones
        bc[c4d.MDATA_EXTRUDE_CREATECAPS    ] = capot
        
        return c4d.utils.SendModelingCommand(command = c4d.ID_MODELING_EXTRUDE_TOOL,
                                list = [obj],
                                bc = bc,
                                mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
                                doc = doc)
        
        
    @staticmethod
    def ReductionDePolys(obj, doc, intensite, courbe = False) :
        
        """Applique le déformateur Réduction de polys."""
        
        # Déformateur Biseau
        defReduc = c4d.BaseObject(c4d.Opolyreduction)
        defReduc[c4d.POLYREDUCTIONOBJECT_STRENGTH] = intensite
        defReduc[c4d.POLYREDUCTIONOBJECT_BOUNDARY] = courbe
        defReduc.InsertUnder(obj)
        
        res = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[obj], doc=doc)
        
        if res : doc.InsertObject(res[0])
        
        defReduc.Remove()
        
        if res : return res[0]
        return None
    
    
    @staticmethod
    def SupprimerPolygones(obj, doc) :
        
        """Supprime la sélection de polygones courante."""
        
        c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_DELETE,
                                list = [obj],
                                mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
                                doc = doc)
    
    
    @staticmethod
    def BaseSelectAretesToutes(obj) :
        
        """Renvoie le BaseSelect de toutes les arêtes sélectionnées."""
        
        bs = c4d.BaseSelect()
        nbPolys = obj.GetPolygonCount()
        bs.SelectAll(nbPolys * 4)
        
        return bs
    
    
    @staticmethod
    def BaseSelectPolysDepuisAretes(obj, bsAretes) :
        
        """Renvoie le BaseSelect des polys ayant au moins une arête sélectionnée."""
        
        bs = c4d.BaseSelect()
        nbAretes = obj.GetPolygonCount() * 4
        
        for i, sel in enumerate(bsAretes.GetAll(nbAretes)) :
            if sel :
                bs.Select(int(i / 4))
        
        return bs
    
    
    @staticmethod
    def BaseSelectPolysTous(obj) :
        
        """Renvoie le BaseSelect de tous les polys sélectionnées."""
        
        bs = c4d.BaseSelect()
        nbPolys = obj.GetPolygonCount()
        bs.SelectAll(nbPolys)
        
        return bs
    
    
    @staticmethod
    def SelectionnePolysTous(obj) :
        
        """Sélectionne tous les polygones."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonS()
            bs.SelectAll(obj.GetPolygonCount())
    
    
    @staticmethod
    def SelectionnePolysInverse(obj) :
        
        """Inverse la sélection de polys courante."""
        
        if obj.CheckType(c4d.Opolygon) :
            bs = obj.GetPolygonS()
            bs.ToggleAll(0, obj.GetPolygonCount())
    
    
    @staticmethod
    def SelectionneAretesToutes(obj, doc) :
        
        """Sélectionne toutes les arêtes."""
        
        c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_SELECTALL,
                                list = [obj],
                                mode = c4d.MODELINGCOMMANDMODE_EDGESELECTION,
                                doc = doc)
    
    
    @staticmethod
    def SelectionneAretesInverse(obj, doc) :
        
        """Inverse la sélection d'arêtes courante."""
        
        c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_SELECTINVERSE,
                                list = [obj],
                                mode = c4d.MODELINGCOMMANDMODE_EDGESELECTION,
                                doc = doc)