1 from __future__
import division
27 from builtins
import object
31 from maya.api
import OpenMaya, OpenMayaUI, OpenMayaRender
32 from maya
import OpenMayaRender
as OpenMayaRenderV1
34 logger = logging.getLogger(
'pySquareScaleManipContext')
42 class PlaneMath(object):
44 This utility class represents a mathematical plane and performs intersection
49 Initialze the member variables of the class.
56 def set_plane( self, point_on_plane, normal_to_plane ):
58 Define the plane by supplying a point on the plane and the plane's normal.
61 _normal_to_plane.normalize()
64 self.a = _normal_to_plane.x
65 self.b = _normal_to_plane.y
66 self.c = _normal_to_plane.z
67 self.d = -(self.a*point_on_plane.x + self.b*point_on_plane.y + self.c*point_on_plane.z)
69 def intersect( self, line ):
71 Intersect the plane with the given line. Return the intersection point if an intersection
72 occurs. Otherwise, raise an exception.
74 denominator = self.a*line[1].x + self.b*line[1].y + self.c*line[1].z
77 if (denominator < .00001):
80 t = -(self.d + self.a*line[0].x + self.b*line[0].y + self.c*line[0].z) / denominator
83 return line[0] + t * line[1]
85 class LineMath(object):
87 This utility class represents a mathematical line and returns the closest point
88 on the line to a given point.
92 Initialze the member variables of the class.
97 def set_line( self, line_point, line_direction ):
99 Define the line by supplying a point on the line and the line's direction.
103 self.direction.normalize()
105 def closest_point( self, to_point ):
107 Determine and return the point on the line which is closest to the given point.
109 t = self.direction * ( to_point - self.point )
110 return self.point + ( self.direction * t )
112 class SquareGeometry(object):
114 This utility class defines methods for returning the four corner points of a unit square
120 Return the top left corner of the square.
126 Return the top right corner of the square.
132 Return the bottom left corner of the square.
138 Return the bottom right corner of the square.
148 This is the subclassed manipulator node. It scales the selected objects
149 in the X direction based on dragging movements by the user.
151 kNodeName =
'squareScaleContextManipulator'
156 Initialize the manipulator member variables.
161 self.point_on_plane = SquareGeometry.top_left()
165 self.right_index = -1
166 self.bottom_index = -1
170 self.bottom_name = -1
177 self.translate_x = 0.0
178 self.translate_y = 0.0
179 self.translate_z = 0.0
184 self.normal_to_plane = v1 ^ v2
187 self.normal_to_plane.normalize()
188 self.plane = PlaneMath()
189 self.plane.set_plane( self.point_on_plane, self.normal_to_plane )
201 def postConstructor(self):
202 self.top_index = self.addDoubleValue(
'topValue', 0 )
203 self.right_index = self.addDoubleValue(
'rightValue', 0 )
204 self.bottom_index = self.addDoubleValue(
'bottomValue', 0 )
205 self.left_index = self.addDoubleValue(
'leftValue', 0 )
207 gl_pickable_item = self.glFirstHandle()
208 self.top_name = gl_pickable_item
209 self.bottom_name = gl_pickable_item + 1
210 self.right_name = gl_pickable_item + 2
211 self.left_name = gl_pickable_item + 3
214 def connectToDependNode(self, depend_node):
216 Connect the manipulator to the given dependency node.
225 scale_x_plug = nodeFn.findPlug(
'scaleX',
True)
227 logger.info(
" Could not find scaleX plug!")
232 plug_index = self.connectPlugToValue(scale_x_plug, self.right_index)
234 logger.info(
" Could not connectPlugToValue!")
237 self.finishAddingManips()
242 Update the region dragged by the mouse.
246 tl = SquareGeometry.top_left()
247 tr = SquareGeometry.top_right()
248 bl = SquareGeometry.bottom_left()
249 br = SquareGeometry.bottom_right()
253 active = self.glActiveName()
255 if ( active == self.top_name ):
256 tl += self.mouse_point_gl_name
257 tr += self.mouse_point_gl_name
258 if ( active == self.bottom_name ):
259 bl += self.mouse_point_gl_name
260 br += self.mouse_point_gl_name
261 if ( active == self.right_name ):
262 tr += self.mouse_point_gl_name
263 br += self.mouse_point_gl_name
264 if ( active == self.left_name ):
265 tl += self.mouse_point_gl_name
266 bl += self.mouse_point_gl_name
268 return [tl, tr, bl, br]
271 def draw(self, view, path, style, status):
273 Draw the manupulator in a legacy viewport.
277 gl_renderer = OpenMayaRenderV1.MHardwareRenderer.theRenderer()
278 gl_ft = gl_renderer.glFunctionTable()
280 [tl, tr, bl, br] = self.pre_draw()
287 gl_ft.glMatrixMode( OpenMayaRenderV1.MGL_MODELVIEW )
289 gl_ft.glTranslatef( self.translate_x, self.translate_y, self.translate_z )
290 gl_ft.glRotatef( math.degrees(self.rotate_z), 0.0, 0.0, 1.0 )
291 gl_ft.glRotatef( math.degrees(self.rotate_y), 0.0, 1.0, 0.0 )
292 gl_ft.glRotatef( math.degrees(self.rotate_x), 1.0, 0.0, 0.0 )
296 self.colorAndName( view, self.top_name,
False, self.mainColor() )
297 gl_ft.glBegin( OpenMayaRenderV1.MGL_LINES )
298 gl_ft.glVertex3f( tl.x, tl.y, tl.z )
299 gl_ft.glVertex3f( tr.x, tr.y, tr.z )
303 self.colorAndName( view, self.right_name,
True, self.mainColor() )
304 gl_ft.glBegin( OpenMayaRenderV1.MGL_LINES )
305 gl_ft.glVertex3f( tr.x, tr.y, tr.z )
306 gl_ft.glVertex3f( br.x, br.y, br.z )
310 self.colorAndName( view, self.bottom_name,
False, self.mainColor() )
311 gl_ft.glBegin( OpenMayaRenderV1.MGL_LINES )
312 gl_ft.glVertex3f( br.x, br.y, br.z )
313 gl_ft.glVertex3f( bl.x, bl.y, bl.z )
317 self.colorAndName( view, self.left_name,
True, self.mainColor() )
318 gl_ft.glBegin( OpenMayaRenderV1.MGL_LINES )
319 gl_ft.glVertex3f( bl.x, bl.y, bl.z )
320 gl_ft.glVertex3f( tl.x, tl.y, tl.z )
330 def preDrawUI(self, view):
332 Cache the viewport for use in VP 2.0 drawing.
337 def drawUI(self, draw_manager, frame_context):
339 Draw the manupulator in a VP 2.0 viewport.
342 [tl, tr, bl, br] = self.pre_draw()
345 xform.rotateByComponents([math.degrees(self.rotate_x), \
346 math.degrees(self.rotate_y), \
347 math.degrees(self.rotate_z), \
348 OpenMaya.MTransformationMatrix.kZYX], \
349 OpenMaya.MSpace.kWorld)
351 mat = xform.asMatrix()
358 draw_manager.beginDrawable(OpenMayaRender.MUIDrawManager.kNonSelectable, self.top_name)
359 self.setHandleColor(draw_manager, self.top_name, self.dimmedColor())
360 draw_manager.line(tl, tr)
361 draw_manager.endDrawable()
364 draw_manager.beginDrawable(OpenMayaRender.MUIDrawManager.kSelectable, self.right_name)
365 self.setHandleColor(draw_manager, self.right_name, self.mainColor())
366 draw_manager.line(tr, br)
367 draw_manager.endDrawable()
370 draw_manager.beginDrawable(OpenMayaRender.MUIDrawManager.kNonSelectable, self.bottom_name)
371 self.setHandleColor(draw_manager, self.bottom_name, self.dimmedColor())
372 draw_manager.line(br, bl)
373 draw_manager.endDrawable()
376 draw_manager.beginDrawable(OpenMayaRender.MUIDrawManager.kSelectable, self.left_name)
377 self.setHandleColor(draw_manager, self.left_name, self.mainColor())
378 draw_manager.line(bl, tl)
379 draw_manager.endDrawable()
382 def doPress( self, view ):
384 Handle the mouse press event in a VP2.0 viewport.
387 self.mouse_point_gl_name = OpenMaya.MPoint.kOrigin
388 self.update_drag_information()
391 def doDrag( self, view ):
393 Handle the mouse drag event in a VP2.0 viewport.
395 self.update_drag_information()
398 def doRelease( self, view ):
400 Handle the mouse release event in a VP2.0 viewport.
404 def set_draw_transform_info( self, rotation, translation ):
406 Store the given rotation and translation.
408 self.rotate_x = rotation[0]
409 self.rotate_y = rotation[1]
410 self.rotate_z = rotation[2]
411 self.translate_x = translation[0]
412 self.translate_y = translation[1]
413 self.translate_z = translation[2]
415 def update_drag_information( self ):
417 Update the mouse's intersection location with the manipulator
420 self.local_mouse = self.mouseRay()
423 mouse_intersection_with_manip_plane = self.plane.intersect( self.local_mouse )
425 self.mouse_point_gl_name = mouse_intersection_with_manip_plane
427 active = self.glActiveName()
431 if ( active == self.top_name ):
434 if ( active == self.bottom_name ):
437 if ( active == self.right_name ):
440 if ( active == self.left_name ):
452 line.set_line( start, vab )
456 cpt = line.closest_point( self.mouse_point_gl_name )
457 self.mouse_point_gl_name -= cpt
459 min_change_value = min( self.mouse_point_gl_name.x, self.mouse_point_gl_name.y, self.mouse_point_gl_name.z )
460 max_change_value = max( self.mouse_point_gl_name.x, self.mouse_point_gl_name.y, self.mouse_point_gl_name.z )
461 if ( active == self.right_name ):
462 self.setDoubleValue( self.right_index, max_change_value )
463 if ( active == self.left_name ):
464 self.setDoubleValue( self.right_index, min_change_value )
470 This command class is used to create instances of the SquareScaleManipContext class.
472 kPluginCmdName =
"squareScaleManipContext"
479 return SquareScaleManipContextCmd()
483 Create and return an instance of the SquareScaleManipContext class.
485 return SquareScaleManipContext()
489 This context handles all mouse interaction in the viewport when activated.
490 When activated, it creates and manages an instance of the SquareScaleManuplator
491 class on the selected objects.
494 kContextName =
'SquareScaleManipContext'
502 Initialize the members of the SquareScaleManipContext class.
505 self.setTitleString(
'Plug-in manipulator: ' + SquareScaleManipContext.kContextName)
506 self.manipulator_class_ptr =
None
507 self.first_object_selected =
None
508 self.active_list_modified_msg_id = -1
511 def toolOnSetup(self, event):
513 Set the help string and selection list callback.
515 self.setHelpString(
'Move the object using the manipulator')
517 SquareScaleManipContext.update_manipulators_cb(self)
520 OpenMaya.MModelMessage.kActiveListModified, \
521 SquareScaleManipContext.update_manipulators_cb, self)
527 def toolOffCleanup(self):
529 Unregister the selection list callback.
533 self.active_list_modified_msg_id = -1
540 def namesOfAttributes(self, attribute_names):
542 Return the names of the attributes of the selected objects this context will be modifying.
544 attribute_names.append(
'scaleX')
547 def setInitialState(self):
549 Set the initial transform of the manipulator.
552 xformMatrix = xform.transformation()
553 translation = xformMatrix.translation( OpenMaya.MSpace.kWorld )
554 rotation = xformMatrix.rotation(
False)
556 self.manipulator_class_ptr.set_draw_transform_info( rotation, translation )
559 def valid_geometry_selected(self):
561 Check to make sure the selected objects have transforms.
569 logger.info(
" Could not get active selection")
572 if (
not list)
or (list.length() == 0):
575 while not iter.isDone():
576 depend_node = iter.getDependNode()
577 if (depend_node.isNull()
or (
not depend_node.hasFn(OpenMaya.MFn.kTransform))):
583 def update_manipulators_cb(ctx):
585 Callback that creates the manipulator if valid geometry is selected. Also removes
586 the manipulator if no geometry is selected. Handles connecting the manipulator to
587 multiply selected nodes.
590 ctx.deleteManipulators()
592 logger.info(
" No manipulators to delete")
595 if not ctx.valid_geometry_selected():
599 ctx.manipulator_class_ptr =
None
600 ctx.first_object_selected = OpenMaya.MObject.kNullObj
602 (manipulator, manip_object) = SquareScaleManipulator.newManipulator(
'SquareScaleContextManipulator')
606 ctx.manipulator_class_ptr = manipulator
609 ctx.addManipulator(manip_object)
614 while not iter.isDone():
615 depend_node = iter.getDependNode()
619 if (
not manipulator.connectToDependNode(depend_node)):
624 if ( ctx.first_object_selected == OpenMaya.MObject.kNullObj ):
625 ctx.first_object_selected = depend_node
629 ctx.setInitialState()
635 update_manipulators_cb = staticmethod(update_manipulators_cb)
639 def initializePlugin(plugin):
643 pluginFn.registerContextCommand( SquareScaleManipContextCmd.kPluginCmdName, SquareScaleManipContextCmd.creator)
645 sys.stderr.write(
"Failed to register context command: %s\n" % SquareScaleManipContextCmd.kPluginCmdName)
649 pluginFn.registerNode( SquareScaleManipulator.kNodeName, SquareScaleManipulator.kTypeId, \
650 SquareScaleManipulator.creator, SquareScaleManipulator.initialize, \
651 OpenMaya.MPxNode.kManipulatorNode)
653 sys.stderr.write(
"Failed to register node: %s\n" % SquareScaleManipulator.kNodeName)
658 def uninitializePlugin(plugin):
661 pluginFn.deregisterContextCommand(SquareScaleManipContextCmd.kPluginCmdName)
664 "Failed to unregister command: %s\n" % SquareScaleManipContextCmd.kPluginCmdName)
668 pluginFn.deregisterNode(SquareScaleManipulator.kTypeId)
671 "Failed to unregister node: %s\n" % SquareScaleManipulator.kNodeName)