printrun-src/printrun/gl/libtatlin/actors.py

changeset 46
cce0af6351f0
parent 15
0bbb006204fc
equal deleted inserted replaced
45:c82943fb205f 46:cce0af6351f0
63 def triangulate_box(i1, i2, i3, i4, 63 def triangulate_box(i1, i2, i3, i4,
64 j1, j2, j3, j4): 64 j1, j2, j3, j4):
65 return [i1, i2, j2, j2, j1, i1, i2, i3, j3, j3, j2, i2, 65 return [i1, i2, j2, j2, j1, i1, i2, i3, j3, j3, j2, i2,
66 i3, i4, j4, j4, j3, i3, i4, i1, j1, j1, j4, i4] 66 i3, i4, j4, j4, j3, i3, i4, i1, j1, j1, j4, i4]
67 67
68 class BoundingBox(object): 68 class BoundingBox:
69 """ 69 """
70 A rectangular box (cuboid) enclosing a 3D model, defined by lower and upper corners. 70 A rectangular box (cuboid) enclosing a 3D model, defined by lower and upper corners.
71 """ 71 """
72 def __init__(self, upper_corner, lower_corner): 72 def __init__(self, upper_corner, lower_corner):
73 self.upper_corner = upper_corner 73 self.upper_corner = upper_corner
87 def height(self): 87 def height(self):
88 height = abs(self.upper_corner[2] - self.lower_corner[2]) 88 height = abs(self.upper_corner[2] - self.lower_corner[2])
89 return round(height, 2) 89 return round(height, 2)
90 90
91 91
92 class Platform(object): 92 class Platform:
93 """ 93 """
94 Platform on which models are placed. 94 Platform on which models are placed.
95 """ 95 """
96 graduations_major = 10 96
97 97 def __init__(self, build_dimensions, light = False, circular = False, grid = (1, 10)):
98 def __init__(self, build_dimensions, light = False, circular = False):
99 self.light = light 98 self.light = light
100 self.circular = circular 99 self.circular = circular
101 self.width = build_dimensions[0] 100 self.width = build_dimensions[0]
102 self.depth = build_dimensions[1] 101 self.depth = build_dimensions[1]
103 self.height = build_dimensions[2] 102 self.height = build_dimensions[2]
104 self.xoffset = build_dimensions[3] 103 self.xoffset = build_dimensions[3]
105 self.yoffset = build_dimensions[4] 104 self.yoffset = build_dimensions[4]
106 self.zoffset = build_dimensions[5] 105 self.zoffset = build_dimensions[5]
106 self.grid = grid
107 107
108 self.color_grads_minor = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.1) 108 self.color_grads_minor = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.1)
109 self.color_grads_interm = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.2) 109 self.color_grads_interm = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.2)
110 self.color_grads_major = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.33) 110 self.color_grads_major = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.33)
111 111
120 glPushMatrix() 120 glPushMatrix()
121 121
122 glTranslatef(self.xoffset, self.yoffset, self.zoffset) 122 glTranslatef(self.xoffset, self.yoffset, self.zoffset)
123 123
124 def color(i): 124 def color(i):
125 if i % self.graduations_major == 0: 125 if i % self.grid[1] == 0:
126 glColor4f(*self.color_grads_major) 126 glColor4f(*self.color_grads_major)
127 elif i % (self.graduations_major / 2) == 0: 127 elif i % (self.grid[1] // 2) == 0:
128 glColor4f(*self.color_grads_interm) 128 glColor4f(*self.color_grads_interm)
129 else: 129 else:
130 if self.light: return False 130 if self.light: return False
131 glColor4f(*self.color_grads_minor) 131 glColor4f(*self.color_grads_minor)
132 return True 132 return True
133 133
134 # draw the grid 134 # draw the grid
135 glBegin(GL_LINES) 135 glBegin(GL_LINES)
136 if self.circular: # Draw a circular grid 136 if self.circular: # Draw a circular grid
137 for i in range(0, int(math.ceil(self.width + 1))): 137 for i in numpy.arange(0, int(math.ceil(self.width + 1)), self.grid[0]):
138 angle = math.asin(2 * float(i) / self.width - 1) 138 angle = math.asin(2 * float(i) / self.width - 1)
139 x = (math.cos(angle) + 1) * self.depth / 2 139 x = (math.cos(angle) + 1) * self.depth / 2
140 if color(i): 140 if color(i):
141 glVertex3f(float(i), self.depth - x, 0.0) 141 glVertex3f(float(i), self.depth - x, 0.0)
142 glVertex3f(float(i), x, 0.0) 142 glVertex3f(float(i), x, 0.0)
143 143
144 for i in range(0, int(math.ceil(self.depth + 1))): 144 for i in numpy.arange(0, int(math.ceil(self.depth + 1)), self.grid[0]):
145 angle = math.acos(2 * float(i) / self.depth - 1) 145 angle = math.acos(2 * float(i) / self.depth - 1)
146 x = (math.sin(angle) + 1) * self.width / 2 146 x = (math.sin(angle) + 1) * self.width / 2
147 if color(i): 147 if color(i):
148 glVertex3f(self.width - x, float(i), 0.0) 148 glVertex3f(self.width - x, float(i), 0.0)
149 glVertex3f(x, float(i), 0.0) 149 glVertex3f(x, float(i), 0.0)
150 else: # Draw a rectangular grid 150 else: # Draw a rectangular grid
151 for i in range(0, int(math.ceil(self.width + 1))): 151 for i in numpy.arange(0, int(math.ceil(self.width + 1)), self.grid[0]):
152 if color(i): 152 if color(i):
153 glVertex3f(float(i), 0.0, 0.0) 153 glVertex3f(float(i), 0.0, 0.0)
154 glVertex3f(float(i), self.depth, 0.0) 154 glVertex3f(float(i), self.depth, 0.0)
155 155
156 for i in range(0, int(math.ceil(self.depth + 1))): 156 for i in numpy.arange(0, int(math.ceil(self.depth + 1)), self.grid[0]):
157 if color(i): 157 if color(i):
158 glVertex3f(0, float(i), 0.0) 158 glVertex3f(0, float(i), 0.0)
159 glVertex3f(self.width, float(i), 0.0) 159 glVertex3f(self.width, float(i), 0.0)
160 glEnd() 160 glEnd()
161 161
172 def display(self, mode_2d=False): 172 def display(self, mode_2d=False):
173 # FIXME: using the list sometimes results in graphical corruptions 173 # FIXME: using the list sometimes results in graphical corruptions
174 # glCallList(self.display_list) 174 # glCallList(self.display_list)
175 self.draw() 175 self.draw()
176 176
177 class PrintHead(object): 177 class PrintHead:
178 def __init__(self): 178 def __init__(self):
179 self.color = (43. / 255, 0., 175. / 255, 1.0) 179 self.color = (43. / 255, 0., 175. / 255, 1.0)
180 self.scale = 5 180 self.scale = 5
181 self.height = 5 181 self.height = 5
182 182
207 glLineWidth(3.0) 207 glLineWidth(3.0)
208 glCallList(self.display_list) 208 glCallList(self.display_list)
209 glLineWidth(orig_linewidth) 209 glLineWidth(orig_linewidth)
210 glDisable(GL_LINE_SMOOTH) 210 glDisable(GL_LINE_SMOOTH)
211 211
212 class Model(object): 212 class Model:
213 """ 213 """
214 Parent class for models that provides common functionality. 214 Parent class for models that provides common functionality.
215 """ 215 """
216 AXIS_X = (1, 0, 0) 216 AXIS_X = (1, 0, 0)
217 AXIS_Y = (0, 1, 0) 217 AXIS_Y = (0, 1, 0)
313 gline_idx += 1 313 gline_idx += 1
314 layer_idx += 1 314 layer_idx += 1
315 gline_idx = 0 315 gline_idx = 0
316 return None 316 return None
317 317
318 def interpolate_arcs(gline, prev_gline):
319 if gline.command == "G2" or gline.command == "G3":
320 rx = gline.i if gline.i is not None else 0
321 ry = gline.j if gline.j is not None else 0
322 r = math.sqrt(rx*rx + ry*ry)
323
324 cx = prev_gline.current_x + rx
325 cy = prev_gline.current_y + ry
326
327 a_start = math.atan2(-ry, -rx)
328 dx = gline.current_x - cx
329 dy = gline.current_y - cy
330 a_end = math.atan2(dy, dx)
331 a_delta = a_end - a_start
332
333 if gline.command == "G3" and a_delta <= 0:
334 a_delta += math.pi * 2
335 elif gline.command == "G2" and a_delta >= 0:
336 a_delta -= math.pi * 2
337
338 z0 = prev_gline.current_z
339 dz = gline.current_z - z0
340
341 # max segment size: 0.5mm, max num of segments: 100
342 segments = math.ceil(abs(a_delta) * r * 2 / 0.5)
343 if segments > 100:
344 segments = 100
345
346 for t in range(segments):
347 a = t / segments * a_delta + a_start
348
349 mid = (
350 cx + math.cos(a) * r,
351 cy + math.sin(a) * r,
352 z0 + t / segments * dz
353 )
354 yield mid
355
356 yield (gline.current_x, gline.current_y, gline.current_z)
357
358
318 class GcodeModel(Model): 359 class GcodeModel(Model):
319 """ 360 """
320 Model for displaying Gcode data. 361 Model for displaying Gcode data.
321 """ 362 """
322 363
361 402
362 # Max number of values which can be generated per gline 403 # Max number of values which can be generated per gline
363 # to store coordinates/colors/normals. 404 # to store coordinates/colors/normals.
364 # Nicely enough we have 3 per kind of thing for all kinds. 405 # Nicely enough we have 3 per kind of thing for all kinds.
365 coordspervertex = 3 406 coordspervertex = 3
407 buffered_color_len = 3 # 4th color component (alpha) is ignored
366 verticesperline = 8 408 verticesperline = 8
367 coordsperline = coordspervertex * verticesperline 409 coordsperline = coordspervertex * verticesperline
368 coords_count = lambda nlines: nlines * coordsperline 410 coords_count = lambda nlines: nlines * coordsperline
369 411
370 travelverticesperline = 2 412 travelverticesperline = 2
387 travel_vertices = self.travels = numpy.zeros(ntravelcoords, dtype = GLfloat) 429 travel_vertices = self.travels = numpy.zeros(ntravelcoords, dtype = GLfloat)
388 travel_vertex_k = 0 430 travel_vertex_k = 0
389 vertices = self.vertices = numpy.zeros(ncoords, dtype = GLfloat) 431 vertices = self.vertices = numpy.zeros(ncoords, dtype = GLfloat)
390 vertex_k = 0 432 vertex_k = 0
391 colors = self.colors = numpy.zeros(ncoords, dtype = GLfloat) 433 colors = self.colors = numpy.zeros(ncoords, dtype = GLfloat)
434
392 color_k = 0 435 color_k = 0
393 normals = self.normals = numpy.zeros(ncoords, dtype = GLfloat) 436 normals = self.normals = numpy.zeros(ncoords, dtype = GLfloat)
394 normal_k = 0
395 indices = self.indices = numpy.zeros(nindices, dtype = GLuint) 437 indices = self.indices = numpy.zeros(nindices, dtype = GLuint)
396 index_k = 0 438 index_k = 0
397 self.layer_idxs_map = {} 439 self.layer_idxs_map = {}
398 self.layer_stops = [0] 440 self.layer_stops = [0]
399 441
400 prev_is_extruding = False
401 prev_move_normal_x = None 442 prev_move_normal_x = None
402 prev_move_normal_y = None 443 prev_move_normal_y = None
403 prev_move_angle = None 444 prev_move_angle = None
404
405 prev_pos = (0, 0, 0) 445 prev_pos = (0, 0, 0)
446 prev_gline = None
406 layer_idx = 0 447 layer_idx = 0
407 448
408 self.printed_until = 0 449 self.printed_until = 0
409 self.only_current = False 450 self.only_current = False
410 451
433 if not gline.is_move: 474 if not gline.is_move:
434 continue 475 continue
435 if gline.x is None and gline.y is None and gline.z is None: 476 if gline.x is None and gline.y is None and gline.z is None:
436 continue 477 continue
437 has_movement = True 478 has_movement = True
438 current_pos = (gline.current_x, gline.current_y, gline.current_z) 479 for current_pos in interpolate_arcs(gline, prev_gline):
439 if not gline.extruding: 480 if not gline.extruding:
440 travel_vertices[travel_vertex_k] = prev_pos[0] 481 if self.travels.size < (travel_vertex_k + 100 * 6):
441 travel_vertices[travel_vertex_k + 1] = prev_pos[1] 482 # arc interpolation extra points allocation
442 travel_vertices[travel_vertex_k + 2] = prev_pos[2] 483 # if not enough room for another 100 points now,
443 travel_vertices[travel_vertex_k + 3] = current_pos[0] 484 # allocate enough and 50% extra to minimize separate allocations
444 travel_vertices[travel_vertex_k + 4] = current_pos[1] 485 ratio = (travel_vertex_k + 100 * 6) / self.travels.size * 1.5
445 travel_vertices[travel_vertex_k + 5] = current_pos[2] 486 # print(f"gl realloc travel {self.travels.size} -> {int(self.travels.size * ratio)}")
446 travel_vertex_k += 6 487 self.travels.resize(int(self.travels.size * ratio), refcheck = False)
447 prev_is_extruding = False 488
448 else: 489 travel_vertices[travel_vertex_k:travel_vertex_k+3] = prev_pos
449 gline_color = self.movement_color(gline) 490 travel_vertices[travel_vertex_k + 3:travel_vertex_k + 6] = current_pos
450 491 travel_vertex_k += 6
451 next_move = get_next_move(model_data, layer_idx, gline_idx) 492 else:
452 next_is_extruding = (next_move.extruding 493 delta_x = current_pos[0] - prev_pos[0]
453 if next_move is not None else False) 494 delta_y = current_pos[1] - prev_pos[1]
454 495 norm = delta_x * delta_x + delta_y * delta_y
455 delta_x = current_pos[0] - prev_pos[0] 496 if norm == 0: # Don't draw anything if this move is Z+E only
456 delta_y = current_pos[1] - prev_pos[1] 497 continue
457 norm = delta_x * delta_x + delta_y * delta_y 498 norm = math.sqrt(norm)
458 if norm == 0: # Don't draw anything if this move is Z+E only 499 move_normal_x = - delta_y / norm
459 continue 500 move_normal_y = delta_x / norm
460 norm = math.sqrt(norm) 501 move_angle = math.atan2(delta_y, delta_x)
461 move_normal_x = - delta_y / norm 502
462 move_normal_y = delta_x / norm 503 # FIXME: compute these dynamically
463 move_angle = math.atan2(delta_y, delta_x) 504 path_halfwidth = self.path_halfwidth * 1.2
464 505 path_halfheight = self.path_halfheight * 1.2
465 # FIXME: compute these dynamically 506
466 path_halfwidth = self.path_halfwidth * 1.2 507 new_indices = []
467 path_halfheight = self.path_halfheight * 1.2 508 new_vertices = []
468 509 new_normals = []
469 new_indices = [] 510 if prev_gline and prev_gline.extruding:
470 new_vertices = [] 511 # Store previous vertices indices
471 new_normals = [] 512 prev_id = vertex_k // 3 - 4
472 if prev_is_extruding: 513 avg_move_normal_x = (prev_move_normal_x + move_normal_x) / 2
473 # Store previous vertices indices 514 avg_move_normal_y = (prev_move_normal_y + move_normal_y) / 2
474 prev_id = vertex_k / 3 - 4 515 norm = avg_move_normal_x * avg_move_normal_x + avg_move_normal_y * avg_move_normal_y
475 avg_move_normal_x = (prev_move_normal_x + move_normal_x) / 2 516 if norm == 0:
476 avg_move_normal_y = (prev_move_normal_y + move_normal_y) / 2 517 avg_move_normal_x = move_normal_x
477 norm = avg_move_normal_x * avg_move_normal_x + avg_move_normal_y * avg_move_normal_y 518 avg_move_normal_y = move_normal_y
478 if norm == 0: 519 else:
479 avg_move_normal_x = move_normal_x 520 norm = math.sqrt(norm)
480 avg_move_normal_y = move_normal_y 521 avg_move_normal_x /= norm
522 avg_move_normal_y /= norm
523 delta_angle = move_angle - prev_move_angle
524 delta_angle = (delta_angle + twopi) % twopi
525 fact = abs(math.cos(delta_angle / 2))
526 # If move is turning too much, avoid creating a big peak
527 # by adding an intermediate box
528 if fact < 0.5:
529 # FIXME: It looks like there's some heavy code duplication here...
530 hw = path_halfwidth
531 p1x = prev_pos[0] - hw * prev_move_normal_x
532 p2x = prev_pos[0] + hw * prev_move_normal_x
533 p1y = prev_pos[1] - hw * prev_move_normal_y
534 p2y = prev_pos[1] + hw * prev_move_normal_y
535 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] + path_halfheight))
536 new_vertices.extend((p1x, p1y, prev_pos[2]))
537 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] - path_halfheight))
538 new_vertices.extend((p2x, p2y, prev_pos[2]))
539 new_normals.extend((0, 0, 1))
540 new_normals.extend((-prev_move_normal_x, -prev_move_normal_y, 0))
541 new_normals.extend((0, 0, -1))
542 new_normals.extend((prev_move_normal_x, prev_move_normal_y, 0))
543 first = vertex_k // 3
544 # Link to previous
545 new_indices += triangulate_box(prev_id, prev_id + 1,
546 prev_id + 2, prev_id + 3,
547 first, first + 1,
548 first + 2, first + 3)
549 p1x = prev_pos[0] - hw * move_normal_x
550 p2x = prev_pos[0] + hw * move_normal_x
551 p1y = prev_pos[1] - hw * move_normal_y
552 p2y = prev_pos[1] + hw * move_normal_y
553 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] + path_halfheight))
554 new_vertices.extend((p1x, p1y, prev_pos[2]))
555 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] - path_halfheight))
556 new_vertices.extend((p2x, p2y, prev_pos[2]))
557 new_normals.extend((0, 0, 1))
558 new_normals.extend((-move_normal_x, -move_normal_y, 0))
559 new_normals.extend((0, 0, -1))
560 new_normals.extend((move_normal_x, move_normal_y, 0))
561 prev_id += 4
562 first += 4
563 # Link to previous
564 new_indices += triangulate_box(prev_id, prev_id + 1,
565 prev_id + 2, prev_id + 3,
566 first, first + 1,
567 first + 2, first + 3)
568 else:
569 hw = path_halfwidth / fact
570 # Compute vertices
571 p1x = prev_pos[0] - hw * avg_move_normal_x
572 p2x = prev_pos[0] + hw * avg_move_normal_x
573 p1y = prev_pos[1] - hw * avg_move_normal_y
574 p2y = prev_pos[1] + hw * avg_move_normal_y
575 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] + path_halfheight))
576 new_vertices.extend((p1x, p1y, prev_pos[2]))
577 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] - path_halfheight))
578 new_vertices.extend((p2x, p2y, prev_pos[2]))
579 new_normals.extend((0, 0, 1))
580 new_normals.extend((-avg_move_normal_x, -avg_move_normal_y, 0))
581 new_normals.extend((0, 0, -1))
582 new_normals.extend((avg_move_normal_x, avg_move_normal_y, 0))
583 first = vertex_k // 3
584 # Link to previous
585 new_indices += triangulate_box(prev_id, prev_id + 1,
586 prev_id + 2, prev_id + 3,
587 first, first + 1,
588 first + 2, first + 3)
481 else: 589 else:
482 norm = math.sqrt(norm) 590 # Compute vertices normal to the current move and cap it
483 avg_move_normal_x /= norm 591 p1x = prev_pos[0] - path_halfwidth * move_normal_x
484 avg_move_normal_y /= norm 592 p2x = prev_pos[0] + path_halfwidth * move_normal_x
485 delta_angle = move_angle - prev_move_angle 593 p1y = prev_pos[1] - path_halfwidth * move_normal_y
486 delta_angle = (delta_angle + twopi) % twopi 594 p2y = prev_pos[1] + path_halfwidth * move_normal_y
487 fact = abs(math.cos(delta_angle / 2))
488 # If move is turning too much, avoid creating a big peak
489 # by adding an intermediate box
490 if fact < 0.5:
491 # FIXME: It looks like there's some heavy code duplication here...
492 hw = path_halfwidth
493 p1x = prev_pos[0] - hw * prev_move_normal_x
494 p2x = prev_pos[0] + hw * prev_move_normal_x
495 p1y = prev_pos[1] - hw * prev_move_normal_y
496 p2y = prev_pos[1] + hw * prev_move_normal_y
497 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] + path_halfheight))
498 new_vertices.extend((p1x, p1y, prev_pos[2]))
499 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] - path_halfheight))
500 new_vertices.extend((p2x, p2y, prev_pos[2]))
501 new_normals.extend((0, 0, 1))
502 new_normals.extend((-prev_move_normal_x, -prev_move_normal_y, 0))
503 new_normals.extend((0, 0, -1))
504 new_normals.extend((prev_move_normal_x, prev_move_normal_y, 0))
505 first = vertex_k / 3
506 # Link to previous
507 new_indices += triangulate_box(prev_id, prev_id + 1,
508 prev_id + 2, prev_id + 3,
509 first, first + 1,
510 first + 2, first + 3)
511 p1x = prev_pos[0] - hw * move_normal_x
512 p2x = prev_pos[0] + hw * move_normal_x
513 p1y = prev_pos[1] - hw * move_normal_y
514 p2y = prev_pos[1] + hw * move_normal_y
515 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] + path_halfheight)) 595 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] + path_halfheight))
516 new_vertices.extend((p1x, p1y, prev_pos[2])) 596 new_vertices.extend((p1x, p1y, prev_pos[2]))
517 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] - path_halfheight)) 597 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] - path_halfheight))
518 new_vertices.extend((p2x, p2y, prev_pos[2])) 598 new_vertices.extend((p2x, p2y, prev_pos[2]))
519 new_normals.extend((0, 0, 1)) 599 new_normals.extend((0, 0, 1))
520 new_normals.extend((-move_normal_x, -move_normal_y, 0)) 600 new_normals.extend((-move_normal_x, -move_normal_y, 0))
521 new_normals.extend((0, 0, -1)) 601 new_normals.extend((0, 0, -1))
522 new_normals.extend((move_normal_x, move_normal_y, 0)) 602 new_normals.extend((move_normal_x, move_normal_y, 0))
523 prev_id += 4 603 first = vertex_k // 3
524 first += 4 604 new_indices = triangulate_rectangle(first, first + 1,
525 # Link to previous 605 first + 2, first + 3)
526 new_indices += triangulate_box(prev_id, prev_id + 1, 606
527 prev_id + 2, prev_id + 3, 607 next_move = get_next_move(model_data, layer_idx, gline_idx)
528 first, first + 1, 608 next_is_extruding = next_move and next_move.extruding
529 first + 2, first + 3) 609 if not next_is_extruding:
530 else: 610 # Compute caps and link everything
531 hw = path_halfwidth / fact 611 p1x = current_pos[0] - path_halfwidth * move_normal_x
532 # Compute vertices 612 p2x = current_pos[0] + path_halfwidth * move_normal_x
533 p1x = prev_pos[0] - hw * avg_move_normal_x 613 p1y = current_pos[1] - path_halfwidth * move_normal_y
534 p2x = prev_pos[0] + hw * avg_move_normal_x 614 p2y = current_pos[1] + path_halfwidth * move_normal_y
535 p1y = prev_pos[1] - hw * avg_move_normal_y 615 new_vertices.extend((current_pos[0], current_pos[1], current_pos[2] + path_halfheight))
536 p2y = prev_pos[1] + hw * avg_move_normal_y 616 new_vertices.extend((p1x, p1y, current_pos[2]))
537 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] + path_halfheight)) 617 new_vertices.extend((current_pos[0], current_pos[1], current_pos[2] - path_halfheight))
538 new_vertices.extend((p1x, p1y, prev_pos[2])) 618 new_vertices.extend((p2x, p2y, current_pos[2]))
539 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] - path_halfheight))
540 new_vertices.extend((p2x, p2y, prev_pos[2]))
541 new_normals.extend((0, 0, 1)) 619 new_normals.extend((0, 0, 1))
542 new_normals.extend((-avg_move_normal_x, -avg_move_normal_y, 0)) 620 new_normals.extend((-move_normal_x, -move_normal_y, 0))
543 new_normals.extend((0, 0, -1)) 621 new_normals.extend((0, 0, -1))
544 new_normals.extend((avg_move_normal_x, avg_move_normal_y, 0)) 622 new_normals.extend((move_normal_x, move_normal_y, 0))
545 first = vertex_k / 3 623 end_first = vertex_k // 3 + len(new_vertices) // 3 - 4
546 # Link to previous 624 new_indices += triangulate_rectangle(end_first + 3, end_first + 2,
547 new_indices += triangulate_box(prev_id, prev_id + 1, 625 end_first + 1, end_first)
548 prev_id + 2, prev_id + 3, 626 new_indices += triangulate_box(first, first + 1,
549 first, first + 1, 627 first + 2, first + 3,
550 first + 2, first + 3) 628 end_first, end_first + 1,
551 else: 629 end_first + 2, end_first + 3)
552 # Compute vertices normal to the current move and cap it 630
553 p1x = prev_pos[0] - path_halfwidth * move_normal_x 631 if self.indices.size < (index_k + len(new_indices) + 100 * indicesperline):
554 p2x = prev_pos[0] + path_halfwidth * move_normal_x 632 # arc interpolation extra points allocation
555 p1y = prev_pos[1] - path_halfwidth * move_normal_y 633 ratio = (index_k + len(new_indices) + 100 * indicesperline) / self.indices.size * 1.5
556 p2y = prev_pos[1] + path_halfwidth * move_normal_y 634 # print(f"gl realloc print {self.vertices.size} -> {int(self.vertices.size * ratio)}")
557 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] + path_halfheight)) 635 self.vertices.resize(int(self.vertices.size * ratio), refcheck = False)
558 new_vertices.extend((p1x, p1y, prev_pos[2])) 636 self.colors.resize(int(self.colors.size * ratio), refcheck = False)
559 new_vertices.extend((prev_pos[0], prev_pos[1], prev_pos[2] - path_halfheight)) 637 self.normals.resize(int(self.normals.size * ratio), refcheck = False)
560 new_vertices.extend((p2x, p2y, prev_pos[2])) 638 self.indices.resize(int(self.indices.size * ratio), refcheck = False)
561 new_normals.extend((0, 0, 1)) 639
562 new_normals.extend((-move_normal_x, -move_normal_y, 0)) 640 for new_i, item in enumerate(new_indices):
563 new_normals.extend((0, 0, -1)) 641 indices[index_k + new_i] = item
564 new_normals.extend((move_normal_x, move_normal_y, 0)) 642 index_k += len(new_indices)
565 first = vertex_k / 3 643
566 new_indices = triangulate_rectangle(first, first + 1, 644 new_vertices_len = len(new_vertices)
567 first + 2, first + 3) 645 vertices[vertex_k:vertex_k+new_vertices_len] = new_vertices
568 646 normals[vertex_k:vertex_k+new_vertices_len] = new_normals
569 if not next_is_extruding: 647 vertex_k += new_vertices_len
570 # Compute caps and link everything 648
571 p1x = current_pos[0] - path_halfwidth * move_normal_x 649 new_vertices_count = new_vertices_len//coordspervertex
572 p2x = current_pos[0] + path_halfwidth * move_normal_x 650 # settings support alpha (transperancy), but it is ignored here
573 p1y = current_pos[1] - path_halfwidth * move_normal_y 651 gline_color = self.movement_color(gline)[:buffered_color_len]
574 p2y = current_pos[1] + path_halfwidth * move_normal_y 652 for vi in range(new_vertices_count):
575 new_vertices.extend((current_pos[0], current_pos[1], current_pos[2] + path_halfheight)) 653 colors[color_k:color_k+buffered_color_len] = gline_color
576 new_vertices.extend((p1x, p1y, current_pos[2])) 654 color_k += buffered_color_len
577 new_vertices.extend((current_pos[0], current_pos[1], current_pos[2] - path_halfheight)) 655
578 new_vertices.extend((p2x, p2y, current_pos[2])) 656 prev_move_normal_x = move_normal_x
579 new_normals.extend((0, 0, 1)) 657 prev_move_normal_y = move_normal_y
580 new_normals.extend((-move_normal_x, -move_normal_y, 0)) 658 prev_move_angle = move_angle
581 new_normals.extend((0, 0, -1)) 659
582 new_normals.extend((move_normal_x, move_normal_y, 0)) 660 prev_pos = current_pos
583 end_first = vertex_k / 3 + len(new_vertices) / 3 - 4 661 prev_gline = gline
584 new_indices += triangulate_rectangle(end_first + 3, end_first + 2, 662 count_travel_indices.append(travel_vertex_k // 3)
585 end_first + 1, end_first)
586 new_indices += triangulate_box(first, first + 1,
587 first + 2, first + 3,
588 end_first, end_first + 1,
589 end_first + 2, end_first + 3)
590
591 for new_i, item in enumerate(new_indices):
592 indices[index_k + new_i] = item
593 index_k += len(new_indices)
594 for new_i, item in enumerate(new_vertices):
595 vertices[vertex_k + new_i] = item
596 vertex_k += len(new_vertices)
597 for new_i, item in enumerate(new_normals):
598 normals[normal_k + new_i] = item
599 normal_k += len(new_normals)
600 new_colors = list(gline_color)[:-1] * (len(new_vertices) / 3)
601 for new_i, item in enumerate(new_colors):
602 colors[color_k + new_i] = item
603 color_k += len(new_colors)
604
605 prev_is_extruding = True
606 prev_move_normal_x = move_normal_x
607 prev_move_normal_y = move_normal_y
608 prev_move_angle = move_angle
609
610 prev_pos = current_pos
611 count_travel_indices.append(travel_vertex_k / 3)
612 count_print_indices.append(index_k) 663 count_print_indices.append(index_k)
613 count_print_vertices.append(vertex_k / 3) 664 count_print_vertices.append(vertex_k // 3)
614 gline.gcview_end_vertex = len(count_print_indices) - 1 665 gline.gcview_end_vertex = len(count_print_indices) - 1
615 666
616 if has_movement: 667 if has_movement:
617 self.layer_stops.append(len(count_print_indices) - 1) 668 self.layer_stops.append(len(count_print_indices) - 1)
618 self.layer_idxs_map[layer_idx] = len(self.layer_stops) - 1 669 self.layer_idxs_map[layer_idx] = len(self.layer_stops) - 1
635 (model_data.zmin, model_data.zmax, model_data.height)) 686 (model_data.zmin, model_data.zmax, model_data.height))
636 687
637 self.travels.resize(travel_vertex_k, refcheck = False) 688 self.travels.resize(travel_vertex_k, refcheck = False)
638 self.vertices.resize(vertex_k, refcheck = False) 689 self.vertices.resize(vertex_k, refcheck = False)
639 self.colors.resize(color_k, refcheck = False) 690 self.colors.resize(color_k, refcheck = False)
640 self.normals.resize(normal_k, refcheck = False) 691 self.normals.resize(vertex_k, refcheck = False)
641 self.indices.resize(index_k, refcheck = False) 692 self.indices.resize(index_k, refcheck = False)
642 693
643 self.layer_stops = array.array('L', self.layer_stops) 694 self.layer_stops = array.array('L', self.layer_stops)
644 self.count_travel_indices = array.array('L', count_travel_indices) 695 self.count_travel_indices = array.array('L', count_travel_indices)
645 self.count_print_indices = array.array('L', count_print_indices) 696 self.count_print_indices = array.array('L', count_print_indices)
653 self.fully_loaded = True 704 self.fully_loaded = True
654 705
655 t_end = time.time() 706 t_end = time.time()
656 707
657 logging.debug(_('Initialized 3D visualization in %.2f seconds') % (t_end - t_start)) 708 logging.debug(_('Initialized 3D visualization in %.2f seconds') % (t_end - t_start))
658 logging.debug(_('Vertex count: %d') % ((len(self.vertices) + len(self.travels)) / 3)) 709 logging.debug(_('Vertex count: %d') % ((len(self.vertices) + len(self.travels)) // 3))
659 yield None 710 yield None
660 711
661 def copy(self): 712 def copy(self):
662 copy = GcodeModel() 713 copy = GcodeModel()
663 for var in ["vertices", "colors", "travels", "indices", "normals", 714 for var in ["vertices", "colors", "travels", "indices", "normals",
670 setattr(copy, var, getattr(self, var)) 721 setattr(copy, var, getattr(self, var))
671 copy.loaded = True 722 copy.loaded = True
672 copy.fully_loaded = True 723 copy.fully_loaded = True
673 copy.initialized = False 724 copy.initialized = False
674 return copy 725 return copy
726
727 def update_colors(self):
728 """Rebuild gl color buffer without loading. Used after color settings edit"""
729 ncoords = self.count_print_vertices[-1]
730 colors = numpy.empty(ncoords*3, dtype = GLfloat)
731 cur_vertex = 0
732 gline_i = 1
733 for gline in self.gcode.lines:
734 if gline.gcview_end_vertex:
735 gline_color = self.movement_color(gline)[:3]
736 last_vertex = self.count_print_vertices[gline_i]
737 gline_i += 1
738 while cur_vertex < last_vertex:
739 colors[cur_vertex*3:cur_vertex*3+3] = gline_color
740 cur_vertex += 1
741 if self.vertex_color_buffer:
742 self.vertex_color_buffer.delete()
743 self.vertex_color_buffer = numpy2vbo(colors, use_vbos = self.use_vbos)
675 744
676 # ------------------------------------------------------------------------ 745 # ------------------------------------------------------------------------
677 # DRAWING 746 # DRAWING
678 # ------------------------------------------------------------------------ 747 # ------------------------------------------------------------------------
679 748
867 vertex_k = 0 936 vertex_k = 0
868 colors = self.colors = numpy.zeros(nlines * 8, dtype = GLfloat) 937 colors = self.colors = numpy.zeros(nlines * 8, dtype = GLfloat)
869 color_k = 0 938 color_k = 0
870 self.printed_until = -1 939 self.printed_until = -1
871 self.only_current = False 940 self.only_current = False
941 prev_gline = None
872 while layer_idx < len(model_data.all_layers): 942 while layer_idx < len(model_data.all_layers):
873 with self.lock: 943 with self.lock:
874 nlines = len(model_data) 944 nlines = len(model_data)
875 if nlines * 6 != vertices.size: 945 if nlines * 6 > vertices.size:
876 self.vertices.resize(nlines * 6, refcheck = False) 946 self.vertices.resize(nlines * 6, refcheck = False)
877 self.colors.resize(nlines * 8, refcheck = False) 947 self.colors.resize(nlines * 8, refcheck = False)
878 layer = model_data.all_layers[layer_idx] 948 layer = model_data.all_layers[layer_idx]
879 has_movement = False 949 has_movement = False
880 for gline in layer: 950 for gline in layer:
881 if not gline.is_move: 951 if not gline.is_move:
882 continue 952 continue
883 if gline.x is None and gline.y is None and gline.z is None: 953 if gline.x is None and gline.y is None and gline.z is None:
884 continue 954 continue
955
885 has_movement = True 956 has_movement = True
886 vertices[vertex_k] = prev_pos[0] 957 for current_pos in interpolate_arcs(gline, prev_gline):
887 vertices[vertex_k + 1] = prev_pos[1] 958
888 vertices[vertex_k + 2] = prev_pos[2] 959 if self.vertices.size < (vertex_k + 100 * 6):
889 current_pos = (gline.current_x, gline.current_y, gline.current_z) 960 # arc interpolation extra points allocation
890 vertices[vertex_k + 3] = current_pos[0] 961 ratio = (vertex_k + 100 * 6) / self.vertices.size * 1.5
891 vertices[vertex_k + 4] = current_pos[1] 962 # print(f"gl realloc lite {self.vertices.size} -> {int(self.vertices.size * ratio)}")
892 vertices[vertex_k + 5] = current_pos[2] 963 self.vertices.resize(int(self.vertices.size * ratio), refcheck = False)
893 vertex_k += 6 964 self.colors.resize(int(self.colors.size * ratio), refcheck = False)
894 965
895 vertex_color = self.movement_color(gline) 966
896 colors[color_k] = vertex_color[0] 967 vertices[vertex_k] = prev_pos[0]
897 colors[color_k + 1] = vertex_color[1] 968 vertices[vertex_k + 1] = prev_pos[1]
898 colors[color_k + 2] = vertex_color[2] 969 vertices[vertex_k + 2] = prev_pos[2]
899 colors[color_k + 3] = vertex_color[3] 970 vertices[vertex_k + 3] = current_pos[0]
900 colors[color_k + 4] = vertex_color[0] 971 vertices[vertex_k + 4] = current_pos[1]
901 colors[color_k + 5] = vertex_color[1] 972 vertices[vertex_k + 5] = current_pos[2]
902 colors[color_k + 6] = vertex_color[2] 973 vertex_k += 6
903 colors[color_k + 7] = vertex_color[3] 974
904 color_k += 8 975 vertex_color = self.movement_color(gline)
905 976 colors[color_k] = vertex_color[0]
906 prev_pos = current_pos 977 colors[color_k + 1] = vertex_color[1]
907 gline.gcview_end_vertex = vertex_k / 3 978 colors[color_k + 2] = vertex_color[2]
979 colors[color_k + 3] = vertex_color[3]
980 colors[color_k + 4] = vertex_color[0]
981 colors[color_k + 5] = vertex_color[1]
982 colors[color_k + 6] = vertex_color[2]
983 colors[color_k + 7] = vertex_color[3]
984 color_k += 8
985
986 prev_pos = current_pos
987 prev_gline = gline
988 gline.gcview_end_vertex = vertex_k // 3
908 989
909 if has_movement: 990 if has_movement:
910 self.layer_stops.append(vertex_k / 3) 991 self.layer_stops.append(vertex_k // 3)
911 self.layer_idxs_map[layer_idx] = len(self.layer_stops) - 1 992 self.layer_idxs_map[layer_idx] = len(self.layer_stops) - 1
912 self.max_layers = len(self.layer_stops) - 1 993 self.max_layers = len(self.layer_stops) - 1
913 self.num_layers_to_draw = self.max_layers + 1 994 self.num_layers_to_draw = self.max_layers + 1
914 self.initialized = False 995 self.initialized = False
915 self.loaded = True 996 self.loaded = True
934 self.fully_loaded = True 1015 self.fully_loaded = True
935 1016
936 t_end = time.time() 1017 t_end = time.time()
937 1018
938 logging.debug(_('Initialized 3D visualization in %.2f seconds') % (t_end - t_start)) 1019 logging.debug(_('Initialized 3D visualization in %.2f seconds') % (t_end - t_start))
939 logging.debug(_('Vertex count: %d') % (len(self.vertices) / 3)) 1020 logging.debug(_('Vertex count: %d') % (len(self.vertices) // 3))
940 yield None 1021 yield None
941 1022
942 def copy(self): 1023 def copy(self):
943 copy = GcodeModelLight() 1024 copy = GcodeModelLight()
944 for var in ["vertices", "colors", "max_layers", 1025 for var in ["vertices", "colors", "max_layers",

mercurial