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()
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)
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 and not 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.viewMode == 'gcode':
1032 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1033 glDisable(GL_DEPTH_TEST)
1036 glTranslate(0,-4,-10)
1037 glColor4ub(60,60,60,255)
1038 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1041 #Draw the object box-shadow, so you can see where it will collide with other objects.
1042 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1043 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1045 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1047 glEnable(GL_CULL_FACE)
1048 glColor4f(0,0,0,0.12)
1050 glVertex3f(-size[0], size[1], 0.1)
1051 glVertex3f(-size[0], -size[1], 0.1)
1052 glVertex3f( size[0], -size[1], 0.1)
1053 glVertex3f( size[0], size[1], 0.1)
1055 glDisable(GL_CULL_FACE)
1058 #Draw the outline of the selected object, on top of everything else except the GUI.
1059 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1060 glDisable(GL_DEPTH_TEST)
1061 glEnable(GL_CULL_FACE)
1062 glEnable(GL_STENCIL_TEST)
1064 glStencilFunc(GL_EQUAL, 0, 255)
1066 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1068 glColor4f(1,1,1,0.5)
1069 self._renderObject(self._selectedObj)
1070 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1072 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1073 glDisable(GL_STENCIL_TEST)
1074 glDisable(GL_CULL_FACE)
1075 glEnable(GL_DEPTH_TEST)
1077 if self._selectedObj is not None:
1079 pos = self.getObjectCenterPos()
1080 glTranslate(pos[0], pos[1], pos[2])
1083 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1084 glDisable(GL_DEPTH_TEST)
1087 glTranslate(0,-4,-10)
1088 glColor4ub(60,60,60,255)
1089 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1092 def _renderObject(self, obj, brightness = False, addSink = True):
1095 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1097 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1099 if self.tempMatrix is not None and obj == self._selectedObj:
1100 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1101 glMultMatrixf(tempMatrix)
1103 offset = obj.getDrawOffset()
1104 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1106 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1107 glMultMatrixf(tempMatrix)
1110 for m in obj._meshList:
1112 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1114 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1119 def _drawMachine(self):
1120 glEnable(GL_CULL_FACE)
1123 if profile.getPreference('machine_type') == 'ultimaker':
1124 glColor4f(1,1,1,0.5)
1125 self._objectShader.bind()
1126 self._renderObject(self._platformMesh, False, False)
1127 self._objectShader.unbind()
1129 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1130 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1131 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1132 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1133 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1134 v4 = [ size[0] / 2, size[1] / 2, 0]
1135 v5 = [ size[0] / 2,-size[1] / 2, 0]
1136 v6 = [-size[0] / 2, size[1] / 2, 0]
1137 v7 = [-size[0] / 2,-size[1] / 2, 0]
1139 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1140 glEnableClientState(GL_VERTEX_ARRAY)
1141 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1143 glColor4ub(5, 171, 231, 64)
1144 glDrawArrays(GL_QUADS, 0, 4)
1145 glColor4ub(5, 171, 231, 96)
1146 glDrawArrays(GL_QUADS, 4, 8)
1147 glColor4ub(5, 171, 231, 128)
1148 glDrawArrays(GL_QUADS, 12, 8)
1149 glDisableClientState(GL_VERTEX_ARRAY)
1151 sx = self._machineSize[0]
1152 sy = self._machineSize[1]
1153 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1154 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1159 x1 = max(min(x1, sx/2), -sx/2)
1160 y1 = max(min(y1, sy/2), -sy/2)
1161 x2 = max(min(x2, sx/2), -sx/2)
1162 y2 = max(min(y2, sy/2), -sy/2)
1163 if (x & 1) == (y & 1):
1164 glColor4ub(5, 171, 231, 127)
1166 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1168 glVertex3f(x1, y1, -0.02)
1169 glVertex3f(x2, y1, -0.02)
1170 glVertex3f(x2, y2, -0.02)
1171 glVertex3f(x1, y2, -0.02)
1175 glDisable(GL_CULL_FACE)
1177 def _generateGCodeVBOs(self, layer):
1179 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1180 if ':' in extrudeType:
1181 extruder = int(extrudeType[extrudeType.find(':')+1:])
1182 extrudeType = extrudeType[0:extrudeType.find(':')]
1185 pointList = numpy.zeros((0,3), numpy.float32)
1187 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1189 a = numpy.concatenate((a[:-1], a[1:]), 1)
1190 a = a.reshape((len(a) * 2, 3))
1191 pointList = numpy.concatenate((pointList, a))
1192 ret.append(opengl.GLVBO(pointList))
1195 def _generateGCodeVBOs2(self, layer):
1196 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1197 filamentArea = math.pi * filamentRadius * filamentRadius
1200 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1201 if ':' in extrudeType:
1202 extruder = int(extrudeType[extrudeType.find(':')+1:])
1203 extrudeType = extrudeType[0:extrudeType.find(':')]
1206 pointList = numpy.zeros((0,3), numpy.float32)
1208 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1210 if extrudeType == 'FILL':
1213 normal = a[1:] - a[:-1]
1214 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1215 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1218 ePerDist = path['extrusion'][1:] / lens
1219 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1221 normal[:,0] *= lineWidth
1222 normal[:,1] *= lineWidth
1224 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1225 b = numpy.concatenate((b, a[1:] + normal), 1)
1226 b = numpy.concatenate((b, a[1:] - normal), 1)
1227 b = numpy.concatenate((b, a[:-1] - normal), 1)
1228 b = numpy.concatenate((b, a[:-1] + normal), 1)
1229 b = b.reshape((len(b) * 4, 3))
1232 normal2 = normal[:-1] + normal[1:]
1233 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1234 normal2[:,0] /= lens2
1235 normal2[:,1] /= lens2
1236 normal2[:,0] *= lineWidth[:-1]
1237 normal2[:,1] *= lineWidth[:-1]
1239 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1240 c = numpy.concatenate((c, a[1:-1]), 1)
1241 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1242 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1243 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1245 c = numpy.concatenate((c, a[1:-1]), 1)
1246 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1247 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1248 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1250 c = c.reshape((len(c) * 8, 3))
1252 pointList = numpy.concatenate((pointList, b, c))
1254 pointList = numpy.concatenate((pointList, b))
1255 ret.append(opengl.GLVBO(pointList))
1257 pointList = numpy.zeros((0,3), numpy.float32)
1259 if path['type'] == 'move':
1260 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1261 a = numpy.concatenate((a[:-1], a[1:]), 1)
1262 a = a.reshape((len(a) * 2, 3))
1263 pointList = numpy.concatenate((pointList, a))
1264 if path['type'] == 'retract':
1265 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1266 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1267 a = a.reshape((len(a) * 2, 3))
1268 pointList = numpy.concatenate((pointList, a))
1269 ret.append(opengl.GLVBO(pointList))
1273 def getObjectCenterPos(self):
1274 if self._selectedObj is None:
1275 return [0.0, 0.0, 0.0]
1276 pos = self._selectedObj.getPosition()
1277 size = self._selectedObj.getSize()
1278 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1280 def getObjectBoundaryCircle(self):
1281 if self._selectedObj is None:
1283 return self._selectedObj.getBoundaryCircle()
1285 def getObjectSize(self):
1286 if self._selectedObj is None:
1287 return [0.0, 0.0, 0.0]
1288 return self._selectedObj.getSize()
1290 def getObjectMatrix(self):
1291 if self._selectedObj is None:
1292 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1293 return self._selectedObj.getMatrix()
1295 class shaderEditor(wx.Dialog):
1296 def __init__(self, parent, callback, v, f):
1297 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1298 self._callback = callback
1299 s = wx.BoxSizer(wx.VERTICAL)
1301 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1302 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1303 s.Add(self._vertex, 1, flag=wx.EXPAND)
1304 s.Add(self._fragment, 1, flag=wx.EXPAND)
1306 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1307 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1309 self.SetPosition(self.GetParent().GetPosition())
1310 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1313 def OnText(self, e):
1314 self._callback(self._vertex.GetValue(), self._fragment.GetValue())