chiark / gitweb /
Account for multiple extruders in the new object size polygons.
[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.updateProfileToControls()
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
638                 if self._selectedObj is not None:
639                         scale = self._selectedObj.getScale()
640                         size = self._selectedObj.getSize()
641                         self.scaleXctrl.setValue(round(scale[0], 2))
642                         self.scaleYctrl.setValue(round(scale[1], 2))
643                         self.scaleZctrl.setValue(round(scale[2], 2))
644                         self.scaleXmmctrl.setValue(round(size[0], 2))
645                         self.scaleYmmctrl.setValue(round(size[1], 2))
646                         self.scaleZmmctrl.setValue(round(size[2], 2))
647
648         def OnKeyChar(self, keyCode):
649                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
650                         if self._selectedObj is not None:
651                                 self._deleteObject(self._selectedObj)
652                                 self.QueueRefresh()
653                 if keyCode == wx.WXK_UP:
654                         self.layerSelect.setValue(self.layerSelect.getValue() + 1)
655                         self.QueueRefresh()
656                 elif keyCode == wx.WXK_DOWN:
657                         self.layerSelect.setValue(self.layerSelect.getValue() - 1)
658                         self.QueueRefresh()
659                 elif keyCode == wx.WXK_PAGEUP:
660                         self.layerSelect.setValue(self.layerSelect.getValue() + 10)
661                         self.QueueRefresh()
662                 elif keyCode == wx.WXK_PAGEDOWN:
663                         self.layerSelect.setValue(self.layerSelect.getValue() - 10)
664                         self.QueueRefresh()
665
666                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
667                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
668                 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
669                         from collections import defaultdict
670                         from gc import get_objects
671                         self._beforeLeakTest = defaultdict(int)
672                         for i in get_objects():
673                                 self._beforeLeakTest[type(i)] += 1
674                 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
675                         from collections import defaultdict
676                         from gc import get_objects
677                         self._afterLeakTest = defaultdict(int)
678                         for i in get_objects():
679                                 self._afterLeakTest[type(i)] += 1
680                         for k in self._afterLeakTest:
681                                 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
682                                         print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
683
684         def ShaderUpdate(self, v, f):
685                 s = opengl.GLShader(v, f)
686                 if s.isValid():
687                         self._objectLoadShader.release()
688                         self._objectLoadShader = s
689                         for obj in self._scene.objects():
690                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
691                         self.QueueRefresh()
692
693         def OnMouseDown(self,e):
694                 self._mouseX = e.GetX()
695                 self._mouseY = e.GetY()
696                 self._mouseClick3DPos = self._mouse3Dpos
697                 self._mouseClickFocus = self._focusObj
698                 if e.ButtonDClick():
699                         self._mouseState = 'doubleClick'
700                 else:
701                         self._mouseState = 'dragOrClick'
702                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
703                 p0 -= self.getObjectCenterPos() - self._viewTarget
704                 p1 -= self.getObjectCenterPos() - self._viewTarget
705                 if self.tool.OnDragStart(p0, p1):
706                         self._mouseState = 'tool'
707                 if self._mouseState == 'dragOrClick':
708                         if e.GetButton() == 1:
709                                 if self._focusObj is not None:
710                                         self._selectObject(self._focusObj, False)
711                                         self.QueueRefresh()
712
713         def OnMouseUp(self, e):
714                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
715                         return
716                 if self._mouseState == 'dragOrClick':
717                         if e.GetButton() == 1:
718                                 self._selectObject(self._focusObj)
719                         if e.GetButton() == 3:
720                                         menu = wx.Menu()
721                                         if self._focusObj is not None:
722                                                 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
723                                                 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
724                                                 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
725                                                 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
726                                         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:
727                                                 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
728                                         if len(self._scene.objects()) > 0:
729                                                 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
730                                         if menu.MenuItemCount > 0:
731                                                 self.PopupMenu(menu)
732                                         menu.Destroy()
733                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
734                         self._scene.pushFree()
735                         self.sceneUpdated()
736                 elif self._mouseState == 'tool':
737                         if self.tempMatrix is not None and self._selectedObj is not None:
738                                 self._selectedObj.applyMatrix(self.tempMatrix)
739                                 self._scene.pushFree()
740                                 self._selectObject(self._selectedObj)
741                         self.tempMatrix = None
742                         self.tool.OnDragEnd()
743                         self.sceneUpdated()
744                 self._mouseState = None
745
746         def OnMouseMotion(self,e):
747                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
748                 p0 -= self.getObjectCenterPos() - self._viewTarget
749                 p1 -= self.getObjectCenterPos() - self._viewTarget
750
751                 if e.Dragging() and self._mouseState is not None:
752                         if self._mouseState == 'tool':
753                                 self.tool.OnDrag(p0, p1)
754                         elif not e.LeftIsDown() and e.RightIsDown():
755                                 self._mouseState = 'drag'
756                                 if wx.GetKeyState(wx.WXK_SHIFT):
757                                         a = math.cos(math.radians(self._yaw)) / 3.0
758                                         b = math.sin(math.radians(self._yaw)) / 3.0
759                                         self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
760                                         self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
761                                         self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
762                                         self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
763                                 else:
764                                         self._yaw += e.GetX() - self._mouseX
765                                         self._pitch -= e.GetY() - self._mouseY
766                                 if self._pitch > 170:
767                                         self._pitch = 170
768                                 if self._pitch < 10:
769                                         self._pitch = 10
770                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
771                                 self._mouseState = 'drag'
772                                 self._zoom += e.GetY() - self._mouseY
773                                 if self._zoom < 1:
774                                         self._zoom = 1
775                                 if self._zoom > numpy.max(self._machineSize) * 3:
776                                         self._zoom = numpy.max(self._machineSize) * 3
777                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
778                                 self._mouseState = 'dragObject'
779                                 z = max(0, self._mouseClick3DPos[2])
780                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
781                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
782                                 p0[2] -= z
783                                 p1[2] -= z
784                                 p2[2] -= z
785                                 p3[2] -= z
786                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
787                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
788                                 diff = cursorZ1 - cursorZ0
789                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
790                 if not e.Dragging() or self._mouseState != 'tool':
791                         self.tool.OnMouseMove(p0, p1)
792
793                 self._mouseX = e.GetX()
794                 self._mouseY = e.GetY()
795
796         def OnMouseWheel(self, e):
797                 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
798                 delta = max(min(delta,4),-4)
799                 self._zoom *= 1.0 - delta / 10.0
800                 if self._zoom < 1.0:
801                         self._zoom = 1.0
802                 if self._zoom > numpy.max(self._machineSize) * 3:
803                         self._zoom = numpy.max(self._machineSize) * 3
804                 self.Refresh()
805
806         def OnMouseLeave(self, e):
807                 #self._mouseX = -1
808                 pass
809
810         def getMouseRay(self, x, y):
811                 if self._viewport is None:
812                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
813                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
814                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
815                 p0 -= self._viewTarget
816                 p1 -= self._viewTarget
817                 return p0, p1
818
819         def _init3DView(self):
820                 # set viewing projection
821                 size = self.GetSize()
822                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
823                 glLoadIdentity()
824
825                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
826
827                 glDisable(GL_RESCALE_NORMAL)
828                 glDisable(GL_LIGHTING)
829                 glDisable(GL_LIGHT0)
830                 glEnable(GL_DEPTH_TEST)
831                 glDisable(GL_CULL_FACE)
832                 glDisable(GL_BLEND)
833                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
834
835                 glClearColor(0.8, 0.8, 0.8, 1.0)
836                 glClearStencil(0)
837                 glClearDepth(1.0)
838
839                 glMatrixMode(GL_PROJECTION)
840                 glLoadIdentity()
841                 aspect = float(size.GetWidth()) / float(size.GetHeight())
842                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
843
844                 glMatrixMode(GL_MODELVIEW)
845                 glLoadIdentity()
846                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
847
848         def OnPaint(self,e):
849                 connectionEntry = self._printerConnectionManager.getAvailableConnection()
850                 if machineCom.machineIsConnected():
851                         self.printButton._imageID = 6
852                         self.printButton._tooltip = _("Print")
853                 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
854                         self.printButton._imageID = 2
855                         self.printButton._tooltip = _("Toolpath to SD")
856                 elif connectionEntry is not None:
857                         self.printButton._imageID = connectionEntry.icon
858                         self.printButton._tooltip = _("Print with %s") % (connectionEntry.name)
859                 else:
860                         self.printButton._imageID = 3
861                         self.printButton._tooltip = _("Save toolpath")
862
863                 if self._animView is not None:
864                         self._viewTarget = self._animView.getPosition()
865                         if self._animView.isDone():
866                                 self._animView = None
867                 if self._animZoom is not None:
868                         self._zoom = self._animZoom.getPosition()
869                         if self._animZoom.isDone():
870                                 self._animZoom = None
871                 if self.viewMode == 'gcode' and self._gcode is not None:
872                         try:
873                                 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
874                         except:
875                                 pass
876                 if self._objectShader is None:
877                         if opengl.hasShaderSupport():
878                                 self._objectShader = opengl.GLShader("""
879 varying float light_amount;
880
881 void main(void)
882 {
883     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
884     gl_FrontColor = gl_Color;
885
886         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
887         light_amount += 0.2;
888 }
889                                 ""","""
890 varying float light_amount;
891
892 void main(void)
893 {
894         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
895 }
896                                 """)
897                                 self._objectOverhangShader = opengl.GLShader("""
898 uniform float cosAngle;
899 uniform mat3 rotMatrix;
900 varying float light_amount;
901
902 void main(void)
903 {
904     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
905     gl_FrontColor = gl_Color;
906
907         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
908         light_amount += 0.2;
909         if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
910         {
911                 light_amount = -10.0;
912         }
913 }
914                                 ""","""
915 varying float light_amount;
916
917 void main(void)
918 {
919         if (light_amount == -10.0)
920         {
921                 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
922         }else{
923                 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
924         }
925 }
926                                 """)
927                                 self._objectLoadShader = opengl.GLShader("""
928 uniform float intensity;
929 uniform float scale;
930 varying float light_amount;
931
932 void main(void)
933 {
934         vec4 tmp = gl_Vertex;
935     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
936     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
937     gl_Position = gl_ModelViewProjectionMatrix * tmp;
938     gl_FrontColor = gl_Color;
939
940         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
941         light_amount += 0.2;
942 }
943                         ""","""
944 uniform float intensity;
945 varying float light_amount;
946
947 void main(void)
948 {
949         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
950 }
951                                 """)
952                         if self._objectShader is None or not self._objectShader.isValid():
953                                 self._objectShader = opengl.GLFakeShader()
954                                 self._objectOverhangShader = opengl.GLFakeShader()
955                                 self._objectLoadShader = None
956                 self._init3DView()
957                 glTranslate(0,0,-self._zoom)
958                 glRotate(-self._pitch, 1,0,0)
959                 glRotate(self._yaw, 0,0,1)
960                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
961
962                 self._viewport = glGetIntegerv(GL_VIEWPORT)
963                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
964                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
965
966                 glClearColor(1,1,1,1)
967                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
968
969                 if self.viewMode != 'gcode':
970                         for n in xrange(0, len(self._scene.objects())):
971                                 obj = self._scene.objects()[n]
972                                 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
973                                 self._renderObject(obj)
974
975                 if self._mouseX > -1:
976                         glFlush()
977                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
978                         if n < len(self._scene.objects()):
979                                 self._focusObj = self._scene.objects()[n]
980                         else:
981                                 self._focusObj = None
982                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
983                         #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
984                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
985                         self._mouse3Dpos -= self._viewTarget
986
987                 self._init3DView()
988                 glTranslate(0,0,-self._zoom)
989                 glRotate(-self._pitch, 1,0,0)
990                 glRotate(self._yaw, 0,0,1)
991                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
992
993                 if self.viewMode == 'gcode':
994                         if self._gcode is not None and self._gcode.layerList is None:
995                                 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
996                                 self._gcodeLoadThread.daemon = True
997                                 self._gcodeLoadThread.start()
998                         if self._gcode is not None and self._gcode.layerList is not None:
999                                 glPushMatrix()
1000                                 if profile.getMachineSetting('machine_center_is_zero') != 'True':
1001                                         glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
1002                                 t = time.time()
1003                                 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
1004                                 for n in xrange(0, drawUpTill):
1005                                         c = 1.0 - float(drawUpTill - n) / 15
1006                                         c = max(0.3, c)
1007                                         if len(self._gcodeVBOs) < n + 1:
1008                                                 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
1009                                                 if time.time() - t > 0.5:
1010                                                         self.QueueRefresh()
1011                                                         break
1012                                         #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
1013                                         if n == drawUpTill - 1:
1014                                                 if len(self._gcodeVBOs[n]) < 9:
1015                                                         self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
1016                                                 glColor3f(c, 0, 0)
1017                                                 self._gcodeVBOs[n][8].render(GL_QUADS)
1018                                                 glColor3f(c/2, 0, c)
1019                                                 self._gcodeVBOs[n][9].render(GL_QUADS)
1020                                                 glColor3f(0, c, c/2)
1021                                                 self._gcodeVBOs[n][10].render(GL_QUADS)
1022                                                 glColor3f(c, 0, 0)
1023                                                 self._gcodeVBOs[n][11].render(GL_QUADS)
1024
1025                                                 glColor3f(0, c, 0)
1026                                                 self._gcodeVBOs[n][12].render(GL_QUADS)
1027                                                 glColor3f(c/2, c/2, 0.0)
1028                                                 self._gcodeVBOs[n][13].render(GL_QUADS)
1029                                                 glColor3f(0, c, c)
1030                                                 self._gcodeVBOs[n][14].render(GL_QUADS)
1031                                                 self._gcodeVBOs[n][15].render(GL_QUADS)
1032                                                 glColor3f(0, 0, c)
1033                                                 self._gcodeVBOs[n][16].render(GL_LINES)
1034                                         else:
1035                                                 glColor3f(c, 0, 0)
1036                                                 self._gcodeVBOs[n][0].render(GL_LINES)
1037                                                 glColor3f(c/2, 0, c)
1038                                                 self._gcodeVBOs[n][1].render(GL_LINES)
1039                                                 glColor3f(0, c, c/2)
1040                                                 self._gcodeVBOs[n][2].render(GL_LINES)
1041                                                 glColor3f(c, 0, 0)
1042                                                 self._gcodeVBOs[n][3].render(GL_LINES)
1043
1044                                                 glColor3f(0, c, 0)
1045                                                 self._gcodeVBOs[n][4].render(GL_LINES)
1046                                                 glColor3f(c/2, c/2, 0.0)
1047                                                 self._gcodeVBOs[n][5].render(GL_LINES)
1048                                                 glColor3f(0, c, c)
1049                                                 self._gcodeVBOs[n][6].render(GL_LINES)
1050                                                 self._gcodeVBOs[n][7].render(GL_LINES)
1051                                 glPopMatrix()
1052                 else:
1053                         glStencilFunc(GL_ALWAYS, 1, 1)
1054                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1055
1056                         if self.viewMode == 'overhang':
1057                                 self._objectOverhangShader.bind()
1058                                 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1059                         else:
1060                                 self._objectShader.bind()
1061                         for obj in self._scene.objects():
1062                                 if obj._loadAnim is not None:
1063                                         if obj._loadAnim.isDone():
1064                                                 obj._loadAnim = None
1065                                         else:
1066                                                 continue
1067                                 brightness = 1.0
1068                                 if self._focusObj == obj:
1069                                         brightness = 1.2
1070                                 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1071                                         brightness = 0.8
1072
1073                                 if self._selectedObj == obj or self._selectedObj is None:
1074                                         #If we want transparent, then first render a solid black model to remove the printer size lines.
1075                                         if self.viewMode == 'transparent':
1076                                                 glColor4f(0, 0, 0, 0)
1077                                                 self._renderObject(obj)
1078                                                 glEnable(GL_BLEND)
1079                                                 glBlendFunc(GL_ONE, GL_ONE)
1080                                                 glDisable(GL_DEPTH_TEST)
1081                                                 brightness *= 0.5
1082                                         if self.viewMode == 'xray':
1083                                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1084                                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1085                                         glEnable(GL_STENCIL_TEST)
1086
1087                                 if self.viewMode == 'overhang':
1088                                         if self._selectedObj == obj and self.tempMatrix is not None:
1089                                                 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1090                                         else:
1091                                                 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1092
1093                                 if not self._scene.checkPlatform(obj):
1094                                         glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1095                                         self._renderObject(obj)
1096                                 else:
1097                                         self._renderObject(obj, brightness)
1098                                 glDisable(GL_STENCIL_TEST)
1099                                 glDisable(GL_BLEND)
1100                                 glEnable(GL_DEPTH_TEST)
1101                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1102
1103                         if self.viewMode == 'xray':
1104                                 glPushMatrix()
1105                                 glLoadIdentity()
1106                                 glEnable(GL_STENCIL_TEST)
1107                                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1108                                 glDisable(GL_DEPTH_TEST)
1109                                 for i in xrange(2, 15, 2):
1110                                         glStencilFunc(GL_EQUAL, i, 0xFF)
1111                                         glColor(float(i)/10, float(i)/10, float(i)/5)
1112                                         glBegin(GL_QUADS)
1113                                         glVertex3f(-1000,-1000,-10)
1114                                         glVertex3f( 1000,-1000,-10)
1115                                         glVertex3f( 1000, 1000,-10)
1116                                         glVertex3f(-1000, 1000,-10)
1117                                         glEnd()
1118                                 for i in xrange(1, 15, 2):
1119                                         glStencilFunc(GL_EQUAL, i, 0xFF)
1120                                         glColor(float(i)/10, 0, 0)
1121                                         glBegin(GL_QUADS)
1122                                         glVertex3f(-1000,-1000,-10)
1123                                         glVertex3f( 1000,-1000,-10)
1124                                         glVertex3f( 1000, 1000,-10)
1125                                         glVertex3f(-1000, 1000,-10)
1126                                         glEnd()
1127                                 glPopMatrix()
1128                                 glDisable(GL_STENCIL_TEST)
1129                                 glEnable(GL_DEPTH_TEST)
1130
1131                         self._objectShader.unbind()
1132
1133                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1134                         glEnable(GL_BLEND)
1135                         if self._objectLoadShader is not None:
1136                                 self._objectLoadShader.bind()
1137                                 glColor4f(0.2, 0.6, 1.0, 1.0)
1138                                 for obj in self._scene.objects():
1139                                         if obj._loadAnim is None:
1140                                                 continue
1141                                         self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1142                                         self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1143                                         self._renderObject(obj)
1144                                 self._objectLoadShader.unbind()
1145                                 glDisable(GL_BLEND)
1146
1147                 self._drawMachine()
1148
1149                 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1150                         glEnable(GL_BLEND)
1151                         z = self._usbPrintMonitor.getZ()
1152                         size = self._machineSize
1153                         glColor4ub(255,255,0,128)
1154                         glBegin(GL_QUADS)
1155                         glVertex3f(-size[0]/2,-size[1]/2, z)
1156                         glVertex3f( size[0]/2,-size[1]/2, z)
1157                         glVertex3f( size[0]/2, size[1]/2, z)
1158                         glVertex3f(-size[0]/2, size[1]/2, z)
1159                         glEnd()
1160
1161                 if self.viewMode == 'gcode':
1162                         if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1163                                 glDisable(GL_DEPTH_TEST)
1164                                 glPushMatrix()
1165                                 glLoadIdentity()
1166                                 glTranslate(0,-4,-10)
1167                                 glColor4ub(60,60,60,255)
1168                                 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1169                                 glPopMatrix()
1170                 else:
1171                         #Draw the object box-shadow, so you can see where it will collide with other objects.
1172                         if self._selectedObj is not None:
1173                                 glEnable(GL_BLEND)
1174                                 glEnable(GL_CULL_FACE)
1175                                 glColor4f(0,0,0,0.16)
1176                                 glDepthMask(False)
1177                                 for obj in self._scene.objects():
1178                                         glPushMatrix()
1179                                         glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1180                                         glBegin(GL_TRIANGLE_FAN)
1181                                         for p in obj._boundaryHull[::-1]:
1182                                                 glVertex3f(p[0], p[1], 0)
1183                                         glEnd()
1184                                         glPopMatrix()
1185                                 glPushMatrix()
1186                                 glColor4f(0,0,0,0.06)
1187                                 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1188                                 glBegin(GL_TRIANGLE_FAN)
1189                                 for p in self._selectedObj._printAreaHull[::-1]:
1190                                         glVertex3f(p[0], p[1], 0)
1191                                 glEnd()
1192                                 glBegin(GL_TRIANGLE_FAN)
1193                                 for p in self._selectedObj._headAreaMinHull[::-1]:
1194                                         glVertex3f(p[0], p[1], 0)
1195                                 glEnd()
1196                                 glDepthMask(True)
1197                                 glDisable(GL_CULL_FACE)
1198                                 glPopMatrix()
1199
1200                         #Draw the outline of the selected object, on top of everything else except the GUI.
1201                         if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1202                                 glDisable(GL_DEPTH_TEST)
1203                                 glEnable(GL_CULL_FACE)
1204                                 glEnable(GL_STENCIL_TEST)
1205                                 glDisable(GL_BLEND)
1206                                 glStencilFunc(GL_EQUAL, 0, 255)
1207
1208                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1209                                 glLineWidth(2)
1210                                 glColor4f(1,1,1,0.5)
1211                                 self._renderObject(self._selectedObj)
1212                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1213
1214                                 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1215                                 glDisable(GL_STENCIL_TEST)
1216                                 glDisable(GL_CULL_FACE)
1217                                 glEnable(GL_DEPTH_TEST)
1218
1219                         if self._selectedObj is not None:
1220                                 glPushMatrix()
1221                                 pos = self.getObjectCenterPos()
1222                                 glTranslate(pos[0], pos[1], pos[2])
1223                                 self.tool.OnDraw()
1224                                 glPopMatrix()
1225                 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1226                         glDisable(GL_DEPTH_TEST)
1227                         glPushMatrix()
1228                         glLoadIdentity()
1229                         glTranslate(0,-4,-10)
1230                         glColor4ub(60,60,60,255)
1231                         opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1232                         glPopMatrix()
1233
1234         def _renderObject(self, obj, brightness = False, addSink = True):
1235                 glPushMatrix()
1236                 if addSink:
1237                         glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1238                 else:
1239                         glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1240
1241                 if self.tempMatrix is not None and obj == self._selectedObj:
1242                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1243                         glMultMatrixf(tempMatrix)
1244
1245                 offset = obj.getDrawOffset()
1246                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1247
1248                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1249                 glMultMatrixf(tempMatrix)
1250
1251                 n = 0
1252                 for m in obj._meshList:
1253                         if m.vbo is None:
1254                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1255                         if brightness:
1256                                 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1257                                 n += 1
1258                         m.vbo.render()
1259                 glPopMatrix()
1260
1261         def _drawMachine(self):
1262                 glEnable(GL_CULL_FACE)
1263                 glEnable(GL_BLEND)
1264
1265                 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1266
1267                 machine = profile.getMachineSetting('machine_type')
1268                 if machine.startswith('ultimaker'):
1269                         if machine not in self._platformMesh:
1270                                 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1271                                 if len(meshes) > 0:
1272                                         self._platformMesh[machine] = meshes[0]
1273                                 else:
1274                                         self._platformMesh[machine] = None
1275                                 if machine == 'ultimaker2':
1276                                         self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1277                                 else:
1278                                         self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1279                         glColor4f(1,1,1,0.5)
1280                         self._objectShader.bind()
1281                         self._renderObject(self._platformMesh[machine], False, False)
1282                         self._objectShader.unbind()
1283
1284                         #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1285                         if machine == 'ultimaker2':
1286                                 if not hasattr(self._platformMesh[machine], 'texture'):
1287                                         self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1288                                 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1289                                 glEnable(GL_TEXTURE_2D)
1290                                 glPushMatrix()
1291                                 glColor4f(1,1,1,1)
1292
1293                                 glTranslate(0,150,-5)
1294                                 h = 50
1295                                 d = 8
1296                                 w = 100
1297                                 glEnable(GL_BLEND)
1298                                 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1299                                 glBegin(GL_QUADS)
1300                                 glTexCoord2f(1, 0)
1301                                 glVertex3f( w, 0, h)
1302                                 glTexCoord2f(0, 0)
1303                                 glVertex3f(-w, 0, h)
1304                                 glTexCoord2f(0, 1)
1305                                 glVertex3f(-w, 0, 0)
1306                                 glTexCoord2f(1, 1)
1307                                 glVertex3f( w, 0, 0)
1308
1309                                 glTexCoord2f(1, 0)
1310                                 glVertex3f(-w, d, h)
1311                                 glTexCoord2f(0, 0)
1312                                 glVertex3f( w, d, h)
1313                                 glTexCoord2f(0, 1)
1314                                 glVertex3f( w, d, 0)
1315                                 glTexCoord2f(1, 1)
1316                                 glVertex3f(-w, d, 0)
1317                                 glEnd()
1318                                 glDisable(GL_TEXTURE_2D)
1319                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1320                                 glPopMatrix()
1321                 else:
1322                         glColor4f(0,0,0,1)
1323                         glLineWidth(3)
1324                         glBegin(GL_LINES)
1325                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1326                         glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1327                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1328                         glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1329                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1330                         glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1331                         glEnd()
1332
1333                 glDepthMask(False)
1334
1335                 polys = profile.getMachineSizePolygons()
1336                 height = profile.getMachineSettingFloat('machine_height')
1337                 glBegin(GL_QUADS)
1338                 for n in xrange(0, len(polys[0])):
1339                         if n % 2 == 0:
1340                                 glColor4ub(5, 171, 231, 96)
1341                         else:
1342                                 glColor4ub(5, 171, 231, 64)
1343                         glVertex3f(polys[0][n][0], polys[0][n][1], height)
1344                         glVertex3f(polys[0][n][0], polys[0][n][1], 0)
1345                         glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0)
1346                         glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height)
1347                 glEnd()
1348                 glColor4ub(5, 171, 231, 128)
1349                 glBegin(GL_TRIANGLE_FAN)
1350                 for p in polys[0][::-1]:
1351                         glVertex3f(p[0], p[1], height)
1352                 glEnd()
1353
1354                 #Draw checkerboard
1355                 if self._platformTexture is None:
1356                         self._platformTexture = opengl.loadGLTexture('checkerboard.png')
1357                         glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1358                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
1359                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
1360                 glColor4f(1,1,1,0.5)
1361                 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1362                 glEnable(GL_TEXTURE_2D)
1363                 glBegin(GL_TRIANGLE_FAN)
1364                 for p in polys[0]:
1365                         glTexCoord2f(p[0]/20, p[1]/20)
1366                         glVertex3f(p[0], p[1], 0)
1367                 glEnd()
1368                 glDisable(GL_TEXTURE_2D)
1369                 glColor4ub(127, 127, 127, 200)
1370                 for poly in polys[1:]:
1371                         glBegin(GL_TRIANGLE_FAN)
1372                         for p in poly:
1373                                 glTexCoord2f(p[0]/20, p[1]/20)
1374                                 glVertex3f(p[0], p[1], 0)
1375                         glEnd()
1376
1377                 glDepthMask(True)
1378                 glDisable(GL_BLEND)
1379                 glDisable(GL_CULL_FACE)
1380
1381         def _generateGCodeVBOs(self, layer):
1382                 ret = []
1383                 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1384                         if ':' in extrudeType:
1385                                 extruder = int(extrudeType[extrudeType.find(':')+1:])
1386                                 extrudeType = extrudeType[0:extrudeType.find(':')]
1387                         else:
1388                                 extruder = None
1389                         pointList = numpy.zeros((0,3), numpy.float32)
1390                         for path in layer:
1391                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1392                                         a = path['points']
1393                                         a = numpy.concatenate((a[:-1], a[1:]), 1)
1394                                         a = a.reshape((len(a) * 2, 3))
1395                                         pointList = numpy.concatenate((pointList, a))
1396                         ret.append(opengl.GLVBO(pointList))
1397                 return ret
1398
1399         def _generateGCodeVBOs2(self, layer):
1400                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1401                 filamentArea = math.pi * filamentRadius * filamentRadius
1402                 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1403
1404                 ret = []
1405                 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1406                         if ':' in extrudeType:
1407                                 extruder = int(extrudeType[extrudeType.find(':')+1:])
1408                                 extrudeType = extrudeType[0:extrudeType.find(':')]
1409                         else:
1410                                 extruder = None
1411                         pointList = numpy.zeros((0,3), numpy.float32)
1412                         for path in layer:
1413                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1414                                         a = path['points']
1415                                         if extrudeType == 'FILL':
1416                                                 a[:,2] += 0.01
1417
1418                                         normal = a[1:] - a[:-1]
1419                                         lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1420                                         normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1421                                         normal[:,2] /= lens
1422
1423                                         ePerDist = path['extrusion'][1:] / lens
1424                                         if useFilamentArea:
1425                                                 lineWidth = ePerDist / path['layerThickness'] / 2.0
1426                                         else:
1427                                                 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1428
1429                                         normal[:,0] *= lineWidth
1430                                         normal[:,1] *= lineWidth
1431
1432                                         b = numpy.zeros((len(a)-1, 0), numpy.float32)
1433                                         b = numpy.concatenate((b, a[1:] + normal), 1)
1434                                         b = numpy.concatenate((b, a[1:] - normal), 1)
1435                                         b = numpy.concatenate((b, a[:-1] - normal), 1)
1436                                         b = numpy.concatenate((b, a[:-1] + normal), 1)
1437                                         b = b.reshape((len(b) * 4, 3))
1438
1439                                         if len(a) > 2:
1440                                                 normal2 = normal[:-1] + normal[1:]
1441                                                 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1442                                                 normal2[:,0] /= lens2
1443                                                 normal2[:,1] /= lens2
1444                                                 normal2[:,0] *= lineWidth[:-1]
1445                                                 normal2[:,1] *= lineWidth[:-1]
1446
1447                                                 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1448                                                 c = numpy.concatenate((c, a[1:-1]), 1)
1449                                                 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1450                                                 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1451                                                 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1452
1453                                                 c = numpy.concatenate((c, a[1:-1]), 1)
1454                                                 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1455                                                 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1456                                                 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1457
1458                                                 c = c.reshape((len(c) * 8, 3))
1459
1460                                                 pointList = numpy.concatenate((pointList, b, c))
1461                                         else:
1462                                                 pointList = numpy.concatenate((pointList, b))
1463                         ret.append(opengl.GLVBO(pointList))
1464
1465                 pointList = numpy.zeros((0,3), numpy.float32)
1466                 for path in layer:
1467                         if path['type'] == 'move':
1468                                 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1469                                 a = numpy.concatenate((a[:-1], a[1:]), 1)
1470                                 a = a.reshape((len(a) * 2, 3))
1471                                 pointList = numpy.concatenate((pointList, a))
1472                         if path['type'] == 'retract':
1473                                 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1474                                 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1475                                 a = a.reshape((len(a) * 2, 3))
1476                                 pointList = numpy.concatenate((pointList, a))
1477                 ret.append(opengl.GLVBO(pointList))
1478
1479                 return ret
1480
1481         def getObjectCenterPos(self):
1482                 if self._selectedObj is None:
1483                         return [0.0, 0.0, 0.0]
1484                 pos = self._selectedObj.getPosition()
1485                 size = self._selectedObj.getSize()
1486                 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1487
1488         def getObjectBoundaryCircle(self):
1489                 if self._selectedObj is None:
1490                         return 0.0
1491                 return self._selectedObj.getBoundaryCircle()
1492
1493         def getObjectSize(self):
1494                 if self._selectedObj is None:
1495                         return [0.0, 0.0, 0.0]
1496                 return self._selectedObj.getSize()
1497
1498         def getObjectMatrix(self):
1499                 if self._selectedObj is None:
1500                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1501                 return self._selectedObj.getMatrix()
1502
1503 class shaderEditor(wx.Dialog):
1504         def __init__(self, parent, callback, v, f):
1505                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1506                 self._callback = callback
1507                 s = wx.BoxSizer(wx.VERTICAL)
1508                 self.SetSizer(s)
1509                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1510                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1511                 s.Add(self._vertex, 1, flag=wx.EXPAND)
1512                 s.Add(self._fragment, 1, flag=wx.EXPAND)
1513
1514                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1515                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1516
1517                 self.SetPosition(self.GetParent().GetPosition())
1518                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1519                 self.Show()
1520
1521         def OnText(self, e):
1522                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())