chiark / gitweb /
Update to the bed leveling wizard to allow insertion of filament, and fixing the...
[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,1.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))
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                 if self._isSimpleMode:
442                         self._scene.arrangeAll()
443                 self.sceneUpdated()
444
445         def _selectObject(self, obj, zoom = True):
446                 if obj != self._selectedObj:
447                         self._selectedObj = obj
448                         self.updateProfileToControls()
449                         self.updateToolButtons()
450                 if zoom and obj is not None:
451                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
452                         self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
453                         newZoom = obj.getBoundaryCircle() * 6
454                         if newZoom > numpy.max(self._machineSize) * 3:
455                                 newZoom = numpy.max(self._machineSize) * 3
456                         self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
457
458         def updateProfileToControls(self):
459                 oldSimpleMode = self._isSimpleMode
460                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
461                 if self._isSimpleMode and not oldSimpleMode:
462                         self._scene.arrangeAll()
463                         self.sceneUpdated()
464                 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
465                 self._objColors[0] = profile.getPreferenceColour('model_colour')
466                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
467                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
468                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
469                 self._scene.setMachineSize(self._machineSize)
470                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
471                 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'))
472
473                 if self._selectedObj is not None:
474                         scale = self._selectedObj.getScale()
475                         size = self._selectedObj.getSize()
476                         self.scaleXctrl.setValue(round(scale[0], 2))
477                         self.scaleYctrl.setValue(round(scale[1], 2))
478                         self.scaleZctrl.setValue(round(scale[2], 2))
479                         self.scaleXmmctrl.setValue(round(size[0], 2))
480                         self.scaleYmmctrl.setValue(round(size[1], 2))
481                         self.scaleZmmctrl.setValue(round(size[2], 2))
482
483         def OnKeyChar(self, keyCode):
484                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
485                         if self._selectedObj is not None:
486                                 self._deleteObject(self._selectedObj)
487                                 self.QueueRefresh()
488                 if keyCode == wx.WXK_UP:
489                         self.layerSelect.setValue(self.layerSelect.getValue() + 1)
490                         self.QueueRefresh()
491                 elif keyCode == wx.WXK_DOWN:
492                         self.layerSelect.setValue(self.layerSelect.getValue() - 1)
493                         self.QueueRefresh()
494                 elif keyCode == wx.WXK_PAGEUP:
495                         self.layerSelect.setValue(self.layerSelect.getValue() + 10)
496                         self.QueueRefresh()
497                 elif keyCode == wx.WXK_PAGEDOWN:
498                         self.layerSelect.setValue(self.layerSelect.getValue() - 10)
499                         self.QueueRefresh()
500
501                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
502                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
503                 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
504                         from collections import defaultdict
505                         from gc import get_objects
506                         self._beforeLeakTest = defaultdict(int)
507                         for i in get_objects():
508                                 self._beforeLeakTest[type(i)] += 1
509                 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
510                         from collections import defaultdict
511                         from gc import get_objects
512                         self._afterLeakTest = defaultdict(int)
513                         for i in get_objects():
514                                 self._afterLeakTest[type(i)] += 1
515                         for k in self._afterLeakTest:
516                                 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
517                                         print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
518
519         def ShaderUpdate(self, v, f):
520                 s = opengl.GLShader(v, f)
521                 if s.isValid():
522                         self._objectLoadShader.release()
523                         self._objectLoadShader = s
524                         for obj in self._scene.objects():
525                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
526                         self.QueueRefresh()
527
528         def OnMouseDown(self,e):
529                 self._mouseX = e.GetX()
530                 self._mouseY = e.GetY()
531                 self._mouseClick3DPos = self._mouse3Dpos
532                 self._mouseClickFocus = self._focusObj
533                 if e.ButtonDClick():
534                         self._mouseState = 'doubleClick'
535                 else:
536                         self._mouseState = 'dragOrClick'
537                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
538                 p0 -= self.getObjectCenterPos() - self._viewTarget
539                 p1 -= self.getObjectCenterPos() - self._viewTarget
540                 if self.tool.OnDragStart(p0, p1):
541                         self._mouseState = 'tool'
542                 if self._mouseState == 'dragOrClick':
543                         if e.GetButton() == 1:
544                                 if self._focusObj is not None:
545                                         self._selectObject(self._focusObj, False)
546                                         self.QueueRefresh()
547
548         def OnMouseUp(self, e):
549                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
550                         return
551                 if self._mouseState == 'dragOrClick':
552                         if e.GetButton() == 1:
553                                 self._selectObject(self._focusObj)
554                         if e.GetButton() == 3:
555                                         menu = wx.Menu()
556                                         if self._focusObj is not None:
557                                                 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
558                                                 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
559                                                 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
560                                         if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
561                                                 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
562                                         if len(self._scene.objects()) > 0:
563                                                 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
564                                         if menu.MenuItemCount > 0:
565                                                 self.PopupMenu(menu)
566                                         menu.Destroy()
567                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
568                         self._scene.pushFree()
569                         self.sceneUpdated()
570                 elif self._mouseState == 'tool':
571                         if self.tempMatrix is not None and self._selectedObj is not None:
572                                 self._selectedObj.applyMatrix(self.tempMatrix)
573                                 self._scene.pushFree()
574                                 self._selectObject(self._selectedObj)
575                         self.tempMatrix = None
576                         self.tool.OnDragEnd()
577                         self.sceneUpdated()
578                 self._mouseState = None
579
580         def OnMouseMotion(self,e):
581                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
582                 p0 -= self.getObjectCenterPos() - self._viewTarget
583                 p1 -= self.getObjectCenterPos() - self._viewTarget
584
585                 if e.Dragging() and self._mouseState is not None:
586                         if self._mouseState == 'tool':
587                                 self.tool.OnDrag(p0, p1)
588                         elif not e.LeftIsDown() and e.RightIsDown():
589                                 self._mouseState = 'drag'
590                                 if wx.GetKeyState(wx.WXK_SHIFT):
591                                         a = math.cos(math.radians(self._yaw)) / 3.0
592                                         b = math.sin(math.radians(self._yaw)) / 3.0
593                                         self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
594                                         self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
595                                         self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
596                                         self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
597                                 else:
598                                         self._yaw += e.GetX() - self._mouseX
599                                         self._pitch -= e.GetY() - self._mouseY
600                                 if self._pitch > 170:
601                                         self._pitch = 170
602                                 if self._pitch < 10:
603                                         self._pitch = 10
604                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
605                                 self._mouseState = 'drag'
606                                 self._zoom += e.GetY() - self._mouseY
607                                 if self._zoom < 1:
608                                         self._zoom = 1
609                                 if self._zoom > numpy.max(self._machineSize) * 3:
610                                         self._zoom = numpy.max(self._machineSize) * 3
611                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
612                                 self._mouseState = 'dragObject'
613                                 z = max(0, self._mouseClick3DPos[2])
614                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
615                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
616                                 p0[2] -= z
617                                 p1[2] -= z
618                                 p2[2] -= z
619                                 p3[2] -= z
620                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
621                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
622                                 diff = cursorZ1 - cursorZ0
623                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
624                 if not e.Dragging() or self._mouseState != 'tool':
625                         self.tool.OnMouseMove(p0, p1)
626
627                 self._mouseX = e.GetX()
628                 self._mouseY = e.GetY()
629
630         def OnMouseWheel(self, e):
631                 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
632                 delta = max(min(delta,4),-4)
633                 self._zoom *= 1.0 - delta / 10.0
634                 if self._zoom < 1.0:
635                         self._zoom = 1.0
636                 if self._zoom > numpy.max(self._machineSize) * 3:
637                         self._zoom = numpy.max(self._machineSize) * 3
638                 self.Refresh()
639
640         def getMouseRay(self, x, y):
641                 if self._viewport is None:
642                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
643                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
644                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
645                 p0 -= self._viewTarget
646                 p1 -= self._viewTarget
647                 return p0, p1
648
649         def _init3DView(self):
650                 # set viewing projection
651                 size = self.GetSize()
652                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
653                 glLoadIdentity()
654
655                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
656
657                 glDisable(GL_RESCALE_NORMAL)
658                 glDisable(GL_LIGHTING)
659                 glDisable(GL_LIGHT0)
660                 glEnable(GL_DEPTH_TEST)
661                 glDisable(GL_CULL_FACE)
662                 glDisable(GL_BLEND)
663                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
664
665                 glClearColor(0.8, 0.8, 0.8, 1.0)
666                 glClearStencil(0)
667                 glClearDepth(1.0)
668
669                 glMatrixMode(GL_PROJECTION)
670                 glLoadIdentity()
671                 aspect = float(size.GetWidth()) / float(size.GetHeight())
672                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
673
674                 glMatrixMode(GL_MODELVIEW)
675                 glLoadIdentity()
676                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
677
678         def OnPaint(self,e):
679                 if machineCom.machineIsConnected():
680                         self.printButton._imageID = 6
681                         self.printButton._tooltip = 'Print'
682                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
683                         self.printButton._imageID = 2
684                         self.printButton._tooltip = 'Toolpath to SD'
685                 else:
686                         self.printButton._imageID = 3
687                         self.printButton._tooltip = 'Save toolpath'
688
689                 if self._animView is not None:
690                         self._viewTarget = self._animView.getPosition()
691                         if self._animView.isDone():
692                                 self._animView = None
693                 if self._animZoom is not None:
694                         self._zoom = self._animZoom.getPosition()
695                         if self._animZoom.isDone():
696                                 self._animZoom = None
697                 if self._objectShader is None:
698                         self._objectShader = opengl.GLShader("""
699 varying float light_amount;
700
701 void main(void)
702 {
703     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
704     gl_FrontColor = gl_Color;
705
706         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
707         light_amount += 0.2;
708 }
709                         ""","""
710 varying float light_amount;
711
712 void main(void)
713 {
714         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
715 }
716                         """)
717                         self._objectLoadShader = opengl.GLShader("""
718 uniform float intensity;
719 uniform float scale;
720 varying float light_amount;
721
722 void main(void)
723 {
724         vec4 tmp = gl_Vertex;
725     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
726     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
727     gl_Position = gl_ModelViewProjectionMatrix * tmp;
728     gl_FrontColor = gl_Color;
729
730         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
731         light_amount += 0.2;
732 }
733                         ""","""
734 uniform float intensity;
735 varying float light_amount;
736
737 void main(void)
738 {
739         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
740 }
741                         """)
742                 self._init3DView()
743                 glTranslate(0,0,-self._zoom)
744                 glRotate(-self._pitch, 1,0,0)
745                 glRotate(self._yaw, 0,0,1)
746                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
747
748                 self._viewport = glGetIntegerv(GL_VIEWPORT)
749                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
750                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
751
752                 glClearColor(1,1,1,1)
753                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
754
755                 if self.viewMode != 'gcode':
756                         for n in xrange(0, len(self._scene.objects())):
757                                 obj = self._scene.objects()[n]
758                                 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
759                                 self._renderObject(obj)
760
761                 if self._mouseX > -1:
762                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
763                         if n < len(self._scene.objects()):
764                                 self._focusObj = self._scene.objects()[n]
765                         else:
766                                 self._focusObj = None
767                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
768                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
769                         self._mouse3Dpos -= self._viewTarget
770
771                 self._init3DView()
772                 glTranslate(0,0,-self._zoom)
773                 glRotate(-self._pitch, 1,0,0)
774                 glRotate(self._yaw, 0,0,1)
775                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
776
777                 if self.viewMode == 'gcode':
778                         if self._gcode is not None:
779                                 glPushMatrix()
780                                 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
781                                 t = time.time()
782                                 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
783                                 for n in xrange(0, drawUpTill):
784                                         c = 1.0 - float(drawUpTill - n) / 15
785                                         c = max(0.3, c)
786                                         if len(self._gcodeVBOs) < n + 1:
787                                                 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
788                                                 if time.time() - t > 0.5:
789                                                         self.QueueRefresh()
790                                                         break
791                                         #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
792                                         if n == drawUpTill - 1:
793                                                 if len(self._gcodeVBOs[n]) < 6:
794                                                         self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
795                                                 glColor3f(c, 0, 0)
796                                                 self._gcodeVBOs[n][5].render(GL_QUADS)
797                                                 glColor3f(0, c, 0)
798                                                 self._gcodeVBOs[n][6].render(GL_QUADS)
799                                                 glColor3f(c/2, c/2, 0.0)
800                                                 self._gcodeVBOs[n][7].render(GL_QUADS)
801                                                 glColor3f(0, c, c)
802                                                 self._gcodeVBOs[n][8].render(GL_QUADS)
803                                                 self._gcodeVBOs[n][9].render(GL_QUADS)
804                                         else:
805                                                 glColor3f(c, 0, 0)
806                                                 self._gcodeVBOs[n][0].render(GL_LINES)
807                                                 glColor3f(0, c, 0)
808                                                 self._gcodeVBOs[n][1].render(GL_LINES)
809                                                 glColor3f(c/2, c/2, 0.0)
810                                                 self._gcodeVBOs[n][2].render(GL_LINES)
811                                                 glColor3f(0, c, c)
812                                                 self._gcodeVBOs[n][3].render(GL_LINES)
813                                                 self._gcodeVBOs[n][4].render(GL_LINES)
814                                 glPopMatrix()
815                 else:
816                         glStencilFunc(GL_ALWAYS, 1, 1)
817                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
818
819                         self._objectShader.bind()
820                         for obj in self._scene.objects():
821                                 if obj._loadAnim is not None:
822                                         if obj._loadAnim.isDone():
823                                                 obj._loadAnim = None
824                                         else:
825                                                 continue
826                                 brightness = 1.0
827                                 if self._focusObj == obj:
828                                         brightness = 1.2
829                                 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
830                                         brightness = 0.8
831
832                                 if self._selectedObj == obj or self._selectedObj is None:
833                                         #If we want transparent, then first render a solid black model to remove the printer size lines.
834                                         if self.viewMode == 'transparent':
835                                                 glColor4f(0, 0, 0, 0)
836                                                 self._renderObject(obj)
837                                                 glEnable(GL_BLEND)
838                                                 glBlendFunc(GL_ONE, GL_ONE)
839                                                 glDisable(GL_DEPTH_TEST)
840                                                 brightness *= 0.5
841                                         if self.viewMode == 'xray':
842                                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
843                                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
844                                         glEnable(GL_STENCIL_TEST)
845
846                                 if not self._scene.checkPlatform(obj):
847                                         glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
848                                         self._renderObject(obj)
849                                 else:
850                                         self._renderObject(obj, brightness)
851                                 glDisable(GL_STENCIL_TEST)
852                                 glDisable(GL_BLEND)
853                                 glEnable(GL_DEPTH_TEST)
854                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
855
856                         if self.viewMode == 'xray':
857                                 glPushMatrix()
858                                 glLoadIdentity()
859                                 glEnable(GL_STENCIL_TEST)
860                                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
861                                 glDisable(GL_DEPTH_TEST)
862                                 for i in xrange(2, 15, 2):
863                                         glStencilFunc(GL_EQUAL, i, 0xFF)
864                                         glColor(float(i)/10, float(i)/10, float(i)/5)
865                                         glBegin(GL_QUADS)
866                                         glVertex3f(-1000,-1000,-10)
867                                         glVertex3f( 1000,-1000,-10)
868                                         glVertex3f( 1000, 1000,-10)
869                                         glVertex3f(-1000, 1000,-10)
870                                         glEnd()
871                                 for i in xrange(1, 15, 2):
872                                         glStencilFunc(GL_EQUAL, i, 0xFF)
873                                         glColor(float(i)/10, 0, 0)
874                                         glBegin(GL_QUADS)
875                                         glVertex3f(-1000,-1000,-10)
876                                         glVertex3f( 1000,-1000,-10)
877                                         glVertex3f( 1000, 1000,-10)
878                                         glVertex3f(-1000, 1000,-10)
879                                         glEnd()
880                                 glPopMatrix()
881                                 glDisable(GL_STENCIL_TEST)
882                                 glEnable(GL_DEPTH_TEST)
883
884                         self._objectShader.unbind()
885
886                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
887                         glEnable(GL_BLEND)
888                         self._objectLoadShader.bind()
889                         glColor4f(0.2, 0.6, 1.0, 1.0)
890                         for obj in self._scene.objects():
891                                 if obj._loadAnim is None:
892                                         continue
893                                 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
894                                 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
895                                 self._renderObject(obj)
896                         self._objectLoadShader.unbind()
897                         glDisable(GL_BLEND)
898
899                 self._drawMachine()
900
901                 if self.viewMode == 'gcode':
902                         pass
903                 else:
904                         #Draw the object box-shadow, so you can see where it will collide with other objects.
905                         if self._selectedObj is not None and len(self._scene.objects()) > 1:
906                                 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
907                                 glPushMatrix()
908                                 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
909                                 glEnable(GL_BLEND)
910                                 glEnable(GL_CULL_FACE)
911                                 glColor4f(0,0,0,0.12)
912                                 glBegin(GL_QUADS)
913                                 glVertex3f(-size[0],  size[1], 0.1)
914                                 glVertex3f(-size[0], -size[1], 0.1)
915                                 glVertex3f( size[0], -size[1], 0.1)
916                                 glVertex3f( size[0],  size[1], 0.1)
917                                 glEnd()
918                                 glDisable(GL_CULL_FACE)
919                                 glPopMatrix()
920
921                         #Draw the outline of the selected object, on top of everything else except the GUI.
922                         if self._selectedObj is not None and self._selectedObj._loadAnim is None:
923                                 glDisable(GL_DEPTH_TEST)
924                                 glEnable(GL_CULL_FACE)
925                                 glEnable(GL_STENCIL_TEST)
926                                 glDisable(GL_BLEND)
927                                 glStencilFunc(GL_EQUAL, 0, 255)
928
929                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
930                                 glLineWidth(2)
931                                 glColor4f(1,1,1,0.5)
932                                 self._renderObject(self._selectedObj)
933                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
934
935                                 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
936                                 glDisable(GL_STENCIL_TEST)
937                                 glDisable(GL_CULL_FACE)
938                                 glEnable(GL_DEPTH_TEST)
939
940                         if self._selectedObj is not None:
941                                 glPushMatrix()
942                                 pos = self.getObjectCenterPos()
943                                 glTranslate(pos[0], pos[1], pos[2])
944                                 self.tool.OnDraw()
945                                 glPopMatrix()
946
947         def _renderObject(self, obj, brightness = False):
948                 glPushMatrix()
949                 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
950
951                 if self.tempMatrix is not None and obj == self._selectedObj:
952                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
953                         glMultMatrixf(tempMatrix)
954
955                 offset = obj.getDrawOffset()
956                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
957
958                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
959                 glMultMatrixf(tempMatrix)
960
961                 n = 0
962                 for m in obj._meshList:
963                         if m.vbo is None:
964                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
965                         if brightness:
966                                 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
967                                 n += 1
968                         m.vbo.render()
969                 glPopMatrix()
970
971         def _drawMachine(self):
972                 glEnable(GL_CULL_FACE)
973                 glEnable(GL_BLEND)
974
975                 if profile.getPreference('machine_type') == 'ultimaker':
976                         glColor4f(1,1,1,0.5)
977                         self._objectShader.bind()
978                         self._renderObject(self._platformMesh)
979                         self._objectShader.unbind()
980
981                 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
982                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
983                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
984                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
985                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
986                 v4 = [ size[0] / 2, size[1] / 2, 0]
987                 v5 = [ size[0] / 2,-size[1] / 2, 0]
988                 v6 = [-size[0] / 2, size[1] / 2, 0]
989                 v7 = [-size[0] / 2,-size[1] / 2, 0]
990
991                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
992                 glEnableClientState(GL_VERTEX_ARRAY)
993                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
994
995                 glColor4ub(5, 171, 231, 64)
996                 glDrawArrays(GL_QUADS, 0, 4)
997                 glColor4ub(5, 171, 231, 96)
998                 glDrawArrays(GL_QUADS, 4, 8)
999                 glColor4ub(5, 171, 231, 128)
1000                 glDrawArrays(GL_QUADS, 12, 8)
1001
1002                 sx = self._machineSize[0]
1003                 sy = self._machineSize[1]
1004                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1005                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1006                                 x1 = x * 10
1007                                 x2 = x1 + 10
1008                                 y1 = y * 10
1009                                 y2 = y1 + 10
1010                                 x1 = max(min(x1, sx/2), -sx/2)
1011                                 y1 = max(min(y1, sy/2), -sy/2)
1012                                 x2 = max(min(x2, sx/2), -sx/2)
1013                                 y2 = max(min(y2, sy/2), -sy/2)
1014                                 if (x & 1) == (y & 1):
1015                                         glColor4ub(5, 171, 231, 127)
1016                                 else:
1017                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1018                                 glBegin(GL_QUADS)
1019                                 glVertex3f(x1, y1, -0.02)
1020                                 glVertex3f(x2, y1, -0.02)
1021                                 glVertex3f(x2, y2, -0.02)
1022                                 glVertex3f(x1, y2, -0.02)
1023                                 glEnd()
1024
1025                 glDisableClientState(GL_VERTEX_ARRAY)
1026                 glDisable(GL_BLEND)
1027                 glDisable(GL_CULL_FACE)
1028
1029         def _generateGCodeVBOs(self, layer):
1030                 ret = []
1031                 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1032                         pointList = numpy.zeros((0,3), numpy.float32)
1033                         for path in layer:
1034                                 if path.type == 'extrude' and path.pathType == extrudeType:
1035                                         a = numpy.array(path.points, numpy.float32)
1036                                         a = numpy.concatenate((a[:-1], a[1:]), 1)
1037                                         a = a.reshape((len(a) * 2, 3))
1038                                         pointList = numpy.concatenate((pointList, a))
1039                         ret.append(opengl.GLVBO(pointList))
1040                 return ret
1041
1042         def _generateGCodeVBOs2(self, layer):
1043                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1044                 filamentArea = math.pi * filamentRadius * filamentRadius
1045
1046                 ret = []
1047                 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1048                         pointList = numpy.zeros((0,3), numpy.float32)
1049                         for path in layer:
1050                                 if path.type == 'extrude' and path.pathType == extrudeType:
1051                                         a = numpy.array(path.points, numpy.float32)
1052                                         if extrudeType == 'FILL':
1053                                                 a[:,2] += 0.01
1054
1055                                         normal = a[1:] - a[:-1]
1056                                         lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1057                                         normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1058                                         normal[:,2] /= lens
1059
1060                                         ePerDist = path.extrusion[1:] / lens
1061                                         lineWidth = ePerDist * (filamentArea / path.layerThickness / 2)
1062
1063                                         normal[:,0] *= lineWidth
1064                                         normal[:,1] *= lineWidth
1065
1066                                         b = numpy.zeros((len(a)-1, 0), numpy.float32)
1067                                         b = numpy.concatenate((b, a[:-1] + normal), 1)
1068                                         b = numpy.concatenate((b, a[1:] + normal), 1)
1069                                         b = numpy.concatenate((b, a[1:] - normal), 1)
1070                                         b = numpy.concatenate((b, a[:-1] - normal), 1)
1071                                         b = b.reshape((len(b) * 4, 3))
1072
1073                                         pointList = numpy.concatenate((pointList, b))
1074                         ret.append(opengl.GLVBO(pointList))
1075                 return ret
1076
1077         def getObjectCenterPos(self):
1078                 if self._selectedObj is None:
1079                         return [0.0, 0.0, 0.0]
1080                 pos = self._selectedObj.getPosition()
1081                 size = self._selectedObj.getSize()
1082                 return [pos[0], pos[1], size[2]/2]
1083
1084         def getObjectBoundaryCircle(self):
1085                 if self._selectedObj is None:
1086                         return 0.0
1087                 return self._selectedObj.getBoundaryCircle()
1088
1089         def getObjectSize(self):
1090                 if self._selectedObj is None:
1091                         return [0.0, 0.0, 0.0]
1092                 return self._selectedObj.getSize()
1093
1094         def getObjectMatrix(self):
1095                 if self._selectedObj is None:
1096                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1097                 return self._selectedObj.getMatrix()
1098
1099 class shaderEditor(wx.Dialog):
1100         def __init__(self, parent, callback, v, f):
1101                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1102                 self._callback = callback
1103                 s = wx.BoxSizer(wx.VERTICAL)
1104                 self.SetSizer(s)
1105                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1106                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1107                 s.Add(self._vertex, 1, flag=wx.EXPAND)
1108                 s.Add(self._fragment, 1, flag=wx.EXPAND)
1109
1110                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1111                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1112
1113                 self.SetPosition(self.GetParent().GetPosition())
1114                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1115                 self.Show()
1116
1117         def OnText(self, e):
1118                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())