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