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