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)
109 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
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._gcodeFilename)
169 if self._gcodeFilename == self._slicer.getGCodeFilename():
170 self._slicer.submitSliceInfoOnline()
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 = self._scene._objectList[0].getName() + '.gcode'
183 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
188 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._gcodeFilename), menu.Append(-1, 'Print with USB'))
189 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
190 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
194 def showSaveGCode(self):
195 defPath = profile.getPreference('lastFile')
196 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
197 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
198 dlg.SetFilename(self._scene._objectList[0].getName())
199 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
200 if dlg.ShowModal() != wx.ID_OK:
203 filename = dlg.GetPath()
206 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
208 def _copyFile(self, fileA, fileB, allowEject = False):
210 size = float(os.stat(fileA).st_size)
211 with open(fileA, 'rb') as fsrc:
212 with open(fileB, 'wb') as fdst:
214 buf = fsrc.read(16*1024)
218 self.printButton.setProgressBar(float(fsrc.tell()) / size)
223 self.notification.message("Failed to save")
226 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...'))
228 self.notification.message("Saved as %s" % (fileB))
229 self.printButton.setProgressBar(None)
230 if fileA == self._slicer.getGCodeFilename():
231 self._slicer.submitSliceInfoOnline()
233 def _showSliceLog(self):
234 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
238 def OnToolSelect(self, button):
239 if self.rotateToolButton.getSelected():
240 self.tool = previewTools.toolRotate(self)
241 elif self.scaleToolButton.getSelected():
242 self.tool = previewTools.toolScale(self)
243 elif self.mirrorToolButton.getSelected():
244 self.tool = previewTools.toolNone(self)
246 self.tool = previewTools.toolNone(self)
247 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
248 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
249 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
250 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
251 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
252 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
253 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
254 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
256 def updateToolButtons(self):
257 if self._selectedObj is None:
261 self.rotateToolButton.setHidden(hidden)
262 self.scaleToolButton.setHidden(hidden)
263 self.mirrorToolButton.setHidden(hidden)
265 self.rotateToolButton.setSelected(False)
266 self.scaleToolButton.setSelected(False)
267 self.mirrorToolButton.setSelected(False)
270 def OnViewChange(self):
271 if self.viewSelection.getValue() == 4:
272 self.viewMode = 'gcode'
273 if self._gcode is not None and self._gcode.layerList is not None:
274 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
275 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
276 self._selectObject(None)
277 elif self.viewSelection.getValue() == 1:
278 self.viewMode = 'overhang'
279 elif self.viewSelection.getValue() == 2:
280 self.viewMode = 'transparent'
281 elif self.viewSelection.getValue() == 3:
282 self.viewMode = 'xray'
284 self.viewMode = 'normal'
285 self.layerSelect.setHidden(self.viewMode != 'gcode')
288 def OnRotateReset(self, button):
289 if self._selectedObj is None:
291 self._selectedObj.resetRotation()
292 self._scene.pushFree()
293 self._selectObject(self._selectedObj)
296 def OnLayFlat(self, button):
297 if self._selectedObj is None:
299 self._selectedObj.layFlat()
300 self._scene.pushFree()
301 self._selectObject(self._selectedObj)
304 def OnScaleReset(self, button):
305 if self._selectedObj is None:
307 self._selectedObj.resetScale()
308 self._selectObject(self._selectedObj)
309 self.updateProfileToControls()
312 def OnScaleMax(self, button):
313 if self._selectedObj is None:
315 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
316 self._scene.pushFree()
317 self._selectObject(self._selectedObj)
318 self.updateProfileToControls()
321 def OnMirror(self, axis):
322 if self._selectedObj is None:
324 self._selectedObj.mirror(axis)
327 def OnScaleEntry(self, value, axis):
328 if self._selectedObj is None:
334 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
335 self.updateProfileToControls()
336 self._scene.pushFree()
337 self._selectObject(self._selectedObj)
340 def OnScaleEntryMM(self, value, axis):
341 if self._selectedObj is None:
347 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
348 self.updateProfileToControls()
349 self._scene.pushFree()
350 self._selectObject(self._selectedObj)
353 def OnDeleteAll(self, e):
354 while len(self._scene.objects()) > 0:
355 self._deleteObject(self._scene.objects()[0])
356 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
358 def OnMultiply(self, e):
359 if self._focusObj is None:
362 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
363 if dlg.ShowModal() != wx.ID_OK:
372 self._scene.add(newObj)
373 self._scene.centerAll()
374 if not self._scene.checkPlatform(newObj):
379 self.notification.message("Could not create more then %d items" % (n - 1))
380 self._scene.remove(newObj)
381 self._scene.centerAll()
384 def OnSplitObject(self, e):
385 if self._focusObj is None:
387 self._scene.remove(self._focusObj)
388 for obj in self._focusObj.split(self._splitCallback):
389 if numpy.max(obj.getSize()) > 2.0:
391 self._scene.centerAll()
392 self._selectObject(None)
395 def _splitCallback(self, progress):
398 def OnMergeObjects(self, e):
399 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
401 self._scene.merge(self._selectedObj, self._focusObj)
404 def sceneUpdated(self):
405 self._sceneUpdateTimer.Start(500, True)
406 self._slicer.abortSlicer()
407 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
410 def _onRunSlicer(self, e):
411 if self._isSimpleMode:
412 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
413 self._slicer.runSlicer(self._scene)
414 if self._isSimpleMode:
415 profile.resetTempOverride()
417 def _updateSliceProgress(self, progressValue, ready):
419 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
421 self.printButton.setDisabled(not ready)
422 if progressValue >= 0.0:
423 self.printButton.setProgressBar(progressValue)
425 self.printButton.setProgressBar(None)
426 if self._gcode is not None:
428 for layerVBOlist in self._gcodeVBOs:
429 for vbo in layerVBOlist:
430 self.glReleaseList.append(vbo)
433 self.printButton.setProgressBar(None)
434 cost = self._slicer.getFilamentCost()
436 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
438 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
439 self._gcode = gcodeInterpreter.gcode()
440 self._gcodeFilename = self._slicer.getGCodeFilename()
442 self.printButton.setBottomText('')
445 def _loadGCode(self):
446 self._gcode.progressCallback = self._gcodeLoadCallback
447 self._gcode.load(self._gcodeFilename)
449 def _gcodeLoadCallback(self, progress):
450 if self._gcode is None:
452 if len(self._gcode.layerList) % 15 == 0:
454 if self._gcode is None:
456 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
457 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
458 self.layerSelect.setValue(self.layerSelect.getMaxValue())
460 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
461 if self.viewMode == 'gcode':
465 def loadScene(self, fileList):
466 for filename in fileList:
468 objList = meshLoader.loadMeshes(filename)
470 traceback.print_exc()
473 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
475 self._scene.centerAll()
476 self._selectObject(obj)
479 def _deleteObject(self, obj):
480 if obj == self._selectedObj:
481 self._selectObject(None)
482 if obj == self._focusObj:
483 self._focusObj = None
484 self._scene.remove(obj)
485 for m in obj._meshList:
486 if m.vbo is not None and m.vbo.decRef():
487 self.glReleaseList.append(m.vbo)
492 def _selectObject(self, obj, zoom = True):
493 if obj != self._selectedObj:
494 self._selectedObj = obj
495 self.updateProfileToControls()
496 self.updateToolButtons()
497 if zoom and obj is not None:
498 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
499 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
500 newZoom = obj.getBoundaryCircle() * 6
501 if newZoom > numpy.max(self._machineSize) * 3:
502 newZoom = numpy.max(self._machineSize) * 3
503 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
505 def updateProfileToControls(self):
506 oldSimpleMode = self._isSimpleMode
507 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
508 if self._isSimpleMode and not oldSimpleMode:
509 self._scene.arrangeAll()
511 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
512 self._objColors[0] = profile.getPreferenceColour('model_colour')
513 self._objColors[1] = profile.getPreferenceColour('model_colour2')
514 self._objColors[2] = profile.getPreferenceColour('model_colour3')
515 self._objColors[3] = profile.getPreferenceColour('model_colour4')
516 self._scene.setMachineSize(self._machineSize)
517 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
518 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'))
520 if self._selectedObj is not None:
521 scale = self._selectedObj.getScale()
522 size = self._selectedObj.getSize()
523 self.scaleXctrl.setValue(round(scale[0], 2))
524 self.scaleYctrl.setValue(round(scale[1], 2))
525 self.scaleZctrl.setValue(round(scale[2], 2))
526 self.scaleXmmctrl.setValue(round(size[0], 2))
527 self.scaleYmmctrl.setValue(round(size[1], 2))
528 self.scaleZmmctrl.setValue(round(size[2], 2))
530 def OnKeyChar(self, keyCode):
531 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
532 if self._selectedObj is not None:
533 self._deleteObject(self._selectedObj)
535 if keyCode == wx.WXK_UP:
536 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
538 elif keyCode == wx.WXK_DOWN:
539 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
541 elif keyCode == wx.WXK_PAGEUP:
542 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
544 elif keyCode == wx.WXK_PAGEDOWN:
545 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
548 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
549 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
550 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
551 from collections import defaultdict
552 from gc import get_objects
553 self._beforeLeakTest = defaultdict(int)
554 for i in get_objects():
555 self._beforeLeakTest[type(i)] += 1
556 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
557 from collections import defaultdict
558 from gc import get_objects
559 self._afterLeakTest = defaultdict(int)
560 for i in get_objects():
561 self._afterLeakTest[type(i)] += 1
562 for k in self._afterLeakTest:
563 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
564 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
566 def ShaderUpdate(self, v, f):
567 s = opengl.GLShader(v, f)
569 self._objectLoadShader.release()
570 self._objectLoadShader = s
571 for obj in self._scene.objects():
572 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
575 def OnMouseDown(self,e):
576 self._mouseX = e.GetX()
577 self._mouseY = e.GetY()
578 self._mouseClick3DPos = self._mouse3Dpos
579 self._mouseClickFocus = self._focusObj
581 self._mouseState = 'doubleClick'
583 self._mouseState = 'dragOrClick'
584 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
585 p0 -= self.getObjectCenterPos() - self._viewTarget
586 p1 -= self.getObjectCenterPos() - self._viewTarget
587 if self.tool.OnDragStart(p0, p1):
588 self._mouseState = 'tool'
589 if self._mouseState == 'dragOrClick':
590 if e.GetButton() == 1:
591 if self._focusObj is not None:
592 self._selectObject(self._focusObj, False)
595 def OnMouseUp(self, e):
596 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
598 if self._mouseState == 'dragOrClick':
599 if e.GetButton() == 1:
600 self._selectObject(self._focusObj)
601 if e.GetButton() == 3:
603 if self._focusObj is not None:
604 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
605 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
606 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
607 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
608 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
609 if len(self._scene.objects()) > 0:
610 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
611 if menu.MenuItemCount > 0:
614 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
615 self._scene.pushFree()
617 elif self._mouseState == 'tool':
618 if self.tempMatrix is not None and self._selectedObj is not None:
619 self._selectedObj.applyMatrix(self.tempMatrix)
620 self._scene.pushFree()
621 self._selectObject(self._selectedObj)
622 self.tempMatrix = None
623 self.tool.OnDragEnd()
625 self._mouseState = None
627 def OnMouseMotion(self,e):
628 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
629 p0 -= self.getObjectCenterPos() - self._viewTarget
630 p1 -= self.getObjectCenterPos() - self._viewTarget
632 if e.Dragging() and self._mouseState is not None:
633 if self._mouseState == 'tool':
634 self.tool.OnDrag(p0, p1)
635 elif not e.LeftIsDown() and e.RightIsDown():
636 self._mouseState = 'drag'
637 if wx.GetKeyState(wx.WXK_SHIFT):
638 a = math.cos(math.radians(self._yaw)) / 3.0
639 b = math.sin(math.radians(self._yaw)) / 3.0
640 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
641 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
642 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
643 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
645 self._yaw += e.GetX() - self._mouseX
646 self._pitch -= e.GetY() - self._mouseY
647 if self._pitch > 170:
651 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
652 self._mouseState = 'drag'
653 self._zoom += e.GetY() - self._mouseY
656 if self._zoom > numpy.max(self._machineSize) * 3:
657 self._zoom = numpy.max(self._machineSize) * 3
658 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
659 self._mouseState = 'dragObject'
660 z = max(0, self._mouseClick3DPos[2])
661 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
662 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
667 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
668 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
669 diff = cursorZ1 - cursorZ0
670 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
671 if not e.Dragging() or self._mouseState != 'tool':
672 self.tool.OnMouseMove(p0, p1)
674 self._mouseX = e.GetX()
675 self._mouseY = e.GetY()
677 def OnMouseWheel(self, e):
678 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
679 delta = max(min(delta,4),-4)
680 self._zoom *= 1.0 - delta / 10.0
683 if self._zoom > numpy.max(self._machineSize) * 3:
684 self._zoom = numpy.max(self._machineSize) * 3
687 def OnMouseLeave(self, e):
689 self._focusObj = None
691 def getMouseRay(self, x, y):
692 if self._viewport is None:
693 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
694 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
695 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
696 p0 -= self._viewTarget
697 p1 -= self._viewTarget
700 def _init3DView(self):
701 # set viewing projection
702 size = self.GetSize()
703 glViewport(0, 0, size.GetWidth(), size.GetHeight())
706 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
708 glDisable(GL_RESCALE_NORMAL)
709 glDisable(GL_LIGHTING)
711 glEnable(GL_DEPTH_TEST)
712 glDisable(GL_CULL_FACE)
714 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
716 glClearColor(0.8, 0.8, 0.8, 1.0)
720 glMatrixMode(GL_PROJECTION)
722 aspect = float(size.GetWidth()) / float(size.GetHeight())
723 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
725 glMatrixMode(GL_MODELVIEW)
727 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
730 if machineCom.machineIsConnected():
731 self.printButton._imageID = 6
732 self.printButton._tooltip = 'Print'
733 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
734 self.printButton._imageID = 2
735 self.printButton._tooltip = 'Toolpath to SD'
737 self.printButton._imageID = 3
738 self.printButton._tooltip = 'Save toolpath'
740 if self._animView is not None:
741 self._viewTarget = self._animView.getPosition()
742 if self._animView.isDone():
743 self._animView = None
744 if self._animZoom is not None:
745 self._zoom = self._animZoom.getPosition()
746 if self._animZoom.isDone():
747 self._animZoom = None
748 if self.viewMode == 'gcode' and self._gcode is not None:
750 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
753 if self._objectShader is None:
754 self._objectShader = opengl.GLShader("""
755 varying float light_amount;
759 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
760 gl_FrontColor = gl_Color;
762 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
766 varying float light_amount;
770 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
773 self._objectOverhangShader = opengl.GLShader("""
774 uniform float cosAngle;
775 uniform mat3 rotMatrix;
776 varying float light_amount;
780 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
781 gl_FrontColor = gl_Color;
783 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
785 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
787 light_amount = -10.0;
791 varying float light_amount;
795 if (light_amount == -10.0)
797 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
799 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
803 self._objectLoadShader = opengl.GLShader("""
804 uniform float intensity;
806 varying float light_amount;
810 vec4 tmp = gl_Vertex;
811 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
812 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
813 gl_Position = gl_ModelViewProjectionMatrix * tmp;
814 gl_FrontColor = gl_Color;
816 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
820 uniform float intensity;
821 varying float light_amount;
825 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
829 glTranslate(0,0,-self._zoom)
830 glRotate(-self._pitch, 1,0,0)
831 glRotate(self._yaw, 0,0,1)
832 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
834 self._viewport = glGetIntegerv(GL_VIEWPORT)
835 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
836 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
838 glClearColor(1,1,1,1)
839 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
841 if self.viewMode != 'gcode':
842 for n in xrange(0, len(self._scene.objects())):
843 obj = self._scene.objects()[n]
844 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
845 self._renderObject(obj)
847 if self._mouseX > -1:
849 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
850 if n < len(self._scene.objects()):
851 self._focusObj = self._scene.objects()[n]
853 self._focusObj = None
854 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
855 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
856 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
857 self._mouse3Dpos -= self._viewTarget
860 glTranslate(0,0,-self._zoom)
861 glRotate(-self._pitch, 1,0,0)
862 glRotate(self._yaw, 0,0,1)
863 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
865 if self.viewMode == 'gcode':
866 if self._gcode is not None and self._gcode.layerList is None:
867 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
868 self._gcodeLoadThread.daemon = True
869 self._gcodeLoadThread.start()
870 if self._gcode is not None and self._gcode.layerList is not None:
872 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
874 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
875 for n in xrange(0, drawUpTill):
876 c = 1.0 - float(drawUpTill - n) / 15
878 if len(self._gcodeVBOs) < n + 1:
879 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
880 if time.time() - t > 0.5:
883 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
884 if n == drawUpTill - 1:
885 if len(self._gcodeVBOs[n]) < 6:
886 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
888 self._gcodeVBOs[n][5].render(GL_QUADS)
890 self._gcodeVBOs[n][6].render(GL_QUADS)
891 glColor3f(c/2, c/2, 0.0)
892 self._gcodeVBOs[n][7].render(GL_QUADS)
894 self._gcodeVBOs[n][8].render(GL_QUADS)
895 self._gcodeVBOs[n][9].render(GL_QUADS)
897 self._gcodeVBOs[n][10].render(GL_LINES)
900 self._gcodeVBOs[n][0].render(GL_LINES)
902 self._gcodeVBOs[n][1].render(GL_LINES)
903 glColor3f(c/2, c/2, 0.0)
904 self._gcodeVBOs[n][2].render(GL_LINES)
906 self._gcodeVBOs[n][3].render(GL_LINES)
907 self._gcodeVBOs[n][4].render(GL_LINES)
910 glStencilFunc(GL_ALWAYS, 1, 1)
911 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
913 if self.viewMode == 'overhang':
914 self._objectOverhangShader.bind()
915 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
917 self._objectShader.bind()
918 for obj in self._scene.objects():
919 if obj._loadAnim is not None:
920 if obj._loadAnim.isDone():
925 if self._focusObj == obj:
927 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
930 if self._selectedObj == obj or self._selectedObj is None:
931 #If we want transparent, then first render a solid black model to remove the printer size lines.
932 if self.viewMode == 'transparent':
933 glColor4f(0, 0, 0, 0)
934 self._renderObject(obj)
936 glBlendFunc(GL_ONE, GL_ONE)
937 glDisable(GL_DEPTH_TEST)
939 if self.viewMode == 'xray':
940 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
941 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
942 glEnable(GL_STENCIL_TEST)
944 if self.viewMode == 'overhang':
945 if self._selectedObj == obj and self.tempMatrix is not None:
946 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
948 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
950 if not self._scene.checkPlatform(obj):
951 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
952 self._renderObject(obj)
954 self._renderObject(obj, brightness)
955 glDisable(GL_STENCIL_TEST)
957 glEnable(GL_DEPTH_TEST)
958 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
960 if self.viewMode == 'xray':
963 glEnable(GL_STENCIL_TEST)
964 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
965 glDisable(GL_DEPTH_TEST)
966 for i in xrange(2, 15, 2):
967 glStencilFunc(GL_EQUAL, i, 0xFF)
968 glColor(float(i)/10, float(i)/10, float(i)/5)
970 glVertex3f(-1000,-1000,-10)
971 glVertex3f( 1000,-1000,-10)
972 glVertex3f( 1000, 1000,-10)
973 glVertex3f(-1000, 1000,-10)
975 for i in xrange(1, 15, 2):
976 glStencilFunc(GL_EQUAL, i, 0xFF)
977 glColor(float(i)/10, 0, 0)
979 glVertex3f(-1000,-1000,-10)
980 glVertex3f( 1000,-1000,-10)
981 glVertex3f( 1000, 1000,-10)
982 glVertex3f(-1000, 1000,-10)
985 glDisable(GL_STENCIL_TEST)
986 glEnable(GL_DEPTH_TEST)
988 self._objectShader.unbind()
990 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
992 self._objectLoadShader.bind()
993 glColor4f(0.2, 0.6, 1.0, 1.0)
994 for obj in self._scene.objects():
995 if obj._loadAnim is None:
997 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
998 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
999 self._renderObject(obj)
1000 self._objectLoadShader.unbind()
1005 if self.viewMode == 'gcode':
1006 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1007 glDisable(GL_DEPTH_TEST)
1010 glTranslate(0,-4,-10)
1011 glColor4ub(60,60,60,255)
1012 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1015 #Draw the object box-shadow, so you can see where it will collide with other objects.
1016 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1017 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1019 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1021 glEnable(GL_CULL_FACE)
1022 glColor4f(0,0,0,0.12)
1024 glVertex3f(-size[0], size[1], 0.1)
1025 glVertex3f(-size[0], -size[1], 0.1)
1026 glVertex3f( size[0], -size[1], 0.1)
1027 glVertex3f( size[0], size[1], 0.1)
1029 glDisable(GL_CULL_FACE)
1032 #Draw the outline of the selected object, on top of everything else except the GUI.
1033 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1034 glDisable(GL_DEPTH_TEST)
1035 glEnable(GL_CULL_FACE)
1036 glEnable(GL_STENCIL_TEST)
1038 glStencilFunc(GL_EQUAL, 0, 255)
1040 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1042 glColor4f(1,1,1,0.5)
1043 self._renderObject(self._selectedObj)
1044 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1046 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1047 glDisable(GL_STENCIL_TEST)
1048 glDisable(GL_CULL_FACE)
1049 glEnable(GL_DEPTH_TEST)
1051 if self._selectedObj is not None:
1053 pos = self.getObjectCenterPos()
1054 glTranslate(pos[0], pos[1], pos[2])
1058 def _renderObject(self, obj, brightness = False, addSink = True):
1061 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1063 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1065 if self.tempMatrix is not None and obj == self._selectedObj:
1066 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1067 glMultMatrixf(tempMatrix)
1069 offset = obj.getDrawOffset()
1070 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1072 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1073 glMultMatrixf(tempMatrix)
1076 for m in obj._meshList:
1078 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1080 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1085 def _drawMachine(self):
1086 glEnable(GL_CULL_FACE)
1089 if profile.getPreference('machine_type') == 'ultimaker':
1090 glColor4f(1,1,1,0.5)
1091 self._objectShader.bind()
1092 self._renderObject(self._platformMesh, False, False)
1093 self._objectShader.unbind()
1095 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1096 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1097 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1098 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1099 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1100 v4 = [ size[0] / 2, size[1] / 2, 0]
1101 v5 = [ size[0] / 2,-size[1] / 2, 0]
1102 v6 = [-size[0] / 2, size[1] / 2, 0]
1103 v7 = [-size[0] / 2,-size[1] / 2, 0]
1105 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1106 glEnableClientState(GL_VERTEX_ARRAY)
1107 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1109 glColor4ub(5, 171, 231, 64)
1110 glDrawArrays(GL_QUADS, 0, 4)
1111 glColor4ub(5, 171, 231, 96)
1112 glDrawArrays(GL_QUADS, 4, 8)
1113 glColor4ub(5, 171, 231, 128)
1114 glDrawArrays(GL_QUADS, 12, 8)
1116 sx = self._machineSize[0]
1117 sy = self._machineSize[1]
1118 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1119 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1124 x1 = max(min(x1, sx/2), -sx/2)
1125 y1 = max(min(y1, sy/2), -sy/2)
1126 x2 = max(min(x2, sx/2), -sx/2)
1127 y2 = max(min(y2, sy/2), -sy/2)
1128 if (x & 1) == (y & 1):
1129 glColor4ub(5, 171, 231, 127)
1131 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1133 glVertex3f(x1, y1, -0.02)
1134 glVertex3f(x2, y1, -0.02)
1135 glVertex3f(x2, y2, -0.02)
1136 glVertex3f(x1, y2, -0.02)
1139 glDisableClientState(GL_VERTEX_ARRAY)
1141 glDisable(GL_CULL_FACE)
1143 def _generateGCodeVBOs(self, layer):
1145 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1146 pointList = numpy.zeros((0,3), numpy.float32)
1148 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1150 a = numpy.concatenate((a[:-1], a[1:]), 1)
1151 a = a.reshape((len(a) * 2, 3))
1152 pointList = numpy.concatenate((pointList, a))
1153 ret.append(opengl.GLVBO(pointList))
1156 def _generateGCodeVBOs2(self, layer):
1157 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1158 filamentArea = math.pi * filamentRadius * filamentRadius
1161 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1162 pointList = numpy.zeros((0,3), numpy.float32)
1164 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1166 if extrudeType == 'FILL':
1169 normal = a[1:] - a[:-1]
1170 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1171 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1174 ePerDist = path['extrusion'][1:] / lens
1175 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1177 normal[:,0] *= lineWidth
1178 normal[:,1] *= lineWidth
1180 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1181 b = numpy.concatenate((b, a[1:] + normal), 1)
1182 b = numpy.concatenate((b, a[1:] - normal), 1)
1183 b = numpy.concatenate((b, a[:-1] - normal), 1)
1184 b = numpy.concatenate((b, a[:-1] + normal), 1)
1185 #b = numpy.concatenate((b, a[:-1]), 1)
1186 #b = numpy.concatenate((b, a[:-1]), 1)
1187 b = b.reshape((len(b) * 4, 3))
1190 normal2 = normal[:-1] + normal[1:]
1191 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1192 normal2[:,0] /= lens2
1193 normal2[:,1] /= lens2
1194 normal2[:,0] *= lineWidth[:-1]
1195 normal2[:,1] *= lineWidth[:-1]
1197 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1198 c = numpy.concatenate((c, a[1:-1]), 1)
1199 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1200 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1201 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1203 c = numpy.concatenate((c, a[1:-1]), 1)
1204 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1205 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1206 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1208 c = c.reshape((len(c) * 8, 3))
1210 pointList = numpy.concatenate((pointList, b, c))
1212 pointList = numpy.concatenate((pointList, b))
1213 ret.append(opengl.GLVBO(pointList))
1215 pointList = numpy.zeros((0,3), numpy.float32)
1217 if path['type'] == 'move':
1218 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1219 a = numpy.concatenate((a[:-1], a[1:]), 1)
1220 a = a.reshape((len(a) * 2, 3))
1221 pointList = numpy.concatenate((pointList, a))
1222 if path['type'] == 'retract':
1223 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1224 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1225 a = a.reshape((len(a) * 2, 3))
1226 pointList = numpy.concatenate((pointList, a))
1227 ret.append(opengl.GLVBO(pointList))
1231 def getObjectCenterPos(self):
1232 if self._selectedObj is None:
1233 return [0.0, 0.0, 0.0]
1234 pos = self._selectedObj.getPosition()
1235 size = self._selectedObj.getSize()
1236 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1238 def getObjectBoundaryCircle(self):
1239 if self._selectedObj is None:
1241 return self._selectedObj.getBoundaryCircle()
1243 def getObjectSize(self):
1244 if self._selectedObj is None:
1245 return [0.0, 0.0, 0.0]
1246 return self._selectedObj.getSize()
1248 def getObjectMatrix(self):
1249 if self._selectedObj is None:
1250 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1251 return self._selectedObj.getMatrix()
1253 class shaderEditor(wx.Dialog):
1254 def __init__(self, parent, callback, v, f):
1255 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1256 self._callback = callback
1257 s = wx.BoxSizer(wx.VERTICAL)
1259 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1260 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1261 s.Add(self._vertex, 1, flag=wx.EXPAND)
1262 s.Add(self._fragment, 1, flag=wx.EXPAND)
1264 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1265 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1267 self.SetPosition(self.GetParent().GetPosition())
1268 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1271 def OnText(self, e):
1272 self._callback(self._vertex.GetValue(), self._fragment.GetValue())