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

    Set Vector Y to 0 (in World Space)

    Cinema 4D SDK
    r20 python
    3
    4
    735
    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

      Hi,

      I understand retrieving world space has been discussed before like in this thread (https://developers.maxon.net/forum/topic/11586/get-deformed-points-from-skin-deformer)

      Sorry for asking the questions, but I can't seem to set the Vector Y of the points to 0 (in world space). Basically, I'm mimicking the "Set Point Value" command.

      You can see an illustration of the problem here:
      https://www.dropbox.com/s/v7ty3z52hh0ie89/c4d103_set_y_vector_to_world_zero.png?dl=0

      For the illustration file, you can check it here:
      https://www.dropbox.com/s/takdka6wkj4jw71/c4d103_set_y_vector_to_world_zero.c4d?dl=0

      The code used is as follow:

      import c4d
      
      def set_Y_vector(obj, value):
      
          oldPoints = obj.GetAllPoints()
          obj_mat = obj.GetMg()
          inv_obj_mat = ~obj_mat # Not used
      
          newLocalPoints = [c4d.Vector(p[0], p[1]*value, p[2]) for p in oldPoints]
          
          # The zero Y value in newLocalPoints is in local space. So I'm trying to convert  it on global space below
          
          newWorldPoints = [p * obj_mat for p in newLocalPoints]
      
          obj.SetAllPoints(newWorldPoints)
      
          obj.Message (c4d.MSG_UPDATE) 
          c4d.EventAdd()
          
      set_Y_vector(op, 0)
      

      Is there a way around this?

      Thank you for looking at the problem.

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

        Without actually trying the code:

        1. newLocalPoints is in local space. This is where you zero the y value. Which means that the point will now have a y of 0 in local space.

        2. Then you multiply the world matrix with the new positions. BUT: This world matrix is already part of the object that contains the points. Now your points have applied the matrix twice, first through the object and then through this multiplication.

        3. While the local y is 0, the multiplication with obj_mat will give the point a global y anyway because obj_mat may contain a translation in space. Moreover, the obj itself does, too. So, your point's y will definitely not be world 0 in the end.

        What you really need to do:

        1. Determine the world vector for the points (it's only a vector and not a matrix since a point does not contain a rotation or scale). This is done by applying obj_mat to each point vector, resulting in a new vector for each point. Why? Because your object's transformation in space (which is the meaning of obj_mat) is affecting all points in the object's local system.

        2. For this global vector, you set the y component to 0.

        3. Then you transform this global vector back to the object's local space by applying inv_obj_mat. Note that after this inverse transformation, the local y may no longer be 0!

        4. Profit.

        Okay, I wrote the script after all. You owe me a beer.

        import c4d
        from c4d import gui
        
        def set_Y_vector(obj, value):
            oldPoints = obj.GetAllPoints()
            obj_mat = obj.GetMg()
            inv_obj_mat = ~obj_mat
        
            newWorldPoints = [p * obj_mat for p in oldPoints]
            newLocalPoints = [inv_obj_mat * c4d.Vector(p[0], p[1]*value, p[2]) for p in newWorldPoints]
        
            obj.SetAllPoints(newLocalPoints)
            obj.Message (c4d.MSG_UPDATE) 
            c4d.EventAdd()
            
        
        def main():
            set_Y_vector(op, 0)
        
        if __name__=='__main__':
            main()
        
        1 Reply Last reply Reply Quote 3
        • r_giganteR
          r_gigante
          last edited by r_gigante

          Hi Bentraje, thanks for reaching out us.

          With regard to your request, I second all the considerations that @Cairyn kindly provided reminding once again that points positions are always stored in local space and must be always defined in local space to unpredictable results due to transformation matrices being applied twice.

          On top of this, looking at your snippet, if your intent is to locate the spline points Y-coordinate at a certain value in global space, I'd refrain from using the multiplication but rather use the passed value directly as new value.

          Last but not least, I've optimized the code with one single for-loop

          def set_Y_vector(obj, value):
          
              oldLocalPoints = obj.GetAllPoints()
              obj_mat = obj.GetMg()
              inv_obj_mat = ~obj_mat
          
              # Non-preallocation penalty is neglectable 
              newLocalPoints = [] 
              for p in oldLocalPoints:
                  # get the world position of the current point
                  worldpoint = obj_mat * p
                 # set the local position on the new point by setting the y-coordinate to the desired value and transform by the inverted matrix
                  newLocalPoint = inv_obj_mat * c4d.Vector(worldpoint[0], value, worldpoint[2] )
                  # just append
                  newLocalPoints.append(newLocalPoint)
          
              # set the new points
              obj.SetAllPoints(newLocalPoints)
              
              # notify Cinema 
              obj.Message (c4d.MSG_UPDATE) 
              c4d.EventAdd()
          

          Best, Riccardo

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

            @Cairyn and @r_gigante

            RE: BUT: This world matrix is already part of the object that contains the points. Now your points have applied the matrix twice, first through the object and then through this multiplication.

            Thanks for the clarification.

            Works as expected. (And I agree, I owe you a beer Cairyn hehehe).

            Have a great day ahead!

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