web-dev-qa-db-ja.com

線と横軸のなす角度はどうやって計算するのですか?

プログラミング言語(Python、C#など)で、私は線と水平軸の間の角度を計算する方法を決定する必要がありますか?

私はイメージが私が欲しいものを最もよく表すと思います:

no words can describe this

与えられた(P1バツ、P1)と(P2)バツ、P2)この角度を計算するための最良の方法は何ですか?原点は最上位にあり、正の象限だけが使用されます。

246
orlp

まず始点と終点の違いを見つけます(ここでは、線は無限に伸びて特定の点から始まらないため、「線」ではなく、より有向線分になります)。

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

次に角度を計算します(P1の正のX軸からP1の正のY軸までの角度)。

angleInDegrees = arctan(deltaY / deltaX) * 180 / PI

しかし、arctanは理想的ではないかもしれません、違いをこのように分割することは角度がどの四分円にあるかを区別するために必要な区別を消すからです(下記参照)。使用している言語にatan2関数が含まれている場合は、代わりに次のコードを使用してください。

angleInDegrees = atan2(deltaY, deltaX) * 180 / PI

編集(2017年2月22日):しかし、一般的に、cossinの適切な角度を取得するためだけにatan2(deltaY,deltaX)を呼び出すのは巧妙ではないかもしれません。そのような場合は、代わりに次のことを頻繁に実行できます。

  1. (deltaX, deltaY)をベクトルとして扱います。
  2. そのベクトルを単位ベクトルに正規化します。そのためには、長さが0でない限り、deltaXdeltaYをベクトルの長さ(sqrt(deltaX*deltaX+deltaY*deltaY))で除算します。
  3. その後、deltaXはベクトルと水平軸の間の角度の余弦になります(P1の正のXから正のY軸の方向)。
  4. そしてdeltaYは今やその角度の正弦となるでしょう。
  5. ベクトルの長さが0の場合、ベクトルと水平軸の間に角度はありません(したがって、意味のある正弦と余弦はありません)。

編集(2017年2月28日):(deltaX, deltaY)を正規化しなくても:

  • deltaXの符号は、手順3で説明した余弦が正か負かを示します。
  • deltaYの符号は、ステップ4で説明したサインが正か負かを示します。
  • deltaXdeltaYの記号は、P1の正のX軸に対して、角度がどの四分円にあるかを示します。
    • +deltaX+deltaY:0〜90度.
    • -deltaX+deltaY:90から180度。
    • -deltaX-deltaY:180〜270度(-180〜-90度)。
    • +deltaX-deltaY:270〜360度(-90〜0度).

ラジアンを使ったPythonでの実装(2015年7月19日にEric Leschinskiが提供しました。彼は私の答えを編集しました):

from math import *
def angle_trunc(a):
    while a < 0.0:
        a += pi * 2
    return a

def getAngleBetweenPoints(x_orig, y_orig, x_landmark, y_landmark):
    deltaY = y_landmark - y_orig
    deltaX = x_landmark - x_orig
    return angle_trunc(atan2(deltaY, deltaX))

angle = getAngleBetweenPoints(5, 2, 1,4)
assert angle >= 0, "angle must be >= 0"
angle = getAngleBetweenPoints(1, 1, 2, 1)
assert angle == 0, "expecting angle to be 0"
angle = getAngleBetweenPoints(2, 1, 1, 1)
assert abs(pi - angle) <= 0.01, "expecting angle to be pi, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 3)
assert abs(angle - pi/2) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 0)
assert abs(angle - (pi+pi/2)) <= 0.01, "expecting angle to be pi+pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(1, 1, 2, 2)
assert abs(angle - (pi/4)) <= 0.01, "expecting angle to be pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -2, -2)
assert abs(angle - (pi+pi/4)) <= 0.01, "expecting angle to be pi+pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -1, 2)
assert abs(angle - (pi/2)) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)

すべてのテストに合格しました。 https://en.wikipedia.org/wiki/Unit_circle を参照してください。

381
Peter O.

申し訳ありませんが、Peterの答えは間違っていると確信しています。 y軸はページの下に行くことに注意してください(グラフィックでは一般的です)。そのため、deltaYの計算を逆にする必要があります。そうしないと、間違った答えが得られます。

検討してください:

System.out.println (Math.toDegrees(Math.atan2(1,1)));
System.out.println (Math.toDegrees(Math.atan2(-1,1)));
System.out.println (Math.toDegrees(Math.atan2(1,-1)));
System.out.println (Math.toDegrees(Math.atan2(-1,-1)));

与える

45.0
-45.0
135.0
-135.0

したがって、上記の例で、P1が(1,1)でP2が(2,2)の場合(Yがページの下に向かって増加するため)、上のコードは図の例で45.0度になりますが、これは誤りです。 deltaYの計算順序を変更すると正しく動作します。

49
user1641082

私はPythonでうまくいっている解決策を見つけました!

from math import atan2,degrees

def GetAngleOfLineBetweenTwoPoints(p1, p2):
    return degrees(atan2(p2 - p1, 1))

print GetAngleOfLineBetweenTwoPoints(1,3)
1
dctremblay

正確な問題を考えて、私たちを正の軸がDOWNを動かすことを意味する(スクリーンやインターフェースビューのような)特別な座標系にして、あなたはこの関数をこのように適応させY座標を負にする必要があります:

Swift 2.0での例

func angle_between_two_points(pa:CGPoint,pb:CGPoint)->Double{
    let deltaY:Double = (Double(-pb.y) - Double(-pa.y))
    let deltaX:Double = (Double(pb.x) - Double(pa.x))
    var a = atan2(deltaY,deltaX)
    while a < 0.0 {
        a = a + M_PI*2
    }
    return a
}

この関数は質問に正解します。答えはラジアンであるため、角度で角度を表示するための使用法は次のとおりです。

let p1 = CGPoint(x: 1.5, y: 2) //estimated coords of p1 in question
let p2 = CGPoint(x: 2, y : 3) //estimated coords of p2 in question

print(angle_between_two_points(p1, pb: p2) / (M_PI/180))
//returns 296.56
1
philippe
deltaY = Math.Abs(P2.y - P1.y);
deltaX = Math.Abs(P2.x - P1.x);

angleInDegrees = Math.atan2(deltaY, deltaX) * 180 / PI

if(p2.y > p1.y) // Second point is lower than first, angle goes down (180-360)
{
  if(p2.x < p1.x)//Second point is to the left of first (180-270)
    angleInDegrees += 180;
  else (270-360)
    angleInDegrees += 270;
}
else if (p2.x < p1.x) //Second point is top left of first (90-180)
  angleInDegrees += 90;
0
mamashare

0から2πまでの角度の公式。

X = x 2 - x 1とy = y 2 - y 1があります。

xとyの任意の値x = y = 0の場合、結果は未定義です。

f(x,y)=pi()-pi()/2*(1+sign(x))*(1-sign(y^2))

     -pi()/4*(2+sign(x))*sign(y)

     -sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
0

参照 "Peter O"に基づく..これがJavaバージョンです

private static final float angleBetweenPoints(PointF a, PointF b) {
float deltaY = b.y - a.y;
float deltaX = b.x - a.x;
return (float) (Math.atan2(deltaY, deltaX)); }
0

matlab関数:

function [lineAngle] = getLineAngle(x1, y1, x2, y2) 
    deltaY = y2 - y1;
    deltaX = x2 - x1;

    lineAngle = rad2deg(atan2(deltaY, deltaX));

    if deltaY < 0
        lineAngle = lineAngle + 360;
    end
end
0
Benas