-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcoordinates.py
More file actions
61 lines (48 loc) · 2.25 KB
/
coordinates.py
File metadata and controls
61 lines (48 loc) · 2.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import math
PI = math.pi
TWOPI = 2.0 * PI
# main transformations
def getAnglesFromCartesian(x, y, lengthA=1, lengthB=1):
## check that the point is a valid option for these segment lengths
d = math.sqrt(x**2 + y**2)
minDist = abs(lengthA - lengthB)
maxDist = abs(lengthA + lengthB)
if not minDist <= d <= maxDist:
raise UnreachablePointError(x=x, y=y, d=d, minDist=minDist, maxDist=maxDist)
## get one solution
# theta_0 is the angle of the base segment, if the point were at (0, d)
if d > 0:
theta_0 = (PI / 2.0) - math.acos((d**2 + lengthA**2 - lengthB**2) /
(2.0 * d * lengthA))
else:
theta_0 = 0
# angleB is the angle of the second segment, relative to the base segment's rotation
angleB = PI - theta_0 - math.acos((lengthA / lengthB) * math.cos(theta_0))
# mu is the clockwise rotation angle to get from (0, d) to (x, y)
mu = math.atan2(x, y)
# angleA is the true angle of the base segment (theta_0 rotated clockwise by mu)
angleA = theta_0 - mu
# angleB stays the same after this mu rotation, since it was calculated relative
# to the base segment's rotation
## to get the second solution, reflect the segments across the line from (0, 0)
## to (x, y) (the radial line)
radialLineAngle = math.atan2(y, x)
reflectedAngleA = radialLineAngle + (radialLineAngle - angleA)
reflectedAngleB = -angleB
return ((angleA, angleB), (reflectedAngleA, reflectedAngleB))
def getCartesianFromAngles(angleA, angleB, lengthA=1, lengthB=1):
x = lengthA * math.cos(angleA) + lengthB * math.cos(angleA + angleB)
y = lengthA * math.sin(angleA) + lengthB * math.sin(angleA + angleB)
return x, y
# helpers
def getNearestAngleClone(angle, target):
direction = 1 if angle < target else -1
angleClone = angle
while abs(angleClone - target) > PI:
angleClone += direction * TWOPI
return angleClone
class UnreachablePointError(Exception):
def __init__(self, x, y, d, minDist, maxDist):
errMsgTemplate = "Distance to ({}, {}) is {}. It must be between {} and {}."
message = errMsgTemplate.format(x, y, d, minDist, maxDist)
super(UnreachablePointError, self).__init__(message)