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