3点から角度を計算する方法は?[閉まっている]


120

あなたがこれを持っているとしましょう:

P1 = (x=2, y=50)
P2 = (x=9, y=40)
P3 = (x=5, y=20)

それP1が円の​​中心点であると仮定します。それは常に同じです。私はで構成されて角度たいP2P3の隣にある角度、または他の言葉ではP1。正確には内角。常に鋭角になるため、-90度未満になります。

私は思った:男、それは単純な幾何学の数学です。しかし、私は今約6時間式を探しましたが、arccosやvector scalar productのような複雑なNASAのことについて話している人しか見つかりません。まるで冷蔵庫の中にいるような気がします。

これが簡単な問題だと思う数学の教祖たち?ここではプログラミング言語は重要ではないと思いますが、それが重要だと考える人にとっては、JavaとObjective-Cです。両方に必要ですが、タグ付けしていません。

回答:


87

P1が頂点となる角度を意味する場合、コサイン法則を使用するとうまくいくはずです。

Arccos((P 12 2 + P 13 2 -P 23 2)/(2 * P 12 * P 13))

ここで、P 12は、P1からP2までのセグメントの長さで、次の式で計算されます。

sqrt((P1 x -P2 x2 +(P1 y -P2 y2



@Rafa Firenze cos ^ -1はacosの一般的な表記ですが、acosはあいまいさが少なくなっています。en.wikipedia.org/wiki/Inverse_trigonometric_functions
2014年

何の問題もないので編集は残しておきますが、数学/ CS / EEの学位を持っているため、cos ^ -1が最も一般的な表記法です。
ランスロバーツ

1
少数の言語だけが 'power of'のキャレットを使用しているので、arcosと呼ばない場合は、単にcos⁻¹と入力してください。(指数の入力が難しい商用オペレーティングシステムを使用している場合は、購入できるキー配列アプリケーション、またはインストールできるブラウザープラグインがあると思います。または、ウェブ検索してコピーして貼り付けることもできます。)
Michael Scheper、2015

1
@ MichaelScheper、htmlが制限されているコメントでキャレットのみを使用していました。私は確かに実際の答えでは下付き/上付き表記を使用します。
ランスロバーツ

47

2つのベクトル(1つは点P1からP2へ、もう1つはP1からP3へ)と考えると、非常に単純になります。

したがって:
a =(p1.x-p2.x、p1.y-p2.y)
b =(p1.x-p3.x、p1.y-p3.y)

次に、ドット積の式を反転させて
ドット積
、角度を取得します。
2つのベクトル間の角度

それドット積が意味することを覚えておいてください:a1 * b1 + a2 * b2(ここでは2次元のみ...)


1
ベクトルの大きさ
Daniel Little

atan2ソリューションを確認します。
Luc Boissaye

25

角度計算を処理する最良の方法はatan2(y, x)、指定された点x, yがその点と原点からのX+軸からの角度を返すことを使用することです。

計算が

double result = atan2(P3.y - P1.y, P3.x - P1.x) -
                atan2(P2.y - P1.y, P2.x - P1.x);

つまり、あなたは基本的にで2点を変換する-P1(ように他の言葉であなたはすべてのものを翻訳P1起源で終了するまで)、その後、あなたは絶対角度の差を考慮P3してのをP2

の利点atan2は、完全な円が表されることです(-πからπまでの任意の数値を取得できます)。代わりにacos、正しい結果を計算するために符号に応じていくつかのケースを処理する必要があります。

の唯一の特異点atan2(0, 0)...です。これは、その両方P2P3ありP1、その場合とは異なる必要があることを意味します。その場合、角度について話しても意味がありません。


ご回答有難うございます。それがまさに私が探していたものです。単純な解決策で、値が負のときに2piを追加するだけで、反時計回りの角度を簡単に取得できます。
マリオ

@marcpt:これatan2はまさにこの問題に必要なものですが、この質問にアクセスするほとんどの人は、このacosベースの解決策がなぜ悪いのか読めない、または理解できないようです。幸いに私のために私は、「誰かがインターネット上で間違っている」(左xkcd.com/386何年も前)の位相を、私は:-)明白な防御のための戦いを開始するつもりはない
6502

これを指摘してくれてありがとう、でもこのように3Dを扱えますか?
nicoco

1
@nicoco:3次元でどのように角度を定義しますか?より具体的には、角度は負またはpi(180度)を超えることができますか?3Dの2つの平行でないベクトルは平面を定義しますが、平面を2つの側面から「見る」ことができます。一方の側面から見ると、AはBの「左側」に表示され、もう一方から見ると「右側」に表示されます。 。
6502

@ 6505ご回答いただきありがとうございます。問題を考える前に投稿しました。しかし、私はそれを今理解しました。
nicoco 2017

19

JavaScriptの例を挙げてみましょう。私はそれで多くのことを戦いました。

/**
 * Calculates the angle (in radians) between two vectors pointing outward from one center
 *
 * @param p0 first point
 * @param p1 second point
 * @param c center point
 */
function find_angle(p0,p1,c) {
    var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
                        Math.pow(c.y-p0.y,2)); // p0->c (b)   
    var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
                        Math.pow(c.y-p1.y,2)); // p1->c (a)
    var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
                         Math.pow(p1.y-p0.y,2)); // p0->p1 (c)
    return Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
}

