chiark / gitweb /
9fa2ff2685ef16e805792363dded8d0a2bccc42f
[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
7 import OpenGL
8 #OpenGL.ERROR_CHECKING = False
9 from OpenGL.GLU import *
10 from OpenGL.GL import *
11
12 from Cura.util import profile
13 from Cura.gui.util import openglHelpers
14 from Cura.gui.util import openglGui
15
16 class engineResultView(object):
17         def __init__(self, parent):
18                 self._parent = parent
19                 self._result = None
20                 self._enabled = False
21                 self._gcodeLoadProgress = 0
22                 self._layerVBOs = []
23                 self._layer20VBOs = []
24
25                 self.layerSelect = openglGui.glSlider(self._parent, 10000, 0, 1, (-1,-2), lambda : self._parent.QueueRefresh())
26
27         def setResult(self, result):
28                 if self._result == result:
29                         return
30
31                 self._result = result
32
33                 #Clean the saved VBO's
34                 for layer in self._layerVBOs:
35                         for typeName in layer.keys():
36                                 self._parent.glReleaseList.append(layer[typeName])
37                 for layer in self._layer20VBOs:
38                         for typeName in layer.keys():
39                                 self._parent.glReleaseList.append(layer[typeName])
40                 self._layerVBOs = []
41                 self._layer20VBOs = []
42
43         def setEnabled(self, enabled):
44                 self._enabled = enabled
45                 self.layerSelect.setHidden(not enabled)
46
47         def _gcodeLoadCallback(self, result, progress):
48                 if result != self._result:
49                         #Abort loading from this thread.
50                         return True
51                 self._gcodeLoadProgress = progress
52                 self._parent._queueRefresh()
53                 return False
54
55         def OnDraw(self):
56                 if not self._enabled:
57                         return
58
59                 result = self._result
60                 if result is not None and result._polygons is not None:
61                         self.layerSelect.setRange(1, len(result._polygons))
62                 if result is not None:
63                         gcodeLayers = result.getGCodeLayers(self._gcodeLoadCallback)
64                 else:
65                         gcodeLayers = None
66
67                 glPushMatrix()
68                 glEnable(GL_BLEND)
69                 if profile.getMachineSetting('machine_center_is_zero') != 'True':
70                         glTranslate(-profile.getMachineSettingFloat('machine_width') / 2, -profile.getMachineSettingFloat('machine_depth') / 2, 0)
71                 glLineWidth(2)
72
73                 layerNr = self.layerSelect.getValue()
74                 if layerNr == self.layerSelect.getMaxValue():
75                         layerNr = max(layerNr, len(result._polygons))
76                 viewZ = (layerNr - 1) * profile.getProfileSettingFloat('layer_height') + profile.getProfileSettingFloat('bottom_thickness')
77                 self._parent._viewTarget[2] = viewZ
78                 msize = max(profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'))
79                 lineTypeList = [
80                         ('inset0',     'WALL-OUTER', [1,0,0,1]),
81                         ('insetx',     'WALL-INNER', [0,1,0,1]),
82                         ('openoutline', None,        [1,0,0,1]),
83                         ('skin',       'FILL',       [1,1,0,1]),
84                         ('infill',      None,        [1,1,0,1]),
85                         ('support',    'SUPPORT',    [0,1,1,1]),
86                         ('skirt',      'SKIRT',      [0,1,1,1]),
87                         ('outline',     None,        [0,0,0,1])
88                 ]
89                 n = layerNr - 1
90                 generatedVBO = False
91                 while n >= 0:
92                         if layerNr - n > 30 and n % 20 == 0:
93                                 idx = n / 20
94                                 while len(self._layer20VBOs) < idx + 1:
95                                         self._layer20VBOs.append({})
96                                 if result is not None and result._polygons is not None and n + 20 < len(result._polygons):
97                                         layerVBOs = self._layer20VBOs[idx]
98                                         for typeName, typeNameGCode, color in lineTypeList:
99                                                 if (typeName in result._polygons[n + 19]) or (typeName == 'skirt' and typeName in result._polygons[n]):
100                                                         if typeName not in layerVBOs:
101                                                                 if generatedVBO:
102                                                                         continue
103                                                                 polygons = []
104                                                                 for i in xrange(0, 20):
105                                                                         if typeName in result._polygons[n + i]:
106                                                                                 polygons += result._polygons[n + i][typeName]
107                                                                 layerVBOs[typeName] = self._polygonsToVBO_lines(polygons)
108                                                                 generatedVBO = True
109                                                         glColor4f(color[0]*0.5,color[1]*0.5,color[2]*0.5,color[3])
110                                                         layerVBOs[typeName].render()
111                                 n -= 20
112                         else:
113                                 c = 1.0 - ((layerNr - n) - 1) * 0.05
114                                 c = max(0.5, c)
115                                 while len(self._layerVBOs) < n + 1:
116                                         self._layerVBOs.append({})
117                                 layerVBOs = self._layerVBOs[n]
118                                 if gcodeLayers is not None and layerNr - 10 < n < (len(gcodeLayers) - 1):
119                                         for typeNamePolygons, typeName, color in lineTypeList:
120                                                 if typeName is None:
121                                                         continue
122                                                 if 'GCODE-' + typeName not in layerVBOs:
123                                                         layerVBOs['GCODE-' + typeName] = self._gcodeToVBO_quads(gcodeLayers[n+1:n+2], typeName)
124                                                 glColor4f(color[0]*c,color[1]*c,color[2]*c,color[3])
125                                                 layerVBOs['GCODE-' + typeName].render()
126
127                                         if n == layerNr - 1:
128                                                 if 'GCODE-MOVE' not in layerVBOs:
129                                                         layerVBOs['GCODE-MOVE'] = self._gcodeToVBO_lines(gcodeLayers[n+1:n+2])
130                                                 glColor4f(0,0,c,1)
131                                                 layerVBOs['GCODE-MOVE'].render()
132                                 elif result is not None and result._polygons is not None and n < len(result._polygons):
133                                         polygons = result._polygons[n]
134                                         for typeName, typeNameGCode, color in lineTypeList:
135                                                 if typeName in polygons:
136                                                         if typeName not in layerVBOs:
137                                                                 layerVBOs[typeName] = self._polygonsToVBO_lines(polygons[typeName])
138                                                         glColor4f(color[0]*c,color[1]*c,color[2]*c,color[3])
139                                                         layerVBOs[typeName].render()
140                                 n -= 1
141                 glPopMatrix()
142                 if generatedVBO:
143                         self._parent._queueRefresh()
144
145                 if gcodeLayers is not None and self._gcodeLoadProgress != 0.0 and self._gcodeLoadProgress != 1.0:
146                         glPushMatrix()
147                         glLoadIdentity()
148                         glTranslate(0,-0.8,-2)
149                         glColor4ub(60,60,60,255)
150                         openglHelpers.glDrawStringCenter(_("Loading toolpath for visualization (%d%%)") % (self._gcodeLoadProgress * 100))
151                         glPopMatrix()
152
153         def _polygonsToVBO_lines(self, polygons):
154                 verts = numpy.zeros((0, 3), numpy.float32)
155                 indices = numpy.zeros((0), numpy.uint32)
156                 for poly in polygons:
157                         if len(poly) > 2:
158                                 i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32)
159                                 i[-1] = len(verts)
160                                 i = numpy.dstack((i[0:-1],i[1:])).flatten()
161                         else:
162                                 i = numpy.arange(len(verts), len(verts) + len(poly), 1, numpy.uint32)
163                         indices = numpy.concatenate((indices, i), 0)
164                         verts = numpy.concatenate((verts, poly), 0)
165                 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
166
167         def _polygonsToVBO_quads(self, polygons):
168                 verts = numpy.zeros((0, 3), numpy.float32)
169                 indices = numpy.zeros((0), numpy.uint32)
170                 for poly in polygons:
171                         i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32)
172                         i2 = numpy.arange(len(verts) + len(poly), len(verts) + len(poly) + len(poly) + 1, 1, numpy.uint32)
173                         i[-1] = len(verts)
174                         i2[-1] = len(verts) + len(poly)
175                         i = numpy.dstack((i[0:-1],i2[0:-1],i2[1:],i[1:])).flatten()
176                         indices = numpy.concatenate((indices, i), 0)
177                         verts = numpy.concatenate((verts, poly), 0)
178                         verts = numpy.concatenate((verts, poly * numpy.array([1,0,1],numpy.float32) + numpy.array([0,-100,0],numpy.float32)), 0)
179                 return openglHelpers.GLVBO(GL_QUADS, verts, indicesArray=indices)
180
181         def _gcodeToVBO_lines(self, gcodeLayers, extrudeType):
182                 if ':' in extrudeType:
183                         extruder = int(extrudeType[extrudeType.find(':')+1:])
184                         extrudeType = extrudeType[0:extrudeType.find(':')]
185                 else:
186                         extruder = None
187                 verts = numpy.zeros((0, 3), numpy.float32)
188                 indices = numpy.zeros((0), numpy.uint32)
189                 for layer in gcodeLayers:
190                         for path in layer:
191                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
192                                         i = numpy.arange(len(verts), len(verts) + len(path['points']), 1, numpy.uint32)
193                                         i = numpy.dstack((i[0:-1],i[1:])).flatten()
194                                         indices = numpy.concatenate((indices, i), 0)
195                                         verts = numpy.concatenate((verts, path['points']))
196                 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
197
198         def _gcodeToVBO_quads(self, gcodeLayers, extrudeType):
199                 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
200                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
201                 filamentArea = math.pi * filamentRadius * filamentRadius
202
203                 if ':' in extrudeType:
204                         extruder = int(extrudeType[extrudeType.find(':')+1:])
205                         extrudeType = extrudeType[0:extrudeType.find(':')]
206                 else:
207                         extruder = None
208
209                 verts = numpy.zeros((0, 3), numpy.float32)
210                 indices = numpy.zeros((0), numpy.uint32)
211                 for layer in gcodeLayers:
212                         for path in layer:
213                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
214                                         a = path['points']
215                                         if extrudeType == 'FILL':
216                                                 a[:,2] += 0.01
217
218                                         #Construct the normals of each line 90deg rotated on the X/Y plane
219                                         normals = a[1:] - a[:-1]
220                                         lengths = numpy.sqrt(normals[:,0]**2 + normals[:,1]**2)
221                                         normals[:,0], normals[:,1] = -normals[:,1] / lengths, normals[:,0] / lengths
222                                         normals[:,2] /= lengths
223
224                                         ePerDist = path['extrusion'][1:] / lengths
225                                         if useFilamentArea:
226                                                 lineWidth = ePerDist / path['layerThickness'] / 2.0
227                                         else:
228                                                 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
229
230                                         normals[:,0] *= lineWidth
231                                         normals[:,1] *= lineWidth
232
233                                         b = numpy.zeros((len(a)-1, 0), numpy.float32)
234                                         b = numpy.concatenate((b, a[1:] + normals), 1)
235                                         b = numpy.concatenate((b, a[1:] - normals), 1)
236                                         b = numpy.concatenate((b, a[:-1] - normals), 1)
237                                         b = numpy.concatenate((b, a[:-1] + normals), 1)
238                                         b = b.reshape((len(b) * 4, 3))
239
240                                         i = numpy.arange(len(verts), len(verts) + len(b), 1, numpy.uint32)
241
242                                         verts = numpy.concatenate((verts, b))
243                                         indices = numpy.concatenate((indices, i))
244                 return openglHelpers.GLVBO(GL_QUADS, verts, indicesArray=indices)
245
246         def _gcodeToVBO_lines(self, gcodeLayers):
247                 verts = numpy.zeros((0,3), numpy.float32)
248                 indices = numpy.zeros((0), numpy.uint32)
249                 for layer in gcodeLayers:
250                         for path in layer:
251                                 if path['type'] == 'move':
252                                         a = path['points'] + numpy.array([0,0,0.02], numpy.float32)
253                                         i = numpy.arange(len(verts), len(verts) + len(a), 1, numpy.uint32)
254                                         i = numpy.dstack((i[0:-1],i[1:])).flatten()
255                                         verts = numpy.concatenate((verts, a))
256                                         indices = numpy.concatenate((indices, i))
257                                 if path['type'] == 'retract':
258                                         a = path['points'] + numpy.array([0,0,0.02], numpy.float32)
259                                         a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
260                                         a = a.reshape((len(a) * 2, 3))
261                                         i = numpy.arange(len(verts), len(verts) + len(a), 1, numpy.uint32)
262                                         verts = numpy.concatenate((verts, a))
263                                         indices = numpy.concatenate((indices, i))
264                 return openglHelpers.GLVBO(GL_LINES, verts, indicesArray=indices)
265
266         def OnKeyChar(self, keyCode):
267                 if not self._enabled:
268                         return
269
270                 if wx.GetKeyState(wx.WXK_SHIFT) or wx.GetKeyState(wx.WXK_CONTROL):
271                         if keyCode == wx.WXK_UP:
272                                 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
273                                 self._parent.QueueRefresh()
274                                 return True
275                         elif keyCode == wx.WXK_DOWN:
276                                 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
277                                 self._parent.QueueRefresh()
278                                 return True
279                         elif keyCode == wx.WXK_PAGEUP:
280                                 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
281                                 self._parent.QueueRefresh()
282                                 return True
283                         elif keyCode == wx.WXK_PAGEDOWN:
284                                 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
285                                 self._parent.QueueRefresh()
286                                 return True
287                 return False
288
289                 # if self.viewMode == 'gcode' and self._gcode is not None:
290                 #       try:
291                 #               self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
292                 #       except:
293                 #               pass
294
295         # def _loadGCode(self):
296         #       self._gcode.progressCallback = self._gcodeLoadCallback
297         #       if self._gcodeFilename is not None:
298         #               self._gcode.load(self._gcodeFilename)
299         #       else:
300         #               self._gcode.load(self._gcodeData)
301         #
302         # def _gcodeLoadCallback(self, progress):
303         #       if not self or self._gcode is None:
304         #               return True
305         #       if len(self._gcode.layerList) % 15 == 0:
306         #               time.sleep(0.1)
307         #       if self._gcode is None:
308         #               return True
309         #       self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
310         #       if self.viewMode == 'gcode':
311         #               self._queueRefresh()
312         #       return False
313
314                         # if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
315                         #       glDisable(GL_DEPTH_TEST)
316                         #       glPushMatrix()
317                         #       glLoadIdentity()
318                         #       glTranslate(0,-4,-10)
319                         #       glColor4ub(60,60,60,255)
320                         #       opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
321                         #       glPopMatrix()
322
323
324 # if self._gcode is not None:
325 #                       self._gcode = None
326 #                       for layerVBOlist in self._gcodeVBOs:
327 #                               for vbo in layerVBOlist:
328 #                                       self.glReleaseList.append(vbo)
329 #                       self._gcodeVBOs = []
330
331                         # if self._gcode is not None and self._gcode.layerList is None:
332                         #       self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
333                         #       self._gcodeLoadThread.daemon = True
334                         #       self._gcodeLoadThread.start()
335                         #
336
337                         #
338                         # if self._gcode is not None and self._gcode.layerList is not None:
339                         #       glPushMatrix()
340                         #       if profile.getMachineSetting('machine_center_is_zero') != 'True':
341                         #               glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
342                         #       t = time.time()
343                         #       drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
344                         #       for n in xrange(0, drawUpTill):
345                         #               c = 1.0 - float(drawUpTill - n) / 15
346                         #               c = max(0.3, c)
347                         #               if len(self._gcodeVBOs) < n + 1:
348                         #                       self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
349                         #                       if time.time() - t > 0.5:
350                         #                               self.QueueRefresh()
351                         #                               break
352                         #               #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
353                         #               if n == drawUpTill - 1:
354                         #                       if len(self._gcodeVBOs[n]) < 9:
355                         #                               self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
356                         #                       glColor3f(c, 0, 0)
357                         #                       self._gcodeVBOs[n][8].render(GL_QUADS)
358                         #                       glColor3f(c/2, 0, c)
359                         #                       self._gcodeVBOs[n][9].render(GL_QUADS)
360                         #                       glColor3f(0, c, c/2)
361                         #                       self._gcodeVBOs[n][10].render(GL_QUADS)
362                         #                       glColor3f(c, 0, 0)
363                         #                       self._gcodeVBOs[n][11].render(GL_QUADS)
364                         #
365                         #                       glColor3f(0, c, 0)
366                         #                       self._gcodeVBOs[n][12].render(GL_QUADS)
367                         #                       glColor3f(c/2, c/2, 0.0)
368                         #                       self._gcodeVBOs[n][13].render(GL_QUADS)
369                         #                       glColor3f(0, c, c)
370                         #                       self._gcodeVBOs[n][14].render(GL_QUADS)
371                         #                       self._gcodeVBOs[n][15].render(GL_QUADS)
372                         #                       glColor3f(0, 0, c)
373                         #                       self._gcodeVBOs[n][16].render(GL_LINES)
374                         #               else:
375                         #                       glColor3f(c, 0, 0)
376                         #                       self._gcodeVBOs[n][0].render(GL_LINES)
377                         #                       glColor3f(c/2, 0, c)
378                         #                       self._gcodeVBOs[n][1].render(GL_LINES)
379                         #                       glColor3f(0, c, c/2)
380                         #                       self._gcodeVBOs[n][2].render(GL_LINES)
381                         #                       glColor3f(c, 0, 0)
382                         #                       self._gcodeVBOs[n][3].render(GL_LINES)
383                         #
384                         #                       glColor3f(0, c, 0)
385                         #                       self._gcodeVBOs[n][4].render(GL_LINES)
386                         #                       glColor3f(c/2, c/2, 0.0)
387                         #                       self._gcodeVBOs[n][5].render(GL_LINES)
388                         #                       glColor3f(0, c, c)
389                         #                       self._gcodeVBOs[n][6].render(GL_LINES)
390                         #                       self._gcodeVBOs[n][7].render(GL_LINES)
391                         #       glPopMatrix()
392         #
393         # def _generateGCodeVBOs(self, layer):
394         #       ret = []
395         #       for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
396         #               if ':' in extrudeType:
397         #                       extruder = int(extrudeType[extrudeType.find(':')+1:])
398         #                       extrudeType = extrudeType[0:extrudeType.find(':')]
399         #               else:
400         #                       extruder = None
401         #               pointList = numpy.zeros((0,3), numpy.float32)
402         #               for path in layer:
403         #                       if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
404         #                               a = path['points']
405         #                               a = numpy.concatenate((a[:-1], a[1:]), 1)
406         #                               a = a.reshape((len(a) * 2, 3))
407         #                               pointList = numpy.concatenate((pointList, a))
408         #               ret.append(opengl.GLVBO(pointList))
409         #       return ret
410         #
411         # def _generateGCodeVBOs2(self, layer):
412         #       filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
413         #       filamentArea = math.pi * filamentRadius * filamentRadius
414         #       useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
415         #
416         #       ret = []
417         #       for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
418         #               if ':' in extrudeType:
419         #                       extruder = int(extrudeType[extrudeType.find(':')+1:])
420         #                       extrudeType = extrudeType[0:extrudeType.find(':')]
421         #               else:
422         #                       extruder = None
423         #               pointList = numpy.zeros((0,3), numpy.float32)
424         #               for path in layer:
425         #                       if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
426         #                               a = path['points']
427         #                               if extrudeType == 'FILL':
428         #                                       a[:,2] += 0.01
429         #
430         #                               normal = a[1:] - a[:-1]
431         #                               lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
432         #                               normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
433         #                               normal[:,2] /= lens
434         #
435         #                               ePerDist = path['extrusion'][1:] / lens
436         #                               if useFilamentArea:
437         #                                       lineWidth = ePerDist / path['layerThickness'] / 2.0
438         #                               else:
439         #                                       lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
440         #
441         #                               normal[:,0] *= lineWidth
442         #                               normal[:,1] *= lineWidth
443         #
444         #                               b = numpy.zeros((len(a)-1, 0), numpy.float32)
445         #                               b = numpy.concatenate((b, a[1:] + normal), 1)
446         #                               b = numpy.concatenate((b, a[1:] - normal), 1)
447         #                               b = numpy.concatenate((b, a[:-1] - normal), 1)
448         #                               b = numpy.concatenate((b, a[:-1] + normal), 1)
449         #                               b = b.reshape((len(b) * 4, 3))
450         #
451         #                               if len(a) > 2:
452         #                                       normal2 = normal[:-1] + normal[1:]
453         #                                       lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
454         #                                       normal2[:,0] /= lens2
455         #                                       normal2[:,1] /= lens2
456         #                                       normal2[:,0] *= lineWidth[:-1]
457         #                                       normal2[:,1] *= lineWidth[:-1]
458         #
459         #                                       c = numpy.zeros((len(a)-2, 0), numpy.float32)
460         #                                       c = numpy.concatenate((c, a[1:-1]), 1)
461         #                                       c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
462         #                                       c = numpy.concatenate((c, a[1:-1]+normal2), 1)
463         #                                       c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
464         #
465         #                                       c = numpy.concatenate((c, a[1:-1]), 1)
466         #                                       c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
467         #                                       c = numpy.concatenate((c, a[1:-1]-normal2), 1)
468         #                                       c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
469         #
470         #                                       c = c.reshape((len(c) * 8, 3))
471         #
472         #                                       pointList = numpy.concatenate((pointList, b, c))
473         #                               else:
474         #                                       pointList = numpy.concatenate((pointList, b))
475         #               ret.append(opengl.GLVBO(pointList))
476         #
477         #       pointList = numpy.zeros((0,3), numpy.float32)
478         #       for path in layer:
479         #               if path['type'] == 'move':
480         #                       a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
481         #                       a = numpy.concatenate((a[:-1], a[1:]), 1)
482         #                       a = a.reshape((len(a) * 2, 3))
483         #                       pointList = numpy.concatenate((pointList, a))
484         #               if path['type'] == 'retract':
485         #                       a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
486         #                       a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
487         #                       a = a.reshape((len(a) * 2, 3))
488         #                       pointList = numpy.concatenate((pointList, a))
489         #       ret.append(opengl.GLVBO(pointList))
490         #
491         #       return ret