Bir çizgi ile yatay eksen arasındaki açı nasıl hesaplanır?


248

Bir programlama dilinde (Python, C #, vb.) Bir çizgi ve yatay eksen arasındaki açının nasıl hesaplanacağını belirlemem gerekir?

Bence bir görüntü istediğimi en iyi şekilde tanımlar:

hiçbir kelime bunu tarif edemez

(P1 x , P1 y ) ve (P2 x , P2 y ) verildiğinde, bu açıyı hesaplamanın en iyi yolu nedir? Başlangıç ​​noktası üst soldadır ve sadece pozitif kadran kullanılır.


Yanıtlar:


388

İlk olarak başlangıç ​​noktası ve bitiş noktası arasındaki farkı bulun (burada, çizgiler "sonsuz" olduğundan ve belirli bir noktada başlamadığından, "çizgi" değil, daha çok yönlendirilmiş bir çizgi segmentidir.

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

Ardından açıyı (pozitif X ekseninden P1pozitif Y eksenine doğru giden P1) hesaplayın .

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

Ancak arctanideal olmayabilir, çünkü farklılıkları bu şekilde bölmek, açının hangi kadranı ayırt etmek için gerekli ayrımı silecektir (aşağıya bakınız). Dilinizde bir atan2işlev varsa aşağıdakileri kullanın :

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

EDIT (22 Şubat 2017): Bununla birlikte, genel olarak, atan2(deltaY,deltaX)sadece doğru açıyı almak için çağrıda bulunur cosve sinyetersiz olabilir. Bu gibi durumlarda, genellikle aşağıdakileri yapabilirsiniz:

  1. (deltaX, deltaY)Bir vektör gibi davranın .
  2. Bu vektörü birim vektöre normalize edin. Bunu bölmek yapmak için deltaXve deltaYvektörün uzunluğu (tarafından sqrt(deltaX*deltaX+deltaY*deltaY)uzunluğu 0 olmadıkça).
  3. Bundan sonra, deltaXşimdi vektör ve yatay eksen arasındaki açının kosinüsü olacaktır (pozitif X'ten pozitif Y eksenine doğru yönde P1).
  4. Ve deltaYşimdi bu açının sinüsü olacak.
  5. Vektörün uzunluğu 0 ise, yatay eksen ile onun arasında bir açı olmaz (bu nedenle anlamlı bir sinüs ve kosinüs içermez).

EDIT (28 Şubat 2017): Normalleştirmeden bile (deltaX, deltaY):

  • İşareti, deltaX3. adımda açıklanan kosinüsün pozitif veya negatif olup olmadığını söyleyecektir.
  • İşareti, deltaY4. adımda açıklanan sinüsün pozitif veya negatif olup olmadığını söyleyecektir.
  • Aşağıdaki konumdaki pozitif X eksenine göre açının hangi çeyrek dairenin işaretleri olduğunu deltaXve deltaYsize söyleyecektir P1:
    • +deltaX, +deltaY: 0 ila 90 derece.
    • -deltaX, +deltaY: 90 ila 180 derece.
    • -deltaX, -deltaY: 180 ila 270 derece (-180 ila -90 derece).
    • +deltaX, -deltaY: 270 ila 360 derece (-90 ila 0 derece).

Python'da radyan kullanan bir uygulama (cevabımı düzenleyen Eric Leschinski tarafından 19 Temmuz 2015'te sağlandı):

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)

Tüm testler geçer. Bkz. Https://en.wikipedia.org/wiki/Unit_circle


35
Bunu bulduysanız ve JAVASCRiPT kullanıyorsanız, Math.sin ve Math.cos'un radyan aldığını ve dolayısıyla sonucu dereceye dönüştürmenize gerek olmadığını belirtmek çok önemlidir! Bu yüzden * 180 / PI bitini dikkate almayın. Bunu bulmam 4 saatimi aldı. :)
sidonaldson

Dikey eksen boyunca açıyı hesaplamak için ne kullanılmalıdır?
ZeMoon

3
@akashg: 90 - angleInDegrees ?
jbaums

Neden 90 - angleInDegrees yapmamız gerekiyor, bunun bir nedeni var mı? Lütfen aynısını açıklığa kavuşturun.
Praveen Matanam

2
@sidonaldson Bu sadece Javascript'ten daha fazlası, C, C #, C ++, Java vb. Henüz varsayılan olarak dereceleri destekleyen bir dil görmedim.
Pharap

50

Üzgünüm, ama Peter'ın cevabının yanlış olduğundan eminim. Y ekseninin sayfa aşağı gittiğine dikkat edin (grafiklerde yaygındır). Bu nedenle deltaY hesaplaması tersine çevrilmelidir veya yanlış cevabı alırsınız.

Düşünmek:

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

verir

45.0
-45.0
135.0
-135.0

Dolayısıyla, yukarıdaki örnekte P1 (1,1) ve P2 (2,2) ise [Y sayfayı aşağı doğru artırdığından], yukarıdaki kod gösterilen örnek için 45,0 derece verecektir, bu yanlıştır. Delta hesaplamasının sırasını değiştirin ve düzgün çalışıyor.


3
Önerdiğiniz gibi geri çevirdim ve rotasyonum geri döndü.
Şeytan'ın Avukatı

1
double arc = Math.atan2(mouse.y - obj.getPy(), mouse.x - obj.getPx()); degrees = Math.toDegrees(arc); if (degrees < 0) degrees += 360; else if (degrees > 360) degrees -= 360;
Marcus Becker

Açının bulunduğunuz dairenin çeyreğine bağlıdır: İlk çeyrekte (90 dereceye kadar) deltaX ve deltaY (Math.abs) için pozitif değerler, ikinci (90-180) kullanımda deltaX'ın soyut değerini olumsuzlamak, üçüncü (180-270) 'de hem deltaX hem de deltaY'yi ve dördüncü int (270-360) sadece deltaY'yi olumsuzlamak - aşağıdaki cevabımı görün
mamashare

1

Python'da iyi çalışan bir çözüm buldum!

from math import atan2,degrees

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

print GetAngleOfLineBetweenTwoPoints(1,3)

1

Tam soruyu göz önünde bulundurarak, bizi pozitif eksenin AŞAĞI hareket ettirmek (ekran veya arayüz görünümü gibi) anlamına geldiği bir "özel" koordinat sistemine koymak, bu işlevi şu şekilde uyarlamanız ve Y koordinatlarını negatif yapmanız gerekir:

Swift 2.0 örneği

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
}

