Maths - Angle between vectors

How do we calculate the angle between two vectors?

For 2D Vectors

This is relatively simple because there is only one degree of freedom for 2D rotations. If v1 and v2 are normalised so that |v1|=|v2|=1, then,

angle = acos(v1•v2)

where:

The only problem is, this won't give all possible values between 0° and 360°, or -180° and +180°. In other words, it won't tell us if v1 is ahead or behind v2, to go from v1 to v2 is the opposite direction from v2 to v1.

In most math libraries acos will usually return a value between 0 and π (in radians) which is 0° and 180°.

If we want a + or - value to indicate which vector is ahead, then we probably need to use the atan2 function (as explained on this page). using:

angle of 2 relative to 1= atan2(v2.y,v2.x) - atan2(v1.y,v1.x)

For a discussion of the issues to be aware of when using this formula see the page here.

For 3D Vectors

Axis Angle Result

This is easiest to calculate using axis-angle representation because:

as explained here

this is taken from this discussion.

So, if v1 and v2 are normalised so that |v1|=|v2|=1, then,

angle = acos(v1•v2)

axis = norm(v1 x v2)

If the vectors are parallel (angle = 0 or 180 degrees) then the length of v1 x v2 will be zero because sin(0)=sin(180)=0. In the zero case the axis does not matter and can be anything because there is no rotation round it. In the 180 degree case the axis can be anything at 90 degrees to the vectors so there is a whole range of possible axies.

angle (degrees) sin(angle) cos(angle) v1•v2 v1 x v2
0 0 1 1 0,0,0
90 1 0 0 unit len
180 0 -1 -1 0,0,0
270 -1 0 0 unit len

Quaternion Result

One approach might be to define a quaternion which, when multiplied by a vector, rotates it:

p2=q * p1

This almost works as explained on this page.

However, to rotate a vector, we must use this formula:

p2=q * p1 * conj(q)

where:

This is a bit messy to solve for q, I am therefore grateful to minorlogic for the following approach which converts the axis angle result to a quaternion:

The axis angle can be converted to a quaternion as follows, let x,y,z,w be elements of quaternion, these can be expressed in terms of axis angle as explained here.

angle = arcos(v1•v2/ |v1||v2|)
axis = norm(v1 x v2)
s = sin(angle/2)
x = axis.x *s
y = axis.y *s
z = axis.z *s
w = cos(angle/2)

We can use this half angle trig formula on this page: sin(angle/2) = 0.5 sin(angle) / cos(angle/2)

so substituting in quaternion formula gives:
s = 0.5 sin(angle) / cos(angle/2)
x = norm(v1 x v2).x *s
y = norm(v1 x v2).y *s
z = norm(v1 x v2).z *s
w = cos(angle/2)

multiply x,y,z and w by 2* cos(angle/2) (this will de normalise the quaternion but we can always normalise later)

x = norm(v1 x v2).x * sin(angle)
y = norm(v1 x v2).y * sin(angle)
z = norm(v1 x v2).z * sin(angle)
w = 2 * cos(angle/2) * cos(angle/2)

now substitute half angle trig formula on this page: cos(angle/2) = sqrt(0.5*(1 + cos (angle)))

x = norm(v1 x v2).x * sin(angle)
y = norm(v1 x v2).y * sin(angle)
z = norm(v1 x v2).z * sin(angle)
w = 1 + cos (angle)

because |v1 x v2| = |v1||v2| sin(angle) we can normalise (v1 x v2) by dividing it with sin(angle),

also apply v1•v2 = |v1||v2| cos(angle)so,

x = (v1 x v2).x / |v1||v2|
y = (v1 x v2).y/ |v1||v2|
z = (v1 x v2).z/ |v1||v2|
w = 1 + v1•v2 / |v1||v2|

If v1 and v2 are already normalised then |v1||v2|=1 so,

x = (v1 x v2).x
y = (v1 x v2).y
z = (v1 x v2).z
w = 1 + v1•v2

If v1 and v2 are not already normalised then multiply by |v1||v2| gives:

x = (v1 x v2).x
y = (v1 x v2).y
z = (v1 x v2).z
w = |v1||v2| + v1•v2

Matrix Result

Using the quaternion to matrix conversion here we get:

1 - 2*qy2 - 2*qz2 2*qx*qy - 2*qz*qw 2*qx*qz + 2*qy*qw
2*qx*qy + 2*qz*qw 1 - 2*qx2 - 2*qz2 2*qy*qz - 2*qx*qw
2*qx*qz - 2*qy*qw 2*qy*qz + 2*qx*qw 1 - 2*qx2 - 2*qy2

so substituting the quaternion results above into the matrix we get:

1 - 2*(v1 x v2).y2 - 2*(v1 x v2).z2 2*(v1 x v2).x*(v1 x v2).y - 2*(v1 x v2).z*(1 + v1•v2) 2*(v1 x v2).x*(v1 x v2).z + 2*(v1 x v2).y*(1 + v1•v2)
2*(v1 x v2).x*(v1 x v2).y + 2*(v1 x v2).z*(1 + v1•v2) 1 - 2*(v1 x v2).x2 - 2*(v1 x v2).z2 2*(v1 x v2).y*(v1 x v2).z - 2*(v1 x v2).x*(1 + v1•v2)
2*(v1 x v2).x*(v1 x v2).z - 2*(v1 x v2).y*(1 + v1•v2) 2*(v1 x v2).y*(v1 x v2).z + 2*(v1 x v2).x*(1 + v1•v2) 1 - 2*(v1 x v2).x2 - 2*(v1 x v2).y2

Substituting the following expansions:

