blob: 232d1a72ebc16ad7aac7e73d9c123cdab838ada2 [file] [log] [blame]
Parker Schuh19b93b12018-03-02 23:26:58 -08001import numpy
2
3# joint_center in x-y space.
4joint_center = (-12.275, 11.775)
5
6# Joint distances (l1 = "proximal", l2 = "distal")
7l1 = 46.25
8l2 = 43.75
9
10# Convert from x-y coordinates to theta coordinates.
11# orientation is a bool. This orientation is c_i mod 2.
12# where c_i is the circular index, or the position in the
13# "hyperextension" zones. "cross_point" allows shifting the place where
14# it rounds the result so that it draws nicer (no other functional differences).
15def to_theta(x, y, orient, cross_point = -numpy.pi):
16 x -= joint_center[0]
17 y -= joint_center[1]
18 l3 = numpy.sqrt(x ** 2 + y ** 2)
19 t3 = numpy.arctan2(y, x)
20 t1 = numpy.arccos((l1 ** 2 + l3 ** 2 - l2 ** 2) / (2 * l1 * l3))
21
22 if orient:
23 t1 = -t1
24 t1 += t3
25 t1 = (t1 - cross_point) % (2 * numpy.pi) + cross_point
26 t2 = numpy.arctan2(y - l1 * numpy.sin(t1), x - l1 * numpy.cos(t1))
27 return (t1, t2)
28
29# Simple trig to go back from theta1, theta2 to x-y
30def to_xy(t1, t2):
31 x = numpy.cos(t1) * l1 + numpy.cos(t2) * l2 + joint_center[0]
32 y = numpy.sin(t1) * l1 + numpy.sin(t2) * l2 + joint_center[1]
33 orient = ((t2 - t1) % (2 * numpy.pi)) < numpy.pi
34 return (x, y, orient)
35
36# Draw a list of lines to a cairo context.
37def draw_lines(cr, lines):
38 cr.move_to(lines[0][0], lines[0][1])
39 for pt in lines[1:]:
40 cr.line_to(pt[0], pt[1])
41
42max_dist = 1.0
43max_dist_theta = numpy.pi / 64
44
45# Subdivide in theta space.
46def subdivide_theta(lines):
47 out = []
48 last_pt = lines[0]
49 out.append(last_pt)
50 for n_pt in lines[1:]:
51 for pt in subdivide(last_pt, n_pt, max_dist_theta):
52 out.append(pt)
53 last_pt = n_pt
54
55 return out
56
57# subdivide in xy space.
58def subdivide_xy(lines, max_dist = max_dist):
59 out = []
60 last_pt = lines[0]
61 out.append(last_pt)
62 for n_pt in lines[1:]:
63 for pt in subdivide(last_pt, n_pt, max_dist):
64 out.append(pt)
65 last_pt = n_pt
66
67 return out
68
69# to_theta, but distinguishes between
70def to_theta_with_ci(x, y, ci):
71 t1, t2 = to_theta(x, y, (ci % 2) == 0)
72 n_ci = int(numpy.floor((t2 - t1) / numpy.pi))
73 t2 = t2 + ((ci - n_ci)) * numpy.pi
74 return numpy.array((t1, t2))
75
76# alpha is in [0, 1] and is the weight to merge a and b.
77def alpha_blend(a, b, alpha):
78 return b * alpha + (1 - alpha) * a
79
80# Pure vector normalization.
81def normalize(v):
82 norm = numpy.linalg.norm(v)
83 if norm == 0:
84 return v
85 return v / norm
86
87# CI is circular index and allows selecting between all the stats that map
88# to the same x-y state (by giving them an integer index).
89# This will compute approximate first and second derivatives with respect
90# to path length.
91def to_theta_with_ci_and_derivs(x, y, dx, dy, c_i_select):
92 a = to_theta_with_ci(x, y, c_i_select)
93 b = to_theta_with_ci(x + dx * 0.0001, y + dy * 0.0001, c_i_select)
94 c = to_theta_with_ci(x - dx * 0.0001, y - dy * 0.0001, c_i_select)
95 d1 = normalize(b - a)
96 d2 = normalize(c - a)
97 accel = (d1 + d2) / numpy.linalg.norm(a - b)
98 return (a[0], a[1], d1[0], d1[1], accel[0], accel[1])
99
100# Generic subdivision algorithm.
101def subdivide(p1, p2, max_dist):
102 dx = p2[0] - p1[0]
103 dy = p2[1] - p1[1]
104 dist = numpy.sqrt(dx ** 2 + dy ** 2)
105 n = int(numpy.ceil(dist / max_dist))
106 return [(alpha_blend(p1[0], p2[0], float(i) / n),
107 alpha_blend(p1[1], p2[1], float(i) / n)) for i in range(1, n + 1)]
108
109# subdivision thresholds.
110max_dist = 1.0
111max_dist_theta = numpy.pi / 64
112
113# convert from an xy space loop into a theta loop.
114# All segements are expected go from one "hyper-extension" boundary
115# to another, thus we must go backwards over the "loop" to get a loop in
116# x-y space.
117def to_theta_loop(lines, cross_point = -numpy.pi):
118 out = []
119 last_pt = lines[0]
120 for n_pt in lines[1:]:
121 for pt in subdivide(last_pt, n_pt, max_dist):
122 out.append(to_theta(pt[0], pt[1], True, cross_point))
123 last_pt = n_pt
124 for n_pt in reversed(lines[:-1]):
125 for pt in subdivide(last_pt, n_pt, max_dist):
126 out.append(to_theta(pt[0], pt[1], False, cross_point))
127 last_pt = n_pt
128 return out
129
130# Convert a loop (list of line segments) into
131# The name incorrectly suggests that it is cyclic.
132def back_to_xy_loop(lines):
133 out = []
134 last_pt = lines[0]
135 out.append(to_xy(last_pt[0], last_pt[1]))
136 for n_pt in lines[1:]:
137 for pt in subdivide(last_pt, n_pt, max_dist_theta):
138 out.append(to_xy(pt[0], pt[1]))
139 last_pt = n_pt
140
141 return out
142
143 items = [to_xy(t1, t2) for t1, t2 in lines]
144 return [(item[0], item[1]) for item in items]
145
146# Segment in angle space.
147class AngleSegment:
148 def __init__(self, st, ed):
149 self.st = st
150 self.ed = ed
151 def __repr__(self):
152 return "AngleSegment(%s, %s)" % (repr(self.st), repr(self.ed))
153
154 def DrawTo(self, cr, theta_version):
155 if (theta_version):
156 cr.move_to(self.st[0], self.st[1])
157 cr.line_to(self.ed[0], self.ed[1])
158 else:
159 draw_lines(cr, back_to_xy_loop([self.st, self.ed]))
160
161 def ToThetaPoints(self):
162 return [self.st, self.ed]
163
164# Segment in X-Y space.
165class XYSegment:
166 def __init__(self, st, ed):
167 self.st = st
168 self.ed = ed
169 def __repr__(self):
170 return "XYSegment(%s, %s)" % (repr(self.st), repr(self.ed))
171 def DrawTo(self, cr, theta_version):
172 if (theta_version):
173 t1, t2 = self.st
174 c_i_select = int(numpy.floor((self.st[1] - self.st[0]) / numpy.pi))
175 st = to_xy(*self.st)
176 ed = to_xy(*self.ed)
177
178 ln = [(st[0], st[1]), (ed[0], ed[1])]
179 draw_lines(cr, [to_theta_with_ci(x, y, c_i_select) for x, y in subdivide_xy(ln)])
180 else:
181 st = to_xy(*self.st)
182 ed = to_xy(*self.ed)
183 cr.move_to(st[0], st[1])
184 cr.line_to(ed[0], ed[1])
185
186 # Converts to points in theta space via to_theta_with_ci_and_derivs
187 def ToThetaPoints(self):
188 t1, t2 = self.st
189 c_i_select = int(numpy.floor((self.st[1] - self.st[0]) / numpy.pi))
190 st = to_xy(*self.st)
191 ed = to_xy(*self.ed)
192
193 ln = [(st[0], st[1]), (ed[0], ed[1])]
194
195 dx = ed[0] - st[0]
196 dy = ed[1] - st[1]
197 mag = numpy.sqrt((dx) ** 2 + (dy) ** 2)
198 dx /= mag
199 dy /= mag
200
201 return [to_theta_with_ci_and_derivs(x, y, dx, dy, c_i_select) for x, y in subdivide_xy(ln, 1.0)]
202
203segs = [XYSegment((1.3583511559969876, 0.99753029519739866), (0.97145546090878643, -1.4797428713062153))]
204segs = [XYSegment((1.3583511559969876, 0.9975302951973987), (1.5666193247337956, 0.042054827580659759))]