使用 python 计算径向角,在 clockwise/counterclockwise 方向,给定像素坐标(然后反之亦然)
Using python to calculate radial angle, in clockwise/counterclockwise directions, given pixel coordinates (and then vice-versa)
对于我不会涉及的上下文,我需要两个本质上互为倒数的函数。
angle_to()
应该 return 时针从 0° 到连接 p1
和 p2
的直线必须转动的度数(即 p1
为旋转中心),其中p1
和p2
均为像素坐标.
point_pos()
应该 return 长度为 amplitude
的时钟指针转动时的像素坐标 angle
.
对于两者,正 x 轴 = 0° = 3 点,参数 rotation
应该在计算开始之前沿顺时针或逆时针方向移动该轴;然后所说的计算应该与这个调整后的参考在同一方向上移动。
我在每个方面的进展都包含在下面;失败是:
当clockwise=False时,return是顺时针条件的正确答案;当 clockwise=True 时,angle_between() return 是带有舍入误差的正确答案,而 point_pos() 给出了完全错误的答案。
我还附上了我在 Illustrator 中模拟的视觉解释,作为对互联网无法解决此问题的道歉,以防我所寻求的内容不清楚。
编辑: 根据下面的一个答案清理了不必要复杂的一行。
from math import sin, cos, radians, pi, atan2, degrees
def angle_to(p1, p2, rotation=0, clockwise=False):
if abs(rotation) > 360:
rotation %= 360
p2 = list(p2)
p2[0] = p2[0] - p1[0]
p2[1] = p2[1] - p1[1]
angle = degrees(atan2(p2[1], p2[0]))
if clockwise:
angle -= rotation
return angle if angle > 0 else angle + 360
else:
angle = (360 - angle if angle > 0 else -1 * angle) - rotation
return angle if angle > 0 else angle + 360
def point_pos(origin, amplitude, angle, rotation=0, clockwise=False):
if abs(rotation) > 360:
rotation %= 360
if clockwise:
rotation *= -1
if clockwise:
angle -= rotation
angle = angle if angle > 0 else angle + 360
else:
angle = (360 - angle if angle > 0 else -1 * angle) - rotation
angle = angle if angle > 0 else angle + 360
theta_rad = radians(angle)
return int(origin[0] + amplitude * cos(theta_rad)), int(origin[1] + amplitude * sin(theta_rad))
编辑 #2: 根据要求,这里有一些失败的输出:
angle_to()
是顺时针和逆时针翻转(当我试图修复它时,我最终得到的答案完全是错误的),顺时针方向,不同方向旋转和计算
>>> print angle_to((100,100), (25,25)) # should be 225
135.0
>>> print angle_to((100,100), (25,25), 45) # should be 180
90.0
>>> print angle_to((100,100), (25,25), clockwise=True) # should be 135
225.0
>>> print angle_to((100,100), (25,25), 45, clockwise=True) # should be 90
180.0
point_pos()
只是逆时针方向错了
# dunno what these should be (i'm bad at trig) but when I visually place the
# given p1 and the output p2 on screen it's obvious that they're wrong
>>> print point_pos((100,100), 75, 225)
(46, 153)
>>> print point_pos((100,100), 75, 225, 45)
(100, 175)
# these are basically correct, rounding-errors notwithstanding
>>> print point_pos((100,100), 75, 225, clockwise=True)
(46, 46)
>>> print point_pos((100,100), 75, 225, 45, clockwise=True)
(99, 25)
回复:“angle_to()
应该 return 时针从 p1 到 p2 必须转动的度数”
在您的代码中,在使用 atan2
计算角度之前,您从 p2 中减去点 p1 的坐标。本质上,您将 p1 视为时钟的中心,因此谈论 "travelling from p1 to p2 by a rotation" 没有任何意义。您需要指定三个点:旋转所围绕的中心、点 1 和点 2。如果坐标是 xc、yc、x1、y1、x2、y2,那么您需要执行类似这个:
angle1 = atan2(y1-yc, x1-xc)
angle2 = atan2(y2-yc, x2-xc)
relative_angle = angle1 - angle2
# now convert to degrees and handle +/-360 issues.
使用新规范更新:"return the number of degrees a clockhand would have to turn to travel from 0° to the line connecting p1 to p2":
angle = degrees(atan2(p2[1], p2[0]))
这将 return 顺时针角度(以像素坐标表示)在 -pi 到 +pi(-180 到 +180 度)的范围内。在您的示例中,angle_to((100,100), (25,25))
("want 225, but get 135"),atan2
将导致 -135 度,这意味着逆时针 +135 度。这就是您想要的答案(模数 360 度),因为您没有指定时钟指针应该顺时针还是逆时针转动(您只指定相对于 3 点钟位置的起始位置是顺时针还是逆时针) .但是,根据默认为 False
的 clockwise
的值,您会做一些复杂的事情。
如果你想确保时钟指针顺时针转动,那么你应该在结果角度上加上 360 度,如果它是负的,而不是反转角度。
(注:我删除了旧答案,前两条评论参考旧答案。)
这个:
angle = (360 - angle if angle > 0 else -1 * angle) - rotation
我不知道你想在那里实现什么,但这确实没有达到你想要的效果。刚好有-angle
反射角度;将角度方向从逆时针更改为顺时针,注意您处于条件的逆时针分支中。然后你添加 360
,这就把一切都搞砸了。 else 分支只是将角度乘以 -1
- 再次反转它。顺时针分支是您需要反转角度的地方(并添加 360 以确保角度为正)。
这是您的函数的简单版本,没有额外的旋转参数:
def angle_to(p1, p2, clockwise=False):
p2 = list(p2)
p2[0] = p2[0] - p1[0]
p2[1] = (p2[1] - p1[1])
angle = degrees(atan2(p2[1], p2[0]))
angle = 360 + angle if angle < 0 else angle
return angle if not clockwise else -angle+360
您的其他函数在这些行中遇到完全相同的问题:
if clockwise:
angle -= rotation
angle = angle if angle > 0 else angle + 360
else:
angle = (360 - angle if angle > 0 else -1 * angle) - rotation
angle = angle if angle > 0 else angle + 360
应该是:
angle -= rotation
if clockwise:
angle = -angle+360 if angle > 0 else -angle
else:
angle = angle if angle > 0 else angle + 360
您可以通过使用一些简单的规则来大大简化您的代码。简单的代码不太可能有错误。
首先,顺时针和逆时针之间的转换只是将符号反转:angle = -angle
。
其次,要将角度限制在 [0, 360)
范围内,您只需使用 angle % 360
。无论角度开始时是负数还是正数,整数还是浮点数,这都有效。
def angle_to(p1, p2, rotation=0, clockwise=False):
angle = degrees(atan2(p2[1] - p1[1], p2[0] - p1[0])) - rotation
if not clockwise:
angle = -angle
return angle % 360
对于我不会涉及的上下文,我需要两个本质上互为倒数的函数。
angle_to()
应该 return 时针从 0° 到连接 p1
和 p2
的直线必须转动的度数(即 p1
为旋转中心),其中p1
和p2
均为像素坐标.
point_pos()
应该 return 长度为 amplitude
的时钟指针转动时的像素坐标 angle
.
对于两者,正 x 轴 = 0° = 3 点,参数 rotation
应该在计算开始之前沿顺时针或逆时针方向移动该轴;然后所说的计算应该与这个调整后的参考在同一方向上移动。
我在每个方面的进展都包含在下面;失败是:
当clockwise=False时,return是顺时针条件的正确答案;当 clockwise=True 时,angle_between() return 是带有舍入误差的正确答案,而 point_pos() 给出了完全错误的答案。
我还附上了我在 Illustrator 中模拟的视觉解释,作为对互联网无法解决此问题的道歉,以防我所寻求的内容不清楚。
编辑: 根据下面的一个答案清理了不必要复杂的一行。
from math import sin, cos, radians, pi, atan2, degrees
def angle_to(p1, p2, rotation=0, clockwise=False):
if abs(rotation) > 360:
rotation %= 360
p2 = list(p2)
p2[0] = p2[0] - p1[0]
p2[1] = p2[1] - p1[1]
angle = degrees(atan2(p2[1], p2[0]))
if clockwise:
angle -= rotation
return angle if angle > 0 else angle + 360
else:
angle = (360 - angle if angle > 0 else -1 * angle) - rotation
return angle if angle > 0 else angle + 360
def point_pos(origin, amplitude, angle, rotation=0, clockwise=False):
if abs(rotation) > 360:
rotation %= 360
if clockwise:
rotation *= -1
if clockwise:
angle -= rotation
angle = angle if angle > 0 else angle + 360
else:
angle = (360 - angle if angle > 0 else -1 * angle) - rotation
angle = angle if angle > 0 else angle + 360
theta_rad = radians(angle)
return int(origin[0] + amplitude * cos(theta_rad)), int(origin[1] + amplitude * sin(theta_rad))
编辑 #2: 根据要求,这里有一些失败的输出:
angle_to()
是顺时针和逆时针翻转(当我试图修复它时,我最终得到的答案完全是错误的),顺时针方向,不同方向旋转和计算
>>> print angle_to((100,100), (25,25)) # should be 225
135.0
>>> print angle_to((100,100), (25,25), 45) # should be 180
90.0
>>> print angle_to((100,100), (25,25), clockwise=True) # should be 135
225.0
>>> print angle_to((100,100), (25,25), 45, clockwise=True) # should be 90
180.0
point_pos()
只是逆时针方向错了
# dunno what these should be (i'm bad at trig) but when I visually place the
# given p1 and the output p2 on screen it's obvious that they're wrong
>>> print point_pos((100,100), 75, 225)
(46, 153)
>>> print point_pos((100,100), 75, 225, 45)
(100, 175)
# these are basically correct, rounding-errors notwithstanding
>>> print point_pos((100,100), 75, 225, clockwise=True)
(46, 46)
>>> print point_pos((100,100), 75, 225, 45, clockwise=True)
(99, 25)
回复:“angle_to()
应该 return 时针从 p1 到 p2 必须转动的度数”
在您的代码中,在使用 atan2
计算角度之前,您从 p2 中减去点 p1 的坐标。本质上,您将 p1 视为时钟的中心,因此谈论 "travelling from p1 to p2 by a rotation" 没有任何意义。您需要指定三个点:旋转所围绕的中心、点 1 和点 2。如果坐标是 xc、yc、x1、y1、x2、y2,那么您需要执行类似这个:
angle1 = atan2(y1-yc, x1-xc)
angle2 = atan2(y2-yc, x2-xc)
relative_angle = angle1 - angle2
# now convert to degrees and handle +/-360 issues.
使用新规范更新:"return the number of degrees a clockhand would have to turn to travel from 0° to the line connecting p1 to p2":
angle = degrees(atan2(p2[1], p2[0]))
这将 return 顺时针角度(以像素坐标表示)在 -pi 到 +pi(-180 到 +180 度)的范围内。在您的示例中,angle_to((100,100), (25,25))
("want 225, but get 135"),atan2
将导致 -135 度,这意味着逆时针 +135 度。这就是您想要的答案(模数 360 度),因为您没有指定时钟指针应该顺时针还是逆时针转动(您只指定相对于 3 点钟位置的起始位置是顺时针还是逆时针) .但是,根据默认为 False
的 clockwise
的值,您会做一些复杂的事情。
如果你想确保时钟指针顺时针转动,那么你应该在结果角度上加上 360 度,如果它是负的,而不是反转角度。
(注:我删除了旧答案,前两条评论参考旧答案。)
这个:
angle = (360 - angle if angle > 0 else -1 * angle) - rotation
我不知道你想在那里实现什么,但这确实没有达到你想要的效果。刚好有-angle
反射角度;将角度方向从逆时针更改为顺时针,注意您处于条件的逆时针分支中。然后你添加 360
,这就把一切都搞砸了。 else 分支只是将角度乘以 -1
- 再次反转它。顺时针分支是您需要反转角度的地方(并添加 360 以确保角度为正)。
这是您的函数的简单版本,没有额外的旋转参数:
def angle_to(p1, p2, clockwise=False):
p2 = list(p2)
p2[0] = p2[0] - p1[0]
p2[1] = (p2[1] - p1[1])
angle = degrees(atan2(p2[1], p2[0]))
angle = 360 + angle if angle < 0 else angle
return angle if not clockwise else -angle+360
您的其他函数在这些行中遇到完全相同的问题:
if clockwise:
angle -= rotation
angle = angle if angle > 0 else angle + 360
else:
angle = (360 - angle if angle > 0 else -1 * angle) - rotation
angle = angle if angle > 0 else angle + 360
应该是:
angle -= rotation
if clockwise:
angle = -angle+360 if angle > 0 else -angle
else:
angle = angle if angle > 0 else angle + 360
您可以通过使用一些简单的规则来大大简化您的代码。简单的代码不太可能有错误。
首先,顺时针和逆时针之间的转换只是将符号反转:angle = -angle
。
其次,要将角度限制在 [0, 360)
范围内,您只需使用 angle % 360
。无论角度开始时是负数还是正数,整数还是浮点数,这都有效。
def angle_to(p1, p2, rotation=0, clockwise=False):
angle = degrees(atan2(p2[1] - p1[1], p2[0] - p1[0])) - rotation
if not clockwise:
angle = -angle
return angle % 360