Jak obliczyć kąt między linią a osią poziomą?

W języku programowania (Python, C#, etc) muszę określić jak obliczyć kąt między linią a osią poziomą?

Myślę, że obraz najlepiej opisuje to, czego chcę:

żadne słowa tego nie opisują

Podane (P1 x, P1 y ) i (P2 x , P2 y) jaki jest najlepszy sposób obliczenia tego kąta? Początek jest w topleft i tylko dodatni kwadrant jest używany.

Author: Richard Howes, 2011-09-28

7 answers

Najpierw znajdź różnicę między punktem początkowym a punktem końcowym (tutaj jest to bardziej ukierunkowany odcinek linii, a nie "Linia", ponieważ linie rozciągają się w nieskończoność i nie zaczynają się w określonym punkcie).

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

Następnie Oblicz kąt (który biegnie od dodatniej osi X w P1 do dodatniej osi Y w P1).

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

Ale arctan może nie być idealne, ponieważ dzielenie różnic w ten sposób usunie rozróżnienie potrzebne do rozróżnienia, który kwadrant kąta jest w (patrz niżej). Jeśli twój język zawiera funkcję atan2, użyj następującej opcji:

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

EDIT (Feb. 22, 2017): ogólnie jednak wywołanie atan2(deltaY,deltaX) tylko po to, aby uzyskać właściwy kąt dla cos i sin może być nieeleganckie. W takich przypadkach często można zamiast tego wykonać następujące czynności:]}

  1. traktować (deltaX, deltaY) jako wektor.
  2. znormalizuj ten wektor do wektora jednostkowego. Aby to zrobić, podziel deltaX i deltaY przez długość wektora (sqrt(deltaX*deltaX+deltaY*deltaY)), chyba że długość wynosi 0.
  3. Po to, deltaX będzie teraz cosinusem kąta między wektorem a osią poziomą (w kierunku od dodatniej X do dodatniej osi Y w P1).
  4. i deltaY będzie teraz sinusem tego kąta.
  5. jeśli długość wektora wynosi 0, nie będzie miał kąta między nim a osią poziomą (więc nie będzie miał wymownego sinusa i cosinusa).

EDIT (Feb. 28, 2017): nawet bez normalizacji (deltaX, deltaY):

  • znak deltaX powie Ci czy cosinus opisany w kroku 3 jest dodatni czy ujemny.
  • znak deltaY powie Ci, czy sinus opisany w kroku 4 jest dodatni czy ujemny.
  • znaki deltaX i deltaY powiedzą ci, w którym kwadrancie znajduje się kąt, w stosunku do dodatniej osi X w P1:
    • +deltaX, +deltaY: 0 do 90 stopni.
    • -deltaX, +deltaY: 90 do 180 stopni.
    • -deltaX, -deltaY: 180 do 270 stopni (od -180 do -90 stopni).
    • +deltaX, -deltaY: 270 do 360 stopni (-90 do 0 stopni).

Implementacja w Pythonie z użyciem radianów (dostarczona 19 lipca 2015 przez Erica Leschinskiego, który edytował moją odpowiedź):

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)
Wszystkie testy przeszły pomyślnie. Zobacz https://en.wikipedia.org/wiki/Unit_circle
 379
Author: Peter O.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-28 11:21:34

Przepraszam, ale jestem pewien, że odpowiedź Petera jest błędna. Należy pamiętać, że oś y schodzi w dół strony(często w grafice). W związku z tym obliczenia deltaY muszą zostać odwrócone, w przeciwnym razie otrzymasz złą odpowiedź.

Rozważmy:

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

Daje

45.0
-45.0
135.0
-135.0

Więc jeśli w powyższym przykładzie P1 jest (1,1) i P2 jest (2,2) [ponieważ y zwiększa się w dół strony], powyższy kod da 45.0 stopni dla pokazanego przykładu, co jest błędne. Zmień kolejność obliczeń deltaY i działa jak należy.

 48
Author: user1641082,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-13 00:24:51

Znalazłem rozwiązanie w Pythonie, które działa dobrze !

from math import atan2,degrees

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

print GetAngleOfLineBetweenTwoPoints(1,3)
 1
Author: dctremblay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-06 07:09:41

Biorąc pod uwagę dokładne pytanie, umieszczając nas w "specjalnym" systemie współrzędnych, w którym dodatnia oś oznacza poruszanie się w dół (jak ekran lub widok interfejsu), musisz dostosować tę funkcję w ten sposób, a ujemne współrzędne Y:

Przykład w 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
}

Ta funkcja daje poprawną odpowiedź na pytanie. Odpowiedź jest w radianach, więc użycie, aby zobaczyć kąty w stopniach, jest następujące:

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
Author: philippe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-11 13:32:12

Na podstawie odniesienia "Peter O".. Oto wersja 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
Author: Venkateswara Rao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-14 03:15:51
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
Author: mamashare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-07 10:53:22

Funkcja 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
Author: Benas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-26 13:08:48