- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Examples

# simpleSolverNode.py

    #-
    # ==========================================================================
    # Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All
    # rights reserved.
    #
    # The coded instructions, statements, computer programs, and/or related
    # material (collectively the "Data") in these files contain unpublished
    # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
    # licensors, which is protected by U.S. and Canadian federal copyright
    # law and by international treaties.
    #
    # The Data is provided for use exclusively by You. You have the right
    # to use, modify, and incorporate this Data into other products for
    # purposes authorized by the Autodesk software license agreement,
    # without fee.
    #
    # The copyright notices in the Software and this entire statement,
    # including the above license grant, this restriction and the
    # following disclaimer, must be included in all copies of the
    # Software, in whole or in part, and all derivative works of
    # the Software, unless such copies or derivative works are solely
    # in the form of machine-executable object code generated by a
    # source language processor.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
    # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
    # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
    # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
    # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
    # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
    # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
    # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
    # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
    # OR PROBABILITY OF SUCH DAMAGES.
    #
    # ==========================================================================
    #+

    #
    # Creation Date:   4 October 2006
    #
    # Example Plugin: simpleSolverNode.py
    #
    #
    # Single-bone, single-plane ik-solver.
    #
    # This plugin demonstrates how to create and register an ik-solver.
    # Due to the complex nature of ik solvers, this plugin only
    # works with 2-joint skeletons (1 bone) in the x-y plane.
    #
    # To use the solver, create a single bone (joint tool).
    # Then type the following in the command window:
    #
    #   import maya.cmds as cmds
    #
    #   cmds.createNode("spSimpleSolverNode", name="spSimpleSolverNode1")
    #   cmds.ikHandle(sol="spSimpleSolverNode1", sj="joint1", ee="joint2")
    #
    # This creates a handle that can be dragged around in the x-y
    # plane.
    #

    # imports
    import maya.OpenMaya as OpenMaya
    import maya.OpenMayaUI as OpenMayaUI
    import maya.OpenMayaMPx as OpenMayaMPx
    import maya.OpenMayaAnim as OpenMayaAnim
    import math, sys

    # consts
    kSolverNodeName = "spSimpleSolverNode"
    kSolverNodeId = OpenMaya.MTypeId(0x8700a)

    class simpleSolverNode(OpenMayaMPx.MPxIkSolverNode):
        def __init__(self):
            OpenMayaMPx.MPxIkSolverNode.__init__(self)

        def solverTypeName(self):
            return kSolverNodeName

        def doSolve(self):
            self.doSimpleSolver()

        def doSimpleSolver(self):
            """
            Solve single joint in the x-y plane
                - first it calculates the angle between the handle and the end-effector.
                - then it determines which way to rotate the joint.
            """
            handle_group = self.handleGroup()
            handle = handle_group.handle(0)
            handlePath = OpenMaya.MDagPath.getAPathTo(handle)
            fnHandle = OpenMayaAnim.MFnIkHandle(handlePath)

            # Get the position of the end_effector
            end_effector = OpenMaya.MDagPath()
            fnHandle.getEffector(end_effector)
            tran = OpenMaya.MFnTransform(end_effector)
            effector_position = tran.rotatePivot(OpenMaya.MSpace.kWorld)

            # Get the position of the handle
            handle_position = fnHandle.rotatePivot(OpenMaya.MSpace.kWorld)

            # Get the start joint position
            start_joint = OpenMaya.MDagPath()
            fnHandle.getStartJoint(start_joint)
            start_transform = OpenMaya.MFnTransform(start_joint)
            start_position = start_transform.rotatePivot(OpenMaya.MSpace.kWorld)

            # Calculate the rotation angle
            v1 = start_position - effector_position
            v2 = start_position - handle_position
            angle = v1.angle(v2)

            # -------- Figure out which way to rotate --------
            #
            #  define two vectors U and V as follows
            #  U   =   EndEffector(E) - StartJoint(S)
            #  N   =   Normal to U passing through EndEffector
            #
            #  Clip handle_position to half-plane U to determine the region it
            #  lies in. Use the region to determine  the rotation direction.
            #
            #             U
            #             ^              Region      Rotation
            #             |  B
            #            (E)---N            A          C-C-W
            #         A   |                 B           C-W
            #             |  B
            #             |
            #            (S)
            #

            rot = 0.0   # Rotation about Z-axis

            # U and N define a half-plane to clip the handle against
            U = effector_position - start_position
            U.normalize()

            # Get a normal to U
            zAxis = OpenMaya.MVector(0.0, 0.0, 1.0)
            N = U ^ zAxis # Cross product
            N.normalize()

            # P is the handle position vector
            P = handle_position - effector_position

            # Determine the rotation direction
            PdotN = P[0]*N[0] + P[1]*N[1]
            if PdotN < 0:
                rot = angle # counter-clockwise
            else:
                rot = -1.0 * angle  # clockwise

            # get and set the Joint Angles
            jointAngles = OpenMaya.MDoubleArray()
            try:
                self._getJointAngles(jointAngles)
            except:
                # getting angles failed, do nothing
                pass
            else:
                jointAngles.set(jointAngles[0] + rot, 0)
                self._setJointAngles(jointAngles)

    ##############################################################################

    def nodeCreator():
        return OpenMayaMPx.asMPxPtr(simpleSolverNode())

    def nodeInitializer():
        # nothing to initialize
        pass

    # initialize the script plug-in
    def initializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")

        try:
            mplugin.registerNode(kSolverNodeName, kSolverNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kIkSolverNode)
        except:
            sys.stderr.write("Failed to register node: %s" % kSolverNodeName)
            raise

    # uninitialize the script plug-in
    def uninitializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject)
        try:
            mplugin.deregisterNode(kSolverNodeId)
        except:
            sys.stderr.write("Failed to unregister node: %s" % kSolverNodeName)
            raise