How do you collapse complex dependies in order?

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

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.


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


'''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()

    # CSTO all local hierarchies in #objects and replace these root nodes with their collapsed
    # counter parts.
    result = c4d.utils.SendModelingCommand(c4d.MCOMMAND_CURRENTSTATETOOBJECT, objects, 

    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)
        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()

        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)

        new: c4d.BaseObject = joined[0]
        doc.InsertObject(new, parent, pred)
        doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, new)


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.")

if __name__ == "__main__":

MAXON SDK Specialist