chiark / gitweb /
One extra test to make sure the result is not None
[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._gcodeLoadProgress = 0
23                 self._resultLock = threading.Lock()
24                 self._layerVBOs = []
25                 self._layer20VBOs = []
26
27                 self.layerSelect = openglGui.glSlider(self._parent, 10000, 0, 1, (-1,-2), lambda : self._parent.QueueRefresh())
28
29         def setResult(self, result):
30                 if self._result == result:
31                         return
32                 if result is None:
33                         self.setEnabled(False)
34
35                 self._resultLock.acquire()
36                 self._result = result
37
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])
45                 self._layerVBOs = []
46                 self._layer20VBOs = []
47                 self._resultLock.release()
48
49         def setEnabled(self, enabled):
50                 self._enabled = enabled
51                 self.layerSelect.setHidden(not enabled)
52
53         def _gcodeLoadCallback(self, result, progress):
54                 if result != self._result:
55                         #Abort loading from this thread.
56                         return True
57                 self._gcodeLoadProgress = progress
58                 self._parent._queueRefresh()
59                 return False
60
61         def OnDraw(self):
62                 if not self._enabled:
63                         return
64
65                 self._resultLock.acquire()
66                 result = self._result
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))
73                 else:
74                         gcodeLayers = None
75
76                 glPushMatrix()
77                 glEnable(GL_BLEND)
78                 if profile.getMachineSetting('machine_center_is_zero') != 'True':
79                         glTranslate(-profile.getMachineSettingFloat('machine_width') / 2, -profile.getMachineSettingFloat('machine_depth') / 2, 0)
80                 glLineWidth(2)
81
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]
87                 else:
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'))
91                 lineTypeList = [
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])
100                 ]
101                 n = layerNr - 1
102                 generatedVBO = False
103                 if result is not None:
104                         while n >= 0:
105                                 if layerNr - n > 30 and n % 20 == 0 and len(result._polygons) > 0:
106                                         idx = n / 20
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]:
116                                                                                 allow = True
117                                                         if allow:
118                                                                 if typeName not in layerVBOs:
119                                                                         if generatedVBO:
120                                                                                 continue
121                                                                         polygons = []
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)
126                                                                         generatedVBO = True
127                                                                 glColor4f(color[0]*0.5,color[1]*0.5,color[2]*0.5,color[3])
128                                                                 layerVBOs[typeName].render()
129                                         n -= 20
130                                 else:
131                                         c = 1.0 - ((layerNr - n) - 1) * 0.05
132                                         c = max(0.5, c)
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:
138                                                         if typeName is None:
139                                                                 continue
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()
144
145                                                 if n == layerNr - 1:
146                                                         if 'GCODE-MOVE' not in layerVBOs:
147                                                                 layerVBOs['GCODE-MOVE'] = self._gcodeToVBO_lines(gcodeLayers[n+1:n+2])
148                                                         glColor4f(0,0,c,1)
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()
158                                         n -= 1
159                 glPopMatrix()
160                 if generatedVBO:
161                         self._parent._queueRefresh()
162
163                 if gcodeLayers is not None and self._gcodeLoadProgress != 0.0 and self._gcodeLoadProgress != 1.0:
164                         glPushMatrix()
165                         glLoadIdentity()
166                         glTranslate(0,-0.8,-2)
167                         glColor4ub(60,60,60,255)
168                         openglHelpers.glDrawStringCenter(_("Loading toolpath for visualization (%d%%)") % (self._gcodeLoadProgress * 100))
169                         glPopMatrix()
170                 self._resultLock.release()
171
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:
176                         if len(poly) > 2:
177                                 i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32)
178                                 i[-1] = len(verts)
179                                 i = numpy.dstack((i[0:-1],i[1:])).flatten()
180                         else:
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)
185
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)
192                         i[-1] = len(verts)
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)
199
200         def _gcodeToVBO_lines(self, gcodeLayers, extrudeType):
201                 if ':' in extrudeType:
202                         extruder = int(extrudeType[extrudeType.find(':')+1:])
203                         extrudeType = extrudeType[0:extrudeType.find(':')]
204                 else:
205                         extruder = None
206                 verts = numpy.zeros((0, 3), numpy.float32)
207                 indices = numpy.zeros((0), numpy.uint32)
208                 for layer in gcodeLayers:
209                         for path in layer:
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)
216
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
221
222                 if ':' in extrudeType:
223                         extruder = int(extrudeType[extrudeType.find(':')+1:])
224                         extrudeType = extrudeType[0:extrudeType.find(':')]
225                 else:
226                         extruder = None
227
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                                         a = path['points']
234                                         if extrudeType == 'FILL':
235                                                 a[:,2] += 0.01
236
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
242
243                                         ePerDist = path['extrusion'][1:] / lengths
244                                         if useFilamentArea:
245                                                 lineWidth = ePerDist / path['layerThickness'] / 2.0
246                                         else:
247                                                 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
248
249                                         normals[:,0] *= lineWidth
250                                         normals[:,1] *= lineWidth
251
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))
258
259                                         i = numpy.arange(len(verts), len(verts) + len(b), 1, numpy.uint32)
260
261                                         verts = numpy.concatenate((verts, b))
262                                         indices = numpy.concatenate((indices, i))
263                 return openglHelpers.GLVBO(GL_QUADS, verts, indicesArray=indices)
264
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:
269                         for path in layer:
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)
284
285         def OnKeyChar(self, keyCode):
286                 if not self._enabled:
287                         return
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()
293                                 return True
294                         elif keyCode == wx.WXK_DOWN:
295                                 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
296                                 self._parent.QueueRefresh()
297                                 return True
298                         elif keyCode == wx.WXK_PAGEUP:
299                                 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
300                                 self._parent.QueueRefresh()
301                                 return True
302                         elif keyCode == wx.WXK_PAGEDOWN:
303                                 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
304                                 self._parent.QueueRefresh()
305                                 return True
306                 return False