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