3D Vector Rotation Issue

This topic is 4250 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

Hello All, I'm having some trouble calculating the rotation between two 3D Vectors. I have come up empty handy thus far in my search, only finding a few 'not-so-clean' solutions. Here's the deal, for simplicity sakes, I'll use normalized vectors, v1 = {1, 0, 0} v2 = {0.7070, 0.7070, 0} //45 degree Rotation from v1 v3 = {0.7070, -0.7070, 0} //315 (or -45) degree Rotation from v1 The dot product between two vectors only returns a value between 0 and 180; so: arccos(v1 dot v2) = 45 degrees (assuming conversion from radians) arccos(v1 dot v3) = 45 degrees I need to find a clean method to determine whether I need to subtract my new found angle from 360 to get a proper rotation ammount. as the proper calculation for v3 should be: arccos(v1 dot v3) = 45 degrees 360 - 45 = 315; Any help would be appreciated, Thanks

Share on other sites
If you have an 'atan2' function available, a somewhat nicer algorithm for finding the (unsigned) angle between two vectors in 3D is:
angle = atan2(length(cross(v1,v2)),dot(v1,v2));
This is more stable than the acos() method, and also does not require that the input be unit length.

The corresponding 2D algo is:
angle = atan2(perp_dot(v1,v2),dot(v1,v2));
The difference being that the latter returns the signed rather than unsigned angle.

In 3D you must have a reference plane to determine the sign of the angle between two vectors. Given the normal to such a plane, after using the algo mentioned above you can find the sign as:
sign(dot(cross(v1,v2)),normal)
The example you posted however is essentially 2D. This may just be incidental, although it's common to need to solve 2D problems in a 3D context. If it will always be 2D, use the second algorithm. If it will in general be 3D, use the first, and choose a reference vector to determine the sign.

Also, if you don't have access to an atan2 function, there are other ways to determine the sign, so post back if you need help with that.

'Hope that helps.

Share on other sites

I came across a solution that was very similiar to that, but ran into a small problem with it.

How can you generate the 3D reference plane, and always have the normal pointing in the correct direction. The plane would need to be computed at call time, as it needs to be on the same plane as the two given vectors.

The method I know to calculate a plane is:

Ax + By + Cz + D = 0

where:
x,y,z are values of a point on the plane(or one of the vectors in my case),
A,B,C are the values of the normal calculated from the cross(v1,v2)

The problem is that my reference plane does not create a consistent normal as the normal calculation uses the cross between v1 and v2 (or v1 and v3). The normal needs to point the same direction on the reference plane, no matter if I'm measuring the 45 degree angle or the 315 degree angle, otherwise it's not really a reference plane.

So what would the proper method be to calculate a consistent normal to the plane created by (v1 and v2) as well as (v1 and v3).

I should also add that my calculations are in 3D Space, the vectors I supplied were for simplicity sakes only.

Share on other sites
It would certainly be possible to address the above questions, but I think they'd be easier to answer if you were to explain in more detail what you're trying to do. Is this for sorting mesh edges? Determining which direction to turn a ship in a 3D space sim? For vehicle AI on an irregular terrain? Given more info I'm sure someone could recommend an appropriate solution.

Share on other sites
Sorry about that, I should have done that from the start.

I need to create a function that can precisely extract the degrees of rotation from a given rotation matrix. This is essentially what I have so far.

I'm attempting to calculate all my rotations by comparing the world Z vector (0,0,1) to the Z vector (at vector) of the given rotation matrix.

vAt = Extract Z Column of Rotation Matrix

RotationVector.x = angleBetween( v(0,0,1), normalize(0, vAt.y, vAt.z) );
RotationVector.y = angleBetween( v(0,0,1), normalize(vAt.x, 0, vAt.z) );
RotationVector.z = angleBetween( v(1,0,0), normalize(vAt.x, vAt.y, 0) );

Hopefully this clarifies my problem a little bit more.

Share on other sites
Ok, it sounds like you want a matrix-to-Euler-angle conversion, in which case you're reinventing the wheel a bit, as there are already algorithms and code samples for this to be found online.

Extracting Euler angles from a matrix is a bit tricky because a) there are many different orders (xyz, zyx, etc.) in which the angles can be extracted, and b) there isn't always a unique solution.

