Pages

Sunday, February 4, 2024

Finding a Perpendicular Vector Using Dot Product

     While setting up a rig for the turtle, I decided to add IK/FK snapping functionality to help with animation. To set this up, I needed a way to calculate the pole vector for the legs when snapping IK to FK. I found this great tutorial by Greg Hendrix explaining how to do that. https://vimeo.com/240328348

    As I incorporated this into my rig though, I was confused on how he found the vector that's perpendicular to the middle joint. After experimenting with it, I found a different method that makes sense to me, so I want to record some notes on how it works.

     Basically, I needed this vector to help place the pole vector. With this vector, I could subtract it from the mid joint to get a new vector that's perpendicular to the IK line. This new vector could then be scaled depending on how far I want the pole vector to be from the mid joint.

    The first step was to find the length along the IK line up to the perpendicular point. I found this through calculating the dot product of the rootToMid line and ikLine. The important thing that I realized is that ikLine needs to be normalized for this to work. If I didn't normalize, it returned a length that was scaled by the ikLine vector. By normalizing ikLine, it gave the exact distance to the perpendicular point.

    In python, I wrote this out as follows.

 ikLineMidLength = rootToMid * ikLine.normal()

    After getting the length, I divided it by the ikLine length to get a scale value. 

ikLineScale = ikLineMidLength / ikLine.length()

    Finally, I multiplied the ikLine vector by the scale value to get a new vector at the perpendicular point.

ikMidVector = (ikLine * ikLineScale) + rootJntVector

    Here's the Python code that I used to find the perpendicular vector and place a locator at it's coordinates. After finding that vector, I also needed to offset it by the root vector. The initial vector subtractions caused everything to be relative to the origin, so it needed to be moved back to match the original joint locations.

import maya.cmds as cmds
import maya.OpenMaya as om

def addLocator(inputVector, inputName):
    cmds.spaceLocator( p=(inputVector.x, inputVector.y, inputVector.z), n=inputName )
    
# Joint Positions
rootJointPos = cmds.xform('rootJoint', q=True, ws=True, t=True)
midJointPos = cmds.xform('midJoint', q=True, ws=True, t=True)
endJointPos = cmds.xform('endJoint', q=True, ws=True, t=True)

# Joint Vectors
rootJntVector = om.MVector(rootJointPos[0], rootJointPos[1], rootJointPos[2])
midJntVector = om.MVector(midJointPos[0], midJointPos[1], midJointPos[2])
endJntVector = om.MVector(endJointPos[0], endJointPos[1], endJointPos[2])

# Distance Vectors
ikLine = endJntVector - rootJntVector
rootToMid = midJntVector - rootJntVector

ikLineMidLength = rootToMid * ikLine.normal() # Find the distance to the point on ikLine that is perpendicular to the midJoint.
ikLineScale = ikLineMidLength / ikLine.length() # Find the ratio of the perpendicular distance to the length of the ikLine.
ikMidVector = (ikLine * ikLineScale) + rootJntVector # Scale the ikLine by the ratio to find the vector at the perpendicular distance.

# Place Locator at Perpendicular Vector
addLocator(ikMidVector, "ikMidVector")

 

    Initially I had some trouble figuring this out. In particular, I was stuck on the mid length dot product until I realized that the IK Line needed to be normalized. Hopefully I can go back to this post if I ever forget how this works.

    Aside from that, I've also started on a script where you can provide 3 input joints and automatically place a new pole vector based on them. I'll make another post about that when it's ready.