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:
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 theinfo.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.
-
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