blob: 16ac93da32bbb16f1e7b4db003b0085a32a101fe [file] [log] [blame]
Parker Schuh19b93b12018-03-02 23:26:58 -08001import numpy
2
3# joint_center in x-y space.
Parker Schuhdc682952018-03-03 18:24:01 -08004joint_center = (-0.299, 0.299)
Parker Schuh19b93b12018-03-02 23:26:58 -08005
6# Joint distances (l1 = "proximal", l2 = "distal")
Parker Schuhdc682952018-03-03 18:24:01 -08007l1 = 46.25 * 0.0254
8l2 = 43.75 * 0.0254
9
Parker Schuh19b93b12018-03-02 23:26:58 -080010
11# Convert from x-y coordinates to theta coordinates.
Parker Schuhdc682952018-03-03 18:24:01 -080012# orientation is a bool. This orientation is circular_index mod 2.
13# where circular_index is the circular index, or the position in the
Parker Schuh19b93b12018-03-02 23:26:58 -080014# "hyperextension" zones. "cross_point" allows shifting the place where
15# it rounds the result so that it draws nicer (no other functional differences).
Parker Schuhdc682952018-03-03 18:24:01 -080016def to_theta(pt, circular_index, cross_point=-numpy.pi):
17 orient = (circular_index % 2) == 0
18 x = pt[0]
19 y = pt[1]
20 x -= joint_center[0]
21 y -= joint_center[1]
22 l3 = numpy.hypot(x, y)
23 t3 = numpy.arctan2(y, x)
24 theta1 = numpy.arccos((l1**2 + l3**2 - l2**2) / (2 * l1 * l3))
Parker Schuh19b93b12018-03-02 23:26:58 -080025
Parker Schuhdc682952018-03-03 18:24:01 -080026 if orient:
27 theta1 = -theta1
28 theta1 += t3
29 theta1 = (theta1 - cross_point) % (2 * numpy.pi) + cross_point
30 theta2 = numpy.arctan2(y - l1 * numpy.sin(theta1),
31 x - l1 * numpy.cos(theta1))
32 return numpy.array((theta1, theta2))
33
Parker Schuh19b93b12018-03-02 23:26:58 -080034
35# Simple trig to go back from theta1, theta2 to x-y
Parker Schuhdc682952018-03-03 18:24:01 -080036def to_xy(theta1, theta2):
37 x = numpy.cos(theta1) * l1 + numpy.cos(theta2) * l2 + joint_center[0]
38 y = numpy.sin(theta1) * l1 + numpy.sin(theta2) * l2 + joint_center[1]
39 orient = ((theta2 - theta1) % (2.0 * numpy.pi)) < numpy.pi
40 return (x, y, orient)
41
42
43def get_circular_index(theta):
44 return int(numpy.floor((theta[1] - theta[0]) / numpy.pi))
45
46
47def get_xy(theta):
48 theta1 = theta[0]
49 theta2 = theta[1]
50 x = numpy.cos(theta1) * l1 + numpy.cos(theta2) * l2 + joint_center[0]
51 y = numpy.sin(theta1) * l1 + numpy.sin(theta2) * l2 + joint_center[1]
52 return numpy.array((x, y))
53
Parker Schuh19b93b12018-03-02 23:26:58 -080054
55# Draw a list of lines to a cairo context.
56def draw_lines(cr, lines):
Parker Schuhdc682952018-03-03 18:24:01 -080057 cr.move_to(lines[0][0], lines[0][1])
58 for pt in lines[1:]:
59 cr.line_to(pt[0], pt[1])
Parker Schuh19b93b12018-03-02 23:26:58 -080060
Parker Schuhdc682952018-03-03 18:24:01 -080061
62max_dist = 0.01
Parker Schuh19b93b12018-03-02 23:26:58 -080063max_dist_theta = numpy.pi / 64
Parker Schuhdc682952018-03-03 18:24:01 -080064xy_end_circle_size = 0.01
65theta_end_circle_size = 0.07
66
Parker Schuh19b93b12018-03-02 23:26:58 -080067
68# Subdivide in theta space.
69def subdivide_theta(lines):
Parker Schuhdc682952018-03-03 18:24:01 -080070 out = []
71 last_pt = lines[0]
72 out.append(last_pt)
73 for n_pt in lines[1:]:
74 for pt in subdivide(last_pt, n_pt, max_dist_theta):
75 out.append(pt)
76 last_pt = n_pt
Parker Schuh19b93b12018-03-02 23:26:58 -080077
Parker Schuhdc682952018-03-03 18:24:01 -080078 return out
79
Parker Schuh19b93b12018-03-02 23:26:58 -080080
81# subdivide in xy space.
Parker Schuhdc682952018-03-03 18:24:01 -080082def subdivide_xy(lines, max_dist=max_dist):
83 out = []
84 last_pt = lines[0]
85 out.append(last_pt)
86 for n_pt in lines[1:]:
87 for pt in subdivide(last_pt, n_pt, max_dist):
88 out.append(pt)
89 last_pt = n_pt
Parker Schuh19b93b12018-03-02 23:26:58 -080090
Parker Schuhdc682952018-03-03 18:24:01 -080091 return out
92
93
94def to_theta_with_ci(pt, circular_index):
95 return to_theta_with_circular_index(pt[0], pt[1], circular_index)
96
Parker Schuh19b93b12018-03-02 23:26:58 -080097
98# to_theta, but distinguishes between
Parker Schuhdc682952018-03-03 18:24:01 -080099def to_theta_with_circular_index(x, y, circular_index):
100 theta1, theta2 = to_theta((x, y), circular_index)
101 n_circular_index = int(numpy.floor((theta2 - theta1) / numpy.pi))
102 theta2 = theta2 + ((circular_index - n_circular_index)) * numpy.pi
103 return numpy.array((theta1, theta2))
104
Parker Schuh19b93b12018-03-02 23:26:58 -0800105
106# alpha is in [0, 1] and is the weight to merge a and b.
107def alpha_blend(a, b, alpha):
Parker Schuhdc682952018-03-03 18:24:01 -0800108 """Blends a and b.
Parker Schuh19b93b12018-03-02 23:26:58 -0800109
Parker Schuhdc682952018-03-03 18:24:01 -0800110 Args:
111 alpha: double, Ratio. Needs to be in [0, 1] and is the weight to blend a
112 and b.
113 """
114 return b * alpha + (1.0 - alpha) * a
115
116
Parker Schuh19b93b12018-03-02 23:26:58 -0800117def normalize(v):
Parker Schuhdc682952018-03-03 18:24:01 -0800118 """Normalize a vector while handling 0 length vectors."""
119 norm = numpy.linalg.norm(v)
120 if norm == 0:
121 return v
122 return v / norm
123
Parker Schuh19b93b12018-03-02 23:26:58 -0800124
125# CI is circular index and allows selecting between all the stats that map
126# to the same x-y state (by giving them an integer index).
127# This will compute approximate first and second derivatives with respect
128# to path length.
Parker Schuhdc682952018-03-03 18:24:01 -0800129def to_theta_with_circular_index_and_derivs(x, y, dx, dy,
130 circular_index_select):
131 a = to_theta_with_circular_index(x, y, circular_index_select)
132 b = to_theta_with_circular_index(x + dx * 0.0001, y + dy * 0.0001,
133 circular_index_select)
134 c = to_theta_with_circular_index(x - dx * 0.0001, y - dy * 0.0001,
135 circular_index_select)
136 d1 = normalize(b - a)
137 d2 = normalize(c - a)
138 accel = (d1 + d2) / numpy.linalg.norm(a - b)
139 return (a[0], a[1], d1[0], d1[1], accel[0], accel[1])
140
141
142def to_theta_with_ci_and_derivs(p_prev, p, p_next, c_i_select):
143 a = to_theta(p, c_i_select)
144 b = to_theta(p_next, c_i_select)
145 c = to_theta(p_prev, c_i_select)
146 d1 = normalize(b - a)
147 d2 = normalize(c - a)
148 accel = (d1 + d2) / numpy.linalg.norm(a - b)
149 return (a[0], a[1], d1[0], d1[1], accel[0], accel[1])
150
Parker Schuh19b93b12018-03-02 23:26:58 -0800151
152# Generic subdivision algorithm.
153def subdivide(p1, p2, max_dist):
Parker Schuhdc682952018-03-03 18:24:01 -0800154 dx = p2[0] - p1[0]
155 dy = p2[1] - p1[1]
156 dist = numpy.sqrt(dx**2 + dy**2)
157 n = int(numpy.ceil(dist / max_dist))
158 return [(alpha_blend(p1[0], p2[0],
159 float(i) / n), alpha_blend(p1[1], p2[1],
160 float(i) / n))
161 for i in range(1, n + 1)]
Parker Schuh19b93b12018-03-02 23:26:58 -0800162
Parker Schuh19b93b12018-03-02 23:26:58 -0800163
164# convert from an xy space loop into a theta loop.
165# All segements are expected go from one "hyper-extension" boundary
166# to another, thus we must go backwards over the "loop" to get a loop in
167# x-y space.
Parker Schuhdc682952018-03-03 18:24:01 -0800168def to_theta_loop(lines, cross_point=-numpy.pi):
169 out = []
170 last_pt = lines[0]
171 for n_pt in lines[1:]:
172 for pt in subdivide(last_pt, n_pt, max_dist):
173 out.append(to_theta(pt, 0, cross_point))
174 last_pt = n_pt
175 for n_pt in reversed(lines[:-1]):
176 for pt in subdivide(last_pt, n_pt, max_dist):
177 out.append(to_theta(pt, 1, cross_point))
178 last_pt = n_pt
179 return out
180
Parker Schuh19b93b12018-03-02 23:26:58 -0800181
182# Convert a loop (list of line segments) into
183# The name incorrectly suggests that it is cyclic.
184def back_to_xy_loop(lines):
Parker Schuhdc682952018-03-03 18:24:01 -0800185 out = []
186 last_pt = lines[0]
187 out.append(to_xy(last_pt[0], last_pt[1]))
188 for n_pt in lines[1:]:
189 for pt in subdivide(last_pt, n_pt, max_dist_theta):
190 out.append(to_xy(pt[0], pt[1]))
191 last_pt = n_pt
Parker Schuh19b93b12018-03-02 23:26:58 -0800192
Parker Schuhdc682952018-03-03 18:24:01 -0800193 return out
Parker Schuh19b93b12018-03-02 23:26:58 -0800194
Parker Schuh19b93b12018-03-02 23:26:58 -0800195
196# Segment in angle space.
197class AngleSegment:
Parker Schuhdc682952018-03-03 18:24:01 -0800198 def __init__(self, start, end, name=None):
199 """Creates an angle segment.
Parker Schuh19b93b12018-03-02 23:26:58 -0800200
Parker Schuhdc682952018-03-03 18:24:01 -0800201 Args:
202 start: (double, double), The start of the segment in theta1, theta2
203 coordinates in radians
204 end: (double, double), The end of the segment in theta1, theta2
205 coordinates in radians
206 """
207 self.start = start
208 self.end = end
209 self.name = name
Parker Schuh19b93b12018-03-02 23:26:58 -0800210
Parker Schuhdc682952018-03-03 18:24:01 -0800211 def __repr__(self):
212 return "AngleSegment(%s, %s)" % (repr(self.start), repr(self.end))
Parker Schuh19b93b12018-03-02 23:26:58 -0800213
Parker Schuhdc682952018-03-03 18:24:01 -0800214 def DrawTo(self, cr, theta_version):
215 if theta_version:
216 cr.move_to(self.start[0], self.start[1] + theta_end_circle_size)
217 cr.arc(self.start[0], self.start[1], theta_end_circle_size, 0,
218 2.0 * numpy.pi)
219 cr.move_to(self.end[0], self.end[1] + theta_end_circle_size)
220 cr.arc(self.end[0], self.end[1], theta_end_circle_size, 0,
221 2.0 * numpy.pi)
222 cr.move_to(self.start[0], self.start[1])
223 cr.line_to(self.end[0], self.end[1])
224 else:
225 start_xy = to_xy(self.start[0], self.start[1])
226 end_xy = to_xy(self.end[0], self.end[1])
227 draw_lines(cr, back_to_xy_loop([self.start, self.end]))
228 cr.move_to(start_xy[0] + xy_end_circle_size, start_xy[1])
229 cr.arc(start_xy[0], start_xy[1], xy_end_circle_size, 0,
230 2.0 * numpy.pi)
231 cr.move_to(end_xy[0] + xy_end_circle_size, end_xy[1])
232 cr.arc(end_xy[0], end_xy[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
233
234 def ToThetaPoints(self):
235 dx = self.end[0] - self.start[0]
236 dy = self.end[1] - self.start[1]
237 mag = numpy.hypot(dx, dy)
238 dx /= mag
239 dy /= mag
240
241 return [(self.start[0], self.start[1], dx, dy, 0.0, 0.0),
242 (self.end[0], self.end[1], dx, dy, 0.0, 0.0)]
243
244
Parker Schuh19b93b12018-03-02 23:26:58 -0800245class XYSegment:
Parker Schuhdc682952018-03-03 18:24:01 -0800246 """Straight line in XY space."""
Parker Schuh19b93b12018-03-02 23:26:58 -0800247
Parker Schuhdc682952018-03-03 18:24:01 -0800248 def __init__(self, start, end, name=None):
249 """Creates an XY segment.
Parker Schuh19b93b12018-03-02 23:26:58 -0800250
Parker Schuhdc682952018-03-03 18:24:01 -0800251 Args:
252 start: (double, double), The start of the segment in theta1, theta2
253 coordinates in radians
254 end: (double, double), The end of the segment in theta1, theta2
255 coordinates in radians
256 """
257 self.start = start
258 self.end = end
259 self.name = name
Parker Schuh19b93b12018-03-02 23:26:58 -0800260
Parker Schuhdc682952018-03-03 18:24:01 -0800261 def __repr__(self):
262 return "XYSegment(%s, %s)" % (repr(self.start), repr(self.end))
Parker Schuh19b93b12018-03-02 23:26:58 -0800263
Parker Schuhdc682952018-03-03 18:24:01 -0800264 def DrawTo(self, cr, theta_version):
265 if theta_version:
266 theta1, theta2 = self.start
267 circular_index_select = int(
268 numpy.floor((self.start[1] - self.start[0]) / numpy.pi))
269 start = get_xy(self.start)
270 end = get_xy(self.end)
Parker Schuh19b93b12018-03-02 23:26:58 -0800271
Parker Schuhdc682952018-03-03 18:24:01 -0800272 ln = [(start[0], start[1]), (end[0], end[1])]
273 draw_lines(cr, [
274 to_theta_with_circular_index(x, y, circular_index_select)
275 for x, y in subdivide_xy(ln)
276 ])
277 cr.move_to(self.start[0] + theta_end_circle_size, self.start[1])
278 cr.arc(self.start[0], self.start[1], theta_end_circle_size, 0,
279 2.0 * numpy.pi)
280 cr.move_to(self.end[0] + theta_end_circle_size, self.end[1])
281 cr.arc(self.end[0], self.end[1], theta_end_circle_size, 0,
282 2.0 * numpy.pi)
283 else:
284 start = get_xy(self.start)
285 end = get_xy(self.end)
286 cr.move_to(start[0], start[1])
287 cr.line_to(end[0], end[1])
288 cr.move_to(start[0] + xy_end_circle_size, start[1])
289 cr.arc(start[0], start[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
290 cr.move_to(end[0] + xy_end_circle_size, end[1])
291 cr.arc(end[0], end[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
Parker Schuh19b93b12018-03-02 23:26:58 -0800292
Parker Schuhdc682952018-03-03 18:24:01 -0800293 def ToThetaPoints(self):
294 """ Converts to points in theta space via to_theta_with_circular_index_and_derivs"""
295 theta1, theta2 = self.start
296 circular_index_select = int(
297 numpy.floor((self.start[1] - self.start[0]) / numpy.pi))
298 start = get_xy(self.start)
299 end = get_xy(self.end)
300
301 ln = [(start[0], start[1]), (end[0], end[1])]
302
303 dx = end[0] - start[0]
304 dy = end[1] - start[1]
305 mag = numpy.hypot(dx, dy)
306 dx /= mag
307 dy /= mag
308
309 return [
310 to_theta_with_circular_index_and_derivs(x, y, dx, dy,
311 circular_index_select)
312 for x, y in subdivide_xy(ln, 0.01)
313 ]
314
315
316def spline_eval(start, control1, control2, end, alpha):
317 a = alpha_blend(start, control1, alpha)
318 b = alpha_blend(control1, control2, alpha)
319 c = alpha_blend(control2, end, alpha)
320 return alpha_blend(
321 alpha_blend(a, b, alpha), alpha_blend(b, c, alpha), alpha)
322
323
324def subdivide_spline(start, control1, control2, end):
325 # TODO: pick N based on spline parameters? or otherwise change it to be more evenly spaced?
326 n = 100
327 for i in range(0, n + 1):
328 yield i / float(n)
329
330
331class SplineSegment:
332 def __init__(self, start, control1, control2, end, name=None):
333 self.start = start
334 self.control1 = control1
335 self.control2 = control2
336 self.end = end
337 self.name = name
338
339 def __repr__(self):
340 return "XYSegment(%s, %s, &s, %s)" % (repr(self.start),
341 repr(self.control1),
342 repr(self.control2),
343 repr(self.end))
344
345 def DrawTo(self, cr, theta_version):
346 if (theta_version):
347 c_i_select = get_circular_index(self.start)
348 start = get_xy(self.start)
349 control1 = get_xy(self.control1)
350 control2 = get_xy(self.control2)
351 end = get_xy(self.end)
352
353 draw_lines(cr, [
354 to_theta(
355 spline_eval(start, control1, control2, end, alpha),
356 c_i_select)
357 for alpha in subdivide_spline(start, control1, control2, end)
358 ])
359 else:
360 start = get_xy(self.start)
361 control1 = get_xy(self.control1)
362 control2 = get_xy(self.control2)
363 end = get_xy(self.end)
364 #cr.move_to(start[0], start[1])
365 draw_lines(cr, [
366 spline_eval(start, control1, control2, end, alpha)
367 for alpha in subdivide_spline(start, control1, control2, end)
368 ])
369 # cr.spline_to(control1[0], control1[1], control2[0], control2[1], end[0], end[1])
370
371 def ToThetaPoints(self):
372 t1, t2 = self.start
373 c_i_select = get_circular_index(self.start)
374 start = get_xy(self.start)
375 control1 = get_xy(self.control1)
376 control2 = get_xy(self.control2)
377 end = get_xy(self.end)
378
379 return [
380 to_theta_with_ci_and_derivs(
381 spline_eval(start, control1, control2, end, alpha - 0.00001),
382 spline_eval(start, control1, control2, end, alpha),
383 spline_eval(start, control1, control2, end, alpha + 0.00001),
384 c_i_select)
385 for alpha in subdivide_spline(start, control1, control2, end)
386 ]
387
388
389tall_box_x = 0.401
390tall_box_y = 0.13
391
392short_box_x = 0.431
393short_box_y = 0.082
394
395ready_above_box = to_theta_with_circular_index(
396 tall_box_x, tall_box_y + 0.08, circular_index=-1)
397tall_box_grab = to_theta_with_circular_index(
398 tall_box_x, tall_box_y, circular_index=-1)
399short_box_grab = to_theta_with_circular_index(
400 short_box_x, short_box_y, circular_index=-1)
401
402# TODO(austin): Drive the front/back off the same numbers a bit better.
403front_high_box = to_theta_with_circular_index(0.378, 2.46, circular_index=-1)
404front_middle2_box = to_theta_with_circular_index(
405 0.732, 2.268, circular_index=-1)
406front_middle1_box = to_theta_with_circular_index(
407 0.878, 1.885, circular_index=-1)
408front_low_box = to_theta_with_circular_index(0.926, 1.522, circular_index=-1)
409back_high_box = to_theta_with_circular_index(-0.75, 2.48, circular_index=0)
410back_middle2_box = to_theta_with_circular_index(
411 -0.732, 2.268, circular_index=0)
412back_middle1_box = to_theta_with_circular_index(
413 -0.878, 1.885, circular_index=0)
414back_low_box = to_theta_with_circular_index(-0.926, 1.522, circular_index=0)
415
416front_switch = to_theta_with_circular_index(0.88, 0.967, circular_index=-1)
417back_switch = to_theta_with_circular_index(-0.88, 0.967, circular_index=-2)
418
419neutral = to_theta_with_circular_index(0.0, 0.33, circular_index=-1)
420
421up = to_theta_with_circular_index(0.0, 2.547, circular_index=-1)
422
423up_c1 = to_theta((0.63, 1.17), circular_index=-1)
424up_c2 = to_theta((0.65, 1.62), circular_index=-1)
425
426front_high_box_c1 = to_theta((0.63, 1.04), circular_index=-1)
427front_high_box_c2 = to_theta((0.50, 1.60), circular_index=-1)
428
429front_middle2_box_c1 = to_theta((0.41, 0.83), circular_index=-1)
430front_middle2_box_c2 = to_theta((0.52, 1.30), circular_index=-1)
431
432front_middle1_box_c1 = to_theta((0.34, 0.82), circular_index=-1)
433front_middle1_box_c2 = to_theta((0.48, 1.15), circular_index=-1)
434
435ready_above_box_c1 = to_theta((0.38, 0.33), circular_index=-1)
436ready_above_box_c2 = to_theta((0.42, 0.51), circular_index=-1)
437
438points = [(ready_above_box, "ReadyAboveBox"),
439 (tall_box_grab, "TallBoxGrab"),
440 (short_box_grab, "ShortBoxGrab"),
441 (front_high_box, "FrontHighBox"),
442 (front_middle2_box, "FrontMiddle2Box"),
443 (front_middle1_box, "FrontMiddle1Box"),
444 (front_low_box, "FrontLowBox"),
445 (back_high_box, "BackHighBox"),
446 (back_middle2_box, "BackMiddle2Box"),
447 (back_middle1_box, "BackMiddle1Box"),
448 (back_low_box, "BackLowBox"),
449 (front_switch, "FrontSwitch"),
450 (back_switch, "BackSwitch"),
451 (neutral, "Neutral"),
452 (up, "Up")] # yapf: disable
453
454# We need to define critical points so we can create paths connecting them.
455# TODO(austin): Attach velocities to the slow ones.
456named_segments = [
457 XYSegment(ready_above_box, tall_box_grab, "ReadyToTallBox"),
458 XYSegment(ready_above_box, short_box_grab, "ReadyToShortBox"),
459 XYSegment(tall_box_grab, short_box_grab, "TallToShortBox"),
460 SplineSegment(neutral, ready_above_box_c1, ready_above_box_c2,
461 ready_above_box, "ReadyToNeutral"),
462 SplineSegment(neutral, up_c1, up_c2, up, "NeutralToUp"),
463 SplineSegment(neutral, front_high_box_c1, front_high_box_c2,
464 front_high_box, "NeutralToFrontHigh"),
465 SplineSegment(neutral, front_middle2_box_c1, front_middle2_box_c2,
Austin Schuh7dfccf62018-03-03 21:28:14 -0800466 front_middle2_box, "NeutralToFrontMiddle2"),
Parker Schuhdc682952018-03-03 18:24:01 -0800467 SplineSegment(neutral, front_middle1_box_c1, front_middle1_box_c2,
Austin Schuh7dfccf62018-03-03 21:28:14 -0800468 front_middle1_box, "NeutralToFrontMiddle1"),
Parker Schuhdc682952018-03-03 18:24:01 -0800469]
470
471unnamed_segments = [
472 AngleSegment(neutral, back_switch),
473 XYSegment(neutral, front_switch),
474
475 XYSegment(up, front_high_box),
476 XYSegment(up, front_middle2_box),
477 XYSegment(up, front_middle1_box),
478 XYSegment(up, front_low_box),
479 XYSegment(front_high_box, front_middle2_box),
480 XYSegment(front_high_box, front_middle1_box),
481 XYSegment(front_high_box, front_low_box),
482 XYSegment(front_middle2_box, front_middle1_box),
483 XYSegment(front_middle2_box, front_low_box),
484 XYSegment(front_middle1_box, front_low_box),
485 XYSegment(front_switch, front_low_box),
486 XYSegment(front_switch, up),
487 XYSegment(front_switch, front_high_box),
488 AngleSegment(up, back_high_box),
489 AngleSegment(up, back_middle2_box),
490 AngleSegment(up, back_middle1_box),
491 XYSegment(back_high_box, back_middle2_box),
492 XYSegment(back_high_box, back_middle1_box),
493 XYSegment(back_high_box, back_low_box),
494 XYSegment(back_middle2_box, back_middle1_box),
495 XYSegment(back_middle2_box, back_low_box),
496 XYSegment(back_middle1_box, back_low_box),
497]
498
499segments = named_segments + unnamed_segments