Bu işlev soruya doğru bir cevap verir. Cevap radyan cinsindendir, bu nedenle açıları derece cinsinden görüntülemek için kullanımı:

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

0

Referans "Peter O" dayanmaktadır .. İşte java sürümü

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 fonksiyonu:

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

0 ila 2pi arasında bir açı için bir formül.

X = x2-x1 ve y = y2-y1 vardır. Formül,

herhangi bir x ve y değeri. X = y = 0 için sonuç tanımsızdır.

F (x, y) = pi () - pi () / 2 * (1 + işareti (x)) * (1-işareti (y ^ 2))

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

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

0
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;

Kodunuzun bir anlamı yok: başka (270-360) .. ne?
WDUK

0
import math
from collections import namedtuple


Point = namedtuple("Point", ["x", "y"])


def get_angle(p1: Point, p2: Point) -> float:
    """Get the angle of this line with the horizontal axis."""
    dx = p2.x - p1.x
    dy = p2.y - p1.y
    theta = math.atan2(dy, dx)
    angle = math.degrees(theta)  # angle is in (-180, 180]
    if angle < 0:
        angle = 360 + angle
    return angle

Test yapmak

Test için hipotezin test senaryoları oluşturmasına izin verdim .

resim açıklamasını buraya girin

import hypothesis.strategies as s
from hypothesis import given


@given(s.floats(min_value=0.0, max_value=360.0))
def test_angle(angle: float):
    epsilon = 0.0001
    x = math.cos(math.radians(angle))
    y = math.sin(math.radians(angle))
    p1 = Point(0, 0)
    p2 = Point(x, y)
    assert abs(get_angle(p1, p2) - angle) < epsilon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.