For a context I won't get into, I need two functions that are essentially reciprocals of each other.
angle_to()
should return the number of degrees a clockhand would have to turn to travel from 0° to the line connecting p1
to p2
(ie. p1
is the center of rotation), and where both p1
and p2
are pixel coordinates.
point_pos()
should return the pixel coordinates of where a clockhand of length amplitude
would be had it turned angle
.
For both, the positive x-axis = 0° = 3 o'clock, and the argument rotation
should shift that axis before the calculation begins in either the clockwise or counter-clockwise direction; then said calculation should move in the same direction with this adjusted reference.
My progress on each is included below; the failure is:
When clockwise=False, it returns the correct answer for the clockwise condition; when clockwise=True, angle_between() returns the right answer with a rounding error, and point_pos() gives me the wrong answer entirely.
I've also attached a visual explanation I mocked up in Illustrator as an apology to the internet for being unable to solve this and in case what I'm seeking isn't clear.
Edit: cleaned up a line that unnecessarily complicated as per one answer below.
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))
Edit #2: Upon request, here's some failed output:
angle_to()
is flipping clockwise and counterclockwise (when I've tried to fix it, I end up getting wrong answers altogether), and in the clockwise direction, rotating and calculating in different directions
>>> 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()
is just wrong in the counterclockwise direction
# 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)
clockwise=False
, it returns the correct answer for the clockwise condition; whenclockwise=True
,angle_between()
returns the right answer with a rounding error, andpoint_pos()
gives me the wrong answer entirely.angle_to
is supposed to do. One thing though: be aware that your graphics coordinates have the y axis pointing downward, while a standard y axis points upwards. So, angles to use with atan2, cos, sin may be different from what you expect.