3

ですから、Delphiで2つのTPoint間の角度を取得しようとしていますが、予想よりも難しいことがわかりました。私が得ている結果は説明できません(「度」の部分に問題があるようです。または、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バリアントでテストすると、上記は機能します。しかし、今では、静的な合計が返されるようです(2番目のTPointを「移動」しても変化しません)。

あなたの疑問に備えて; 「モジュロ」演算子を作成したのは、「mod」演算子で使用される除算演算子が0に丸められ、切り捨てられないためです(したがって、負の数は機能しません)。

編集:TPoint( )が2番目のTPoint()のX軸に沿ってドラッグされた場合でも、他のポイントから離れると(およびその逆に)返される値(角度)がGetPointAngle()増加することに注意しました。pcpc

編集

皆さんは自分よりも優れています。私はほとんどの答えを調べましたが、最良の答えを選ぶのは難しいようです。そして、皆さんはそのような詳細ですべてを書いたので、私は同じ詳細ですべてを調べます:-)

また、最初の投稿で共有しなかったのは、関数がDLLとしてエクスポートされ、別のパスカルインタープリター(デルファイ互換)から到達できることです。

ついに解決策(変更):

GetPointAngle(P1, P2: TPoint)に:GetPointAngle(const P1, P2: TPoint)

^定数を宣言する必要性がわかりません...

4

3 に答える 3

7

これらの2点の間に形成される線のX軸に対する角度を計算したいとします。

ここに画像の説明を入力してください

この状況では、次の式が適用されます。

Tan(a) = (P2.Y - P1.Y) / (P2.X - P1.X)

これは次のように解釈されます。

a = ArcTan((P2.Y - P1.Y) / (P2.X - P1.X))

2つのポイントの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()増加することに注意しました。pc

場合によります。上の図を見ると、2番目の点がx軸に沿ってさらに移動すると、角度が減少します。2番目の点がy軸に沿ってさらに移動すると、角度が大きくなります。もちろん、これは両方のポイントがどちらの象限にあるかによって異なります。

さらに、コードはの最初のパラメーターを否定ArcTan2し、結果からさらに90°を減算します。それが何を意味するのか、それが意図的なものかどうかはわかりませんが、予期しない結果の原因となる可能性があります。

于 2013-03-24T08:51:58.790 に答える
4

あなたが探しているのは、2つのベクトル間の角度だと思います。これがこの図のθです。

ここに画像の説明を入力してください

代数内積は、幾何学的に<v 1、v 2 > = | v 1 || v2 | cosθとして表すことができます。これを再配置して、θ= cos -1 <v 1、v 2 > /(| 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

さて、あなたの問題を解釈するもう一つの方法は、あなたが2つのポイントを取り、それらの間の線を形成したいということです。そして、その線と水平線の間の角度を見つけます。この図で説明されているように:

ここに画像の説明を入力してください

それでも、2つのベクトル間の角度として表すことができます。最初のベクトルはp2 - p 1で、もう1つは水平方向のベクトル(0、1)です。それらの2つをフィードするAngleBetweenVectorsと、答えが得られます。垂直に対する角度を測定したい場合は、同じアイデアを使用できます。

うまくいけば、実際に問題が何であれ、問題を解決するのに十分なものがここにあります。

于 2013-03-24T08:27:24.570 に答える
1

次のコードは、Delphi7とFPC2.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.
于 2013-03-24T09:53:13.253 に答える