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

    Help with Matrix Manipulation after c4d.MCOMMAND_JOIN

    Cinema 4D SDK
    python r23 r20
    3
    16
    2.8k
    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.
    • ferdinandF
      ferdinand
      last edited by

      Hi @mogh,

      thank you for reaching out to us and especially for sharing your own solution to your question. Am I correct assuming that your second post answered your question or are there things that remain unclear?

      Cheers,
      Ferdinand

      MAXON SDK Specialist
      developers.maxon.net

      1 Reply Last reply Reply Quote 0
      • M
        mogh
        last edited by

        Hi Zipit,
        Matrix manipulation still does not behave as I want ... the polygon objects jump and rotate
        I want them to stay in their postion after the join comand ....

        I brute forced almost every combination of matrix , ~matrix, local matrix no luck ...

        I am out of ideas
        kind regards

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

          Hi @mogh,

          unfortunately it is not quite clear to me what you are trying to do, because both your snippets are not executable, i.e. are just snippets or include pseudo-code. However, from this general setting and your wording in your first posting, I assume you are simply trying "to move the axis of an object without moving its points", i.e. do what the move axis tool does. Below you find a simple script which shows you how to change the transform of a PointObject without changing the global coordinates of its points.

          If this is not what you are looking for, I would ask you to show us code that is executable and to explain in more detail what kind of transform you are looking for.

          Cheers,
          Ferdinand

          """Demonstrates how to "move" the transform of a point object.
          
          "Moves" the transform of a PointObject without changing the global 
          coordinates of its points, i.e. does what the move axis tool does. 
          
          You have to select a point object and then another object and it will move 
          the axis of the point object to the scond one.
          """
          
          import c4d
          
          def set_point_object_transform(node, transform):
              """Sets the global transform of a point object while keeping its points
              in place.
          
              Args:
                  node (c4d.PointObject): The point object to move the axis for.
                  transform (c4d.Matrix): The new global transform for the object.
              
              Raises:
                  TypeError: When node or transform are not of specified type.
              """
              if (not isinstance(node, c4d.PointObject) or 
                  not isinstance(transform, c4d.Matrix)):
                  msg = f"Illegal argument types: {type(node)}{type(transform)}"
                  raise TypeError(msg)
          
              mg = node.GetMg()
              # Move the points in the global frame and then into the new frame.
              points = [p * mg * ~transform for p in node.GetAllPoints()]
              # Set the points and stuff ;)
              node.SetAllPoints(points)
              node.Message(c4d.MSG_UPDATE)
              node.SetMg(transform)
          
          def main():
              """Runs set_point_object_transform() on the current selection.
              """
              nodes = doc.GetActiveObjects(0)
              if len(nodes) < 2:
                  return
          
              node, transform = nodes[0], nodes[1].GetMg()
              set_point_object_transform(node, transform)
              c4d.EventAdd()
          
          if __name__ == "__main__":
              main()
          

          MAXON SDK Specialist
          developers.maxon.net

          1 Reply Last reply Reply Quote 0
          • M
            mogh
            last edited by

            Thank you for your patience and time Zipit.

            I implemented your code into mine, but sadly it does the same as mine ... all the polygon objects move and rotate.

            I will make a stripped down version of the code so you can have a better look at it.

            Thank you for your time.
            kind regards
            mogh

            1 Reply Last reply Reply Quote 0
            • M
              mogh
              last edited by mogh

              So this is the compact version I could come up with. (including your Code @Zipit)

              Please add some poly Objects under Nulls called "Axis" into your scene, an nest them arbitrary, with random rotation and location to se the problem when they are joined.

              Thank you for your time.

              #!py3
              import c4d, sys, os
              from c4d import gui
              from c4d.documents import GetActiveDocument
              #Version 1.4 Striped version
              # This Script needs Null-Objects Called "Axis" with polygon objects to run
              
              def GetNextObject(op):
                  if not op: return
                  if op.GetDown(): return op.GetDown()
                  while op.GetUp() and not op.GetNext():
                      op = op.GetUp()
                  return op.GetNext()
              
              def get_all_objects (op):
                  allachsen_list = list()
                  all_objects_list = list()
                  while op:
                      if op.GetName() == 'Achsen-Objekt' or op.GetName() == 'Axis' :
                          allachsen_list.append(op)
                      all_objects_list.append(op)
                      op = GetNextObject(op)
                  return all_objects_list, allachsen_list
              
              def JoinCommand(doc, op):
                  res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_JOIN,
                                              list = [op],
                                              mode = c4d.MODELINGCOMMANDMODE_ALL,
                                              doc = doc)
                                              
                  # Cheks if the command didn't failed
                  if res is False:
                      raise TypeError("return value of Join command is not valid")
                  elif res is True:
                      print ("Command successful. But no object.")
                  elif isinstance(res, list):
                      if c4d.GetC4DVersion() < 21000: res[0].SetAbsPos(c4d.Vector()) 
                      op.Remove()
                      return res[0] # Returns the first item containing the object of the list.  ??? GetClone() ???
              
              def set_point_object_transform(node, transform):
                  if (not isinstance(node, c4d.PointObject) or 
                      not isinstance(transform, c4d.Matrix)):
                      msg = f"Illegal argument types: {type(node)}{type(transform)}"
                      raise TypeError(msg)
              
                  mg = node.GetMg()
                  # Move the points in the global frame and then into the new frame.
                  points = [p * mg * ~transform for p in node.GetAllPoints()]
                  # Set the points and stuff ;)
                  node.SetAllPoints(points)
                  node.Message(c4d.MSG_UPDATE)
                  node.SetMg(transform)
              
              def joinmanagment(n):
                  # n "Axis" null will be not alive in a few steps get everything we need from it
                  if n.GetUp() :
                      parent = n.GetUp()
                  else:
                      print ("No Parent To Axis Null. Probably not save to run this sript anyway.")
                      c4d.StatusClear()
                      c4d.StatusSetText ('No Parent found! - Probalby mo CAD import Doc. Script Stopped.')
                      exit()
                      return False
              
                  parentmg = n.GetMg()
                  newobject = JoinCommand(doc, n) # combine the poly objects
              
                  if not newobject.IsAlive():
                      raise TypeError("Object is not alive.")
                      return False
              
                  newobject.SetName(str(parent.GetName()))
                  newobject.InsertUnder(parent)
                  
                  #node, transform = nodes[0], nodes[1].GetMg()
                  set_point_object_transform(newobject, parentmg)
              
              def main():
                  c4d.CallCommand(13957) # Konsole löschen
                  doc = GetActiveDocument()
                  op = doc.GetFirstObject()
              
                  c4d.StatusSetSpin()
                  all_objects, allachsen = get_all_objects(op) # get two lists
                  null_counter = len(allachsen)
              
                  if null_counter == 0: # check if found something to do.
                      c4d.StatusClear()
                      c4d.StatusSetText ('No Axis Objects found, nothing to do here.')
                      print ("No Axis Objects found, nothing to do here.")
                      exit()
              
                  counter = len(all_objects)
                  secondcounter = 0    
                  c4d.StatusSetText ('%s Objects are processed.' %(null_counter))
              
                  for n in allachsen:
                      secondcounter += 1
                      c4d.StatusSetBar(100*secondcounter/counter) #statusbar
                      if joinmanagment(n) == False:
                          break
              
                  c4d.StatusClear()
                  c4d.EventAdd() # update cinema 4d
                  print ('END OF SCRIPT')
                  return True
              
              if __name__=='__main__':
                  main()
              
              1 Reply Last reply Reply Quote 0
              • X
                x_nerve
                last edited by

                Hi:

                The merge object simply generates a new polygon object, because the merge in the modeling command cannot be used, so it can be resolved in other ways.The answer to your question is, in fact, how the connection generator works.I've written it out, but I can't generate an N-gon face.Create a new blank object, the object to be connected as a subset of the blank object, and then click the middle key of the mouse to select all blank objects and all subsets to achieve the effect of connection generator.

                import c4d
                
                #e-mail : [email protected]
                
                def main():
                    nodes = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
                    if nodes == None : return
                    Object = [i for i in nodes
                            if i.GetType() == 5100]
                
                    node = c4d.BaseObject(5100)
                    doc.InsertObject(node)
                    c4d.EventAdd()
                
                    points = []
                
                    Number_One = 0
                    while Number_One < len(Object):
                
                        for i in Object[Number_One].GetAllPoints():
                
                            n = i * Object[Number_One].GetMg()
                            points.append(n * ~node.GetMg())
                
                        Number_One = Number_One + 1
                
                    Polygons = [i.GetAllPolygons() for i in Object]
                
                    Cpolygons = Polygons[0]
                
                    Polygon_Indexe = []
                    if len(Object) > 1:
                
                        Number_Two = 0
                        while Number_Two < len(Object) - 1:
                
                            Point_Count = [ Object[0].GetPointCount() ]
                            if Number_Two > 0:
                
                                for i in Object[:-1 * (len(Object) - Number_Two )]:
                                    Point_Count.append(Point_Count[-1] + i.GetPointCount())
                
                            for i in Polygons[Number_Two  + 1]:
                
                                if str(i).count("d") == 1:
                
                                    Polygon_Indexe.append(c4d.CPolygon(i.a + Point_Count[-1] ,i.b +Point_Count[-1] ,i.c + Point_Count[-1] ,i.d + Point_Count[-1] ))
                
                                else:
                
                                    Polygon_Indexe.append(c4d.CPolygon(i.a + Point_Count[-1] ,i.b + Point_Count[-1] ,i.c +Point_Count[-1] ))
                
                            Number_Two  = Number_Two + 1
                
                    Cpolygons += Polygon_Indexe
                
                    node.ResizeObject(len(points),len(Cpolygons))
                    node.SetAllPoints(points)
                
                    for i in range(len(Cpolygons)):
                        node.SetPolygon(i,Cpolygons[i])
                
                        node.Message(c4d.MSG_UPDATE)
                        c4d.EventAdd()
                
                    node.Message(c4d.MSG_UPDATE)
                    c4d.EventAdd()
                
                if __name__=='__main__':
                    main()
                
                1 Reply Last reply Reply Quote 0
                • M
                  mogh
                  last edited by

                  @x_nerve as I understand you copy polygons and point from one object to another, which is a solution, still I do not understand why the join command would not work.

                  I get a polygon object from my join comand which is ok, it just needs to reposition on its old place with all the axis and nulls kept, and oriented as it was.

                  kind regards
                  mogh

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

                    Hi @mogh,

                    thank you @x_nerve for jumping in, we really appreciate that, but I am not quite sure that I can follow, if you think there is a bug in MCOMMAND_JOIN, we would ask you to report that in a separate thread, because as I said, I currently do not really fully grasp the problem.

                    @mogh

                    I understand your problem better now, and the direct answer you are looking for is to change line 50 in your script from:

                    points = [p * mg * ~transform for p in node.GetAllPoints()]

                    to

                    points = [p * ~transform for p in node.GetAllPoints()]

                    You have to do that, because you are operating in local coordinates in the way the rest of the script works. At least this is my current understanding of your goals. At the end of the post you will find both the modified full script and the test scene I did run your script on.

                    As a more general advice, since I saw that you did delete my docstring: You should write them for all your methods/functions/classes, because this would have told you that that specific function was operating in global space. Coding is mostly an exercise in breaking stuff down into manageable parts, and doc strings help to express that "divide and conquer" strategy, rather than viewing a script as "a single thing". Which in turn then makes revisiting or debugging your code easier.

                    Cheers,
                    Ferdinand

                    PC12991_scene.c4d

                    #!py3
                    import c4d, sys, os
                    from c4d import gui
                    from c4d.documents import GetActiveDocument
                    #Version 1.4 Striped version
                    # This Script needs Null-Objects Called "Axis" with polygon objects to run
                    
                    def GetNextObject(op):
                        if not op: return
                        if op.GetDown(): return op.GetDown()
                        while op.GetUp() and not op.GetNext():
                            op = op.GetUp()
                        return op.GetNext()
                    
                    def get_all_objects (op):
                        allachsen_list = list()
                        all_objects_list = list()
                        while op:
                            if op.GetName() == 'Achsen-Objekt' or op.GetName() == 'Axis' :
                                allachsen_list.append(op)
                            all_objects_list.append(op)
                            op = GetNextObject(op)
                        return all_objects_list, allachsen_list
                    
                    def JoinCommand(doc, op):
                        res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_JOIN,
                                                    list = [op],
                                                    mode = c4d.MODELINGCOMMANDMODE_ALL,
                                                    doc = doc)
                    
                        # Cheks if the command didn't failed
                        if res is False:
                            raise TypeError("return value of Join command is not valid")
                        elif res is True:
                            print ("Command successful. But no object.")
                        elif isinstance(res, list):
                            if c4d.GetC4DVersion() < 21000: res[0].SetAbsPos(c4d.Vector())
                            op.Remove()
                            return res[0] # Returns the first item containing the object of the list.  ??? GetClone() ???
                    
                    def set_point_object_transform(node, transform):
                        if (not isinstance(node, c4d.PointObject) or
                            not isinstance(transform, c4d.Matrix)):
                            msg = f"Illegal argument types: {type(node)}{type(transform)}"
                            raise TypeError(msg)
                        
                        print ("sp:", node, transform)
                        mg = node.GetMg()
                        # Move the points in the global frame and then into the new frame.
                        points = [p * ~transform for p in node.GetAllPoints()]
                        # Set the points and stuff ;)
                        node.SetAllPoints(points)
                        node.Message(c4d.MSG_UPDATE)
                        node.SetMg(transform)
                    
                    def joinmanagment(n):
                        # n "Axis" null will be not alive in a few steps get everything we need from it
                        if n.GetUp() :
                            parent = n.GetUp()
                        else:
                            print ("No Parent To Axis Null. Probably not save to run this sript anyway.")
                            c4d.StatusClear()
                            c4d.StatusSetText ('No Parent found! - Probalby mo CAD import Doc. Script Stopped.')
                            exit()
                            return False
                    
                        parentmg = n.GetMg()
                    
                        newobject = JoinCommand(doc, n) # combine the poly objects
                    
                        if not newobject.IsAlive():
                            raise TypeError("Object is not alive.")
                            return False
                    
                        newobject.SetName(str(parent.GetName()))
                        newobject.InsertUnder(parent)
                        newobject.SetMg(newobject.GetMl())
                        # node, transform = nodes[0], nodes[1].GetMg()(add QA contact)
                        set_point_object_transform(newobject, parentmg)
                    
                    def main():
                        c4d.CallCommand(13957) # Konsole löschen
                        doc = GetActiveDocument()
                        op = doc.GetFirstObject()
                    
                        c4d.StatusSetSpin()
                        all_objects, allachsen = get_all_objects(op) # get two lists
                        null_counter = len(allachsen)
                    
                        if null_counter == 0: # check if found something to do.
                            c4d.StatusClear()
                            c4d.StatusSetText ('No Axis Objects found, nothing to do here.')
                            print ("No Axis Objects found, nothing to do here.")
                            exit()
                    
                        counter = len(all_objects)
                        secondcounter = 0
                        c4d.StatusSetText ('%s Objects are processed.' %(null_counter))
                    
                        for n in allachsen:
                            secondcounter += 1
                            c4d.StatusSetBar(100*secondcounter/counter) #statusbar
                            if joinmanagment(n) == False:
                                break
                    
                        c4d.StatusClear()
                        c4d.EventAdd() # update cinema 4d
                        print ('END OF SCRIPT')
                        return True
                    
                    if __name__=='__main__':
                        main()
                    

                    MAXON SDK Specialist
                    developers.maxon.net

                    1 Reply Last reply Reply Quote 0
                    • M
                      mogh
                      last edited by

                      @zipit thank you very much - position and rotation seem to be working now. (staying put)

                      another problem ocured though "NormalTag" information seems to flip and maually flipping back does not help ... so all the polygons are "shaded" weird now. --- the callcomand "connect and delete" which I want to replace because of speed keeps them intact.

                      Should I open a new thread or leafe the problem open ?

                      regarding docstring: i just wanted the code to be slim so people can read it fast without scrolling its still in my lengthy script. Will keep it next time.

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

                        Hi,

                        we would appreciate it if you would open a new thread for that. As a quick tip, aside from using SMC, inverting the normal of a polygon just means inverting its point index order. I have shown it for example here. But this can come with multiple problems, especially regarding texture coordinates. I think have dealt with problem here on the forum multiple times (including dealing with secondary mesh attributes like texture coordinates). If any questions remain, please feel free to open a new thread.

                        About the doc strings: In the end you should do what you are comfortable with. It was just a tip and a little insight on why many people find it useful.

                        Cheers,
                        Ferdinand

                        MAXON SDK Specialist
                        developers.maxon.net

                        1 Reply Last reply Reply Quote 0
                        • X
                          x_nerve
                          last edited by x_nerve

                          Hi:

                          @mogh

                          The MCOMMAND_JOIN modeling command is now ready to execute properly.The merged object cannot be directly visible in the document, so the merged object must be inserted into the document.Also, the global matrix of the merged object is the same as the first object in the parent-child list.I also got it from the forum.

                          import c4d
                          from c4d import utils
                          
                          #e-mail : [email protected]
                          
                          def main():
                          
                              nodes = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
                              if nodes == None : return
                              Objects = [i for i in nodes
                                      if i.GetType() == 5100]
                          
                              settings = c4d.BaseContainer()
                              settings[c4d.MDATA_JOIN_MERGE_SELTAGS] = True
                              
                              result = c4d.utils.SendModelingCommand(
                                  c4d.MCOMMAND_JOIN, Objects ,
                                  c4d.MODELINGCOMMANDMODE_ALL, settings, doc)
                          
                              doc.InsertObject(result[0])
                              c4d.EventAdd()
                              
                              points = []
                          
                              Number_One = 0
                              while Number_One < len(Objects):
                          
                                  for i in Objects[Number_One].GetAllPoints():
                          
                                      n = i * Objects[Number_One].GetMg()
                                      points.append(n * ~result[0].GetMg())
                          
                                  Number_One = Number_One + 1
                          
                              result[0].SetAllPoints(points)
                          
                              result[0].Message(c4d.MSG_UPDATE)
                              c4d.EventAdd()
                              
                              print (result[0])
                          
                          if __name__=='__main__':
                              main()
                          
                          
                          1 Reply Last reply Reply Quote 0
                          • ferdinandF
                            ferdinand
                            last edited by

                            Hi @mogh,

                            I initially did not want to make things super complicated here, which is why I did a bit work around the oddities of your script in my answer. Given the other threads of yours which deal with that project, it seems advisable to point out that a cleaner version of your script could look like the script attached at the end. The doc string will tell you about the pro's and con's. You can also try uncommenting the c4d.EventAdd in line 155 to see if you can get Cinema more into sync (regarding https://developers.maxon.net/forum/topic/13007).

                            Cheers,
                            Ferdinand

                            """Demonstrates how to handle MCOMMAND_JOIN while keeping things in place.
                            
                            You can also use the old version, but there you would have to deal with the
                            normal tags. This version mostly just rectifies some minor oddities in your
                            original code. This version does not need to transform the vertices, but 
                            instead places the new object by the inverse transform of its former parent
                            (see line 123).
                            
                            Things get much more complicated (and computationally expensive) when you use 
                            the old version, since you then have to also modify the normals. The 
                            disadvantage  of this version is that the axis placement is not "as nice".
                            
                            See https://developers.maxon.net/forum/topic/13004/ for how to deal with the normal
                            tags.
                            """
                            
                            import c4d
                            
                            
                            def get_nodes_by_name(doc, patterns):
                                """Returns all nodes in a document whose name appears in patterns.
                            
                                Note:
                                    Despite its name, the strings in `patterns` are not evaluated as
                                    regular expressions at the moment. In fact this function does not
                                    ensure that the elements in patters are strings at all.
                            
                                Args:
                                    doc (c4d.documents.BaseDocument): The document to get the nodes from.
                                    patterns (list[str]): The name patterns to get the nodes for.
                            
                                Returns:
                                    list[c4d.BaseObjeect]: The nodes in `doc` which match patterns.
                            
                                Raises:
                                    TypeError: On invalid argument types.
                                """
                                # Validate arguments.
                                if not isinstance(doc, c4d.documents.BaseDocument):
                                    msg = f"Expected BaseDocument for `doc`. Received: {type(doc)}."
                                    raise TypeError(msg)
                                if not isinstance(patterns, list):
                                    msg = f"Expected list for `patterns`. Received: {type(patterns)}."
                                    raise TypeError(msg)
                            
                                # Get all relevant nodes.
                                result, visited = [], []
                                node = doc.GetFirstObject()
                                while node is not None:
                                    # Append new nodes to the visited and result lists.
                                    if node not in visited:
                                        visited.append(node)
                                    if node.GetName() in patterns and node not in result:
                                        result.append(node)
                                    # Traverse the scene graph depth first.
                                    node_down = node.GetDown()
                                    if node_down and node_down not in visited:
                                        node = node_down
                                    elif node.GetNext():
                                        node = node.GetNext()
                                    else:
                                        node = node.GetUp()
                                return result
                            
                            
                            def join_with_children(node, merge_selection_tags=True):
                                """Joins the children of `node` into a single object.
                            
                                Also adjusts the transform of the resulting node and removes the old
                                geometry and inserts the new one. 
                            
                                Args:
                                    node (c4d.BaseObject): The node to join the children for.
                                    merge_selection_tags (bool, optional): If to also merge selections.
                                     Defaults to `True`.
                            
                                Returns:
                                    bool: If the operation was successful.
                            
                                Raises:
                                    RuntimeError: When SMC failed.
                                    TypeError: On invalid argument types.
                                    ValueError: When `node` is not attached to a document.
                                """
                                # Validate arguments.
                                if not isinstance(node, c4d.BaseObject):
                                    msg = f"Expected BaseObject for `node`. Received: {type(node)}"
                                    raise TypeError(msg)
                            
                                doc = node.GetDocument()
                                if doc is None:
                                    msg = f"Node is not attached to a document: {node}."
                                    raise ValueError(msg)
                            
                                # A special condition of yours I am just mimicking here.
                                parent = node.GetUp()
                                if parent is None:
                                    return
                            
                                # Run SMC
                                smc_data = c4d.BaseContainer()
                                smc_data[c4d.MDATA_JOIN_MERGE_SELTAGS] = merge_selection_tags
                                result = c4d.utils.SendModelingCommand(
                                    command=c4d.MCOMMAND_JOIN,
                                    list=[node],
                                    mode=c4d.MODELINGCOMMANDMODE_ALL,
                                    bc=smc_data,
                                    doc=doc,
                                    flags=c4d.MODELINGCOMMANDFLAGS_NONE)
                            
                                # Evaluate SMC output.
                                if (not isinstance(result, list) or
                                    len(result) == 0 or
                                        not isinstance(result[0], c4d.BaseObject)):
                                    msg = f"SendModelingCommand failed on: {node}"
                                    raise RuntimeError(msg)
                            
                                # This didn't not make much sense, since you did overwrite the transform
                                # of result[0] on a later point anyways.
                                # if c4d.GetC4DVersion() < 21000:
                                #     result[0].SetAbsPos(c4d.Vector())
                            
                                # Replace the old with the new geometry.
                                result = result[0]
                                # Your renaming does not make much sense to me, since every new node
                                # will then just be named after its parent. i.e. "CADimport_XXX", feel
                                # free to uncomment, if this was intended.
                                # result.SetName(parent.GetName())
                                result.SetMg(~parent.GetMg())
                                result.InsertUnder(parent)
                                node.Remove()
                            
                            
                            def main():
                                """Does the whole CAD cleanup thing.
                                """
                                # The name patterns for the nodes to collapse.
                                patterns = ["Achsen-Objekt", "Axis"]
                                # Get all nodes that match that pattern.
                                targets = get_nodes_by_name(doc, patterns)
                                count = len(targets)
                            
                                c4d.StatusSetSpin()
                                # Loop over these nodes.
                                #
                                # Your approach did not respect the special case when an `axis` object
                                # is a direct child of another `axis` object. Which will lead to
                                # incorrect results, since you (and I here) traverse the scene graph
                                # top-down-depth-first. I did not address this to keep the code relatively
                                # familiar.
                                for i, node in enumerate(targets):
                                    # Join one of the targets and do some mild interface indication.
                                    join_with_children(node)
                                    # Uncomment to let Cinema update for each modification of the scene.
                                    # c4d.EventAdd()
                                    c4d.StatusSetText(f"{i}/{count} groups joined.")
                                # Clean up after ourselves.
                                c4d.StatusSetText("")
                                c4d.StatusClear()
                                c4d.EventAdd()
                            
                            
                            if __name__ == '__main__':
                                main()
                            

                            MAXON SDK Specialist
                            developers.maxon.net

                            1 Reply Last reply Reply Quote 1
                            • M
                              mogh
                              last edited by

                              I am building of your script from here on. Thank You.

                              FYI zipit:
                              While your def get_nodes_by_name(doc, patterns) might be saver and more sound it takes 10x longer then my version to collect all axis ... (which is not the slow party of the script and could be neglegted - I was just buffled by the profiling i ran)

                              kind regards
                              mogh

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

                                Hi,

                                ten times slower sounds rough. It is probably because I was a bit lazy and used a lookup table (visited) to traverse the scene-graph, which is a rather expensive thing to do. Feel free to use the fixed traversal if performance becomes an issue here.

                                Cheers,
                                Ferdinand

                                MAXON SDK Specialist
                                developers.maxon.net

                                1 Reply Last reply Reply Quote 0
                                • M mogh referenced this topic on
                                • First post
                                  Last post