1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
14 OpenGL.ERROR_CHECKING = False
15 from OpenGL.GLU import *
16 from OpenGL.GL import *
18 from Cura.gui import printWindow
19 from Cura.util import profile
20 from Cura.util import meshLoader
21 from Cura.util import objectScene
22 from Cura.util import resources
23 from Cura.util import sliceEngine
24 from Cura.util import machineCom
25 from Cura.util import removableStorage
26 from Cura.util import gcodeInterpreter
27 from Cura.gui.util import previewTools
28 from Cura.gui.util import opengl
29 from Cura.gui.util import openglGui
31 class SceneView(openglGui.glGuiPanel):
32 def __init__(self, parent):
33 super(SceneView, self).__init__(parent)
38 self._scene = objectScene.Scene()
41 self._objectShader = None
43 self._selectedObj = None
44 self._objColors = [None,None,None,None]
47 self._mouseState = None
48 self._viewTarget = numpy.array([0,0,0], numpy.float32)
51 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
52 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
53 self._isSimpleMode = True
56 self._modelMatrix = None
57 self._projMatrix = None
58 self.tempMatrix = None
60 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
61 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.showPrintWindow)
62 self.printButton.setDisabled(True)
65 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
66 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
67 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
69 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
70 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
72 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
73 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
75 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
76 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
77 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
79 self.rotateToolButton.setExpandArrow(True)
80 self.scaleToolButton.setExpandArrow(True)
81 self.mirrorToolButton.setExpandArrow(True)
83 self.scaleForm = openglGui.glFrame(self, (2, -2))
84 openglGui.glGuiLayoutGrid(self.scaleForm)
85 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
86 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
87 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
88 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
89 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
90 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
91 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
92 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
93 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
94 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
95 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
96 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
97 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
98 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
100 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
101 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
103 self.notification = openglGui.glNotification(self, (0, 0))
105 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
106 self._sceneUpdateTimer = wx.Timer(self)
107 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
108 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
112 self.updateToolButtons()
113 self.updateProfileToControls()
115 def showLoadModel(self, button = 1):
117 dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
118 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
119 if dlg.ShowModal() != wx.ID_OK:
122 filenames = dlg.GetPaths()
124 if len(filenames) < 1:
126 profile.putPreference('lastFile', filenames[0])
128 for filename in filenames:
129 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
130 ext = filename[filename.rfind('.')+1:].upper()
131 if ext == 'G' or ext == 'GCODE':
132 gcodeFilename = filename
133 if gcodeFilename is not None:
134 if self._gcode is not None:
136 for layerVBOlist in self._gcodeVBOs:
137 for vbo in layerVBOlist:
138 self.glReleaseList.append(vbo)
140 self._gcode = gcodeInterpreter.gcode()
141 self._gcode.progressCallback = self._gcodeLoadCallback
142 self._thread = threading.Thread(target=self._loadGCode, args=(gcodeFilename,))
143 self._thread.daemon = True
145 self.printButton.setBottomText('')
146 self.viewSelection.setValue(4)
147 self.printButton.setDisabled(False)
150 if self.viewSelection.getValue() == 4:
151 self.viewSelection.setValue(0)
153 self.loadScene(filenames)
155 def showSaveModel(self):
156 if len(self._scene.objects()) < 1:
158 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
159 dlg.SetWildcard(meshLoader.saveWildcardFilter())
160 if dlg.ShowModal() != wx.ID_OK:
163 filename = dlg.GetPath()
165 meshLoader.saveMeshes(filename, self._scene.objects())
167 def showPrintWindow(self, button):
169 if machineCom.machineIsConnected():
170 printWindow.printFile(self._gcode.filename)
171 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
172 drives = removableStorage.getPossibleSDcardDrives()
174 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))
175 if dlg.ShowModal() != wx.ID_OK:
178 drive = drives[dlg.GetSelection()]
182 filename = os.path.basename(profile.getPreference('lastFile'))
183 filename = filename[0:filename.rfind('.')] + '.gcode'
184 threading.Thread(target=self._copyFile,args=(self._gcode.filename, drive[1] + filename, drive[1])).start()
189 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._gcode.filename), menu.Append(-1, 'Print with USB'))
190 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
191 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
195 def showSaveGCode(self):
196 defPath = profile.getPreference('lastFile')
197 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
198 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
199 dlg.SetFilename(os.path.basename(defPath))
200 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
201 if dlg.ShowModal() != wx.ID_OK:
204 filename = dlg.GetPath()
207 threading.Thread(target=self._copyFile,args=(self._gcode.filename, filename)).start()
209 def _copyFile(self, fileA, fileB, allowEject = False):
211 size = float(os.stat(fileA).st_size)
212 with open(fileA, 'rb') as fsrc:
213 with open(fileB, 'wb') as fdst:
215 buf = fsrc.read(16*1024)
219 self.printButton.setProgressBar(float(fsrc.tell()) / size)
222 self.notification.message("Failed to save")
225 self.notification.message("Saved as %s" % (fileB), lambda : self.notification.message('You can now eject the card.') if removableStorage.ejectDrive(allowEject) else self.notification.message('Safe remove failed...'))
227 self.notification.message("Saved as %s" % (fileB))
228 self.printButton.setProgressBar(None)
231 def _showSliceLog(self):
232 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
236 def OnToolSelect(self, button):
237 if self.rotateToolButton.getSelected():
238 self.tool = previewTools.toolRotate(self)
239 elif self.scaleToolButton.getSelected():
240 self.tool = previewTools.toolScale(self)
241 elif self.mirrorToolButton.getSelected():
242 self.tool = previewTools.toolNone(self)
244 self.tool = previewTools.toolNone(self)
245 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
246 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
247 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
248 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
249 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
250 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
251 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
252 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
254 def updateToolButtons(self):
255 if self._selectedObj is None:
259 self.rotateToolButton.setHidden(hidden)
260 self.scaleToolButton.setHidden(hidden)
261 self.mirrorToolButton.setHidden(hidden)
263 self.rotateToolButton.setSelected(False)
264 self.scaleToolButton.setSelected(False)
265 self.mirrorToolButton.setSelected(False)
268 def OnViewChange(self):
269 if self.viewSelection.getValue() == 4:
270 self.viewMode = 'gcode'
271 if self._gcode is not None:
272 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
273 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
274 self._selectObject(None)
275 elif self.viewSelection.getValue() == 1:
276 self.viewMode = 'overhang'
277 elif self.viewSelection.getValue() == 2:
278 self.viewMode = 'transparent'
279 elif self.viewSelection.getValue() == 3:
280 self.viewMode = 'xray'
282 self.viewMode = 'normal'
283 self.layerSelect.setHidden(self.viewMode != 'gcode')
286 def OnRotateReset(self, button):
287 if self._selectedObj is None:
289 self._selectedObj.resetRotation()
290 self._scene.pushFree()
291 self._selectObject(self._selectedObj)
294 def OnLayFlat(self, button):
295 if self._selectedObj is None:
297 self._selectedObj.layFlat()
298 self._scene.pushFree()
299 self._selectObject(self._selectedObj)
302 def OnScaleReset(self, button):
303 if self._selectedObj is None:
305 self._selectedObj.resetScale()
306 self._selectObject(self._selectedObj)
307 self.updateProfileToControls()
310 def OnScaleMax(self, button):
311 if self._selectedObj is None:
313 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
314 self._scene.pushFree()
315 self._selectObject(self._selectedObj)
316 self.updateProfileToControls()
319 def OnMirror(self, axis):
320 if self._selectedObj is None:
322 self._selectedObj.mirror(axis)
325 def OnScaleEntry(self, value, axis):
326 if self._selectedObj is None:
332 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
333 self.updateProfileToControls()
334 self._scene.pushFree()
335 self._selectObject(self._selectedObj)
338 def OnScaleEntryMM(self, value, axis):
339 if self._selectedObj is None:
345 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
346 self.updateProfileToControls()
347 self._scene.pushFree()
348 self._selectObject(self._selectedObj)
351 def OnDeleteAll(self, e):
352 while len(self._scene.objects()) > 0:
353 self._deleteObject(self._scene.objects()[0])
354 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
356 def OnMultiply(self, e):
357 if self._focusObj is None:
360 dlg = wx.NumberEntryDialog(self, "How many items do you want?", "Copies", "Multiply", 2, 1, 100)
361 if dlg.ShowModal() != wx.ID_OK:
364 cnt = dlg.GetValue() - 1
370 self._scene.add(newObj)
371 self._scene.centerAll()
372 if not self._scene.checkPlatform(newObj):
377 self.notification.message("Could not create more then %d items" % (n))
378 self._scene.remove(newObj)
379 self._scene.centerAll()
382 def OnSplitObject(self, e):
383 if self._focusObj is None:
385 self._scene.remove(self._focusObj)
386 for obj in self._focusObj.split(self._splitCallback):
388 self._scene.centerAll()
389 self._selectObject(None)
392 def _splitCallback(self, progress):
395 def OnMergeObjects(self, e):
396 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
398 self._scene.merge(self._selectedObj, self._focusObj)
401 def sceneUpdated(self):
402 self._sceneUpdateTimer.Start(500, True)
403 self._slicer.abortSlicer()
404 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
407 def _onRunSlicer(self, e):
408 if self._isSimpleMode:
409 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
410 self._slicer.runSlicer(self._scene)
411 if self._isSimpleMode:
412 profile.resetTempOverride()
414 def _updateSliceProgress(self, progressValue, ready):
415 self.printButton.setDisabled(not ready)
416 if progressValue >= 0.0:
417 self.printButton.setProgressBar(progressValue)
419 self.printButton.setProgressBar(None)
420 if self._gcode is not None:
422 for layerVBOlist in self._gcodeVBOs:
423 for vbo in layerVBOlist:
424 self.glReleaseList.append(vbo)
427 self.printButton.setProgressBar(None)
428 cost = self._slicer.getFilamentCost()
430 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
432 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
433 self._gcode = gcodeInterpreter.gcode()
434 self._gcode.progressCallback = self._gcodeLoadCallback
435 self._thread = threading.Thread(target=self._loadGCode)
436 self._thread.daemon = True
439 self.printButton.setBottomText('')
442 def _loadGCode(self, filename = None):
444 filename = self._slicer.getGCodeFilename()
445 self._gcode.load(filename)
447 def _gcodeLoadCallback(self, progress):
448 if self._gcode is None:
450 if len(self._gcode.layerList) % 5 == 0:
452 if self._gcode is None:
454 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
455 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
456 self.layerSelect.setValue(self.layerSelect.getMaxValue())
458 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
459 if self.viewMode == 'gcode':
463 def loadScene(self, fileList):
464 for filename in fileList:
466 objList = meshLoader.loadMeshes(filename)
468 traceback.print_exc()
471 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
473 self._scene.centerAll()
474 self._selectObject(obj)
477 def _deleteObject(self, obj):
478 if obj == self._selectedObj:
479 self._selectObject(None)
480 if obj == self._focusObj:
481 self._focusObj = None
482 self._scene.remove(obj)
483 for m in obj._meshList:
484 if m.vbo is not None and m.vbo.decRef():
485 self.glReleaseList.append(m.vbo)
490 def _selectObject(self, obj, zoom = True):
491 if obj != self._selectedObj:
492 self._selectedObj = obj
493 self.updateProfileToControls()
494 self.updateToolButtons()
495 if zoom and obj is not None:
496 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
497 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
498 newZoom = obj.getBoundaryCircle() * 6
499 if newZoom > numpy.max(self._machineSize) * 3:
500 newZoom = numpy.max(self._machineSize) * 3
501 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
503 def updateProfileToControls(self):
504 oldSimpleMode = self._isSimpleMode
505 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
506 if self._isSimpleMode and not oldSimpleMode:
507 self._scene.arrangeAll()
509 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
510 self._objColors[0] = profile.getPreferenceColour('model_colour')
511 self._objColors[1] = profile.getPreferenceColour('model_colour2')
512 self._objColors[2] = profile.getPreferenceColour('model_colour3')
513 self._objColors[3] = profile.getPreferenceColour('model_colour4')
514 self._scene.setMachineSize(self._machineSize)
515 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
516 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'))
518 if self._selectedObj is not None:
519 scale = self._selectedObj.getScale()
520 size = self._selectedObj.getSize()
521 self.scaleXctrl.setValue(round(scale[0], 2))
522 self.scaleYctrl.setValue(round(scale[1], 2))
523 self.scaleZctrl.setValue(round(scale[2], 2))
524 self.scaleXmmctrl.setValue(round(size[0], 2))
525 self.scaleYmmctrl.setValue(round(size[1], 2))
526 self.scaleZmmctrl.setValue(round(size[2], 2))
528 def OnKeyChar(self, keyCode):
529 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
530 if self._selectedObj is not None:
531 self._deleteObject(self._selectedObj)
533 if keyCode == wx.WXK_UP:
534 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
536 elif keyCode == wx.WXK_DOWN:
537 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
539 elif keyCode == wx.WXK_PAGEUP:
540 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
542 elif keyCode == wx.WXK_PAGEDOWN:
543 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
546 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
547 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
548 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
549 from collections import defaultdict
550 from gc import get_objects
551 self._beforeLeakTest = defaultdict(int)
552 for i in get_objects():
553 self._beforeLeakTest[type(i)] += 1
554 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
555 from collections import defaultdict
556 from gc import get_objects
557 self._afterLeakTest = defaultdict(int)
558 for i in get_objects():
559 self._afterLeakTest[type(i)] += 1
560 for k in self._afterLeakTest:
561 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
562 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
564 def ShaderUpdate(self, v, f):
565 s = opengl.GLShader(v, f)
567 self._objectLoadShader.release()
568 self._objectLoadShader = s
569 for obj in self._scene.objects():
570 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
573 def OnMouseDown(self,e):
574 self._mouseX = e.GetX()
575 self._mouseY = e.GetY()
576 self._mouseClick3DPos = self._mouse3Dpos
577 self._mouseClickFocus = self._focusObj
579 self._mouseState = 'doubleClick'
581 self._mouseState = 'dragOrClick'
582 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
583 p0 -= self.getObjectCenterPos() - self._viewTarget
584 p1 -= self.getObjectCenterPos() - self._viewTarget
585 if self.tool.OnDragStart(p0, p1):
586 self._mouseState = 'tool'
587 if self._mouseState == 'dragOrClick':
588 if e.GetButton() == 1:
589 if self._focusObj is not None:
590 self._selectObject(self._focusObj, False)
593 def OnMouseUp(self, e):
594 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
596 if self._mouseState == 'dragOrClick':
597 if e.GetButton() == 1:
598 self._selectObject(self._focusObj)
599 if e.GetButton() == 3:
601 if self._focusObj is not None:
602 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
603 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
604 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
605 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
606 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
607 if len(self._scene.objects()) > 0:
608 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
609 if menu.MenuItemCount > 0:
612 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
613 self._scene.pushFree()
615 elif self._mouseState == 'tool':
616 if self.tempMatrix is not None and self._selectedObj is not None:
617 self._selectedObj.applyMatrix(self.tempMatrix)
618 self._scene.pushFree()
619 self._selectObject(self._selectedObj)
620 self.tempMatrix = None
621 self.tool.OnDragEnd()
623 self._mouseState = None
625 def OnMouseMotion(self,e):
626 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
627 p0 -= self.getObjectCenterPos() - self._viewTarget
628 p1 -= self.getObjectCenterPos() - self._viewTarget
630 if e.Dragging() and self._mouseState is not None:
631 if self._mouseState == 'tool':
632 self.tool.OnDrag(p0, p1)
633 elif not e.LeftIsDown() and e.RightIsDown():
634 self._mouseState = 'drag'
635 if wx.GetKeyState(wx.WXK_SHIFT):
636 a = math.cos(math.radians(self._yaw)) / 3.0
637 b = math.sin(math.radians(self._yaw)) / 3.0
638 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
639 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
640 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
641 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
643 self._yaw += e.GetX() - self._mouseX
644 self._pitch -= e.GetY() - self._mouseY
645 if self._pitch > 170:
649 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
650 self._mouseState = 'drag'
651 self._zoom += e.GetY() - self._mouseY
654 if self._zoom > numpy.max(self._machineSize) * 3:
655 self._zoom = numpy.max(self._machineSize) * 3
656 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
657 self._mouseState = 'dragObject'
658 z = max(0, self._mouseClick3DPos[2])
659 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
660 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
665 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
666 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
667 diff = cursorZ1 - cursorZ0
668 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
669 if not e.Dragging() or self._mouseState != 'tool':
670 self.tool.OnMouseMove(p0, p1)
672 self._mouseX = e.GetX()
673 self._mouseY = e.GetY()
675 def OnMouseWheel(self, e):
676 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
677 delta = max(min(delta,4),-4)
678 self._zoom *= 1.0 - delta / 10.0
681 if self._zoom > numpy.max(self._machineSize) * 3:
682 self._zoom = numpy.max(self._machineSize) * 3
685 def getMouseRay(self, x, y):
686 if self._viewport is None:
687 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
688 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
689 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
690 p0 -= self._viewTarget
691 p1 -= self._viewTarget
694 def _init3DView(self):
695 # set viewing projection
696 size = self.GetSize()
697 glViewport(0, 0, size.GetWidth(), size.GetHeight())
700 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
702 glDisable(GL_RESCALE_NORMAL)
703 glDisable(GL_LIGHTING)
705 glEnable(GL_DEPTH_TEST)
706 glDisable(GL_CULL_FACE)
708 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
710 glClearColor(0.8, 0.8, 0.8, 1.0)
714 glMatrixMode(GL_PROJECTION)
716 aspect = float(size.GetWidth()) / float(size.GetHeight())
717 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
719 glMatrixMode(GL_MODELVIEW)
721 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
724 if machineCom.machineIsConnected():
725 self.printButton._imageID = 6
726 self.printButton._tooltip = 'Print'
727 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
728 self.printButton._imageID = 2
729 self.printButton._tooltip = 'Toolpath to SD'
731 self.printButton._imageID = 3
732 self.printButton._tooltip = 'Save toolpath'
734 if self._animView is not None:
735 self._viewTarget = self._animView.getPosition()
736 if self._animView.isDone():
737 self._animView = None
738 if self._animZoom is not None:
739 self._zoom = self._animZoom.getPosition()
740 if self._animZoom.isDone():
741 self._animZoom = None
742 if self.viewMode == 'gcode' and self._gcode is not None:
744 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
747 if self._objectShader is None:
748 self._objectShader = opengl.GLShader("""
749 varying float light_amount;
753 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
754 gl_FrontColor = gl_Color;
756 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
760 varying float light_amount;
764 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
767 self._objectOverhangShader = opengl.GLShader("""
768 uniform float cosAngle;
769 uniform mat3 rotMatrix;
770 varying float light_amount;
774 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
775 gl_FrontColor = gl_Color;
777 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
779 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
781 light_amount = -10.0;
785 varying float light_amount;
789 if (light_amount == -10.0)
791 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
793 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
797 self._objectLoadShader = opengl.GLShader("""
798 uniform float intensity;
800 varying float light_amount;
804 vec4 tmp = gl_Vertex;
805 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
806 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
807 gl_Position = gl_ModelViewProjectionMatrix * tmp;
808 gl_FrontColor = gl_Color;
810 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
814 uniform float intensity;
815 varying float light_amount;
819 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
823 glTranslate(0,0,-self._zoom)
824 glRotate(-self._pitch, 1,0,0)
825 glRotate(self._yaw, 0,0,1)
826 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
828 self._viewport = glGetIntegerv(GL_VIEWPORT)
829 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
830 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
832 glClearColor(1,1,1,1)
833 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
835 if self.viewMode != 'gcode':
836 for n in xrange(0, len(self._scene.objects())):
837 obj = self._scene.objects()[n]
838 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
839 self._renderObject(obj)
841 if self._mouseX > -1:
843 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
844 if n < len(self._scene.objects()):
845 self._focusObj = self._scene.objects()[n]
847 self._focusObj = None
848 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
849 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
850 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
851 self._mouse3Dpos -= self._viewTarget
854 glTranslate(0,0,-self._zoom)
855 glRotate(-self._pitch, 1,0,0)
856 glRotate(self._yaw, 0,0,1)
857 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
859 if self.viewMode == 'gcode':
860 if self._gcode is not None:
862 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
864 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
865 for n in xrange(0, drawUpTill):
866 c = 1.0 - float(drawUpTill - n) / 15
868 if len(self._gcodeVBOs) < n + 1:
869 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
870 if time.time() - t > 0.5:
873 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
874 if n == drawUpTill - 1:
875 if len(self._gcodeVBOs[n]) < 6:
876 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
878 self._gcodeVBOs[n][5].render(GL_QUADS)
880 self._gcodeVBOs[n][6].render(GL_QUADS)
881 glColor3f(c/2, c/2, 0.0)
882 self._gcodeVBOs[n][7].render(GL_QUADS)
884 self._gcodeVBOs[n][8].render(GL_QUADS)
885 self._gcodeVBOs[n][9].render(GL_QUADS)
887 self._gcodeVBOs[n][10].render(GL_LINES)
890 self._gcodeVBOs[n][0].render(GL_LINES)
892 self._gcodeVBOs[n][1].render(GL_LINES)
893 glColor3f(c/2, c/2, 0.0)
894 self._gcodeVBOs[n][2].render(GL_LINES)
896 self._gcodeVBOs[n][3].render(GL_LINES)
897 self._gcodeVBOs[n][4].render(GL_LINES)
900 glStencilFunc(GL_ALWAYS, 1, 1)
901 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
903 if self.viewMode == 'overhang':
904 self._objectOverhangShader.bind()
905 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(60)))
907 self._objectShader.bind()
908 for obj in self._scene.objects():
909 if obj._loadAnim is not None:
910 if obj._loadAnim.isDone():
915 if self._focusObj == obj:
917 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
920 if self._selectedObj == obj or self._selectedObj is None:
921 #If we want transparent, then first render a solid black model to remove the printer size lines.
922 if self.viewMode == 'transparent':
923 glColor4f(0, 0, 0, 0)
924 self._renderObject(obj)
926 glBlendFunc(GL_ONE, GL_ONE)
927 glDisable(GL_DEPTH_TEST)
929 if self.viewMode == 'xray':
930 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
931 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
932 glEnable(GL_STENCIL_TEST)
934 if self.viewMode == 'overhang':
935 if self._selectedObj == obj and self.tempMatrix is not None:
936 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
938 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
940 if not self._scene.checkPlatform(obj):
941 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
942 self._renderObject(obj)
944 self._renderObject(obj, brightness)
945 glDisable(GL_STENCIL_TEST)
947 glEnable(GL_DEPTH_TEST)
948 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
950 if self.viewMode == 'xray':
953 glEnable(GL_STENCIL_TEST)
954 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
955 glDisable(GL_DEPTH_TEST)
956 for i in xrange(2, 15, 2):
957 glStencilFunc(GL_EQUAL, i, 0xFF)
958 glColor(float(i)/10, float(i)/10, float(i)/5)
960 glVertex3f(-1000,-1000,-10)
961 glVertex3f( 1000,-1000,-10)
962 glVertex3f( 1000, 1000,-10)
963 glVertex3f(-1000, 1000,-10)
965 for i in xrange(1, 15, 2):
966 glStencilFunc(GL_EQUAL, i, 0xFF)
967 glColor(float(i)/10, 0, 0)
969 glVertex3f(-1000,-1000,-10)
970 glVertex3f( 1000,-1000,-10)
971 glVertex3f( 1000, 1000,-10)
972 glVertex3f(-1000, 1000,-10)
975 glDisable(GL_STENCIL_TEST)
976 glEnable(GL_DEPTH_TEST)
978 self._objectShader.unbind()
980 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
982 self._objectLoadShader.bind()
983 glColor4f(0.2, 0.6, 1.0, 1.0)
984 for obj in self._scene.objects():
985 if obj._loadAnim is None:
987 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
988 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
989 self._renderObject(obj)
990 self._objectLoadShader.unbind()
995 if self.viewMode == 'gcode':
998 #Draw the object box-shadow, so you can see where it will collide with other objects.
999 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1000 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1002 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1004 glEnable(GL_CULL_FACE)
1005 glColor4f(0,0,0,0.12)
1007 glVertex3f(-size[0], size[1], 0.1)
1008 glVertex3f(-size[0], -size[1], 0.1)
1009 glVertex3f( size[0], -size[1], 0.1)
1010 glVertex3f( size[0], size[1], 0.1)
1012 glDisable(GL_CULL_FACE)
1015 #Draw the outline of the selected object, on top of everything else except the GUI.
1016 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1017 glDisable(GL_DEPTH_TEST)
1018 glEnable(GL_CULL_FACE)
1019 glEnable(GL_STENCIL_TEST)
1021 glStencilFunc(GL_EQUAL, 0, 255)
1023 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1025 glColor4f(1,1,1,0.5)
1026 self._renderObject(self._selectedObj)
1027 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1029 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1030 glDisable(GL_STENCIL_TEST)
1031 glDisable(GL_CULL_FACE)
1032 glEnable(GL_DEPTH_TEST)
1034 if self._selectedObj is not None:
1036 pos = self.getObjectCenterPos()
1037 glTranslate(pos[0], pos[1], pos[2])
1041 def _renderObject(self, obj, brightness = False, addSink = True):
1044 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1046 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1048 if self.tempMatrix is not None and obj == self._selectedObj:
1049 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1050 glMultMatrixf(tempMatrix)
1052 offset = obj.getDrawOffset()
1053 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1055 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1056 glMultMatrixf(tempMatrix)
1059 for m in obj._meshList:
1061 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1063 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1068 def _drawMachine(self):
1069 glEnable(GL_CULL_FACE)
1072 if profile.getPreference('machine_type') == 'ultimaker':
1073 glColor4f(1,1,1,0.5)
1074 self._objectShader.bind()
1075 self._renderObject(self._platformMesh, False, False)
1076 self._objectShader.unbind()
1078 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1079 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1080 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1081 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1082 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1083 v4 = [ size[0] / 2, size[1] / 2, 0]
1084 v5 = [ size[0] / 2,-size[1] / 2, 0]
1085 v6 = [-size[0] / 2, size[1] / 2, 0]
1086 v7 = [-size[0] / 2,-size[1] / 2, 0]
1088 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1089 glEnableClientState(GL_VERTEX_ARRAY)
1090 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1092 glColor4ub(5, 171, 231, 64)
1093 glDrawArrays(GL_QUADS, 0, 4)
1094 glColor4ub(5, 171, 231, 96)
1095 glDrawArrays(GL_QUADS, 4, 8)
1096 glColor4ub(5, 171, 231, 128)
1097 glDrawArrays(GL_QUADS, 12, 8)
1099 sx = self._machineSize[0]
1100 sy = self._machineSize[1]
1101 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1102 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1107 x1 = max(min(x1, sx/2), -sx/2)
1108 y1 = max(min(y1, sy/2), -sy/2)
1109 x2 = max(min(x2, sx/2), -sx/2)
1110 y2 = max(min(y2, sy/2), -sy/2)
1111 if (x & 1) == (y & 1):
1112 glColor4ub(5, 171, 231, 127)
1114 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1116 glVertex3f(x1, y1, -0.02)
1117 glVertex3f(x2, y1, -0.02)
1118 glVertex3f(x2, y2, -0.02)
1119 glVertex3f(x1, y2, -0.02)
1122 glDisableClientState(GL_VERTEX_ARRAY)
1124 glDisable(GL_CULL_FACE)
1126 def _generateGCodeVBOs(self, layer):
1128 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1129 pointList = numpy.zeros((0,3), numpy.float32)
1131 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1133 a = numpy.concatenate((a[:-1], a[1:]), 1)
1134 a = a.reshape((len(a) * 2, 3))
1135 pointList = numpy.concatenate((pointList, a))
1136 ret.append(opengl.GLVBO(pointList))
1139 def _generateGCodeVBOs2(self, layer):
1140 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1141 filamentArea = math.pi * filamentRadius * filamentRadius
1144 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1145 pointList = numpy.zeros((0,3), numpy.float32)
1147 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1149 if extrudeType == 'FILL':
1152 normal = a[1:] - a[:-1]
1153 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1154 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1157 ePerDist = path['extrusion'][1:] / lens
1158 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1160 normal[:,0] *= lineWidth
1161 normal[:,1] *= lineWidth
1163 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1164 b = numpy.concatenate((b, a[1:] + normal), 1)
1165 b = numpy.concatenate((b, a[1:] - normal), 1)
1166 b = numpy.concatenate((b, a[:-1] - normal), 1)
1167 b = numpy.concatenate((b, a[:-1] + normal), 1)
1168 #b = numpy.concatenate((b, a[:-1]), 1)
1169 #b = numpy.concatenate((b, a[:-1]), 1)
1170 b = b.reshape((len(b) * 4, 3))
1173 normal2 = normal[:-1] + normal[1:]
1174 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1175 normal2[:,0] /= lens2
1176 normal2[:,1] /= lens2
1177 normal2[:,0] *= lineWidth[:-1]
1178 normal2[:,1] *= lineWidth[:-1]
1180 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1181 c = numpy.concatenate((c, a[1:-1]), 1)
1182 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1183 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1184 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1186 c = numpy.concatenate((c, a[1:-1]), 1)
1187 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1188 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1189 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1191 c = c.reshape((len(c) * 8, 3))
1193 pointList = numpy.concatenate((pointList, b, c))
1195 pointList = numpy.concatenate((pointList, b))
1196 ret.append(opengl.GLVBO(pointList))
1198 pointList = numpy.zeros((0,3), numpy.float32)
1200 if path['type'] == 'move' or path['type'] == 'retract':
1202 a = numpy.concatenate((a[:-1], a[1:]), 1)
1203 a = a.reshape((len(a) * 2, 3))
1204 pointList = numpy.concatenate((pointList, a))
1205 ret.append(opengl.GLVBO(pointList))
1209 def getObjectCenterPos(self):
1210 if self._selectedObj is None:
1211 return [0.0, 0.0, 0.0]
1212 pos = self._selectedObj.getPosition()
1213 size = self._selectedObj.getSize()
1214 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1216 def getObjectBoundaryCircle(self):
1217 if self._selectedObj is None:
1219 return self._selectedObj.getBoundaryCircle()
1221 def getObjectSize(self):
1222 if self._selectedObj is None:
1223 return [0.0, 0.0, 0.0]
1224 return self._selectedObj.getSize()
1226 def getObjectMatrix(self):
1227 if self._selectedObj is None:
1228 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1229 return self._selectedObj.getMatrix()
1231 class shaderEditor(wx.Dialog):
1232 def __init__(self, parent, callback, v, f):
1233 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1234 self._callback = callback
1235 s = wx.BoxSizer(wx.VERTICAL)
1237 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1238 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1239 s.Add(self._vertex, 1, flag=wx.EXPAND)
1240 s.Add(self._fragment, 1, flag=wx.EXPAND)
1242 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1243 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1245 self.SetPosition(self.GetParent().GetPosition())
1246 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1249 def OnText(self, e):
1250 self._callback(self._vertex.GetValue(), self._fragment.GetValue())