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