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._gcodeFilename = None
41 self._gcodeLoadThread = None
42 self._objectShader = None
43 self._objectLoadShader = None
45 self._selectedObj = None
46 self._objColors = [None,None,None,None]
49 self._mouseState = None
50 self._viewTarget = numpy.array([0,0,0], numpy.float32)
53 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
54 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
55 self._isSimpleMode = True
56 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
59 self._modelMatrix = None
60 self._projMatrix = None
61 self.tempMatrix = None
63 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
64 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.OnPrintButton)
65 self.printButton.setDisabled(True)
68 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
69 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
70 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
72 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
73 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
75 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
76 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
78 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
79 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
80 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
82 self.rotateToolButton.setExpandArrow(True)
83 self.scaleToolButton.setExpandArrow(True)
84 self.mirrorToolButton.setExpandArrow(True)
86 self.scaleForm = openglGui.glFrame(self, (2, -2))
87 openglGui.glGuiLayoutGrid(self.scaleForm)
88 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
89 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
90 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
91 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
92 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
93 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
94 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
95 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
96 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
97 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
98 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
99 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
100 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
101 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
103 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
104 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
106 self.notification = openglGui.glNotification(self, (0, 0))
108 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
109 self._sceneUpdateTimer = wx.Timer(self)
110 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
111 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
112 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
116 self.updateToolButtons()
117 self.updateProfileToControls()
119 def showLoadModel(self, button = 1):
121 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)
122 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
123 if dlg.ShowModal() != wx.ID_OK:
126 filenames = dlg.GetPaths()
128 if len(filenames) < 1:
130 profile.putPreference('lastFile', filenames[0])
132 for filename in filenames:
133 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
134 ext = filename[filename.rfind('.')+1:].upper()
135 if ext == 'G' or ext == 'GCODE':
136 gcodeFilename = filename
137 if gcodeFilename is not None:
138 if self._gcode is not None:
140 for layerVBOlist in self._gcodeVBOs:
141 for vbo in layerVBOlist:
142 self.glReleaseList.append(vbo)
144 self._gcode = gcodeInterpreter.gcode()
145 self._gcodeFilename = gcodeFilename
146 self.printButton.setBottomText('')
147 self.viewSelection.setValue(4)
148 self.printButton.setDisabled(False)
151 if self.viewSelection.getValue() == 4:
152 self.viewSelection.setValue(0)
154 self.loadScene(filenames)
156 def showSaveModel(self):
157 if len(self._scene.objects()) < 1:
159 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
160 dlg.SetWildcard(meshLoader.saveWildcardFilter())
161 if dlg.ShowModal() != wx.ID_OK:
164 filename = dlg.GetPath()
166 meshLoader.saveMeshes(filename, self._scene.objects())
168 def OnPrintButton(self, button):
170 if machineCom.machineIsConnected():
171 self.showPrintWindow()
172 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
173 drives = removableStorage.getPossibleSDcardDrives()
175 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))
176 if dlg.ShowModal() != wx.ID_OK:
179 drive = drives[dlg.GetSelection()]
183 filename = self._scene._objectList[0].getName() + '.gcode'
184 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
189 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, 'Print with USB'))
190 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
191 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
195 def showPrintWindow(self):
196 if self._gcodeFilename is None:
198 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
199 if self._gcodeFilename == self._slicer.getGCodeFilename():
200 self._slicer.submitSliceInfoOnline()
202 def showSaveGCode(self):
203 defPath = profile.getPreference('lastFile')
204 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
205 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
206 dlg.SetFilename(self._scene._objectList[0].getName())
207 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
208 if dlg.ShowModal() != wx.ID_OK:
211 filename = dlg.GetPath()
214 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
216 def _copyFile(self, fileA, fileB, allowEject = False):
218 size = float(os.stat(fileA).st_size)
219 with open(fileA, 'rb') as fsrc:
220 with open(fileB, 'wb') as fdst:
222 buf = fsrc.read(16*1024)
226 self.printButton.setProgressBar(float(fsrc.tell()) / size)
231 self.notification.message("Failed to save")
234 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...'))
236 self.notification.message("Saved as %s" % (fileB))
237 self.printButton.setProgressBar(None)
238 if fileA == self._slicer.getGCodeFilename():
239 self._slicer.submitSliceInfoOnline()
241 def _showSliceLog(self):
242 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
246 def OnToolSelect(self, button):
247 if self.rotateToolButton.getSelected():
248 self.tool = previewTools.toolRotate(self)
249 elif self.scaleToolButton.getSelected():
250 self.tool = previewTools.toolScale(self)
251 elif self.mirrorToolButton.getSelected():
252 self.tool = previewTools.toolNone(self)
254 self.tool = previewTools.toolNone(self)
255 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
256 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
257 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
258 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
259 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
260 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
261 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
262 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
264 def updateToolButtons(self):
265 if self._selectedObj is None:
269 self.rotateToolButton.setHidden(hidden)
270 self.scaleToolButton.setHidden(hidden)
271 self.mirrorToolButton.setHidden(hidden)
273 self.rotateToolButton.setSelected(False)
274 self.scaleToolButton.setSelected(False)
275 self.mirrorToolButton.setSelected(False)
278 def OnViewChange(self):
279 if self.viewSelection.getValue() == 4:
280 self.viewMode = 'gcode'
281 if self._gcode is not None and self._gcode.layerList is not None:
282 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
283 self._selectObject(None)
284 elif self.viewSelection.getValue() == 1:
285 self.viewMode = 'overhang'
286 elif self.viewSelection.getValue() == 2:
287 self.viewMode = 'transparent'
288 elif self.viewSelection.getValue() == 3:
289 self.viewMode = 'xray'
291 self.viewMode = 'normal'
292 self.layerSelect.setHidden(self.viewMode != 'gcode')
295 def OnRotateReset(self, button):
296 if self._selectedObj is None:
298 self._selectedObj.resetRotation()
299 self._scene.pushFree()
300 self._selectObject(self._selectedObj)
303 def OnLayFlat(self, button):
304 if self._selectedObj is None:
306 self._selectedObj.layFlat()
307 self._scene.pushFree()
308 self._selectObject(self._selectedObj)
311 def OnScaleReset(self, button):
312 if self._selectedObj is None:
314 self._selectedObj.resetScale()
315 self._selectObject(self._selectedObj)
316 self.updateProfileToControls()
319 def OnScaleMax(self, button):
320 if self._selectedObj is None:
322 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
323 self._scene.pushFree()
324 self._selectObject(self._selectedObj)
325 self.updateProfileToControls()
328 def OnMirror(self, axis):
329 if self._selectedObj is None:
331 self._selectedObj.mirror(axis)
334 def OnScaleEntry(self, value, axis):
335 if self._selectedObj is None:
341 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
342 self.updateProfileToControls()
343 self._scene.pushFree()
344 self._selectObject(self._selectedObj)
347 def OnScaleEntryMM(self, value, axis):
348 if self._selectedObj is None:
354 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
355 self.updateProfileToControls()
356 self._scene.pushFree()
357 self._selectObject(self._selectedObj)
360 def OnDeleteAll(self, e):
361 while len(self._scene.objects()) > 0:
362 self._deleteObject(self._scene.objects()[0])
363 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
365 def OnMultiply(self, e):
366 if self._focusObj is None:
369 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
370 if dlg.ShowModal() != wx.ID_OK:
379 self._scene.add(newObj)
380 self._scene.centerAll()
381 if not self._scene.checkPlatform(newObj):
386 self.notification.message("Could not create more then %d items" % (n - 1))
387 self._scene.remove(newObj)
388 self._scene.centerAll()
391 def OnSplitObject(self, e):
392 if self._focusObj is None:
394 self._scene.remove(self._focusObj)
395 for obj in self._focusObj.split(self._splitCallback):
396 if numpy.max(obj.getSize()) > 2.0:
398 self._scene.centerAll()
399 self._selectObject(None)
402 def _splitCallback(self, progress):
405 def OnMergeObjects(self, e):
406 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
408 self._scene.merge(self._selectedObj, self._focusObj)
411 def sceneUpdated(self):
412 self._sceneUpdateTimer.Start(500, True)
413 self._slicer.abortSlicer()
414 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
417 def _onRunSlicer(self, e):
418 if self._isSimpleMode:
419 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
420 self._slicer.runSlicer(self._scene)
421 if self._isSimpleMode:
422 profile.resetTempOverride()
424 def _updateSliceProgress(self, progressValue, ready):
426 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
428 self.printButton.setDisabled(not ready)
429 if progressValue >= 0.0:
430 self.printButton.setProgressBar(progressValue)
432 self.printButton.setProgressBar(None)
433 if self._gcode is not None:
435 for layerVBOlist in self._gcodeVBOs:
436 for vbo in layerVBOlist:
437 self.glReleaseList.append(vbo)
440 self.printButton.setProgressBar(None)
441 cost = self._slicer.getFilamentCost()
443 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
445 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
446 self._gcode = gcodeInterpreter.gcode()
447 self._gcodeFilename = self._slicer.getGCodeFilename()
449 self.printButton.setBottomText('')
452 def _loadGCode(self):
453 self._gcode.progressCallback = self._gcodeLoadCallback
454 self._gcode.load(self._gcodeFilename)
456 def _gcodeLoadCallback(self, progress):
457 if self._gcode is None:
459 if len(self._gcode.layerList) % 15 == 0:
461 if self._gcode is None:
463 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
464 if self.viewMode == 'gcode':
468 def loadScene(self, fileList):
469 for filename in fileList:
471 objList = meshLoader.loadMeshes(filename)
473 traceback.print_exc()
476 if self._objectLoadShader is not None:
477 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
481 self._scene.centerAll()
482 self._selectObject(obj)
485 def _deleteObject(self, obj):
486 if obj == self._selectedObj:
487 self._selectObject(None)
488 if obj == self._focusObj:
489 self._focusObj = None
490 self._scene.remove(obj)
491 for m in obj._meshList:
492 if m.vbo is not None and m.vbo.decRef():
493 self.glReleaseList.append(m.vbo)
498 def _selectObject(self, obj, zoom = True):
499 if obj != self._selectedObj:
500 self._selectedObj = obj
501 self.updateProfileToControls()
502 self.updateToolButtons()
503 if zoom and obj is not None:
504 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
505 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
506 newZoom = obj.getBoundaryCircle() * 6
507 if newZoom > numpy.max(self._machineSize) * 3:
508 newZoom = numpy.max(self._machineSize) * 3
509 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
511 def updateProfileToControls(self):
512 oldSimpleMode = self._isSimpleMode
513 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
514 if self._isSimpleMode != oldSimpleMode:
515 self._scene.arrangeAll()
517 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
518 self._objColors[0] = profile.getPreferenceColour('model_colour')
519 self._objColors[1] = profile.getPreferenceColour('model_colour2')
520 self._objColors[2] = profile.getPreferenceColour('model_colour3')
521 self._objColors[3] = profile.getPreferenceColour('model_colour4')
522 self._scene.setMachineSize(self._machineSize)
523 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
524 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'))
526 if self._selectedObj is not None:
527 scale = self._selectedObj.getScale()
528 size = self._selectedObj.getSize()
529 self.scaleXctrl.setValue(round(scale[0], 2))
530 self.scaleYctrl.setValue(round(scale[1], 2))
531 self.scaleZctrl.setValue(round(scale[2], 2))
532 self.scaleXmmctrl.setValue(round(size[0], 2))
533 self.scaleYmmctrl.setValue(round(size[1], 2))
534 self.scaleZmmctrl.setValue(round(size[2], 2))
536 def OnKeyChar(self, keyCode):
537 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
538 if self._selectedObj is not None:
539 self._deleteObject(self._selectedObj)
541 if keyCode == wx.WXK_UP:
542 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
544 elif keyCode == wx.WXK_DOWN:
545 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
547 elif keyCode == wx.WXK_PAGEUP:
548 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
550 elif keyCode == wx.WXK_PAGEDOWN:
551 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
554 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
555 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
556 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
557 from collections import defaultdict
558 from gc import get_objects
559 self._beforeLeakTest = defaultdict(int)
560 for i in get_objects():
561 self._beforeLeakTest[type(i)] += 1
562 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
563 from collections import defaultdict
564 from gc import get_objects
565 self._afterLeakTest = defaultdict(int)
566 for i in get_objects():
567 self._afterLeakTest[type(i)] += 1
568 for k in self._afterLeakTest:
569 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
570 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
572 def ShaderUpdate(self, v, f):
573 s = opengl.GLShader(v, f)
575 self._objectLoadShader.release()
576 self._objectLoadShader = s
577 for obj in self._scene.objects():
578 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
581 def OnMouseDown(self,e):
582 self._mouseX = e.GetX()
583 self._mouseY = e.GetY()
584 self._mouseClick3DPos = self._mouse3Dpos
585 self._mouseClickFocus = self._focusObj
587 self._mouseState = 'doubleClick'
589 self._mouseState = 'dragOrClick'
590 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
591 p0 -= self.getObjectCenterPos() - self._viewTarget
592 p1 -= self.getObjectCenterPos() - self._viewTarget
593 if self.tool.OnDragStart(p0, p1):
594 self._mouseState = 'tool'
595 if self._mouseState == 'dragOrClick':
596 if e.GetButton() == 1:
597 if self._focusObj is not None:
598 self._selectObject(self._focusObj, False)
601 def OnMouseUp(self, e):
602 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
604 if self._mouseState == 'dragOrClick':
605 if e.GetButton() == 1:
606 self._selectObject(self._focusObj)
607 if e.GetButton() == 3:
609 if self._focusObj is not None:
610 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
611 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
612 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
613 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
614 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
615 if len(self._scene.objects()) > 0:
616 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
617 if menu.MenuItemCount > 0:
620 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
621 self._scene.pushFree()
623 elif self._mouseState == 'tool':
624 if self.tempMatrix is not None and self._selectedObj is not None:
625 self._selectedObj.applyMatrix(self.tempMatrix)
626 self._scene.pushFree()
627 self._selectObject(self._selectedObj)
628 self.tempMatrix = None
629 self.tool.OnDragEnd()
631 self._mouseState = None
633 def OnMouseMotion(self,e):
634 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
635 p0 -= self.getObjectCenterPos() - self._viewTarget
636 p1 -= self.getObjectCenterPos() - self._viewTarget
638 if e.Dragging() and self._mouseState is not None:
639 if self._mouseState == 'tool':
640 self.tool.OnDrag(p0, p1)
641 elif not e.LeftIsDown() and e.RightIsDown():
642 self._mouseState = 'drag'
643 if wx.GetKeyState(wx.WXK_SHIFT):
644 a = math.cos(math.radians(self._yaw)) / 3.0
645 b = math.sin(math.radians(self._yaw)) / 3.0
646 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
647 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
648 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
649 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
651 self._yaw += e.GetX() - self._mouseX
652 self._pitch -= e.GetY() - self._mouseY
653 if self._pitch > 170:
657 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
658 self._mouseState = 'drag'
659 self._zoom += e.GetY() - self._mouseY
662 if self._zoom > numpy.max(self._machineSize) * 3:
663 self._zoom = numpy.max(self._machineSize) * 3
664 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
665 self._mouseState = 'dragObject'
666 z = max(0, self._mouseClick3DPos[2])
667 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
668 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
673 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
674 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
675 diff = cursorZ1 - cursorZ0
676 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
677 if not e.Dragging() or self._mouseState != 'tool':
678 self.tool.OnMouseMove(p0, p1)
680 self._mouseX = e.GetX()
681 self._mouseY = e.GetY()
683 def OnMouseWheel(self, e):
684 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
685 delta = max(min(delta,4),-4)
686 self._zoom *= 1.0 - delta / 10.0
689 if self._zoom > numpy.max(self._machineSize) * 3:
690 self._zoom = numpy.max(self._machineSize) * 3
693 def OnMouseLeave(self, e):
697 def getMouseRay(self, x, y):
698 if self._viewport is None:
699 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
700 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
701 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
702 p0 -= self._viewTarget
703 p1 -= self._viewTarget
706 def _init3DView(self):
707 # set viewing projection
708 size = self.GetSize()
709 glViewport(0, 0, size.GetWidth(), size.GetHeight())
712 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
714 glDisable(GL_RESCALE_NORMAL)
715 glDisable(GL_LIGHTING)
717 glEnable(GL_DEPTH_TEST)
718 glDisable(GL_CULL_FACE)
720 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
722 glClearColor(0.8, 0.8, 0.8, 1.0)
726 glMatrixMode(GL_PROJECTION)
728 aspect = float(size.GetWidth()) / float(size.GetHeight())
729 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
731 glMatrixMode(GL_MODELVIEW)
733 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
736 if machineCom.machineIsConnected():
737 self.printButton._imageID = 6
738 self.printButton._tooltip = 'Print'
739 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
740 self.printButton._imageID = 2
741 self.printButton._tooltip = 'Toolpath to SD'
743 self.printButton._imageID = 3
744 self.printButton._tooltip = 'Save toolpath'
746 if self._animView is not None:
747 self._viewTarget = self._animView.getPosition()
748 if self._animView.isDone():
749 self._animView = None
750 if self._animZoom is not None:
751 self._zoom = self._animZoom.getPosition()
752 if self._animZoom.isDone():
753 self._animZoom = None
754 if self.viewMode == 'gcode' and self._gcode is not None:
756 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
759 if self._objectShader is None:
760 if opengl.hasShaderSupport():
761 self._objectShader = opengl.GLShader("""
762 varying float light_amount;
766 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
767 gl_FrontColor = gl_Color;
769 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
773 varying float light_amount;
777 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
780 self._objectOverhangShader = opengl.GLShader("""
781 uniform float cosAngle;
782 uniform mat3 rotMatrix;
783 varying float light_amount;
787 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
788 gl_FrontColor = gl_Color;
790 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
792 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
794 light_amount = -10.0;
798 varying float light_amount;
802 if (light_amount == -10.0)
804 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
806 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
810 self._objectLoadShader = opengl.GLShader("""
811 uniform float intensity;
813 varying float light_amount;
817 vec4 tmp = gl_Vertex;
818 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
819 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
820 gl_Position = gl_ModelViewProjectionMatrix * tmp;
821 gl_FrontColor = gl_Color;
823 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
827 uniform float intensity;
828 varying float light_amount;
832 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
835 if self._objectShader == None or not self._objectShader.isValid():
836 self._objectShader = opengl.GLFakeShader()
837 self._objectOverhangShader = opengl.GLFakeShader()
838 self._objectLoadShader = None
840 glTranslate(0,0,-self._zoom)
841 glRotate(-self._pitch, 1,0,0)
842 glRotate(self._yaw, 0,0,1)
843 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
845 self._viewport = glGetIntegerv(GL_VIEWPORT)
846 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
847 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
849 glClearColor(1,1,1,1)
850 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
852 if self.viewMode != 'gcode':
853 for n in xrange(0, len(self._scene.objects())):
854 obj = self._scene.objects()[n]
855 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
856 self._renderObject(obj)
858 if self._mouseX > -1:
860 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
861 if n < len(self._scene.objects()):
862 self._focusObj = self._scene.objects()[n]
864 self._focusObj = None
865 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
866 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
867 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
868 self._mouse3Dpos -= self._viewTarget
871 glTranslate(0,0,-self._zoom)
872 glRotate(-self._pitch, 1,0,0)
873 glRotate(self._yaw, 0,0,1)
874 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
876 if self.viewMode == 'gcode':
877 if self._gcode is not None and self._gcode.layerList is None:
878 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
879 self._gcodeLoadThread.daemon = True
880 self._gcodeLoadThread.start()
881 if self._gcode is not None and self._gcode.layerList is not None:
883 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
885 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
886 for n in xrange(0, drawUpTill):
887 c = 1.0 - float(drawUpTill - n) / 15
889 if len(self._gcodeVBOs) < n + 1:
890 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
891 if time.time() - t > 0.5:
894 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
895 if n == drawUpTill - 1:
896 if len(self._gcodeVBOs[n]) < 9:
897 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
899 self._gcodeVBOs[n][8].render(GL_QUADS)
901 self._gcodeVBOs[n][9].render(GL_QUADS)
903 self._gcodeVBOs[n][10].render(GL_QUADS)
905 self._gcodeVBOs[n][11].render(GL_QUADS)
908 self._gcodeVBOs[n][12].render(GL_QUADS)
909 glColor3f(c/2, c/2, 0.0)
910 self._gcodeVBOs[n][13].render(GL_QUADS)
912 self._gcodeVBOs[n][14].render(GL_QUADS)
913 self._gcodeVBOs[n][15].render(GL_QUADS)
915 self._gcodeVBOs[n][16].render(GL_LINES)
918 self._gcodeVBOs[n][0].render(GL_LINES)
920 self._gcodeVBOs[n][1].render(GL_LINES)
922 self._gcodeVBOs[n][2].render(GL_LINES)
924 self._gcodeVBOs[n][3].render(GL_LINES)
927 self._gcodeVBOs[n][4].render(GL_LINES)
928 glColor3f(c/2, c/2, 0.0)
929 self._gcodeVBOs[n][5].render(GL_LINES)
931 self._gcodeVBOs[n][6].render(GL_LINES)
932 self._gcodeVBOs[n][7].render(GL_LINES)
935 glStencilFunc(GL_ALWAYS, 1, 1)
936 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
938 if self.viewMode == 'overhang':
939 self._objectOverhangShader.bind()
940 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
942 self._objectShader.bind()
943 for obj in self._scene.objects():
944 if obj._loadAnim is not None:
945 if obj._loadAnim.isDone():
950 if self._focusObj == obj:
952 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
955 if self._selectedObj == obj or self._selectedObj is None:
956 #If we want transparent, then first render a solid black model to remove the printer size lines.
957 if self.viewMode == 'transparent':
958 glColor4f(0, 0, 0, 0)
959 self._renderObject(obj)
961 glBlendFunc(GL_ONE, GL_ONE)
962 glDisable(GL_DEPTH_TEST)
964 if self.viewMode == 'xray':
965 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
966 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
967 glEnable(GL_STENCIL_TEST)
969 if self.viewMode == 'overhang':
970 if self._selectedObj == obj and self.tempMatrix is not None:
971 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
973 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
975 if not self._scene.checkPlatform(obj):
976 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
977 self._renderObject(obj)
979 self._renderObject(obj, brightness)
980 glDisable(GL_STENCIL_TEST)
982 glEnable(GL_DEPTH_TEST)
983 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
985 if self.viewMode == 'xray':
988 glEnable(GL_STENCIL_TEST)
989 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
990 glDisable(GL_DEPTH_TEST)
991 for i in xrange(2, 15, 2):
992 glStencilFunc(GL_EQUAL, i, 0xFF)
993 glColor(float(i)/10, float(i)/10, float(i)/5)
995 glVertex3f(-1000,-1000,-10)
996 glVertex3f( 1000,-1000,-10)
997 glVertex3f( 1000, 1000,-10)
998 glVertex3f(-1000, 1000,-10)
1000 for i in xrange(1, 15, 2):
1001 glStencilFunc(GL_EQUAL, i, 0xFF)
1002 glColor(float(i)/10, 0, 0)
1004 glVertex3f(-1000,-1000,-10)
1005 glVertex3f( 1000,-1000,-10)
1006 glVertex3f( 1000, 1000,-10)
1007 glVertex3f(-1000, 1000,-10)
1010 glDisable(GL_STENCIL_TEST)
1011 glEnable(GL_DEPTH_TEST)
1013 self._objectShader.unbind()
1015 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1017 if self._objectLoadShader is not None:
1018 self._objectLoadShader.bind()
1019 glColor4f(0.2, 0.6, 1.0, 1.0)
1020 for obj in self._scene.objects():
1021 if obj._loadAnim is None:
1023 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1024 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1025 self._renderObject(obj)
1026 self._objectLoadShader.unbind()
1031 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1033 z = self._usbPrintMonitor.getZ()
1034 size = self._machineSize
1035 glColor4ub(255,255,0,128)
1037 glVertex3f(-size[0]/2,-size[1]/2, z)
1038 glVertex3f( size[0]/2,-size[1]/2, z)
1039 glVertex3f( size[0]/2, size[1]/2, z)
1040 glVertex3f(-size[0]/2, size[1]/2, z)
1043 if self.viewMode == 'gcode':
1044 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1045 glDisable(GL_DEPTH_TEST)
1048 glTranslate(0,-4,-10)
1049 glColor4ub(60,60,60,255)
1050 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1053 #Draw the object box-shadow, so you can see where it will collide with other objects.
1054 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1055 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1057 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1059 glEnable(GL_CULL_FACE)
1060 glColor4f(0,0,0,0.12)
1062 glVertex3f(-size[0], size[1], 0.1)
1063 glVertex3f(-size[0], -size[1], 0.1)
1064 glVertex3f( size[0], -size[1], 0.1)
1065 glVertex3f( size[0], size[1], 0.1)
1067 glDisable(GL_CULL_FACE)
1070 #Draw the outline of the selected object, on top of everything else except the GUI.
1071 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1072 glDisable(GL_DEPTH_TEST)
1073 glEnable(GL_CULL_FACE)
1074 glEnable(GL_STENCIL_TEST)
1076 glStencilFunc(GL_EQUAL, 0, 255)
1078 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1080 glColor4f(1,1,1,0.5)
1081 self._renderObject(self._selectedObj)
1082 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1084 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1085 glDisable(GL_STENCIL_TEST)
1086 glDisable(GL_CULL_FACE)
1087 glEnable(GL_DEPTH_TEST)
1089 if self._selectedObj is not None:
1091 pos = self.getObjectCenterPos()
1092 glTranslate(pos[0], pos[1], pos[2])
1095 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1096 glDisable(GL_DEPTH_TEST)
1099 glTranslate(0,-4,-10)
1100 glColor4ub(60,60,60,255)
1101 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1104 def _renderObject(self, obj, brightness = False, addSink = True):
1107 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1109 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1111 if self.tempMatrix is not None and obj == self._selectedObj:
1112 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1113 glMultMatrixf(tempMatrix)
1115 offset = obj.getDrawOffset()
1116 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1118 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1119 glMultMatrixf(tempMatrix)
1122 for m in obj._meshList:
1124 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1126 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1131 def _drawMachine(self):
1132 glEnable(GL_CULL_FACE)
1135 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1137 if profile.getPreference('machine_type') == 'ultimaker':
1138 glColor4f(1,1,1,0.5)
1139 self._objectShader.bind()
1140 self._renderObject(self._platformMesh, False, False)
1141 self._objectShader.unbind()
1146 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1147 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1148 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1149 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1150 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1151 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1154 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1155 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1156 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1157 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1158 v4 = [ size[0] / 2, size[1] / 2, 0]
1159 v5 = [ size[0] / 2,-size[1] / 2, 0]
1160 v6 = [-size[0] / 2, size[1] / 2, 0]
1161 v7 = [-size[0] / 2,-size[1] / 2, 0]
1163 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1164 glEnableClientState(GL_VERTEX_ARRAY)
1165 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1167 glColor4ub(5, 171, 231, 64)
1168 glDrawArrays(GL_QUADS, 0, 4)
1169 glColor4ub(5, 171, 231, 96)
1170 glDrawArrays(GL_QUADS, 4, 8)
1171 glColor4ub(5, 171, 231, 128)
1172 glDrawArrays(GL_QUADS, 12, 8)
1173 glDisableClientState(GL_VERTEX_ARRAY)
1175 sx = self._machineSize[0]
1176 sy = self._machineSize[1]
1177 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1178 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1183 x1 = max(min(x1, sx/2), -sx/2)
1184 y1 = max(min(y1, sy/2), -sy/2)
1185 x2 = max(min(x2, sx/2), -sx/2)
1186 y2 = max(min(y2, sy/2), -sy/2)
1187 if (x & 1) == (y & 1):
1188 glColor4ub(5, 171, 231, 127)
1190 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1192 glVertex3f(x1, y1, -0.02)
1193 glVertex3f(x2, y1, -0.02)
1194 glVertex3f(x2, y2, -0.02)
1195 glVertex3f(x1, y2, -0.02)
1199 glDisable(GL_CULL_FACE)
1201 def _generateGCodeVBOs(self, layer):
1203 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1204 if ':' in extrudeType:
1205 extruder = int(extrudeType[extrudeType.find(':')+1:])
1206 extrudeType = extrudeType[0:extrudeType.find(':')]
1209 pointList = numpy.zeros((0,3), numpy.float32)
1211 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1213 a = numpy.concatenate((a[:-1], a[1:]), 1)
1214 a = a.reshape((len(a) * 2, 3))
1215 pointList = numpy.concatenate((pointList, a))
1216 ret.append(opengl.GLVBO(pointList))
1219 def _generateGCodeVBOs2(self, layer):
1220 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1221 filamentArea = math.pi * filamentRadius * filamentRadius
1224 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1225 if ':' in extrudeType:
1226 extruder = int(extrudeType[extrudeType.find(':')+1:])
1227 extrudeType = extrudeType[0:extrudeType.find(':')]
1230 pointList = numpy.zeros((0,3), numpy.float32)
1232 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1234 if extrudeType == 'FILL':
1237 normal = a[1:] - a[:-1]
1238 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1239 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1242 ePerDist = path['extrusion'][1:] / lens
1243 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1245 normal[:,0] *= lineWidth
1246 normal[:,1] *= lineWidth
1248 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1249 b = numpy.concatenate((b, a[1:] + normal), 1)
1250 b = numpy.concatenate((b, a[1:] - normal), 1)
1251 b = numpy.concatenate((b, a[:-1] - normal), 1)
1252 b = numpy.concatenate((b, a[:-1] + normal), 1)
1253 b = b.reshape((len(b) * 4, 3))
1256 normal2 = normal[:-1] + normal[1:]
1257 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1258 normal2[:,0] /= lens2
1259 normal2[:,1] /= lens2
1260 normal2[:,0] *= lineWidth[:-1]
1261 normal2[:,1] *= lineWidth[:-1]
1263 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1264 c = numpy.concatenate((c, a[1:-1]), 1)
1265 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1266 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1267 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1269 c = numpy.concatenate((c, a[1:-1]), 1)
1270 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1271 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1272 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1274 c = c.reshape((len(c) * 8, 3))
1276 pointList = numpy.concatenate((pointList, b, c))
1278 pointList = numpy.concatenate((pointList, b))
1279 ret.append(opengl.GLVBO(pointList))
1281 pointList = numpy.zeros((0,3), numpy.float32)
1283 if path['type'] == 'move':
1284 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1285 a = numpy.concatenate((a[:-1], a[1:]), 1)
1286 a = a.reshape((len(a) * 2, 3))
1287 pointList = numpy.concatenate((pointList, a))
1288 if path['type'] == 'retract':
1289 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1290 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1291 a = a.reshape((len(a) * 2, 3))
1292 pointList = numpy.concatenate((pointList, a))
1293 ret.append(opengl.GLVBO(pointList))
1297 def getObjectCenterPos(self):
1298 if self._selectedObj is None:
1299 return [0.0, 0.0, 0.0]
1300 pos = self._selectedObj.getPosition()
1301 size = self._selectedObj.getSize()
1302 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1304 def getObjectBoundaryCircle(self):
1305 if self._selectedObj is None:
1307 return self._selectedObj.getBoundaryCircle()
1309 def getObjectSize(self):
1310 if self._selectedObj is None:
1311 return [0.0, 0.0, 0.0]
1312 return self._selectedObj.getSize()
1314 def getObjectMatrix(self):
1315 if self._selectedObj is None:
1316 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1317 return self._selectedObj.getMatrix()
1319 class shaderEditor(wx.Dialog):
1320 def __init__(self, parent, callback, v, f):
1321 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1322 self._callback = callback
1323 s = wx.BoxSizer(wx.VERTICAL)
1325 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1326 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1327 s.Add(self._vertex, 1, flag=wx.EXPAND)
1328 s.Add(self._fragment, 1, flag=wx.EXPAND)
1330 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1331 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1333 self.SetPosition(self.GetParent().GetPosition())
1334 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1337 def OnText(self, e):
1338 self._callback(self._vertex.GetValue(), self._fragment.GetValue())