Maxwell Henderson | f5123fe | 2023-02-04 13:44:41 -0800 | [diff] [blame] | 1 | import numpy |
Maxwell Henderson | f5123fe | 2023-02-04 13:44:41 -0800 | [diff] [blame] | 2 | |
| 3 | # joint_center in x-y space. |
| 4 | joint_center = (-0.299, 0.299) |
| 5 | |
| 6 | # Joint distances (l1 = "proximal", l2 = "distal") |
| 7 | l1 = 46.25 * 0.0254 |
| 8 | l2 = 43.75 * 0.0254 |
| 9 | |
| 10 | max_dist = 0.01 |
| 11 | max_dist_theta = numpy.pi / 64 |
| 12 | xy_end_circle_size = 0.01 |
| 13 | theta_end_circle_size = 0.07 |
| 14 | |
| 15 | |
Maxwell Henderson | f5123fe | 2023-02-04 13:44:41 -0800 | [diff] [blame] | 16 | # Convert from x-y coordinates to theta coordinates. |
| 17 | # orientation is a bool. This orientation is circular_index mod 2. |
| 18 | # where circular_index is the circular index, or the position in the |
| 19 | # "hyperextension" zones. "cross_point" allows shifting the place where |
| 20 | # it rounds the result so that it draws nicer (no other functional differences). |
| 21 | def to_theta(pt, circular_index, cross_point=-numpy.pi): |
| 22 | orient = (circular_index % 2) == 0 |
| 23 | x = pt[0] |
| 24 | y = pt[1] |
| 25 | x -= joint_center[0] |
| 26 | y -= joint_center[1] |
| 27 | l3 = numpy.hypot(x, y) |
| 28 | t3 = numpy.arctan2(y, x) |
| 29 | theta1 = numpy.arccos((l1**2 + l3**2 - l2**2) / (2 * l1 * l3)) |
| 30 | |
| 31 | if orient: |
| 32 | theta1 = -theta1 |
| 33 | theta1 += t3 |
| 34 | theta1 = (theta1 - cross_point) % (2 * numpy.pi) + cross_point |
| 35 | theta2 = numpy.arctan2(y - l1 * numpy.sin(theta1), |
| 36 | x - l1 * numpy.cos(theta1)) |
| 37 | return numpy.array((theta1, theta2)) |
| 38 | |
| 39 | |
| 40 | # Simple trig to go back from theta1, theta2 to x-y |
| 41 | def to_xy(theta1, theta2): |
| 42 | x = numpy.cos(theta1) * l1 + numpy.cos(theta2) * l2 + joint_center[0] |
| 43 | y = numpy.sin(theta1) * l1 + numpy.sin(theta2) * l2 + joint_center[1] |
| 44 | orient = ((theta2 - theta1) % (2.0 * numpy.pi)) < numpy.pi |
| 45 | return (x, y, orient) |
| 46 | |
| 47 | |
| 48 | def get_circular_index(theta): |
| 49 | return int(numpy.floor((theta[1] - theta[0]) / numpy.pi)) |
| 50 | |
| 51 | |
| 52 | def get_xy(theta): |
| 53 | theta1 = theta[0] |
| 54 | theta2 = theta[1] |
| 55 | x = numpy.cos(theta1) * l1 + numpy.cos(theta2) * l2 + joint_center[0] |
| 56 | y = numpy.sin(theta1) * l1 + numpy.sin(theta2) * l2 + joint_center[1] |
| 57 | return numpy.array((x, y)) |
| 58 | |
| 59 | |
| 60 | # Subdivide in theta space. |
| 61 | def subdivide_theta(lines): |
| 62 | out = [] |
| 63 | last_pt = lines[0] |
| 64 | out.append(last_pt) |
| 65 | for n_pt in lines[1:]: |
| 66 | for pt in subdivide(last_pt, n_pt, max_dist_theta): |
| 67 | out.append(pt) |
| 68 | last_pt = n_pt |
| 69 | |
| 70 | return out |
| 71 | |
| 72 | |
| 73 | # subdivide in xy space. |
| 74 | def subdivide_xy(lines, max_dist=max_dist): |
| 75 | out = [] |
| 76 | last_pt = lines[0] |
| 77 | out.append(last_pt) |
| 78 | for n_pt in lines[1:]: |
| 79 | for pt in subdivide(last_pt, n_pt, max_dist): |
| 80 | out.append(pt) |
| 81 | last_pt = n_pt |
| 82 | |
| 83 | return out |
| 84 | |
| 85 | |
| 86 | def to_theta_with_ci(pt, circular_index): |
| 87 | return to_theta_with_circular_index(pt[0], pt[1], circular_index) |
| 88 | |
| 89 | |
| 90 | # to_theta, but distinguishes between |
| 91 | def to_theta_with_circular_index(x, y, circular_index): |
| 92 | theta1, theta2 = to_theta((x, y), circular_index) |
| 93 | n_circular_index = int(numpy.floor((theta2 - theta1) / numpy.pi)) |
| 94 | theta2 = theta2 + ((circular_index - n_circular_index)) * numpy.pi |
| 95 | return numpy.array((theta1, theta2)) |
| 96 | |
| 97 | |
| 98 | # alpha is in [0, 1] and is the weight to merge a and b. |
| 99 | def alpha_blend(a, b, alpha): |
| 100 | """Blends a and b. |
| 101 | |
| 102 | Args: |
| 103 | alpha: double, Ratio. Needs to be in [0, 1] and is the weight to blend a |
| 104 | and b. |
| 105 | """ |
| 106 | return b * alpha + (1.0 - alpha) * a |
| 107 | |
| 108 | |
| 109 | def normalize(v): |
| 110 | """Normalize a vector while handling 0 length vectors.""" |
| 111 | norm = numpy.linalg.norm(v) |
| 112 | if norm == 0: |
| 113 | return v |
| 114 | return v / norm |
| 115 | |
| 116 | |
| 117 | # CI is circular index and allows selecting between all the stats that map |
| 118 | # to the same x-y state (by giving them an integer index). |
| 119 | # This will compute approximate first and second derivatives with respect |
| 120 | # to path length. |
| 121 | def to_theta_with_circular_index_and_derivs(x, y, dx, dy, |
| 122 | circular_index_select): |
| 123 | a = to_theta_with_circular_index(x, y, circular_index_select) |
| 124 | b = to_theta_with_circular_index(x + dx * 0.0001, y + dy * 0.0001, |
| 125 | circular_index_select) |
| 126 | c = to_theta_with_circular_index(x - dx * 0.0001, y - dy * 0.0001, |
| 127 | circular_index_select) |
| 128 | d1 = normalize(b - a) |
| 129 | d2 = normalize(c - a) |
| 130 | accel = (d1 + d2) / numpy.linalg.norm(a - b) |
| 131 | return (a[0], a[1], d1[0], d1[1], accel[0], accel[1]) |
| 132 | |
| 133 | |
| 134 | def to_theta_with_ci_and_derivs(p_prev, p, p_next, c_i_select): |
| 135 | a = to_theta(p, c_i_select) |
| 136 | b = to_theta(p_next, c_i_select) |
| 137 | c = to_theta(p_prev, c_i_select) |
| 138 | d1 = normalize(b - a) |
| 139 | d2 = normalize(c - a) |
| 140 | accel = (d1 + d2) / numpy.linalg.norm(a - b) |
| 141 | return (a[0], a[1], d1[0], d1[1], accel[0], accel[1]) |
| 142 | |
| 143 | |
| 144 | # Generic subdivision algorithm. |
| 145 | def subdivide(p1, p2, max_dist): |
| 146 | dx = p2[0] - p1[0] |
| 147 | dy = p2[1] - p1[1] |
| 148 | dist = numpy.sqrt(dx**2 + dy**2) |
| 149 | n = int(numpy.ceil(dist / max_dist)) |
| 150 | return [(alpha_blend(p1[0], p2[0], |
| 151 | float(i) / n), alpha_blend(p1[1], p2[1], |
| 152 | float(i) / n)) |
| 153 | for i in range(1, n + 1)] |
| 154 | |
| 155 | |
| 156 | # convert from an xy space loop into a theta loop. |
| 157 | # All segements are expected go from one "hyper-extension" boundary |
| 158 | # to another, thus we must go backwards over the "loop" to get a loop in |
| 159 | # x-y space. |
| 160 | def to_theta_loop(lines, cross_point=-numpy.pi): |
| 161 | out = [] |
| 162 | last_pt = lines[0] |
| 163 | for n_pt in lines[1:]: |
| 164 | for pt in subdivide(last_pt, n_pt, max_dist): |
| 165 | out.append(to_theta(pt, 0, cross_point)) |
| 166 | last_pt = n_pt |
| 167 | for n_pt in reversed(lines[:-1]): |
| 168 | for pt in subdivide(last_pt, n_pt, max_dist): |
| 169 | out.append(to_theta(pt, 1, cross_point)) |
| 170 | last_pt = n_pt |
| 171 | return out |
| 172 | |
| 173 | |
| 174 | # Convert a loop (list of line segments) into |
| 175 | # The name incorrectly suggests that it is cyclic. |
| 176 | def back_to_xy_loop(lines): |
| 177 | out = [] |
| 178 | last_pt = lines[0] |
| 179 | out.append(to_xy(last_pt[0], last_pt[1])) |
| 180 | for n_pt in lines[1:]: |
| 181 | for pt in subdivide(last_pt, n_pt, max_dist_theta): |
| 182 | out.append(to_xy(pt[0], pt[1])) |
| 183 | last_pt = n_pt |
| 184 | |
| 185 | return out |
| 186 | |
| 187 | |
| 188 | def spline_eval(start, control1, control2, end, alpha): |
| 189 | a = alpha_blend(start, control1, alpha) |
| 190 | b = alpha_blend(control1, control2, alpha) |
| 191 | c = alpha_blend(control2, end, alpha) |
| 192 | return alpha_blend(alpha_blend(a, b, alpha), alpha_blend(b, c, alpha), |
| 193 | alpha) |
| 194 | |
| 195 | |
| 196 | def subdivide_spline(start, control1, control2, end): |
| 197 | # TODO: pick N based on spline parameters? or otherwise change it to be more evenly spaced? |
| 198 | n = 100 |
| 199 | for i in range(0, n + 1): |
| 200 | yield i / float(n) |
| 201 | |
| 202 | |
| 203 | def get_derivs(t_prev, t, t_next): |
| 204 | c, a, b = t_prev, t, t_next |
| 205 | d1 = normalize(b - a) |
| 206 | d2 = normalize(c - a) |
| 207 | accel = (d1 + d2) / numpy.linalg.norm(a - b) |
| 208 | return (a[0], a[1], d1[0], d1[1], accel[0], accel[1]) |
| 209 | |
| 210 | |
Maxwell Henderson | 83cf6d6 | 2023-02-10 20:29:26 -0800 | [diff] [blame^] | 211 | # Draw a list of lines to a cairo context. |
Maxwell Henderson | f5123fe | 2023-02-04 13:44:41 -0800 | [diff] [blame] | 212 | def draw_lines(cr, lines): |
| 213 | cr.move_to(lines[0][0], lines[0][1]) |
| 214 | for pt in lines[1:]: |
| 215 | cr.line_to(pt[0], pt[1]) |
Maxwell Henderson | f5123fe | 2023-02-04 13:44:41 -0800 | [diff] [blame] | 216 | |
| 217 | |
| 218 | # Segment in angle space. |
| 219 | class AngleSegment: |
| 220 | |
| 221 | def __init__(self, start, end, name=None, alpha_unitizer=None, vmax=None): |
| 222 | """Creates an angle segment. |
| 223 | |
| 224 | Args: |
| 225 | start: (double, double), The start of the segment in theta1, theta2 |
| 226 | coordinates in radians |
| 227 | end: (double, double), The end of the segment in theta1, theta2 |
| 228 | coordinates in radians |
| 229 | """ |
| 230 | self.start = start |
| 231 | self.end = end |
| 232 | self.name = name |
| 233 | self.alpha_unitizer = alpha_unitizer |
| 234 | self.vmax = vmax |
| 235 | |
| 236 | def __repr__(self): |
| 237 | return "AngleSegment(%s, %s)" % (repr(self.start), repr(self.end)) |
| 238 | |
| 239 | def DrawTo(self, cr, theta_version): |
| 240 | if theta_version: |
| 241 | cr.move_to(self.start[0], self.start[1] + theta_end_circle_size) |
| 242 | cr.arc(self.start[0], self.start[1], theta_end_circle_size, 0, |
| 243 | 2.0 * numpy.pi) |
| 244 | cr.move_to(self.end[0], self.end[1] + theta_end_circle_size) |
| 245 | cr.arc(self.end[0], self.end[1], theta_end_circle_size, 0, |
| 246 | 2.0 * numpy.pi) |
| 247 | cr.move_to(self.start[0], self.start[1]) |
| 248 | cr.line_to(self.end[0], self.end[1]) |
| 249 | else: |
| 250 | start_xy = to_xy(self.start[0], self.start[1]) |
| 251 | end_xy = to_xy(self.end[0], self.end[1]) |
| 252 | draw_lines(cr, back_to_xy_loop([self.start, self.end])) |
| 253 | cr.move_to(start_xy[0] + xy_end_circle_size, start_xy[1]) |
| 254 | cr.arc(start_xy[0], start_xy[1], xy_end_circle_size, 0, |
| 255 | 2.0 * numpy.pi) |
| 256 | cr.move_to(end_xy[0] + xy_end_circle_size, end_xy[1]) |
| 257 | cr.arc(end_xy[0], end_xy[1], xy_end_circle_size, 0, 2.0 * numpy.pi) |
| 258 | |
| 259 | def ToThetaPoints(self): |
| 260 | dx = self.end[0] - self.start[0] |
| 261 | dy = self.end[1] - self.start[1] |
| 262 | mag = numpy.hypot(dx, dy) |
| 263 | dx /= mag |
| 264 | dy /= mag |
| 265 | |
| 266 | return [(self.start[0], self.start[1], dx, dy, 0.0, 0.0), |
| 267 | (self.end[0], self.end[1], dx, dy, 0.0, 0.0)] |
| 268 | |
| 269 | |
| 270 | class XYSegment: |
| 271 | """Straight line in XY space.""" |
| 272 | |
| 273 | def __init__(self, start, end, name=None, alpha_unitizer=None, vmax=None): |
| 274 | """Creates an XY segment. |
| 275 | |
| 276 | Args: |
| 277 | start: (double, double), The start of the segment in theta1, theta2 |
| 278 | coordinates in radians |
| 279 | end: (double, double), The end of the segment in theta1, theta2 |
| 280 | coordinates in radians |
| 281 | """ |
| 282 | self.start = start |
| 283 | self.end = end |
| 284 | self.name = name |
| 285 | self.alpha_unitizer = alpha_unitizer |
| 286 | self.vmax = vmax |
| 287 | |
| 288 | def __repr__(self): |
| 289 | return "XYSegment(%s, %s)" % (repr(self.start), repr(self.end)) |
| 290 | |
| 291 | def DrawTo(self, cr, theta_version): |
| 292 | if theta_version: |
| 293 | theta1, theta2 = self.start |
| 294 | circular_index_select = int( |
| 295 | numpy.floor((self.start[1] - self.start[0]) / numpy.pi)) |
| 296 | start = get_xy(self.start) |
| 297 | end = get_xy(self.end) |
| 298 | |
| 299 | ln = [(start[0], start[1]), (end[0], end[1])] |
| 300 | draw_lines(cr, [ |
| 301 | to_theta_with_circular_index(x, y, circular_index_select) |
| 302 | for x, y in subdivide_xy(ln) |
| 303 | ]) |
| 304 | cr.move_to(self.start[0] + theta_end_circle_size, self.start[1]) |
| 305 | cr.arc(self.start[0], self.start[1], theta_end_circle_size, 0, |
| 306 | 2.0 * numpy.pi) |
| 307 | cr.move_to(self.end[0] + theta_end_circle_size, self.end[1]) |
| 308 | cr.arc(self.end[0], self.end[1], theta_end_circle_size, 0, |
| 309 | 2.0 * numpy.pi) |
| 310 | else: |
| 311 | start = get_xy(self.start) |
| 312 | end = get_xy(self.end) |
| 313 | cr.move_to(start[0], start[1]) |
| 314 | cr.line_to(end[0], end[1]) |
| 315 | cr.move_to(start[0] + xy_end_circle_size, start[1]) |
| 316 | cr.arc(start[0], start[1], xy_end_circle_size, 0, 2.0 * numpy.pi) |
| 317 | cr.move_to(end[0] + xy_end_circle_size, end[1]) |
| 318 | cr.arc(end[0], end[1], xy_end_circle_size, 0, 2.0 * numpy.pi) |
| 319 | |
| 320 | def ToThetaPoints(self): |
| 321 | """ Converts to points in theta space via to_theta_with_circular_index_and_derivs""" |
| 322 | theta1, theta2 = self.start |
| 323 | circular_index_select = int( |
| 324 | numpy.floor((self.start[1] - self.start[0]) / numpy.pi)) |
| 325 | start = get_xy(self.start) |
| 326 | end = get_xy(self.end) |
| 327 | |
| 328 | ln = [(start[0], start[1]), (end[0], end[1])] |
| 329 | |
| 330 | dx = end[0] - start[0] |
| 331 | dy = end[1] - start[1] |
| 332 | mag = numpy.hypot(dx, dy) |
| 333 | dx /= mag |
| 334 | dy /= mag |
| 335 | |
| 336 | return [ |
| 337 | to_theta_with_circular_index_and_derivs(x, y, dx, dy, |
| 338 | circular_index_select) |
| 339 | for x, y in subdivide_xy(ln, 0.01) |
| 340 | ] |
| 341 | |
| 342 | |
| 343 | class SplineSegment: |
| 344 | |
| 345 | def __init__(self, |
| 346 | start, |
| 347 | control1, |
| 348 | control2, |
| 349 | end, |
| 350 | name=None, |
| 351 | alpha_unitizer=None, |
| 352 | vmax=None): |
| 353 | self.start = start |
| 354 | self.control1 = control1 |
| 355 | self.control2 = control2 |
| 356 | self.end = end |
| 357 | self.name = name |
| 358 | self.alpha_unitizer = alpha_unitizer |
| 359 | self.vmax = vmax |
| 360 | |
| 361 | def __repr__(self): |
| 362 | return "SplineSegment(%s, %s, %s, %s)" % (repr( |
| 363 | self.start), repr(self.control1), repr( |
| 364 | self.control2), repr(self.end)) |
| 365 | |
| 366 | def DrawTo(self, cr, theta_version): |
| 367 | if theta_version: |
| 368 | c_i_select = get_circular_index(self.start) |
| 369 | start = get_xy(self.start) |
| 370 | control1 = get_xy(self.control1) |
| 371 | control2 = get_xy(self.control2) |
| 372 | end = get_xy(self.end) |
| 373 | |
| 374 | draw_lines(cr, [ |
| 375 | to_theta(spline_eval(start, control1, control2, end, alpha), |
| 376 | c_i_select) |
| 377 | for alpha in subdivide_spline(start, control1, control2, end) |
| 378 | ]) |
| 379 | cr.move_to(self.start[0] + theta_end_circle_size, self.start[1]) |
| 380 | cr.arc(self.start[0], self.start[1], theta_end_circle_size, 0, |
| 381 | 2.0 * numpy.pi) |
| 382 | cr.move_to(self.end[0] + theta_end_circle_size, self.end[1]) |
| 383 | cr.arc(self.end[0], self.end[1], theta_end_circle_size, 0, |
| 384 | 2.0 * numpy.pi) |
| 385 | else: |
| 386 | start = get_xy(self.start) |
| 387 | control1 = get_xy(self.control1) |
| 388 | control2 = get_xy(self.control2) |
| 389 | end = get_xy(self.end) |
| 390 | |
| 391 | draw_lines(cr, [ |
| 392 | spline_eval(start, control1, control2, end, alpha) |
| 393 | for alpha in subdivide_spline(start, control1, control2, end) |
| 394 | ]) |
| 395 | |
| 396 | cr.move_to(start[0] + xy_end_circle_size, start[1]) |
| 397 | cr.arc(start[0], start[1], xy_end_circle_size, 0, 2.0 * numpy.pi) |
| 398 | cr.move_to(end[0] + xy_end_circle_size, end[1]) |
| 399 | cr.arc(end[0], end[1], xy_end_circle_size, 0, 2.0 * numpy.pi) |
| 400 | |
| 401 | def ToThetaPoints(self): |
| 402 | t1, t2 = self.start |
| 403 | c_i_select = get_circular_index(self.start) |
| 404 | start = get_xy(self.start) |
| 405 | control1 = get_xy(self.control1) |
| 406 | control2 = get_xy(self.control2) |
| 407 | end = get_xy(self.end) |
| 408 | |
| 409 | return [ |
| 410 | to_theta_with_ci_and_derivs( |
| 411 | spline_eval(start, control1, control2, end, alpha - 0.00001), |
| 412 | spline_eval(start, control1, control2, end, alpha), |
| 413 | spline_eval(start, control1, control2, end, alpha + 0.00001), |
| 414 | c_i_select) |
| 415 | for alpha in subdivide_spline(start, control1, control2, end) |
| 416 | ] |
| 417 | |
| 418 | |
| 419 | class ThetaSplineSegment: |
| 420 | |
| 421 | def __init__(self, |
| 422 | start, |
| 423 | control1, |
| 424 | control2, |
| 425 | end, |
| 426 | name=None, |
| 427 | alpha_unitizer=None, |
| 428 | vmax=None): |
| 429 | self.start = start |
| 430 | self.control1 = control1 |
| 431 | self.control2 = control2 |
| 432 | self.end = end |
| 433 | self.name = name |
| 434 | self.alpha_unitizer = alpha_unitizer |
| 435 | self.vmax = vmax |
| 436 | |
| 437 | def __repr__(self): |
| 438 | return "ThetaSplineSegment(%s, %s, &s, %s)" % (repr( |
| 439 | self.start), repr(self.control1), repr( |
| 440 | self.control2), repr(self.end)) |
| 441 | |
| 442 | def DrawTo(self, cr, theta_version): |
| 443 | if (theta_version): |
| 444 | draw_lines(cr, [ |
| 445 | spline_eval(self.start, self.control1, self.control2, self.end, |
| 446 | alpha) |
| 447 | for alpha in subdivide_spline(self.start, self.control1, |
| 448 | self.control2, self.end) |
| 449 | ]) |
| 450 | else: |
| 451 | start = get_xy(self.start) |
| 452 | end = get_xy(self.end) |
| 453 | |
| 454 | draw_lines(cr, [ |
| 455 | get_xy( |
| 456 | spline_eval(self.start, self.control1, self.control2, |
| 457 | self.end, alpha)) |
| 458 | for alpha in subdivide_spline(self.start, self.control1, |
| 459 | self.control2, self.end) |
| 460 | ]) |
| 461 | |
| 462 | cr.move_to(start[0] + xy_end_circle_size, start[1]) |
| 463 | cr.arc(start[0], start[1], xy_end_circle_size, 0, 2.0 * numpy.pi) |
| 464 | cr.move_to(end[0] + xy_end_circle_size, end[1]) |
| 465 | cr.arc(end[0], end[1], xy_end_circle_size, 0, 2.0 * numpy.pi) |
| 466 | |
| 467 | def ToThetaPoints(self): |
| 468 | return [ |
| 469 | get_derivs( |
| 470 | spline_eval(self.start, self.control1, self.control2, self.end, |
| 471 | alpha - 0.00001), |
| 472 | spline_eval(self.start, self.control1, self.control2, self.end, |
| 473 | alpha), |
| 474 | spline_eval(self.start, self.control1, self.control2, self.end, |
| 475 | alpha + 0.00001)) |
| 476 | for alpha in subdivide_spline(self.start, self.control1, |
| 477 | self.control2, self.end) |
| 478 | ] |
| 479 | |
| 480 | |
| 481 | def expand_points(points, max_distance): |
| 482 | """Expands a list of points to be at most max_distance apart |
| 483 | |
| 484 | Generates the paths to connect the new points to the closest input points, |
| 485 | and the paths connecting the points. |
| 486 | |
| 487 | Args: |
| 488 | points, list of tuple of point, name, The points to start with and fill |
| 489 | in. |
| 490 | max_distance, float, The max distance between two points when expanding |
| 491 | the graph. |
| 492 | |
| 493 | Return: |
| 494 | points, edges |
| 495 | """ |
| 496 | result_points = [points[0]] |
| 497 | result_paths = [] |
| 498 | for point, name in points[1:]: |
| 499 | previous_point = result_points[-1][0] |
| 500 | previous_point_xy = get_xy(previous_point) |
| 501 | circular_index = get_circular_index(previous_point) |
| 502 | |
| 503 | point_xy = get_xy(point) |
| 504 | norm = numpy.linalg.norm(point_xy - previous_point_xy) |
| 505 | num_points = int(numpy.ceil(norm / max_distance)) |
| 506 | last_iteration_point = previous_point |
| 507 | for subindex in range(1, num_points): |
| 508 | subpoint = to_theta(alpha_blend(previous_point_xy, point_xy, |
| 509 | float(subindex) / num_points), |
| 510 | circular_index=circular_index) |
| 511 | result_points.append( |
| 512 | (subpoint, '%s%dof%d' % (name, subindex, num_points))) |
| 513 | result_paths.append( |
| 514 | XYSegment(last_iteration_point, subpoint, vmax=6.0)) |
| 515 | if (last_iteration_point != previous_point).any(): |
| 516 | result_paths.append(XYSegment(previous_point, subpoint)) |
| 517 | if subindex == num_points - 1: |
| 518 | result_paths.append(XYSegment(subpoint, point, vmax=6.0)) |
| 519 | else: |
| 520 | result_paths.append(XYSegment(subpoint, point)) |
| 521 | last_iteration_point = subpoint |
| 522 | result_points.append((point, name)) |
| 523 | |
| 524 | return result_points, result_paths |