Captain
Captain

Reputation: 151

Finding the shortest distance between two angles

I've been trying to find a way of finding the shortest distance between two angles. The angles are in the interval -360 < 360 and are given in degrees.

In short, i need a simple way (the simpler the better) to find the shortest distance between two angles, let's call them angle1 and angle2. Angle1 is the angle i want to get to, angle 2 is the angle i am at. After this i want to use an if function to determine the rotation direction of an entity.

Pseudocode:

CloseDistance = (Find shortest distance between angle1 (where i want to go) and angle2 (where i am))

if (CloseDistance > Something)
  {Rotate to the right} else {Rotate to the left}

Upvotes: 12

Views: 19432

Answers (6)

bacata.borisov
bacata.borisov

Reputation: 93

I found the most suitable solution in an answer here

I am also using array to store the last two angles - I am doing that because I receive a new angle every second (via external source). It is written in Swift but can be easily transformed for any other language

if angleArray.count > 1 {
                                                
//prepare the two angles
let sourceAngle = angleArray[0]
let targetAngle = angleArray[1]
                                                
//get all three different distances
let alpha = targetAngle - sourceAngle
let beta = targetAngle - sourceAngle + 360
let gamma = targetAngle - sourceAngle - 360

//get the lowest one value by absolute value
var A = min(abs(alpha), abs(beta), abs(gamma))
//get the proper sign - plus or minus
switch A {
case abs(alpha):
A = alpha
case abs(beta):
A = beta
default:
A = gamma
}
//add it to the source angle
let newAngle = sourceAngle + A
//remove the last element of the array
angleArray.remove(at: 1)
return newAngle
}

It works flawlessly in my app where I represent wind and heading values which change a lot around the 0/360 overlap region.

Upvotes: 0

2-sticks
2-sticks

Reputation: 65

Here's my Python version which I find easier to explain:

def smallestAngle(currentAngle, targetAngle) -> int:
    # Subtract the angles, constraining the value to [0, 360)
    diff = ( targetAngle - currentAngle) % 360

    # If we are more than 180 we're taking the long way around.
    # Let's instead go in the shorter, negative direction
    if diff > 180 :
        diff = -(360 - diff)
    return diff

Beware that some systems' modulo functions return negative values and will not work! If you are using Javascript, here is a modulo function you seek:

// Javascript always-positive modulo function
const mod = (n, m) => ((n % m) + m) % m

I was asked to show this in C# so here you go...

public static double smallestAngle(double currentAngle, double targetAngle)
{
    double diff = (targetAngle - currentAngle) % 360;
    return diff <= 180 ? diff : -(360 - diff);
}

Upvotes: 5

Kyle
Kyle

Reputation: 6684

public static double AngleDifference( double angle1, double angle2 )
{
    double diff = ( angle2 - angle1 + 180 ) % 360 - 180;
    return diff < -180 ? diff + 360 : diff;
}

This works by noticing that we want to take the difference between the two angles, angle1 and angle2 and wrap that result to the range [-180, 179). The mod operator allows us to wrap something to the range [0, n). I.e. x % n "wraps" (with a caveat for x < 0) x to the range [0, n).

Our range starts at -180 rather than 0, so we shift it over by adding 180. Then we wrap to 360, then we shift back. That's what the first line of the method does.

The second line takes care of that little wrinkle with negative numbers. If angle2 - angle1 + 180 happened to be less than 0, then diff will be less than -180. In that case we just wrap it back into range by adding 360 to it. Otherwise we do nothing.

As an added bonus, the input angles are completely unconstrained. They need not be between -360 and 360. They can be anything.

Upvotes: 37

Bhaskar
Bhaskar

Reputation: 1036

The Simplest way I found is

double closedistance = (destangle - startangle) % 360

abs(closedistance) gives you the required distance.

sign of closedistance (= closedistance/abs(closedistance)) gives the direction of rotation (+ anticlockwise, - clockwise) or just check it like this

if (closedistance > 0) {} // anticlockwise
else {} // clockwise

This works for any value of angles.

Upvotes: 1

Mark Shevchenko
Mark Shevchenko

Reputation: 8197

It seems you need result angle in interval -180..+179. Negative signs means right rotation, positive means left (or vice versa).

Ergo, you need the modular arithmetic with modulo 360.

In C# remaidner operator does what you need:

var distance = (destinationAngle - sourceAngle) % 360;

Unfortunately it gives result in interval -359..+359. To correct this you can transform too large values to interval -180..+179:

var distance = (destinationAngle - sourceAngle) % 360;
if (distance < -180)
    distance += 360;
else if (distance > 179)
    distance -= 360;

Asymmetric ends of interval (-180 and +179) have appeared because -180 and +180 both are the same angle, so you should choose one of them to avoid ambiguity.

F.e.

   Destination | Source | Result
            45 |     30 |     15
            30 |     45 |    -15
           -45 |    -30 |    -15
           -30 |    -45 |     15
360 + 45 = 405 |     30 |     15
          -405 |    -30 |    -15

As I can see it what you need. If your angles are doubles, use Math.IEEERemainder method.

Upvotes: 4

Mauro De Biasio
Mauro De Biasio

Reputation: 1146

The maximum distance between 2 angles is 180 degrees, just pick an angle that is between 0 and 360

int angle1 = n;
int angle2 = n2;

if(angle1 < 0)
   angle1 += 360;

if(angle2 < 0)
   angle2 += 360;

if (angle2 > angle1 && angle2 - angle1 <= 180 )
   //go clockwise
else if (angle2 > angle1 && angle2 - angle1 > 180 )
   //go counter clockwise
else if (angle1 > angle2 && angle1 - angle2 <= 180 )
   //go counter clockwise
else if (angle1 > angle2 && angle1 - angle2 > 180 )
   //go clockwise

Upvotes: 1

Related Questions