Fonctions utilitaires applicables sur un modèle 3D
@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 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 CalculeGroupesParPolyPhong(obj, n = None, angle = None):
"""
Calcule pour chaque poly l'ID du groupe de polygones auquel il appartient, en séparant par les arêtes non lissées
Paramètres :
obj (PolygonObject) - Objet
n (Neighbor) - Neighbor initialisé de l'objet
angle (float) - Angle limite, None pour utiliser le lissage Phong
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
if not n:
n = c4d.utils.Neighbor()
n.Init(obj)
def GetPhongTag(o):
if not o:
return None
tags = o.GetTags()
for tag in tags:
if tag.GetType() == c4d.Tphong:
return tag
angleLimite = 0.0
utiliserEdgeBreak = True
if angle is not None:
angleLimite = angle
else:
phongTag = GetPhongTag(obj)
if phongTag:
angleLimite = phongTag[c4d.PHONGTAG_PHONG_ANGLE] if phongTag[c4d.PHONGTAG_PHONG_ANGLELIMIT] else 3.141592653589793
utiliserEdgeBreak = phongTag[c4d.PHONGTAG_PHONG_USEEDGES]
aretesPhongBS = obj.GetShadingBreak(True, utiliserEdgeBreak, True, angleLimite, angleLimite)
aretesPhong = aretesPhongBS.GetAll(nbPolys * 4)
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]
p4 = p * 4
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 = n.GetNeighbor(poly.c, poly.d, p) if poly.c != poly.d else c4d.NOTOK
if voisA != c4d.NOTOK and polysGroupe[voisA] == -1 and not aretesPhong[p4]:
polysATraiterSuivant.append(voisA)
if voisB != c4d.NOTOK and polysGroupe[voisB] == -1 and not aretesPhong[p4 + 1]:
polysATraiterSuivant.append(voisB)
if voisC != c4d.NOTOK and polysGroupe[voisC] == -1 and not aretesPhong[p4 + 2]:
polysATraiterSuivant.append(voisC)
if voisD != c4d.NOTOK and polysGroupe[voisD] == -1 and not aretesPhong[p4 + 3]:
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 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 IntersectionSplines(objs, precision=0.001, axe='y'):
"""
Ajoute des points d'intersections à des splines 2D.
Paramètres :
objs (liste de SplineObject) - Liste des splines à modifier
precision (float) - Précision (ou arrondi) du calcul des intersections
axe (string) - Axe de profondeur (x, y, ou z)
"""
aretes = []
aretesAModifier = []
points = []
mgis = []
# Aretes
for s, spline in enumerate(objs):
mg = spline.GetMg()
mgis.append(~mg)
nbPoints = spline.GetPointCount()
pts = [mg * x for x in spline.GetAllPoints()]
points.append(pts)
segments = spline.GetSegmentCount()
getSegment = spline.GetSegment
if 0 == segments:
segments = 1
getSegment = lambda i: {'closed': spline[c4d.SPLINEOBJECT_CLOSED] and nbPoints > 2, 'cnt': nbPoints}
j = 0
for i in range(segments):
segment = getSegment(i)
cnt = segment['cnt']
closed = (segment['closed'] or spline[c4d.SPLINEOBJECT_CLOSED]) and cnt > 2
if cnt == 1:
j += 1
continue
for k in range(cnt):
if k == 0:
continue
b = k + j
a = b - 1
aretes.append((s, a, b, i, False, k == cnt - 1))
aretesAModifier.append([])
if closed:
a = j
b = j + cnt - 1
aretes.append((s, a, b, i, True, False))
aretesAModifier.append([])
j += cnt
# Intersections
def estLoinDe(x, pa, pb, qa, qb, arr):
return ((x - pa).GetLengthSquared() > arr and
(x - pb).GetLengthSquared() > arr and
(x - qa).GetLengthSquared() > arr and
(x - qb).GetLengthSquared() > arr)
def estLoinDe2(x, pa, pb, arr):
return ((x - pa).GetLengthSquared() > arr and
(x - pb).GetLengthSquared() > arr)
def point3dVers2d(v):
if axe == 'y':
return c4d.Vector(v.x, v.z, 0.0)
elif axe == 'x':
return c4d.Vector(v.y, v.z, 0.0)
return c4d.Vector(v.x, v.y, 0.0)
def point2dVers3d(v):
if axe == 'y':
return c4d.Vector(v.x, 0.0, v.y)
elif axe == 'x':
return c4d.Vector(0.0, v.x, v.y)
return c4d.Vector(v.x, v.y, 0.0)
imax = len(aretes)
arrondi = precision
arrondi2 = precision * precision
PointLineSegmentDistance2D = c4d.utils.PointLineSegmentDistance2D
for i in range(imax):
arete = aretes[i]
spline = objs[arete[0]]
mgi = mgis[arete[0]]
pts = points[arete[0]]
pa = pts[arete[1]]
pb = pts[arete[2]]
for j in range(i + 1, imax):
arete2 = aretes[j]
spline2 = objs[arete2[0]]
mgi2 = mgis[arete2[0]]
pts2 = points[arete2[0]]
qa = pts2[arete2[1]]
qb = pts2[arete2[2]]
if pa == qa or pa == qb or pb == qa or pb == qb:
continue
if not estLoinDe2(pa, qa, qb, arrondi2) or not estLoinDe2(pb, qa, qb, arrondi2):
continue
if not estLoinDe2(qa, pa, pb, arrondi2) or not estLoinDe2(qb, pa, pb, arrondi2):
continue
pa2d = point3dVers2d(pa)
pb2d = point3dVers2d(pb)
qa2d = point3dVers2d(qa)
qb2d = point3dVers2d(qb)
intersection = IntersectionSegments2DVector(pa2d, pb2d, qa2d, qb2d)
if intersection is not None:
if arrondi < 0.001 or estLoinDe(intersection, pa, pb, qa, qb, arrondi2):
intersection = point2dVers3d(intersection)
aretesAModifier[i].append(mgi * intersection)
aretesAModifier[j].append(mgi2 * intersection)
if arrondi < 0.001:
continue
pointProche = PointLineSegmentDistance2D(pa2d, pb2d, qa2d)
if pointProche[0] < arrondi:
pt = point2dVers3d(pointProche[1])
spline2.SetPoint(arete2[1], mgi2 * pt)
aretesAModifier[i].append(mgi * pt)
pointProche = PointLineSegmentDistance2D(pa2d, pb2d, qb2d)
if pointProche[0] < arrondi:
pt = point2dVers3d(pointProche[1])
spline2.SetPoint(arete2[2], mgi2 * pt)
aretesAModifier[i].append(mgi * pt)
pointProche = PointLineSegmentDistance2D(qa2d, qb2d, pa2d)
if pointProche[0] < arrondi:
pt = point2dVers3d(pointProche[1])
spline.SetPoint(arete[1], mgi * pt)
aretesAModifier[j].append(mgi2 * pt)
pointProche = PointLineSegmentDistance2D(qa2d, qb2d, pb2d)
if pointProche[0] < arrondi:
pt = point2dVers3d(pointProche[1])
spline.SetPoint(arete[2], mgi * pt)
aretesAModifier[j].append(mgi2 * pt)
# Ajout des nouveaux points
def pointsToDic(_pts, precision):
dic = {}
for p in _pts:
dic[(int(round(p.x / precision)), int(round(p.z / precision)))] = p
return dic
spline = None
splinePrec = None
segmentPrec = None
nouveauxPoints = []
nouveauxSegments = [0]
def finaliser(spline, nouveauxSegments, nouveauxPoints):
nbPts = len(nouveauxPoints)
spline.ResizeObject(nbPts, len(nouveauxSegments))
for i, segmentNbPts in enumerate(nouveauxSegments):
spline.SetSegment(i, segmentNbPts, False)
spline.SetAllPoints(nouveauxPoints)
spline.Message(c4d.MSG_UPDATE)
for i, pointsEnPlus in enumerate(aretesAModifier):
arete = aretes[i]
spline = objs[arete[0]]
if splinePrec is None: splinePrec = spline
segment = arete[3]
if segmentPrec is None: segmentPrec = segment
areteDeFermeture = arete[4]
derniereArete = arete[5]
a = arete[1]
b = arete[2]
if splinePrec != spline:
finaliser(splinePrec, nouveauxSegments, nouveauxPoints)
nouveauxPoints = []
nouveauxSegments = [0]
elif segmentPrec != segment:
nouveauxSegments.append(0)
if not areteDeFermeture:
prem = spline.GetPoint(a)
pointsEnPlus.append(prem)
if derniereArete:
pointsEnPlus.append(spline.GetPoint(b))
# Supprime les doublons
pointsEnPlus = list(pointsToDic(pointsEnPlus, precision).values())
# Tri par position par rapport au premier point
pointsEnPlus.sort(key=lambda x: (prem - x).GetLengthSquared())
for pt in pointsEnPlus:
nouveauxPoints.append(pt)
nouveauxSegments[-1] += 1
else:
# Tri par position par rapport au premier point
pointsEnPlus.sort(key=lambda x: -(prem - x).GetLengthSquared())
for pt in pointsEnPlus:
nouveauxPoints.append(pt)
nouveauxSegments[-1] += 1
splinePrec = spline
segmentPrec = segment
finaliser(spline, nouveauxSegments, nouveauxPoints)
@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 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 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 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 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 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 QuadrangleEstConvexe(a, b, c, d) :
"""
Détermine si un quadrangle est convexe ou concave.
Paramètres :
a (Vector) - Point A
b (Vector) - Point B
c (Vector) - Point C
d (Vector) - Point D
Renvoie :
(bool) - True si le quadrangle est convexe
"""
nabc = (a - c).Cross(b - c)
nacd = (a - d).Cross(c - d)
if nabc.Dot(nacd) < 0 : return False
nabd = (a - d).Cross(b - d)
ndbc = (d - c).Cross(b - c)
if nabd.Dot(ndbc) < 0 : return False
return True
@staticmethod
def CalculeCercleDansTriangleIsocele(a, b, c):
"""
Calcule le cercle passant par B et C dans un triangle isocèle en A
Paramètres :
a (Vector) - Point A
b (Vector) - Point B
c (Vector) - Point C
Renvoie :
(Vector) - Position du cercle
(float) - Rayon du cercle
"""
c = (b + c) * 0.5
acN = (c - a).GetNormalized()
bc2 = (c - b).GetLengthSquared()
ac = (c - a).GetLength()
if ac == 0.0: return c, 0.0
cd = bc2 / ac
d = c + acN * cd
return d, (d - b).GetLength()
@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. :
if rxs == 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
nb (int) - Taille du tableau
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
nb (int) - Taille du tableau
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 Vector ou de float) - Liste des points de contrôles
nb (int) - Taille du tableau
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 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 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()
return True
@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)
keyboard_arrow_down