(v1 x v2).x = v1.y * v2.z - v2.y * v1.z
(v1 x v2).y = v1.z * v2.x - v2.z * v1.x
(v1 x v2).z = v1.x * v2.y - v2.x * v1.y
(v1 x v2).x2 = v1.y * v2.z * v1.y * v2.z + v2.y * v1.z * v2.y * v1.z - 2 * v2.y * v1.z * v1.y * v2.z
(v1 x v2).y2 = v1.z * v2.x * v1.z * v2.x + v2.z * v1.x * v2.z * v1.x - 2* v2.z * v1.x * v1.z * v2.x
(v1 x v2).z2 = v1.x * v2.y * v1.x * v2.y +v2.x * v1.y * v2.x * v1.y - 2 * v2.x * v1.y * v1.x * v2.y
v1•v2 = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z

This is getting far too complicated ! can anyone help me simplify this?

Thank you again to minorlogic who gave me the following solution:

Hi !
and i think can help in matrix version.

you can use :
https://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToMatrix/index.htm

And will get some thing :

matrix33 RotAngonst vector3& from, const vector3& to )
{
from.norm();
to.norm();

vector3 vs = cross(from, to); // axis multiplied by sin

vector3 v(vs);
v.norm(); // axis of rotation
float ca = dot(from, to) ; // cos angle

vector3 vt(v*(1.0f - ca));

matrix33 rotM;
rotM.M11 = vt.x * v.x + ca;
rotM.M22 = vt.y * v.y + ca;
rotM.M33 = vt.z * v.z + ca;

vt.x *= v.y;
vt.z *= v.x;
vt.y *= v.z;

rotM.M12 = vt.x - vs.z;
rotM.M13 = vt.z + vs.y;
rotM.M21 = vt.x + vs.z;
rotM.M23 = vt.y - vs.x;
rotM.M31 = vt.z - vs.y;
rotM.M32 = vt.y + vs.x;
return rotM;
}

Code

axis-angle version
sfrotation angleBetween(sfvec3f v1,sfvec3f v2) { 
	float angle; 
	// turn vectors into unit vectors 
	n1 = v1.norm();
	n2 = v2.norm(); 
	angle = Math.acos( sfvec3f.dot(n1,n2) ); 
	// if no noticable rotation is available return zero rotation
	// this way we avoid Cross product artifacts 
	if( Math.abs(angle) < 0.0001 ) return new sfrotation( 0, 0, 1, 0 ); 
	// in this case there are 2 lines on the same axis 
	if(Math.abs(angle)-Math.pi) < 0.001){ 
		n1 = n1.Rotx( 0.5f ); 
		// there are an infinite number of normals 
		// in this case. Anyone of these normals will be 
		// a valid rotation (180 degrees). so I rotate the curr axis by 0.5 radians this way we get one of these normals 
	}
 	sfvec3f axis = n1;
	axis.cross(n2);
	return new sfrotation(axis.x,axis.y,axis.z,angle); 
}
quaternion version
/** note v1 and v2 dont have to be nomalised, thanks to minorlogic for telling me about this:
* https://www.euclideanspace.com/maths/algebra/vectors/angleBetween/minorlogic.htm
*/
sfquat angleBetween(sfvec3f v1,sfvec3f v2) { 
	float d = sfvec3f.dot(v1,v2); 
	sfvec3f axis = v1;
	axis.cross(v2);
    float qw = (float)Math.sqrt(v1.len_squared()*v2.len_squared()) + d;
	if (qw < 0.0001) { // vectors are 180 degrees apart
		return (new sfquat(0,-v1.z,v1.y,v1.x)).norm;
	} 
	sfquat q= new sfquat(qw,axis.x,axis.y,axis.z); 
    return q.norm();
} 

matrix version

sfmatrix angleBetween(sfvec3f v1,sfvec3f v2) {
	// turn vectors into unit vectors
	n1 = v1.norm();
	n2 = v2.norm(); 	sfvec3f vs = new sfvec3f(n1);
	vs.cross(n2); // axis multiplied by sin	sfvec3f v = new sfvec3f(vs);
	v = v.norm(); // axis of rotation
   
	float ca = dot(n1, n2) ; // cos angle	sfvec3f vt = new sfvec3f(v);	vt.scale((1.0f - ca);	sfmatrix rotM = new sfmatrix();
	rotM.m11 = vt.x * v.x + ca;
	rotM.m22 = vt.y * v.y + ca;
	rotM.m33 = vt.z * v.z + ca;	vt.x *= v.y;
	vt.z *= v.x;
	vt.y *= v.z;	rotM.m12 = vt.x - vs.z;
	rotM.m13 = vt.z + vs.y;
	rotM.m21 = vt.x + vs.z;
	rotM.m23 = vt.y - vs.x;
	rotM.m31 = vt.z - vs.y;
	rotM.m32 = vt.y + vs.x;
	return rotM;
}

see also code from minorlogic


metadata block
see also:

quaternion of mid angle

Correspondence about this page

Book Shop - Further reading.

Where I can, I have put links to Amazon for books that are relevant to the subject, click on the appropriate country flag to get more details of the book or to buy it from them.

cover If you are interested in 3D games, this looks like a good book to have on the shelf. If, like me, you want to have know the theory and how it is derived then there is a lot for you here. Including - Graphics pipeline, scenegraph, picking, collision detection, bezier curves, surfaces, key frame animation, level of detail, terrain, quadtrees & octtrees, special effects, numerical methods. Includes CDROM with code.

Other Math Books

Terminology and Notation

Specific to this page here:

 

This site may have errors. Don't use for critical systems.

Copyright (c) 1998-2023 Martin John Baker - All rights reserved - privacy policy.