Get World Position of Obj A's position and Set it to Obj B's position



  • Hi,

    I want to get the world position of Obj A's position and set it into the world position of Obj B's positions. As a background, I cannot directly constraint Obj A and Obj B since they have different pivot axis.

    The Get World Position is okay. Thanks to this thread.
    I'm having trouble with the Set World Position. I initially thought that I can get it by reverse engineer in the previous code but I get errors.

    Specifically, TypeError: unsupported operand type(s) for /: 'c4d.Vector' and 'c4d.Matrix' in the GlobalToLocal function.
    which make sense, but, uhm, I really don't know how to solve it.

    Here is the code so far

    import c4d
    
    # Declare object variables
    newObject = doc.SearchObject("newObj")
    oldObject = doc.SearchObject("oldObj")
    totalPoints = len(newObject.GetAllPoints())
    
    '''
    Get the new position
    '''
    
    def LocalToGlobal(obj, local_pos):
        """
        Returns a point in local coordinate in global space.
        """
        obj_mg = obj.GetMg()
        return obj_mg * local_pos
    
    def GetPointGlobal(point_object, point_index):
        """
        Return the position of a point in Global Space
        """
        ppos = point_object.GetPoint(point_index)  # Get the point in local coords
        return LocalToGlobal(point_object, ppos)  # Return the point in global space
    
    # Get the new world position
    newPos = []
    
    for idx in range(totalPoints):
        newPos.append(GetPointGlobal(newObject, idx))
    
    '''
    Assign the new position
    '''
    # Set the new world position
    
    
    def GlobalToLocal(obj, global_pos):
        """
        Returns a point in local coordinate in global space.
        """
        obj_mg = obj.GetMg()
        return (global_pos / obj_mg)
    
    newLocalPos = []
    
    for idx in newPos:
        newPos.append(GlobalToLocal(oldObject, idx))
    print (newLocalPos)
    
    oldObject.SetAllPoints(newLocalPos)
    oldObject.Message(c4d.MSG_UPDATE)
    
    c4d.EventAdd()
    

    Thank you for looking at my problem.



  • You can't divide by a matrix, you need to multiply by the inverted matrix (unary prefix operator ~).
    Note that the multiplication in matrix calculations is not commutative.



  • @Cairyn

    Thanks for the response. For some reason, C4D freezes. I guess it should not since I only have 8 points. I'm doing it with a simple cube.

    I tried both of these codes

    return (global_pos * (-1*obj_mg))
    or
    return (~obj_mg * global_pos)

    Should I modify other parts of my code?



  • Hi @Bentraje,

    Before providing a more complete answer, we were not sure of your final goal.
    You want to define the axis of obj A to axis of obj B but the point shouldn't move in the global space, is it correct?

    Cheers,
    Maxime.



  • @m_adam

    Sorry for the confusion.
    I'm not after the axis of Obj A and Obj B. I just want them to stay as it. I'm after the world position of Object of A's points and directly plug it into Object B's points.

    For better understanding,
    please see a simple illustration image here:
    https://www.dropbox.com/s/v3fxji8z1ct536c/c4d079_get_set_world_position.jpg?dl=0

    If you need more details, please let me know. Thank you.



  • So regarding your script why it crashes, it's simply because you make an infinite loop with this code since you add each time something more to iterate.

    for idx in newPos:
        newPos.append(GlobalToLocal(oldObject, idx))
    

    Just to make it cleaner I've called objA and objB.
    Then the needed steps are:

    1. Get World Position from points of objB.
    2. Convert these World Position in the local space of objA.

    So as you may be already aware regarding our Matrix Fundamentals where is how to achieve both operations.

    • Local Position to World Position corresponds to the obj global matrix where the position is locals to multiplied by the local position.
    • World Position to Local Position corresponds to the inverse obj global matrix where the position will be locals multiplied by the world position.

    Finally, before providing with the solution I would like to remind you to please always execute your code in the main function. The main reason is that if you transform your script as a button in the Dialog, every code that is in the global scope of your script is executed for each redraw of the Cinema 4D GUI (which is pretty intensive!). Moreover please always check for objects, if they exist or not try to adopt a defensive programming style in order to avoid any issues.

    import c4d
    
    def main():
        # Declares object variables
        ObjA = doc.SearchObject("A")
        ObjB = doc.SearchObject("B")
    
        # Checks if objects are found
        if ObjA is None or ObjB is None:
            raise ValueError("Can't found a and b object")
    
        # Checks if they are polygon object
        if not isinstance(ObjA, c4d.PolygonObject) or not isinstance(ObjB, c4d.PolygonObject):
            raise TypeError("Objects are not PolygonObject")
    
        # Checks Point count is the same for both obj
        allPtsObjA = ObjA.GetAllPoints()
        allPtsObjB = ObjB.GetAllPoints()
        if len(allPtsObjA) != len(allPtsObjB):
            raise ValueError("Object does not get the same pount count")
    
        # Retrieves all points of B in World Position, by multipling each local position of ObjB.GetAllPoints() by ObjB.GetMg()
        allPtsObjBWorld = [pt * ObjB.GetMg() for pt in allPtsObjB]
    
        # Retrieves All points of B from World to Local Position of ObjA, by multipling each world position of ObjB by the inverse objA.GetMg()
        allPtsObjANewLocal = [pt * ~ObjA.GetMg() for pt in allPtsObjBWorld]
        
        # Sets New points position
        ObjA.SetAllPoints(allPtsObjANewLocal)
        ObjA.Message(c4d.MSG_UPDATE)
        
        # Updates Cinema 4D
        c4d.EventAdd()
    
    # Execute main()
    if __name__=='__main__':
        main()
    

    If you have any questions, please let me know.
    Cheers,
    Maxime.



  • @m_adam

    Thanks for the code. The script works as expected. It's also cool that you managed to bring it to essentially three lines of codes with the list comprehension.

    RE: matrix fundamentals
    I was able to read that page but I kinda passed it thinking that Going Backwards section SetMl(ml) and SetMg(mg) is only applicable to the object level and not on the component level.

    RE: main function
    Just wondering does it have to be in a main function (like literally the name of the function is main) or just inside the if __name__=='__main__':? Does C4D places special treatment to the word "main"?

    I was confused at that previously and later realized that if __name__=='__main__': is not specific to C4D but used in python in general in checking if the script is referenced to other script, which I don't do at the moment. So I just skip it.

    So would this code be just fine?

    if __name__=='__main__':
        executeMe()
    


  • @bentraje said in Get World Position of Obj A's position and Set it to Obj B's position:

    RE: main function
    Just wondering does it have to be in a main function (like literally the name of the function is main) or just inside the if __name__=='__main__':? Does C4D places special treatment to the word "main"?

    I was confused at that previously and later realized that if __name__=='__main__': is not specific to C4D but used in python in general in checking if the script is referenced to other script, which I don't do at the moment. So I just skip it.

    So would this code be just fine?

    if __name__=='__main__':
        executeMe()
    

    Yes this is correct.


Log in to reply