calculate angles between two vectors

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Auradrummer
Posts: 260
Joined: Thu Apr 17, 2008 1:38 pm
Location: Brasopolis - Brazil

calculate angles between two vectors

Post by Auradrummer »

Hello guys,

Is a quite simple, but a useful tool. I think there is nothing like this in class vector3d. So, I built this inside vector3d.h and worked fine.

Code: Select all

        //! Get angle between vectors
        T getAngleBetween (const vector3d<T>& vec2)
        {
            f32 angle;              
            angle = acos((X * vec2.X + Y * vec2.Y + Z * vec2.Z)/(
            sqrt(X*X + Y*Y + Z*Z) * sqrt(pow(vec2.X,2) + pow(vec2.Y,2) + pow(vec2.Z,2))
            ));
            return angle;
        } 
That's it.
Professional Software Developer and Amateur Game Designer ;-)
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

maybe I don't get your goal, but what angle do you calculate there ??? :shock:
I thought there are 3 angles between 2 vectors (X, Y and Z), but you're calculating just one angle !!!

also because it uses a template it should be:

Code: Select all

T angle;
and not

Code: Select all

f32 angle;
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
porcus
Posts: 149
Joined: Sun May 27, 2007 6:24 pm
Location: Germany

Post by porcus »

There a two angles which can be returned by this function (the bigger
and the smaller one). If you want to make sure to get always the smaller
angle you have to use: abs(X * vec2.X + Y * vec2.Y + Z * vec2.Z) instead
of (X * vec2.X + Y * vec2.Y + Z * vec2.Z).
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

@Acki: Given any two arbitrary vectors, they lie in a plane. The code that Auradrummer wrote calculates this angle.

@Auradrummer: Actually, although not completely self-evident, this functionality is possible with the current implementation of vector3d. It's great that you're studying these new topics though and contributing what you are learning! I just want to give you a little bit of constructive criticism because I know of you, from the past, and I know you are truly trying.

Firstly, Acki is correct, you should use the template parameter T; secondly, in its current state, your code is hard to read and the style is incoherent. That's an easy fix. For example, here is code that does the same thing, but is a bit more readable:

Code: Select all

//! Get planar angle between vectors
T getAngleBetween (const vector3d<T>& vec2)
{
	const T dotProduct = X * vec2.X + Y * vec2.Y + Z * vec2.Z;
	const T length1 = sqrt (X * X + Y * Y + Z * Z);
	const T length2 = sqrt (vec2.X * vec2.X + vec2.Y * vec2.Y + vec2.Z * vec2.Z);
	
	const T cosAngle = dotProduct / (length1 * length2);
	return acos (cosAngle);
}
Now with that exercise out of the way, it's time to talk about how Irrlicht already provides the capability for you to do this. You could either normalize the vectors before hand and then calculate the dot product [1], or calculate the dot product and then divide by the sum of the lengths like your code does [2].

[1]: A note must be made that vector3d<T>::normalze does modify the vector.

Code: Select all

vec1.normalize();
vec2.normalize();

const f32 angle = vec1.dotProduct( vec2 );
[2]: Equivalent to what you wrote above

Code: Select all

const f32 dp = vec1.dotProduct( vec2 );
const f32 angle = dp / (vec1.getLength() + vec2.getLength());
So the reason you probably don't see that code inside of vector3d is because the implementation in code is trivial. You could of course keep the modification for yourself in your own copy of Irrlicht, but I don't think the developers plan on putting the code into the class.

At any rate, great topic Auradrummer. Keep up the hard work.
TheQuestion = 2B || !2B
DavidJE13
Posts: 165
Joined: Tue Jan 09, 2007 7:17 pm

Post by DavidJE13 »

Just saw a small typo - lengths should be multiplied not added, and an arccos is missing;

Code: Select all

