web-dev-qa-db-ja.com

3点から角度を計算する方法は?

これがあるとしましょう:

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

P1が円の中心点であると仮定します。常に同じです。 P2P3で構成される角度、つまりP1の隣の角度が必要です。正確な内角。常に鋭角になるため、-90度未満です。

私は思った:人間、それは単純な幾何学の数学だ。しかし、私は今約6時間の公式を探しており、arccoやベクトルスカラー製品などの複雑なNASAのことについて話している人だけを見つけました。頭が冷蔵庫の中にあるように感じます。

これは簡単な問題だと思う数学の達人がいますか?ここではプログラミング言語は重要ではないと思いますが、それが必要だと思う人にとっては:JavaとObjective-C。両方に必要ですが、これらにはタグを付けていません。

120
HelloMoon

P1が頂点である角度を意味する場合、 Law of Cosines を使用すると動作します。

arccos((P122 + P132 -P232)/(2 * P12 * P13))

p12 は、P1からP2までのセグメントの長さです。

sqrt((P1バツ -P2バツ2 +(P1y -P2y2

87
Lance Roberts

ポイントP1からP2までとP1からP3までの2つのベクトルと考えると、非常に簡単になります。

そう:
a =(p1.x-p2.x、p1.y-p2.y)
b =(p1.x-p3.x、p1.y-p3.y)

次に、内積公式を逆にすることができます。
dot product
角度を取得するには:
angle between two vectors

覚えておいて dot product ちょうど意味:a1 * b1 + a2 * b2(ここではちょうど2つの次元...)

47
Andrea Ambu

角度の計算に対処する最良の方法は、atan2(y, x)を使用することです。x, yは、その点からの角度を返し、Originに対するX+軸を返します。

計算が

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

つまり、基本的に-P1で2つのポイントを変換し(つまり、P1がOriginで終わるようにすべてを変換します)、P3の絶対角度の違いとP2の。

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

atan2の唯一の特異点は(0, 0)...です。つまり、P2P3は両方ともP1とは異なる必要があります。角度について話す感覚。

25
6502

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-canvasを使用した例

19
shaman.sir

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

良い説明と式については、 こちら をご覧ください。

alt text

15
Andre Miller

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)

12
Treb

最近、同様の問題に遭遇しました。正と負の角度を区別する必要があるだけです。これが誰にも役立つ場合、Androidのタッチイベントでの回転の検出について this mailing list から取得したコードスニペットをお勧めします。

 @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;
 }
8
Marc

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

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


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

angle

ϴを知りたいので、αβ最初に。さて、どんな直線に対しても-

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)

Eqn-4のtan α(eqn-2から)およびtan b(eqn-3から)の値を置き換え、簡略化を適用すると、

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)) )

それだ!


今、次の図を取ります-

angle

この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;
    }
7
Minhas Kamal

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

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

または、もっと読む こちら

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

4

my angle demo program

最近、私も同じ問題を抱えています... 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;
4
antonio

これは、円上の点に対して水平から反時計回りに角度(0〜360)を返すC#メソッドです。

    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
Paul
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))))
2
Dominic

そこにIS高校の数学を使用してこれに対する簡単な答えがあります。

あなたが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に変更します。

0
James Penner