Dynamic HandleInfo.direction



  • 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



  • 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



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



  • 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



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


Log in to reply