chiark / gitweb /
Add extruder offset passing to the engine. Update some tooltips.
[cura.git] / Cura / gui / sceneView.py
1 from __future__ import absolute_import
2
3 import wx
4 import numpy
5 import time
6 import os
7 import traceback
8 import shutil
9 import threading
10 import math
11
12 import OpenGL
13 OpenGL.ERROR_CHECKING = False
14 from OpenGL.GLU import *
15 from OpenGL.GL import *
16
17 from Cura.gui import printWindow
18 from Cura.util import profile
19 from Cura.util import meshLoader
20 from Cura.util import objectScene
21 from Cura.util import resources
22 from Cura.util import sliceEngine
23 from Cura.util import machineCom
24 from Cura.util import removableStorage
25 from Cura.util import gcodeInterpreter
26 from Cura.gui.util import previewTools
27 from Cura.gui.util import opengl
28 from Cura.gui.util import openglGui
29
30 class SceneView(openglGui.glGuiPanel):
31         def __init__(self, parent):
32                 super(SceneView, self).__init__(parent)
33
34                 self._yaw = 30
35                 self._pitch = 60
36                 self._zoom = 300
37                 self._scene = objectScene.Scene()
38                 self._gcode = None
39                 self._gcodeVBOs = []
40                 self._objectShader = None
41                 self._focusObj = None
42                 self._selectedObj = None
43                 self._objColors = [None,None,None,None]
44                 self._mouseX = -1
45                 self._mouseY = -1
46                 self._mouseState = None
47                 self._viewTarget = numpy.array([0,0,0], numpy.float32)
48                 self._animView = None
49                 self._animZoom = None
50                 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
51                 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
52                 self._isSimpleMode = True
53
54                 self._viewport = None
55                 self._modelMatrix = None
56                 self._projMatrix = None
57                 self.tempMatrix = None
58
59                 self.openFileButton      = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
60                 self.printButton         = openglGui.glButton(self, 6, 'Print', (1,0), self.showPrintWindow)
61                 self.printButton.setDisabled(True)
62
63                 group = []
64                 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
65                 self.scaleToolButton  = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
66                 self.mirrorToolButton  = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
67
68                 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
69                 self.layFlatButton       = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
70
71                 self.resetScaleButton    = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
72                 self.scaleMaxButton      = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
73
74                 self.mirrorXButton       = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
75                 self.mirrorYButton       = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
76                 self.mirrorZButton       = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
77
78                 self.rotateToolButton.setExpandArrow(True)
79                 self.scaleToolButton.setExpandArrow(True)
80                 self.mirrorToolButton.setExpandArrow(True)
81
82                 self.scaleForm = openglGui.glFrame(self, (2, -2))
83                 openglGui.glGuiLayoutGrid(self.scaleForm)
84                 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
85                 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
86                 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
87                 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
88                 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
89                 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
90                 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
91                 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
92                 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
93                 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
94                 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
95                 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
96                 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
97                 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
98
99                 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,11,15,23], ['Normal', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
100                 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
101
102                 self.notification = openglGui.glNotification(self, (0, 0))
103
104                 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
105                 self._sceneUpdateTimer = wx.Timer(self)
106                 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
107                 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
108
109                 self.OnViewChange()
110                 self.OnToolSelect(0)
111                 self.updateToolButtons()
112                 self.updateProfileToControls()
113
114         def showLoadModel(self, button = 1):
115                 if button == 1:
116                         dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
117                         dlg.SetWildcard(meshLoader.loadWildcardFilter())
118                         if dlg.ShowModal() != wx.ID_OK:
119                                 dlg.Destroy()
120                                 return
121                         filename = dlg.GetPath()
122                         dlg.Destroy()
123                         if not(os.path.exists(filename)):
124                                 return False
125                         profile.putPreference('lastFile', filename)
126                         self.GetParent().GetParent().GetParent().addToModelMRU(filename)
127                         self.loadScene([filename])
128
129         def showSaveModel(self):
130                 if len(self._scene.objects()) < 1:
131                         return
132                 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
133                 dlg.SetWildcard(meshLoader.saveWildcardFilter())
134                 if dlg.ShowModal() != wx.ID_OK:
135                         dlg.Destroy()
136                         return
137                 filename = dlg.GetPath()
138                 dlg.Destroy()
139                 meshLoader.saveMeshes(filename, self._scene.objects())
140
141         def showPrintWindow(self, button):
142                 if button == 1:
143                         if machineCom.machineIsConnected():
144                                 printWindow.printFile(self._slicer.getGCodeFilename())
145                         elif len(removableStorage.getPossibleSDcardDrives()) > 0:
146                                 drives = removableStorage.getPossibleSDcardDrives()
147                                 if len(drives) > 1:
148                                         dlg = wx.SingleChoiceDialog(self, "Select SD drive", "Multiple removable drives have been found,\nplease select your SD card drive", map(lambda n: n[0], drives))
149                                         if dlg.ShowModal() != wx.ID_OK:
150                                                 dlg.Destroy()
151                                                 return
152                                         drive = drives[dlg.GetSelection()]
153                                         dlg.Destroy()
154                                 else:
155                                         drive = drives[0]
156                                 filename = os.path.basename(profile.getPreference('lastFile'))
157                                 filename = filename[0:filename.rfind('.')] + '.gcode'
158                                 try:
159                                         shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
160                                 except:
161                                         self.notification.message("Failed to save to SD card")
162                                 else:
163                                         self.notification.message("Saved as %s" % (drive[1] + filename), lambda : self.notification.message('You can now eject the card.') if removableStorage.ejectDrive(drive[1]) else self.notification.message('Safe remove failed...'))
164                         else:
165                                 self.showSaveGCode()
166                 if button == 3:
167                         menu = wx.Menu()
168                         self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._slicer.getGCodeFilename()), menu.Append(-1, 'Print with USB'))
169                         self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
170                         self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
171                         self.PopupMenu(menu)
172                         menu.Destroy()
173
174         def showSaveGCode(self):
175                 defPath = profile.getPreference('lastFile')
176                 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
177                 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
178                 dlg.SetFilename(defPath)
179                 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
180                 if dlg.ShowModal() != wx.ID_OK:
181                         dlg.Destroy()
182                         return
183                 filename = dlg.GetPath()
184                 dlg.Destroy()
185
186                 try:
187                         shutil.copy(self._slicer.getGCodeFilename(), filename)
188                 except:
189                         self.notification.message("Failed to save")
190                 else:
191                         self.notification.message("Saved as %s" % (filename))
192
193         def _showSliceLog(self):
194                 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
195                 dlg.ShowModal()
196                 dlg.Destroy()
197
198         def OnToolSelect(self, button):
199                 if self.rotateToolButton.getSelected():
200                         self.tool = previewTools.toolRotate(self)
201                 elif self.scaleToolButton.getSelected():
202                         self.tool = previewTools.toolScale(self)
203                 elif self.mirrorToolButton.getSelected():
204                         self.tool = previewTools.toolNone(self)
205                 else:
206                         self.tool = previewTools.toolNone(self)
207                 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
208                 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
209                 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
210                 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
211                 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
212                 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
213                 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
214                 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
215
216         def updateToolButtons(self):
217                 if self._selectedObj is None:
218                         hidden = True
219                 else:
220                         hidden = False
221                 self.rotateToolButton.setHidden(hidden)
222                 self.scaleToolButton.setHidden(hidden)
223                 self.mirrorToolButton.setHidden(hidden)
224                 if hidden:
225                         self.rotateToolButton.setSelected(False)
226                         self.scaleToolButton.setSelected(False)
227                         self.mirrorToolButton.setSelected(False)
228                         self.OnToolSelect(0)
229
230         def OnViewChange(self):
231                 if self.viewSelection.getValue() == 3:
232                         self.viewMode = 'gcode'
233                         if self._gcode is not None:
234                                 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
235                                 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
236                         self._selectObject(None)
237                 elif self.viewSelection.getValue() == 1:
238                         self.viewMode = 'transparent'
239                 elif self.viewSelection.getValue() == 2:
240                         self.viewMode = 'xray'
241                 else:
242                         self.viewMode = 'normal'
243                 self.layerSelect.setHidden(self.viewMode != 'gcode')
244                 self.openFileButton.setHidden(self.viewMode == 'gcode')
245                 self.QueueRefresh()
246
247         def OnRotateReset(self, button):
248                 if self._selectedObj is None:
249                         return
250                 self._selectedObj.resetRotation()
251                 self._scene.pushFree()
252                 self._selectObject(self._selectedObj)
253                 self.sceneUpdated()
254
255         def OnLayFlat(self, button):
256                 if self._selectedObj is None:
257                         return
258                 self._selectedObj.layFlat()
259                 self._scene.pushFree()
260                 self._selectObject(self._selectedObj)
261                 self.sceneUpdated()
262
263         def OnScaleReset(self, button):
264                 if self._selectedObj is None:
265                         return
266                 self._selectedObj.resetScale()
267                 self._selectObject(self._selectedObj)
268                 self.updateProfileToControls()
269                 self.sceneUpdated()
270
271         def OnScaleMax(self, button):
272                 if self._selectedObj is None:
273                         return
274                 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
275                 self._scene.pushFree()
276                 self._selectObject(self._selectedObj)
277                 self.updateProfileToControls()
278                 self.sceneUpdated()
279
280         def OnMirror(self, axis):
281                 if self._selectedObj is None:
282                         return
283                 self._selectedObj.mirror(axis)
284                 self.sceneUpdated()
285
286         def OnScaleEntry(self, value, axis):
287                 if self._selectedObj is None:
288                         return
289                 try:
290                         value = float(value)
291                 except:
292                         return
293                 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
294                 self.updateProfileToControls()
295                 self._scene.pushFree()
296                 self._selectObject(self._selectedObj)
297                 self.sceneUpdated()
298
299         def OnScaleEntryMM(self, value, axis):
300                 if self._selectedObj is None:
301                         return
302                 try:
303                         value = float(value)
304                 except:
305                         return
306                 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
307                 self.updateProfileToControls()
308                 self._scene.pushFree()
309                 self._selectObject(self._selectedObj)
310                 self.sceneUpdated()
311
312         def OnDeleteAll(self, e):
313                 while len(self._scene.objects()) > 0:
314                         self._deleteObject(self._scene.objects()[0])
315                 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
316
317         def OnMultiply(self, e):
318                 if self._focusObj is None:
319                         return
320                 obj = self._focusObj
321                 dlg = wx.NumberEntryDialog(self, "How many items do you want?", "Copies", "Multiply", 2, 1, 100)
322                 if dlg.ShowModal() != wx.ID_OK:
323                         dlg.Destroy()
324                         return
325                 cnt = dlg.GetValue() - 1
326                 dlg.Destroy()
327                 n = 0
328                 while True:
329                         n += 1
330                         newObj = obj.copy()
331                         self._scene.add(newObj)
332                         self._scene.centerAll()
333                         if not self._scene.checkPlatform(newObj):
334                                 break
335                         if n > cnt:
336                                 break
337                 if n <= cnt:
338                         self.notification.message("Could not create more then %d items" % (n))
339                 self._scene.remove(newObj)
340                 self._scene.centerAll()
341                 self.sceneUpdated()
342
343         def OnSplitObject(self, e):
344                 if self._focusObj is None:
345                         return
346                 self._scene.remove(self._focusObj)
347                 for obj in self._focusObj.split(self._splitCallback):
348                         self._scene.add(obj)
349                 self._scene.centerAll()
350                 self._selectObject(None)
351                 self.sceneUpdated()
352
353         def _splitCallback(self, progress):
354                 print progress
355
356         def OnMergeObjects(self, e):
357                 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
358                         return
359                 self._scene.merge(self._selectedObj, self._focusObj)
360                 self.sceneUpdated()
361
362         def sceneUpdated(self):
363                 self._sceneUpdateTimer.Start(1, True)
364                 self._slicer.abortSlicer()
365                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
366                 self.QueueRefresh()
367
368         def _onRunSlicer(self, e):
369                 if self._isSimpleMode:
370                         self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
371                 self._slicer.runSlicer(self._scene)
372                 if self._isSimpleMode:
373                         profile.resetTempOverride()
374
375         def _updateSliceProgress(self, progressValue, ready):
376                 self.printButton.setDisabled(not ready)
377                 if progressValue >= 0.0:
378                         self.printButton.setProgressBar(progressValue)
379                 else:
380                         self.printButton.setProgressBar(None)
381                 if self._gcode is not None:
382                         self._gcode = None
383                         for layerVBOlist in self._gcodeVBOs:
384                                 for vbo in layerVBOlist:
385                                         self.glReleaseList.append(vbo)
386                         self._gcodeVBOs = []
387                 if ready:
388                         self.printButton.setProgressBar(None)
389                         cost = self._slicer.getFilamentCost()
390                         if cost is not None:
391                                 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
392                         else:
393                                 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
394                         self._gcode = gcodeInterpreter.gcode()
395                         self._gcode.progressCallback = self._gcodeLoadCallback
396                         self._thread = threading.Thread(target=self._loadGCode)
397                         self._thread.daemon = True
398                         self._thread.start()
399                 else:
400                         self.printButton.setBottomText('')
401                 self.QueueRefresh()
402
403         def _loadGCode(self):
404                 self._gcode.load(self._slicer.getGCodeFilename())
405
406         def _gcodeLoadCallback(self, progress):
407                 if self._gcode is None:
408                         return True
409                 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
410                         self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
411                         self.layerSelect.setValue(self.layerSelect.getMaxValue())
412                 else:
413                         self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
414                 return False
415
416         def loadScene(self, fileList):
417                 for filename in fileList:
418                         try:
419                                 objList = meshLoader.loadMeshes(filename)
420                         except:
421                                 traceback.print_exc()
422                         else:
423                                 for obj in objList:
424                                         obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
425                                         self._scene.add(obj)
426                                         self._scene.centerAll()
427                                         self._selectObject(obj)
428                 self.sceneUpdated()
429
430         def _deleteObject(self, obj):
431                 if obj == self._selectedObj:
432                         self._selectObject(None)
433                 if obj == self._focusObj:
434                         self._focusObj = None
435                 self._scene.remove(obj)
436                 for m in obj._meshList:
437                         if m.vbo is not None and m.vbo.decRef():
438                                 self.glReleaseList.append(m.vbo)
439                 import gc
440                 gc.collect()
441                 self.sceneUpdated()
442
443         def _selectObject(self, obj, zoom = True):
444                 if obj != self._selectedObj:
445                         self._selectedObj = obj
446                         self.updateProfileToControls()
447                         self.updateToolButtons()
448                 if zoom and obj is not None:
449                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
450                         self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
451                         newZoom = obj.getBoundaryCircle() * 6
452                         if newZoom > numpy.max(self._machineSize) * 3:
453                                 newZoom = numpy.max(self._machineSize) * 3
454                         self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
455
456         def updateProfileToControls(self):
457                 oldSimpleMode = self._isSimpleMode
458                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
459                 if self._isSimpleMode and not oldSimpleMode:
460                         self._scene.arrangeAll()
461                         self.sceneUpdated()
462                 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
463                 self._objColors[0] = profile.getPreferenceColour('model_colour')
464                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
465                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
466                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
467                 self._scene.setMachineSize(self._machineSize)
468                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
469                 self._scene.setHeadSize(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_min_y'), profile.getPreferenceFloat('extruder_head_size_max_y'), profile.getPreferenceFloat('extruder_head_size_height'))
470
471                 if self._selectedObj is not None:
472                         scale = self._selectedObj.getScale()
473                         size = self._selectedObj.getSize()
474                         self.scaleXctrl.setValue(round(scale[0], 2))
475                         self.scaleYctrl.setValue(round(scale[1], 2))
476                         self.scaleZctrl.setValue(round(scale[2], 2))
477                         self.scaleXmmctrl.setValue(round(size[0], 2))
478                         self.scaleYmmctrl.setValue(round(size[1], 2))
479                         self.scaleZmmctrl.setValue(round(size[2], 2))
480
481         def OnKeyChar(self, keyCode):
482                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
483                         if self._selectedObj is not None:
484                                 self._deleteObject(self._selectedObj)
485                                 self.QueueRefresh()
486                 if keyCode == wx.WXK_UP:
487                         self.layerSelect.setValue(self.layerSelect.getValue() + 1)
488                         self.QueueRefresh()
489                 elif keyCode == wx.WXK_DOWN:
490                         self.layerSelect.setValue(self.layerSelect.getValue() - 1)
491                         self.QueueRefresh()
492                 elif keyCode == wx.WXK_PAGEUP:
493                         self.layerSelect.setValue(self.layerSelect.getValue() + 10)
494                         self.QueueRefresh()
495                 elif keyCode == wx.WXK_PAGEDOWN:
496                         self.layerSelect.setValue(self.layerSelect.getValue() - 10)
497                         self.QueueRefresh()
498
499                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
500                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
501                 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
502                         from collections import defaultdict
503                         from gc import get_objects
504                         self._beforeLeakTest = defaultdict(int)
505                         for i in get_objects():
506                                 self._beforeLeakTest[type(i)] += 1
507                 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
508                         from collections import defaultdict
509                         from gc import get_objects
510                         self._afterLeakTest = defaultdict(int)
511                         for i in get_objects():
512                                 self._afterLeakTest[type(i)] += 1
513                         for k in self._afterLeakTest:
514                                 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
515                                         print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
516
517         def ShaderUpdate(self, v, f):
518                 s = opengl.GLShader(v, f)
519                 if s.isValid():
520                         self._objectLoadShader.release()
521                         self._objectLoadShader = s
522                         for obj in self._scene.objects():
523                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
524                         self.QueueRefresh()
525
526         def OnMouseDown(self,e):
527                 self._mouseX = e.GetX()
528                 self._mouseY = e.GetY()
529                 self._mouseClick3DPos = self._mouse3Dpos
530                 self._mouseClickFocus = self._focusObj
531                 if e.ButtonDClick():
532                         self._mouseState = 'doubleClick'
533                 else:
534                         self._mouseState = 'dragOrClick'
535                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
536                 p0 -= self.getObjectCenterPos() - self._viewTarget
537                 p1 -= self.getObjectCenterPos() - self._viewTarget
538                 if self.tool.OnDragStart(p0, p1):
539                         self._mouseState = 'tool'
540                 if self._mouseState == 'dragOrClick':
541                         if e.GetButton() == 1:
542                                 if self._focusObj is not None:
543                                         self._selectObject(self._focusObj, False)
544                                         self.QueueRefresh()
545
546         def OnMouseUp(self, e):
547                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
548                         return
549                 if self._mouseState == 'dragOrClick':
550                         if e.GetButton() == 1:
551                                 self._selectObject(self._focusObj)
552                         if e.GetButton() == 3:
553                                         menu = wx.Menu()
554                                         if self._focusObj is not None:
555                                                 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
556                                                 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
557                                                 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
558                                         if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
559                                                 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
560                                         if len(self._scene.objects()) > 0:
561                                                 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
562                                         if menu.MenuItemCount > 0:
563                                                 self.PopupMenu(menu)
564                                         menu.Destroy()
565                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
566                         self._scene.pushFree()
567                         self.sceneUpdated()
568                 elif self._mouseState == 'tool':
569                         if self.tempMatrix is not None and self._selectedObj is not None:
570                                 self._selectedObj.applyMatrix(self.tempMatrix)
571                                 self._scene.pushFree()
572                                 self._selectObject(self._selectedObj)
573                         self.tempMatrix = None
574                         self.tool.OnDragEnd()
575                         self.sceneUpdated()
576                 self._mouseState = None
577
578         def OnMouseMotion(self,e):
579                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
580                 p0 -= self.getObjectCenterPos() - self._viewTarget
581                 p1 -= self.getObjectCenterPos() - self._viewTarget
582
583                 if e.Dragging() and self._mouseState is not None:
584                         if self._mouseState == 'tool':
585                                 self.tool.OnDrag(p0, p1)
586                         elif not e.LeftIsDown() and e.RightIsDown():
587                                 self._mouseState = 'drag'
588                                 if wx.GetKeyState(wx.WXK_SHIFT):
589                                         a = math.cos(math.radians(self._yaw)) / 3.0
590                                         b = math.sin(math.radians(self._yaw)) / 3.0
591                                         self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
592                                         self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
593                                         self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
594                                         self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
595                                 else:
596                                         self._yaw += e.GetX() - self._mouseX
597                                         self._pitch -= e.GetY() - self._mouseY
598                                 if self._pitch > 170:
599                                         self._pitch = 170
600                                 if self._pitch < 10:
601                                         self._pitch = 10
602                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
603                                 self._mouseState = 'drag'
604                                 self._zoom += e.GetY() - self._mouseY
605                                 if self._zoom < 1:
606                                         self._zoom = 1
607                                 if self._zoom > numpy.max(self._machineSize) * 3:
608                                         self._zoom = numpy.max(self._machineSize) * 3
609                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
610                                 self._mouseState = 'dragObject'
611                                 z = max(0, self._mouseClick3DPos[2])
612                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
613                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
614                                 p0[2] -= z
615                                 p1[2] -= z
616                                 p2[2] -= z
617                                 p3[2] -= z
618                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
619                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
620                                 diff = cursorZ1 - cursorZ0
621                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
622                 if not e.Dragging() or self._mouseState != 'tool':
623                         self.tool.OnMouseMove(p0, p1)
624
625                 self._mouseX = e.GetX()
626                 self._mouseY = e.GetY()
627
628         def OnMouseWheel(self, e):
629                 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
630                 delta = max(min(delta,4),-4)
631                 self._zoom *= 1.0 - delta / 10.0
632                 if self._zoom < 1.0:
633                         self._zoom = 1.0
634                 if self._zoom > numpy.max(self._machineSize) * 3:
635                         self._zoom = numpy.max(self._machineSize) * 3
636                 self.Refresh()
637
638         def getMouseRay(self, x, y):
639                 if self._viewport is None:
640                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
641                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
642                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
643                 p0 -= self._viewTarget
644                 p1 -= self._viewTarget
645                 return p0, p1
646
647         def _init3DView(self):
648                 # set viewing projection
649                 size = self.GetSize()
650                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
651                 glLoadIdentity()
652
653                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
654
655                 glDisable(GL_RESCALE_NORMAL)
656                 glDisable(GL_LIGHTING)
657                 glDisable(GL_LIGHT0)
658                 glEnable(GL_DEPTH_TEST)
659                 glDisable(GL_CULL_FACE)
660                 glDisable(GL_BLEND)
661                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
662
663                 glClearColor(0.8, 0.8, 0.8, 1.0)
664                 glClearStencil(0)
665                 glClearDepth(1.0)
666
667                 glMatrixMode(GL_PROJECTION)
668                 glLoadIdentity()
669                 aspect = float(size.GetWidth()) / float(size.GetHeight())
670                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
671
672                 glMatrixMode(GL_MODELVIEW)
673                 glLoadIdentity()
674                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
675
676         def OnPaint(self,e):
677                 if machineCom.machineIsConnected():
678                         self.printButton._imageID = 6
679                         self.printButton._tooltip = 'Print'
680                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
681                         self.printButton._imageID = 2
682                         self.printButton._tooltip = 'Toolpath to SD'
683                 else:
684                         self.printButton._imageID = 3
685                         self.printButton._tooltip = 'Save toolpath'
686
687                 if self._animView is not None:
688                         self._viewTarget = self._animView.getPosition()
689                         if self._animView.isDone():
690                                 self._animView = None
691                 if self._animZoom is not None:
692                         self._zoom = self._animZoom.getPosition()
693                         if self._animZoom.isDone():
694                                 self._animZoom = None
695                 if self.viewMode == 'gcode' and self._gcode is not None:
696                         try:
697                                 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1].points[0][2]
698                         except:
699                                 pass
700                 if self._objectShader is None:
701                         self._objectShader = opengl.GLShader("""
702 varying float light_amount;
703
704 void main(void)
705 {
706     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
707     gl_FrontColor = gl_Color;
708
709         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
710         light_amount += 0.2;
711 }
712                         ""","""
713 varying float light_amount;
714
715 void main(void)
716 {
717         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
718 }
719                         """)
720                         self._objectLoadShader = opengl.GLShader("""
721 uniform float intensity;
722 uniform float scale;
723 varying float light_amount;
724
725 void main(void)
726 {
727         vec4 tmp = gl_Vertex;
728     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
729     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
730     gl_Position = gl_ModelViewProjectionMatrix * tmp;
731     gl_FrontColor = gl_Color;
732
733         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
734         light_amount += 0.2;
735 }
736                         ""","""
737 uniform float intensity;
738 varying float light_amount;
739
740 void main(void)
741 {
742         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
743 }
744                         """)
745                 self._init3DView()
746                 glTranslate(0,0,-self._zoom)
747                 glRotate(-self._pitch, 1,0,0)
748                 glRotate(self._yaw, 0,0,1)
749                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
750
751                 self._viewport = glGetIntegerv(GL_VIEWPORT)
752                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
753                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
754
755                 glClearColor(1,1,1,1)
756                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
757
758                 if self.viewMode != 'gcode':
759                         for n in xrange(0, len(self._scene.objects())):
760                                 obj = self._scene.objects()[n]
761                                 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
762                                 self._renderObject(obj)
763
764                 if self._mouseX > -1:
765                         glFlush()
766                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
767                         if n < len(self._scene.objects()):
768                                 self._focusObj = self._scene.objects()[n]
769                         else:
770                                 self._focusObj = None
771                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
772                         #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
773                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
774                         self._mouse3Dpos -= self._viewTarget
775
776                 self._init3DView()
777                 glTranslate(0,0,-self._zoom)
778                 glRotate(-self._pitch, 1,0,0)
779                 glRotate(self._yaw, 0,0,1)
780                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
781
782                 if self.viewMode == 'gcode':
783                         if self._gcode is not None:
784                                 glPushMatrix()
785                                 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
786                                 t = time.time()
787                                 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
788                                 for n in xrange(0, drawUpTill):
789                                         c = 1.0 - float(drawUpTill - n) / 15
790                                         c = max(0.3, c)
791                                         if len(self._gcodeVBOs) < n + 1:
792                                                 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
793                                                 if time.time() - t > 0.5:
794                                                         self.QueueRefresh()
795                                                         break
796                                         #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
797                                         if n == drawUpTill - 1:
798                                                 if len(self._gcodeVBOs[n]) < 6:
799                                                         self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
800                                                 glColor3f(c, 0, 0)
801                                                 self._gcodeVBOs[n][5].render(GL_QUADS)
802                                                 glColor3f(0, c, 0)
803                                                 self._gcodeVBOs[n][6].render(GL_QUADS)
804                                                 glColor3f(c/2, c/2, 0.0)
805                                                 self._gcodeVBOs[n][7].render(GL_QUADS)
806                                                 glColor3f(0, c, c)
807                                                 self._gcodeVBOs[n][8].render(GL_QUADS)
808                                                 self._gcodeVBOs[n][9].render(GL_QUADS)
809                                         else:
810                                                 glColor3f(c, 0, 0)
811                                                 self._gcodeVBOs[n][0].render(GL_LINES)
812                                                 glColor3f(0, c, 0)
813                                                 self._gcodeVBOs[n][1].render(GL_LINES)
814                                                 glColor3f(c/2, c/2, 0.0)
815                                                 self._gcodeVBOs[n][2].render(GL_LINES)
816                                                 glColor3f(0, c, c)
817                                                 self._gcodeVBOs[n][3].render(GL_LINES)
818                                                 self._gcodeVBOs[n][4].render(GL_LINES)
819                                 glPopMatrix()
820                 else:
821                         glStencilFunc(GL_ALWAYS, 1, 1)
822                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
823
824                         self._objectShader.bind()
825                         for obj in self._scene.objects():
826                                 if obj._loadAnim is not None:
827                                         if obj._loadAnim.isDone():
828                                                 obj._loadAnim = None
829                                         else:
830                                                 continue
831                                 brightness = 1.0
832                                 if self._focusObj == obj:
833                                         brightness = 1.2
834                                 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
835                                         brightness = 0.8
836
837                                 if self._selectedObj == obj or self._selectedObj is None:
838                                         #If we want transparent, then first render a solid black model to remove the printer size lines.
839                                         if self.viewMode == 'transparent':
840                                                 glColor4f(0, 0, 0, 0)
841                                                 self._renderObject(obj)
842                                                 glEnable(GL_BLEND)
843                                                 glBlendFunc(GL_ONE, GL_ONE)
844                                                 glDisable(GL_DEPTH_TEST)
845                                                 brightness *= 0.5
846                                         if self.viewMode == 'xray':
847                                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
848                                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
849                                         glEnable(GL_STENCIL_TEST)
850
851                                 if not self._scene.checkPlatform(obj):
852                                         glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
853                                         self._renderObject(obj)
854                                 else:
855                                         self._renderObject(obj, brightness)
856                                 glDisable(GL_STENCIL_TEST)
857                                 glDisable(GL_BLEND)
858                                 glEnable(GL_DEPTH_TEST)
859                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
860
861                         if self.viewMode == 'xray':
862                                 glPushMatrix()
863                                 glLoadIdentity()
864                                 glEnable(GL_STENCIL_TEST)
865                                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
866                                 glDisable(GL_DEPTH_TEST)
867                                 for i in xrange(2, 15, 2):
868                                         glStencilFunc(GL_EQUAL, i, 0xFF)
869                                         glColor(float(i)/10, float(i)/10, float(i)/5)
870                                         glBegin(GL_QUADS)
871                                         glVertex3f(-1000,-1000,-10)
872                                         glVertex3f( 1000,-1000,-10)
873                                         glVertex3f( 1000, 1000,-10)
874                                         glVertex3f(-1000, 1000,-10)
875                                         glEnd()
876                                 for i in xrange(1, 15, 2):
877                                         glStencilFunc(GL_EQUAL, i, 0xFF)
878                                         glColor(float(i)/10, 0, 0)
879                                         glBegin(GL_QUADS)
880                                         glVertex3f(-1000,-1000,-10)
881                                         glVertex3f( 1000,-1000,-10)
882                                         glVertex3f( 1000, 1000,-10)
883                                         glVertex3f(-1000, 1000,-10)
884                                         glEnd()
885                                 glPopMatrix()
886                                 glDisable(GL_STENCIL_TEST)
887                                 glEnable(GL_DEPTH_TEST)
888
889                         self._objectShader.unbind()
890
891                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
892                         glEnable(GL_BLEND)
893                         self._objectLoadShader.bind()
894                         glColor4f(0.2, 0.6, 1.0, 1.0)
895                         for obj in self._scene.objects():
896                                 if obj._loadAnim is None:
897                                         continue
898                                 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
899                                 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
900                                 self._renderObject(obj)
901                         self._objectLoadShader.unbind()
902                         glDisable(GL_BLEND)
903
904                 self._drawMachine()
905
906                 if self.viewMode == 'gcode':
907                         pass
908                 else:
909                         #Draw the object box-shadow, so you can see where it will collide with other objects.
910                         if self._selectedObj is not None and len(self._scene.objects()) > 1:
911                                 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
912                                 glPushMatrix()
913                                 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
914                                 glEnable(GL_BLEND)
915                                 glEnable(GL_CULL_FACE)
916                                 glColor4f(0,0,0,0.12)
917                                 glBegin(GL_QUADS)
918                                 glVertex3f(-size[0],  size[1], 0.1)
919                                 glVertex3f(-size[0], -size[1], 0.1)
920                                 glVertex3f( size[0], -size[1], 0.1)
921                                 glVertex3f( size[0],  size[1], 0.1)
922                                 glEnd()
923                                 glDisable(GL_CULL_FACE)
924                                 glPopMatrix()
925
926                         #Draw the outline of the selected object, on top of everything else except the GUI.
927                         if self._selectedObj is not None and self._selectedObj._loadAnim is None:
928                                 glDisable(GL_DEPTH_TEST)
929                                 glEnable(GL_CULL_FACE)
930                                 glEnable(GL_STENCIL_TEST)
931                                 glDisable(GL_BLEND)
932                                 glStencilFunc(GL_EQUAL, 0, 255)
933
934                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
935                                 glLineWidth(2)
936                                 glColor4f(1,1,1,0.5)
937                                 self._renderObject(self._selectedObj)
938                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
939
940                                 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
941                                 glDisable(GL_STENCIL_TEST)
942                                 glDisable(GL_CULL_FACE)
943                                 glEnable(GL_DEPTH_TEST)
944
945                         if self._selectedObj is not None:
946                                 glPushMatrix()
947                                 pos = self.getObjectCenterPos()
948                                 glTranslate(pos[0], pos[1], pos[2])
949                                 self.tool.OnDraw()
950                                 glPopMatrix()
951
952         def _renderObject(self, obj, brightness = False):
953                 glPushMatrix()
954                 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
955
956                 if self.tempMatrix is not None and obj == self._selectedObj:
957                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
958                         glMultMatrixf(tempMatrix)
959
960                 offset = obj.getDrawOffset()
961                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
962
963                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
964                 glMultMatrixf(tempMatrix)
965
966                 n = 0
967                 for m in obj._meshList:
968                         if m.vbo is None:
969                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
970                         if brightness:
971                                 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
972                                 n += 1
973                         m.vbo.render()
974                 glPopMatrix()
975
976         def _drawMachine(self):
977                 glEnable(GL_CULL_FACE)
978                 glEnable(GL_BLEND)
979
980                 if profile.getPreference('machine_type') == 'ultimaker':
981                         glColor4f(1,1,1,0.5)
982                         self._objectShader.bind()
983                         self._renderObject(self._platformMesh)
984                         self._objectShader.unbind()
985
986                 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
987                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
988                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
989                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
990                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
991                 v4 = [ size[0] / 2, size[1] / 2, 0]
992                 v5 = [ size[0] / 2,-size[1] / 2, 0]
993                 v6 = [-size[0] / 2, size[1] / 2, 0]
994                 v7 = [-size[0] / 2,-size[1] / 2, 0]
995
996                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
997                 glEnableClientState(GL_VERTEX_ARRAY)
998                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
999
1000                 glColor4ub(5, 171, 231, 64)
1001                 glDrawArrays(GL_QUADS, 0, 4)
1002                 glColor4ub(5, 171, 231, 96)
1003                 glDrawArrays(GL_QUADS, 4, 8)
1004                 glColor4ub(5, 171, 231, 128)
1005                 glDrawArrays(GL_QUADS, 12, 8)
1006
1007                 sx = self._machineSize[0]
1008                 sy = self._machineSize[1]
1009                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1010                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1011                                 x1 = x * 10
1012                                 x2 = x1 + 10
1013                                 y1 = y * 10
1014                                 y2 = y1 + 10
1015                                 x1 = max(min(x1, sx/2), -sx/2)
1016                                 y1 = max(min(y1, sy/2), -sy/2)
1017                                 x2 = max(min(x2, sx/2), -sx/2)
1018                                 y2 = max(min(y2, sy/2), -sy/2)
1019                                 if (x & 1) == (y & 1):
1020                                         glColor4ub(5, 171, 231, 127)
1021                                 else:
1022                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1023                                 glBegin(GL_QUADS)
1024                                 glVertex3f(x1, y1, -0.02)
1025                                 glVertex3f(x2, y1, -0.02)
1026                                 glVertex3f(x2, y2, -0.02)
1027                                 glVertex3f(x1, y2, -0.02)
1028                                 glEnd()
1029
1030                 glDisableClientState(GL_VERTEX_ARRAY)
1031                 glDisable(GL_BLEND)
1032                 glDisable(GL_CULL_FACE)
1033
1034         def _generateGCodeVBOs(self, layer):
1035                 ret = []
1036                 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1037                         pointList = numpy.zeros((0,3), numpy.float32)
1038                         for path in layer:
1039                                 if path.type == 'extrude' and path.pathType == extrudeType:
1040                                         a = path.points
1041                                         a = numpy.concatenate((a[:-1], a[1:]), 1)
1042                                         a = a.reshape((len(a) * 2, 3))
1043                                         pointList = numpy.concatenate((pointList, a))
1044                         ret.append(opengl.GLVBO(pointList))
1045                 return ret
1046
1047         def _generateGCodeVBOs2(self, layer):
1048                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1049                 filamentArea = math.pi * filamentRadius * filamentRadius
1050
1051                 ret = []
1052                 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1053                         pointList = numpy.zeros((0,3), numpy.float32)
1054                         for path in layer:
1055                                 if path.type == 'extrude' and path.pathType == extrudeType:
1056                                         a = path.points
1057                                         if extrudeType == 'FILL':
1058                                                 a[:,2] += 0.01
1059
1060                                         normal = a[1:] - a[:-1]
1061                                         lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1062                                         normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1063                                         normal[:,2] /= lens
1064
1065                                         ePerDist = path.extrusion[1:] / lens
1066                                         lineWidth = ePerDist * (filamentArea / path.layerThickness / 2)
1067
1068                                         normal[:,0] *= lineWidth
1069                                         normal[:,1] *= lineWidth
1070
1071                                         b = numpy.zeros((len(a)-1, 0), numpy.float32)
1072                                         b = numpy.concatenate((b, a[:-1] + normal), 1)
1073                                         b = numpy.concatenate((b, a[1:] + normal), 1)
1074                                         b = numpy.concatenate((b, a[1:] - normal), 1)
1075                                         b = numpy.concatenate((b, a[:-1] - normal), 1)
1076                                         b = b.reshape((len(b) * 4, 3))
1077
1078                                         pointList = numpy.concatenate((pointList, b))
1079                         ret.append(opengl.GLVBO(pointList))
1080                 return ret
1081
1082         def getObjectCenterPos(self):
1083                 if self._selectedObj is None:
1084                         return [0.0, 0.0, 0.0]
1085                 pos = self._selectedObj.getPosition()
1086                 size = self._selectedObj.getSize()
1087                 return [pos[0], pos[1], size[2]/2]
1088
1089         def getObjectBoundaryCircle(self):
1090                 if self._selectedObj is None:
1091                         return 0.0
1092                 return self._selectedObj.getBoundaryCircle()
1093
1094         def getObjectSize(self):
1095                 if self._selectedObj is None:
1096                         return [0.0, 0.0, 0.0]
1097                 return self._selectedObj.getSize()
1098
1099         def getObjectMatrix(self):
1100                 if self._selectedObj is None:
1101                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1102                 return self._selectedObj.getMatrix()
1103
1104 class shaderEditor(wx.Dialog):
1105         def __init__(self, parent, callback, v, f):
1106                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1107                 self._callback = callback
1108                 s = wx.BoxSizer(wx.VERTICAL)
1109                 self.SetSizer(s)
1110                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1111                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1112                 s.Add(self._vertex, 1, flag=wx.EXPAND)
1113                 s.Add(self._fragment, 1, flag=wx.EXPAND)
1114
1115                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1116                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1117
1118                 self.SetPosition(self.GetParent().GetPosition())
1119                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1120                 self.Show()
1121
1122         def OnText(self, e):
1123                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())