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

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

    Cinema 4D SDK
    r20 python
    3
    8
    2.5k
    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.
    • B
      bentraje
      last edited by bentraje

      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.

      1 Reply Last reply Reply Quote 0
      • CairynC
        Cairyn
        last edited by

        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.

        1 Reply Last reply Reply Quote 2
        • B
          bentraje
          last edited by

          @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?

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

            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.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            1 Reply Last reply Reply Quote 0
            • B
              bentraje
              last edited by

              @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.

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

                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.

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                1 Reply Last reply Reply Quote 4
                • B
                  bentraje
                  last edited by

                  @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()
                  
                  M 1 Reply Last reply Reply Quote 0
                  • M
                    m_adam @bentraje
                    last edited by

                    @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.

                    MAXON SDK Specialist

                    Development Blog, MAXON Registered Developer

                    1 Reply Last reply Reply Quote 3
                    • First post
                      Last post