Move just the axis of a polygonal object



  • I have a list with all the coordinates of the vertexes of a polygonal object, all in global coordinates, that I got with:

    op_mg = child.GetMg()
    points = op.GetAllPoints()
                    
    for p,pt in enumerate(points) :
        points[p]=pt*op_mg
    

    After setting the global matrix of my polygonal object to the matrix that I want its axis to be, now I want to relocate all the points the location where they were previously (globally).
    But I want it to be able to happen, no matter how deep inside a hierarchy my polygonal object is.
    Meaning... I want to be able to relocate the axis of my polygonal object to a specific global location, even if my object is inside a hierarchy, but I want to keep all the vertexes in their initial global location.
    How can I do this?!?
    I have been working with matrixes but no matter what I do, the points of the object always end up moving.



  • It's actually the axis and the points that have to move.



  • Hello @rui_mac

    you don't really need to deal with hierarchy as long as you are using global space.
    you have to come back to local space with the new matrix, by multiplying the points position with the inverse matrix.
    Be sure to also move the axis of the object as @mp5gosu stated and to ask the object to update itself.

            # Retrieves selected object
            op = doc.GetActiveObject()
            if op is None:
                raise TypeError("There's no object selected.")
        
            # Retrieves the matrix of the object
            opmg = op.GetMg()
        
            # Retrieves the point in global space
            pointsGlob = [p * opmg for p in op.GetAllPoints()]
        
            # Starts the undo process (the initial scene to be restored after an undo action)
            doc.StartUndo()
        
            # Notifies a change on the obj
            doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
        
            # Updates the matrix
            opmg.off = c4d.Vector(0)
            # Updates the axis of the object
            op.SetMg(opmg)
        
            # Notifies the object, that internal data changed and it needs to be updated
            op.Message(c4d.MSG_UPDATE)
        
            # Inverses the matrix to come back to local space
            invopmg = ~opmg
        
            # Retrieves points from global to local space
            newPoints = [p * invopmg for p in pointsGlob]
        
            # Updates the points
            doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
            op.SetAllPoints(newPoints)
        
            # Ends the undo process (the final scene state)
            doc.EndUndo()
        
            # Updates Cinema 4D
            c4d.EventAdd()
        
    

    Cheers
    Manuel



  • That is what I was trying to do, but it is still not working.

    Lets assume I have a list to objects that are the objects that are all children of a Null. The objects can be children or childs of childs, or even deeper, but they all are inside a Null.
    I want all the geometry of those children to remain in the same spacial location but I wan all the axis to move to the location of the Null (no matter how deep the objects are in the hierarchy.
    My current code is as follows:

    main_mg = op.GetMg() # the global matrix of the parent Null
    
    # op_list is a list that contains all the childs of the Null.
    # Some of them are childs of childs or even deeper.
    
    doc.StartUndo()
    
    for op in op_list:
    
        child_mg = op.GetMg()
        pointsG = [p * child_mg for p in op.GetAllPoints()]
        
        doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
    
        op.SetMg(main_mg)
        op.Message(c4d.MSG_UPDATE)
        inv_mg = ~child_mg
        nPoints = [p * inv_mg for p in pointsG]
        
        doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
        op.SetAllPoints(nPoints)
        
    doc.EndUndo()
    c4d.EventAdd()
    
    

    But it is not working!!
    All the geometry still moves.



  • @rui_mac said in Move just the axis of a polygonal object:

    inv_mg = ~child_mg

    Be careful about the matrix you are using as reference to go from global to local.
    The object new matrix now is the same as the null. If you are using the old one (child_mg), the points will move the same way you moved the axis. But if you are using the new one, the points be changed to global to local and in fact will not move. (and that's the goal)

    inv_mg = ~main_mg
    

    forget about the hierarchy in that case.
    It's just matrix operation local to global and global to local.

    @m_adam share his code, he think that could help you in your project.

    Let me know if it's still not clear.

    Cheers
    Manuel



  • Thank you so much. It is clearer now.
    It works!!



  • Spoke too soon.
    It works, but not for childs of childs of childs...
    This is my current code:

    import c4d
    
    op_list=[]
    
    def Get_Op(op):
        global op_list
    
        while (op):
            op_list.append(op)
            Get_Op(op.GetDown())
            op = op.GetNext()
        return None
    
    
    # Main function
    def main():
        global op_list
    
        selected = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_0)
        if len(selected)!=1: return
        op = selected[0]
        
        main_mg = op.GetMg()
    
        child = op.GetDown()
        op_list = []
    
        if child != None:
            Get_Op(child)
            
            if len(op_list)!= 0:
    
                doc.StartUndo()
    
                for op in op_list:
                    
                    if op.GetType()==c4d.Opolygon:
    
                        child_mg = op.GetMg()
                        pointsGlob = [p * child_mg for p in op.GetAllPoints()]
    
                        doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
                        op.SetMg(main_mg)
                        op.Message(c4d.MSG_UPDATE)
                        
                        inv_mg = ~main_mg
                        newPoints = [p * inv_mg for p in pointsGlob]
    
                        doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
                        op.SetAllPoints(newPoints)
                        op.Message(c4d.MSG_UPDATE)
    
                doc.EndUndo()
    
                c4d.EventAdd()
    
    # Execute main()
    if __name__=='__main__':
        main()
    

    And it doesn't work with the Cap1 and Cap2 objects, in this hierarchy:

    alt text

    Everything remains in the same location, with the axis moved to the location of the outermost parent Null, except the Cap1 and Cap2 objects, as their geometry changes location.



  • after trying several solutions, i've change your function with this one for non recursive iteration. and using the same idea to local to global and global to local but with the child "locoal" matrix. (the one you get with GetMl)

    import c4d
    
    def GetNextObject(op):
        # Non recursive hierarchy iteration
        if op==None:
            return None
      
        if op.GetDown():
            return op.GetDown()
      
        while not op.GetNext() and op.GetUp():
            op = op.GetUp()
      
        return op.GetNext()
        
        
    def UpdateObject (op, newpos):
        # Update the object by moving it to the new position 
        # and moving back the points to their old position
    
        # Store the old matrix of the object
        old_mg = op.GetMg()
    
        # Retrieve the points position in global space
        pointsGlob = [p * old_mg for p in op.GetAllPoints()]
    
        # Add an Undo state to the document
        doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
    
        # update the object position matrix    
        op.SetMg(newpos)
    
        # Inverses the matrix to come back to local space
        inv_mg = ~newpos
    
        # Retrieves points from global to local space
        newPoints = [p * inv_mg for p in pointsGlob]
    
        # Add an Undo state to the document
        doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
    
         # Updates the points
        op.SetAllPoints(newPoints)
    
        # Notifies a change on the obj
        op.Message(c4d.MSG_UPDATE)
    
    def UpdateChildsMatrix(op, oldMg, newMg):
        # Update the Childrend Matrix after the parent have been moved
    
        op = op.GetDown()
        # For All child of the object
        while op:
            # Store the local matrix to a global space with the old matrix object
            newChildMg = oldMg * op.GetMl()
    
            # Calculate the new local matrix of the object.
            newChildMg =  ~newMg * newChildMg
    
            # Set the new local matrix of the object        
            op.SetMl(newChildMg)
    
            # Notifies a change on the obj
            op.Message(c4d.MSG_UPDATE)    
    
            # Get the next Object if any
            op = op.GetNext()
        
        
    
    
    # Main function
    def main():
        # Get all Selected Object
        selected = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE)
    
        if len(selected) != 1:
            return
    
        mainOp = selected[0]
    
        op_list = []
    
        # Get the first object if any
        nextObj = GetNextObject(mainOp)
        while nextObj:
            # Add all object found in the array
            op_list.append(nextObj)
            # Retreive the next Object
            nextObj = GetNextObject(nextObj)
    
        # Retreive the global position of the main object
        main_mg = mainOp.GetMg()
    
        # if there's no child, just leave
        if len(op_list) == 0:
            return
    
        # Starts the undo process (the initial scene to be restored after an undo action)
        doc.StartUndo()
    
        for op in op_list:
            # Check if it's a polygon Object
            if op.GetType()==c4d.Opolygon:
                #save the matrix to have a chance to update the child
                savedMatrix = op.GetMg()
    
                # Move the object to the new location without moving the points
                UpdateObject(op,main_mg)
    
                # Because we moved the parent, we should move back the children.
                UpdateChildsMatrix(op, savedMatrix, main_mg)
                        
                
    
        # Ends the undo process (the final scene state)
        doc.EndUndo()
        
        # Updates Cinema 4D
        c4d.EventAdd()
    
    # Execute main()
    if __name__=='__main__':
        main()
    

    Cheers
    Manuel



  • Thank you so much, Manuel.
    It was, mainly, that children matrix update that was missing.
    Now, it works fine ☺



  • hi,

    when you will be sure that it's solved, don't forget to mark this thread as solved please.

    Cheers
    Manuel