chiark / gitweb /
e6e116a9296b723b2f4e4bc70dd78c7d2305c7cc
[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._isSimpleMode = True
60                 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
61                 self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager()
62
63                 self._viewport = None
64                 self._modelMatrix = None
65                 self._projMatrix = None
66                 self.tempMatrix = None
67
68                 self.openFileButton      = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
69                 self.printButton         = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
70                 self.printButton.setDisabled(True)
71
72                 group = []
73                 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
74                 self.scaleToolButton  = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
75                 self.mirrorToolButton  = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
76
77                 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
78                 self.layFlatButton       = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
79
80                 self.resetScaleButton    = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
81                 self.scaleMaxButton      = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
82
83                 self.mirrorXButton       = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
84                 self.mirrorYButton       = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
85                 self.mirrorZButton       = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
86
87                 self.rotateToolButton.setExpandArrow(True)
88                 self.scaleToolButton.setExpandArrow(True)
89                 self.mirrorToolButton.setExpandArrow(True)
90
91                 self.scaleForm = openglGui.glFrame(self, (2, -2))
92                 openglGui.glGuiLayoutGrid(self.scaleForm)
93                 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
94                 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
95                 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
96                 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
97                 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
98                 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
99                 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
100                 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
101                 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
102                 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
103                 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
104                 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
105                 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
106                 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
107
108                 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
109                 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
110
111                 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
112                 self.youMagineButton.setDisabled(True)
113
114                 self.notification = openglGui.glNotification(self, (0, 0))
115
116                 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
117                 self._sceneUpdateTimer = wx.Timer(self)
118                 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
119                 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
120                 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
121
122                 self.OnViewChange()
123                 self.OnToolSelect(0)
124                 self.updateToolButtons()
125                 self.updateProfileToControls()
126
127         def loadGCodeFile(self, filename):
128                 self.OnDeleteAll(None)
129                 if self._gcode is not None:
130                         self._gcode = None
131                         for layerVBOlist in self._gcodeVBOs:
132                                 for vbo in layerVBOlist:
133                                         self.glReleaseList.append(vbo)
134                         self._gcodeVBOs = []
135                 self._gcode = gcodeInterpreter.gcode()
136                 self._gcodeFilename = filename
137                 self.printButton.setBottomText('')
138                 self.viewSelection.setValue(4)
139                 self.printButton.setDisabled(False)
140                 self.youMagineButton.setDisabled(True)
141                 self.OnViewChange()
142
143         def loadSceneFiles(self, filenames):
144                 self.youMagineButton.setDisabled(False)
145                 #if self.viewSelection.getValue() == 4:
146                 #       self.viewSelection.setValue(0)
147                 #       self.OnViewChange()
148                 self.loadScene(filenames)
149
150         def loadFiles(self, filenames):
151                 mainWindow = self.GetParent().GetParent().GetParent()
152                 # only one GCODE file can be active
153                 # so if single gcode file, process this
154                 # otherwise ignore all gcode files
155                 gcodeFilename = None
156                 if len(filenames) == 1:
157                         filename = filenames[0]
158                         ext = os.path.splitext(filename)[1].lower()
159                         if ext == '.g' or ext == '.gcode':
160                                 gcodeFilename = filename
161                                 mainWindow.addToModelMRU(filename)
162                 if gcodeFilename is not None:
163                         self.loadGCodeFile(gcodeFilename)
164                 else:
165                         # process directories and special file types
166                         # and keep scene files for later processing
167                         scene_filenames = []
168                         ignored_types = dict()
169                         # use file list as queue
170                         # pop first entry for processing and append new files at end
171                         while filenames:
172                                 filename = filenames.pop(0)
173                                 if os.path.isdir(filename):
174                                         # directory: queue all included files and directories
175                                         filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
176                                 else:
177                                         ext = os.path.splitext(filename)[1].lower()
178                                         if ext == '.ini':
179                                                 profile.loadProfile(filename)
180                                                 mainWindow.addToProfileMRU(filename)
181                                         elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
182                                                 scene_filenames.append(filename)
183                                                 mainWindow.addToModelMRU(filename)
184                                         else:
185                                                 ignored_types[ext] = 1
186                         if ignored_types:
187                                 ignored_types = ignored_types.keys()
188                                 ignored_types.sort()
189                                 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
190                         mainWindow.updateProfileToAllControls()
191                         # now process all the scene files
192                         if scene_filenames:
193                                 self.loadSceneFiles(scene_filenames)
194                                 self._selectObject(None)
195                                 self.sceneUpdated()
196                                 newZoom = numpy.max(self._machineSize)
197                                 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
198                                 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
199
200         def showLoadModel(self, button = 1):
201                 if button == 1:
202                         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)
203                         dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
204                         if dlg.ShowModal() != wx.ID_OK:
205                                 dlg.Destroy()
206                                 return
207                         filenames = dlg.GetPaths()
208                         dlg.Destroy()
209                         if len(filenames) < 1:
210                                 return False
211                         profile.putPreference('lastFile', filenames[0])
212                         self.loadFiles(filenames)
213
214         def showSaveModel(self):
215                 if len(self._scene.objects()) < 1:
216                         return
217                 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
218                 dlg.SetWildcard(meshLoader.saveWildcardFilter())
219                 if dlg.ShowModal() != wx.ID_OK:
220                         dlg.Destroy()
221                         return
222                 filename = dlg.GetPath()
223                 dlg.Destroy()
224                 meshLoader.saveMeshes(filename, self._scene.objects())
225
226         def OnPrintButton(self, button):
227                 if button == 1:
228                         connectionEntry = self._printerConnectionManager.getAvailableConnection()
229                         if machineCom.machineIsConnected():
230                                 self.showPrintWindow()
231                         elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
232                                 drives = removableStorage.getPossibleSDcardDrives()
233                                 if len(drives) > 1:
234                                         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))
235                                         if dlg.ShowModal() != wx.ID_OK:
236                                                 dlg.Destroy()
237                                                 return
238                                         drive = drives[dlg.GetSelection()]
239                                         dlg.Destroy()
240                                 else:
241                                         drive = drives[0]
242                                 filename = self._scene._objectList[0].getName() + '.gcode'
243                                 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
244                         elif connectionEntry is not None:
245                                 connection = connectionEntry.connection
246                                 if connectionEntry.window is None or not connectionEntry.window:
247                                         connectionEntry.window = printWindow2.printWindow(connection)
248                                 connectionEntry.window.Show()
249                                 connectionEntry.window.Raise()
250                                 if not connection.loadFile(self._gcodeFilename):
251                                         if connection.isPrinting():
252                                                 self.notification.message("Cannot start print, because other print still running.")
253                                         else:
254                                                 self.notification.message("Failed to start print...")
255                         else:
256                                 self.showSaveGCode()
257                 if button == 3:
258                         menu = wx.Menu()
259                         self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
260                         self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
261                         self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
262                         self.PopupMenu(menu)
263                         menu.Destroy()
264
265         def showPrintWindow(self):
266                 if self._gcodeFilename is None:
267                         return
268                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
269                         wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING)
270                         return
271                 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
272                 if self._gcodeFilename == self._slicer.getGCodeFilename():
273                         self._slicer.submitSliceInfoOnline()
274
275         def showSaveGCode(self):
276                 if len(self._scene._objectList) < 1:
277                         return
278                 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
279                 filename = self._scene._objectList[0].getName() + '.gcode'
280                 dlg.SetFilename(filename)
281                 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
282                 if dlg.ShowModal() != wx.ID_OK:
283                         dlg.Destroy()
284                         return
285                 filename = dlg.GetPath()
286                 dlg.Destroy()
287
288                 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
289
290         def _copyFile(self, fileA, fileB, allowEject = False):
291                 try:
292                         size = float(os.stat(fileA).st_size)
293                         with open(fileA, 'rb') as fsrc:
294                                 with open(fileB, 'wb') as fdst:
295                                         while 1:
296                                                 buf = fsrc.read(16*1024)
297                                                 if not buf:
298                                                         break
299                                                 fdst.write(buf)
300                                                 self.printButton.setProgressBar(float(fsrc.tell()) / size)
301                                                 self._queueRefresh()
302                 except:
303                         import sys
304                         print sys.exc_info()
305                         self.notification.message("Failed to save")
306                 else:
307                         if allowEject:
308                                 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...'))
309                         else:
310                                 self.notification.message("Saved as %s" % (fileB))
311                 self.printButton.setProgressBar(None)
312                 if fileA == self._slicer.getGCodeFilename():
313                         self._slicer.submitSliceInfoOnline()
314
315         def _showSliceLog(self):
316                 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
317                 dlg.ShowModal()
318                 dlg.Destroy()
319
320         def OnToolSelect(self, button):
321                 if self.rotateToolButton.getSelected():
322                         self.tool = previewTools.toolRotate(self)
323                 elif self.scaleToolButton.getSelected():
324                         self.tool = previewTools.toolScale(self)
325                 elif self.mirrorToolButton.getSelected():
326                         self.tool = previewTools.toolNone(self)
327                 else:
328                         self.tool = previewTools.toolNone(self)
329                 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
330                 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
331                 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
332                 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
333                 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
334                 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
335                 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
336                 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
337
338         def updateToolButtons(self):
339                 if self._selectedObj is None:
340                         hidden = True
341                 else:
342                         hidden = False
343                 self.rotateToolButton.setHidden(hidden)
344                 self.scaleToolButton.setHidden(hidden)
345                 self.mirrorToolButton.setHidden(hidden)
346                 if hidden:
347                         self.rotateToolButton.setSelected(False)
348                         self.scaleToolButton.setSelected(False)
349                         self.mirrorToolButton.setSelected(False)
350                         self.OnToolSelect(0)
351
352         def OnViewChange(self):
353                 if self.viewSelection.getValue() == 4:
354                         self.viewMode = 'gcode'
355                         if self._gcode is not None and self._gcode.layerList is not None:
356                                 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
357                         self._selectObject(None)
358                 elif self.viewSelection.getValue() == 1:
359                         self.viewMode = 'overhang'
360                 elif self.viewSelection.getValue() == 2:
361                         self.viewMode = 'transparent'
362                 elif self.viewSelection.getValue() == 3:
363                         self.viewMode = 'xray'
364                 else:
365                         self.viewMode = 'normal'
366                 self.layerSelect.setHidden(self.viewMode != 'gcode')
367                 self.QueueRefresh()
368
369         def OnRotateReset(self, button):
370                 if self._selectedObj is None:
371                         return
372                 self._selectedObj.resetRotation()
373                 self._scene.pushFree()
374                 self._selectObject(self._selectedObj)
375                 self.sceneUpdated()
376
377         def OnLayFlat(self, button):
378                 if self._selectedObj is None:
379                         return
380                 self._selectedObj.layFlat()
381                 self._scene.pushFree()
382                 self._selectObject(self._selectedObj)
383                 self.sceneUpdated()
384
385         def OnScaleReset(self, button):
386                 if self._selectedObj is None:
387                         return
388                 self._selectedObj.resetScale()
389                 self._selectObject(self._selectedObj)
390                 self.updateProfileToControls()
391                 self.sceneUpdated()
392
393         def OnScaleMax(self, button):
394                 if self._selectedObj is None:
395                         return
396                 machine = profile.getMachineSetting('machine_type')
397                 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
398                 self._scene.pushFree()
399                 #self.sceneUpdated()
400                 if machine == "ultimaker2":
401                         #This is bad and Jaime should feel bad!
402                         self._selectedObj.setPosition(numpy.array([0.0,-10.0]))
403                         self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
404                         self._selectedObj.setPosition(numpy.array([0.0,0.0]))
405                         self._scene.pushFree()
406                 else:
407                         self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
408                         self._scene.pushFree()
409                         self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
410                 self._scene.pushFree()
411                 self._selectObject(self._selectedObj)
412                 self.updateProfileToControls()
413                 self.sceneUpdated()
414
415         def OnMirror(self, axis):
416                 if self._selectedObj is None:
417                         return
418                 self._selectedObj.mirror(axis)
419                 self.sceneUpdated()
420
421         def OnScaleEntry(self, value, axis):
422                 if self._selectedObj is None:
423                         return
424                 try:
425                         value = float(value)
426                 except:
427                         return
428                 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
429                 self.updateProfileToControls()
430                 self._scene.pushFree()
431                 self._selectObject(self._selectedObj)
432                 self.sceneUpdated()
433
434         def OnScaleEntryMM(self, value, axis):
435                 if self._selectedObj is None:
436                         return
437                 try:
438                         value = float(value)
439                 except:
440                         return
441                 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
442                 self.updateProfileToControls()
443                 self._scene.pushFree()
444                 self._selectObject(self._selectedObj)
445                 self.sceneUpdated()
446
447         def OnDeleteAll(self, e):
448                 while len(self._scene.objects()) > 0:
449                         self._deleteObject(self._scene.objects()[0])
450                 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
451
452         def OnMultiply(self, e):
453                 if self._focusObj is None:
454                         return
455                 obj = self._focusObj
456                 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
457                 if dlg.ShowModal() != wx.ID_OK:
458                         dlg.Destroy()
459                         return
460                 cnt = dlg.GetValue()
461                 dlg.Destroy()
462                 n = 0
463                 while True:
464                         n += 1
465                         newObj = obj.copy()
466                         self._scene.add(newObj)
467                         self._scene.centerAll()
468                         if not self._scene.checkPlatform(newObj):
469                                 break
470                         if n > cnt:
471                                 break
472                 if n <= cnt:
473                         self.notification.message("Could not create more then %d items" % (n - 1))
474                 self._scene.remove(newObj)
475                 self._scene.centerAll()
476                 self.sceneUpdated()
477
478         def OnSplitObject(self, e):
479                 if self._focusObj is None:
480                         return
481                 self._scene.remove(self._focusObj)
482                 for obj in self._focusObj.split(self._splitCallback):
483                         if numpy.max(obj.getSize()) > 2.0:
484                                 self._scene.add(obj)
485                 self._scene.centerAll()
486                 self._selectObject(None)
487                 self.sceneUpdated()
488
489         def OnCenter(self, e):
490                 if self._focusObj is None:
491                         return
492                 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
493                 self._scene.pushFree()
494                 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
495                 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
496                 self.sceneUpdated()
497
498         def _splitCallback(self, progress):
499                 print progress
500
501         def OnMergeObjects(self, e):
502                 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
503                         if len(self._scene.objects()) == 2:
504                                 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
505                                 self.sceneUpdated()
506                         return
507                 self._scene.merge(self._selectedObj, self._focusObj)
508                 self.sceneUpdated()
509
510         def sceneUpdated(self):
511                 self._sceneUpdateTimer.Start(500, True)
512                 self._slicer.abortSlicer()
513                 self._scene.updateSizeOffsets()
514                 self.QueueRefresh()
515
516         def _onRunSlicer(self, e):
517                 if self._isSimpleMode:
518                         self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
519                 self._slicer.runSlicer(self._scene)
520                 if self._isSimpleMode:
521                         profile.resetTempOverride()
522
523         def _updateSliceProgress(self, progressValue, ready):
524                 if not ready:
525                         if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
526                                 return
527                 self.printButton.setDisabled(not ready)
528                 if progressValue >= 0.0:
529                         self.printButton.setProgressBar(progressValue)
530                 else:
531                         self.printButton.setProgressBar(None)
532                 if self._gcode is not None:
533                         self._gcode = None
534                         for layerVBOlist in self._gcodeVBOs:
535                                 for vbo in layerVBOlist:
536                                         self.glReleaseList.append(vbo)
537                         self._gcodeVBOs = []
538                 if ready:
539                         self.printButton.setProgressBar(None)
540                         text = '%s' % (self._slicer.getPrintTime())
541                         for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
542                                 amount = self._slicer.getFilamentAmount(e)
543                                 if amount is None:
544                                         continue
545                                 text += '\n%s' % (amount)
546                                 cost = self._slicer.getFilamentCost(e)
547                                 if cost is not None:
548                                         text += '\n%s' % (cost)
549                         self.printButton.setBottomText(text)
550                         self._gcode = gcodeInterpreter.gcode()
551                         self._gcodeFilename = self._slicer.getGCodeFilename()
552                 else:
553                         self.printButton.setBottomText('')
554                 self.QueueRefresh()
555
556         def _loadGCode(self):
557                 self._gcode.progressCallback = self._gcodeLoadCallback
558                 self._gcode.load(self._gcodeFilename)
559
560         def _gcodeLoadCallback(self, progress):
561                 if not self or self._gcode is None:
562                         return True
563                 if len(self._gcode.layerList) % 15 == 0:
564                         time.sleep(0.1)
565                 if self._gcode is None:
566                         return True
567                 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
568                 if self.viewMode == 'gcode':
569                         self._queueRefresh()
570                 return False
571
572         def loadScene(self, fileList):
573                 for filename in fileList:
574                         try:
575                                 ext = os.path.splitext(filename)[1].lower()
576                                 if ext in imageToMesh.supportedExtensions():
577                                         imageToMesh.convertImageDialog(self, filename).Show()
578                                         objList = []
579                                 else:
580                                         objList = meshLoader.loadMeshes(filename)
581                         except:
582                                 traceback.print_exc()
583                         else:
584                                 for obj in objList:
585                                         if self._objectLoadShader is not None:
586                                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
587                                         else:
588                                                 obj._loadAnim = None
589                                         self._scene.add(obj)
590                                         if not self._scene.checkPlatform(obj):
591                                                 self._scene.centerAll()
592                                         self._selectObject(obj)
593                                         if obj.getScale()[0] < 1.0:
594                                                 self.notification.message("Warning: Object scaled down.")
595                 self.sceneUpdated()
596
597         def _deleteObject(self, obj):
598                 if obj == self._selectedObj:
599                         self._selectObject(None)
600                 if obj == self._focusObj:
601                         self._focusObj = None
602                 self._scene.remove(obj)
603                 for m in obj._meshList:
604                         if m.vbo is not None and m.vbo.decRef():
605                                 self.glReleaseList.append(m.vbo)
606                 import gc
607                 gc.collect()
608                 self.sceneUpdated()
609
610         def _selectObject(self, obj, zoom = True):
611                 if obj != self._selectedObj:
612                         self._selectedObj = obj
613                         self.updateProfileToControls()
614                         self.updateToolButtons()
615                 if zoom and obj is not None:
616                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
617                         self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
618                         newZoom = obj.getBoundaryCircle() * 6
619                         if newZoom > numpy.max(self._machineSize) * 3:
620                                 newZoom = numpy.max(self._machineSize) * 3
621                         self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
622
623         def updateProfileToControls(self):
624                 oldSimpleMode = self._isSimpleMode
625                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
626                 if self._isSimpleMode != oldSimpleMode:
627                         self._scene.arrangeAll()
628                         self.sceneUpdated()
629                 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
630                 self._objColors[0] = profile.getPreferenceColour('model_colour')
631                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
632                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
633                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
634                 self._scene.setMachineSize(self._machineSize)
635                 self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height'))
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 profile.getMachineSetting('machine_type').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                 #Draw checkerboard
1356                 sx = self._machineSize[0]
1357                 sy = self._machineSize[1]
1358                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1359                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1360                                 x1 = x * 10
1361                                 x2 = x1 + 10
1362                                 y1 = y * 10
1363                                 y2 = y1 + 10
1364                                 x1 = max(min(x1, sx/2), -sx/2)
1365                                 y1 = max(min(y1, sy/2), -sy/2)
1366                                 x2 = max(min(x2, sx/2), -sx/2)
1367                                 y2 = max(min(y2, sy/2), -sy/2)
1368                                 #Black or "white"  checker
1369                                 if (x & 1) == (y & 1):
1370                                         glColor4ub(5, 171, 231, 127)
1371                                 else:
1372                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1373                                 glBegin(GL_QUADS)
1374                                 glVertex3f(x1, y1, 0)
1375                                 glVertex3f(x2, y1, 0)
1376                                 glVertex3f(x2, y2, 0)
1377                                 glVertex3f(x1, y2, 0)
1378                                 glEnd()
1379
1380                 if machine == 'ultimaker2':
1381
1382                         glColor4ub(127, 127, 127, 200)
1383                         #if UM2, draw bat-area zone for head. THe head can't stop there, because its bat-area.
1384                         #UpperRight
1385                         clipWidth = 25
1386                         clipHeight = 10
1387                         posX = sx / 2 - clipWidth
1388                         posY = sy / 2 - clipHeight
1389                         glBegin(GL_QUADS)
1390                         glVertex3f(posX, posY, 0)
1391                         glVertex3f(posX+clipWidth, posY, 0)
1392                         glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1393                         glVertex3f(posX, posY+clipHeight, 0)
1394                         glEnd()
1395                         #UpperLeft
1396                         clipWidth = 25
1397                         clipHeight = 10
1398                         posX = -sx / 2
1399                         posY = sy / 2 - clipHeight
1400                         glBegin(GL_QUADS)
1401                         glVertex3f(posX, posY, 0)
1402                         glVertex3f(posX+clipWidth, posY, 0)
1403                         glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1404                         glVertex3f(posX, posY+clipHeight, 0)
1405                         glEnd()
1406                         #LowerRight
1407                         clipWidth = 25
1408                         clipHeight = 10
1409                         posX = sx / 2 - clipWidth
1410                         posY = -sy / 2
1411                         glBegin(GL_QUADS)
1412                         glVertex3f(posX, posY, 0)
1413                         glVertex3f(posX+clipWidth, posY, 0)
1414                         glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1415                         glVertex3f(posX, posY+clipHeight, 0)
1416                         glEnd()
1417                         #LowerLeft
1418                         clipWidth = 25
1419                         clipHeight = 10
1420                         posX = -sx / 2
1421                         posY = -sy / 2
1422                         glBegin(GL_QUADS)
1423                         glVertex3f(posX, posY, 0)
1424                         glVertex3f(posX+clipWidth, posY, 0)
1425                         glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1426                         glVertex3f(posX, posY+clipHeight, 0)
1427                         glEnd()
1428
1429                 glDepthMask(True)
1430                 glDisable(GL_BLEND)
1431                 glDisable(GL_CULL_FACE)
1432
1433         def _generateGCodeVBOs(self, layer):
1434                 ret = []
1435                 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1436                         if ':' in extrudeType:
1437                                 extruder = int(extrudeType[extrudeType.find(':')+1:])
1438                                 extrudeType = extrudeType[0:extrudeType.find(':')]
1439                         else:
1440                                 extruder = None
1441                         pointList = numpy.zeros((0,3), numpy.float32)
1442                         for path in layer:
1443                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1444                                         a = path['points']
1445                                         a = numpy.concatenate((a[:-1], a[1:]), 1)
1446                                         a = a.reshape((len(a) * 2, 3))
1447                                         pointList = numpy.concatenate((pointList, a))
1448                         ret.append(opengl.GLVBO(pointList))
1449                 return ret
1450
1451         def _generateGCodeVBOs2(self, layer):
1452                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1453                 filamentArea = math.pi * filamentRadius * filamentRadius
1454                 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1455
1456                 ret = []
1457                 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1458                         if ':' in extrudeType:
1459                                 extruder = int(extrudeType[extrudeType.find(':')+1:])
1460                                 extrudeType = extrudeType[0:extrudeType.find(':')]
1461                         else:
1462                                 extruder = None
1463                         pointList = numpy.zeros((0,3), numpy.float32)
1464                         for path in layer:
1465                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1466                                         a = path['points']
1467                                         if extrudeType == 'FILL':
1468                                                 a[:,2] += 0.01
1469
1470                                         normal = a[1:] - a[:-1]
1471                                         lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1472                                         normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1473                                         normal[:,2] /= lens
1474
1475                                         ePerDist = path['extrusion'][1:] / lens
1476                                         if useFilamentArea:
1477                                                 lineWidth = ePerDist / path['layerThickness'] / 2.0
1478                                         else:
1479                                                 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1480
1481                                         normal[:,0] *= lineWidth
1482                                         normal[:,1] *= lineWidth
1483
1484                                         b = numpy.zeros((len(a)-1, 0), numpy.float32)
1485                                         b = numpy.concatenate((b, a[1:] + normal), 1)
1486                                         b = numpy.concatenate((b, a[1:] - normal), 1)
1487                                         b = numpy.concatenate((b, a[:-1] - normal), 1)
1488                                         b = numpy.concatenate((b, a[:-1] + normal), 1)
1489                                         b = b.reshape((len(b) * 4, 3))
1490
1491                                         if len(a) > 2:
1492                                                 normal2 = normal[:-1] + normal[1:]
1493                                                 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1494                                                 normal2[:,0] /= lens2
1495                                                 normal2[:,1] /= lens2
1496                                                 normal2[:,0] *= lineWidth[:-1]
1497                                                 normal2[:,1] *= lineWidth[:-1]
1498
1499                                                 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1500                                                 c = numpy.concatenate((c, a[1:-1]), 1)
1501                                                 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1502                                                 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1503                                                 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1504
1505                                                 c = numpy.concatenate((c, a[1:-1]), 1)
1506                                                 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1507                                                 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1508                                                 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1509
1510                                                 c = c.reshape((len(c) * 8, 3))
1511
1512                                                 pointList = numpy.concatenate((pointList, b, c))
1513                                         else:
1514                                                 pointList = numpy.concatenate((pointList, b))
1515                         ret.append(opengl.GLVBO(pointList))
1516
1517                 pointList = numpy.zeros((0,3), numpy.float32)
1518                 for path in layer:
1519                         if path['type'] == 'move':
1520                                 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1521                                 a = numpy.concatenate((a[:-1], a[1:]), 1)
1522                                 a = a.reshape((len(a) * 2, 3))
1523                                 pointList = numpy.concatenate((pointList, a))
1524                         if path['type'] == 'retract':
1525                                 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1526                                 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1527                                 a = a.reshape((len(a) * 2, 3))
1528                                 pointList = numpy.concatenate((pointList, a))
1529                 ret.append(opengl.GLVBO(pointList))
1530
1531                 return ret
1532
1533         def getObjectCenterPos(self):
1534                 if self._selectedObj is None:
1535                         return [0.0, 0.0, 0.0]
1536                 pos = self._selectedObj.getPosition()
1537                 size = self._selectedObj.getSize()
1538                 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1539
1540         def getObjectBoundaryCircle(self):
1541                 if self._selectedObj is None:
1542                         return 0.0
1543                 return self._selectedObj.getBoundaryCircle()
1544
1545         def getObjectSize(self):
1546                 if self._selectedObj is None:
1547                         return [0.0, 0.0, 0.0]
1548                 return self._selectedObj.getSize()
1549
1550         def getObjectMatrix(self):
1551                 if self._selectedObj is None:
1552                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1553                 return self._selectedObj.getMatrix()
1554
1555 class shaderEditor(wx.Dialog):
1556         def __init__(self, parent, callback, v, f):
1557                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1558                 self._callback = callback
1559                 s = wx.BoxSizer(wx.VERTICAL)
1560                 self.SetSizer(s)
1561                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1562                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1563                 s.Add(self._vertex, 1, flag=wx.EXPAND)
1564                 s.Add(self._fragment, 1, flag=wx.EXPAND)
1565
1566                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1567                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1568
1569                 self.SetPosition(self.GetParent().GetPosition())
1570                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1571                 self.Show()
1572
1573         def OnText(self, e):
1574                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())