おまけ:HTML5キャンバスの例


5
これをより少なくsqrtして二乗することにより、これをより効率的にすることができます。こちらの回答(Rubyで記述)またはこの更新されたデモ(JavaScript)を参照しください。
Phrogz 2014

よりシンプルなソリューションにはatan2を使用できます。
Luc Boissaye 2018年

15

基本的に、P1からP2へのベクトルとP1からP3へのベクトルの2つのベクトルがあります。したがって、必要なのは、2つのベクトル間の角度を計算するための数式だけです。

見て、ここで良い説明と式のために。

代替テキスト


12

P1を円の中心と考えている場合は、複雑すぎると考えています。あなたは単純な三角形を持っているので、あなたの問題はコサイン法則で解決できます。極座標変換などは必要ありません。距離がP1-P2 = A、P2-P3 = BおよびP3-P1 = Cであるとします。

角度= arccos((B ^ 2-A ^ 2-C ^ 2)/ 2AC)

必要なのは、距離A、B、Cの長さを計算することだけです。これらは、ポイントのx座標とy座標、およびピタゴラスの定理から簡単に利用できます。

長さ= sqrt((X2-X1)^ 2 +(Y2-Y1)^ 2)


P1などを(x、y)ではなく個別の値として扱うため、これを実際に実装する方法に少し混乱しています
Dominic

@Dominic Tobias:表記P1-P2 = Aは「Aを計算するには、P1からP2を引く」ではなく、「AをP1からP2までの距離として定義しています」と読む必要があります。これは、2番目の式を使用して計算できます。計算式を読みやすくするために、距離の省略表現を定義したかっただけです。
Trebは、2017

8

最近、似たような問題に遭遇しましたが、正と負の角度を区別するだけで済みました。これが誰かに役立つ場合は、Androidのタッチイベントでの回転の検出について、このメーリングリストから入手したコードスニペットをお勧めします。

 @Override
 public boolean onTouchEvent(MotionEvent e) {
    float x = e.getX();
    float y = e.getY();
    switch (e.getAction()) {
    case MotionEvent.ACTION_MOVE:
       //find an approximate angle between them.

       float dx = x-cx;
       float dy = y-cy;
       double a=Math.atan2(dy,dx);

       float dpx= mPreviousX-cx;
       float dpy= mPreviousY-cy;
       double b=Math.atan2(dpy, dpx);

       double diff  = a-b;
       this.bearing -= Math.toDegrees(diff);
       this.invalidate();
    }
    mPreviousX = x;
    mPreviousY = y;
    return true;
 }

7

説明付きの非常にシンプルな幾何学的ソリューション

数日前、同じ問題に陥り、数学の本に座らなければなりませんでした。基本的な数式をいくつか組み合わせて簡略化することで問題を解決しました。


この図を考えてみましょう-

角度

ϴを知りたいので、まずαβを見つける必要があります。さて、どんな直線でも

y = m * x + c

Let- A =(ax、ay)B =(bx、by)、およびO =(ox、oy)。だからラインOAのために -

oy = m1 * ox + c   ⇒ c = oy - m1 * ox   ...(eqn-1)

