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