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

    Dynamic HandleInfo.direction

    Cinema 4D SDK
    python
    3
    6
    936
    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.
    • mfersaouiM
      mfersaoui
      last edited by mfersaoui

      Hello,

      I'm searching if there a method to dynamically change the HandleInfo.direction depend on the rotation and pitch value of my object. I created the following object plugin example to illustrate exactly what I want to do.

      Screen video:
      dynamic_handleInfo.direction.gif

      pyp file:

      import c4d, os
      from c4d import utils, DescID, BaseObject, bitmaps, plugins, Vector, HandleInfo
      from math import radians
      
      PLUGIN_ID = 1055615
      
      class Osobject(plugins.ObjectData):
      
          def Init(self, op):
      
              self.fs_mg = c4d.Matrix()
              self.head_obj_h = 36
      
              data = op.GetDataInstance()
      
              data.SetFloat(c4d.OSOBJECT_SIZEX, 60.0)
              data.SetFloat(c4d.OSOBJECT_SIZEY, 50.0)
              data.SetFloat(c4d.OSOBJECT_DEPTH, 50.0)
              data.SetFloat(c4d.OSOBJECT_PITCH, radians(-30))
              data.SetFloat(c4d.OSOBJECT_ROTATION, radians(90))
      
              return True
      
          def GetVirtualObjects(self, op, hh):
              dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTY_MATRIX | c4d.DIRTY_DATA)
      
              if not dirty:
                  return op.GetCache(hh)
      
              data = op.GetDataInstance()
      
              size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
              size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
              depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
              pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
              rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
      
              container = BaseObject(c4d.Onull)
              container.SetRelPos(Vector(0, 40, 0))
      
              bar = BaseObject(c4d.Ocylinder)
              bar.SetName("bar")
              bar.InsertUnder(container)
              bar.SetParameter(DescID(c4d.PRIM_CYLINDER_RADIUS), 1.5, c4d.DESCFLAGS_GET_0)
              bar.SetParameter(DescID(c4d.PRIM_CYLINDER_HEIGHT), 40, c4d.DESCFLAGS_GET_0)
              bar.SetParameter(DescID(c4d.PRIM_AXIS), 2, c4d.DESCFLAGS_GET_0)
              bar.SetRelPos(Vector(0, -20, 0))
      
              # Rotation Object
              rot_obj = BaseObject(c4d.Onull)
              rot_obj.SetRelRot(Vector(rotation, 0, 0))
              rot_obj.SetName("rotation")
              rot_obj.InsertUnder(container)
      
              # Pith Object
              pitch_obj = BaseObject(c4d.Onull)
              pitch_obj.SetRelRot(Vector(0, pitch, 0))
              pitch_obj.SetName("pitch")
              pitch_obj.InsertUnder(rot_obj)
      
              # Head Container
              head_container = BaseObject(c4d.Onull)
              head_container.SetName("head_container")
              head_container.InsertUnder(pitch_obj)
      
              # Head Object
              head_obj = BaseObject(c4d.Ocylinder)
              head_obj.SetName("head")
              head_obj.InsertUnder(head_container)
      
              head_obj.SetParameter(DescID(c4d.PRIM_CYLINDER_RADIUS), 10, c4d.DESCFLAGS_GET_0)
              head_obj.SetParameter(DescID(c4d.PRIM_CYLINDER_HEIGHT), self.head_obj_h, c4d.DESCFLAGS_GET_0)
              head_obj.SetParameter(DescID(c4d.PRIM_AXIS), 5, c4d.DESCFLAGS_GET_0)
      
              # depth Object
              depth_obj = BaseObject(c4d.Ocylinder)
              depth_obj.SetName("cyl")
              depth_obj.InsertUnder(head_container)
      
              depth_obj.SetParameter(DescID(c4d.PRIM_CYLINDER_RADIUS), 3, c4d.DESCFLAGS_GET_0)
              depth_obj.SetParameter(DescID(c4d.PRIM_CYLINDER_HEIGHT), depth, c4d.DESCFLAGS_GET_0)
              depth_obj.SetParameter(DescID(c4d.PRIM_AXIS), 5, c4d.DESCFLAGS_GET_0)
      
              depth_obj_pos = self.head_obj_h/2 + depth/2
              depth_obj.SetRelPos(Vector(0, 0, depth_obj_pos))
      
              # Front Surface Object
              fs_obj = BaseObject(c4d.Oplane)
              fs_obj.SetName("front_surface")
              fs_obj.InsertUnder(head_container)
              self.fs_mg = fs_obj.GetMg()
      
              fs_obj.SetParameter(DescID(c4d.PRIM_PLANE_WIDTH), size_x, c4d.DESCFLAGS_GET_0)
              fs_obj.SetParameter(DescID(c4d.PRIM_PLANE_HEIGHT), size_y, c4d.DESCFLAGS_GET_0)
              fs_obj.SetParameter(DescID(c4d.PRIM_PLANE_SUBW), 1, c4d.DESCFLAGS_GET_0)
              fs_obj.SetParameter(DescID(c4d.PRIM_PLANE_SUBH), 1, c4d.DESCFLAGS_GET_0)
              fs_obj.SetParameter(DescID(c4d.PRIM_AXIS), 5, c4d.DESCFLAGS_GET_0)
      
              fs_obj_pos = self.head_obj_h/2 + depth
              fs_obj.SetRelPos(Vector(0, 0, fs_obj_pos))
      
              return container
      
          def GetHandleCount(self, op):
              return 4
      
          def GetHandle(self, op, handle_index, info):
      
              data = op.GetDataInstance()
      
              size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
              size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
              depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
              pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
              rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
      
              size_x = size_x/2
              size_y = size_y/2
              depth = depth+self.head_obj_h/2
      
              mg = self.fs_mg
              pos = mg.off
              rot = utils.MatrixToHPB(mg, c4d.ROTATIONORDER_DEFAULT)
      
              rot_mx = utils.HPBToMatrix(rot)
              rot_mx.off += pos
      
              if handle_index == 0: # Width
                  info.position = rot_mx * Vector(-size_x, 0, depth)
                  info.direction = Vector(1, 0, 0)
              elif handle_index == 1: # Height
                  info.position = rot_mx * Vector(0, size_y, depth)
                  info.direction = Vector(0, 1, 0)
              elif handle_index == 2: # Depth
                  info.position = rot_mx * Vector(0, 0, depth)
                  info.direction = Vector(0, 0, 1)
      
              info.type = c4d.HANDLECONSTRAINTTYPE_LINEAR
      
          def SetHandle(self, op, handle_index, handle_position, info):
      
              handle_origin = HandleInfo()
              self.GetHandle(op, handle_index, handle_origin)
      
              data = op.GetDataInstance()
      
              size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
              size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
              depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
              pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
              rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
      
              size_x = size_x/2
              size_y = size_y/2
      
              value = (handle_position - handle_origin.position) * info.direction
      
              if handle_index == 0:
                  op[c4d.OSOBJECT_SIZEX] -= value
              elif handle_index == 1:
                  op[c4d.OSOBJECT_SIZEY] += value
              elif handle_index == 2:
                  op[c4d.OSOBJECT_DEPTH] -= value
      
          def Draw(self, op, drawpass, bd, bh):
      
              if drawpass != c4d.DRAWPASS_HANDLES:
                  return c4d.DRAWRESULT_SKIP
      
              data = op.GetDataInstance()
      
              size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
              size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
              depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
              pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
              rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
      
              size_x = size_x/2
              size_y = size_y/2
      
              bd.SetMatrix_Matrix(op, bh.GetMg())
              hitid = op.GetHighlightHandle(bd)
      
              for i in xrange(self.GetHandleCount(op)):
                  bd.SetPen(c4d.GetViewColor(c4d.VIEWCOLOR_ACTIVEPOINT))
      
                  info = HandleInfo()
      
                  self.GetHandle(op, i, info)
                  bd.DrawHandle(info.position, c4d.DRAWHANDLE_BIG, 0)
                  bd.SetMatrix_Matrix(op, bh.GetMg())
      
              return c4d.DRAWRESULT_OK
      
      if __name__ == "__main__":
          icon = bitmaps.BaseBitmap()
          icon.InitWith(os.path.join(os.path.dirname(__file__), 'res', 'icon.tif'))
          plugins.RegisterObjectPlugin(PLUGIN_ID, 'Super Object', Osobject, 'osobject', c4d.OBJECT_GENERATOR, icon)
      
      

      And here the full example files: osobject.zip

      Thanks

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

        Hi,

        I am not quite sure if this is a question or the sharing of a solution, depending on how one reads your posting. But if you are interested in aligning the travel normal, the direction, of your handles with the transform you apply to your returned objects, you will simply have to multiply it with the respective rotational part of that transform, i.e. the rotation matrix. Something like this for your code:

        # That is not really a rotation matrix, since it will also affect
        # the translation of a vector, so your naming might be a bit
        # confusing.
        rot_mx = utils.HPBToMatrix(rot)
        rot.mx.off += pos
        # The actual rotation only transform.
        orientation = utils.HPBToMatrix(rot)
        
        if handle_index == 0: # Width
           info.position = rot_mx * Vector(-size_x, 0, depth)
           info.direction = Vector(1, 0, 0) * orientation 
        

        Cheers,
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        mfersaouiM 1 Reply Last reply Reply Quote 0
        • mfersaouiM
          mfersaoui @ferdinand
          last edited by mfersaoui

          Hi, @zipit

          Yes I'm searching a solution. I tried your solution but this does not work. the problem is with the handle point that not moving correctly (check the screen video above)

          I actually using the following method, but is too long and its works at 90%

          def GetAxis(self, pitch, rot):
                  pitch = int(math.degrees(pitch))
                  rot = int(math.degrees(rot))
                  if -90 <= rot < -45 or 225 <= rot < 270:
                      if -45 <= pitch < 45:
                          axis = 0 # +x
                      elif -225 <= pitch < -135 or 135 <= pitch < 225:
                          axis = 1 # -z
                      elif -135 <= pitch < -45:
                          axis = 3 # -y
                      elif 45 <= pitch < 135 or -270 <= pitch < -225:
                          axis = 2 # +y
                      else:
                          axis = 0 # +x
                  elif -45 <= rot < 45:
                      if -45 <= pitch < 45:
                          axis = 4 # +z
                      elif -225 <= pitch < -135 or 135 <= pitch < 225:
                          axis = 5 # -z
                      elif -135 <= pitch < -45:
                          axis = 13 # -y
                      elif 45 <= pitch < 135 or -270 <= pitch < -225:
                          axis = 12 # +y
                      else:
                          axis = 4 # +z
                  elif 45 <= rot < 135:
                      if -45 <= pitch < 45 or -225 <= pitch < -46:
                          axis = 11 # +x
                      elif -225 <= pitch < -135 or 135 <= pitch < 225:
                          axis = 10 # -z
                      elif 135 <= pitch < -45:
                          axis = 23 # -y
                      elif 45 <= pitch < 135 or -270 <= pitch < -225:
                          axis = 22 # +y
                      else:
                          axis = 1 # +x
                  elif 135 <= rot < 225:
                      if -45 <= pitch < 45:
                          axis = 15 # +x
                      elif -225 <= pitch < -135 or 135 <= pitch < 225:
                          axis = 14 # -z
                      elif -135 <= pitch < -45:
                          axis = 33 # -y
                      elif 45 <= pitch < 135 or -270 <= pitch < -225:
                          axis = 32 # +y
                      else:
                          axis = 5 # +x
                  else:
                      axis = 0
          
                  return axis
          
          
             def SwapPoint(self, p, axis):
                      if axis == 0 :
                          return c4d.Vector(-p.z, p.y, p.x)
                      elif axis == 1 :
                          return c4d.Vector(p.z, -p.y, p.x)
                      elif axis == 2 :
                          return c4d.Vector(-p.y, -p.z, p.x)
                      elif axis == 3 :
                          return c4d.Vector(p.y, p.z, p.x)
                      elif axis == 4 :
                          return c4d.Vector(-p.x, p.y, -p.z)
                      elif axis == 5 :
                          return c4d.Vector(-p.x, -p.y, p.z)
                      elif axis == 12 :
                          return c4d.Vector(-p.x, -p.z, -p.y)
                      elif axis == 13 :
                          return c4d.Vector(-p.x, p.z, p.y)
                      elif axis == 10 :
                          return c4d.Vector(-p.z, -p.y, -p.x)
                      elif axis == 11 :
                          return c4d.Vector(p.z, p.y, -p.x)
                      elif axis == 22 :
                          return c4d.Vector(p.y, -p.z, -p.x)
                      elif axis == 23:
                          return c4d.Vector(-p.y, p.z, -p.x)
                      elif axis == 14:
                          return c4d.Vector(p.x, -p.y, -p.z)
                      elif axis == 15:
                          return c4d.Vector(p.x, p.y, p.z)
                      elif axis == 32 :
                          return c4d.Vector(p.x, -p.z, p.y)
                      elif axis == 33 :
                          return c4d.Vector(p.x, p.z, -p.y)
                      return p
          
          axis = self.GetAxis(pitch, rotation)
          
          if handle_index == 0: # Width
              info.position = rot_mx * Vector(-size_x, 0, depth)
              info.direction = self.SwapPoint(info.direction, axis)
          

          Thanks.

          1 Reply Last reply Reply Quote 0
          • ManuelM
            Manuel
            last edited by Manuel

            hi,

            You are overthinking it, your value for the handle should never be something like += It should just reflect what the parameters values are.
            We got this thread where you can see it's should be simple.

            I didn't dive too much in your code, but something like this works better. You probably other logic that isn't working, this look like an implementation error from your side. Not a problem with the API. If you can't resolve your issue with the linked thread, i'll try to have a better look at it.

                def SetHandle(self, op, handle_index, handle_position, info):
            
                    data = op.GetDataInstance()
                    size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
                    size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
                    depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
                    pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
                    rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
            
                    size_x = size_x / 2.0
                    size_y = size_y / 2.0
            
                    value = (handle_position - info.center) * info.direction.GetLength()
                    if handle_index == 0:
                        op[c4d.OSOBJECT_SIZEX] = value.x
                    elif handle_index == 1:
                        op[c4d.OSOBJECT_SIZEY] = value.y
                    elif handle_index == 2:
                        op[c4d.OSOBJECT_DEPTH] = value.z
            

            this solution isn't perfect yet, but i think you will understand the idea and find out why it's not working perfectly.

            Cheers,
            Manuel

            MAXON SDK Specialist

            MAXON Registered Developer

            mfersaouiM 1 Reply Last reply Reply Quote 0
            • mfersaouiM
              mfersaoui @Manuel
              last edited by mfersaoui

              Hi @m_magalhaes,

              I used your suggestion and I replaced the +=. Yes is better, but it's always the same problem. I don't know how to explain the problem, and for that reason I have created this plugin object example. So the best way to understand the problem is by testing the example.

              I will try to explain roughly the problem:
              The handle point is pinned on child object of the main object plugin and this child object has a dynamic (position and rotation) that depend on the Pitch & Rotation values. So I must to dynamically adapt the info.direction depending on the position and rotation of the child object. And concerning this thread, this helped me to control the Pitch and Rotation via Handle but not the (Width, Height and Depth) values (of child object).

              At the moment, I guess I'll just use the SwapPoint() it doesn't have a perfect result but it's acceptable.

              Thanks.

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

                Hello @mfersaoui,

                without any further questions or replies, we will consider this topic to be solved by Monday and flag it accordingly.

                Thank you for your understanding,
                Ferdinand

                MAXON SDK Specialist
                developers.maxon.net

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