AndyTheCornbread
AndyTheCornbread

Reputation: 245

Clockwise right hand turn angle in 2D?

If I have two points p1 and p2 where p1 is the pivot point and p2 is the original direction the user was headed and they have a number of possible directions to go p3...pn in random sequence. How do I get the angles between the choices and the segment formed by p1,p2 as clockwise(right hand) positive values between 0 and 360 so that I can sort them from least to greatest?

Also the points p1...pn will be in any quadrant, I can’t assume they will always be in the positive x,y direction. The grid is a standard Cartesian grid not screen coordinates so Y gets smaller as you go down not larger.

So in this example (sorry for the poor drawing but Paint was all I had on my laptop) I need to get the angles:

Grid showing the points and angles

(p2-p1-p3) ( p2-p1-p4) ( p2-p1-p5) ( p2-p1-p6)

In this order(smallest right hand turn to largest right hand turn): [( p2-p1-p4), ( p2-p1-p6), ( p2-p1-p5), (p2-p1-p3)]

The points in my case are a class called Vertex:

public class Vertex
{
    public double X = 0;
    public double Y = 0;
    public Vertex() { }
    public Vertex(double x, double y)
    {
        X = x;
        Y = y;
    }
 }

And the code for getting the angles and sorting looks like this right now but has a problem:

    private static IEnumerable<Vertex> SortByAngle(Vertex original, Vertex pivot, List<Vertex> choices)
    {
        choices.Sort((v1, v2) => GetTurnAngle(original, pivot, v1).CompareTo(GetTurnAngle(original, pivot, v2)));
        return choices;
    }

    private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
    {
        var a = original.X - pivot.X;
        var b = original.Y - pivot.Y;
        var c = choice.X - pivot.X;
        var d = choice.Y - pivot.Y;

        var rads = Math.Acos(((a * c) + (b * d)) / ((Math.Sqrt(a * a + b * b)) * (Math.Sqrt(c * c + d * d))));

        return (180 / Math.PI * rads);

    }

The problem is the above is if I check it for: original 66,-66 pivot 280,-191 choice 200,-180

I get an angle of 22.460643124 instead of 337.539356876 which means it went counter-clockwise from the original direction to get that angle. I need it to always go clockwise to get the angle.

What am I doing wrong and how do I fix it?

Update: OK so according to what you guys are saying I can probably use some cross product like math to determine CW vs CCW so the new method would look like this:

    private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
    {
        var a = original.X - pivot.X;
        var b = original.Y - pivot.Y;
        var c = choice.X - pivot.X;
        var d = choice.Y - pivot.Y;


        var angle = Math.Acos(((a * c) + (b * d)) / ((Math.Sqrt(a * a + b * b)) * (Math.Sqrt(c * c + d * d))));
        angle = (180 / Math.PI * angle);


        var z = (choice.X - pivot.X) * (original.Y - pivot.Y) - (choice.Y - pivot.Y) * (original.X - pivot.X);
        if (z < 0)
        {
            return 360 - angle;
        }
        return angle;

    }

Update 2:

Using the accepted solution it now looks like so:

    private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
    {

        var angle1 = Math.Atan2(original.Y - pivot.Y, original.X - pivot.X);
        var angle2 = Math.Atan2(choice.Y - pivot.Y, choice.X - pivot.X);
        var angleDiff = (180 / Math.PI * (angle2 - angle1));

        if (angleDiff > 0)//It went CCW so adjust
        {
            return 360 - angleDiff;
        }
        return -angleDiff;//I need the results to be always positive so flip sign

    }

So far as I can tell that works great so far. Thank you guys for the help!

Upvotes: 4

Views: 984

Answers (2)

theodore panagos
theodore panagos

Reputation: 77

You find the Dn=direction from p1 to pn (x=pn.x-p1.x and y=pn.y-p1.y) by the formula:

Dn=f(x,y)=180-90*(1+sign(y))* (1-sign(x^2))-45*(2+sign(y))*sign(x)

     -180/pi()*sign(x*y)*atan((abs(y)-abs(x))/(abs(y)+abs(x)))

So the angles are Angle(p2-p1-pn)=Dn-D2.

Upvotes: 0

weston
weston

Reputation: 54781

Take a look at atan2 function. It takes delta y and delta x, so can distinguish all angles.

angle1 = atan2(p1.y-p0.y, p1.x-p0.x);
angle2 = atan2(p2.y-p0.y, p2.x-p0.x);
angle = angle2 - angle1;

If angle is negative, then CW, if positive CCW (or other way around depending on your axis orientation). Note |angle| may be > 180, in which case you may want to do 360-|angle| and reverse the CW CCW conclusion if you're after the shortest route.

Upvotes: 2

Related Questions