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

For a context that I won’t fall into, I need two functions that are essentially mutual from each other.

angle_to() must return the number of degrees clockwise to go from 0 ° to the line connecting p1 to p2 (i.e. p1 is the center of rotation), and where both p1 and p2 are pixel coordinates.

point_pos() should return the pixel coordinates where the hour of the amplitude should change angle .

For both sides, the positive axis is x = 0 ° = 3 hours, and the rotation argument must shift this axis before the calculation starts either clockwise or counterclockwise; then the specified calculation should move in the same direction with this adjusted link.

My progress on each of them is given below; failure:

When clockwise = False, it returns the correct answer for the state clockwise; when clockwise = True, angle_between () returns the correct answer with a rounding error, and point_pos () gives me the wrong answer entirely.

I also added a visual explanation, which I mocked in Illustrator as an apology on the Internet for not being able to solve it, and it is unclear if I'm looking.

Edit: clearing a line that is unnecessarily complicated according to 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)) 

angle_to () point_pos ()

Edit # 2:. Upon request, an unsuccessful conclusion appeared:

angle_to() flips clockwise and counterclockwise (when I tried to fix it, I end up getting the wrong answers) and rotates clockwise and computes 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() simply 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 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) 
+6
source share
3 answers

You can simplify your code a bit by using a couple of simple rules. Simple code is less likely to have errors.

First, converting between clockwise and counterclockwise means only inverting the sign: angle = -angle .

Secondly, to limit the angle to the range [0, 360) , you simply use angle % 360 . This works whether the angle starts negative or positive, integer or floating point.

 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 
+2
source

Re: " angle_to() should return the number of degrees that should be rotated clockwise to go from p1 to p2"

In your code, you subtract the coordinates of p1 from p2 before calculating the angle using atan2 . In fact, you see p1 as the center of your watch, so it makes no sense to talk about “switching from p1 to p2 to turn”. You will need to specify three points: the center around which you rotate, point 1 and point 2. If the coordinates are xc, yc, x1, y1, x2, y2, then you will need to do something like this:

 angle1 = atan2(y1-yc, x1-xc) angle2 = atan2(y2-yc, x2-xc) relative_angle = angle1 - angle2 # now convert to degrees and handle +/-360 issues. 

update with the new specification: "return the number of degrees that will have to be clockwise to go from 0 ° to the line connecting p1-p2":

 angle = degrees(atan2(p2[1], p2[0])) 

This will return the angle clockwise (in pixel coordinates) in the range from -pi to + pi (-180 to +180 degrees). In your example, angle_to((100,100), (25,25)) ("want 225, but get 135"), atan2 will result in -135 degrees, which means +135 degrees counterclockwise. This is the answer you need (modulo 360 degrees), since you did not specify whether the clock hand should turn cw or ccw (you only indicate whether the starting position is cw or ccw relative to the 3 o'clock position), however, depending clockwise , which defaults to False , you are doing something complicated.

If you want to make sure that the hour hand is turning cw, then you should add 360 degrees to the corner of the result if it is negative, and not return the angle.

(Note: I deleted the old answer, the first two comments relate to the old answer.)

+1
source

This is: angle = (360 - angle if angle > 0 else -1 * angle) - rotation I don't know what you were trying to achieve there, but it really does not do what you want. Only the presence of an -angle reflects an angle; changes the direction of the angle, from counterclockwise to clockwise, noting that you are in the status branches counterclockwise. Then you add 360 and it will ruin everything. The else branch simply multiplies the angle by -1 - it changes it again. Clockwise the branch where you need to change the angle (and add 360 so that the angle is positive). The following is a simple version of your function without an additional rotation parameter:

 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 

Your other function suffers from the same problem in these lines:

 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 

Must be:

 angle -= rotation if clockwise: angle = -angle+360 if angle > 0 else -angle else: angle = angle if angle > 0 else angle + 360 
+1
source

All Articles