Рассчитать угол (по часовой стрелке) между двумя точками

Я давно не использую математику, и это должно быть простой проблемой для решения.

Предположим, у меня есть две точки A: (1, 0) и B: (1, -1).

Я хочу использовать программу (Python или любой другой язык программирования) для вычисления угла по часовой стрелке между A, origin (0, 0) и B. Это будет что-то вроде этого:

angle_clockwise(point1, point2)

Обратите внимание, что порядок параметров имеет значение. Поскольку расчет угла будет по часовой стрелке:

  • Если я вызываю angle_clockwise(A, B), он возвращает 45.
  • Если я вызываю angle_clockwise(B, A), он возвращает 315.

Другими словами, алгоритм выглядит так:

  1. Нарисуйте линию (линию 1) между первой точкой param с помощью (0, 0).
  2. Нарисуйте линию (линию 2) между второй точкой param с помощью (0, 0).
  3. Вращайте линию 1 вокруг (0, 0) по часовой стрелке, пока она не перекрывает линию 2.
  4. Линия углового расстояния, пройденная 1, будет возвращаемым углом.

Есть ли способ кодировать эту проблему?

8 ответов

Решение

Используйте внутреннее произведение и определитель двух векторов. Это действительно то, что вы должны понимать, если хотите понять, как это работает. Вам нужно знать / читать о векторной математике, чтобы понять.

См. https://en.wikipedia.org/wiki/Dot_product и https://en.wikipedia.org/wiki/Determinant

from math import acos
from math import sqrt
from math import pi

def length(v):
    return sqrt(v[0]**2+v[1]**2)
def dot_product(v,w):
   return v[0]*w[0]+v[1]*w[1]
def determinant(v,w):
   return v[0]*w[1]-v[1]*w[0]
def inner_angle(v,w):
   cosx=dot_product(v,w)/(length(v)*length(w))
   rad=acos(cosx) # in radians
   return rad*180/pi # returns degrees
def angle_clockwise(A, B):
    inner=inner_angle(A,B)
    det = determinant(A,B)
    if det<0: #this is a property of the det. If the det < 0 then B is clockwise of A
        return inner
    else: # if the det > 0 then A is immediately clockwise of B
        return 360-inner

При вычислении детерминанта вы объединяете два вектора в матрицу 2 x 2, для которой вы вычисляете детерминант.

Numpy-х arctan2(y, x) вычислит угол против часовой стрелки (значение в радианах между -π и π) между началом координат и точкой (x, y),

Вы могли бы сделать это для ваших очков A а также B, затем вычтите второй угол из первого, чтобы получить угловую разницу со знаком по часовой стрелке. Эта разница будет между -2π и 2π, поэтому, чтобы получить положительный угол между 0 и 2π, вы можете взять модуль по отношению к 2π. Наконец, вы можете конвертировать радианы в градусы, используя np.rad2deg,

import numpy as np

def angle_between(p1, p2):
    ang1 = np.arctan2(*p1[::-1])
    ang2 = np.arctan2(*p2[::-1])
    return np.rad2deg((ang1 - ang2) % (2 * np.pi))

Например:

A = (1, 0)
B = (1, -1)

print(angle_between(A, B))
# 45.

print(angle_between(B, A))
# 315.

Если вы не хотите использовать NumPy, вы можете использовать math.atan2 на месте np.arctan2 и использовать math.degrees (или просто умножить на 180 / math.pi) для пересчета из радианов в градусы. Одним из преимуществ этой версии является то, что вы также можете пройти два (2, ...) массивы для p1 а также p2 чтобы вычислить углы между несколькими парами точек векторизованным способом.

Вот решение, которое не требует cmath,

import math

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

v1 = Vector(0, 1)
v2 = Vector(0, -1)

v1_theta = math.atan2(v1.y, v1.x)
v2_theta = math.atan2(v2.y, v2.x)

r = (v2_theta - v1_theta) * (180.0 / math.pi)

if r < 0:
    r += 360.0

print r

Проверенное решение от 0° до 360°

Это старый поток, но для меня другие решения не работали, поэтому я реализовал свою собственную версию.

Моя функция вернет число от 0 до 360 (исключая 360) для двух точек на экране (т.е. 'y' начинается сверху и увеличивается к низу), где результаты такие же, как у компаса, 0° вверху, увеличивается по часовой стрелке:

def angle_between_points(p1, p2):
    d1 = p2[0] - p1[0]
    d2 = p2[1] - p1[1]
    if d1 == 0:
        if d2 == 0:  # same points?
            deg = 0
        else:
            deg = 0 if p1[1] > p2[1] else 180
    elif d2 == 0:
        deg = 90 if p1[0] < p2[0] else 270
    else:
        deg = math.atan(d2 / d1) / pi * 180
        lowering = p1[1] < p2[1]
        if (lowering and deg < 0) or (not lowering and deg > 0):
            deg += 270
        else:
            deg += 90
    return deg

Проверьте библиотеку cmath python.

>>> import cmath
>>> a_phase = cmath.phase(complex(1,0))
>>> b_phase = cmath.phase(complex(1,-1))
>>> (a_phase - b_phase) * 180 / cmath.pi
45.0
>>> (b_phase - a_phase) * 180 / cmath.pi
-45.0

Вы можете проверить, меньше ли число 0, и добавить к нему 360, если хотите, чтобы все положительные углы тоже были.

в радианах по часовой стрелке от 0 до PI * 2

      static angle(center:Coord, p1:Coord, p2:Coord) {
    var a1 = Math.atan2(p1.y - center.y, p1.x - center.x);
    var a2 = Math.atan2(p2.y - center.y, p2.x -center.x);
    a1 = a1 > 0 ? a1 : Math.PI * 2 + a1;//make angle from 0 to PI * 2
    a2 = a2 > 0 ? a2 : Math.PI * 2 + a2;
    if(a1 > a2) {
        return a1 - a2;
    } else {
        return Math.PI * 2 - (a2 - a1)
    }
}

Крис Сен-Пьер: при использовании вашей функции с:

A = (x=1, y=0)
B = (x=0, y=1)

Это должно быть 90 градусный угол от A в B, Ваша функция вернется 270,

Есть ли ошибка в том, как вы обрабатываете знак det, или я что-то упустил?

Формула, которая вычисляет угол по часовой стрелке и используется при съемке:

f(E,N)=pi()-pi()/2*(1+sign(N))* (1-sign(E^2))-pi()/4*(2+sign(N))*sign(E)

     -sign(N*E)*atan((abs(N)-abs(E))/(abs(N)+abs(E)))

Формула дает углы от 0 до 2pi, начиная с севера и

работает для любого значения N и E.(N = N2-N1 и E = E2-E1)

Для N=E=0 результат не определен.

Другие вопросы по тегам