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