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._gcodeLoadThread = None
42 self._objectShader = None
44 self._selectedObj = None
45 self._objColors = [None,None,None,None]
48 self._mouseState = None
49 self._viewTarget = numpy.array([0,0,0], numpy.float32)
52 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
53 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
54 self._isSimpleMode = True
57 self._modelMatrix = None
58 self._projMatrix = None
59 self.tempMatrix = None
61 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
62 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.showPrintWindow)
63 self.printButton.setDisabled(True)
66 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
67 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
68 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
70 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
71 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
73 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
74 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
76 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
77 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
78 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
80 self.rotateToolButton.setExpandArrow(True)
81 self.scaleToolButton.setExpandArrow(True)
82 self.mirrorToolButton.setExpandArrow(True)
84 self.scaleForm = openglGui.glFrame(self, (2, -2))
85 openglGui.glGuiLayoutGrid(self.scaleForm)
86 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
87 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
88 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
89 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
90 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
91 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
92 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
93 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
94 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
95 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
96 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
97 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
98 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
99 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
101 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
102 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
104 self.notification = openglGui.glNotification(self, (0, 0))
106 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
107 self._sceneUpdateTimer = wx.Timer(self)
108 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
109 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
113 self.updateToolButtons()
114 self.updateProfileToControls()
116 def showLoadModel(self, button = 1):
118 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)
119 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
120 if dlg.ShowModal() != wx.ID_OK:
123 filenames = dlg.GetPaths()
125 if len(filenames) < 1:
127 profile.putPreference('lastFile', filenames[0])
129 for filename in filenames:
130 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
131 ext = filename[filename.rfind('.')+1:].upper()
132 if ext == 'G' or ext == 'GCODE':
133 gcodeFilename = filename
134 if gcodeFilename is not None:
135 if self._gcode is not None:
137 for layerVBOlist in self._gcodeVBOs:
138 for vbo in layerVBOlist:
139 self.glReleaseList.append(vbo)
141 self._gcode = gcodeInterpreter.gcode()
142 self._gcodeFilename = gcodeFilename
143 self.printButton.setBottomText('')
144 self.viewSelection.setValue(4)
145 self.printButton.setDisabled(False)
148 if self.viewSelection.getValue() == 4:
149 self.viewSelection.setValue(0)
151 self.loadScene(filenames)
153 def showSaveModel(self):
154 if len(self._scene.objects()) < 1:
156 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
157 dlg.SetWildcard(meshLoader.saveWildcardFilter())
158 if dlg.ShowModal() != wx.ID_OK:
161 filename = dlg.GetPath()
163 meshLoader.saveMeshes(filename, self._scene.objects())
165 def showPrintWindow(self, button):
167 if machineCom.machineIsConnected():
168 printWindow.printFile(self._gcode.filename)
169 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
170 drives = removableStorage.getPossibleSDcardDrives()
172 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))
173 if dlg.ShowModal() != wx.ID_OK:
176 drive = drives[dlg.GetSelection()]
180 filename = os.path.basename(profile.getPreference('lastFile'))
181 filename = filename[0:filename.rfind('.')] + '.gcode'
182 threading.Thread(target=self._copyFile,args=(self._gcode.filename, drive[1] + filename, drive[1])).start()
187 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._gcode.filename), menu.Append(-1, 'Print with USB'))
188 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
189 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
193 def showSaveGCode(self):
194 defPath = profile.getPreference('lastFile')
195 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
196 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
197 dlg.SetFilename(os.path.basename(defPath))
198 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
199 if dlg.ShowModal() != wx.ID_OK:
202 filename = dlg.GetPath()
205 threading.Thread(target=self._copyFile,args=(self._gcode.filename, filename)).start()
207 def _copyFile(self, fileA, fileB, allowEject = False):
209 size = float(os.stat(fileA).st_size)
210 with open(fileA, 'rb') as fsrc:
211 with open(fileB, 'wb') as fdst:
213 buf = fsrc.read(16*1024)
217 self.printButton.setProgressBar(float(fsrc.tell()) / size)
220 self.notification.message("Failed to save")
223 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...'))
225 self.notification.message("Saved as %s" % (fileB))
226 self.printButton.setProgressBar(None)
229 def _showSliceLog(self):
230 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
234 def OnToolSelect(self, button):
235 if self.rotateToolButton.getSelected():
236 self.tool = previewTools.toolRotate(self)
237 elif self.scaleToolButton.getSelected():
238 self.tool = previewTools.toolScale(self)
239 elif self.mirrorToolButton.getSelected():
240 self.tool = previewTools.toolNone(self)
242 self.tool = previewTools.toolNone(self)
243 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
244 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
245 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
246 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
247 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
248 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
249 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
250 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
252 def updateToolButtons(self):
253 if self._selectedObj is None:
257 self.rotateToolButton.setHidden(hidden)
258 self.scaleToolButton.setHidden(hidden)
259 self.mirrorToolButton.setHidden(hidden)
261 self.rotateToolButton.setSelected(False)
262 self.scaleToolButton.setSelected(False)
263 self.mirrorToolButton.setSelected(False)
266 def OnViewChange(self):
267 if self.viewSelection.getValue() == 4:
268 self.viewMode = 'gcode'
269 if self._gcode is not None and self._gcode.layerList is not None:
270 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
271 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
272 self._selectObject(None)
273 elif self.viewSelection.getValue() == 1:
274 self.viewMode = 'overhang'
275 elif self.viewSelection.getValue() == 2:
276 self.viewMode = 'transparent'
277 elif self.viewSelection.getValue() == 3:
278 self.viewMode = 'xray'
280 self.viewMode = 'normal'
281 self.layerSelect.setHidden(self.viewMode != 'gcode')
284 def OnRotateReset(self, button):
285 if self._selectedObj is None:
287 self._selectedObj.resetRotation()
288 self._scene.pushFree()
289 self._selectObject(self._selectedObj)
292 def OnLayFlat(self, button):
293 if self._selectedObj is None:
295 self._selectedObj.layFlat()
296 self._scene.pushFree()
297 self._selectObject(self._selectedObj)
300 def OnScaleReset(self, button):
301 if self._selectedObj is None:
303 self._selectedObj.resetScale()
304 self._selectObject(self._selectedObj)
305 self.updateProfileToControls()
308 def OnScaleMax(self, button):
309 if self._selectedObj is None:
311 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
312 self._scene.pushFree()
313 self._selectObject(self._selectedObj)
314 self.updateProfileToControls()
317 def OnMirror(self, axis):
318 if self._selectedObj is None:
320 self._selectedObj.mirror(axis)
323 def OnScaleEntry(self, value, axis):
324 if self._selectedObj is None:
330 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
331 self.updateProfileToControls()
332 self._scene.pushFree()
333 self._selectObject(self._selectedObj)
336 def OnScaleEntryMM(self, value, axis):
337 if self._selectedObj is None:
343 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
344 self.updateProfileToControls()
345 self._scene.pushFree()
346 self._selectObject(self._selectedObj)
349 def OnDeleteAll(self, e):
350 while len(self._scene.objects()) > 0:
351 self._deleteObject(self._scene.objects()[0])
352 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
354 def OnMultiply(self, e):
355 if self._focusObj is None:
358 dlg = wx.NumberEntryDialog(self, "How many items do you want?", "Copies", "Multiply", 2, 1, 100)
359 if dlg.ShowModal() != wx.ID_OK:
362 cnt = dlg.GetValue() - 1
368 self._scene.add(newObj)
369 self._scene.centerAll()
370 if not self._scene.checkPlatform(newObj):
375 self.notification.message("Could not create more then %d items" % (n))
376 self._scene.remove(newObj)
377 self._scene.centerAll()
380 def OnSplitObject(self, e):
381 if self._focusObj is None:
383 self._scene.remove(self._focusObj)
384 for obj in self._focusObj.split(self._splitCallback):
386 self._scene.centerAll()
387 self._selectObject(None)
390 def _splitCallback(self, progress):
393 def OnMergeObjects(self, e):
394 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
396 self._scene.merge(self._selectedObj, self._focusObj)
399 def sceneUpdated(self):
400 self._sceneUpdateTimer.Start(500, True)
401 self._slicer.abortSlicer()
402 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
405 def _onRunSlicer(self, e):
406 if self._isSimpleMode:
407 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
408 self._slicer.runSlicer(self._scene)
409 if self._isSimpleMode:
410 profile.resetTempOverride()
412 def _updateSliceProgress(self, progressValue, ready):
413 self.printButton.setDisabled(not ready)
414 if progressValue >= 0.0:
415 self.printButton.setProgressBar(progressValue)
417 self.printButton.setProgressBar(None)
418 if self._gcode is not None:
420 for layerVBOlist in self._gcodeVBOs:
421 for vbo in layerVBOlist:
422 self.glReleaseList.append(vbo)
425 self.printButton.setProgressBar(None)
426 cost = self._slicer.getFilamentCost()
428 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
430 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
431 self._gcode = gcodeInterpreter.gcode()
432 self._gcodeFilename = self._slicer.getGCodeFilename()
434 self.printButton.setBottomText('')
437 def _loadGCode(self):
438 self._gcode.progressCallback = self._gcodeLoadCallback
439 self._gcode.load(self._gcodeFilename)
441 def _gcodeLoadCallback(self, progress):
442 if self._gcode is None:
444 if len(self._gcode.layerList) % 5 == 0:
446 if self._gcode is None:
448 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
449 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
450 self.layerSelect.setValue(self.layerSelect.getMaxValue())
452 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
453 if self.viewMode == 'gcode':
457 def loadScene(self, fileList):
458 for filename in fileList:
460 objList = meshLoader.loadMeshes(filename)
462 traceback.print_exc()
465 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
467 self._scene.centerAll()
468 self._selectObject(obj)
471 def _deleteObject(self, obj):
472 if obj == self._selectedObj:
473 self._selectObject(None)
474 if obj == self._focusObj:
475 self._focusObj = None
476 self._scene.remove(obj)
477 for m in obj._meshList:
478 if m.vbo is not None and m.vbo.decRef():
479 self.glReleaseList.append(m.vbo)
484 def _selectObject(self, obj, zoom = True):
485 if obj != self._selectedObj:
486 self._selectedObj = obj
487 self.updateProfileToControls()
488 self.updateToolButtons()
489 if zoom and obj is not None:
490 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
491 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
492 newZoom = obj.getBoundaryCircle() * 6
493 if newZoom > numpy.max(self._machineSize) * 3:
494 newZoom = numpy.max(self._machineSize) * 3
495 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
497 def updateProfileToControls(self):
498 oldSimpleMode = self._isSimpleMode
499 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
500 if self._isSimpleMode and not oldSimpleMode:
501 self._scene.arrangeAll()
503 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
504 self._objColors[0] = profile.getPreferenceColour('model_colour')
505 self._objColors[1] = profile.getPreferenceColour('model_colour2')
506 self._objColors[2] = profile.getPreferenceColour('model_colour3')
507 self._objColors[3] = profile.getPreferenceColour('model_colour4')
508 self._scene.setMachineSize(self._machineSize)
509 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
510 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'))
512 if self._selectedObj is not None:
513 scale = self._selectedObj.getScale()
514 size = self._selectedObj.getSize()
515 self.scaleXctrl.setValue(round(scale[0], 2))
516 self.scaleYctrl.setValue(round(scale[1], 2))
517 self.scaleZctrl.setValue(round(scale[2], 2))
518 self.scaleXmmctrl.setValue(round(size[0], 2))
519 self.scaleYmmctrl.setValue(round(size[1], 2))
520 self.scaleZmmctrl.setValue(round(size[2], 2))
522 def OnKeyChar(self, keyCode):
523 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
524 if self._selectedObj is not None:
525 self._deleteObject(self._selectedObj)
527 if keyCode == wx.WXK_UP:
528 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
530 elif keyCode == wx.WXK_DOWN:
531 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
533 elif keyCode == wx.WXK_PAGEUP:
534 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
536 elif keyCode == wx.WXK_PAGEDOWN:
537 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
540 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
541 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
542 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
543 from collections import defaultdict
544 from gc import get_objects
545 self._beforeLeakTest = defaultdict(int)
546 for i in get_objects():
547 self._beforeLeakTest[type(i)] += 1
548 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
549 from collections import defaultdict
550 from gc import get_objects
551 self._afterLeakTest = defaultdict(int)
552 for i in get_objects():
553 self._afterLeakTest[type(i)] += 1
554 for k in self._afterLeakTest:
555 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
556 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
558 def ShaderUpdate(self, v, f):
559 s = opengl.GLShader(v, f)
561 self._objectLoadShader.release()
562 self._objectLoadShader = s
563 for obj in self._scene.objects():
564 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
567 def OnMouseDown(self,e):
568 self._mouseX = e.GetX()
569 self._mouseY = e.GetY()
570 self._mouseClick3DPos = self._mouse3Dpos
571 self._mouseClickFocus = self._focusObj
573 self._mouseState = 'doubleClick'
575 self._mouseState = 'dragOrClick'
576 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
577 p0 -= self.getObjectCenterPos() - self._viewTarget
578 p1 -= self.getObjectCenterPos() - self._viewTarget
579 if self.tool.OnDragStart(p0, p1):
580 self._mouseState = 'tool'
581 if self._mouseState == 'dragOrClick':
582 if e.GetButton() == 1:
583 if self._focusObj is not None:
584 self._selectObject(self._focusObj, False)
587 def OnMouseUp(self, e):
588 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
590 if self._mouseState == 'dragOrClick':
591 if e.GetButton() == 1:
592 self._selectObject(self._focusObj)
593 if e.GetButton() == 3:
595 if self._focusObj is not None:
596 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
597 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
598 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
599 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
600 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
601 if len(self._scene.objects()) > 0:
602 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
603 if menu.MenuItemCount > 0:
606 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
607 self._scene.pushFree()
609 elif self._mouseState == 'tool':
610 if self.tempMatrix is not None and self._selectedObj is not None:
611 self._selectedObj.applyMatrix(self.tempMatrix)
612 self._scene.pushFree()
613 self._selectObject(self._selectedObj)
614 self.tempMatrix = None
615 self.tool.OnDragEnd()
617 self._mouseState = None
619 def OnMouseMotion(self,e):
620 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
621 p0 -= self.getObjectCenterPos() - self._viewTarget
622 p1 -= self.getObjectCenterPos() - self._viewTarget
624 if e.Dragging() and self._mouseState is not None:
625 if self._mouseState == 'tool':
626 self.tool.OnDrag(p0, p1)
627 elif not e.LeftIsDown() and e.RightIsDown():
628 self._mouseState = 'drag'
629 if wx.GetKeyState(wx.WXK_SHIFT):
630 a = math.cos(math.radians(self._yaw)) / 3.0
631 b = math.sin(math.radians(self._yaw)) / 3.0
632 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
633 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
634 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
635 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
637 self._yaw += e.GetX() - self._mouseX
638 self._pitch -= e.GetY() - self._mouseY
639 if self._pitch > 170:
643 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
644 self._mouseState = 'drag'
645 self._zoom += e.GetY() - self._mouseY
648 if self._zoom > numpy.max(self._machineSize) * 3:
649 self._zoom = numpy.max(self._machineSize) * 3
650 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
651 self._mouseState = 'dragObject'
652 z = max(0, self._mouseClick3DPos[2])
653 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
654 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
659 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
660 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
661 diff = cursorZ1 - cursorZ0
662 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
663 if not e.Dragging() or self._mouseState != 'tool':
664 self.tool.OnMouseMove(p0, p1)
666 self._mouseX = e.GetX()
667 self._mouseY = e.GetY()
669 def OnMouseWheel(self, e):
670 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
671 delta = max(min(delta,4),-4)
672 self._zoom *= 1.0 - delta / 10.0
675 if self._zoom > numpy.max(self._machineSize) * 3:
676 self._zoom = numpy.max(self._machineSize) * 3
679 def getMouseRay(self, x, y):
680 if self._viewport is None:
681 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
682 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
683 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
684 p0 -= self._viewTarget
685 p1 -= self._viewTarget
688 def _init3DView(self):
689 # set viewing projection
690 size = self.GetSize()
691 glViewport(0, 0, size.GetWidth(), size.GetHeight())
694 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
696 glDisable(GL_RESCALE_NORMAL)
697 glDisable(GL_LIGHTING)
699 glEnable(GL_DEPTH_TEST)
700 glDisable(GL_CULL_FACE)
702 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
704 glClearColor(0.8, 0.8, 0.8, 1.0)
708 glMatrixMode(GL_PROJECTION)
710 aspect = float(size.GetWidth()) / float(size.GetHeight())
711 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
713 glMatrixMode(GL_MODELVIEW)
715 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
718 if machineCom.machineIsConnected():
719 self.printButton._imageID = 6
720 self.printButton._tooltip = 'Print'
721 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
722 self.printButton._imageID = 2
723 self.printButton._tooltip = 'Toolpath to SD'
725 self.printButton._imageID = 3
726 self.printButton._tooltip = 'Save toolpath'
728 if self._animView is not None:
729 self._viewTarget = self._animView.getPosition()
730 if self._animView.isDone():
731 self._animView = None
732 if self._animZoom is not None:
733 self._zoom = self._animZoom.getPosition()
734 if self._animZoom.isDone():
735 self._animZoom = None
736 if self.viewMode == 'gcode' and self._gcode is not None:
738 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
741 if self._objectShader is None:
742 self._objectShader = opengl.GLShader("""
743 varying float light_amount;
747 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
748 gl_FrontColor = gl_Color;
750 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
754 varying float light_amount;
758 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
761 self._objectOverhangShader = opengl.GLShader("""
762 uniform float cosAngle;
763 uniform mat3 rotMatrix;
764 varying float light_amount;
768 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
769 gl_FrontColor = gl_Color;
771 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
773 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
775 light_amount = -10.0;
779 varying float light_amount;
783 if (light_amount == -10.0)
785 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
787 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
791 self._objectLoadShader = opengl.GLShader("""
792 uniform float intensity;
794 varying float light_amount;
798 vec4 tmp = gl_Vertex;
799 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
800 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
801 gl_Position = gl_ModelViewProjectionMatrix * tmp;
802 gl_FrontColor = gl_Color;
804 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
808 uniform float intensity;
809 varying float light_amount;
813 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
817 glTranslate(0,0,-self._zoom)
818 glRotate(-self._pitch, 1,0,0)
819 glRotate(self._yaw, 0,0,1)
820 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
822 self._viewport = glGetIntegerv(GL_VIEWPORT)
823 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
824 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
826 glClearColor(1,1,1,1)
827 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
829 if self.viewMode != 'gcode':
830 for n in xrange(0, len(self._scene.objects())):
831 obj = self._scene.objects()[n]
832 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
833 self._renderObject(obj)
835 if self._mouseX > -1:
837 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
838 if n < len(self._scene.objects()):
839 self._focusObj = self._scene.objects()[n]
841 self._focusObj = None
842 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
843 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
844 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
845 self._mouse3Dpos -= self._viewTarget
848 glTranslate(0,0,-self._zoom)
849 glRotate(-self._pitch, 1,0,0)
850 glRotate(self._yaw, 0,0,1)
851 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
853 if self.viewMode == 'gcode':
854 if self._gcode is not None and self._gcode.layerList is None:
855 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
856 self._gcodeLoadThread.daemon = True
857 self._gcodeLoadThread.start()
858 if self._gcode is not None and self._gcode.layerList is not None:
860 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
862 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
863 for n in xrange(0, drawUpTill):
864 c = 1.0 - float(drawUpTill - n) / 15
866 if len(self._gcodeVBOs) < n + 1:
867 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
868 if time.time() - t > 0.5:
871 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
872 if n == drawUpTill - 1:
873 if len(self._gcodeVBOs[n]) < 6:
874 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
876 self._gcodeVBOs[n][5].render(GL_QUADS)
878 self._gcodeVBOs[n][6].render(GL_QUADS)
879 glColor3f(c/2, c/2, 0.0)
880 self._gcodeVBOs[n][7].render(GL_QUADS)
882 self._gcodeVBOs[n][8].render(GL_QUADS)
883 self._gcodeVBOs[n][9].render(GL_QUADS)
885 self._gcodeVBOs[n][10].render(GL_LINES)
888 self._gcodeVBOs[n][0].render(GL_LINES)
890 self._gcodeVBOs[n][1].render(GL_LINES)
891 glColor3f(c/2, c/2, 0.0)
892 self._gcodeVBOs[n][2].render(GL_LINES)
894 self._gcodeVBOs[n][3].render(GL_LINES)
895 self._gcodeVBOs[n][4].render(GL_LINES)
898 glStencilFunc(GL_ALWAYS, 1, 1)
899 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
901 if self.viewMode == 'overhang':
902 self._objectOverhangShader.bind()
903 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(60)))
905 self._objectShader.bind()
906 for obj in self._scene.objects():
907 if obj._loadAnim is not None:
908 if obj._loadAnim.isDone():
913 if self._focusObj == obj:
915 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
918 if self._selectedObj == obj or self._selectedObj is None:
919 #If we want transparent, then first render a solid black model to remove the printer size lines.
920 if self.viewMode == 'transparent':
921 glColor4f(0, 0, 0, 0)
922 self._renderObject(obj)
924 glBlendFunc(GL_ONE, GL_ONE)
925 glDisable(GL_DEPTH_TEST)
927 if self.viewMode == 'xray':
928 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
929 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
930 glEnable(GL_STENCIL_TEST)
932 if self.viewMode == 'overhang':
933 if self._selectedObj == obj and self.tempMatrix is not None:
934 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
936 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
938 if not self._scene.checkPlatform(obj):
939 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
940 self._renderObject(obj)
942 self._renderObject(obj, brightness)
943 glDisable(GL_STENCIL_TEST)
945 glEnable(GL_DEPTH_TEST)
946 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
948 if self.viewMode == 'xray':
951 glEnable(GL_STENCIL_TEST)
952 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
953 glDisable(GL_DEPTH_TEST)
954 for i in xrange(2, 15, 2):
955 glStencilFunc(GL_EQUAL, i, 0xFF)
956 glColor(float(i)/10, float(i)/10, float(i)/5)
958 glVertex3f(-1000,-1000,-10)
959 glVertex3f( 1000,-1000,-10)
960 glVertex3f( 1000, 1000,-10)
961 glVertex3f(-1000, 1000,-10)
963 for i in xrange(1, 15, 2):
964 glStencilFunc(GL_EQUAL, i, 0xFF)
965 glColor(float(i)/10, 0, 0)
967 glVertex3f(-1000,-1000,-10)
968 glVertex3f( 1000,-1000,-10)
969 glVertex3f( 1000, 1000,-10)
970 glVertex3f(-1000, 1000,-10)
973 glDisable(GL_STENCIL_TEST)
974 glEnable(GL_DEPTH_TEST)
976 self._objectShader.unbind()
978 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
980 self._objectLoadShader.bind()
981 glColor4f(0.2, 0.6, 1.0, 1.0)
982 for obj in self._scene.objects():
983 if obj._loadAnim is None:
985 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
986 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
987 self._renderObject(obj)
988 self._objectLoadShader.unbind()
993 if self.viewMode == 'gcode':
994 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
995 glDisable(GL_DEPTH_TEST)
998 glTranslate(0,-4,-10)
999 glColor4ub(60,60,60,255)
1000 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1003 #Draw the object box-shadow, so you can see where it will collide with other objects.
1004 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1005 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1007 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1009 glEnable(GL_CULL_FACE)
1010 glColor4f(0,0,0,0.12)
1012 glVertex3f(-size[0], size[1], 0.1)
1013 glVertex3f(-size[0], -size[1], 0.1)
1014 glVertex3f( size[0], -size[1], 0.1)
1015 glVertex3f( size[0], size[1], 0.1)
1017 glDisable(GL_CULL_FACE)
1020 #Draw the outline of the selected object, on top of everything else except the GUI.
1021 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1022 glDisable(GL_DEPTH_TEST)
1023 glEnable(GL_CULL_FACE)
1024 glEnable(GL_STENCIL_TEST)
1026 glStencilFunc(GL_EQUAL, 0, 255)
1028 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1030 glColor4f(1,1,1,0.5)
1031 self._renderObject(self._selectedObj)
1032 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1034 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1035 glDisable(GL_STENCIL_TEST)
1036 glDisable(GL_CULL_FACE)
1037 glEnable(GL_DEPTH_TEST)
1039 if self._selectedObj is not None:
1041 pos = self.getObjectCenterPos()
1042 glTranslate(pos[0], pos[1], pos[2])
1046 def _renderObject(self, obj, brightness = False, addSink = True):
1049 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1051 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1053 if self.tempMatrix is not None and obj == self._selectedObj:
1054 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1055 glMultMatrixf(tempMatrix)
1057 offset = obj.getDrawOffset()
1058 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1060 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1061 glMultMatrixf(tempMatrix)
1064 for m in obj._meshList:
1066 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1068 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1073 def _drawMachine(self):
1074 glEnable(GL_CULL_FACE)
1077 if profile.getPreference('machine_type') == 'ultimaker':
1078 glColor4f(1,1,1,0.5)
1079 self._objectShader.bind()
1080 self._renderObject(self._platformMesh, False, False)
1081 self._objectShader.unbind()
1083 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1084 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1085 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1086 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1087 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1088 v4 = [ size[0] / 2, size[1] / 2, 0]
1089 v5 = [ size[0] / 2,-size[1] / 2, 0]
1090 v6 = [-size[0] / 2, size[1] / 2, 0]
1091 v7 = [-size[0] / 2,-size[1] / 2, 0]
1093 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1094 glEnableClientState(GL_VERTEX_ARRAY)
1095 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1097 glColor4ub(5, 171, 231, 64)
1098 glDrawArrays(GL_QUADS, 0, 4)
1099 glColor4ub(5, 171, 231, 96)
1100 glDrawArrays(GL_QUADS, 4, 8)
1101 glColor4ub(5, 171, 231, 128)
1102 glDrawArrays(GL_QUADS, 12, 8)
1104 sx = self._machineSize[0]
1105 sy = self._machineSize[1]
1106 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1107 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1112 x1 = max(min(x1, sx/2), -sx/2)
1113 y1 = max(min(y1, sy/2), -sy/2)
1114 x2 = max(min(x2, sx/2), -sx/2)
1115 y2 = max(min(y2, sy/2), -sy/2)
1116 if (x & 1) == (y & 1):
1117 glColor4ub(5, 171, 231, 127)
1119 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1121 glVertex3f(x1, y1, -0.02)
1122 glVertex3f(x2, y1, -0.02)
1123 glVertex3f(x2, y2, -0.02)
1124 glVertex3f(x1, y2, -0.02)
1127 glDisableClientState(GL_VERTEX_ARRAY)
1129 glDisable(GL_CULL_FACE)
1131 def _generateGCodeVBOs(self, layer):
1133 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1134 pointList = numpy.zeros((0,3), numpy.float32)
1136 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1138 a = numpy.concatenate((a[:-1], a[1:]), 1)
1139 a = a.reshape((len(a) * 2, 3))
1140 pointList = numpy.concatenate((pointList, a))
1141 ret.append(opengl.GLVBO(pointList))
1144 def _generateGCodeVBOs2(self, layer):
1145 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1146 filamentArea = math.pi * filamentRadius * filamentRadius
1149 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1150 pointList = numpy.zeros((0,3), numpy.float32)
1152 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1154 if extrudeType == 'FILL':
1157 normal = a[1:] - a[:-1]
1158 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1159 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1162 ePerDist = path['extrusion'][1:] / lens
1163 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1165 normal[:,0] *= lineWidth
1166 normal[:,1] *= lineWidth
1168 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1169 b = numpy.concatenate((b, a[1:] + normal), 1)
1170 b = numpy.concatenate((b, a[1:] - normal), 1)
1171 b = numpy.concatenate((b, a[:-1] - normal), 1)
1172 b = numpy.concatenate((b, a[:-1] + normal), 1)
1173 #b = numpy.concatenate((b, a[:-1]), 1)
1174 #b = numpy.concatenate((b, a[:-1]), 1)
1175 b = b.reshape((len(b) * 4, 3))
1178 normal2 = normal[:-1] + normal[1:]
1179 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1180 normal2[:,0] /= lens2
1181 normal2[:,1] /= lens2
1182 normal2[:,0] *= lineWidth[:-1]
1183 normal2[:,1] *= lineWidth[:-1]
1185 c = numpy.zeros((len(a)-2, 0), numpy.float32)
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 = numpy.concatenate((c, a[1:-1]), 1)
1192 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1193 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1194 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1196 c = c.reshape((len(c) * 8, 3))
1198 pointList = numpy.concatenate((pointList, b, c))
1200 pointList = numpy.concatenate((pointList, b))
1201 ret.append(opengl.GLVBO(pointList))
1203 pointList = numpy.zeros((0,3), numpy.float32)
1205 if path['type'] == 'move' or path['type'] == 'retract':
1207 a = numpy.concatenate((a[:-1], a[1:]), 1)
1208 a = a.reshape((len(a) * 2, 3))
1209 pointList = numpy.concatenate((pointList, a))
1210 ret.append(opengl.GLVBO(pointList))
1214 def getObjectCenterPos(self):
1215 if self._selectedObj is None:
1216 return [0.0, 0.0, 0.0]
1217 pos = self._selectedObj.getPosition()
1218 size = self._selectedObj.getSize()
1219 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1221 def getObjectBoundaryCircle(self):
1222 if self._selectedObj is None:
1224 return self._selectedObj.getBoundaryCircle()
1226 def getObjectSize(self):
1227 if self._selectedObj is None:
1228 return [0.0, 0.0, 0.0]
1229 return self._selectedObj.getSize()
1231 def getObjectMatrix(self):
1232 if self._selectedObj is None:
1233 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1234 return self._selectedObj.getMatrix()
1236 class shaderEditor(wx.Dialog):
1237 def __init__(self, parent, callback, v, f):
1238 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1239 self._callback = callback
1240 s = wx.BoxSizer(wx.VERTICAL)
1242 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1243 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1244 s.Add(self._vertex, 1, flag=wx.EXPAND)
1245 s.Add(self._fragment, 1, flag=wx.EXPAND)
1247 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1248 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1250 self.SetPosition(self.GetParent().GetPosition())
1251 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1254 def OnText(self, e):
1255 self._callback(self._vertex.GetValue(), self._fragment.GetValue())