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.
![](/forum/assets/uploads/profile/1923-profileavatar.jpeg)
Posts made by matniedoba
-
RE: Generator sticks in a dirty loop
-
Generator sticks in a dirty loop
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)
-
RE: Trouble with GetAndCheckHierarchyClone
Amazing! Thank you Manuel! It works with multiple objects, Cloners etc. Without your help I would not make it
-
RE: Trouble with GetAndCheckHierarchyClone
Hey Manuel,
I tried this method but I encountered another issue. The copy to another document does not work in a setup with a Mograph Effector. Therefore, I am inserting the object to the original document and try to remove the original. But that does not work. When I make ChamferGen editable, it creates the original Connect+Children objects as well as the editable Spline Object. I tried stuff like cloner.Remove() but that did not work. The setup is linked below.
-
RE: Trouble with GetAndCheckHierarchyClone
I also have another question. How should I change the setup to support multiple children? (see image) I tried to insert multiple objects in the tempDoc via a loop but nothing really worked.
-
RE: Trouble with GetAndCheckHierarchyClone
@m_magalhaes
Thank you for your help! It works. I really like the tip of the temporary document. Does it make sense to use c4d.documents.KillDocument(doc) after the process is complete to free up resources?
-
RE: Goodbye
Hey Sebastian,
I wish you the best on your next challenges! Thank you so much for being such a help for the C4D Community! -
Trouble with GetAndCheckHierarchyClone
Hey folks,
I could need your help on building a Python Generator version of the Chamfer Tool. It's based on the Py-Parametric tools build by Andreas back in days. There were very helpful for setting up the generator. But here comes the issue:
Using GetAndCheckHierarchyClone does not work well with the MCOMMAND_CURRENTSTATETOOBJECT (at least this is what I think). When I test the plugin and use a Cloner with Splines under a Connect Object, it only affects the first clone. When I make the Connect Object editable, it works on all objects. See images.
When I bypass the dirty check and add replace "clone" by "op" in line 63 in the "GetVirtualObjects" method, than it works, but very badly.I would appreciate any kind of help
import os import c4d from c4d import plugins, utils, bitmaps ############################################################################################################### #Based on the Py-Parametric Tools by Andreas Block ############################################################################################################### class ModelingCommandGeneratorModifier(plugins.ObjectData): _toolId = -1 _doSelection = False _selectId = -1 def InitCommon(self, toolId, doSelection, selectId): self._toolId = toolId self._doSelection = doSelection self._selectId = selectId def ExecModelingCommand(self, doc, opCtrl, op, parent): if op is None: return splineObjects = [] if op.GetDown().CheckType(c4d.Ospline) is False: splineObjects = utils.SendModelingCommand(command = c4d.MCOMMAND_CURRENTSTATETOOBJECT, list = [op.GetDown()], mode = c4d.MODELINGCOMMANDMODE_ALL, bc = c4d.BaseContainer(), doc = doc) else: splineObjects.append(op.GetDown()) if not splineObjects: return if splineObjects[0].CheckType(c4d.Ospline) is True: res = utils.SendModelingCommand(command = self._toolId, list = splineObjects, mode = c4d.MODELINGCOMMANDMODE_ALL, bc = opCtrl.GetDataInstance(), # settings, doc = doc) if res is True: splineObjects[0].InsertUnderLast(parent) def GetVirtualObjects(self, op, hh): doc = op.GetDocument() 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_NONE, True) if hierarchyClone["dirty"] is False: return hierarchyClone["clone"] clone = hierarchyClone["clone"] if clone is None: return objRet #When I replace "clone" to "op" and bypass the dirty checks, than it works but very sluggish self.ExecModelingCommand(doc, op, clone, objRet) return objRet class ChamferGen(ModelingCommandGeneratorModifier): def Init(self, op): ModelingCommandGeneratorModifier.InitCommon(self, c4d.ID_MODELING_SPLINE_CHAMFER_TOOL, False, -1) InitChamferDesc(self, op) return True def InitChamferDesc(inst, op): inst.InitAttr(op, bool, [c4d.MDATA_SPLINE_CHAMFERFLAT]) inst.InitAttr(op, float, [c4d.MDATA_SPLINE_CHAMFERRADIUS]) op[c4d.MDATA_SPLINE_CHAMFERRADIUS] = 5.0 ############################################################################################################### # Plugin Registration ############################################################################################################### PLUGIN_ID_GENERATOR = 9036026 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, "ChamferGen", path, ChamferGen, "ochamfergen", c4d.OBJECT_GENERATOR | c4d.OBJECT_INPUT) print "ChamferGen 1.0 successfully initialized"
-
RE: c4dpy.exe and VS Code on Windows?
Thanks Maxime for clarifying. So we will wait for your fix
-
RE: c4dpy.exe and VS Code on Windows?
Hey, I run into the same issue on a Mac. I followed the instruction how to use c4dpy in VS Code but when I am pasting the path to c4dpy in the python interpreter settings, it does not accept it. No messages, nothing. I cannot select c4dpy as a python interpreter.
Greetings
Matthรคus -
RE: Porting a python command plugin to R21
The problem is solved. It was so trivial :D. I unchecked the plugins folder which I added in the preferences. My plugins folder was in the C4D directory, so as you described, it was loaded twice.
Leaning for me: Never place a custom plugins folder in the C4D directory!
Recommendation to the SDK team: Maybe you could adjust the error message in the console, which mentions that plugin folders should not be in the C4D directory. I would assume that many users/ devs will place the folder there, because of a habit from older versions.Thank you @r_gigante and @zipit for your quick response!
-
Porting a python command plugin to R21
Hey folks,
I am currently porting a command plugin to C4D R21, because some users reported that it is not working for them. However, on a Mac it works on R21 but I get this error message in the console:
Traceback (most recent call last): File "/Applications/Maxon Cinema 4D R21/PLugins/SelectionToObject/SeletionToObject.pyp", line 271, in <module> okyn = plugins.RegisterCommandPlugin(PLUGIN_ID,"Selection to Object",0,bmp,"Selection to Object",SelectionToObject()) RuntimeError:The plugin ID '1054093999' collides with another plugin ID in 'SeletionToObject'. You have to use a unique ID for registering your plugin! You can get a personal number for free from PluginCafe at www.plugincafe.com
I already got a new plugin ID from PluginCafe but it does not solve the problem.
Here is the main function of the plugin, which throws the error:
PLUGIN_ID = 1054093 if __name__=='__main__': bmp=bitmaps.BaseBitmap() path, fn = os.path.split(__file__) bmp.InitWith(os.path.join(path,"res","Icon.tif")) okyn = plugins.RegisterCommandPlugin(PLUGIN_ID,"Selection to Object",0,bmp,"Selection to Object",SelectionToObject()) if (okyn): print "Selection to Object successfully initialized"
Any help would be appreciated. Thank you