Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if angle lies between 2 other angles

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?

enter image description here

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;
}
like image 281
sazr Avatar asked Jul 10 '12 03:07

sazr


People also ask

How do you check if an angle is between two angles?

The add 180+360 then modulo 360 then subtract 180, effectively just converts everything to the range -180 to 180 degrees (by adding or subtracting 360 degrees). Then you can check the angle difference easily, whether it is within -45 to 45 degrees.

How do you find the distance between two angles?

Divide the height of the object by the tangent of the angle. For this example, let's say the height of the object in question is 150 feet. 150 divided by 1.732 is 86.603. The horizontal distance from the object is 86.603 feet.


7 Answers

bool is_angle_between(int target, int angle1, int angle2) 
{
  // make the angle from angle1 to angle2 to be <= 180 degrees
  int rAngle = ((angle2 - angle1) % 360 + 360) % 360;
  if (rAngle >= 180)
    std::swap(angle1, angle2);

  // check if it passes through zero
  if (angle1 <= angle2)
    return target >= angle1 && target <= angle2;
  else
    return target >= angle1 || target <= angle2;
}  
like image 51
fdermishin Avatar answered Sep 30 '22 16:09

fdermishin


Inspired by a post about Intervals in modular arithmetic:

static bool is_angle_between(int x, int a, int b) {
    b = modN(b - a);
    x = modN(x - a);

    if (b < 180) {
        return x < b;
    } else {
        return b < x;
    }
}

where (in case of checking angles) modN() would be implemented as

// modN(x) is assumed to calculate Euclidean (=non-negative) x % N.
static int modN(int x) {
    const int N = 360;
    int m = x % N;
    if (m < 0) {
        m += N;
    } 
    return m;
}
like image 41
sschuberth Avatar answered Sep 30 '22 18:09

sschuberth


void normalize( float& angle ) 
{
    while ( angle < -180 ) angle += 360;
    while ( angle >  180 ) angle -= 360;
}

bool isWithinRange( float testAngle, float a, float b )
{
    a -= testAngle;
    b -= testAngle;
    normalize( a );
    normalize( b );
    if ( a * b >= 0 )
        return false;
    return fabs( a - b ) < 180;
}
like image 20
iforce2d Avatar answered Sep 30 '22 17:09

iforce2d


If angle2 were always 0, and angle1 were always between 0 and 180, this would be easy:

return angle1 < 180 && 0 < target && target < angle1;

if I'm reading the requirements correctly.

But it's not that hard to get there.

int reduced1 = (angle1 - angle2 + 360) % 360; // and imagine reduced2 = 0
if (180 < reduced1) { angle2 = angle1; reduced1 = 360 - reduced1; } // swap if backwards
int reducedTarget = (target - angle2 + 360) % 360;
return reduced1 < 180 && 0 < reducedTarget && reducedTarget < reduced1;
like image 23
Marc Avatar answered Sep 30 '22 16:09

Marc


I've done this before by comparing angles.

enter image description here

In the sketch above vector AD will be between AB and AC if and only if

angle BAD + angle CAD == angle BAC

Because of floating point inaccuracies I compared the values after rounding them first to say 5 decimal places.

So it comes down to having an angle algorithm between two vectors p and q which is simply put like:

double a = p.DotProduct(q);
double b = p.Length() * q.Length();
return acos(a / b); // radians

I'll leave the vector DotProduct and Length calculations as a google search exercise. And you get vectors simply by subtracting the coordinates of one terminal from the other.

You should of course first check whether AB and AC are parallel or anti-parallel.

like image 34
acraig5075 Avatar answered Sep 30 '22 16:09

acraig5075


All the top answers here are wrong. As such I feel it is necessary for me to post an answer.

I'm just reposting a portion of an answer which I posted here: https://stackoverflow.com/a/42424631/2642059 That answer also deals with the case where you already know which angle is the lefthand side and righthand side of the reflexive angle. But you also need to determine which side of the angle is which.


1st to find the leftmost angle if either of these statements are true angle1 is your leftmost angle:

  1. angle1 <= angle2 && angle2 - angle1 <= PI
  2. angle1 > angle2 && angle1 - angle2 >= PI

