Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Component of a quaternion rotation around an axis

I'm having trouble finding any good information on this topic. Basically I want to find the component of a quaternion rotation, that is around a given axis (not necessarily X, Y or Z - any arbitrary unit vector). Sort of like projecting a quaternion onto a vector. So if I was to ask for the rotation around some axis parallel to the quaternion's axis, I'd get the same quaternion back out. If I was to ask for the rotation around an axis orthogonal to the quaternion's axis, I'd get out an identity quaternion. And in-between... well, that's what I'd like to know how to work out :)

like image 493
Ben Hymers Avatar asked Sep 10 '10 11:09

Ben Hymers


People also ask

How do you rotate a quaternion around an axis?

For rotation quaternions, the inverse equals the conjugate. So for rotation quaternions, q−1 = q* = ( q0, −q1, −q2, −q3 ). Inverting or conjugating a rotation quaternion has the effect of reversing the axis of rotation, which modifies it to rotate in the opposite direction from the original.

What are the components of a quaternion?

A quaternion can be thought of as a four element vector. This vector is composed of two distinct components: a scalar and a 3 element unit vector. The scalar value, w, corresponds to an angle of rotation. The vector term, [x y z], corresponds to an axis of rotation, about which the angle or rotation is performed.

Why do quaternions have 4 components?

Four values make up a quaternion, namely x, y, z and w. Three of the values are used to represent the axis in vector format, and the forth value would be the angle of rotation around the axis." So you could think of it as the rotation of the rotation, in simple terms!


2 Answers

There is an elegant solution for this problem, specially suited for quaternions. It is known as the "swing twist decomposition":

in pseudocode

/**    Decompose the rotation on to 2 parts.    1. Twist - rotation around the "direction" vector    2. Swing - rotation around axis that is perpendicular to "direction" vector    The rotation can be composed back by     rotation = swing * twist     has singularity in case of swing_rotation close to 180 degrees rotation.    if the input quaternion is of non-unit length, the outputs are non-unit as well    otherwise, outputs are both unit */ inline void swing_twist_decomposition( const xxquaternion& rotation,                                        const vector3&      direction,                                        xxquaternion&       swing,                                        xxquaternion&       twist) {     vector3 ra( rotation.x, rotation.y, rotation.z ); // rotation axis     vector3 p = projection( ra, direction ); // return projection v1 on to v2  (parallel component)     twist.set( p.x, p.y, p.z, rotation.w );     twist.normalize();     swing = rotation * twist.conjugated(); } 

And the long answer and derivation of this code can be found here http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/

like image 166
minorlogic Avatar answered Oct 11 '22 10:10

minorlogic


I spent the other day trying to find the exact same thing for an animation editor; here is how I did it:

  1. Take the axis you want to find the rotation around, and find an orthogonal vector to it.
  2. Rotate this new vector using your quaternion.
  3. Project this rotated vector onto the plane the normal of which is your axis
  4. The acos of the dot product of this projected vector and the original orthogonal is your angle.

    public static float FindQuaternionTwist(Quaternion q, Vector3 axis) {     axis.Normalize();      // Get the plane the axis is a normal of     Vector3 orthonormal1, orthonormal2;     ExMath.FindOrthonormals(axis, out orthonormal1, out orthonormal2);      Vector3 transformed = Vector3.Transform(orthonormal1, q);      // Project transformed vector onto plane     Vector3 flattened = transformed - (Vector3.Dot(transformed, axis) * axis);     flattened.Normalize();      // Get angle between original vector and projected transform to get angle around normal     float a = (float)Math.Acos((double)Vector3.Dot(orthonormal1, flattened));      return a; } 

Here is the code to find the orthonormals however you can probably do much better if you only want the one for the above method:

private static Matrix OrthoX = Matrix.CreateRotationX(MathHelper.ToRadians(90)); private static Matrix OrthoY = Matrix.CreateRotationY(MathHelper.ToRadians(90));  public static void FindOrthonormals(Vector3 normal, out Vector3 orthonormal1, out Vector3 orthonormal2) {     Vector3 w = Vector3.Transform(normal, OrthoX);     float dot = Vector3.Dot(normal, w);     if (Math.Abs(dot) > 0.6)     {         w = Vector3.Transform(normal, OrthoY);     }     w.Normalize();      orthonormal1 = Vector3.Cross(normal, w);     orthonormal1.Normalize();     orthonormal2 = Vector3.Cross(normal, orthonormal1);     orthonormal2.Normalize(); } 

Though the above works you may find it doesn't behave as you'd expect. For example, if your quaternion rotates a vector 90 deg. around X and 90 deg. around Y you'll find if you decompose the rotation around Z it will be 90 deg. as well. If you imagine a vector making these rotations then this makes perfect sense but depending on your application it may not be desired behaviour. For my application - constraining skeleton joints - I ended up with a hybrid system. Matrices/Quats used throughout but when it came to the method to constrain the joints I used euler angles internally, decomposing the rotation quat to rotations around X, Y, Z each time.

Good luck, Hope that helped.

like image 26
sebf Avatar answered Oct 11 '22 09:10

sebf