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