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