Jump to content
  • Advertisement
Sign in to follow this  
gretty

Function that determines if angle lies between 2 others

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello

I am trying to figure out whether a angle lies between 2 other angles. I have been trying to create a simple function to perform this but none of my techniques will work for all possible values of the angles.

Can you help me edit my function to correctly determine if a angle lies between 2 other angles?
200bmaf.png
In the above picture; I use the green point as the central point, then I determine the angle of each line to the green point. I then calculate the angle of the black point to the green point. I am trying to check if the angle of the black dot is BETWEEN the 2 lines' angles.

NOTE: In my case; an angle(targetAngle) is said to lie between 2 other angles IF the difference between the 2 angles is < 180 degrees AND the targetAngle lies in the cavity made by those 2 angles.

The following code should work but it fails for these(which do lie between the angle):
- is_angle_between(150, 190, 110)
- is_angle_between(3, 41, 345)

bool is_angle_between(int target, int angle1, int angle2)
{
int rAngle1 = ((iTarget - iAngle1) % 360 + 360) % 360;
int rAngle2 = ((iAngle2 - iAngle1) % 360 + 360) % 360;
return (0 <= rAngle1 && rAngle1 <= rAngle2);
}

// Example usage
is_angle_between(3, 41, 345);


Another technique I attempted which also doesn't work:

int is_angle_between(int target, int angle1, int angle2)
{
int dif1 = angle1-angle2;
int dif2 = angle2-angle1;
int uDif1 = convert_to_positive_angle( dif1 ); // for eg; convert -15 to 345
int uDif2 = convert_to_positive_angle( dif2 );

if (uDif1 <= uDif2) {
if (dif1 < 0) {
return (target <= angle1 && target >= angle2);
}
else return (in_between_numbers(iTarget, iAngle1, iAngle2));
}
else {
if (dif2 < 0) {
return (target <= angle1 && target >= angle2);
}
else return (in_between_numbers(iTarget, iAngle1, iAngle2));
}

return -1;
}

Share this post


Link to post
Share on other sites
Advertisement
I assume that the angle of a line to a point is an angle that starts from the positive x-axis and rotates about the point in a clockwise direction.

As you no doubt have figured out, it is easiest to figure out if an angle is "between" the other angles if the difference between the angles is no bigger than 180 degress. If it expands beyond that, you're bound to run into trouble. Therefore, my suggestion is as follows: add 360 degrees to all angles until they end up in a setup that is most desirable. The angles are going to be in the same place but it'll be easier to figure out if the angle you're testing is between them or not.

Allow me to show you how to do that:

In the above picture you have angles 41 and 345 and you want to see if 3 is between them.

You take the bigger angle and you take away the smaller one to check if they are within 180 degrees of each other. If they are not, add 360 degrees to the smaller one, re-select the angle that's bigger and check again if they are within 180 degrees. Keep repeating until they are.

Now, shifting the angle you're testing is a little different because it is OK for it to be outside of those angles. This time we want to make sure that the target angles is within 360 degress of the biggest angle before we make the test.

Now take the biggest degree, take away the angle you're checking ( 3 degrees ), and divide (clip the remainder) by 360. Take the result, multiply by 360 and add to 3. Now the outer angles should be within 180 degrees of each other and the target angle within 360. Now you can check if 345 < 363 < 401, which comes out to be true.

Here's some code:

theta is the angle that's being tested for sandwiching.


bool is_angle_between(double alpha, double theta, double beta)
{
while( abs(beta - alpha) > 180 )
{
if(beta > alpha)
alpha += 360;
else
beta += 360;
}

// Here I replace alpha with beta if alpha is bigger to keep things consistent
// You can choose the bigger angle however you please
if(alpha > beta)
{
double phi = alpha;
alpha = beta;
beta = phi;
}

int threeSixtyMultiple = (beta - theta)/360;
theta += 360*threeSixtyMultiple;

return (alpha < theta) && (theta < beta)
}


This is completely untested biggrin.png

EDIT: I've tested the function and it seems to work. Sorry about the indentation, something went awry (fixed it ^.^ expanded tabs to spaces) >.> Edited by boogyman19946

Share this post


Link to post
Share on other sites
boogeyman where are you located so I can come over and kiss you! :P

Thank you. I've been racking my brain for hours on this. Thanks again

Share this post


Link to post
Share on other sites
As I have posted many times, using angles is almost always avoidable, and doing so is almost always a good idea. This case is no exception.

Say you have three vectors: target, edge1 and edge2. You want to determine if the vector target lies in between edge1 and edge2. This can be expressed as saying that the pairs of vectors (edge1,target), (target,edge2) and (edge1,edge2) all have the same orientation. You can check if the orientation of a pair of vectors is the same as the orientation of the canonical basis by looking at the sign of the determinant of the matrix whose columns are the two vectors.

Here's some untested code:

float determinant(Vector2D v1, Vector2D v2) {
return v1.x * v2.y - v1.y * v2.x;
}

bool are_same_sign(float a, float b, float c) {
if (a>0) return b>0 && c>0;
if (a<0) return b<0 && c<0;
return false;
}

bool is_vector_between(Vector2D target, Vector2D edge1, Vector2D edge2) {
return are_same_sign(determinant(edge1, target),
determinant(target, edge2),
determinant(edge1, edge2));
}


Notice that the code only involves six multiplications, three subtractions and a few comparisons, while the code you currently have needs to use something like atan2, which is much much more expensive. Also, the code without angles is usually easier to get right and doesn't have special cases. Edited by alvaro

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!