ay = m1 * ax + c   ⇒ ay = m1 * ax + oy - m1 * ox   [from eqn-1]
                   ⇒ ay = m1 * ax + oy - m1 * ox
                   ⇒ m1 = (ay - oy) / (ax - ox)
                   ⇒ tan α = (ay - oy) / (ax - ox)   [m = slope = tan ϴ]   ...(eqn-2)

同様に、ラインOBの場合 -

tan β = (by - oy) / (bx - ox)   ...(eqn-3)

今、私たちは必要ϴ = β - αです。三角法では、

tan (β-α) = (tan β + tan α) / (1 - tan β * tan α)   ...(eqn-4)

tan α(eqn-2から)とtan b(eqn-3から)の値をeqn-4で置き換え、単純化を適用した後、

tan (β-α) = ( (ax-ox)*(by-oy)+(ay-oy)*(bx-ox) ) / ( (ax-ox)*(bx-ox)-(ay-oy)*(by-oy) )

そう、

ϴ = β-α = tan^(-1) ( ((ax-ox)*(by-oy)+(ay-oy)*(bx-ox)) / ((ax-ox)*(bx-ox)-(ay-oy)*(by-oy)) )

それだ!


今、次の図を見てください-

角度

このC#またはJavaメソッドは角度を計算します(ϴ)-

    private double calculateAngle(double P1X, double P1Y, double P2X, double P2Y,
            double P3X, double P3Y){

        double numerator = P2Y*(P1X-P3X) + P1Y*(P3X-P2X) + P3Y*(P2X-P1X);
        double denominator = (P2X-P1X)*(P1X-P3X) + (P2Y-P1Y)*(P1Y-P3Y);
        double ratio = numerator/denominator;

        double angleRad = Math.Atan(ratio);
        double angleDeg = (angleRad*180)/Math.PI;

        if(angleDeg<0){
            angleDeg = 180+angleDeg;
        }

        return angleDeg;
    }

この方法は正三角形にどのように使用できますか?
Vikrant 2017

1
さて、あなたの答えは今うまく機能しています。それは先週の私のコードのロジックの問題でした。
Vikrant 2017

6

Objective-Cでは、これを行うことができます

float xpoint = (((atan2((newPoint.x - oldPoint.x) , (newPoint.y - oldPoint.y)))*180)/M_PI);

または、こちらをご覧ください


7
ええと、違います。3つの点があり、中心は(0,0)にありません。これにより、頂点の角度ではなく、直角三角形の角度が得られます。そして、角度の "xpoint"はどのような名前ですか?
ジムバルター、2015

4

