MCOMMAND_MAKEEDITABLE why does this comand need an object in scene oposed to passing it in Ram?
-
Sorry for the cryptic topic , please change if applicable.
Anyway I figured out that
c4d.utils.SendModelingCommand( command = c4d.MCOMMAND_MAKEEDITABLE, list = [obj], doc=doc)
need an actual "inserted" object while other modeling comands (eg. MCOMMAND_CURRENTSTATETOOBJECT) can work in "RAM" with passed objects?
Minimal example code:
from typing import Optional import c4d doc: c4d.documents.BaseDocument # The active document op: Optional[c4d.BaseObject] # The active object, None if unselected def main() -> None: c4d.CallCommand(13957) # Konsole löschen obj = c4d.BaseObject(c4d.Ocube) doc.InsertObject(obj) # <- why do I need this ! get_points(obj) c4d.EventAdd() def get_points(obj): return_value = c4d.utils.SendModelingCommand( command = c4d.MCOMMAND_MAKEEDITABLE, list = [obj], doc=doc) if return_value is False: raise TypeError("make editable did not work") elif return_value is True: print("make editable should not return true") elif isinstance(return_value, list): newobj = return_value[0] doc.InsertObject(newobj) if __name__ == '__main__': main()
-
Hey @mogh,
Thank you for reaching out to us. Your question is a bit like asking 'Why is water wet?'. The answer is there 'because it is' unless one is willing to get technical. The same applies here, some modeling commands require their inputs to be part of a document and their authors simply designed them to be like this. Discussing the technical details is out of scope of support unless you file a code sharing request (and have a good reason for us granting it).
The SendModelingCommand documentation lines out a few of these commands which require a document, including
MCOMMAND_MAKEEDITABLE
and other than indicated by yourself, alsoMCOMMAND_CURRENTSTATETOOBJECT
. What might not be documented so well, is however the quality of that document. Nothing forces you to use the active document or a document which is loaded. You can very well use a dummy document. This is even required when you are in a threaded context where the modification of loaded documents is forbidden.Find a brief example below.
Cheers,
FerdinandOutput
SMC - count = 114, points[:3] = [Vector(0, -100, 0), Vector(0, 100, 0), Vector(38.268, -92.388, 0)] Cache - count = 114, points[:3] = [Vector(0, -100, 0), Vector(0, 100, 0), Vector(38.268, -92.388, 0)]
Code
"""Demonstrates how to use a dummy document in conjunction with SendModelingCommand() in order to avoid having to modify a loaded document. Also provides an alternative approach using caches. Must be run as a Script Manager script. """ import c4d def main() -> None: """Runs the example. """ # Instantiate an object we want to be subjected to modelling commands and a dummy document to # carry them out in. This prevents us from having to clean up after ourselves in a loaded # document or makes using SMC possible in the first place in contexts where modifying loaded # documents is forbidden. obj: c4d.BaseObject = c4d.BaseObject(c4d.Osphere) tempDoc: c4d.documents.BaseDocument = c4d.documents.BaseDocument() if not obj or not tempDoc: raise MemoryError(f"{obj = }, {tempDoc = }") # Insert the object and execute the command. tempDoc.InsertObject(obj) result: list[c4d.BaseObject] = c4d.utils.SendModelingCommand( command = c4d.MCOMMAND_MAKEEDITABLE, list = [obj], doc=tempDoc) if not result or not isinstance(result[0], c4d.PointObject): raise RuntimeError(f"Command failed.") count: int = result[0].GetPointCount() points: list[c4d.Vector] = result[0].GetAllPoints() print (f"SMC - {count = }, {points[:3] = }") # #tempDoc will be deallocated once the scope of this function is left. We can call Flush() before # that which is not necessary but probably good form. With it, both #obj and #result[0] will be # deallocated. tempDoc.Flush() # ---------------------------------------------------------------------------------------------- # When it is only primitive generators you want to evaluate, i.e., things which have a flat # cache, you can also simply execute the passes on the object, which is probably slightly more # performant than calling SMC (because SMC will also have to do that). obj: c4d.BaseObject = c4d.BaseObject(c4d.Osphere) tempDoc: c4d.documents.BaseDocument = c4d.documents.BaseDocument() if not obj or not tempDoc: raise MemoryError(f"{obj = }, {tempDoc = }") # Build the caches. tempDoc.InsertObject(obj) tempDoc.ExecutePasses(None, False, False, True, c4d.BUILDFLAGS_NONE) # Get the generator cache and its deform cache if there is any (will only be there when # there is a deformer parented to #obj). cache: c4d.PointObject = obj.GetCache() cache = cache.GetDeformCache() or cache if not isinstance(cache, c4d.PointObject): raise RuntimeError(f"Cache structure of {obj} does not meet expectations.") count: int = cache.GetPointCount() points: list[c4d.Vector] = cache.GetAllPoints() print (f"Cache - {count = }, {points[:3] = }") tempDoc.Flush() if __name__ == '__main__': main()
-
Thank You Ferdinand,
just to clarify: I found that bit of the documentation before you pointed it out, but in my mind I could not link "needs a document" to "the object has to be present" ...
anyway thanks for the cached example too - will try to implement it ...
regards mogh
-
Hey @mogh,
just to clarify: I found that bit of the documentation before you pointed it out, but in my mind I could not link "needs a document" to "the object has to be present" ...
Well, the documentation clearly states it:
doc (Optional[c4d.documents.BaseDocument])
– The document for the operation. Should be set if possible. Must be set for MCOMMAND_JOIN, MCOMMAND_MAKEEDITABLE, MCOMMAND_CURRENTSTATETOOBJECT and MCOMMAND_SPLINE_PROJECT.If you set the document, the objects which you pass to this function have to be in the same document. So pay attention that you use one send_modeling_command per document for objects.
But that could be more visible. I'll see if I can rework the function documentation a bit to make that important information more prominent.
Cheers,
Ferdinand