For simplicity let's say that your leftmost angle is l and your rightmost angle is r and you're trying to find if g is between them.

The problem here is the seem. There are essentially 3 positive cases that we're looking for:

  1. l ≤ g ≤ r
  2. l ≤ g ∧ r < l
  3. g ≤ r ∧ r < l

Since you're calculating the lefthand and righthand sides of the angle, you'll notice there is an optimization opportunity here in doing both processes at once. Your function will look like:

if(angle1 <= angle2) {
    if(angle2 - angle1 <= PI) {
        return angle1 <= target && target <= angle2;
    } else {
        return angle2 <= target || target <= angle1;
    }
} else {
    if(angle1 - angle2 <= PI) {
        return angle2 <= target && target <= angle1;
    } else {
        return angle1 <= target || target <= angle2;
    }
}

Or if you need it you could expand into this nightmare condition:

angle1 <= angle2 ?
(angle2 - angle1 <= PI && angle1 <= target && target <= angle2) || (angle2 - angle1 > PI && (angle2 <= target || target <= angle1)) :
(angle1 - angle2 <= PI && angle2 <= target && target <= angle1) || (angle1 - angle2 > PI && (angle1 <= target || target <= angle2))

Note that all this math presumes that your input is in radians and in the range [0 : 2π].

Live Example

like image 30
Jonathan Mee Avatar answered Sep 30 '22 18:09

Jonathan Mee


Is angle T between angles A and B, there are always two answers: true and false.

We need specify what we mean, and in this case we're looking for the normalized small sweep angles and whether our angle is between those values. Given any two angles, there is a reflex angle between them, is the normalized value of T within that reflex angle?

If we rotate A and B and T such that T = 0 and normalize A and B to within +-hemicircumference (180° or 2PI). Then our answer is whether A and B have different signs, and are within a hemicircumference of each other.

If we subtract the angle from test, then add 180° (so A is relative to T+180). Then we mod by 360 giving us a range between [-360°,360°] we add 360° and mod again (note, you could also just check if it's negative and add 360 if it is), giving us a value that is certain to be [0°,360°]. We subtract 180° giving us a value between [-180°,180°] relative to T+180°-180° aka, T. So T is now angle zero and all angles fall within the normalized range. Now we check to make sure the angles have a sign change and that they are not more than 180° apart, we have our answer.

Because the question asks in C++:

bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
    int a_adjust = ((((a - test + 180)) % 360) + 360) % 360 - 180;
    int b_adjust = ((((b - test + 180)) % 360) + 360) % 360 - 180;
    return ((a_adjust ^ b_adjust) < 0) && ((a_adjust - b_adjust) < 180) && ((a_adjust - b_adjust) > -180);
}

We can also do some tricks to simplify out the code and avoid any unneeded modulo ops (see comments below). Normalize will move angle a into the range [-180°,180°] relative to angle t.

int normalized(int a, int test) {
    int n = a - test + 180;
    if ((n > 360) || (n < -360)) n %= 360;
    return (n > 0)? n - 180: n + 180;
}

bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
    int a_adjust = normalized(a,test);
    int b_adjust = normalized(b,test);
    return ((a_adjust ^ b_adjust) < 0) &&
            ((a_adjust > b_adjust)? a_adjust-b_adjust: b_adjust-a_adjust) < 180;
}

Also if we can be sure the range is [0,360], we can make do with a simpler if statement

bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
    int dA = a - test + 180;
    if (dA > 360) {
        dA -= 360;
    }
    int a_adjust = (dA > 0) ? dA - 180 : dA + 180;
    int dB = b - test + 180;
    if (dB > 360) {
        dB -= 360;
    }
    int b_adjust = (dB > 0) ? dB - 180 : dB + 180;
    return ((a_adjust ^ b_adjust) < 0)
            && ((a_adjust > b_adjust) ? a_adjust - b_adjust : b_adjust - a_adjust) < 180;
}

JS Fiddle test of the code

like image 24
Tatarize Avatar answered Sep 30 '22 17:09

Tatarize