Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode.
Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).
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)
Hi @matniedoba, thanks for reaching out us.
With regard to your issue, given that it's not our primary task to debug plugins, to understand what's getting wrong I need you to provide:
I also second @zipit recommendations because improving code design via better function abstraction and documentation will lead to an increased clarity, reliability and, in the end, comprehension. I'll try to find time in the course of the week to have a look at your issue given the items requested above.
Best, R
Hi,
I think your biggest problem is your coding style. And I do not mean that in an offensive way, but simply am pointing out that you seem to have cornered yourself into a state of code that contains bugs where you (and also I) have no clue where they exactly stem from. You might want to read PEP 8 and the excellent Google Python Style Guide.
With that out of the way, the obvious thing first: When you use the dirty state of the children of your generator to determine if it has to be rebuild or not, either touching one of the children or having a child which determines its dirty state by its parent state (i.e. the generator), will result in a loop.
Some points:
None
GetVirtualObjects
c4d.Onull
doc
objRet
GetAndCheckHierarchyClone
ExecModelingCommand
I do not have any more specific advice, since I stopped reading at some point as it got more and more convoluted (sorry, no offense intended). Instead I have some general advice:
GetContour
GVO
SendModellingCommand
I hope this helps.
Cheers, zipit
Thank you @zipit for your comprehensive response, especially with the link to the coding guide. I guess it might be better to create the spline manually from the points which are connected by the edges without the usage of SendModellingCommand. It was good for prototyping functionality but as you mentioned, it is hard to debug. I appreciate you took the time (and I know it's a lot of convoluted code) to read through it. @r_gigante -> the work is currently on my side so you don't need to bother. I think I have enough information to do a second iteration and rewriting this from scratch.