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