1 Principle

Points can be described in many bases. A basis generally1 consists of an origin and an orientation.

Consider the basis with origin = Tychonievich’s Office in Rice 208 and orientation = looking out of his door. In that basis, Einstein Bros Bagles is forward 10 meters, down 4 meters, left 2 meters, or (-2, -4, -10) using our usual -z-is-forward convention.

Consider the basis with origin = front entrace to Rice Hall and orientation = looking out of the building. In that basis, Einstein Bros Bagles is backwards 30 meters, left 1 meter. or (-1, 0, 30).

A homogeneous coordinate matrix performs a change of basis.

The matrix that converts Rice208 coordinates to RiceDoor coordinates is

\begin{bmatrix} 0 & 0 & 1 & 9 \\ 0 & 1 & 0 & 4 \\ -1 & 0 & 0 & 28 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}

Scene graphs generally represent one basis (the child basis) in terms of another basis (the parent basis). By describing the position and orientation of a calf in terms of a thigh, any motion of the thigh automatically moves the calf as well.

This adds a complication when we want to compute orientations to meet constraints: the computations need to happen in a single coordinate system.

2 Points-at example

Consider the constraint left calf points at right ankle. We know how to compute a points-at rotation matrix, but that requires as input the current and desired directions, and as given those are in different coordinate systems. So first we need to get them into the same coordinate system.

2.1 Option 1: all in world coordinates

One option goes as follows:

  1. Convert the right ankle position to world coordinates using the right leg’s transformation hierarchy
  2. Convert the left knee position to world coordinates using the left leg’s transformation hierarchy
  3. Subtract (right ankle − left knee) to get the desired orientation of the left calf
  4. Convert the left ankle position to world coordinates using the left leg’s transformation hierarchy
  5. Subtract (left ankle − left knee) to get the current orientation of the left calf
  6. Compute a rotation matrix to satisfy the constraint
  7. Apply the resulting matrix in world coordinates

This works fine, but it does have an odd gotcha. The resulting matrix has to be applied in world coordinates because it was computed in world coordinates. Thus, instead of the old left calf-to-world basis change matrix T_{\text{hip}} R_{\text{hip}} T_{\text{thigh.l}} R_{\text{thigh.l}} T_{\text{calf.l}} R_{\text{calf.l}} the constraint means we now have R_{\text{constraint}} [T_{\text{hip}} R_{\text{hip}} T_{\text{thigh.l}} R_{\text{thigh.l}} T_{\text{calf.l}} R_{\text{calf.l}}] But the constraint matrix needs to rotate around the knee as its origin, not the world origin, so we actually have T_{\text{origin to knee.l}} R_{\text{constraint}} T_{\text{knee.l to origin}} [T_{\text{hip}} R_{\text{hip}} T_{\text{thigh.l}} R_{\text{thigh.l}} T_{\text{calf.l}} R_{\text{calf.l}}]

We also don’t have any easy way to represent the constraint in the calf’s coordinate system, which means we’d find it challenging to limit the constraint based on rotation limits on the calf or the like. For these and other reasons, option 2 is generally preferred.

2.2 Option 2: all in object coordinates

The other option goes as follows:

  1. Convert the right ankle position to world coordinates using the right leg’s transformation hierarchy
  2. Convert the right ankle position to left calf coordinates using the inverse of the left leg’s transformation hierarchy
  3. Subtract (right ankle − left knee) to get the desired orientation of the left calf
  4. Subtract (left ankle − left knee) to get the current orientation of the left calf
  5. Compute a rotation matrix to satisfy the constraint
  6. Apply the resulting matrix in object coordinates

This process will be cleaner, but requires the inverse of matrices. Fortunately, those are easy to compute:

  • Inverse translation is translation with negated coordinates
  • Inverse rotation is transpose of rotation
  • Inverse A B is B^{-1} A^{-1}

Thus, the right calf-to-world basis change matrix is T_{\text{hip}} R_{\text{hip}} T_{\text{thigh.r}} R_{\text{thigh.r}} T_{\text{calf.r}} R_{\text{calf.r}} and the left world-to-calf basis change matrix is R_{\text{calf.l}}^{-1} T_{\text{calf.l}}^{-1} R_{\text{thigh.l}}^{-1} T_{\text{thigh.l}}^{-1} R_{\text{hip}}^{-1} T_{\text{hip}}^{-1} which combine to make a right-to-left transformation of R_{\text{calf.l}}^{-1} T_{\text{calf.l}}^{-1} R_{\text{thigh.l}}^{-1} T_{\text{thigh.l}}^{-1} T_{\text{thigh.r}} R_{\text{thigh.r}} T_{\text{calf.r}} R_{\text{calf.r}} (the hip transformations cancel out).

But the constraint matrix needs to rotate around the knee as its origin, which is the origin of the left calf, so we apply it as [T_{\text{hip}} R_{\text{hip}} T_{\text{thigh.l}} R_{\text{thigh.l}} T_{\text{calf.l}} R_{\text{calf.l}}]R_{\text{constraint}}

Because the constraint is in the calf’s coordinate space, we can also limit the constraint based on rotation limits on the calf and so on.

3 FABRIK example

Forward And Backward Reaching Inverse Kinematics is a position-based method. It’s input starting positions are typically each in a different coordinate system, so the first step is to transform them all into the same coordinate system. World coordinates are generally adequate for this task, though any one of the bone’s coordinate systems can be used instead if you so desire.

After FABRIK runs, we have a sequence of positions in a single coordinate system. But we need rotations, not positions.

The option-2-like solution is something like the following:

  1. transform the position chain into the first bone’s coordinate system
  2. find and apply a points-at constraint for the first bone
  3. transform the rest of the position chain into the next bone’s coordinate system
  4. find and apply a points-at constraint for that bone
  5. repeat steps 3 and 4 until all bones are oriented

The option-1-like solution is more complicated to describe, but overall similar: we orient each bone from root to top as if each were a point-at constraint.


  1. Bases can have scaling too, but that is not very common.↩︎