符号付き角度(-90)に言及しました。多くのアプリケーションでは、角度にサインがあります(ポジティブとネガティブ、http://en.wikipedia.org/wiki/Angleを参照)。ポイントが(たとえば)P2(1,0)、P1(0,0)、P3(0,1)の場合、角度P3-P1-P2は通常正(PI / 2)ですが、角度P2-P1- P3は負です。辺の長さを使用しても+と-は区別されないため、これが問題になる場合は、ベクトルまたはMath.atan2(a、b)などの関数を使用する必要があります。

角度も2 * PIを超えて拡張できます。これは現在の質問には関係ありませんが、独自のAngleクラスを作成することも十分に重要でした(度とラジアンが混同されないようにするためにも)。角度1が角度2より小さいかどうかに関する質問は、角度の定義方法に大きく依存します。行(-1,0)(0,0)(1,0)がMath.PIまたは-Math.PIのどちらで表されるかを決定することも重要です。


4

私の角度デモプログラム

最近、私も同じ問題を抱えています... Delphiでは、Objective-Cとよく似ています。

procedure TForm1.FormPaint(Sender: TObject);
var ARect: TRect;
    AWidth, AHeight: Integer;
    ABasePoint: TPoint;
    AAngle: Extended;
begin
  FCenter := Point(Width div 2, Height div 2);
  AWidth := Width div 4;
  AHeight := Height div 4;
  ABasePoint := Point(FCenter.X+AWidth, FCenter.Y);
  ARect := Rect(Point(FCenter.X - AWidth, FCenter.Y - AHeight),
    Point(FCenter.X + AWidth, FCenter.Y + AHeight));
  AAngle := ArcTan2(ClickPoint.Y-Center.Y, ClickPoint.X-Center.X) * 180 / pi;
  AngleLabel.Caption := Format('Angle is %5.2f', [AAngle]);
  Canvas.Ellipse(ARect);
  Canvas.MoveTo(FCenter.X, FCenter.Y);
  Canvas.LineTo(FClickPoint.X, FClickPoint.Y);
  Canvas.MoveTo(FCenter.X, FCenter.Y);
  Canvas.LineTo(ABasePoint.X, ABasePoint.Y);
end;

2

次のC#メソッドは、円上のポイントの水平線から反時計回りに角度(0〜360)を返します。

    public static double GetAngle(Point centre, Point point1)
    {
        // Thanks to Dave Hill
        // Turn into a vector (from the origin)
        double x = point1.X - centre.X;
        double y = point1.Y - centre.Y;
        // Dot product u dot v = mag u * mag v * cos theta
        // Therefore theta = cos -1 ((u dot v) / (mag u * mag v))
        // Horizontal v = (1, 0)
        // therefore theta = cos -1 (u.x / mag u)
        // nb, there are 2 possible angles and if u.y is positive then angle is in first quadrant, negative then second quadrant
        double magnitude = Math.Sqrt(x * x + y * y);
        double angle = 0;
        if(magnitude > 0)
            angle = Math.Acos(x / magnitude);

        angle = angle * 180 / Math.PI;
        if (y < 0)
            angle = 360 - angle;

        return angle;
    }

乾杯、ポール


2

function p(x, y) {return {x,y}}

function normaliseToInteriorAngle(angle) {
	if (angle < 0) {
		angle += (2*Math.PI)
	}
	if (angle > Math.PI) {
		angle = 2*Math.PI - angle
	}
	return angle
}

function angle(p1, center, p2) {
	const transformedP1 = p(p1.x - center.x, p1.y - center.y)
	const transformedP2 = p(p2.x - center.x, p2.y - center.y)

	const angleToP1 = Math.atan2(transformedP1.y, transformedP1.x)
	const angleToP2 = Math.atan2(transformedP2.y, transformedP2.x)

	return normaliseToInteriorAngle(angleToP2 - angleToP1)
}

function toDegrees(radians) {
	return 360 * radians / (2 * Math.PI)
}

console.log(toDegrees(angle(p(-10, 0), p(0, 0), p(0, -10))))


0

これには高校の数学を使った簡単な答えがあります。

あなたが3点を持っているとしましょう

ポイントAからBへの角度を取得するには

angle = atan2(A.x - B.x, B.y - A.y)

ポイントBからCへの角度を取得するには

angle2 = atan2(B.x - C.x, C.y - B.y)

Answer = 180 + angle2 - angle
If (answer < 0){
    return answer + 360
}else{
    return answer
}

私が作成した最近のプロジェクトでこのコードを使用しました。BをP1に変更します。必要に応じて、「180 +」を削除することもできます。


-1

まあ、他の答えは必要なすべてをカバーしているようですので、JMonkeyEngineを使用している場合はこれを追加したいと思います:

Vector3f.angleBetween(otherVector)

それが私が探してここに来たものです:)


-2
      Atan2        output in degrees
       PI/2              +90
         |                | 
         |                |    
   PI ---.--- 0   +180 ---.--- 0       
         |                |
         |                |
       -PI/2             +270

public static double CalculateAngleFromHorizontal(double startX, double startY, double endX, double endY)
{
    var atan = Math.Atan2(endY - startY, endX - startX); // Angle in radians
    var angleDegrees = atan * (180 / Math.PI);  // Angle in degrees (can be +/-)
    if (angleDegrees < 0.0)
    {
        angleDegrees = 360.0 + angleDegrees;
    }
    return angleDegrees;
}

// Angle from point2 to point 3 counter clockwise
public static double CalculateAngle0To360(double centerX, double centerY, double x2, double y2, double x3, double y3)
{
    var angle2 = CalculateAngleFromHorizontal(centerX, centerY, x2, y2);
    var angle3 = CalculateAngleFromHorizontal(centerX, centerY, x3, y3);
    return (360.0 + angle3 - angle2)%360;
}

// Smaller angle from point2 to point 3
public static double CalculateAngle0To180(double centerX, double centerY, double x2, double y2, double x3, double y3)
{
    var angle = CalculateAngle0To360(centerX, centerY, x2, y2, x3, y3);
    if (angle > 180.0)
    {
        angle = 360 - angle;
    }
    return angle;
}

}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.