Dan
Dan

Reputation: 155

Calculate difference between 2 directions

How would I calculate the difference between 2 angles in degrees?

This is simple for most as I just use:

var difference = Math.Abs(direction1 - direction2);

However when direction2 is north (0 degrees) and direction1 is north west (eg 318 degrees) obviously this is way out.

What's the correct way to calculate this difference past the north (0) boundary?

Upvotes: 1

Views: 1290

Answers (2)

JonasH
JonasH

Reputation: 36649

Edit: This answer assumes the directions are vectors and not angles. For angles, see Mindswipes answer

In general you take the dot product, use Arccosine to get the angle, and convert to degree by multiplying with 180d / Math.PI. This assumes the directions are normalized.

var angleInDegrees = Math.ACos(direction1.Dot(direction2)) * 180d / Math.PI;

This always produces a positive angle in the range [0, 180]. This works for both 2D and 3D vectors.

If you ware using 2D vectors and want an angle in [0, 360] you can check if the closest rotation is clockwise or counter clockwise by rotating one of the vectors 90 degrees, and checking if it the angle to the other is smaller or larger than 90 degrees:

var isClockwise = direction1.Dot(new Vector(direction2.Y, -direction2.X)) > 0;

If it is clockwise, add 180 degrees to the result. Or vice versa, depending on your coordinate system.

Upvotes: 2

MindSwipe
MindSwipe

Reputation: 7950

That's quite easy, all you need to do is add a check to see if the direction (heading) is larger than 180, and if so, subtract the difference from 360. Like so:

static int GetHeadingDifference(int heading1, int heading2)
{   
    var difference = Math.Abs(heading1 - heading2);
    if (difference > 180)
        return 360 - difference;
    
    return difference;
}

This will return the correct heading. Here a demonstration.


But you may want to wrap this in a custom object to handle everything for you. This is how you could do it:

struct Heading
{
    private const float MaxDegrees = 360;
    
    // Use a float for more accurate heading
    public float Degree {get; set;}
    
    public Heading(float heading)
    {
        Degree = heading;
    }
    
    // Override addition to wrap around
    public static Heading operator +(Heading a, Heading b) 
    {
        var val = (a.Degree + b.Degree) % MaxDegrees;
        return new Heading(val);
    }
    
    // Override subtraction to always result in a number <= 180
    public static Heading operator -(Heading a, Heading b)
    {
        var difference = Math.Abs(a.Degree - b.Degree);
        if (difference > 180)
            return new Heading(MaxDegrees - difference);
        
        return new Heading(difference);
    }
    
    // Override equality to check the actual degree value
    public static bool operator ==(Heading a, Heading b)
    {
        return a.Degree == b.Degree;
    }
    
    // Overriding equality requires overriding inequality
    public static bool operator !=(Heading a, Heading b)
    {
        return !(a == b);
    }
    
    // Override Equals to catch explicit a.Equals(b) calls
    public override bool Equals(object other)
    {
        if (other is not Heading)
            return false;
        
        return ((Heading) other).Degree == Degree;
    }
    
    // When overriding equality you must implement GetHashCode
    public override int GetHashCode()
    {
        // Oerflow is ok here, just wrap
        unchecked
        {
            var hash = 17;
            hash = hash * 23 + Degree.GetHashCode();
            return hash;
        }
    }
}

Demo of it working here


P.S use this if you just want to have your one number, if you actually have vectors, use JonasH's answer

Upvotes: 3

Related Questions