The best example I can think of that you can find online is Ken Shoemake's code, which abstracts the order away using permuted indices for the matrix, thus allowing you to perform the conversion using whatever convention you prefer.

To ask just one more question, why do you need to extract these angles from the matrix? Just curious...

Share on other sites
Excellent, that helps a bunch. I still need to do my homework on the complete method, but this definitely gives me a solid starting point.

I'm extracting the angles from the matrix for use in a custom game engine that utilizes PhysX. Unfortunately PhysX constantly updates the objects matrices, and does not keep track of the rotation ammounts. I need to be able to get and set the rotation values, and until now, I have been unable to find a clean method to do so.

Share on other sites

EDIT:
note outX is radians rotation about X axis (pitch), outY is yaw, outZ is bank,
mat[0][0] is the 1st row 1st column, OpenGL right hand coordinate system.

void	Matrix3x3::ToRadians(float	*outX,float	*outY,float	*outZ){	if(mat[2][2] > -.00001f && mat[2][2] < .00001f)	//it is zero	{		if(mat[2][1] >= 0)			*outX = Export.mVars.PI;		else			*outX = -Export.mVars.PI;	}	else	//it isn't zero	{		*outX	=	atan2(mat[2][1],mat[2][2]);	//pitch	}		*outY	=	asin(-mat[2][0]);	//yaw	if(mat[0][0] > -.00001f && mat[0][0] < .00001f)	//it's zero	{		if(mat[0][0] >= 0)			*outZ = Export.mVars.PI;		else			*outZ = -Export.mVars.PI;	}	else	//it isn't zero	{		*outZ	=	atan2(mat[1][0],mat[0][0]);	//roll	}}

Share on other sites
Hello again,

After a brief break I've come back to again try another stab at the rotation code, and have a few questions about some of the previous posts.

There seems to be a problem when rotating 90 degrees in the X or Z axis, with the given code sample.

For example, a 'Z-axis' 3x3 rotation matrix, for a 90 degree rotation is:

0 1 0
-1 0 0
0 0 1

The code provided by tthibault contains this conditional statement:

if(mat[0][0] > -.00001f && mat[0][0] < .00001f)

So if [0][0] in the matrix is near 0 (which it always is if the rotation is 90 degrees about the Z-axis), it will either return PI, or negative PI. I believe this is incorrect as it should return PI/2. I understand that there are many orders that rotation can be extracted from a matrix, however, I'm assuming that the provided code extracts in the xyz order.

Assuming that we are passing a combined rotation matrix (X*Y*Z) to the given function, does this also require the matrices to be combined in the same manner? (as in matrix multiplication A*B != B*A)

Any suggestions would be muchly appreciated.

Thanks again.

Share on other sites
I happen to be documenting some work I'm doing with coordinate frames and various related computations. I ran across the angle computation algorithm I'm using and recalled your post. You are right that it should be PI/2. The reason for the special checks are to handle the cases where you can get ambiguities.

EDIT: Here's some brief mention of the ambiguities if you are interested (also see the gimbal lock link for a little more explanation):
http://en.wikipedia.org/wiki/Euler_angles#Angle_ranges

Yes the order of matrix multiplication is important. Not sure what the other example was using, but in my case it's a 3-2-1 order (psi, theta, phi in z,y,x order). So to form your combined matrix, you'd multiply in reverse order C_total = C_phi*C_theta*C_psi.
Where C_angle is the transformation for making a rotation by that angle.

For what it's worth, here's my code (in Matlab, probably converted from Fortran77, originally written by a co-worker). cib represents an inertial-to-body transformation matrix, and psi,theta,phi are yaw,pitch,roll).
function [psi,theta,phi] = eulercalc(cib)%function [psi,theta,phi] = eulercalc(cib)%% outputs in rad%% for a 3-2-1 rotation% (psi about z, theta about subsequent y, and phi about final x)%eps = 1d-14;cib13 = max(-1, min(cib(1,3), 1));theta = asin(-cib13);if (abs(abs(theta) - pi/2) <= eps)   if theta < 0      theta = -pi/2;   else      theta = pi/2;   endendif (abs(theta) == pi/2)   psi = atan2(cib(3,2), cib(2,2));   if theta < 0      psi = -psi;   end   phi = 0.0;else   psi = atan2(cib(1,2), cib(1,1));   phi = atan2(cib(2,3), cib(3,3));end

• 15
• 9
• 13
• 41
• 15