const f32 angle = acos( vec1.dotProduct( vec2 ) / (vec1.getLength() * vec2.getLength()) );
(I know that Halifax knows this - I'm simply pointing it out in case somebody tries to use the final code verbatim and has problems)

Also since it hasn't been pointed out yet, x * x is WAY faster than pow( x, 2 )
and in the original code, sqrt(X*X + Y*Y + Z*Z) * sqrt(pow(vec2.X,2) + pow(vec2.Y,2) + pow(vec2.Z,2)) can be sped up hugely by changing it to;

Code: Select all

sqrt( (X*X + Y*Y + Z*Z) * (vec2.X*vec2.X + vec2.Y*vec2.Y + vec2.Z*vec2.Z) )
which has the advantage of only 1 call to sqrt. This can be done with the built-in functions too, so the absolute fastest method for getting the angle between 2 arbitrary (not necessarily normalised) vectors is this;

Code: Select all

const f32 angle = acos( vec1.dotProduct( vec2 ) / core::squareroot( vec1.getLengthSQ() * vec2.getLengthSQ() ) );
edit: wait, I just noticed reciprocal_squareroot in the API;

Code: Select all

const f32 angle = acos( vec1.dotProduct( vec2 ) * core::reciprocal_squareroot( vec1.getLengthSQ() * vec2.getLengthSQ() ) );
has the advantage of being compiled back to the code above in the worst-case, and take advantage of any optimised reciprocal squareroot functions (i.e. if FAST_MATH is turned on)
I actually can't see why this shouldn't be included in Irrlicht;

Code: Select all

T getAngleFrom(const vector3d<T>& other) const
{
    T length = getLengthSQ() * other.getLengthSQ();
    if( core::equals(length, 0.0) )
        return 0.0;
    return acos( dotProduct( other ) * core::reciprocal_squareroot( length ) ) * (T) RADTODEG64;
}
would be quite a nice function to have sometimes
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

Nice catch there DavidJE13, thanks. By the way, very comprehensive post there, I agree with everything you said; furthermore, on second thought, given the optimizations, I can't see why it wouldn't be included either.
TheQuestion = 2B || !2B
pippy3
Posts: 155
Joined: Tue Dec 15, 2009 7:32 am

Post by pippy3 »

Every time I need to get the angle between two vectors I simply normalize them then get the dotproduct.

If one of the vectors are a rotation, and not a direction there's a function in vector3d<T> rotationToDirection.

For example, just today I needed to check if an enemy was facing the player. I had something like this:

Code: Select all

vector3df faceVector = getRotation().rotationToDirection();
vector3df toPlayerVector = line3df(getPosition(), player->getPosition()).getVector().normalize();
f32 dotProduct = faceVector.dotProduct(toPlayerVector);
the closer dotProduct is to 1 the closer the vectors are to each other. Both the vectors must have the length of 1. (so they have to be normalized)

Though OPs solution is easier to read
jeromegz
Posts: 14
Joined: Sat Aug 02, 2008 10:51 am

Post by jeromegz »

sqrt (vec2.X * vec2.X + vec2.Y * vec2.Y + vec2.Z * vec2.Z);

use fsqrt instead :

Code: Select all


double fSqrt (double y) {//no q3 or lemont code, read
    double x, z, tempf;
    unsigned long *tfptr = ((unsigned long *)&tempf) + 1;

	tempf = y;
	*tfptr = (0xbfcdd90a - *tfptr)>>1; /* estimate of 1/sqrt(y) */
	x =  tempf;
	z =  y*0.5;                        /* hoist out the “/2”    */
	x = (1.5*x) - (x*x)*(x*z);         /* iteration formula     */ 
 
	//x = (1.5*x) – (x*x)*(x*z); //repeat for more 
	//x = (1.5*x) – (x*x)*(x*z);
	//x = (1.5*x) – (x*x)*(x*z);/ 
	return x*y;
    }
code under zliblicence / free to use (see Paul Hsieh's) http://www.azillionmonkeys.com/qed/sqroot.html

and

fsqrt(X*X + Y*Y + Z*Z) * sqrt(pow_a(vec2.X,2) + pow_a(vec2.Y,2) + pow_a(vec2.Z,2))
with :

Code: Select all

#define CARRE(x) x*x
unsigned pow_a(int a, int b)
{ return  CARRE(a);
}
Hello
arnir
Competition winner
Posts: 154
Joined: Sat Jan 20, 2007 4:36 pm
Location: Czech Republic

Post by arnir »

wow so many solutions for this problem
so, what is the best solution for this?
I use it frequently in my AI code
thnaks :wink:
programmer is bad designer
designer is bad programmer
REDDemon
Developer
Posts: 1044
Joined: Tue Aug 31, 2010 8:06 pm
Location: Genova (Italy)

Post by REDDemon »

yeah and it is not necessary use acos or sqrt. you can do it also with elementary operations :)
Junior Irrlicht Developer.
Real value in social networks is not about "increasing" number of followers, but about getting in touch with Amazing people.
- by Me
Post Reply