Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    How do you collapse complex dependies in order?

    Cinema 4D SDK
    2023 python
    2
    2
    492
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • FSSF
      FSS
      last edited by FSS

      So, lets assume that a scene has complex dependancies.
      A generator depending on a splinewrapper, depending on a spline, depending on a oSweep, etc. etc.

      How do i succesfully collapse such constructs to editable poly in a python script? My approach would have been to make a dependancy tree and then navigate from the leafs inwards.

      But how do form that graph?

      I know about about

      objectInfo = obj.GetInfo()
      if objectInfo & c4d.OBJECT_INPUT:

      But i need to know which Object is the parent of that Input. Or preferably, if there is a existing solution a pointer to that?

      Or is there a sort of generic, generator dependency tree that can be traversed?

      Regards FSS

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        Hello @fss,

        Thank you for reaching out to us. The Cinema 4D classic API has no dependency graph for its scene elements. If you want such information, you must gather it yourself. This is however a non-trivial task.

        You also have been asking this same question multiple times both here on the forum and via mail, with both Manuel and I giving you multiple times the same answer. To "collapse" things, you must use 'Current State to Object (CSTO)' and then join the results, as first reducing things to their current cache state (CSTO) will remove the dependencies between things. You can/could also do this manually, just as the joining operation, but it is then up to you to develop that.

        Please understand that we will not answer the same question over and over again. We enjoy and encourage discussions with users, but as stated in our forum guidelines:

        We cannot provide support for [...] code design that is in direct violation of Cinema's technical requirements [...]

        Find below an example.

        Cheers,
        Ferdinand

        Result for the fairly complex Mograph asset Example Scenes\Disciplines\Motion Graphics\01 Scenes\Funny Face.c4d:
        connect_and_delete.gif

        Code

        '''Example for mimicking the "Connect & Delete" command in Python.
        
        Must be run from the Script Manager with the root objects selected whose local hierarchies should
        be collapsed. 
        '''
        
        import c4d
        import typing
        
        def Collapse(objects: list[c4d.BaseObject]) -> None:
            """Collapses all items in #objects as individual root nodes into singular objects.
        
            This function mimics the behaviour of the builtin (but unexposed) "Connect & Delete" command 
            by first running the "CSTO" and then "JOIN" command. With setups complex enough, this can still
            fail due to the non-existent dependency graph of the classic API (when one does CSTO things in
            the wrong order). In 99.9% of the cases this will not be the case, but one should get the
            inputs with #GETACTIVEOBJECTFLAGS_SELECTIONORDER as I did below to give the user more control.
            (or alternatively do not batch operate).
            """
            if len(objects) < 1:
                raise RuntimeError()
            doc: c4d.documents.BaseDocument = objects[0].GetDocument()
            doc.StartUndo()
        
            # CSTO all local hierarchies in #objects and replace these root nodes with their collapsed
            # counter parts.
            result = c4d.utils.SendModelingCommand(c4d.MCOMMAND_CURRENTSTATETOOBJECT, objects, 
                c4d.MODELINGCOMMANDMODE_ALL, c4d.BaseContainer(), doc, c4d.MODELINGCOMMANDFLAGS_NONE)
        
            if not result or len(result) != len(objects):
                raise RuntimeError()
        
            for old, new in zip(objects, result):
                parent, pred = old.GetUp(), old.GetPred()
                doc.AddUndo(c4d.UNDOTYPE_DELETEOBJ, old)
                old.Remove()
                doc.InsertObject(new, parent, pred)
                doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, new)
        
            # Join the CSTO results root by root object, and then replace the CSTO results with the final
            # collapsed result. JOIN is a bit weird when it comes to transforms, so we must store the 
            # transform of the to be joined object, then zero it out, and finally apply it to the joined 
            # result again.
            for obj in result:
                mg: c4d.Matrix = obj.GetMg()
                obj.SetMg(c4d.Matrix())
        
                joined = c4d.utils.SendModelingCommand(c4d.MCOMMAND_JOIN, [obj], 
                    c4d.MODELINGCOMMANDMODE_ALL, c4d.BaseContainer(), doc, c4d.MODELINGCOMMANDFLAGS_NONE)
                
                if not joined:
                    raise RuntimeError()
                
                parent, pred = obj.GetUp(), obj.GetPred()
                doc.AddUndo(c4d.UNDOTYPE_DELETEOBJ, obj)
                obj.Remove()
        
                new: c4d.BaseObject = joined[0]
                new.SetMg(mg)
                doc.InsertObject(new, parent, pred)
                doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, new)
        
            doc.EndUndo()
            c4d.EventAdd()
        
        doc: c4d.documents.BaseDocument # The active document
        op: typing.Optional[c4d.BaseObject] # The active object, can be None.
        
        def main() -> None:
            """Runs the #Collapse() function on all currently selected objects as root nodes.
            """
            selection: list[c4d.BaseObject] = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_SELECTIONORDER)
            if len(selection) < 1:
                print("Please select at least one root object.")
            else:
                Collapse(selection)
        
        if __name__ == "__main__":
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • ferdinandF ferdinand referenced this topic on
        • First post
          Last post