Coverage for tatlin/lib/gl/gcodemodel.py: 62%
157 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 05:56 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 05:56 +0000
1# -*- coding: utf-8 -*-
2# Copyright (C) 2011 Denis Kobozev
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software Foundation,
16# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19import math
20import numpy
21import logging
22import time
24from OpenGL.GL import * # type:ignore
25from OpenGL.GLE import * # type:ignore
26from OpenGL.arrays.vbo import VBO
28from .model import Model
30from tatlin.lib import vector
31from tatlin.lib.model.gcode.parser import Movement
34class GcodeModel(Model):
35 """
36 Model for displaying Gcode data.
37 """
39 # vertices for arrow to display the direction of movement
40 arrow = numpy.require(
41 [
42 [0.0, 0.0, 0.0],
43 [0.4, -0.1, 0.0],
44 [0.4, 0.1, 0.0],
45 ],
46 "f",
47 )
48 layer_entry_marker = numpy.require(
49 [
50 [0.23, -0.14, 0.0],
51 [0.0, 0.26, 0.0],
52 [-0.23, -0.14, 0.0],
53 ],
54 "f",
55 )
56 layer_exit_marker = numpy.require(
57 [
58 [-0.23, -0.23, 0.0],
59 [0.23, -0.23, 0.0],
60 [0.23, 0.23, 0.0],
61 [0.23, 0.23, 0.0],
62 [-0.23, 0.23, 0.0],
63 [-0.23, -0.23, 0.0],
64 ],
65 "f",
66 )
68 def load_data(self, model_data, callback=None):
69 t_start = time.time()
71 vertex_list = []
72 color_list = []
73 self.layer_stops = [0]
74 self.layer_heights = []
75 arrow_list = []
76 layer_markers_list = []
77 self.layer_marker_stops = [0]
79 num_layers = len(model_data)
80 callback_every = max(1, int(math.floor(num_layers / 100)))
82 # the first movement designates the starting point
83 start = prev = model_data[0][0]
84 del model_data[0][0]
85 for layer_idx, layer in enumerate(model_data):
86 first = layer[0]
87 for movement in layer:
88 vertex_list.append(prev.v)
89 vertex_list.append(movement.v)
90 arrow = self.arrow
91 # position the arrow with respect to movement
92 arrow = vector.rotate(arrow, movement.angle(prev.v), 0.0, 0.0, 1.0)
93 arrow_list.extend(arrow)
94 vertex_color = self.movement_color(movement)
95 color_list.append(vertex_color)
96 prev = movement
98 self.layer_stops.append(len(vertex_list))
99 self.layer_heights.append(first.v[2])
101 # add the layer entry marker
102 if layer_idx > 0 and len(model_data[layer_idx - 1]) > 0:
103 layer_markers_list.extend(
104 self.layer_entry_marker + model_data[layer_idx - 1][-1].v
105 )
106 elif layer_idx == 0 and len(layer) > 0:
107 layer_markers_list.extend(self.layer_entry_marker + layer[0].v)
109 # add the layer exit marker
110 if len(layer) > 1:
111 layer_markers_list.extend(self.layer_exit_marker + layer[-1].v)
113 self.layer_marker_stops.append(len(layer_markers_list))
115 if callback and layer_idx % callback_every == 0:
116 callback(layer_idx + 1, num_layers)
118 self.vertices = numpy.array(vertex_list, "f")
119 self.colors = numpy.array(color_list, "f")
120 self.arrows = numpy.array(arrow_list, "f")
121 self.layer_markers = numpy.array(layer_markers_list, "f")
123 # by translating the arrow vertices outside of the loop, we achieve a
124 # significant performance gain thanks to numpy. it would be really nice
125 # if we could rotate in a similar fashion...
126 self.arrows = self.arrows + self.vertices[1::2].repeat(3, 0)
128 # for every pair of vertices of the model, there are 3 vertices for the arrow
129 assert len(self.arrows) == (
130 (len(self.vertices) // 2) * 3
131 ), "The 2:3 ratio of model vertices to arrow vertices does not hold."
133 self.max_layers = len(self.layer_stops) - 1
134 self.num_layers_to_draw = self.max_layers
135 self.arrows_enabled = True
136 self.initialized = False
137 self.vertex_count = len(self.vertices)
139 t_end = time.time()
141 logging.info("Initialized Gcode model in %.2f seconds" % (t_end - t_start))
142 logging.info("Vertex count: %d" % self.vertex_count)
144 def movement_color(self, move):
145 """
146 Return the color to use for particular type of movement.
147 """
148 # default movement color is gray
149 color = (0.6, 0.6, 0.6, 0.6)
151 extruder_on = move.flags & Movement.FLAG_EXTRUDER_ON or move.delta_e > 0
152 outer_perimeter = (
153 move.flags & Movement.FLAG_PERIMETER
154 and move.flags & Movement.FLAG_PERIMETER_OUTER
155 )
157 if extruder_on and outer_perimeter:
158 color = (0.0, 0.875, 0.875, 0.6) # cyan
159 elif extruder_on and move.flags & Movement.FLAG_PERIMETER:
160 color = (0.0, 1.0, 0.0, 0.6) # green
161 elif extruder_on and move.flags & Movement.FLAG_LOOP:
162 color = (1.0, 0.875, 0.0, 0.6) # yellow
163 elif extruder_on:
164 color = (1.0, 0.0, 0.0, 0.6) # red
166 return color
168 # ------------------------------------------------------------------------
169 # DRAWING
170 # ------------------------------------------------------------------------
172 def init(self):
173 self.vertex_buffer = VBO(self.vertices, "GL_STATIC_DRAW")
174 self.vertex_color_buffer = VBO(
175 self.colors.repeat(2, 0), "GL_STATIC_DRAW"
176 ) # each pair of vertices shares the color
178 if self.arrows_enabled:
179 self.arrow_buffer = VBO(self.arrows, "GL_STATIC_DRAW")
180 self.arrow_color_buffer = VBO(
181 self.colors.repeat(3, 0), "GL_STATIC_DRAW"
182 ) # each triplet of vertices shares the color
184 self.layer_marker_buffer = VBO(self.layer_markers, "GL_STATIC_DRAW")
186 self.initialized = True
188 def display(self, elevation=0, eye_height=0, mode_ortho=False, mode_2d=False):
189 glPushMatrix()
191 offset_z = self.offset_z if not mode_2d else 0
192 glTranslate(self.offset_x, self.offset_y, offset_z)
194 glEnableClientState(GL_VERTEX_ARRAY)
195 glEnableClientState(GL_COLOR_ARRAY)
197 self._display_movements(elevation, eye_height, mode_ortho, mode_2d)
199 if self.arrows_enabled:
200 self._display_arrows()
202 glDisableClientState(GL_COLOR_ARRAY)
204 if self.arrows_enabled:
205 self._display_layer_markers()
207 glDisableClientState(GL_VERTEX_ARRAY)
208 glPopMatrix()
210 def _display_movements(
211 self, elevation=0, eye_height=0, mode_ortho=False, mode_2d=False
212 ):
213 self.vertex_buffer.bind()
214 glVertexPointer(3, GL_FLOAT, 0, None)
216 self.vertex_color_buffer.bind()
217 glColorPointer(4, GL_FLOAT, 0, None)
219 if mode_2d:
220 glScale(1.0, 1.0, 0.0) # discard z coordinates
221 start = self.layer_stops[self.num_layers_to_draw - 1]
222 end = self.layer_stops[self.num_layers_to_draw]
223 glDrawArrays(GL_LINES, start, end - start)
225 elif mode_ortho:
226 if elevation >= 0:
227 # draw layers in normal order, bottom to top
228 start = 0
229 end = self.layer_stops[self.num_layers_to_draw]
230 glDrawArrays(GL_LINES, start, end - start)
232 else:
233 # draw layers in reverse order, top to bottom
234 stop_idx = self.num_layers_to_draw - 1
235 while stop_idx >= 0:
236 start = self.layer_stops[stop_idx]
237 end = self.layer_stops[stop_idx + 1]
238 glDrawArrays(GL_LINES, start, end - start)
239 stop_idx -= 1
241 else: # 3d projection mode
242 reverse_threshold_layer = self._layer_up_to_height(
243 eye_height - self.offset_z
244 )
246 if reverse_threshold_layer >= 0:
247 # draw layers up to (and including) the threshold in normal order, bottom to top
248 normal_layers_to_draw = min(
249 self.num_layers_to_draw, reverse_threshold_layer + 1
250 )
251 start = 0
252 end = self.layer_stops[normal_layers_to_draw]
253 glDrawArrays(GL_LINES, start, end - start)
255 if reverse_threshold_layer + 1 < self.num_layers_to_draw:
256 # draw layers from the threshold in reverse order, top to bottom
257 stop_idx = self.num_layers_to_draw - 1
258 while stop_idx > reverse_threshold_layer:
259 start = self.layer_stops[stop_idx]
260 end = self.layer_stops[stop_idx + 1]
261 glDrawArrays(GL_LINES, start, end - start)
262 stop_idx -= 1
264 self.vertex_buffer.unbind()
265 self.vertex_color_buffer.unbind()
267 def _layer_up_to_height(self, height):
268 """Return the index of the last layer lower than height."""
269 for idx in range(len(self.layer_heights) - 1, -1, -1):
270 if self.layer_heights[idx] < height:
271 return idx
273 return 0
275 def _display_arrows(self):
276 self.arrow_buffer.bind()
277 glVertexPointer(3, GL_FLOAT, 0, None)
279 self.arrow_color_buffer.bind()
280 glColorPointer(4, GL_FLOAT, 0, None)
282 start = (self.layer_stops[self.num_layers_to_draw - 1] // 2) * 3
283 end = (self.layer_stops[self.num_layers_to_draw] // 2) * 3
285 glDrawArrays(GL_TRIANGLES, start, end - start)
287 self.arrow_buffer.unbind()
288 self.arrow_color_buffer.unbind()
290 def _display_layer_markers(self):
291 self.layer_marker_buffer.bind()
292 glVertexPointer(3, GL_FLOAT, 0, None)
294 start = self.layer_marker_stops[self.num_layers_to_draw - 1]
295 end = self.layer_marker_stops[self.num_layers_to_draw]
297 glColor4f(0.6, 0.6, 0.6, 0.6)
298 glDrawArrays(GL_TRIANGLES, start, end - start)
300 self.layer_marker_buffer.unbind()