Dear Python experts,
I am working on a Python Generator Plugin which creates Splines from Polygon Edges including some cleanup procedures. The problem I am facing is that the Generator works in a simple setup (e.g. it has a Voronoi Fracture or Clone under it) but when it comes in complex setups with more children it spins in an endless dirty loop. It really gets evil when Fields are involved in that setup. I tried everything including SetOptimizeCache(True) or checking dirty states of the children with a loop but nothing really helped here. Maybe it is because of all these modelling commands which probably make a scene dirty.
Could you provide me with some help? I know it is a generic question, I would like to be more specific but I don't know how. I would really appreciate your help here.
Here is the code of the entire plugin:
import os
import c4d
from c4d import plugins, utils, bitmaps
from c4d.utils import SplineHelp
class SplFromEdge(plugins.ObjectData):
def Init(self, node):
# Retrieves the BaseContainer Instance to set the default values
#self.SetOptimizeCache(True)
data = node.GetDataInstance()
if data is None:
return False
data.SetBool(c4d.WELDSIMILAR, False)
data.SetFloat(c4d.THRESHOLD, 0.01)
data.SetInt32(c4d.O_SPLINEFROMEDGEMODE, c4d.O_SPLINEFROMEDGEMODE_TAG)
data.SetBool(c4d.O_SPLINEFROMEDGEMODE_AUTOUPDATE , True)
data.SetBool(c4d.CLOSE,True)
data.SetBool(c4d.O_SPLINEFROMEDGEMODE_OPTIMIZE, True)
return True
#grey out the threshold in the UI
def GetDEnabling(self, node, id, t_data, flags, itemdesc):
data = node.GetDataInstance()
weld = data.GetBool(c4d.WELDSIMILAR)
if id[0].id == c4d.THRESHOLD:
return weld
# Retrieves the current interpolation
inter = node[c4d.O_SPLINEFROMEDGEMODE]
# Defines enable state for the selection
if id[0].id == c4d.EDGESELECTION:
return inter == c4d.O_SPLINEFROMEDGEMODE_TAG
return True
#iterator to go through the whole hierarchy
def walk(self,op) :
if not op: return
elif op.GetDown() :
return op.GetDown()
while op.GetUp() and not op.GetNext():
op = op.GetUp()
return op.GetNext()
#has all modeling operators
def ExecModelingCommand(self, doc,opCtrl, op, parent):
if op is None:
return
data = opCtrl.GetDataInstance()
threshold = data.GetReal(c4d.THRESHOLD)
weld = data.GetBool(c4d.WELDSIMILAR)
mode = data.GetInt32(c4d.O_SPLINEFROMEDGEMODE)
selectionSet = data.GetString(c4d.EDGESELECTION)
optimize = data.GetBool(c4d.O_SPLINEFROMEDGEMODE_OPTIMIZE)
close = data.GetBool(c4d.CLOSE)
splineHelper = SplineHelp()
container = c4d.BaseContainer()
childObject = op.GetClone(c4d.COPYFLAGS_NONE)
# Creates a temporary document.
tempDoc = c4d.documents.BaseDocument()
# Insert the cloned object to the temporary document.
tempDoc.InsertObject(childObject)
tempParent = c4d.BaseObject(c4d.Onull)
if childObject is None or (mode==c4d.O_SPLINEFROMEDGEMODE_TAG and selectionSet==""):
return
parent.SetMg(childObject.GetMg())
c4d.StatusSetText("Creating Splines from Edges")
c4d.StatusSetSpin()
res = utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT,
list=[childObject],
mode=c4d.MODELINGCOMMANDMODE_ALL,
bc=container,
doc=tempDoc)
if res is None:
return
editableObject = res[0]
#select edges based on a selection tag
def selectEdges(obj,tag):
selection = tag.GetBaseSelect()
polyselection = obj.GetEdgeS()
selection.CopyTo(polyselection)
#iterate through the new created polygon objects and look for the ones with an edge selection tag
objectList = []
while editableObject:
if editableObject.CheckType(c4d.Opolygon):
if mode==c4d.O_SPLINEFROMEDGEMODE_TAG:
for i in editableObject.GetTags():
if i.GetName()==selectionSet and i.CheckType(c4d.Tedgeselection):
#select
selectEdges(editableObject,i)
#add to list
objectList.append(editableObject)
elif mode==c4d.O_SPLINEFROMEDGEMODE_ALL:
utils.SendModelingCommand(c4d.MCOMMAND_SELECTALL,
list = [editableObject],
mode = c4d.MODELINGCOMMANDMODE_EDGESELECTION,
bc = c4d.BaseContainer())
objectList.append(editableObject)
editableObject = self.walk(editableObject)
if not objectList:
c4d.StatusClear()
return
for i in objectList:
i.InsertUnderLast(tempParent)
spl = utils.SendModelingCommand(command=c4d.MCOMMAND_EDGE_TO_SPLINE,
list=objectList,
mode=c4d.MODELINGCOMMANDMODE_ALL,
doc=objectList[0].GetDocument())
c4d.StatusSetText("Optimizing Spline Objects")
#Exploding Splines with multiple Segments which came from Edge to Spline
splitList = []
for i in objectList:
child = i.GetDown()
if child is not None:
if child.CheckType(c4d.Ospline):
splineHelper.InitSpline(child)
if splineHelper.GetSegmentCount() !=1:
splitList.append(child)
#Explode Splines using the Explode Command
if splitList:
expSplines = utils.SendModelingCommand(command=c4d.MCOMMAND_EXPLODESEGMENTS,
list=splitList,
mode=c4d.MODELINGCOMMANDMODE_ALL,
flags=c4d.MODELINGCOMMANDFLAGS_NONE,
doc=splitList[0].GetDocument())
#Creating a Spline List which will be later added under a parent object
splineList = []
obj=objectList[0]
while obj:
if obj.CheckType(c4d.Ospline):
splineHelper.InitSpline(obj)
if splineHelper.GetSegmentCount() !=0:
#Add all spline objects to a dedicated spline list. Every object which is not in that list will be deleted
splineList.append(obj)
obj = self.walk(obj)
#optimize the spline for duplicate points
optimizeBC = c4d.BaseContainer()
optimizeBC[c4d.MDATA_OPTIMIZE_POINTS]= optimize
optimizeBC[c4d.MDATA_OPTIMIZE_UNUSEDPOINTS]= True
optimizeBC[c4d.MDATA_OPTIMIZE_TOLERANCE]= 0.01
optimizedSplines = utils.SendModelingCommand(command=c4d.MCOMMAND_OPTIMIZE,
list=splineList,
bc= optimizeBC,
mode=c4d.MODELINGCOMMANDMODE_ALL,
flags=c4d.MODELINGCOMMANDFLAGS_NONE)
#filter overlapping objects
c4d.StatusSetText("Cleaning Up and Welding")
positionList = []
keyList = []
noDuplicateSplineList = []
for i in splineList:
points = i.GetAllPoints()
bary = c4d.Vector(0)
#find the center of all points
for j in points:
bary+=j
bary = bary / len(points)
# move the points by the amount represented by the new axis center
for index, point in enumerate(points) :
point = point - bary
i.SetPoint(index, point)
# notify about the points repositioning
i.Message(c4d.MSG_UPDATE)
# adjust the matrix offset component accordingly
currentMg = i.GetMg()
currentMg.off += bary
# reposition the object in the space
i.SetMg(currentMg)
#adjust object position
polyParent = i.GetUp()
offset = polyParent.GetMg().off
#insert and adjust object position
i.InsertUnderLast(tempParent)
i[c4d.SPLINEOBJECT_CLOSED]=close
currentMg = i.GetMg()
currentMg.off += offset
i.SetMg(currentMg)
#remove overlapping splines if welding is active
if weld:
#check spline object position if it mateches a certain threshold
overlapping = False
if positionList:
for j in positionList:
p1 = i.GetMg().off
p2 = j.GetMg().off
distance = (p2-p1).GetLength()
if distance <= threshold:
overlapping = True
#check if it's overlapping and clear it
key = int((i.GetRad().x+i.GetRad().y+i.GetRad().z)/threshold)
if key in keyList and overlapping:
i.Remove()
else:
keyList.append(key)
positionList.append(i)
noDuplicateSplineList.append(i)
else:
noDuplicateSplineList.append(i)
c4d.StatusClear()
#put everything under the parent null and return it
for i in noDuplicateSplineList:
i.InsertUnderLast(parent)
return parent
def GetVirtualObjects(self, op, hh):
doc = op.GetDocument()
data = op.GetDataInstance()
objInput = op.GetDown()
if objInput is None:
return None
objRet = c4d.BaseObject(c4d.Onull)
if doc is None or objRet is None:
return None
hierarchyClone = op.GetAndCheckHierarchyClone(hh, objInput, c4d.HIERARCHYCLONEFLAGS_ASPOLY, True)
if hierarchyClone["dirty"] is False:
return hierarchyClone["clone"]
clone = hierarchyClone["clone"]
if clone is None:
return op.GetCache()
self.ExecModelingCommand(doc, op, clone, objRet)
return objRet
###############################################################################################################
# Plugin Registration
###############################################################################################################
PLUGIN_ID_GENERATOR = 954679
def RegisterObjectData(id, name, bmpPath, objData, desc, flags):
bmp = bitmaps.BaseBitmap()
bmp.InitWith(os.path.join(bmpPath, "res", "icon.tif"))
plugins.RegisterObjectPlugin(id=id, str=name,
g=objData,
description=desc, icon=bmp,
info=flags)
if __name__ == "__main__":
path, fn = os.path.split(__file__)
RegisterObjectData(PLUGIN_ID_GENERATOR, "Spline from Edge", path, SplFromEdge, "osplinefromedge", c4d.OBJECT_GENERATOR | c4d.OBJECT_INPUT)