Как рассчитать угол между линией и горизонтальной осью?
на языке программирования (Python, C# и т. д) Мне нужно определить, как вычислить угол между линией и горизонтальной оси?
Я думаю, что изображение лучше всего описывает то, что я хочу:
Дано (P1x, P1y) и (P2x, P2y) каков наилучший способ рассчитать этот угол? Начало координат находится в верхнем левом углу и используется только положительный квадрант.
7 ответов:
сначала найдите разницу между начальной точкой и конечной точкой (здесь это скорее направленный отрезок линии, а не "линия", поскольку линии простираются бесконечно и не начинаются в определенной точке).
deltaY = P2_y - P1_y deltaX = P2_x - P1_x
затем вычислите угол (который проходит от положительной оси X в
P1
к положительной оси Y вP1
).angleInDegrees = arctan(deltaY / deltaX) * 180 / PI
но
arctan
не может быть идеальным, потому что разделение различий таким образом стирает различие, необходимое для различите, в каком квадранте находится угол (см. ниже). Вместо этого используйте следующее, Если ваш язык содержитatan2
функция:angleInDegrees = atan2(deltaY, deltaX) * 180 / PI
EDIT (февраль. 22, 2017): в общем, однако, звоните
atan2(deltaY,deltaX)
просто, чтобы получить правильный угол дляcos
иsin
может быть неизящно. В этих случаях вы часто можете сделать следующее:
- лечения
(deltaX, deltaY)
как вектор.- нормализовать этот вектор к единичному вектору. Для этого разделите
deltaX
иdeltaY
по длине вектора (sqrt(deltaX*deltaX+deltaY*deltaY)
), если длина равна 0.- после этого,
deltaX
теперь будет косинус угла между вектором и горизонтальной оси (в направлении от положительной оси X к положительной оси Y наP1
).- и
deltaY
теперь будет синус этого угла.- если длина вектора равна 0, он не будет иметь угла между ним и горизонтальной осью (поэтому он не будет иметь значимого синуса и косинус.)
EDIT (февраль. 28 2017,): даже без нормализации
(deltaX, deltaY)
:
- знак
deltaX
покажет вам, является ли Косинус, описанный в шаге 3, положительным или отрицательным.- знак
deltaY
скажет вам, является ли синус, описанный в шаге 4, положительным или отрицательным.- признаки
deltaX
иdeltaY
скажет вам, в каком квадранте находится угол, по отношению к положительной оси X приP1
:
+deltaX
,+deltaY
: от 0 до 90 градусов.-deltaX
,+deltaY
: от 90 до 180 градусов.-deltaX
,-deltaY
: 180 до 270 градусов (-180 до -90 градусов).+deltaX
,-deltaY
: 270 до 360 градусов (-90 до 0 градусов).
реализация в Python с использованием radians (предоставлено 19 июля 2015 года Эриком Лещинским, который редактировал мой ответ):
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
Извините, но я почти уверен, что ответ Питера неверен. Обратите внимание, что ось 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 расчет и он работает правильно.
Я нашел решение в Python, которое работает хорошо !
from math import atan2,degrees def GetAngleOfLineBetweenTwoPoints(p1, p2): return degrees(atan2(p2 - p1, 1)) print GetAngleOfLineBetweenTwoPoints(1,3)
учитывая точный вопрос, помещая нас в "специальную" систему координат, где положительная ось означает перемещение вниз (например, экран или вид интерфейса), вам нужно адаптировать эту функцию следующим образом, а отрицательные координаты 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
на основе ссылки "Питер О".. Вот версия 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)); }
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;