计算两点之间的角度(顺时针)
Calculate angle (clockwise) between two points
我已经很久没有使用数学了,这应该是一个简单的问题。
假设我有两个点 A:(1, 0) 和 B:(1, -1)。
我想用一个程序(Python 或任何编程语言)来计算 A、原点 (0, 0) 和 B 之间的顺时针角度。它将是这样的:
angle_clockwise(point1, point2)
请注意参数的顺序很重要。由于角度计算将顺时针:
- 如果我调用 angle_clockwise(A, B),它 returns 45.
- 如果我调用 angle_clockwise(B, A),它 returns 315.
也就是说算法是这样的:
- 在第一个点参数与 (0, 0) 之间画一条线(第 1 行)。
- 在第二个点参数与 (0, 0) 之间画一条线(第 2 行)。
- 将第 1 行绕 (0, 0) 顺时针旋转直到与第 2 行重叠。
- 直线 1 行进的 angular 距离将是返回的角度。
有什么方法可以解决这个问题吗?
查看 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。
Numpy 的 arctan2(y, x)
将计算原点和点 (x, y)
.
之间的逆时针角度(-π 和 π 之间的弧度值)
您可以对 A
和 B
点执行此操作,然后从第一个角度减去第二个角度以获得顺时针方向的带符号 angular 差值。这个差异将在 -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
in place of np.arctan2
, and use math.degrees
(或乘以 180 / math.pi
)以将弧度转换为度数。 numpy 版本的一个优点是您还可以为 p1
和 p2
传递两个 (2, ...)
数组,以便以矢量化方式计算多对点之间的角度。
使用两个向量的内积和行列式。如果您想了解其工作原理,这确实是您应该了解的内容。您需要 know/read 了解矢量数学才能理解。
参见:https://en.wikipedia.org/wiki/Dot_product and 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 矩阵,并为其计算行列式。
这是一个不需要 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
Chris St Pierre:将您的函数用于:
A = (x=1, y=0)
B = (x=0, y=1)
这应该是从 A
到 B
的 90
度角。您的功能将 return 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 结果未定义。
经过验证的 0° 到 360° 解决方案
这是一个旧线程,但对我来说其他解决方案效果不佳,所以我实现了自己的版本。
我的函数将为屏幕上的两个点 return 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
以弧度为单位,顺时针,从 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:(1, 0) 和 B:(1, -1)。
我想用一个程序(Python 或任何编程语言)来计算 A、原点 (0, 0) 和 B 之间的顺时针角度。它将是这样的:
angle_clockwise(point1, point2)
请注意参数的顺序很重要。由于角度计算将顺时针:
- 如果我调用 angle_clockwise(A, B),它 returns 45.
- 如果我调用 angle_clockwise(B, A),它 returns 315.
也就是说算法是这样的:
- 在第一个点参数与 (0, 0) 之间画一条线(第 1 行)。
- 在第二个点参数与 (0, 0) 之间画一条线(第 2 行)。
- 将第 1 行绕 (0, 0) 顺时针旋转直到与第 2 行重叠。
- 直线 1 行进的 angular 距离将是返回的角度。
有什么方法可以解决这个问题吗?
查看 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。
Numpy 的 arctan2(y, x)
将计算原点和点 (x, y)
.
您可以对 A
和 B
点执行此操作,然后从第一个角度减去第二个角度以获得顺时针方向的带符号 angular 差值。这个差异将在 -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
in place of np.arctan2
, and use math.degrees
(或乘以 180 / math.pi
)以将弧度转换为度数。 numpy 版本的一个优点是您还可以为 p1
和 p2
传递两个 (2, ...)
数组,以便以矢量化方式计算多对点之间的角度。
使用两个向量的内积和行列式。如果您想了解其工作原理,这确实是您应该了解的内容。您需要 know/read 了解矢量数学才能理解。
参见:https://en.wikipedia.org/wiki/Dot_product and 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 矩阵,并为其计算行列式。
这是一个不需要 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
Chris St Pierre:将您的函数用于:
A = (x=1, y=0)
B = (x=0, y=1)
这应该是从 A
到 B
的 90
度角。您的功能将 return 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 结果未定义。
经过验证的 0° 到 360° 解决方案
这是一个旧线程,但对我来说其他解决方案效果不佳,所以我实现了自己的版本。
我的函数将为屏幕上的两个点 return 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
以弧度为单位,顺时针,从 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)
}
}