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