1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
13 OpenGL.ERROR_CHECKING = False
14 from OpenGL.GLU import *
15 from OpenGL.GL import *
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
30 class SceneView(openglGui.glGuiPanel):
31 def __init__(self, parent):
32 super(SceneView, self).__init__(parent)
37 self._scene = objectScene.Scene()
40 self._gcodeLoadThread = None
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._gcodeFilename = gcodeFilename
142 self.printButton.setBottomText('')
143 self.viewSelection.setValue(4)
144 self.printButton.setDisabled(False)
147 if self.viewSelection.getValue() == 4:
148 self.viewSelection.setValue(0)
150 self.loadScene(filenames)
152 def showSaveModel(self):
153 if len(self._scene.objects()) < 1:
155 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
156 dlg.SetWildcard(meshLoader.saveWildcardFilter())
157 if dlg.ShowModal() != wx.ID_OK:
160 filename = dlg.GetPath()
162 meshLoader.saveMeshes(filename, self._scene.objects())
164 def showPrintWindow(self, button):
166 if machineCom.machineIsConnected():
167 printWindow.printFile(self._gcodeFilename)
168 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
169 drives = removableStorage.getPossibleSDcardDrives()
171 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))
172 if dlg.ShowModal() != wx.ID_OK:
175 drive = drives[dlg.GetSelection()]
179 filename = self._scene._objectList[0].getName() + '.gcode'
180 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
185 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._gcodeFilename), menu.Append(-1, 'Print with USB'))
186 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
187 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
191 def showSaveGCode(self):
192 defPath = profile.getPreference('lastFile')
193 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
194 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
195 dlg.SetFilename(self._scene._objectList[0].getName())
196 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
197 if dlg.ShowModal() != wx.ID_OK:
200 filename = dlg.GetPath()
203 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
205 def _copyFile(self, fileA, fileB, allowEject = False):
207 size = float(os.stat(fileA).st_size)
208 with open(fileA, 'rb') as fsrc:
209 with open(fileB, 'wb') as fdst:
211 buf = fsrc.read(16*1024)
215 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 - numpy.array([1,1,1], numpy.float32))
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):
385 if numpy.max(obj.getSize()) > 2.0:
387 self._scene.centerAll()
388 self._selectObject(None)
391 def _splitCallback(self, progress):
394 def OnMergeObjects(self, e):
395 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
397 self._scene.merge(self._selectedObj, self._focusObj)
400 def sceneUpdated(self):
401 self._sceneUpdateTimer.Start(500, True)
402 self._slicer.abortSlicer()
403 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
406 def _onRunSlicer(self, e):
407 if self._isSimpleMode:
408 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
409 self._slicer.runSlicer(self._scene)
410 if self._isSimpleMode:
411 profile.resetTempOverride()
413 def _updateSliceProgress(self, progressValue, ready):
414 self.printButton.setDisabled(not ready)
415 if progressValue >= 0.0:
416 self.printButton.setProgressBar(progressValue)
418 self.printButton.setProgressBar(None)
419 if self._gcode is not None:
421 for layerVBOlist in self._gcodeVBOs:
422 for vbo in layerVBOlist:
423 self.glReleaseList.append(vbo)
426 self.printButton.setProgressBar(None)
427 cost = self._slicer.getFilamentCost()
429 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
431 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
432 self._gcode = gcodeInterpreter.gcode()
433 self._gcodeFilename = self._slicer.getGCodeFilename()
435 self.printButton.setBottomText('')
438 def _loadGCode(self):
439 self._gcode.progressCallback = self._gcodeLoadCallback
440 self._gcode.load(self._gcodeFilename)
442 def _gcodeLoadCallback(self, progress):
443 if self._gcode is None:
445 if len(self._gcode.layerList) % 15 == 0:
447 if self._gcode is None:
449 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
450 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
451 self.layerSelect.setValue(self.layerSelect.getMaxValue())
453 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
454 if self.viewMode == 'gcode':
458 def loadScene(self, fileList):
459 for filename in fileList:
461 objList = meshLoader.loadMeshes(filename)
463 traceback.print_exc()
466 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
468 self._scene.centerAll()
469 self._selectObject(obj)
472 def _deleteObject(self, obj):
473 if obj == self._selectedObj:
474 self._selectObject(None)
475 if obj == self._focusObj:
476 self._focusObj = None
477 self._scene.remove(obj)
478 for m in obj._meshList:
479 if m.vbo is not None and m.vbo.decRef():
480 self.glReleaseList.append(m.vbo)
485 def _selectObject(self, obj, zoom = True):
486 if obj != self._selectedObj:
487 self._selectedObj = obj
488 self.updateProfileToControls()
489 self.updateToolButtons()
490 if zoom and obj is not None:
491 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
492 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
493 newZoom = obj.getBoundaryCircle() * 6
494 if newZoom > numpy.max(self._machineSize) * 3:
495 newZoom = numpy.max(self._machineSize) * 3
496 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
498 def updateProfileToControls(self):
499 oldSimpleMode = self._isSimpleMode
500 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
501 if self._isSimpleMode and not oldSimpleMode:
502 self._scene.arrangeAll()
504 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
505 self._objColors[0] = profile.getPreferenceColour('model_colour')
506 self._objColors[1] = profile.getPreferenceColour('model_colour2')
507 self._objColors[2] = profile.getPreferenceColour('model_colour3')
508 self._objColors[3] = profile.getPreferenceColour('model_colour4')
509 self._scene.setMachineSize(self._machineSize)
510 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
511 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'))
513 if self._selectedObj is not None:
514 scale = self._selectedObj.getScale()
515 size = self._selectedObj.getSize()
516 self.scaleXctrl.setValue(round(scale[0], 2))
517 self.scaleYctrl.setValue(round(scale[1], 2))
518 self.scaleZctrl.setValue(round(scale[2], 2))
519 self.scaleXmmctrl.setValue(round(size[0], 2))
520 self.scaleYmmctrl.setValue(round(size[1], 2))
521 self.scaleZmmctrl.setValue(round(size[2], 2))
523 def OnKeyChar(self, keyCode):
524 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
525 if self._selectedObj is not None:
526 self._deleteObject(self._selectedObj)
528 if keyCode == wx.WXK_UP:
529 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
531 elif keyCode == wx.WXK_DOWN:
532 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
534 elif keyCode == wx.WXK_PAGEUP:
535 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
537 elif keyCode == wx.WXK_PAGEDOWN:
538 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
541 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
542 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
543 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
544 from collections import defaultdict
545 from gc import get_objects
546 self._beforeLeakTest = defaultdict(int)
547 for i in get_objects():
548 self._beforeLeakTest[type(i)] += 1
549 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
550 from collections import defaultdict
551 from gc import get_objects
552 self._afterLeakTest = defaultdict(int)
553 for i in get_objects():
554 self._afterLeakTest[type(i)] += 1
555 for k in self._afterLeakTest:
556 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
557 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
559 def ShaderUpdate(self, v, f):
560 s = opengl.GLShader(v, f)
562 self._objectLoadShader.release()
563 self._objectLoadShader = s
564 for obj in self._scene.objects():
565 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
568 def OnMouseDown(self,e):
569 self._mouseX = e.GetX()
570 self._mouseY = e.GetY()
571 self._mouseClick3DPos = self._mouse3Dpos
572 self._mouseClickFocus = self._focusObj
574 self._mouseState = 'doubleClick'
576 self._mouseState = 'dragOrClick'
577 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
578 p0 -= self.getObjectCenterPos() - self._viewTarget
579 p1 -= self.getObjectCenterPos() - self._viewTarget
580 if self.tool.OnDragStart(p0, p1):
581 self._mouseState = 'tool'
582 if self._mouseState == 'dragOrClick':
583 if e.GetButton() == 1:
584 if self._focusObj is not None:
585 self._selectObject(self._focusObj, False)
588 def OnMouseUp(self, e):
589 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
591 if self._mouseState == 'dragOrClick':
592 if e.GetButton() == 1:
593 self._selectObject(self._focusObj)
594 if e.GetButton() == 3:
596 if self._focusObj is not None:
597 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
598 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
599 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
600 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
601 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
602 if len(self._scene.objects()) > 0:
603 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
604 if menu.MenuItemCount > 0:
607 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
608 self._scene.pushFree()
610 elif self._mouseState == 'tool':
611 if self.tempMatrix is not None and self._selectedObj is not None:
612 self._selectedObj.applyMatrix(self.tempMatrix)
613 self._scene.pushFree()
614 self._selectObject(self._selectedObj)
615 self.tempMatrix = None
616 self.tool.OnDragEnd()
618 self._mouseState = None
620 def OnMouseMotion(self,e):
621 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
622 p0 -= self.getObjectCenterPos() - self._viewTarget
623 p1 -= self.getObjectCenterPos() - self._viewTarget
625 if e.Dragging() and self._mouseState is not None:
626 if self._mouseState == 'tool':
627 self.tool.OnDrag(p0, p1)
628 elif not e.LeftIsDown() and e.RightIsDown():
629 self._mouseState = 'drag'
630 if wx.GetKeyState(wx.WXK_SHIFT):
631 a = math.cos(math.radians(self._yaw)) / 3.0
632 b = math.sin(math.radians(self._yaw)) / 3.0
633 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
634 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
635 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
636 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
638 self._yaw += e.GetX() - self._mouseX
639 self._pitch -= e.GetY() - self._mouseY
640 if self._pitch > 170:
644 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
645 self._mouseState = 'drag'
646 self._zoom += e.GetY() - self._mouseY
649 if self._zoom > numpy.max(self._machineSize) * 3:
650 self._zoom = numpy.max(self._machineSize) * 3
651 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
652 self._mouseState = 'dragObject'
653 z = max(0, self._mouseClick3DPos[2])
654 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
655 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
660 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
661 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
662 diff = cursorZ1 - cursorZ0
663 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
664 if not e.Dragging() or self._mouseState != 'tool':
665 self.tool.OnMouseMove(p0, p1)
667 self._mouseX = e.GetX()
668 self._mouseY = e.GetY()
670 def OnMouseWheel(self, e):
671 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
672 delta = max(min(delta,4),-4)
673 self._zoom *= 1.0 - delta / 10.0
676 if self._zoom > numpy.max(self._machineSize) * 3:
677 self._zoom = numpy.max(self._machineSize) * 3
680 def getMouseRay(self, x, y):
681 if self._viewport is None:
682 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
683 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
684 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
685 p0 -= self._viewTarget
686 p1 -= self._viewTarget
689 def _init3DView(self):
690 # set viewing projection
691 size = self.GetSize()
692 glViewport(0, 0, size.GetWidth(), size.GetHeight())
695 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
697 glDisable(GL_RESCALE_NORMAL)
698 glDisable(GL_LIGHTING)
700 glEnable(GL_DEPTH_TEST)
701 glDisable(GL_CULL_FACE)
703 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
705 glClearColor(0.8, 0.8, 0.8, 1.0)
709 glMatrixMode(GL_PROJECTION)
711 aspect = float(size.GetWidth()) / float(size.GetHeight())
712 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
714 glMatrixMode(GL_MODELVIEW)
716 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
719 if machineCom.machineIsConnected():
720 self.printButton._imageID = 6
721 self.printButton._tooltip = 'Print'
722 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
723 self.printButton._imageID = 2
724 self.printButton._tooltip = 'Toolpath to SD'
726 self.printButton._imageID = 3
727 self.printButton._tooltip = 'Save toolpath'
729 if self._animView is not None:
730 self._viewTarget = self._animView.getPosition()
731 if self._animView.isDone():
732 self._animView = None
733 if self._animZoom is not None:
734 self._zoom = self._animZoom.getPosition()
735 if self._animZoom.isDone():
736 self._animZoom = None
737 if self.viewMode == 'gcode' and self._gcode is not None:
739 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
742 if self._objectShader is None:
743 self._objectShader = opengl.GLShader("""
744 varying float light_amount;
748 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
749 gl_FrontColor = gl_Color;
751 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
755 varying float light_amount;
759 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
762 self._objectOverhangShader = opengl.GLShader("""
763 uniform float cosAngle;
764 uniform mat3 rotMatrix;
765 varying float light_amount;
769 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
770 gl_FrontColor = gl_Color;
772 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
774 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
776 light_amount = -10.0;
780 varying float light_amount;
784 if (light_amount == -10.0)
786 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
788 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
792 self._objectLoadShader = opengl.GLShader("""
793 uniform float intensity;
795 varying float light_amount;
799 vec4 tmp = gl_Vertex;
800 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
801 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
802 gl_Position = gl_ModelViewProjectionMatrix * tmp;
803 gl_FrontColor = gl_Color;
805 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
809 uniform float intensity;
810 varying float light_amount;
814 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
818 glTranslate(0,0,-self._zoom)
819 glRotate(-self._pitch, 1,0,0)
820 glRotate(self._yaw, 0,0,1)
821 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
823 self._viewport = glGetIntegerv(GL_VIEWPORT)
824 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
825 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
827 glClearColor(1,1,1,1)
828 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
830 if self.viewMode != 'gcode':
831 for n in xrange(0, len(self._scene.objects())):
832 obj = self._scene.objects()[n]
833 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
834 self._renderObject(obj)
836 if self._mouseX > -1:
838 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
839 if n < len(self._scene.objects()):
840 self._focusObj = self._scene.objects()[n]
842 self._focusObj = None
843 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
844 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
845 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
846 self._mouse3Dpos -= self._viewTarget
849 glTranslate(0,0,-self._zoom)
850 glRotate(-self._pitch, 1,0,0)
851 glRotate(self._yaw, 0,0,1)
852 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
854 if self.viewMode == 'gcode':
855 if self._gcode is not None and self._gcode.layerList is None:
856 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
857 self._gcodeLoadThread.daemon = True
858 self._gcodeLoadThread.start()
859 if self._gcode is not None and self._gcode.layerList is not None:
861 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
863 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
864 for n in xrange(0, drawUpTill):
865 c = 1.0 - float(drawUpTill - n) / 15
867 if len(self._gcodeVBOs) < n + 1:
868 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
869 if time.time() - t > 0.5:
872 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
873 if n == drawUpTill - 1:
874 if len(self._gcodeVBOs[n]) < 6:
875 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
877 self._gcodeVBOs[n][5].render(GL_QUADS)
879 self._gcodeVBOs[n][6].render(GL_QUADS)
880 glColor3f(c/2, c/2, 0.0)
881 self._gcodeVBOs[n][7].render(GL_QUADS)
883 self._gcodeVBOs[n][8].render(GL_QUADS)
884 self._gcodeVBOs[n][9].render(GL_QUADS)
886 self._gcodeVBOs[n][10].render(GL_LINES)
889 self._gcodeVBOs[n][0].render(GL_LINES)
891 self._gcodeVBOs[n][1].render(GL_LINES)
892 glColor3f(c/2, c/2, 0.0)
893 self._gcodeVBOs[n][2].render(GL_LINES)
895 self._gcodeVBOs[n][3].render(GL_LINES)
896 self._gcodeVBOs[n][4].render(GL_LINES)
899 glStencilFunc(GL_ALWAYS, 1, 1)
900 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
902 if self.viewMode == 'overhang':
903 self._objectOverhangShader.bind()
904 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
906 self._objectShader.bind()
907 for obj in self._scene.objects():
908 if obj._loadAnim is not None:
909 if obj._loadAnim.isDone():
914 if self._focusObj == obj:
916 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
919 if self._selectedObj == obj or self._selectedObj is None:
920 #If we want transparent, then first render a solid black model to remove the printer size lines.
921 if self.viewMode == 'transparent':
922 glColor4f(0, 0, 0, 0)
923 self._renderObject(obj)
925 glBlendFunc(GL_ONE, GL_ONE)
926 glDisable(GL_DEPTH_TEST)
928 if self.viewMode == 'xray':
929 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
930 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
931 glEnable(GL_STENCIL_TEST)
933 if self.viewMode == 'overhang':
934 if self._selectedObj == obj and self.tempMatrix is not None:
935 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
937 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
939 if not self._scene.checkPlatform(obj):
940 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
941 self._renderObject(obj)
943 self._renderObject(obj, brightness)
944 glDisable(GL_STENCIL_TEST)
946 glEnable(GL_DEPTH_TEST)
947 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
949 if self.viewMode == 'xray':
952 glEnable(GL_STENCIL_TEST)
953 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
954 glDisable(GL_DEPTH_TEST)
955 for i in xrange(2, 15, 2):
956 glStencilFunc(GL_EQUAL, i, 0xFF)
957 glColor(float(i)/10, float(i)/10, float(i)/5)
959 glVertex3f(-1000,-1000,-10)
960 glVertex3f( 1000,-1000,-10)
961 glVertex3f( 1000, 1000,-10)
962 glVertex3f(-1000, 1000,-10)
964 for i in xrange(1, 15, 2):
965 glStencilFunc(GL_EQUAL, i, 0xFF)
966 glColor(float(i)/10, 0, 0)
968 glVertex3f(-1000,-1000,-10)
969 glVertex3f( 1000,-1000,-10)
970 glVertex3f( 1000, 1000,-10)
971 glVertex3f(-1000, 1000,-10)
974 glDisable(GL_STENCIL_TEST)
975 glEnable(GL_DEPTH_TEST)
977 self._objectShader.unbind()
979 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
981 self._objectLoadShader.bind()
982 glColor4f(0.2, 0.6, 1.0, 1.0)
983 for obj in self._scene.objects():
984 if obj._loadAnim is None:
986 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
987 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
988 self._renderObject(obj)
989 self._objectLoadShader.unbind()
994 if self.viewMode == 'gcode':
995 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
996 glDisable(GL_DEPTH_TEST)
999 glTranslate(0,-4,-10)
1000 glColor4ub(60,60,60,255)
1001 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1004 #Draw the object box-shadow, so you can see where it will collide with other objects.
1005 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1006 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1008 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1010 glEnable(GL_CULL_FACE)
1011 glColor4f(0,0,0,0.12)
1013 glVertex3f(-size[0], size[1], 0.1)
1014 glVertex3f(-size[0], -size[1], 0.1)
1015 glVertex3f( size[0], -size[1], 0.1)
1016 glVertex3f( size[0], size[1], 0.1)
1018 glDisable(GL_CULL_FACE)
1021 #Draw the outline of the selected object, on top of everything else except the GUI.
1022 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1023 glDisable(GL_DEPTH_TEST)
1024 glEnable(GL_CULL_FACE)
1025 glEnable(GL_STENCIL_TEST)
1027 glStencilFunc(GL_EQUAL, 0, 255)
1029 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1031 glColor4f(1,1,1,0.5)
1032 self._renderObject(self._selectedObj)
1033 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1035 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1036 glDisable(GL_STENCIL_TEST)
1037 glDisable(GL_CULL_FACE)
1038 glEnable(GL_DEPTH_TEST)
1040 if self._selectedObj is not None:
1042 pos = self.getObjectCenterPos()
1043 glTranslate(pos[0], pos[1], pos[2])
1047 def _renderObject(self, obj, brightness = False, addSink = True):
1050 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1052 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1054 if self.tempMatrix is not None and obj == self._selectedObj:
1055 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1056 glMultMatrixf(tempMatrix)
1058 offset = obj.getDrawOffset()
1059 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1061 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1062 glMultMatrixf(tempMatrix)
1065 for m in obj._meshList:
1067 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1069 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1074 def _drawMachine(self):
1075 glEnable(GL_CULL_FACE)
1078 if profile.getPreference('machine_type') == 'ultimaker':
1079 glColor4f(1,1,1,0.5)
1080 self._objectShader.bind()
1081 self._renderObject(self._platformMesh, False, False)
1082 self._objectShader.unbind()
1084 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1085 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1086 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1087 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1088 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1089 v4 = [ size[0] / 2, size[1] / 2, 0]
1090 v5 = [ size[0] / 2,-size[1] / 2, 0]
1091 v6 = [-size[0] / 2, size[1] / 2, 0]
1092 v7 = [-size[0] / 2,-size[1] / 2, 0]
1094 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1095 glEnableClientState(GL_VERTEX_ARRAY)
1096 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1098 glColor4ub(5, 171, 231, 64)
1099 glDrawArrays(GL_QUADS, 0, 4)
1100 glColor4ub(5, 171, 231, 96)
1101 glDrawArrays(GL_QUADS, 4, 8)
1102 glColor4ub(5, 171, 231, 128)
1103 glDrawArrays(GL_QUADS, 12, 8)
1105 sx = self._machineSize[0]
1106 sy = self._machineSize[1]
1107 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1108 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1113 x1 = max(min(x1, sx/2), -sx/2)
1114 y1 = max(min(y1, sy/2), -sy/2)
1115 x2 = max(min(x2, sx/2), -sx/2)
1116 y2 = max(min(y2, sy/2), -sy/2)
1117 if (x & 1) == (y & 1):
1118 glColor4ub(5, 171, 231, 127)
1120 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1122 glVertex3f(x1, y1, -0.02)
1123 glVertex3f(x2, y1, -0.02)
1124 glVertex3f(x2, y2, -0.02)
1125 glVertex3f(x1, y2, -0.02)
1128 glDisableClientState(GL_VERTEX_ARRAY)
1130 glDisable(GL_CULL_FACE)
1132 def _generateGCodeVBOs(self, layer):
1134 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1135 pointList = numpy.zeros((0,3), numpy.float32)
1137 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1139 a = numpy.concatenate((a[:-1], a[1:]), 1)
1140 a = a.reshape((len(a) * 2, 3))
1141 pointList = numpy.concatenate((pointList, a))
1142 ret.append(opengl.GLVBO(pointList))
1145 def _generateGCodeVBOs2(self, layer):
1146 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1147 filamentArea = math.pi * filamentRadius * filamentRadius
1150 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1151 pointList = numpy.zeros((0,3), numpy.float32)
1153 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1155 if extrudeType == 'FILL':
1158 normal = a[1:] - a[:-1]
1159 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1160 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1163 ePerDist = path['extrusion'][1:] / lens
1164 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1166 normal[:,0] *= lineWidth
1167 normal[:,1] *= lineWidth
1169 b = numpy.zeros((len(a)-1, 0), numpy.float32)
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] + normal), 1)
1174 #b = numpy.concatenate((b, a[:-1]), 1)
1175 #b = numpy.concatenate((b, a[:-1]), 1)
1176 b = b.reshape((len(b) * 4, 3))
1179 normal2 = normal[:-1] + normal[1:]
1180 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1181 normal2[:,0] /= lens2
1182 normal2[:,1] /= lens2
1183 normal2[:,0] *= lineWidth[:-1]
1184 normal2[:,1] *= lineWidth[:-1]
1186 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1187 c = numpy.concatenate((c, a[1:-1]), 1)
1188 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1189 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1190 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1192 c = numpy.concatenate((c, a[1:-1]), 1)
1193 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1194 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1195 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1197 c = c.reshape((len(c) * 8, 3))
1199 pointList = numpy.concatenate((pointList, b, c))
1201 pointList = numpy.concatenate((pointList, b))
1202 ret.append(opengl.GLVBO(pointList))
1204 pointList = numpy.zeros((0,3), numpy.float32)
1206 if path['type'] == 'move':
1207 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1208 a = numpy.concatenate((a[:-1], a[1:]), 1)
1209 a = a.reshape((len(a) * 2, 3))
1210 pointList = numpy.concatenate((pointList, a))
1211 if path['type'] == 'retract':
1212 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1213 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1214 a = a.reshape((len(a) * 2, 3))
1215 pointList = numpy.concatenate((pointList, a))
1216 ret.append(opengl.GLVBO(pointList))
1220 def getObjectCenterPos(self):
1221 if self._selectedObj is None:
1222 return [0.0, 0.0, 0.0]
1223 pos = self._selectedObj.getPosition()
1224 size = self._selectedObj.getSize()
1225 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1227 def getObjectBoundaryCircle(self):
1228 if self._selectedObj is None:
1230 return self._selectedObj.getBoundaryCircle()
1232 def getObjectSize(self):
1233 if self._selectedObj is None:
1234 return [0.0, 0.0, 0.0]
1235 return self._selectedObj.getSize()
1237 def getObjectMatrix(self):
1238 if self._selectedObj is None:
1239 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1240 return self._selectedObj.getMatrix()
1242 class shaderEditor(wx.Dialog):
1243 def __init__(self, parent, callback, v, f):
1244 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1245 self._callback = callback
1246 s = wx.BoxSizer(wx.VERTICAL)
1248 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1249 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1250 s.Add(self._vertex, 1, flag=wx.EXPAND)
1251 s.Add(self._fragment, 1, flag=wx.EXPAND)
1253 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1254 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1256 self.SetPosition(self.GetParent().GetPosition())
1257 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1260 def OnText(self, e):
1261 self._callback(self._vertex.GetValue(), self._fragment.GetValue())