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