# Subdivision inverse - César Vonc - code.vonc.fr
# v 1.1
import os
import c4d
import webbrowser
from c4d import plugins, utils, bitmaps, Vector
from c4d.utils import Neighbor, VectorAngle
MODULE_ID = 1029594
VONC_SUBDINV_NOM = 1000
VONC_SUBDINV_TYPE = 1000
VONC_SUBDINV_TYPE_CC1 = 0
VONC_SUBDINV_TYPE_CC2 = 1
VONC_SUBDINV_TYPE_CC3 = 3
VONC_SUBDINV_TYPE_CCA = 2
VONC_SUBDINV_SUBD = 1002
VONC_SUBDINV_DEPA = 1003
VONC_SUBDINV_INFO = 1004
VONC_SUBDINV_SELP = 1005
VONC_SUBDINV_INVN = 1006
VONC_SUBDINV_DON = 1007
VONC_SUBDINV_ACTU = 1008
class SubdivisionInverse() :
obj = None
doc = None
obj_nbpts = 0
obj_nbpol = 0
obj_n = None
pts_traites = []
pol_traites = []
nouv_polys = []
pts_orig = []
nouv_pts = []
nouv_pts_inv = []
nouv_coor = []
pts_corres = []
pol_corres = []
cor_poly_polynet = [] # Correspondance [id nouv_polys] = id nouv_polys nettoyé
cor_polynet_poly = [] # Correspondance [id nouv_polys nettoyé] = id nouv_polys
_GetAllPolygons = []
_GetPointPolys = []
def _NettoieTab(self, tab) :
tab_net = []
j = 0
i = 0
for val in tab :
if val and len(val) > 2 :
tab_net.append(val)
self.cor_poly_polynet.append(j)
self.cor_polynet_poly.append(i)
j += 1
else : self.cor_poly_polynet.append(-1)
i += 1
return tab_net
def _SommeVecteur(self, obj, vecs) :
if not vecs : return Vector()
s = Vector()
for v in vecs :
s += obj.GetPoint(v)
return s
def _MoyenneVecteur(self, obj, vecs) :
q = len(vecs)
if not q : return Vector()
m = Vector()
for v in vecs :
m += obj.GetPoint(v)
m /= float(q)
return m
def _RempliCoorArete(self, i, pa, ps, nb) :
obj = self.obj
n = self.obj_n
for arete in pa :
pass
def _RempliCoor(self, i, pa, ps, nb, mode) :
obj = self.obj
Po = obj.GetPoint(i)
if mode == 2 :
self.nouv_coor[i] = Po
return
if nb < len(pa) : # Bordure
n = self.obj_n
pa_bor = []
pa_nb = 0
for arete in pa :
if pa_nb == 2 : break
a, b = n.GetEdgePolys(i, arete)
if a == -1 or b == -1 :
pa_bor.append(arete)
pa_nb += 1
Mpa = self._MoyenneVecteur(obj, pa_bor)
pos = 2.0 * Po - Mpa
elif nb == 3 :
if mode == 0 :
Mps = self._MoyenneVecteur(obj, ps)
pos = Po + (Po - Mps) * 2.0
elif mode == 1 or mode == 3 :
# Calcul des coordonnées PA fictives
Mpa = Vector()
Mps = self._MoyenneVecteur(obj, ps)
for arete in pa :
if not self.nouv_coor[arete] :
paA, psA, nbA = self._RecupPaPs(arete, False)
if nbA == 3 : continue
self._RempliCoor(arete, paA, psA, nbA, mode)
Mpa += self.nouv_coor[arete]
Mpa /= float(len(pa))
pos = (Mpa-Po) * 2.0 + Po - (Po-Mps) * 0.75 #0.58335
if mode == 3 :
pos = (pos + Po + (Po - Mps) * 2.0) * 0.5
#return (i, pa, ps, nb)
else :
Spa = self._SommeVecteur(obj, pa)
Sps = self._SommeVecteur(obj, ps)
pos = (Po*nb)/(nb-3.0) + (-4.0*Spa)/(nb*(nb-3.0)) + Sps/(nb*(nb-3.0))
self.nouv_coor[i] = pos
def _RempliPolys(self, i, pts_surf) :
for p in pts_surf :
poly = self.nouv_polys[p]
if not poly : self.nouv_polys[p] = [i]
else : self.nouv_polys[p].append(i)
# Correspondance polys subdivisés > nouv polys
pointpolys = self._GetPointPolys[p] #self.obj_n.GetPointPolys(p)
for pointpoly in pointpolys :
self.pol_corres[pointpoly] = p
def _RecupPo(self, po, pa) :
obj = self.obj
n = self.obj_n
pts_traites = self.pts_traites
pol_traites = self.pol_traites
pos = []
for a in pa :
polys = self._GetPointPolys[a] #n.GetPointPolys(a)
points = []
for pol in polys :
if pol_traites[pol] : continue
p = self._GetAllPolygons[pol] #obj.GetPolygon(pol)
points.extend([p.a, p.b, p.c, p.d])
pol_traites[pol] = True
points = list(set(points))
for pt in points :
if pts_traites[pt] : continue
arete = n.GetEdgePolys(a, pt)
if arete != (-1, -1) :
pos.append(pt)
break
return pos
def _RecupPaPs(self, po, traiteur=True) :
obj = self.obj
n = self.obj_n
pts_traites = self.pts_traites
pol_traites = self.pol_traites
polys = self._GetPointPolys[po] #n.GetPointPolys(po)
nb = len(polys)
points = []
ps = []
pa = []
for p in polys :
if traiteur : pol_traites[p] = True
p = self._GetAllPolygons[p] #obj.GetPolygon(p)
points.extend([p.a, p.b, p.c, p.d])
points = list(set(points))
for pt in points :
if traiteur :
self.pts_corres[pt].append(po)
pts_traites[pt] = True
if pt == po : continue
arete = n.GetEdgePolys(po, pt)
if arete == (-1, -1) :
ps.append(pt)
else :
pa.append(pt)
return pa, ps, nb
def Execute(self, mode, depart, selp, invn) :
if not self.Initialise(depart) : return
obj = self.obj
doc = self.doc
n = self.obj_n
pts_traites = self.pts_traites
pts_orig = self.pts_orig
nouv_pts = self.nouv_pts
nouv_pts_inv = self.nouv_pts_inv
nouv_polys = self.nouv_polys
nouv_coor = self.nouv_coor
_RecupPaPs = self._RecupPaPs
_RecupPo = self._RecupPo
_RempliPolys = self._RempliPolys
_RempliCoor = self._RempliCoor
bs = obj.GetPointS()
pts_a_calc = []
j = 0
i_n = 0
i_tot = 1
for i in pts_orig : # Points de départ pour la recherche des Points Originaux
i_n += 1
i_p = 0
if not pts_traites[i] : # Si le point n'a pas été traité
pts_traites[i] = True
# Récupère les points d'arête, de surface et le nombre de polys autour du point original
pts_arete, pts_surf, nb_poly_cote = _RecupPaPs(i)
# Calcule les nouvelles coordonnées du PO # ou renvoie a_calc pour les calculer plus tard
_RempliCoor(i, pts_arete, pts_surf, nb_poly_cote, mode)
# a_calc = _RempliCoor(i, pts_arete, pts_surf, nb_poly_cote, mode)
# if a_calc : pts_a_calc.append(a_calc)
# Ajoute PO aux futurs nouveaux polys
_RempliPolys(i, pts_surf)
# Récupère les points originaux aux alentours des points d'arête
pts_orig_tour = _RecupPo(i, pts_arete)
pts_orig.extend(pts_orig_tour)
nouv_pts_inv[i] = j # Tableau [PO sur l'obj. subdivisé] = PO de l'obj. non subd.
nouv_pts.append(i) # Liste des PO sur l'obj. subdivisé
i_tot += len(pts_orig_tour)
i_p = 1
j += 1
if i_n+i_p >= i_tot : # Si on atteint le bout de pts_orig
if 0 in pts_traites : # S'il reste des points à traiter
nouv_depart = pts_traites.index(0)
pts_orig.append(nouv_depart) # Ajouter le non traité à traiter
i_tot += 1
### -- Création de l'objet -- ###
### -- Récupération des propriétés et sélections -- ###
proprietes = obj.GetTags()
_Spolsels = []
_Npolsels = []
_Sptnsels = []
_Nptnsels = []
_Suvws = []
_Nuvws = []
for prop in proprietes :
if prop.CheckType(c4d.Tpolygonselection) :
_Npolsels.append(prop)
_Spolsels.append(prop.GetClone())
elif prop.CheckType(c4d.Tpointselection) :
_Nptnsels.append(prop)
_Sptnsels.append(prop.GetClone())
elif prop.CheckType(c4d.Tuvw) :
_Nuvws.append(prop)
_Suvws.append(prop.GetClone())
_ASptnsel = obj.GetPointS().GetClone()
_ANptnsel = obj.GetPointS()
_ASpolsel = obj.GetPolygonS().GetClone()
_ANpolsel = obj.GetPolygonS()
### -- Création de l'objet -- ###
nouv_polys_sale = nouv_polys[:]
nouv_polys = self._NettoieTab(nouv_polys)
nouv_nbpts = len(nouv_pts)
nouv_nbpolys = len(nouv_polys)
# objet = c4d.BaseObject(c4d.Opolygon)
obj.ResizeObject(nouv_nbpts, nouv_nbpolys)
for pti in xrange(nouv_nbpts) :
ans_id = nouv_pts[pti]
nouv_id = nouv_pts_inv[ans_id]
position = nouv_coor[ans_id]
obj.SetPoint(pti, position)
posi = obj.GetPoint
for poli in xrange(nouv_nbpolys) :
poly = nouv_polys[poli]
pa = nouv_pts_inv[poly[0]]
pb = nouv_pts_inv[poly[1]]
pd = nouv_pts_inv[poly[2]]
if len(poly) != 3 :
pc = nouv_pts_inv[poly[3]]
posa = posi(pa)
posb = posi(pb)
posc = posi(pc)
posd = posi(pd)
vAB = posa - posb
vAC = posa - posc
vAD = posa - posd
vBA = posb - posa
vBC = posb - posc
vCA = posc - posa
vCD = posc - posd
# Dénoue les polys si le point D est à l'ouest
if VectorAngle(vCA, -vAD) < VectorAngle(vCA, -vAB) :
if VectorAngle(vBA, -vAD) < VectorAngle(vBA, -vAC) :
pc, pd = pd, pc
else :
if VectorAngle(vBC, -vCD) < VectorAngle(vBC, -vCA) :
pa, pd = pd, pa
else : pc = pd
cpoly = c4d.CPolygon(pa, pb, pc, pd)
obj.SetPolygon(poli, cpoly)
obj.Message(c4d.MSG_UPDATE)
commande = utils.SendModelingCommand(command = c4d.MCOMMAND_ALIGNNORMALS, list = [obj], doc = doc)
if invn : commande = utils.SendModelingCommand(command = c4d.MCOMMAND_REVERSENORMALS, list = [obj], doc = doc)
### -- Modifs des propriétés -- #
cor_polyS_polyN = [] # Correspondance [polys subdivisés] = nouveaux polys NETTOYé
for _p in self.pol_corres : cor_polyS_polyN.append(self.cor_poly_polynet[_p])
for _i, _Npolsel in enumerate(_Npolsels) : # Sélection de polygones
_Spolsel = _Spolsels[_i]
_Sbs = _Spolsel.GetBaseSelect()
_Nbs = _Npolsel.GetBaseSelect()
_Nbs.DeselectAll()
for i, sel in enumerate(_Sbs.GetAll(self.obj_nbpol)) :
if not sel : continue
_p = cor_polyS_polyN[i]
if _p != -1 : _Nbs.Select(_p)
for _i, _Nptnsel in enumerate(_Nptnsels) : # Sélection de points
_Sptnsel = _Sptnsels[_i]
_Sbs = _Sptnsel.GetBaseSelect()
_Nbs = _Nptnsel.GetBaseSelect()
_Nbs.DeselectAll()
for i, sel in enumerate(_Sbs.GetAll(self.obj_nbpts)) :
if not sel : continue
_p = nouv_pts_inv[i]
if _p != -1 and _p < self.obj_nbpts : _Nbs.Select(_p)
_ANpolsel.DeselectAll() # Sélection de polygones active
for i, sel in enumerate(_ASpolsel.GetAll(self.obj_nbpol)) :
if not sel : continue
_p = cor_polyS_polyN[i]
if _p != -1 : _ANpolsel.Select(_p)
_ANptnsel.DeselectAll() # Sélection de points active
for i, sel in enumerate(_ASptnsel.GetAll(self.obj_nbpts)) :
if not sel : continue
_p = nouv_pts_inv[i]
if _p != -1 and _p < self.obj_nbpts : _ANptnsel.Select(_p)
for _i, _Nuvw in enumerate(_Nuvws) : # UVW
_Suvw = _Suvws[_i]
for i in xrange(_Nuvw.GetDataCount()):
k = self.cor_polynet_poly[i]
polys = self._GetPointPolys[k]
# points = nouv_polys[i]
points = []
Npoly = obj.GetPolygon(i)
points.append(nouv_pts[Npoly.a])
points.append(nouv_pts[Npoly.b])
points.append(nouv_pts[Npoly.c])
points.append(nouv_pts[Npoly.d])
abcd = [Vector(), Vector(), Vector(), Vector()]
for poly in polys :
pol = self._GetAllPolygons[poly]
for j, pt in enumerate(points) :
if pol.a == pt :
abcd[j] = _Suvw.GetSlow(poly)["a"]
break
elif pol.b == pt :
abcd[j] = _Suvw.GetSlow(poly)["b"]
break
elif pol.c == pt :
abcd[j] = _Suvw.GetSlow(poly)["c"]
break
elif pol.d == pt :
abcd[j] = _Suvw.GetSlow(poly)["d"]
break
_Nuvw.SetSlow(i, abcd[0], abcd[1], abcd[2], abcd[3])
# c4d.EventAdd()
def Initialise(self, depart) :
if not self.obj : return
if not self.doc : return
obj = self.obj
if not obj.CheckType(c4d.Opolygon) : return
self.obj_nbpts = obj.GetPointCount()
self.obj_nbpol = obj.GetPolygonCount()
if not self.obj_nbpts or not self.obj_nbpol : return
self.obj_n = Neighbor()
self.obj_n.Init(obj)
self.pts_orig = depart
self.pts_traites = [False] * self.obj_nbpts
self.pol_traites = [False] * self.obj_nbpol
self.nouv_polys = [0] * self.obj_nbpts
self.nouv_pts = []
self.nouv_pts_inv = [-1] * self.obj_nbpts
self.nouv_coor = [0] * self.obj_nbpts
self.pol_corres = [0] * self.obj_nbpol
self.pts_corres = [[] for a in range(self.obj_nbpts)]
self.cor_poly_polynet = []
self.cor_polynet_poly = []
self._GetAllPolygons = obj.GetAllPolygons()
self._GetPointPolys = []
for i in xrange(self.obj_nbpts) : self._GetPointPolys.append(self.obj_n.GetPointPolys(i))
return True
def __init__(self, doc, obj) :
self.obj = obj
self.doc = doc
class SubdivisionInverseObjet(c4d.plugins.ObjectData):
subdinv = None
type = 0
subd = 1
depa = 0
selp = False
invn = False
def Message(self, node, type, data) :
if type == c4d.MSG_MENUPREPARE :
node.SetDeformMode(True)
if type == c4d.MSG_DESCRIPTION_COMMAND :
id = data['id'][0].id
if id == VONC_SUBDINV_DON : webbrowser.open("http://code.vonc.fr/", 2, True)
elif id == VONC_SUBDINV_ACTU : node.SetDirty(c4d.DIRTY_DATA)
return True
def Init(self, op) :
donnees = op.GetDataInstance()
self.InitAttr(op, int, [VONC_SUBDINV_TYPE])
self.InitAttr(op, int, [VONC_SUBDINV_SUBD])
self.InitAttr(op, int, [VONC_SUBDINV_DEPA])
self.InitAttr(op, bool, [VONC_SUBDINV_SELP])
self.InitAttr(op, bool, [VONC_SUBDINV_INVN])
op[VONC_SUBDINV_TYPE] = self.type
op[VONC_SUBDINV_SUBD] = self.subd
op[VONC_SUBDINV_DEPA] = self.depa
op[VONC_SUBDINV_SELP] = self.selp
op[VONC_SUBDINV_INVN] = self.invn
return True
def ModifyObject(self, mod, doc, op, op_mg, mod_mg, lod, flags, thread):
if not op.CheckType(c4d.Opolygon) : return True
if not op.GetPointCount() : return True
if not op.GetPolygonCount() : return True
self.type = mod[VONC_SUBDINV_TYPE]
self.subd = mod[VONC_SUBDINV_SUBD]
self.depa = mod[VONC_SUBDINV_DEPA]
self.selp = mod[VONC_SUBDINV_SELP]
self.invn = mod[VONC_SUBDINV_INVN]
self.subdinv = SubdivisionInverse(doc, op)
for k in xrange(self.subd) :
nbp = op.GetPointCount()
depart = [self.depa % nbp]
if self.selp :
depart = []
bs = op.GetPointS()
for i, sel in enumerate(bs.GetAll(nbp)) :
if not sel : continue
depart.append(i)
if not depart : depart = [0]
self.subdinv.Execute(self.type, depart, self.selp, self.invn)
return True
if __name__ == "__main__":
bmp = bitmaps.BaseBitmap()
dir, f = os.path.split(__file__)
fn = os.path.join(dir, "res", "vonc_subdinv.tif")
bmp.InitWith(fn)
c4d.plugins.RegisterObjectPlugin(id=MODULE_ID, str=c4d.plugins.GeLoadString(VONC_SUBDINV_NOM),
g=SubdivisionInverseObjet,
description="vonc_subdinv",
icon=bmp,
info=c4d.OBJECT_MODIFIER)