c# – 2D中的顺时针右转弯角度?

如果我有两个点p1和p2,其中p1是枢轴点,p2是用户前进的原始方向,他们有许多可能的方向以随机顺序进入p3 … pn.如何将选项和由p1,p2形成的段之间的角度设置为0到360之间的顺时针(右手)正值,以便我可以将它们从最小到最大排序?

点p1 … pn也将在任何象限中,我不能假设它们将始终处于正x,y方向.网格是标准的笛卡尔网格而不是屏幕坐标,因此当您向下移动时,Y会变小.

所以在这个例子中(抱歉可怜的绘图但是我的笔记本电脑上只有Paint)我需要获得角度:

(P2-P1-P3)
(p2-p1-p4)
(p2-p1-p5)
(p2-p1-p6)

按此顺序(最小的右手转到最大的右转弯):
[(p2-p1-p4),(p2-p1-p6),(p2-p1-p5),(p2-p1-p3)]

我的案例中的点是一个名为Vertex的类:

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

获取角度和排序的代码现在看起来像这样,但有一个问题:

    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);

    }

问题是以上是我检查它:
原66,-66
枢轴280,-191
选择200,-180

我得到一个22.460643124而不是337.539356876的角度,这意味着它从原来的方向逆时针转到这个角度.我需要它总是顺时针方向来获得角度.

我做错了什么,我该如何解决?

更新:好的,根据你们的说法,我可以使用像数学这样的交叉产品来确定CW vs CCW,所以新方法看起来像这样:

    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;

    }

更新2:

使用已接受的解决方案现在看起来像这样:

    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

    }

到目前为止,我可以说它的效果很好.谢谢你们的帮助!

最佳答案 看看
atan2 function.它需要delta y和delta x,因此可以区分所有角度.

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

如果角度为负,则CW,如果为正CCW(或其他方式,取决于您的轴方向).注意|角度|可能是> 180,在这种情况下你可能想做360- |角度|如果您是在最短的路线之后,则反转CW CCW结论.

点赞