两个向量之间的角度

时间:2013-03-24 07:38:08

标签: delphi math

所以,我试图在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}}

^ 我不明白是否需要声明常量......

3 个答案:

答案 0 :(得分:6)

我假设您想要计算相对于在这两点之间形成的线的X轴的角度。

enter image description here

对于这种情况,适用以下公式:

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;

更新

似乎ArcTan2dó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)

我认为你要找的是两个向量之间的角度。这是图中的θ:

enter image description here

代数点积可以几何表示为&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将其转换为度数。

现在,解释问题的另一种方法是你想要两点并在那之间形成一条线。然后找出那条线和水平线之间的角度。如该图所示:

enter image description here

仍然可以表示为两个向量之间的角度。第一个向量是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.