1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
9 OpenGL.ERROR_CHECKING = False
10 from OpenGL.GLU import *
11 from OpenGL.GL import *
13 from Cura.util import profile
14 from Cura.gui.util import openglHelpers
15 from Cura.gui.util import openglGui
17 class engineResultView(object):
18 def __init__(self, parent):
22 self._gcodeLoadProgress = 0
23 self._resultLock = threading.Lock()
25 self._layer20VBOs = []
27 self.layerSelect = openglGui.glSlider(self._parent, 10000, 0, 1, (-1,-2), lambda : self._parent.QueueRefresh())
29 def setResult(self, result):
30 if self._result == result:
33 self.setEnabled(False)
35 self._resultLock.acquire()
38 #Clean the saved VBO's
39 for layer in self._layerVBOs:
40 for typeName in layer.keys():
41 self._parent.glReleaseList.append(layer[typeName])
42 for layer in self._layer20VBOs:
43 for typeName in layer.keys():
44 self._parent.glReleaseList.append(layer[typeName])
46 self._layer20VBOs = []
47 self._resultLock.release()
49 def setEnabled(self, enabled):
50 self._enabled = enabled
51 self.layerSelect.setHidden(not enabled)
53 def _gcodeLoadCallback(self, result, progress):
54 if result != self._result:
55 #Abort loading from this thread.
57 self._gcodeLoadProgress = progress
58 self._parent._queueRefresh()
65 self._resultLock.acquire()
67 if result is not None:
68 gcodeLayers = result.getGCodeLayers(self._gcodeLoadCallback)
69 if result._polygons is not None and len(result._polygons) > 0:
70 self.layerSelect.setRange(1, len(result._polygons))
71 elif gcodeLayers is not None and len(gcodeLayers) > 0:
72 self.layerSelect.setRange(1, len(gcodeLayers))
78 if profile.getMachineSetting('machine_center_is_zero') != 'True':
79 glTranslate(-profile.getMachineSettingFloat('machine_width') / 2, -profile.getMachineSettingFloat('machine_depth') / 2, 0)
82 layerNr = self.layerSelect.getValue()
83 if layerNr == self.layerSelect.getMaxValue() and result is not None and len(result._polygons) > 0:
84 layerNr = max(layerNr, len(result._polygons))
85 if result is not None and len(result._polygons) > layerNr-1 and 'inset0' in result._polygons[layerNr-1] and len(result._polygons[layerNr-1]['inset0']) > 0 and len(result._polygons[layerNr-1]['inset0'][0]) > 0:
86 viewZ = result._polygons[layerNr-1]['inset0'][0][0][2]
88 viewZ = (layerNr - 1) * profile.getProfileSettingFloat('layer_height') + profile.getProfileSettingFloat('bottom_thickness')
89 self._parent._viewTarget[2] = viewZ
90 msize = max(profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'))
92 ('inset0', 'WALL-OUTER', [1,0,0,1]),
93 ('insetx', 'WALL-INNER', [0,1,0,1]),
94 ('openoutline', None, [1,0,0,1]),
95 ('skin', 'FILL', [1,1,0,1]),
96 ('infill', None, [1,1,0,1]),
97 ('support', 'SUPPORT', [0,1,1,1]),
98 ('skirt', 'SKIRT', [0,1,1,1]),
99 ('outline', None, [0,0,0,1])
103 if result is not None:
105 if layerNr - n > 30 and n % 20 == 0 and len(result._polygons) > 0:
107 while len(self._layer20VBOs) < idx + 1:
108 self._layer20VBOs.append({})
109 if result._polygons is not None and n + 20 < len(result._polygons):
110 layerVBOs = self._layer20VBOs[idx]
111 for typeName, typeNameGCode, color in lineTypeList:
112 allow = typeName in result._polygons[n + 19]
113 if typeName == 'skirt':
114 for i in xrange(0, 20):
115 if typeName in result._polygons[n + i]:
118 if typeName not in layerVBOs:
122 for i in xrange(0, 20):
123 if typeName in result._polygons[n + i]:
124 polygons += result._polygons[n + i][typeName]
125 layerVBOs[typeName] = self._polygonsToVBO_lines(polygons)
127 glColor4f(color[0]*0.5,color[1]*0.5,color[2]*0.5,color[3])
128 layerVBOs[typeName].render()
131 c = 1.0 - ((layerNr - n) - 1) * 0.05
133 while len(self._layerVBOs) < n + 1:
134 self._layerVBOs.append({})
135 layerVBOs = self._layerVBOs[n]
136 if gcodeLayers is not None and ((layerNr - 10 < n < (len(gcodeLayers) - 1)) or len(result._polygons) < 1):
137 for typeNamePolygons, typeName, color in lineTypeList:
140 if 'GCODE-' + typeName not in layerVBOs:
141 layerVBOs['GCODE-' + typeName] = self._gcodeToVBO_quads(gcodeLayers[n+1:n+2], typeName)
142 glColor4f(color[0]*c,color[1]*c,color[2]*c,color[3])
143 layerVBOs['GCODE-' + typeName].render()
146 if 'GCODE-MOVE' not in layerVBOs:
147 layerVBOs['GCODE-MOVE'] = self._gcodeToVBO_lines(gcodeLayers[n+1:n+2])
149 layerVBOs['GCODE-MOVE'].render()
150 elif n < len(result._polygons):
151 polygons = result._polygons[n]
152 for typeName, typeNameGCode, color in lineTypeList:
153 if typeName in polygons:
154 if typeName not in layerVBOs:
155 layerVBOs[typeName] = self._polygonsToVBO_lines(polygons[typeName])
156 glColor4f(color[0]*c,color[1]*c,color[2]*c,color[3])
157 layerVBOs[typeName].render()
161 self._parent._queueRefresh()
163 if gcodeLayers is not None and self._gcodeLoadProgress != 0.0 and self._gcodeLoadProgress != 1.0:
166 glTranslate(0,-0.8,-2)
167 glColor4ub(60,60,60,255)
168 openglHelpers.glDrawStringCenter(_("Loading toolpath for visualization (%d%%)") % (self._gcodeLoadProgress * 100))
170 self._resultLock.release()
172 def _polygonsToVBO_lines(self, polygons):
173 verts = numpy.zeros((0, 3), numpy.float32)
174 indices = numpy.zeros((0), numpy.uint32)
175 for poly in polygons:
177 i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32)
179 i = numpy.dstack((i[0:-1],i[1:])).flatten()
181 i = numpy.arange(len(verts), len(verts) + len(poly), 1, numpy.uint32)
182 indices = numpy.concatenate((indices, i), 0)
183 verts = numpy.concatenate((verts, poly), 0)
184 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
186 def _polygonsToVBO_quads(self, polygons):
187 verts = numpy.zeros((0, 3), numpy.float32)
188 indices = numpy.zeros((0), numpy.uint32)
189 for poly in polygons:
190 i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32)
191 i2 = numpy.arange(len(verts) + len(poly), len(verts) + len(poly) + len(poly) + 1, 1, numpy.uint32)
193 i2[-1] = len(verts) + len(poly)
194 i = numpy.dstack((i[0:-1],i2[0:-1],i2[1:],i[1:])).flatten()
195 indices = numpy.concatenate((indices, i), 0)
196 verts = numpy.concatenate((verts, poly), 0)
197 verts = numpy.concatenate((verts, poly * numpy.array([1,0,1],numpy.float32) + numpy.array([0,-100,0],numpy.float32)), 0)
198 return openglHelpers.GLVBO(GL_QUADS, verts, indicesArray=indices)
200 def _gcodeToVBO_lines(self, gcodeLayers, extrudeType):
201 if ':' in extrudeType:
202 extruder = int(extrudeType[extrudeType.find(':')+1:])
203 extrudeType = extrudeType[0:extrudeType.find(':')]
206 verts = numpy.zeros((0, 3), numpy.float32)
207 indices = numpy.zeros((0), numpy.uint32)
208 for layer in gcodeLayers:
210 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
211 i = numpy.arange(len(verts), len(verts) + len(path['points']), 1, numpy.uint32)
212 i = numpy.dstack((i[0:-1],i[1:])).flatten()
213 indices = numpy.concatenate((indices, i), 0)
214 verts = numpy.concatenate((verts, path['points']))
215 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
217 def _gcodeToVBO_quads(self, gcodeLayers, extrudeType):
218 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
219 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
220 filamentArea = math.pi * filamentRadius * filamentRadius
222 if ':' in extrudeType:
223 extruder = int(extrudeType[extrudeType.find(':')+1:])
224 extrudeType = extrudeType[0:extrudeType.find(':')]
228 verts = numpy.zeros((0, 3), numpy.float32)
229 indices = numpy.zeros((0), numpy.uint32)
230 for layer in gcodeLayers:
232 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
234 if extrudeType == 'FILL':
237 #Construct the normals of each line 90deg rotated on the X/Y plane
238 normals = a[1:] - a[:-1]
239 lengths = numpy.sqrt(normals[:,0]**2 + normals[:,1]**2)
240 normals[:,0], normals[:,1] = -normals[:,1] / lengths, normals[:,0] / lengths
241 normals[:,2] /= lengths
243 ePerDist = path['extrusion'][1:] / lengths
245 lineWidth = ePerDist / path['layerThickness'] / 2.0
247 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
249 normals[:,0] *= lineWidth
250 normals[:,1] *= lineWidth
252 b = numpy.zeros((len(a)-1, 0), numpy.float32)
253 b = numpy.concatenate((b, a[1:] + normals), 1)
254 b = numpy.concatenate((b, a[1:] - normals), 1)
255 b = numpy.concatenate((b, a[:-1] - normals), 1)
256 b = numpy.concatenate((b, a[:-1] + normals), 1)
257 b = b.reshape((len(b) * 4, 3))
259 i = numpy.arange(len(verts), len(verts) + len(b), 1, numpy.uint32)
261 verts = numpy.concatenate((verts, b))
262 indices = numpy.concatenate((indices, i))
263 return openglHelpers.GLVBO(GL_QUADS, verts, indicesArray=indices)
265 def _gcodeToVBO_lines(self, gcodeLayers):
266 verts = numpy.zeros((0,3), numpy.float32)
267 indices = numpy.zeros((0), numpy.uint32)
268 for layer in gcodeLayers:
270 if path['type'] == 'move':
271 a = path['points'] + numpy.array([0,0,0.02], numpy.float32)
272 i = numpy.arange(len(verts), len(verts) + len(a), 1, numpy.uint32)
273 i = numpy.dstack((i[0:-1],i[1:])).flatten()
274 verts = numpy.concatenate((verts, a))
275 indices = numpy.concatenate((indices, i))
276 if path['type'] == 'retract':
277 a = path['points'] + numpy.array([0,0,0.02], numpy.float32)
278 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
279 a = a.reshape((len(a) * 2, 3))
280 i = numpy.arange(len(verts), len(verts) + len(a), 1, numpy.uint32)
281 verts = numpy.concatenate((verts, a))
282 indices = numpy.concatenate((indices, i))
283 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
285 def OnKeyChar(self, keyCode):
286 if not self._enabled:
288 #TODO: This is strange behaviour. Overloaded functionality of keyboard buttons!
289 if wx.GetKeyState(wx.WXK_SHIFT) or wx.GetKeyState(wx.WXK_CONTROL):
290 if keyCode == wx.WXK_UP:
291 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
292 self._parent.QueueRefresh()
294 elif keyCode == wx.WXK_DOWN:
295 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
296 self._parent.QueueRefresh()
298 elif keyCode == wx.WXK_PAGEUP:
299 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
300 self._parent.QueueRefresh()
302 elif keyCode == wx.WXK_PAGEDOWN:
303 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
304 self._parent.QueueRefresh()