Quaternions, Axis - Angle and ArcCos

Started by
10 comments, last by Dirk Gregorius 18 years, 7 months ago
Given a unit quaternion q. This quaternion encodes a rotation around an axis by an angle alpha. The following holds: q.x = sin( alpha / 2 ) * axis.x q.y = sin( alpha / 2 ) * axis.y q.z = sin( alpha / 2 ) * axis.z q.w = cos( alpha / 2 ) In order to get the angle alpha I could simply do: alpha = 2.0f * arccosf( q.w ) This does work as long as -1.0f <= q.w <= 1.0. In my application q.w might become 1.000001 for example and my whole physic system gets srewed up. Also it is possible to clamp I wonder if there is a more stable alternative using trigeometric identities? I thought of the folollowing: Vector3 vec( q.x, q.y, q.z ); float sina_2 = vec.Length(); float cosa_2 = q.w; float tana_2 = sin_a2 / cos_a2; Now I could use atanf() or atan2f() - is any of these functions superior over arccosf() or arcsinf()? Any other recommendations? -Dirk
Advertisement
Unless I'm overlooking something obvious, 2*atan2(length(v),w) should give you the same results as 2*acos(clamp(w,-1,1)), but with greater accuracy and stability.
i dont think the w component will go above 1.0 if you use the D3DXQuaternionNormalize...
it hasnt messed up anything for me... yet
Quote:i dont think the w component will go above 1.0 if you use the D3DXQuaternionNormalize...
I wouldn't count on that. Unless D3DXQuaternionNormalize() does something weird like clamp the results itself, I doubt there's any guarantee on the value of w.

In general it's best not to leave these sorts of things to chance. You should always clamp the argument to acos() or asin() unless (as is sometimes the case) you are absolutely certain of the range of the input.
I have wrapper functions for all trigeometric functions. I think I will clamp the values there and output a warning in the DebugBuild if an argument goes over 1 + Eps or below -1 - Eps.

-Dirk
I don't think this problem will cause you much headache, I mean we use unit quaternions, the last compoent has range -1,1 the closest you could get to one is cos when theta is 0 degrees, try calling the cos function with 0, 0+delta, 0 - delta I think in each case it'll be below one by nature of the algorithm, but I'm not sure tho, try it.

Tim

[Edited by - timw on September 12, 2005 5:30:03 PM]
Quote:Original post by timw
I don't think this problem will cause you much headache, ...

The problem is that an operation (such as multiplying by another quaternion) could cause the w term to go outside the range [-1,1]. If it does, it won't be by much, but even 1.000001 will cause acos() to blow up.

BTW, the same problem exists for getting the angle between two unit vectors by finding the arccosine of the dot product:
    angle = acos( dot( v0, v1 ) );  // <-- ouch 
In a perfect world there would be no problems, but in these cases floating-point precision comes into play.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
I didn't know acos was that sensitive, I wrote some quaternion code a while back, but never needed to extract the angle.
Quote:

BTW, the same problem exists for getting the angle between two unit vectors by finding the arccosine of the dot product:

angle = acos( dot( v0, v1 ) ); // <-- ouch


This is exactly where I get blown up. I am writing some kind of physic code and during the constraint satisfaction through relaxation when I try to impose an orientation constraint I get blown up during the iteration when q.w gets slightly over 1.0.

-Dirk
jyk's solution looks like it should work.

For a physics constraint problem in particular it may be possible to improve the situation by turning around the problem. Instead of finding the angle by the arccosine of the dot product and then comparing it to a constraint angle, find the constraint on the dot product by taking the cosine of the constraint angle. This is more robust than acos and can make the calculations simpler.

This topic is closed to new replies.

Advertisement