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