chiark / gitweb /
Refuse to USB print with UltiGCode flavor.
[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                         self._selectedObj.setPosition(numpy.array([0.0,-22.5]))
387
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,-15]))
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
622                 if self._selectedObj is not None:
623                         scale = self._selectedObj.getScale()
624                         size = self._selectedObj.getSize()
625                         self.scaleXctrl.setValue(round(scale[0], 2))
626                         self.scaleYctrl.setValue(round(scale[1], 2))
627                         self.scaleZctrl.setValue(round(scale[2], 2))
628                         self.scaleXmmctrl.setValue(round(size[0], 2))
629                         self.scaleYmmctrl.setValue(round(size[1], 2))
630                         self.scaleZmmctrl.setValue(round(size[2], 2))
631
632         def OnKeyChar(self, keyCode):
633                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
634                         if self._selectedObj is not None:
635                                 self._deleteObject(self._selectedObj)
636                                 self.QueueRefresh()
637                 if keyCode == wx.WXK_UP:
638                         self.layerSelect.setValue(self.layerSelect.getValue() + 1)
639                         self.QueueRefresh()
640                 elif keyCode == wx.WXK_DOWN:
641                         self.layerSelect.setValue(self.layerSelect.getValue() - 1)
642                         self.QueueRefresh()
643                 elif keyCode == wx.WXK_PAGEUP:
644                         self.layerSelect.setValue(self.layerSelect.getValue() + 10)
645                         self.QueueRefresh()
646                 elif keyCode == wx.WXK_PAGEDOWN:
647                         self.layerSelect.setValue(self.layerSelect.getValue() - 10)
648                         self.QueueRefresh()
649
650                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
651                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
652                 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
653                         from collections import defaultdict
654                         from gc import get_objects
655                         self._beforeLeakTest = defaultdict(int)
656                         for i in get_objects():
657                                 self._beforeLeakTest[type(i)] += 1
658                 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
659                         from collections import defaultdict
660                         from gc import get_objects
661                         self._afterLeakTest = defaultdict(int)
662                         for i in get_objects():
663                                 self._afterLeakTest[type(i)] += 1
664                         for k in self._afterLeakTest:
665                                 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
666                                         print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
667
668         def ShaderUpdate(self, v, f):
669                 s = opengl.GLShader(v, f)
670                 if s.isValid():
671                         self._objectLoadShader.release()
672                         self._objectLoadShader = s
673                         for obj in self._scene.objects():
674                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
675                         self.QueueRefresh()
676
677         def OnMouseDown(self,e):
678                 self._mouseX = e.GetX()
679                 self._mouseY = e.GetY()
680                 self._mouseClick3DPos = self._mouse3Dpos
681                 self._mouseClickFocus = self._focusObj
682                 if e.ButtonDClick():
683                         self._mouseState = 'doubleClick'
684                 else:
685                         self._mouseState = 'dragOrClick'
686                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
687                 p0 -= self.getObjectCenterPos() - self._viewTarget
688                 p1 -= self.getObjectCenterPos() - self._viewTarget
689                 if self.tool.OnDragStart(p0, p1):
690                         self._mouseState = 'tool'
691                 if self._mouseState == 'dragOrClick':
692                         if e.GetButton() == 1:
693                                 if self._focusObj is not None:
694                                         self._selectObject(self._focusObj, False)
695                                         self.QueueRefresh()
696
697         def OnMouseUp(self, e):
698                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
699                         return
700                 if self._mouseState == 'dragOrClick':
701                         if e.GetButton() == 1:
702                                 self._selectObject(self._focusObj)
703                         if e.GetButton() == 3:
704                                         menu = wx.Menu()
705                                         if self._focusObj is not None:
706                                                 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
707                                                 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
708                                                 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
709                                                 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
710                                         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:
711                                                 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
712                                         if len(self._scene.objects()) > 0:
713                                                 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
714                                         if menu.MenuItemCount > 0:
715                                                 self.PopupMenu(menu)
716                                         menu.Destroy()
717                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
718                         self._scene.pushFree()
719                         self.sceneUpdated()
720                 elif self._mouseState == 'tool':
721                         if self.tempMatrix is not None and self._selectedObj is not None:
722                                 self._selectedObj.applyMatrix(self.tempMatrix)
723                                 self._scene.pushFree()
724                                 self._selectObject(self._selectedObj)
725                         self.tempMatrix = None
726                         self.tool.OnDragEnd()
727                         self.sceneUpdated()
728                 self._mouseState = None
729
730         def OnMouseMotion(self,e):
731                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
732                 p0 -= self.getObjectCenterPos() - self._viewTarget
733                 p1 -= self.getObjectCenterPos() - self._viewTarget
734
735                 if e.Dragging() and self._mouseState is not None:
736                         if self._mouseState == 'tool':
737                                 self.tool.OnDrag(p0, p1)
738                         elif not e.LeftIsDown() and e.RightIsDown():
739                                 self._mouseState = 'drag'
740                                 if wx.GetKeyState(wx.WXK_SHIFT):
741                                         a = math.cos(math.radians(self._yaw)) / 3.0
742                                         b = math.sin(math.radians(self._yaw)) / 3.0
743                                         self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
744                                         self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
745                                         self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
746                                         self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
747                                 else:
748                                         self._yaw += e.GetX() - self._mouseX
749                                         self._pitch -= e.GetY() - self._mouseY
750                                 if self._pitch > 170:
751                                         self._pitch = 170
752                                 if self._pitch < 10:
753                                         self._pitch = 10
754                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
755                                 self._mouseState = 'drag'
756                                 self._zoom += e.GetY() - self._mouseY
757                                 if self._zoom < 1:
758                                         self._zoom = 1
759                                 if self._zoom > numpy.max(self._machineSize) * 3:
760                                         self._zoom = numpy.max(self._machineSize) * 3
761                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
762                                 self._mouseState = 'dragObject'
763                                 z = max(0, self._mouseClick3DPos[2])
764                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
765                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
766                                 p0[2] -= z
767                                 p1[2] -= z
768                                 p2[2] -= z
769                                 p3[2] -= z
770                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
771                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
772                                 diff = cursorZ1 - cursorZ0
773                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
774                 if not e.Dragging() or self._mouseState != 'tool':
775                         self.tool.OnMouseMove(p0, p1)
776
777                 self._mouseX = e.GetX()
778                 self._mouseY = e.GetY()
779
780         def OnMouseWheel(self, e):
781                 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
782                 delta = max(min(delta,4),-4)
783                 self._zoom *= 1.0 - delta / 10.0
784                 if self._zoom < 1.0:
785                         self._zoom = 1.0
786                 if self._zoom > numpy.max(self._machineSize) * 3:
787                         self._zoom = numpy.max(self._machineSize) * 3
788                 self.Refresh()
789
790         def OnMouseLeave(self, e):
791                 #self._mouseX = -1
792                 pass
793
794         def getMouseRay(self, x, y):
795                 if self._viewport is None:
796                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
797                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
798                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
799                 p0 -= self._viewTarget
800                 p1 -= self._viewTarget
801                 return p0, p1
802
803         def _init3DView(self):
804                 # set viewing projection
805                 size = self.GetSize()
806                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
807                 glLoadIdentity()
808
809                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
810
811                 glDisable(GL_RESCALE_NORMAL)
812                 glDisable(GL_LIGHTING)
813                 glDisable(GL_LIGHT0)
814                 glEnable(GL_DEPTH_TEST)
815                 glDisable(GL_CULL_FACE)
816                 glDisable(GL_BLEND)
817                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
818
819                 glClearColor(0.8, 0.8, 0.8, 1.0)
820                 glClearStencil(0)
821                 glClearDepth(1.0)
822
823                 glMatrixMode(GL_PROJECTION)
824                 glLoadIdentity()
825                 aspect = float(size.GetWidth()) / float(size.GetHeight())
826                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
827
828                 glMatrixMode(GL_MODELVIEW)
829                 glLoadIdentity()
830                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
831
832         def OnPaint(self,e):
833                 if machineCom.machineIsConnected():
834                         self.printButton._imageID = 6
835                         self.printButton._tooltip = _("Print")
836                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
837                         self.printButton._imageID = 2
838                         self.printButton._tooltip = _("Toolpath to SD")
839                 else:
840                         self.printButton._imageID = 3
841                         self.printButton._tooltip = _("Save toolpath")
842
843                 if self._animView is not None:
844                         self._viewTarget = self._animView.getPosition()
845                         if self._animView.isDone():
846                                 self._animView = None
847                 if self._animZoom is not None:
848                         self._zoom = self._animZoom.getPosition()
849                         if self._animZoom.isDone():
850                                 self._animZoom = None
851                 if self.viewMode == 'gcode' and self._gcode is not None:
852                         try:
853                                 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
854                         except:
855                                 pass
856                 if self._objectShader is None:
857                         if opengl.hasShaderSupport():
858                                 self._objectShader = opengl.GLShader("""
859 varying float light_amount;
860
861 void main(void)
862 {
863     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
864     gl_FrontColor = gl_Color;
865
866         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
867         light_amount += 0.2;
868 }
869                                 ""","""
870 varying float light_amount;
871
872 void main(void)
873 {
874         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
875 }
876                                 """)
877                                 self._objectOverhangShader = opengl.GLShader("""
878 uniform float cosAngle;
879 uniform mat3 rotMatrix;
880 varying float light_amount;
881
882 void main(void)
883 {
884     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
885     gl_FrontColor = gl_Color;
886
887         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
888         light_amount += 0.2;
889         if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
890         {
891                 light_amount = -10.0;
892         }
893 }
894                                 ""","""
895 varying float light_amount;
896
897 void main(void)
898 {
899         if (light_amount == -10.0)
900         {
901                 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
902         }else{
903                 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
904         }
905 }
906                                 """)
907                                 self._objectLoadShader = opengl.GLShader("""
908 uniform float intensity;
909 uniform float scale;
910 varying float light_amount;
911
912 void main(void)
913 {
914         vec4 tmp = gl_Vertex;
915     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
916     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
917     gl_Position = gl_ModelViewProjectionMatrix * tmp;
918     gl_FrontColor = gl_Color;
919
920         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
921         light_amount += 0.2;
922 }
923                         ""","""
924 uniform float intensity;
925 varying float light_amount;
926
927 void main(void)
928 {
929         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
930 }
931                                 """)
932                         if self._objectShader is None or not self._objectShader.isValid():
933                                 self._objectShader = opengl.GLFakeShader()
934                                 self._objectOverhangShader = opengl.GLFakeShader()
935                                 self._objectLoadShader = None
936                 self._init3DView()
937                 glTranslate(0,0,-self._zoom)
938                 glRotate(-self._pitch, 1,0,0)
939                 glRotate(self._yaw, 0,0,1)
940                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
941
942                 self._viewport = glGetIntegerv(GL_VIEWPORT)
943                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
944                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
945
946                 glClearColor(1,1,1,1)
947                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
948
949                 if self.viewMode != 'gcode':
950                         for n in xrange(0, len(self._scene.objects())):
951                                 obj = self._scene.objects()[n]
952                                 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
953                                 self._renderObject(obj)
954
955                 if self._mouseX > -1:
956                         glFlush()
957                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
958                         if n < len(self._scene.objects()):
959                                 self._focusObj = self._scene.objects()[n]
960                         else:
961                                 self._focusObj = None
962                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
963                         #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
964                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
965                         self._mouse3Dpos -= self._viewTarget
966
967                 self._init3DView()
968                 glTranslate(0,0,-self._zoom)
969                 glRotate(-self._pitch, 1,0,0)
970                 glRotate(self._yaw, 0,0,1)
971                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
972
973                 if self.viewMode == 'gcode':
974                         if self._gcode is not None and self._gcode.layerList is None:
975                                 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
976                                 self._gcodeLoadThread.daemon = True
977                                 self._gcodeLoadThread.start()
978                         if self._gcode is not None and self._gcode.layerList is not None:
979                                 glPushMatrix()
980                                 if profile.getMachineSetting('machine_center_is_zero') != 'True':
981                                         glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
982                                 t = time.time()
983                                 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
984                                 for n in xrange(0, drawUpTill):
985                                         c = 1.0 - float(drawUpTill - n) / 15
986                                         c = max(0.3, c)
987                                         if len(self._gcodeVBOs) < n + 1:
988                                                 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
989                                                 if time.time() - t > 0.5:
990                                                         self.QueueRefresh()
991                                                         break
992                                         #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
993                                         if n == drawUpTill - 1:
994                                                 if len(self._gcodeVBOs[n]) < 9:
995                                                         self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
996                                                 glColor3f(c, 0, 0)
997                                                 self._gcodeVBOs[n][8].render(GL_QUADS)
998                                                 glColor3f(c/2, 0, c)
999                                                 self._gcodeVBOs[n][9].render(GL_QUADS)
1000                                                 glColor3f(0, c, c/2)
1001                                                 self._gcodeVBOs[n][10].render(GL_QUADS)
1002                                                 glColor3f(c, 0, 0)
1003                                                 self._gcodeVBOs[n][11].render(GL_QUADS)
1004
1005                                                 glColor3f(0, c, 0)
1006                                                 self._gcodeVBOs[n][12].render(GL_QUADS)
1007                                                 glColor3f(c/2, c/2, 0.0)
1008                                                 self._gcodeVBOs[n][13].render(GL_QUADS)
1009                                                 glColor3f(0, c, c)
1010                                                 self._gcodeVBOs[n][14].render(GL_QUADS)
1011                                                 self._gcodeVBOs[n][15].render(GL_QUADS)
1012                                                 glColor3f(0, 0, c)
1013                                                 self._gcodeVBOs[n][16].render(GL_LINES)
1014                                         else:
1015                                                 glColor3f(c, 0, 0)
1016                                                 self._gcodeVBOs[n][0].render(GL_LINES)
1017                                                 glColor3f(c/2, 0, c)
1018                                                 self._gcodeVBOs[n][1].render(GL_LINES)
1019                                                 glColor3f(0, c, c/2)
1020                                                 self._gcodeVBOs[n][2].render(GL_LINES)
1021                                                 glColor3f(c, 0, 0)
1022                                                 self._gcodeVBOs[n][3].render(GL_LINES)
1023
1024                                                 glColor3f(0, c, 0)
1025                                                 self._gcodeVBOs[n][4].render(GL_LINES)
1026                                                 glColor3f(c/2, c/2, 0.0)
1027                                                 self._gcodeVBOs[n][5].render(GL_LINES)
1028                                                 glColor3f(0, c, c)
1029                                                 self._gcodeVBOs[n][6].render(GL_LINES)
1030                                                 self._gcodeVBOs[n][7].render(GL_LINES)
1031                                 glPopMatrix()
1032                 else:
1033                         glStencilFunc(GL_ALWAYS, 1, 1)
1034                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1035
1036                         if self.viewMode == 'overhang':
1037                                 self._objectOverhangShader.bind()
1038                                 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1039                         else:
1040                                 self._objectShader.bind()
1041                         for obj in self._scene.objects():
1042                                 if obj._loadAnim is not None:
1043                                         if obj._loadAnim.isDone():
1044                                                 obj._loadAnim = None
1045                                         else:
1046                                                 continue
1047                                 brightness = 1.0
1048                                 if self._focusObj == obj:
1049                                         brightness = 1.2
1050                                 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1051                                         brightness = 0.8
1052
1053                                 if self._selectedObj == obj or self._selectedObj is None:
1054                                         #If we want transparent, then first render a solid black model to remove the printer size lines.
1055                                         if self.viewMode == 'transparent':
1056                                                 glColor4f(0, 0, 0, 0)
1057                                                 self._renderObject(obj)
1058                                                 glEnable(GL_BLEND)
1059                                                 glBlendFunc(GL_ONE, GL_ONE)
1060                                                 glDisable(GL_DEPTH_TEST)
1061                                                 brightness *= 0.5
1062                                         if self.viewMode == 'xray':
1063                                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1064                                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1065                                         glEnable(GL_STENCIL_TEST)
1066
1067                                 if self.viewMode == 'overhang':
1068                                         if self._selectedObj == obj and self.tempMatrix is not None:
1069                                                 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1070                                         else:
1071                                                 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1072
1073                                 if not self._scene.checkPlatform(obj):
1074                                         glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1075                                         self._renderObject(obj)
1076                                 else:
1077                                         self._renderObject(obj, brightness)
1078                                 glDisable(GL_STENCIL_TEST)
1079                                 glDisable(GL_BLEND)
1080                                 glEnable(GL_DEPTH_TEST)
1081                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1082
1083                         if self.viewMode == 'xray':
1084                                 glPushMatrix()
1085                                 glLoadIdentity()
1086                                 glEnable(GL_STENCIL_TEST)
1087                                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1088                                 glDisable(GL_DEPTH_TEST)
1089                                 for i in xrange(2, 15, 2):
1090                                         glStencilFunc(GL_EQUAL, i, 0xFF)
1091                                         glColor(float(i)/10, float(i)/10, float(i)/5)
1092                                         glBegin(GL_QUADS)
1093                                         glVertex3f(-1000,-1000,-10)
1094                                         glVertex3f( 1000,-1000,-10)
1095                                         glVertex3f( 1000, 1000,-10)
1096                                         glVertex3f(-1000, 1000,-10)
1097                                         glEnd()
1098                                 for i in xrange(1, 15, 2):
1099                                         glStencilFunc(GL_EQUAL, i, 0xFF)
1100                                         glColor(float(i)/10, 0, 0)
1101                                         glBegin(GL_QUADS)
1102                                         glVertex3f(-1000,-1000,-10)
1103                                         glVertex3f( 1000,-1000,-10)
1104                                         glVertex3f( 1000, 1000,-10)
1105                                         glVertex3f(-1000, 1000,-10)
1106                                         glEnd()
1107                                 glPopMatrix()
1108                                 glDisable(GL_STENCIL_TEST)
1109                                 glEnable(GL_DEPTH_TEST)
1110
1111                         self._objectShader.unbind()
1112
1113                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1114                         glEnable(GL_BLEND)
1115                         if self._objectLoadShader is not None:
1116                                 self._objectLoadShader.bind()
1117                                 glColor4f(0.2, 0.6, 1.0, 1.0)
1118                                 for obj in self._scene.objects():
1119                                         if obj._loadAnim is None:
1120                                                 continue
1121                                         self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1122                                         self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1123                                         self._renderObject(obj)
1124                                 self._objectLoadShader.unbind()
1125                                 glDisable(GL_BLEND)
1126
1127                 self._drawMachine()
1128
1129                 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1130                         glEnable(GL_BLEND)
1131                         z = self._usbPrintMonitor.getZ()
1132                         size = self._machineSize
1133                         glColor4ub(255,255,0,128)
1134                         glBegin(GL_QUADS)
1135                         glVertex3f(-size[0]/2,-size[1]/2, z)
1136                         glVertex3f( size[0]/2,-size[1]/2, z)
1137                         glVertex3f( size[0]/2, size[1]/2, z)
1138                         glVertex3f(-size[0]/2, size[1]/2, z)
1139                         glEnd()
1140
1141                 if self.viewMode == 'gcode':
1142                         if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1143                                 glDisable(GL_DEPTH_TEST)
1144                                 glPushMatrix()
1145                                 glLoadIdentity()
1146                                 glTranslate(0,-4,-10)
1147                                 glColor4ub(60,60,60,255)
1148                                 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1149                                 glPopMatrix()
1150                 else:
1151                         #Draw the object box-shadow, so you can see where it will collide with other objects.
1152                         if self._selectedObj is not None and len(self._scene.objects()) > 1:
1153                                 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1154                                 glPushMatrix()
1155                                 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1156                                 glEnable(GL_BLEND)
1157                                 glEnable(GL_CULL_FACE)
1158                                 glColor4f(0,0,0,0.12)
1159                                 glBegin(GL_QUADS)
1160                                 glVertex3f(-size[0],  size[1], 0.1)
1161                                 glVertex3f(-size[0], -size[1], 0.1)
1162                                 glVertex3f( size[0], -size[1], 0.1)
1163                                 glVertex3f( size[0],  size[1], 0.1)
1164                                 glEnd()
1165                                 glDisable(GL_CULL_FACE)
1166                                 glPopMatrix()
1167
1168                         #Draw the outline of the selected object, on top of everything else except the GUI.
1169                         if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1170                                 glDisable(GL_DEPTH_TEST)
1171                                 glEnable(GL_CULL_FACE)
1172                                 glEnable(GL_STENCIL_TEST)
1173                                 glDisable(GL_BLEND)
1174                                 glStencilFunc(GL_EQUAL, 0, 255)
1175
1176                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1177                                 glLineWidth(2)
1178                                 glColor4f(1,1,1,0.5)
1179                                 self._renderObject(self._selectedObj)
1180                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1181
1182                                 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1183                                 glDisable(GL_STENCIL_TEST)
1184                                 glDisable(GL_CULL_FACE)
1185                                 glEnable(GL_DEPTH_TEST)
1186
1187                         if self._selectedObj is not None:
1188                                 glPushMatrix()
1189                                 pos = self.getObjectCenterPos()
1190                                 glTranslate(pos[0], pos[1], pos[2])
1191                                 self.tool.OnDraw()
1192                                 glPopMatrix()
1193                 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1194                         glDisable(GL_DEPTH_TEST)
1195                         glPushMatrix()
1196                         glLoadIdentity()
1197                         glTranslate(0,-4,-10)
1198                         glColor4ub(60,60,60,255)
1199                         opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1200                         glPopMatrix()
1201
1202         def _renderObject(self, obj, brightness = False, addSink = True):
1203                 glPushMatrix()
1204                 if addSink:
1205                         glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1206                 else:
1207                         glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1208
1209                 if self.tempMatrix is not None and obj == self._selectedObj:
1210                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1211                         glMultMatrixf(tempMatrix)
1212
1213                 offset = obj.getDrawOffset()
1214                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1215
1216                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1217                 glMultMatrixf(tempMatrix)
1218
1219                 n = 0
1220                 for m in obj._meshList:
1221                         if m.vbo is None:
1222                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1223                         if brightness:
1224                                 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1225                                 n += 1
1226                         m.vbo.render()
1227                 glPopMatrix()
1228
1229         def _drawMachine(self):
1230                 glEnable(GL_CULL_FACE)
1231                 glEnable(GL_BLEND)
1232
1233                 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1234
1235                 machine = profile.getMachineSetting('machine_type')
1236                 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1237                         if machine not in self._platformMesh:
1238                                 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1239                                 if len(meshes) > 0:
1240                                         self._platformMesh[machine] = meshes[0]
1241                                 else:
1242                                         self._platformMesh[machine] = None
1243                                 if machine == 'ultimaker2':
1244                                         self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1245                                 else:
1246                                         self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1247                         glColor4f(1,1,1,0.5)
1248                         self._objectShader.bind()
1249                         self._renderObject(self._platformMesh[machine], False, False)
1250                         self._objectShader.unbind()
1251
1252                         #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1253                         if machine == 'ultimaker2':
1254                                 if not hasattr(self._platformMesh[machine], 'texture'):
1255                                         self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1256                                 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1257                                 glEnable(GL_TEXTURE_2D)
1258                                 glPushMatrix()
1259                                 glColor4f(1,1,1,1)
1260
1261                                 glTranslate(0,150,-5)
1262                                 h = 50
1263                                 d = 8
1264                                 w = 100
1265                                 glEnable(GL_BLEND)
1266                                 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1267                                 glBegin(GL_QUADS)
1268                                 glTexCoord2f(1, 0)
1269                                 glVertex3f( w, 0, h)
1270                                 glTexCoord2f(0, 0)
1271                                 glVertex3f(-w, 0, h)
1272                                 glTexCoord2f(0, 1)
1273                                 glVertex3f(-w, 0, 0)
1274                                 glTexCoord2f(1, 1)
1275                                 glVertex3f( w, 0, 0)
1276
1277                                 glTexCoord2f(1, 0)
1278                                 glVertex3f(-w, d, h)
1279                                 glTexCoord2f(0, 0)
1280                                 glVertex3f( w, d, h)
1281                                 glTexCoord2f(0, 1)
1282                                 glVertex3f( w, d, 0)
1283                                 glTexCoord2f(1, 1)
1284                                 glVertex3f(-w, d, 0)
1285                                 glEnd()
1286                                 glDisable(GL_TEXTURE_2D)
1287                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1288                                 glPopMatrix()
1289                 else:
1290                         glColor4f(0,0,0,1)
1291                         glLineWidth(3)
1292                         glBegin(GL_LINES)
1293                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1294                         glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1295                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1296                         glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1297                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1298                         glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1299                         glEnd()
1300
1301                 #Cornerpoints for big blue square
1302                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1303                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1304                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1305                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1306                 v4 = [ size[0] / 2, size[1] / 2, 0]
1307                 v5 = [ size[0] / 2,-size[1] / 2, 0]
1308                 v6 = [-size[0] / 2, size[1] / 2, 0]
1309                 v7 = [-size[0] / 2,-size[1] / 2, 0]
1310
1311                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1312                 glEnableClientState(GL_VERTEX_ARRAY)
1313                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1314
1315                 glColor4ub(5, 171, 231, 64)
1316                 glDrawArrays(GL_QUADS, 0, 4)
1317                 glColor4ub(5, 171, 231, 96)
1318                 glDrawArrays(GL_QUADS, 4, 8)
1319                 glColor4ub(5, 171, 231, 128)
1320                 glDrawArrays(GL_QUADS, 12, 8)
1321                 glDisableClientState(GL_VERTEX_ARRAY)
1322
1323                 #Draw checkerboard
1324                 sx = self._machineSize[0]
1325                 sy = self._machineSize[1]
1326                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1327                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1328                                 x1 = x * 10
1329                                 x2 = x1 + 10
1330                                 y1 = y * 10
1331                                 y2 = y1 + 10
1332                                 x1 = max(min(x1, sx/2), -sx/2)
1333                                 y1 = max(min(y1, sy/2), -sy/2)
1334                                 x2 = max(min(x2, sx/2), -sx/2)
1335                                 y2 = max(min(y2, sy/2), -sy/2)
1336                                 #Black or "white"  checker
1337                                 if (x & 1) == (y & 1):
1338                                         glColor4ub(5, 171, 231, 127)
1339                                 else:
1340                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1341                                 glBegin(GL_QUADS)
1342                                 glVertex3f(x1, y1, -0.02) #Draw bit below z0 to prevent zfighting.
1343                                 glVertex3f(x2, y1, -0.02)
1344                                 glVertex3f(x2, y2, -0.02)
1345                                 glVertex3f(x1, y2, -0.02)
1346                                 glEnd()
1347
1348                 if machine == 'ultimaker2':
1349
1350                         glColor4ub(127, 127, 127, 200)
1351                         #if UM2, draw bat-area zone for head. THe head can't stop there, because its bat-area.
1352                         #UpperRight
1353                         clipWidth = 50;
1354                         clipHeight = 35;
1355                         posX = sx / 2 - clipWidth;
1356                         posY = sy / 2 - clipHeight;
1357                         glBegin(GL_QUADS)
1358                         glVertex3f(posX, posY, 0.1)
1359                         glVertex3f(posX+clipWidth, posY, 0.1)
1360                         glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1361                         glVertex3f(posX, posY+clipHeight, 0.1)
1362                         glEnd()
1363                         #UpperLeft
1364                         clipWidth = 55;
1365                         clipHeight = 35;
1366                         posX = -sx / 2;
1367                         posY = sy / 2 - clipHeight;
1368                         glBegin(GL_QUADS)
1369                         glVertex3f(posX, posY, 0.1)
1370                         glVertex3f(posX+clipWidth, posY, 0.1)
1371                         glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1372                         glVertex3f(posX, posY+clipHeight, 0.1)
1373                         glEnd()
1374                         #LowerRight
1375                         clipWidth = 50;
1376                         clipHeight = 5;
1377                         posX = sx / 2 - clipWidth;
1378                         posY = -sy / 2;
1379                         glBegin(GL_QUADS)
1380                         glVertex3f(posX, posY, 0.1)
1381                         glVertex3f(posX+clipWidth, posY, 0.1)
1382                         glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1383                         glVertex3f(posX, posY+clipHeight, 0.1)
1384                         glEnd()
1385                         #LowerLeft
1386                         clipWidth = 55;
1387                         clipHeight = 5;
1388                         posX = -sx / 2;
1389                         posY = -sy / 2;
1390                         glBegin(GL_QUADS)
1391                         glVertex3f(posX, posY, 0.1)
1392                         glVertex3f(posX+clipWidth, posY, 0.1)
1393                         glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1394                         glVertex3f(posX, posY+clipHeight, 0.1)
1395                         glEnd()
1396
1397
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())