chiark / gitweb /
Merge branch 'LulzBot-devel' into taz5-nozzle-size
[cura.git] / Cura / gui / util / engineResultView.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import wx
4 import numpy
5 import math
6 import threading
7
8 import OpenGL
9 OpenGL.ERROR_CHECKING = False
10 from OpenGL.GLU import *
11 from OpenGL.GL import *
12
13 from Cura.util import profile
14 from Cura.gui.util import openglHelpers
15 from Cura.gui.util import openglGui
16
17 class engineResultView(object):
18         def __init__(self, parent):
19                 self._parent = parent
20                 self._result = None
21                 self._enabled = False
22                 self._singleLayer = False
23                 self._gcodeLoadProgress = 0
24                 self._resultLock = threading.Lock()
25                 self._layerVBOs = []
26                 self._layer20VBOs = []
27
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
30
31         def setResult(self, result):
32                 if self._result == result:
33                         return
34                 if result is None:
35                         self._singleLayer = False
36                         self.layerSelect.setHidden(True)
37                         self.singleLayerToggle.setHidden(True)
38                 else:
39                         self.setEnabled(self._enabled)
40
41                 self._resultLock.acquire()
42                 self._result = result
43
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])
51                 self._layerVBOs = []
52                 self._layer20VBOs = []
53                 self._resultLock.release()
54
55         def OnSingleLayerToggle(self, button):
56                 self._singleLayer = not self._singleLayer
57                 if self._singleLayer:
58                         self.singleLayerToggle._tooltip = "Multi Layer"
59                 else:
60                         self.singleLayerToggle._tooltip = "Single Layer"
61
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)
67                 
68
69         def _gcodeLoadCallback(self, result, progress):
70                 if result != self._result:
71                         #Abort loading from this thread.
72                         return True
73                 self._gcodeLoadProgress = progress
74                 self._parent._queueRefresh()
75                 return False
76
77         def OnDraw(self):
78                 if not self._enabled:
79                         return
80
81                 self._resultLock.acquire()
82                 result = self._result
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))
89                 else:
90                         gcodeLayers = None
91
92                 glPushMatrix()
93                 glEnable(GL_BLEND)
94                 if profile.getMachineSetting('machine_center_is_zero') != 'True':
95                         glTranslate(-profile.getMachineSettingFloat('machine_width') / 2, -profile.getMachineSettingFloat('machine_depth') / 2, 0)
96                 glLineWidth(2)
97
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]
103                 else:
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'))
107                 lineTypeList = [
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])
116                 ]
117                 n = layerNr - 1
118                 generatedVBO = False
119                 if result is not None:
120                         while n >= 0:
121                                 if layerNr - n > 30 and n % 20 == 0 and len(result._polygons) > 0:
122                                         idx = n / 20
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]:
132                                                                                 allow = True
133                                                         if allow:
134                                                                 if typeName not in layerVBOs:
135                                                                         if generatedVBO:
136                                                                                 continue
137                                                                         polygons = []
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)
142                                                                         generatedVBO = True
143
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()
147                                         n -= 20
148                                 else:
149                                         c = 1.0 - ((layerNr - n) - 1) * 0.05
150                                         c = max(0.5, c)
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:
156                                                         if typeName is None:
157                                                                 continue
158                                                         if 'GCODE-' + typeName not in layerVBOs:
159                                                                 layerVBOs['GCODE-' + typeName] = self._gcodeToVBO_quads(gcodeLayers[n+1:n+2], typeName)
160
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()
164
165                                                 if n == layerNr - 1:
166                                                         if 'GCODE-MOVE' not in layerVBOs:
167                                                                 layerVBOs['GCODE-MOVE'] = self._gcodeToVBO_lines(gcodeLayers[n+1:n+2])
168                                                         glColor4f(0,0,c,1)
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])
176
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()
180                                         n -= 1
181                 glPopMatrix()
182                 if generatedVBO:
183                         self._parent._queueRefresh()
184
185                 if gcodeLayers is not None and self._gcodeLoadProgress != 0.0 and self._gcodeLoadProgress != 1.0:
186                         glPushMatrix()
187                         glLoadIdentity()
188                         glTranslate(0,-0.8,-2)
189                         glColor4ub(60,60,60,255)
190                         openglHelpers.glDrawStringCenter(_("Loading toolpath for visualization (%d%%)") % (self._gcodeLoadProgress * 100))
191                         glPopMatrix()
192                 self._resultLock.release()
193
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:
198                         if len(poly) > 2:
199                                 i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32)
200                                 i[-1] = len(verts)
201                                 i = numpy.dstack((i[0:-1],i[1:])).flatten()
202                         else:
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)
207
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)
214                         i[-1] = len(verts)
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)
221
222         def _gcodeToVBO_lines(self, gcodeLayers, extrudeType):
223                 if ':' in extrudeType:
224                         extruder = int(extrudeType[extrudeType.find(':')+1:])
225                         extrudeType = extrudeType[0:extrudeType.find(':')]
226                 else:
227                         extruder = None
228                 verts = numpy.zeros((0, 3), numpy.float32)
229                 indices = numpy.zeros((0), numpy.uint32)
230                 for layer in gcodeLayers:
231                         for path in layer:
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)
238
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
243
244                 if ':' in extrudeType:
245                         extruder = int(extrudeType[extrudeType.find(':')+1:])
246                         extrudeType = extrudeType[0:extrudeType.find(':')]
247                 else:
248                         extruder = None
249
250                 verts = numpy.zeros((0, 3), numpy.float32)
251                 indices = numpy.zeros((0), numpy.uint32)
252                 for layer in gcodeLayers:
253                         for path in layer:
254                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
255                                         a = path['points']
256                                         if extrudeType == 'FILL':
257                                                 a[:,2] += 0.01
258
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
264
265                                         ePerDist = path['extrusion'][1:] / lengths
266                                         if useFilamentArea:
267                                                 lineWidth = ePerDist / path['layerThickness'] / 2.0
268                                         else:
269                                                 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
270
271                                         normals[:,0] *= lineWidth
272                                         normals[:,1] *= lineWidth
273
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))
280
281                                         i = numpy.arange(len(verts), len(verts) + len(b), 1, numpy.uint32)
282
283                                         verts = numpy.concatenate((verts, b))
284                                         indices = numpy.concatenate((indices, i))
285                 return openglHelpers.GLVBO(GL_QUADS, verts, indicesArray=indices)
286
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:
291                         for path in layer:
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)
306
307         def OnKeyChar(self, keyCode):
308                 if not self._enabled:
309                         return
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()
315                                 return True
316                         elif keyCode == wx.WXK_DOWN:
317                                 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
318                                 self._parent.QueueRefresh()
319                                 return True
320                         elif keyCode == wx.WXK_PAGEUP:
321                                 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
322                                 self._parent.QueueRefresh()
323                                 return True
324                         elif keyCode == wx.WXK_PAGEDOWN:
325                                 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
326                                 self._parent.QueueRefresh()
327                                 return True
328                 return False