如果我有两个点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与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
}
到目前为止,我可以说它的效果很好。谢谢你们的帮助!
答案 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。