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