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._singleLayer = False
23 self._gcodeLoadProgress = 0
24 self._resultLock = threading.Lock()
26 self._layer20VBOs = []
28 self.layerSelect = openglGui.glSlider(self._parent, 10000, 1, 1, (-1,-2), lambda : self._parent.QueueRefresh())
29 self.singleLayerToggle = openglGui.glButton(self._parent, 27, _("Single Layer"), (-1.1,-1.4), self.OnSingleLayerToggle, 0.75) #stay at 75% size of the base size
31 def setResult(self, result):
32 if self._result == result:
35 self._singleLayer = False
36 self.layerSelect.setHidden(True)
37 self.singleLayerToggle.setHidden(True)
39 self.setEnabled(self._enabled)
41 self._resultLock.acquire()
44 #Clean the saved VBO's
45 for layer in self._layerVBOs:
46 for typeName in layer.keys():
47 self._parent.glReleaseList.append(layer[typeName])
48 for layer in self._layer20VBOs:
49 for typeName in layer.keys():
50 self._parent.glReleaseList.append(layer[typeName])
52 self._layer20VBOs = []
53 self._resultLock.release()
55 def OnSingleLayerToggle(self, button):
56 self._singleLayer = not self._singleLayer
58 self.singleLayerToggle._tooltip = "Multi Layer"
60 self.singleLayerToggle._tooltip = "Single Layer"
62 def setEnabled(self, enabled):
63 self._enabled = enabled
64 self._singleLayer = False
65 self.layerSelect.setHidden(not enabled)
66 self.singleLayerToggle.setHidden(not enabled)
69 def _gcodeLoadCallback(self, result, progress):
70 if result != self._result:
71 #Abort loading from this thread.
73 self._gcodeLoadProgress = progress
74 self._parent._queueRefresh()
81 self._resultLock.acquire()
83 if result is not None:
84 gcodeLayers = result.getGCodeLayers(self._gcodeLoadCallback)
85 if result._polygons is not None and len(result._polygons) > 0:
86 self.layerSelect.setRange(1, len(result._polygons))
87 elif gcodeLayers is not None and len(gcodeLayers) > 0:
88 self.layerSelect.setRange(1, len(gcodeLayers))
94 if profile.getMachineSetting('machine_center_is_zero') != 'True':
95 glTranslate(-profile.getMachineSettingFloat('machine_width') / 2, -profile.getMachineSettingFloat('machine_depth') / 2, 0)
98 layerNr = self.layerSelect.getValue()
99 if layerNr == self.layerSelect.getMaxValue() and result is not None and len(result._polygons) > 0:
100 layerNr = max(layerNr, len(result._polygons))
101 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:
102 viewZ = result._polygons[layerNr-1]['inset0'][0][0][2]
104 viewZ = (layerNr - 1) * profile.getProfileSettingFloat('layer_height') + profile.getProfileSettingFloat('bottom_thickness')
105 self._parent._viewTarget[2] = viewZ
106 msize = max(profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'))
108 ('inset0', 'WALL-OUTER', [1,0,0,1]),
109 ('insetx', 'WALL-INNER', [0,1,0,1]),
110 ('openoutline', None, [1,0,0,1]),
111 ('skin', 'SKIN', [1,1,0,1]),
112 ('infill', 'FILL', [1,1,0,1]),
113 ('support', 'SUPPORT', [0,1,1,1]),
114 ('skirt', 'SKIRT', [0,1,1,1]),
115 ('outline', None, [0,0,0,1])
119 if result is not None:
121 if layerNr - n > 30 and n % 20 == 0 and len(result._polygons) > 0:
123 while len(self._layer20VBOs) < idx + 1:
124 self._layer20VBOs.append({})
125 if result._polygons is not None and n + 20 < len(result._polygons):
126 layerVBOs = self._layer20VBOs[idx]
127 for typeName, typeNameGCode, color in lineTypeList:
128 allow = typeName in result._polygons[n + 19]
129 if typeName == 'skirt':
130 for i in xrange(0, 20):
131 if typeName in result._polygons[n + i]:
134 if typeName not in layerVBOs:
138 for i in xrange(0, 20):
139 if typeName in result._polygons[n + i]:
140 polygons += result._polygons[n + i][typeName]
141 layerVBOs[typeName] = self._polygonsToVBO_lines(polygons)
144 if not self._singleLayer or n == layerNr - 1:
145 glColor4f(color[0]*0.5,color[1]*0.5,color[2]*0.5,color[3])
146 layerVBOs[typeName].render()
149 c = 1.0 - ((layerNr - n) - 1) * 0.05
151 while len(self._layerVBOs) < n + 1:
152 self._layerVBOs.append({})
153 layerVBOs = self._layerVBOs[n]
154 if gcodeLayers is not None and ((layerNr - 10 < n < (len(gcodeLayers) - 1)) or len(result._polygons) < 1):
155 for typeNamePolygons, typeName, color in lineTypeList:
158 if 'GCODE-' + typeName not in layerVBOs:
159 layerVBOs['GCODE-' + typeName] = self._gcodeToVBO_quads(gcodeLayers[n+1:n+2], typeName)
161 if not self._singleLayer or n == layerNr - 1:
162 glColor4f(color[0]*c,color[1]*c,color[2]*c,color[3])
163 layerVBOs['GCODE-' + typeName].render()
166 if 'GCODE-MOVE' not in layerVBOs:
167 layerVBOs['GCODE-MOVE'] = self._gcodeToVBO_lines(gcodeLayers[n+1:n+2])
169 layerVBOs['GCODE-MOVE'].render()
170 elif n < len(result._polygons):
171 polygons = result._polygons[n]
172 for typeName, typeNameGCode, color in lineTypeList:
173 if typeName in polygons:
174 if typeName not in layerVBOs:
175 layerVBOs[typeName] = self._polygonsToVBO_lines(polygons[typeName])
177 if not self._singleLayer or n == layerNr - 1:
178 glColor4f(color[0]*c,color[1]*c,color[2]*c,color[3])
179 layerVBOs[typeName].render()
183 self._parent._queueRefresh()
185 if gcodeLayers is not None and self._gcodeLoadProgress != 0.0 and self._gcodeLoadProgress != 1.0:
188 glTranslate(0,-0.8,-2)
189 glColor4ub(60,60,60,255)
190 openglHelpers.glDrawStringCenter(_("Loading toolpath for visualization (%d%%)") % (self._gcodeLoadProgress * 100))
192 self._resultLock.release()
194 def _polygonsToVBO_lines(self, polygons):
195 verts = numpy.zeros((0, 3), numpy.float32)
196 indices = numpy.zeros((0), numpy.uint32)
197 for poly in polygons:
199 i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32)
201 i = numpy.dstack((i[0:-1],i[1:])).flatten()
203 i = numpy.arange(len(verts), len(verts) + len(poly), 1, numpy.uint32)
204 indices = numpy.concatenate((indices, i), 0)
205 verts = numpy.concatenate((verts, poly), 0)
206 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
208 def _polygonsToVBO_quads(self, polygons):
209 verts = numpy.zeros((0, 3), numpy.float32)
210 indices = numpy.zeros((0), numpy.uint32)
211 for poly in polygons:
212 i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32)
213 i2 = numpy.arange(len(verts) + len(poly), len(verts) + len(poly) + len(poly) + 1, 1, numpy.uint32)
215 i2[-1] = len(verts) + len(poly)
216 i = numpy.dstack((i[0:-1],i2[0:-1],i2[1:],i[1:])).flatten()
217 indices = numpy.concatenate((indices, i), 0)
218 verts = numpy.concatenate((verts, poly), 0)
219 verts = numpy.concatenate((verts, poly * numpy.array([1,0,1],numpy.float32) + numpy.array([0,-100,0],numpy.float32)), 0)
220 return openglHelpers.GLVBO(GL_QUADS, verts, indicesArray=indices)
222 def _gcodeToVBO_lines(self, gcodeLayers, extrudeType):
223 if ':' in extrudeType:
224 extruder = int(extrudeType[extrudeType.find(':')+1:])
225 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):
233 i = numpy.arange(len(verts), len(verts) + len(path['points']), 1, numpy.uint32)
234 i = numpy.dstack((i[0:-1],i[1:])).flatten()
235 indices = numpy.concatenate((indices, i), 0)
236 verts = numpy.concatenate((verts, path['points']))
237 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
239 def _gcodeToVBO_quads(self, gcodeLayers, extrudeType):
240 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
241 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
242 filamentArea = math.pi * filamentRadius * filamentRadius
244 if ':' in extrudeType:
245 extruder = int(extrudeType[extrudeType.find(':')+1:])
246 extrudeType = extrudeType[0:extrudeType.find(':')]
250 verts = numpy.zeros((0, 3), numpy.float32)
251 indices = numpy.zeros((0), numpy.uint32)
252 for layer in gcodeLayers:
254 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
256 if extrudeType == 'FILL':
259 #Construct the normals of each line 90deg rotated on the X/Y plane
260 normals = a[1:] - a[:-1]
261 lengths = numpy.sqrt(normals[:,0]**2 + normals[:,1]**2)
262 normals[:,0], normals[:,1] = -normals[:,1] / lengths, normals[:,0] / lengths
263 normals[:,2] /= lengths
265 ePerDist = path['extrusion'][1:] / lengths
267 lineWidth = ePerDist / path['layerThickness'] / 2.0
269 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
271 normals[:,0] *= lineWidth
272 normals[:,1] *= lineWidth
274 b = numpy.zeros((len(a)-1, 0), numpy.float32)
275 b = numpy.concatenate((b, a[1:] + normals), 1)
276 b = numpy.concatenate((b, a[1:] - normals), 1)
277 b = numpy.concatenate((b, a[:-1] - normals), 1)
278 b = numpy.concatenate((b, a[:-1] + normals), 1)
279 b = b.reshape((len(b) * 4, 3))
281 i = numpy.arange(len(verts), len(verts) + len(b), 1, numpy.uint32)
283 verts = numpy.concatenate((verts, b))
284 indices = numpy.concatenate((indices, i))
285 return openglHelpers.GLVBO(GL_QUADS, verts, indicesArray=indices)
287 def _gcodeToVBO_lines(self, gcodeLayers):
288 verts = numpy.zeros((0,3), numpy.float32)
289 indices = numpy.zeros((0), numpy.uint32)
290 for layer in gcodeLayers:
292 if path['type'] == 'move':
293 a = path['points'] + numpy.array([0,0,0.02], numpy.float32)
294 i = numpy.arange(len(verts), len(verts) + len(a), 1, numpy.uint32)
295 i = numpy.dstack((i[0:-1],i[1:])).flatten()
296 verts = numpy.concatenate((verts, a))
297 indices = numpy.concatenate((indices, i))
298 if path['type'] == 'retract':
299 a = path['points'] + numpy.array([0,0,0.02], numpy.float32)
300 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
301 a = a.reshape((len(a) * 2, 3))
302 i = numpy.arange(len(verts), len(verts) + len(a), 1, numpy.uint32)
303 verts = numpy.concatenate((verts, a))
304 indices = numpy.concatenate((indices, i))
305 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
307 def OnKeyChar(self, keyCode):
308 if not self._enabled:
310 #TODO: This is strange behaviour. Overloaded functionality of keyboard buttons!
311 if wx.GetKeyState(wx.WXK_SHIFT) or wx.GetKeyState(wx.WXK_CONTROL):
312 if keyCode == wx.WXK_UP:
313 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
314 self._parent.QueueRefresh()
316 elif keyCode == wx.WXK_DOWN:
317 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
318 self._parent.QueueRefresh()
320 elif keyCode == wx.WXK_PAGEUP:
321 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
322 self._parent.QueueRefresh()
324 elif keyCode == wx.WXK_PAGEDOWN:
325 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
326 self._parent.QueueRefresh()