所以,我试图在Delphi中获得两个TPoints之间的角度,结果证明它比我预期的更难。我得到的结果我无法解释(似乎是“to degrees”-part的一些问题,或者ArcTan2没有以我期望的形式返回一个总和。 - Delpi-v7:
function Modulo(x,y:Extended): Extended;
var d: Extended;
begin
d := x / y;
Result := (d - floor(d)) * y;
end;
function Degrees(Rads: Extended): Extended;
begin
Result := Rads*(180/Pi);
end;
function GetPointAngle(P1, P2: TPoint): Extended;
begin
Result := Modulo(Degrees(ArcTan2(-(P1.Y - P2.Y), P1.X - P2.X)) - 90, 360);
end;
然而,当我将代码移植到Python或在另一个Pascal变体中测试时,上面的工作。但现在,它似乎返回一个静态的总和(如果我“移动”第二个TPoint则不会改变。)
如果你想知道;我创建“模数”函数只是因为“mod” - 运算符中使用的除法运算符舍入为0而不是向下(因此负数不起作用)。
编辑:我注意到GetPointAngle()
从p
越远离另一个点c
(反之亦然)时,p
返回的值(角度)增加,即使是TPoint( c
)沿第二个TPoint(GetPointAngle(P1, P2: TPoint)
)的X轴拖动。
编辑:
你们已经超越了自己,我已经查看了大部分答案,而且似乎很难选择最佳答案!既然你们都写了这么详细的东西,我会用相同的细节来处理所有事情: - )
另外:我在最初的帖子中没有分享的是,我的函数被导出为DLL,可以从另一个pascal-interpretor(与delphi兼容)到达。
最后解决方案(已更改):
GetPointAngle(const P1, P2: TPoint)
致:{{1}}
^ 我不明白是否需要声明常量......
答案 0 :(得分:6)
我假设您想要计算相对于在这两点之间形成的线的X轴的角度。
对于这种情况,适用以下公式:
Tan(a) = (P2.Y - P1.Y) / (P2.X - P1.X)
转换为:
a = ArcTan((P2.Y - P1.Y) / (P2.X - P1.X))
当两个点具有相同的X坐标时,这显然会导致EDivByZero
异常,因此您必须自己处理。此外,ArcTan
导致角度在0°..90°范围内(即0..π/ 2),因此忽略了正确的象限,而ArcTan2
导致角度在-180°范围内..180°。向结果添加360°以将负角度转换为正数:
function AngleOfLine(const P1, P2: TPoint): Double;
begin
if P2.X = P1.X then
if P2.Y > P1.Y then
Result := 90
else
Result := 270
else
Result := RadToDeg(ArcTan2(P2.Y - P1.Y, P2.X - P1.X));
if Result < 0 then
Result := Result + 360;
end;
结果是:
A := AngleOfLine(Point(10, 10), Point(20, 10)); // 0
A := AngleOfLine(Point(10, 10), Point(20, 20)); // 45
A := AngleOfLine(Point(10, 10), Point(10, 20)); // 90
A := AngleOfLine(Point(10, 10), Point(0, 20)); // 135
A := AngleOfLine(Point(10, 10), Point(0, 10)); // 180
A := AngleOfLine(Point(10, 10), Point(0, 0)); // 225
A := AngleOfLine(Point(10, 10), Point(10, 0)); // 270
A := AngleOfLine(Point(10, 10), Point(20, 0)); // 315
现在,这是相对于世界坐标系,其正Y轴默认指向上方。如果要将结果转换为正Y轴向下的设备坐标系,则从360°中减去结果:
Result := 360 - Result;
似乎ArcTan2
dóes照顾除零,(即使在D7中,尽管有文档)所以例程变得更加简单:
function AngleOfLine(const P1, P2: TPoint): Double;
begin
Result := RadToDeg(ArcTan2((P2.Y - P1.Y),(P2.X - P1.X)));
if Result < 0 then
Result := Result + 360;
end;
我注意到,当
GetPointAngle()
远离另一个点p
时,c
返回的值会增加(反之亦然)。
这取决于。查看上图,如果第二个点沿x轴进一步移动,则角度会减小。如果第二点沿y轴进一步移动,则角度增加。当然,这取决于两个点都在哪个象限。
此外,您的代码会取消ArcTan2
的第一个参数,并从结果中减去另外90°。我不知道你的意思是否是故意的,但它可能是意外结果的来源。
答案 1 :(得分:4)
我认为你要找的是两个向量之间的角度。这是图中的θ:
代数点积可以几何表示为&lt; v 1 ,v 2 &gt; = | v 1 || v 2 |cosθ。这可以重新排列以找到θ= cos -1 &lt; v 1 ,v 2 &gt; /(| v 1 < /子> || v <子> 2 子> |)
function DotProduct(const v1, v2: TPoint): Integer;
begin
Result := v1.X*v2.X + v1.Y*v2.Y;
end;
function Magnitude(const v: TPoint): Double;
begin
Result := Sqrt(Sqr(v.X)+Sqr(v.Y));
end;
function AngleBetweenVectors(const v1, v2: TPoint): Double;
var
Magv1, Magv2: Double;
begin
Magv1 := Magnitude(v1);
Magv2 := Magnitude(v2);
if abs(Magv1*Magv2)=0.0 then
Result := 0.0
else
Result := ArcCos(EnsureRange(DotProduct(v1,v2)/(Magv1*Magv2), -1.0, 1.0));
end;
以弧度返回一个角度。您可以使用RadToDeg()
单元中的Math
将其转换为度数。
现在,解释问题的另一种方法是你想要两点并在那之间形成一条线。然后找出那条线和水平线之间的角度。如该图所示:
仍然可以表示为两个向量之间的角度。第一个向量是p 2 -p 1 ,另一个是水平方向的向量,(0,1)。将这两个人加入AngleBetweenVectors
并获得答案。如果你想测量垂直角度,那么你可以使用相同的想法。
希望你能够解决问题,无论它实际上是什么。
答案 2 :(得分:1)
以下代码使用Delphi 7和FPC 2.7.1返回相同的结果,看起来是正确的 所以主要的问题是:我们期待什么以及我们拥有什么?
program Project2;
{$APPTYPE CONSOLE}
uses
Math;
{.$define speed}
function CalcAngle(const lx, ly: extended): extended; {$ifdef speed} inline; {$endif}
begin
Result := RadToDeg(ArcTan2(ly, lx));
end;
function Modulo(x, y: extended): extended; {$ifdef speed} inline; {$endif}
var
d: extended;
begin
d := x / y;
Result := (d - floor(d)) * y;
end;
function Degrees(Rads: Extended): Extended;
begin
Result := Rads*(180/Pi);
end;
function Modulo2(x: extended): extended; {$ifdef speed} inline; {$endif}
begin
if x < 0 then
Result := 360 + x
else
Result := x;
end;
function GetPointAngle(const lx, ly: integer): Extended;
begin
Result := Modulo(Degrees(ArcTan2(ly, lx)) - 90, 360);
end;
procedure OutTest(const lx, ly: extended);
var
a: extended;
begin
a := CalcAngle(lx, ly);
Writeln(
a: 10: 4,
Modulo(a - 90, 360):10:4,
GetPointAngle(round(lx), round(ly)):10:4);
end;
begin
OutTest(2, 0);
OutTest(0, 2);
OutTest(-2, 2);
OutTest(-2, -2);
OutTest(2, 3);
OutTest(100, 2);
Readln;
end.