使用 python 计算径向角,在 clockwise/counterclockwise 方向,给定像素坐标(然后反之亦然)

Using python to calculate radial angle, in clockwise/counterclockwise directions, given pixel coordinates (and then vice-versa)

对于我不会涉及的上下文,我需要两个本质上互为倒数的函数。

angle_to() 应该 return 时针从 0° 到连接 p1p2 的直线必须转动的度数(即 p1为旋转中心),其中p1p2均为像素坐标.

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 点钟位置的起始位置是顺时针还是逆时针) .但是,根据默认为 Falseclockwise 的值,您会做一些复杂的事情。

如果你想确保时钟指针顺时针转动,那么你应该在结果角度上加上 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