# Problem with acos

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

## Recommended Posts

OK here is my function to calculate the angle between two vectors.

float AngleBetweenVectors(const Vector3& vector1,const Vector3& vector2)
{
//A.B
float dotProduct = Dot(vector1,vector2);

//|A|*|B|
float vectorsMagnitude = vector1.magnitude() * vector2.magnitude();

//avoid division by zero
if(vectorsMagnitude == 0)
return 0;

//cos(theta) = A.B / |A|*|B|
float angle = acos( dotProduct / vectorsMagnitude );

//check the number
if(_isnan(angle))
{
return 0;
}

return angle;
}


I use this to check if the player is facing a NPC in my game.I calculate the vector from the player to the NPC and normalize it and then I calculate the normalized facing vector of the player and use the above method to calculate the angle between these vectors.If the angle between them is less than 60 degress the player is facing the NPC.Everything works perfect except sometimes when I am facing the NPC exactly 180 degrees away the method fails and tells me that I am facing the NPC.I stepped through the code and check the values in this method.THe dot product returns -1 which is correct as the two vectors are facing away from each other the vector magnitude returns 0.99999 something which is almost correct,but then the acos(dot/magnitude) returns a indefinite number which is caught by isnan and the method returns 0 where infact the angle is 180.So whats up?Is this a precision problem? I made a dirty fix like this in the isnan part : //check the number if(_isnan(angle)) { if(dotProduct < 0) return 180; else return 0; } But it is ugly so is there any way to solve this problem maybe by using doubles or something else?

##### Share on other sites
acos requires an argument greater or equal -1 and less or equal +1. In your case you give it fabs(1.0/0.999) > 1, and that cause the trouble, I think.
Quote:
 from acos man pageacos(x) returns a NAN and raises the "invalid" floating-point exception for |x| > 1.

You should restrict the argument to avoid the problem:
float arg = dot/magnitude;if(arg>+1) arg = +1;if(arg<-1) arg = -1;float angle = acos(arg);bool iSeeYou = angle < viewConeAngle;

Even better, you avoid to use acos at all. You can do somethink like
float arg = dot/magnitude;bool iSeeYou = arg > cosOfViewConeAngle;

##### Share on other sites
haegarr's suggestion is probably the way to go, but if for some reason you do want to compute the angle itself, a more robust way to compute the (unsigned) angle between two vectors in 3-d is as follows:
angle = atan2(length(cross(a, b)), dot(a, b));

##### Share on other sites
Quote:
 I calculate the vector from the player to the NPC and normalize it and then I calculate the normalized facing vector of the player and use the above method to calculate the angle between these vectors.If the angle between them is less than 60 degress the player is facing the NPC.
Why not skip the acos and the isnan stuff and just check whether the dot product of the two vectors is greater than cos(60°), that is, 0.5?

##### Share on other sites
Quote:
 Why not skip the acos and the isnan stuff and just check whether the dot product of the two vectors is greater than cos(60°), that is, 0.5?
I think that's what haegarr was getting at in his code sample (if I'm reading it right, that is).

##### Share on other sites
Quote:
 Original post by jykhaegarr's suggestion is probably the way to go, but if for some reason you do want to compute the angle itself, a more robust way to compute the (unsigned) angle between two vectors in 3-d is as follows:angle = atan2(length(cross(a, b)), dot(a, b));

If it's not too much trouble, mind explaining how that works? I'm having a little trouble visualizing it.

##### Share on other sites
Quote:
 Original post by nullsquaredIf it's not too much trouble, mind explaining how that works?
For sure:

|a x b| = |a| * |b| * sin( <a,b> )
a . b = |a| * |b| * cos( <a, b> )

|a x b| / (a . b) = sin( <a,b> ) / cos( <a,b> ) = tan( <a,b> )

Hence
<a,b> = atan( |a x b| / (a . b) ) = atan2( |a x b|, (a . b) )

EDIT: tan( y/x ) is in general not really the same as atan2( y, x ) because atan2 considers all quadarants.

[Edited by - haegarr on February 18, 2010 8:26:33 AM]

##### Share on other sites
Quote:
Original post by jyk
Quote:
 Why not skip the acos and the isnan stuff and just check whether the dot product of the two vectors is greater than cos(60°), that is, 0.5?
I think that's what haegarr was getting at in his code sample (if I'm reading it right, that is).
Yep, you're right, that was my intention.

##### Share on other sites
Quote:
Original post by haegarr
Quote:
 Original post by nullsquaredIf it's not too much trouble, mind explaining how that works?
For sure:

|a x b| = |a| * |b| * sin( <a,b> )
a . b = |a| * |b| * cos( <a, b> )

|a x b| / (a . b) = sin( <a,b> ) / cos( <a,b> ) = tan( <a,b> )

Hence
<a,b> = atan( |a x b| / (a . b) ) = atan2( |a x b|, (a . b) )

Oh cool, thanks for the explanation [smile]

##### Share on other sites
Great help guys thank you.

• 36
• 12
• 10
• 10
• 9
• ### Forum Statistics

• Total Topics
631359
• Total Posts
2999538
×