chiark / gitweb /
Improve the startup a bit, hide the splashscreen at a better timing and do not reload...
[cura.git] / Cura / gui / sceneView.py
1 from __future__ import absolute_import
2
3 import wx
4 import numpy
5 import time
6 import os
7 import traceback
8 import shutil
9 import threading
10 import math
11
12 import OpenGL
13 OpenGL.ERROR_CHECKING = False
14 from OpenGL.GLU import *
15 from OpenGL.GL import *
16
17 from Cura.gui import printWindow
18 from Cura.util import profile
19 from Cura.util import meshLoader
20 from Cura.util import objectScene
21 from Cura.util import resources
22 from Cura.util import sliceEngine
23 from Cura.util import machineCom
24 from Cura.util import removableStorage
25 from Cura.util import gcodeInterpreter
26 from Cura.gui.util import previewTools
27 from Cura.gui.util import opengl
28 from Cura.gui.util import openglGui
29
30 class SceneView(openglGui.glGuiPanel):
31         def __init__(self, parent):
32                 super(SceneView, self).__init__(parent)
33
34                 self._yaw = 30
35                 self._pitch = 60
36                 self._zoom = 300
37                 self._scene = objectScene.Scene()
38                 self._gcode = None
39                 self._gcodeVBOs = []
40                 self._objectShader = None
41                 self._focusObj = None
42                 self._selectedObj = None
43                 self._objColors = [None,None,None,None]
44                 self._mouseX = -1
45                 self._mouseY = -1
46                 self._mouseState = None
47                 self._viewTarget = numpy.array([0,0,0], numpy.float32)
48                 self._animView = None
49                 self._animZoom = None
50                 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
51                 self._platformMesh._drawOffset = numpy.array([0,0,1.5], numpy.float32)
52                 self._isSimpleMode = True
53
54                 self._viewport = None
55                 self._modelMatrix = None
56                 self._projMatrix = None
57                 self.tempMatrix = None
58
59                 self.openFileButton      = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
60                 self.printButton         = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
61                 self.printButton.setDisabled(True)
62
63                 group = []
64                 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
65                 self.scaleToolButton  = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
66                 self.mirrorToolButton  = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
67
68                 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
69                 self.layFlatButton       = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
70
71                 self.resetScaleButton    = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
72                 self.scaleMaxButton      = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
73
74                 self.mirrorXButton       = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
75                 self.mirrorYButton       = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
76                 self.mirrorZButton       = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
77
78                 self.rotateToolButton.setExpandArrow(True)
79                 self.scaleToolButton.setExpandArrow(True)
80                 self.mirrorToolButton.setExpandArrow(True)
81
82                 self.scaleForm = openglGui.glFrame(self, (2, -2))
83                 openglGui.glGuiLayoutGrid(self.scaleForm)
84                 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
85                 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
86                 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
87                 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
88                 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
89                 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
90                 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
91                 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
92                 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
93                 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
94                 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
95                 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
96                 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
97                 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
98
99                 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,11,15,23], ['Normal', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
100                 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
101
102                 self.notification = openglGui.glNotification(self, (0, 0))
103
104                 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
105                 self._sceneUpdateTimer = wx.Timer(self)
106                 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
107                 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
108
109                 self.OnViewChange()
110                 self.OnToolSelect(0)
111                 self.updateToolButtons()
112                 self.updateProfileToControls()
113
114         def ShowLoadModel(self, button):
115                 if button == 1:
116                         dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
117                         dlg.SetWildcard(meshLoader.wildcardFilter())
118                         if dlg.ShowModal() != wx.ID_OK:
119                                 dlg.Destroy()
120                                 return
121                         filename = dlg.GetPath()
122                         dlg.Destroy()
123                         if not(os.path.exists(filename)):
124                                 return False
125                         profile.putPreference('lastFile', filename)
126                         self.GetParent().GetParent().GetParent().addToModelMRU(filename)
127                         self.loadScene([filename])
128
129         def ShowPrintWindow(self, button):
130                 if button == 1:
131                         if machineCom.machineIsConnected():
132                                 printWindow.printFile(self._slicer.getGCodeFilename())
133                         elif len(removableStorage.getPossibleSDcardDrives()) > 0:
134                                 drives = removableStorage.getPossibleSDcardDrives()
135                                 if len(drives) > 1:
136                                         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))
137                                         if dlg.ShowModal() != wx.ID_OK:
138                                                 dlg.Destroy()
139                                                 return
140                                         drive = drives[dlg.GetSelection()]
141                                         dlg.Destroy()
142                                 else:
143                                         drive = drives[0]
144                                 filename = os.path.basename(profile.getPreference('lastFile'))
145                                 filename = filename[0:filename.rfind('.')] + '.gcode'
146                                 try:
147                                         shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
148                                 except:
149                                         self.notification.message("Failed to save to SD card")
150                                 else:
151                                         self.notification.message("Saved as %s" % (drive[1] + filename))
152                         else:
153                                 self.showSaveGCode()
154                 if button == 3:
155                         menu = wx.Menu()
156                         self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._slicer.getGCodeFilename()), menu.Append(-1, 'Print with USB'))
157                         self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
158                         self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
159                         self.PopupMenu(menu)
160                         menu.Destroy()
161
162         def showSaveGCode(self):
163                 defPath = profile.getPreference('lastFile')
164                 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
165                 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
166                 dlg.SetFilename(defPath)
167                 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
168                 if dlg.ShowModal() != wx.ID_OK:
169                         dlg.Destroy()
170                         return
171                 filename = dlg.GetPath()
172                 dlg.Destroy()
173
174                 try:
175                         shutil.copy(self._slicer.getGCodeFilename(), filename)
176                 except:
177                         self.notification.message("Failed to save")
178                 else:
179                         self.notification.message("Saved as %s" % (filename))
180
181         def _showSliceLog(self):
182                 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
183                 dlg.ShowModal()
184                 dlg.Destroy()
185
186         def OnToolSelect(self, button):
187                 if self.rotateToolButton.getSelected():
188                         self.tool = previewTools.toolRotate(self)
189                 elif self.scaleToolButton.getSelected():
190                         self.tool = previewTools.toolScale(self)
191                 elif self.mirrorToolButton.getSelected():
192                         self.tool = previewTools.toolNone(self)
193                 else:
194                         self.tool = previewTools.toolNone(self)
195                 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
196                 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
197                 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
198                 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
199                 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
200                 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
201                 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
202                 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
203
204         def updateToolButtons(self):
205                 if self._selectedObj is None:
206                         hidden = True
207                 else:
208                         hidden = False
209                 self.rotateToolButton.setHidden(hidden)
210                 self.scaleToolButton.setHidden(hidden)
211                 self.mirrorToolButton.setHidden(hidden)
212                 if hidden:
213                         self.rotateToolButton.setSelected(False)
214                         self.scaleToolButton.setSelected(False)
215                         self.mirrorToolButton.setSelected(False)
216                         self.OnToolSelect(0)
217
218         def OnViewChange(self):
219                 if self.viewSelection.getValue() == 3:
220                         self.viewMode = 'gcode'
221                         if self._gcode is not None:
222                                 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
223                                 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
224                         self._selectObject(None)
225                 elif self.viewSelection.getValue() == 1:
226                         self.viewMode = 'transparent'
227                 elif self.viewSelection.getValue() == 2:
228                         self.viewMode = 'xray'
229                 else:
230                         self.viewMode = 'normal'
231                 self.layerSelect.setHidden(self.viewMode != 'gcode')
232                 self.openFileButton.setHidden(self.viewMode == 'gcode')
233                 self.QueueRefresh()
234
235         def OnRotateReset(self, button):
236                 if self._selectedObj is None:
237                         return
238                 self._selectedObj.resetRotation()
239                 self._scene.pushFree()
240                 self._selectObject(self._selectedObj)
241
242         def OnLayFlat(self, button):
243                 if self._selectedObj is None:
244                         return
245                 self._selectedObj.layFlat()
246                 self._scene.pushFree()
247                 self._selectObject(self._selectedObj)
248
249         def OnScaleReset(self, button):
250                 if self._selectedObj is None:
251                         return
252                 self._selectedObj.resetScale()
253
254         def OnScaleMax(self, button):
255                 if self._selectedObj is None:
256                         return
257                 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
258                 self._scene.pushFree()
259                 self._selectObject(self._selectedObj)
260                 self.updateProfileToControls()
261                 self.sceneUpdated()
262
263         def OnMirror(self, axis):
264                 if self._selectedObj is None:
265                         return
266                 self._selectedObj.mirror(axis)
267                 self.sceneUpdated()
268
269         def OnScaleEntry(self, value, axis):
270                 if self._selectedObj is None:
271                         return
272                 try:
273                         value = float(value)
274                 except:
275                         return
276                 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
277                 self.updateProfileToControls()
278                 self._scene.pushFree()
279                 self._selectObject(self._selectedObj)
280                 self.sceneUpdated()
281
282         def OnScaleEntryMM(self, value, axis):
283                 if self._selectedObj is None:
284                         return
285                 try:
286                         value = float(value)
287                 except:
288                         return
289                 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
290                 self.updateProfileToControls()
291                 self._scene.pushFree()
292                 self._selectObject(self._selectedObj)
293                 self.sceneUpdated()
294
295         def OnDeleteAll(self, e):
296                 while len(self._scene.objects()) > 0:
297                         self._deleteObject(self._scene.objects()[0])
298                 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
299
300         def OnMultiply(self, e):
301                 if self._focusObj is None:
302                         return
303                 obj = self._focusObj
304                 dlg = wx.NumberEntryDialog(self, "How many items do you want?", "Copies", "Multiply", 2, 1, 100)
305                 if dlg.ShowModal() != wx.ID_OK:
306                         dlg.Destroy()
307                         return
308                 cnt = dlg.GetValue() - 1
309                 dlg.Destroy()
310                 n = 0
311                 while True:
312                         n += 1
313                         newObj = obj.copy()
314                         self._scene.add(newObj)
315                         self._scene.centerAll()
316                         if not self._scene.checkPlatform(newObj):
317                                 break
318                         if n > cnt:
319                                 break
320                 if n <= cnt:
321                         self.notification.message("Could not create more then %d items" % (n))
322                 self._scene.remove(newObj)
323                 self._scene.centerAll()
324                 self.sceneUpdated()
325
326         def OnSplitObject(self, e):
327                 if self._focusObj is None:
328                         return
329                 self._scene.remove(self._focusObj)
330                 for obj in self._focusObj.split():
331                         self._scene.add(obj)
332                 self._scene.centerAll()
333                 self._selectObject(None)
334                 self.sceneUpdated()
335
336         def OnMergeObjects(self, e):
337                 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
338                         return
339                 self._scene.merge(self._selectedObj, self._focusObj)
340                 self.sceneUpdated()
341
342         def sceneUpdated(self):
343                 self._sceneUpdateTimer.Start(1, True)
344                 self._slicer.abortSlicer()
345                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
346                 self.QueueRefresh()
347
348         def _onRunSlicer(self, e):
349                 if self._isSimpleMode:
350                         self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
351                 self._slicer.runSlicer(self._scene)
352                 if self._isSimpleMode:
353                         profile.resetTempOverride()
354
355         def _updateSliceProgress(self, progressValue, ready):
356                 self.printButton.setDisabled(not ready)
357                 self.printButton.setProgressBar(progressValue)
358                 if self._gcode is not None:
359                         self._gcode = None
360                         for layerVBOlist in self._gcodeVBOs:
361                                 for vbo in layerVBOlist:
362                                         self.glReleaseList.append(vbo)
363                         self._gcodeVBOs = []
364                 if ready:
365                         print self._slicer.getFilamentAmount()
366                         print self._slicer.getPrintTime()
367                         print self._slicer.getFilamentCost()
368                         self._gcode = gcodeInterpreter.gcode()
369                         self._gcode.progressCallback = self._gcodeLoadCallback
370                         self._thread = threading.Thread(target=self._loadGCode)
371                         self._thread.daemon = True
372                         self._thread.start()
373                 self.QueueRefresh()
374
375         def _loadGCode(self):
376                 self._gcode.load(self._slicer.getGCodeFilename())
377
378         def _gcodeLoadCallback(self, progress):
379                 if self._gcode is None:
380                         return True
381                 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
382                         self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
383                         self.layerSelect.setValue(self.layerSelect.getMaxValue())
384                 else:
385                         self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
386                 return False
387
388         def loadScene(self, fileList):
389                 for filename in fileList:
390                         try:
391                                 objList = meshLoader.loadMeshes(filename)
392                         except:
393                                 traceback.print_exc()
394                         else:
395                                 for obj in objList:
396                                         obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
397                                         self._scene.add(obj)
398                                         self._scene.centerAll()
399                                         self._selectObject(obj)
400                 self.sceneUpdated()
401
402         def _deleteObject(self, obj):
403                 if obj == self._selectedObj:
404                         self._selectObject(None)
405                 if obj == self._focusObj:
406                         self._focusObj = None
407                 self._scene.remove(obj)
408                 for m in obj._meshList:
409                         if m.vbo is not None and m.vbo.decRef():
410                                 self.glReleaseList.append(m.vbo)
411                 if self._isSimpleMode:
412                         self._scene.arrangeAll()
413                 self.sceneUpdated()
414
415         def _selectObject(self, obj, zoom = True):
416                 if obj != self._selectedObj:
417                         self._selectedObj = obj
418                         self.updateProfileToControls()
419                         self.updateToolButtons()
420                 if zoom and obj is not None:
421                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
422                         self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
423                         newZoom = obj.getBoundaryCircle() * 6
424                         if newZoom > numpy.max(self._machineSize) * 3:
425                                 newZoom = numpy.max(self._machineSize) * 3
426                         self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
427
428         def updateProfileToControls(self):
429                 oldSimpleMode = self._isSimpleMode
430                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
431                 if self._isSimpleMode and not oldSimpleMode:
432                         self._scene.arrangeAll()
433                         self.sceneUpdated()
434                 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
435                 self._objColors[0] = profile.getPreferenceColour('model_colour')
436                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
437                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
438                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
439                 self._scene.setMachineSize(self._machineSize)
440                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
441                 self._scene.setHeadSize(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_min_y'), profile.getPreferenceFloat('extruder_head_size_max_y'), profile.getPreferenceFloat('extruder_head_size_height'))
442
443                 if self._selectedObj is not None:
444                         scale = self._selectedObj.getScale()
445                         size = self._selectedObj.getSize()
446                         self.scaleXctrl.setValue(round(scale[0], 2))
447                         self.scaleYctrl.setValue(round(scale[1], 2))
448                         self.scaleZctrl.setValue(round(scale[2], 2))
449                         self.scaleXmmctrl.setValue(round(size[0], 2))
450                         self.scaleYmmctrl.setValue(round(size[1], 2))
451                         self.scaleZmmctrl.setValue(round(size[2], 2))
452
453         def OnKeyChar(self, keyCode):
454                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
455                         if self._selectedObj is not None:
456                                 self._deleteObject(self._selectedObj)
457                                 self.QueueRefresh()
458                 if keyCode == wx.WXK_UP:
459                         self.layerSelect.setValue(self.layerSelect.getValue() + 1)
460                         self.QueueRefresh()
461                 elif keyCode == wx.WXK_DOWN:
462                         self.layerSelect.setValue(self.layerSelect.getValue() - 1)
463                         self.QueueRefresh()
464                 elif keyCode == wx.WXK_PAGEUP:
465                         self.layerSelect.setValue(self.layerSelect.getValue() + 10)
466                         self.QueueRefresh()
467                 elif keyCode == wx.WXK_PAGEDOWN:
468                         self.layerSelect.setValue(self.layerSelect.getValue() - 10)
469                         self.QueueRefresh()
470
471                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
472                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
473
474         def ShaderUpdate(self, v, f):
475                 s = opengl.GLShader(v, f)
476                 if s.isValid():
477                         self._objectLoadShader.release()
478                         self._objectLoadShader = s
479                         for obj in self._scene.objects():
480                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
481                         self.QueueRefresh()
482
483         def OnMouseDown(self,e):
484                 self._mouseX = e.GetX()
485                 self._mouseY = e.GetY()
486                 self._mouseClick3DPos = self._mouse3Dpos
487                 self._mouseClickFocus = self._focusObj
488                 if e.ButtonDClick():
489                         self._mouseState = 'doubleClick'
490                 else:
491                         self._mouseState = 'dragOrClick'
492                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
493                 p0 -= self.getObjectCenterPos() - self._viewTarget
494                 p1 -= self.getObjectCenterPos() - self._viewTarget
495                 if self.tool.OnDragStart(p0, p1):
496                         self._mouseState = 'tool'
497                 if self._mouseState == 'dragOrClick':
498                         if e.GetButton() == 1:
499                                 if self._focusObj is not None:
500                                         self._selectObject(self._focusObj, False)
501                                         self.QueueRefresh()
502
503         def OnMouseUp(self, e):
504                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
505                         return
506                 if self._mouseState == 'dragOrClick':
507                         if e.GetButton() == 1:
508                                 self._selectObject(self._focusObj)
509                         if e.GetButton() == 3:
510                                         menu = wx.Menu()
511                                         if self._focusObj is not None:
512                                                 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
513                                                 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
514                                                 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
515                                         if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
516                                                 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
517                                         if len(self._scene.objects()) > 0:
518                                                 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
519                                         if menu.MenuItemCount > 0:
520                                                 self.PopupMenu(menu)
521                                         menu.Destroy()
522                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
523                         self._scene.pushFree()
524                         self.sceneUpdated()
525                 elif self._mouseState == 'tool':
526                         if self.tempMatrix is not None and self._selectedObj is not None:
527                                 self._selectedObj.applyMatrix(self.tempMatrix)
528                                 self._scene.pushFree()
529                                 self._selectObject(self._selectedObj)
530                         self.tempMatrix = None
531                         self.tool.OnDragEnd()
532                         self.sceneUpdated()
533                 self._mouseState = None
534
535         def OnMouseMotion(self,e):
536                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
537                 p0 -= self.getObjectCenterPos() - self._viewTarget
538                 p1 -= self.getObjectCenterPos() - self._viewTarget
539
540                 if e.Dragging() and self._mouseState is not None:
541                         if self._mouseState == 'tool':
542                                 self.tool.OnDrag(p0, p1)
543                         elif not e.LeftIsDown() and e.RightIsDown():
544                                 self._mouseState = 'drag'
545                                 if wx.GetKeyState(wx.WXK_SHIFT):
546                                         a = math.cos(math.radians(self._yaw)) / 3.0
547                                         b = math.sin(math.radians(self._yaw)) / 3.0
548                                         self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
549                                         self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
550                                         self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
551                                         self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
552                                 else:
553                                         self._yaw += e.GetX() - self._mouseX
554                                         self._pitch -= e.GetY() - self._mouseY
555                                 if self._pitch > 170:
556                                         self._pitch = 170
557                                 if self._pitch < 10:
558                                         self._pitch = 10
559                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
560                                 self._mouseState = 'drag'
561                                 self._zoom += e.GetY() - self._mouseY
562                                 if self._zoom < 1:
563                                         self._zoom = 1
564                                 if self._zoom > numpy.max(self._machineSize) * 3:
565                                         self._zoom = numpy.max(self._machineSize) * 3
566                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
567                                 self._mouseState = 'dragObject'
568                                 z = max(0, self._mouseClick3DPos[2])
569                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
570                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
571                                 p0[2] -= z
572                                 p1[2] -= z
573                                 p2[2] -= z
574                                 p3[2] -= z
575                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
576                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
577                                 diff = cursorZ1 - cursorZ0
578                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
579                 if not e.Dragging() or self._mouseState != 'tool':
580                         self.tool.OnMouseMove(p0, p1)
581
582                 self._mouseX = e.GetX()
583                 self._mouseY = e.GetY()
584
585         def OnMouseWheel(self, e):
586                 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
587                 delta = max(min(delta,4),-4)
588                 self._zoom *= 1.0 - delta / 10.0
589                 if self._zoom < 1.0:
590                         self._zoom = 1.0
591                 if self._zoom > numpy.max(self._machineSize) * 3:
592                         self._zoom = numpy.max(self._machineSize) * 3
593                 self.Refresh()
594
595         def getMouseRay(self, x, y):
596                 if self._viewport is None:
597                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
598                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
599                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
600                 p0 -= self._viewTarget
601                 p1 -= self._viewTarget
602                 return p0, p1
603
604         def _init3DView(self):
605                 # set viewing projection
606                 size = self.GetSize()
607                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
608                 glLoadIdentity()
609
610                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
611
612                 glDisable(GL_RESCALE_NORMAL)
613                 glDisable(GL_LIGHTING)
614                 glDisable(GL_LIGHT0)
615                 glEnable(GL_DEPTH_TEST)
616                 glDisable(GL_CULL_FACE)
617                 glDisable(GL_BLEND)
618                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
619
620                 glClearColor(0.8, 0.8, 0.8, 1.0)
621                 glClearStencil(0)
622                 glClearDepth(1.0)
623
624                 glMatrixMode(GL_PROJECTION)
625                 glLoadIdentity()
626                 aspect = float(size.GetWidth()) / float(size.GetHeight())
627                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
628
629                 glMatrixMode(GL_MODELVIEW)
630                 glLoadIdentity()
631                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
632
633         def OnPaint(self,e):
634                 if machineCom.machineIsConnected():
635                         self.printButton._imageID = 6
636                         self.printButton._tooltip = 'Print'
637                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
638                         self.printButton._imageID = 2
639                         self.printButton._tooltip = 'Toolpath to SD'
640                 else:
641                         self.printButton._imageID = 3
642                         self.printButton._tooltip = 'Save toolpath'
643
644                 if self._animView is not None:
645                         self._viewTarget = self._animView.getPosition()
646                         if self._animView.isDone():
647                                 self._animView = None
648                 if self._animZoom is not None:
649                         self._zoom = self._animZoom.getPosition()
650                         if self._animZoom.isDone():
651                                 self._animZoom = None
652                 if self._objectShader is None:
653                         self._objectShader = opengl.GLShader("""
654 varying float light_amount;
655
656 void main(void)
657 {
658     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
659     gl_FrontColor = gl_Color;
660
661         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
662         light_amount += 0.2;
663 }
664                         ""","""
665 varying float light_amount;
666
667 void main(void)
668 {
669         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
670 }
671                         """)
672                         self._objectLoadShader = opengl.GLShader("""
673 uniform float intensity;
674 uniform float scale;
675 varying float light_amount;
676
677 void main(void)
678 {
679         vec4 tmp = gl_Vertex;
680     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
681     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
682     gl_Position = gl_ModelViewProjectionMatrix * tmp;
683     gl_FrontColor = gl_Color;
684
685         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
686         light_amount += 0.2;
687 }
688                         ""","""
689 uniform float intensity;
690 varying float light_amount;
691
692 void main(void)
693 {
694         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
695 }
696                         """)
697                 self._init3DView()
698                 glTranslate(0,0,-self._zoom)
699                 glRotate(-self._pitch, 1,0,0)
700                 glRotate(self._yaw, 0,0,1)
701                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
702
703                 self._viewport = glGetIntegerv(GL_VIEWPORT)
704                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
705                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
706
707                 glClearColor(1,1,1,1)
708                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
709
710                 if self.viewMode != 'gcode':
711                         for n in xrange(0, len(self._scene.objects())):
712                                 obj = self._scene.objects()[n]
713                                 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
714                                 self._renderObject(obj)
715
716                 if self._mouseX > -1:
717                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
718                         if n < len(self._scene.objects()):
719                                 self._focusObj = self._scene.objects()[n]
720                         else:
721                                 self._focusObj = None
722                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
723                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
724                         self._mouse3Dpos -= self._viewTarget
725
726                 self._init3DView()
727                 glTranslate(0,0,-self._zoom)
728                 glRotate(-self._pitch, 1,0,0)
729                 glRotate(self._yaw, 0,0,1)
730                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
731
732                 if self.viewMode == 'gcode':
733                         if self._gcode is not None:
734                                 glPushMatrix()
735                                 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
736                                 t = time.time()
737                                 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
738                                 for n in xrange(0, drawUpTill):
739                                         c = 1.0 - float(drawUpTill - n) / 15
740                                         c = max(0.3, c)
741                                         if len(self._gcodeVBOs) < n + 1:
742                                                 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
743                                                 if time.time() - t > 0.5:
744                                                         self.QueueRefresh()
745                                                         break
746                                         #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
747                                         if n == drawUpTill - 1:
748                                                 if len(self._gcodeVBOs[n]) < 6:
749                                                         self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
750                                                 glColor3f(c, 0, 0)
751                                                 self._gcodeVBOs[n][5].render(GL_QUADS)
752                                                 glColor3f(0, c, 0)
753                                                 self._gcodeVBOs[n][6].render(GL_QUADS)
754                                                 glColor3f(c/2, c/2, 0.0)
755                                                 self._gcodeVBOs[n][7].render(GL_QUADS)
756                                                 glColor3f(0, c, c)
757                                                 self._gcodeVBOs[n][8].render(GL_QUADS)
758                                                 self._gcodeVBOs[n][9].render(GL_QUADS)
759                                         else:
760                                                 glColor3f(c, 0, 0)
761                                                 self._gcodeVBOs[n][0].render(GL_LINES)
762                                                 glColor3f(0, c, 0)
763                                                 self._gcodeVBOs[n][1].render(GL_LINES)
764                                                 glColor3f(c/2, c/2, 0.0)
765                                                 self._gcodeVBOs[n][2].render(GL_LINES)
766                                                 glColor3f(0, c, c)
767                                                 self._gcodeVBOs[n][3].render(GL_LINES)
768                                                 self._gcodeVBOs[n][4].render(GL_LINES)
769                                 glPopMatrix()
770                 else:
771                         glStencilFunc(GL_ALWAYS, 1, 1)
772                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
773
774                         self._objectShader.bind()
775                         for obj in self._scene.objects():
776                                 if obj._loadAnim is not None:
777                                         if obj._loadAnim.isDone():
778                                                 obj._loadAnim = None
779                                         else:
780                                                 continue
781                                 brightness = 1.0
782                                 if self._focusObj == obj:
783                                         brightness = 1.2
784                                 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
785                                         brightness = 0.8
786
787                                 if self._selectedObj == obj or self._selectedObj is None:
788                                         #If we want transparent, then first render a solid black model to remove the printer size lines.
789                                         if self.viewMode == 'transparent':
790                                                 glColor4f(0, 0, 0, 0)
791                                                 self._renderObject(obj)
792                                                 glEnable(GL_BLEND)
793                                                 glBlendFunc(GL_ONE, GL_ONE)
794                                                 glDisable(GL_DEPTH_TEST)
795                                                 brightness *= 0.5
796                                         if self.viewMode == 'xray':
797                                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
798                                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
799                                         glEnable(GL_STENCIL_TEST)
800
801                                 if not self._scene.checkPlatform(obj):
802                                         glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
803                                         self._renderObject(obj)
804                                 else:
805                                         self._renderObject(obj, brightness)
806                                 glDisable(GL_STENCIL_TEST)
807                                 glDisable(GL_BLEND)
808                                 glEnable(GL_DEPTH_TEST)
809                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
810
811                         if self.viewMode == 'xray':
812                                 glPushMatrix()
813                                 glLoadIdentity()
814                                 glEnable(GL_STENCIL_TEST)
815                                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
816                                 glDisable(GL_DEPTH_TEST)
817                                 for i in xrange(2, 15, 2):
818                                         glStencilFunc(GL_EQUAL, i, 0xFF)
819                                         glColor(float(i)/10, float(i)/10, float(i)/5)
820                                         glBegin(GL_QUADS)
821                                         glVertex3f(-1000,-1000,-10)
822                                         glVertex3f( 1000,-1000,-10)
823                                         glVertex3f( 1000, 1000,-10)
824                                         glVertex3f(-1000, 1000,-10)
825                                         glEnd()
826                                 for i in xrange(1, 15, 2):
827                                         glStencilFunc(GL_EQUAL, i, 0xFF)
828                                         glColor(float(i)/10, 0, 0)
829                                         glBegin(GL_QUADS)
830                                         glVertex3f(-1000,-1000,-10)
831                                         glVertex3f( 1000,-1000,-10)
832                                         glVertex3f( 1000, 1000,-10)
833                                         glVertex3f(-1000, 1000,-10)
834                                         glEnd()
835                                 glPopMatrix()
836                                 glDisable(GL_STENCIL_TEST)
837                                 glEnable(GL_DEPTH_TEST)
838
839                         self._objectShader.unbind()
840
841                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
842                         glEnable(GL_BLEND)
843                         self._objectLoadShader.bind()
844                         glColor4f(0.2, 0.6, 1.0, 1.0)
845                         for obj in self._scene.objects():
846                                 if obj._loadAnim is None:
847                                         continue
848                                 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
849                                 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
850                                 self._renderObject(obj)
851                         self._objectLoadShader.unbind()
852                         glDisable(GL_BLEND)
853
854                 self._drawMachine()
855
856                 if self.viewMode == 'gcode':
857                         pass
858                 else:
859                         #Draw the object box-shadow, so you can see where it will collide with other objects.
860                         if self._selectedObj is not None and len(self._scene.objects()) > 1:
861                                 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
862                                 glPushMatrix()
863                                 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
864                                 glEnable(GL_BLEND)
865                                 glEnable(GL_CULL_FACE)
866                                 glColor4f(0,0,0,0.12)
867                                 glBegin(GL_QUADS)
868                                 glVertex3f(-size[0],  size[1], 0.1)
869                                 glVertex3f(-size[0], -size[1], 0.1)
870                                 glVertex3f( size[0], -size[1], 0.1)
871                                 glVertex3f( size[0],  size[1], 0.1)
872                                 glEnd()
873                                 glDisable(GL_CULL_FACE)
874                                 glPopMatrix()
875
876                         #Draw the outline of the selected object, on top of everything else except the GUI.
877                         if self._selectedObj is not None and self._selectedObj._loadAnim is None:
878                                 glDisable(GL_DEPTH_TEST)
879                                 glEnable(GL_CULL_FACE)
880                                 glEnable(GL_STENCIL_TEST)
881                                 glDisable(GL_BLEND)
882                                 glStencilFunc(GL_EQUAL, 0, 255)
883
884                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
885                                 glLineWidth(2)
886                                 glColor4f(1,1,1,0.5)
887                                 self._renderObject(self._selectedObj)
888                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
889
890                                 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
891                                 glDisable(GL_STENCIL_TEST)
892                                 glDisable(GL_CULL_FACE)
893                                 glEnable(GL_DEPTH_TEST)
894
895                         if self._selectedObj is not None:
896                                 glPushMatrix()
897                                 pos = self.getObjectCenterPos()
898                                 glTranslate(pos[0], pos[1], pos[2])
899                                 self.tool.OnDraw()
900                                 glPopMatrix()
901
902         def _renderObject(self, obj, brightness = False):
903                 glPushMatrix()
904                 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
905
906                 if self.tempMatrix is not None and obj == self._selectedObj:
907                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
908                         glMultMatrixf(tempMatrix)
909
910                 offset = obj.getDrawOffset()
911                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
912
913                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
914                 glMultMatrixf(tempMatrix)
915
916                 n = 0
917                 for m in obj._meshList:
918                         if m.vbo is None:
919                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
920                         if brightness:
921                                 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
922                                 n += 1
923                         m.vbo.render()
924                 glPopMatrix()
925
926         def _drawMachine(self):
927                 glEnable(GL_CULL_FACE)
928                 glEnable(GL_BLEND)
929
930                 if profile.getPreference('machine_type') == 'ultimaker':
931                         glColor4f(1,1,1,0.5)
932                         self._objectShader.bind()
933                         self._renderObject(self._platformMesh)
934                         self._objectShader.unbind()
935
936                 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
937                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
938                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
939                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
940                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
941                 v4 = [ size[0] / 2, size[1] / 2, 0]
942                 v5 = [ size[0] / 2,-size[1] / 2, 0]
943                 v6 = [-size[0] / 2, size[1] / 2, 0]
944                 v7 = [-size[0] / 2,-size[1] / 2, 0]
945
946                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
947                 glEnableClientState(GL_VERTEX_ARRAY)
948                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
949
950                 glColor4ub(5, 171, 231, 64)
951                 glDrawArrays(GL_QUADS, 0, 4)
952                 glColor4ub(5, 171, 231, 96)
953                 glDrawArrays(GL_QUADS, 4, 8)
954                 glColor4ub(5, 171, 231, 128)
955                 glDrawArrays(GL_QUADS, 12, 8)
956
957                 sx = self._machineSize[0]
958                 sy = self._machineSize[1]
959                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
960                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
961                                 x1 = x * 10
962                                 x2 = x1 + 10
963                                 y1 = y * 10
964                                 y2 = y1 + 10
965                                 x1 = max(min(x1, sx/2), -sx/2)
966                                 y1 = max(min(y1, sy/2), -sy/2)
967                                 x2 = max(min(x2, sx/2), -sx/2)
968                                 y2 = max(min(y2, sy/2), -sy/2)
969                                 if (x & 1) == (y & 1):
970                                         glColor4ub(5, 171, 231, 127)
971                                 else:
972                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
973                                 glBegin(GL_QUADS)
974                                 glVertex3f(x1, y1, -0.02)
975                                 glVertex3f(x2, y1, -0.02)
976                                 glVertex3f(x2, y2, -0.02)
977                                 glVertex3f(x1, y2, -0.02)
978                                 glEnd()
979
980                 glDisableClientState(GL_VERTEX_ARRAY)
981                 glDisable(GL_BLEND)
982                 glDisable(GL_CULL_FACE)
983
984         def _generateGCodeVBOs(self, layer):
985                 ret = []
986                 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
987                         pointList = numpy.zeros((0,3), numpy.float32)
988                         for path in layer:
989                                 if path.type == 'extrude' and path.pathType == extrudeType:
990                                         a = numpy.array(path.points, numpy.float32)
991                                         a = numpy.concatenate((a[:-1], a[1:]), 1)
992                                         a = a.reshape((len(a) * 2, 3))
993                                         pointList = numpy.concatenate((pointList, a))
994                         ret.append(opengl.GLVBO(pointList))
995                 return ret
996
997         def _generateGCodeVBOs2(self, layer):
998                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
999                 filamentArea = math.pi * filamentRadius * filamentRadius
1000
1001                 ret = []
1002                 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1003                         pointList = numpy.zeros((0,3), numpy.float32)
1004                         for path in layer:
1005                                 if path.type == 'extrude' and path.pathType == extrudeType:
1006                                         a = numpy.array(path.points, numpy.float32)
1007                                         if extrudeType == 'FILL':
1008                                                 a[:,2] += 0.01
1009
1010                                         normal = a[1:] - a[:-1]
1011                                         lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1012                                         normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1013                                         normal[:,2] /= lens
1014
1015                                         ePerDist = path.extrusion[1:] / lens
1016                                         lineWidth = ePerDist * (filamentArea / path.layerThickness / 2)
1017
1018                                         normal[:,0] *= lineWidth
1019                                         normal[:,1] *= lineWidth
1020
1021                                         b = numpy.zeros((len(a)-1, 0), numpy.float32)
1022                                         b = numpy.concatenate((b, a[:-1] + normal), 1)
1023                                         b = numpy.concatenate((b, a[1:] + normal), 1)
1024                                         b = numpy.concatenate((b, a[1:] - normal), 1)
1025                                         b = numpy.concatenate((b, a[:-1] - normal), 1)
1026                                         b = b.reshape((len(b) * 4, 3))
1027
1028                                         pointList = numpy.concatenate((pointList, b))
1029                         ret.append(opengl.GLVBO(pointList))
1030                 return ret
1031
1032         def getObjectCenterPos(self):
1033                 if self._selectedObj is None:
1034                         return [0.0, 0.0, 0.0]
1035                 pos = self._selectedObj.getPosition()
1036                 size = self._selectedObj.getSize()
1037                 return [pos[0], pos[1], size[2]/2]
1038
1039         def getObjectBoundaryCircle(self):
1040                 if self._selectedObj is None:
1041                         return 0.0
1042                 return self._selectedObj.getBoundaryCircle()
1043
1044         def getObjectSize(self):
1045                 if self._selectedObj is None:
1046                         return [0.0, 0.0, 0.0]
1047                 return self._selectedObj.getSize()
1048
1049         def getObjectMatrix(self):
1050                 if self._selectedObj is None:
1051                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1052                 return self._selectedObj.getMatrix()
1053
1054 class shaderEditor(wx.Dialog):
1055         def __init__(self, parent, callback, v, f):
1056                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1057                 self._callback = callback
1058                 s = wx.BoxSizer(wx.VERTICAL)
1059                 self.SetSizer(s)
1060                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1061                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1062                 s.Add(self._vertex, 1, flag=wx.EXPAND)
1063                 s.Add(self._fragment, 1, flag=wx.EXPAND)
1064
1065                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1066                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1067
1068                 self.SetPosition(self.GetParent().GetPosition())
1069                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1070                 self.Show()
1071
1072         def OnText(self, e):
1073                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())