2D中的顺时针右转角度?

时间:2014-01-06 15:07:00

标签: c# math geometry

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

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

所以在这个例子中(对不起绘图很糟糕但是我的笔记本电脑上只有Paint)我需要得到角度:

Grid showing the points and angles

(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与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

    }

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

2 个答案:

答案 0 :(得分:2)

看看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(或其他方式,取决于您的轴方向)。注意|angle|可能是> 180,在这种情况下,如果您使用的是最短路线,则可能需要执行360-|angle|并反转CW CCW结论。

答案 1 :(得分:0)

您可以通过以下公式找到从p1到pn的Dn =方向(x = pn.x-p1.x和y = pn.y-p1.y):

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

CC = g++
CPPFLAGS += -I/usr/local/include
CPPFLAGS += -I/usr/local/include/Poco

CFLAGS += -c
CFLAGS += -g
LDFLAGS += -g
CFLAGS += -std=c++14
LDLIBS += -L/usr/local/lib
LDLIBS += -lPocoFoundation 
LDLIBS += -lPocoUtil
LDLIBS += -lPocoNet
LDLIBS += -Wl,-rpath=/usr/local/lib
SOURCES = test.cpp
OBJECTS = $(SOURCES:.cpp=.o)
EXECUTABLE = test

all: $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(LDFLAGS) $(OBJECTS) $(LDLIBS) -o $@

.cpp.o:
    $(CC) $(CPPFLAGS) $(CFLAGS) $< -o $@

install:
    @echo "Build complete!"

所以角度是Angle(p2-p1-pn)= Dn-D2。