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):
696 def getMouseRay(self, x, y):
697 if self._viewport is None:
698 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
699 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
700 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
701 p0 -= self._viewTarget
702 p1 -= self._viewTarget
705 def _init3DView(self):
706 # set viewing projection
707 size = self.GetSize()
708 glViewport(0, 0, size.GetWidth(), size.GetHeight())
711 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
713 glDisable(GL_RESCALE_NORMAL)
714 glDisable(GL_LIGHTING)
716 glEnable(GL_DEPTH_TEST)
717 glDisable(GL_CULL_FACE)
719 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
721 glClearColor(0.8, 0.8, 0.8, 1.0)
725 glMatrixMode(GL_PROJECTION)
727 aspect = float(size.GetWidth()) / float(size.GetHeight())
728 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
730 glMatrixMode(GL_MODELVIEW)
732 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
735 if machineCom.machineIsConnected():
736 self.printButton._imageID = 6
737 self.printButton._tooltip = 'Print'
738 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
739 self.printButton._imageID = 2
740 self.printButton._tooltip = 'Toolpath to SD'
742 self.printButton._imageID = 3
743 self.printButton._tooltip = 'Save toolpath'
745 if self._animView is not None:
746 self._viewTarget = self._animView.getPosition()
747 if self._animView.isDone():
748 self._animView = None
749 if self._animZoom is not None:
750 self._zoom = self._animZoom.getPosition()
751 if self._animZoom.isDone():
752 self._animZoom = None
753 if self.viewMode == 'gcode' and self._gcode is not None:
755 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
758 if self._objectShader is None:
759 if opengl.hasShaderSupport():
760 self._objectShader = opengl.GLShader("""
761 varying float light_amount;
765 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
766 gl_FrontColor = gl_Color;
768 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
772 varying float light_amount;
776 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
779 self._objectOverhangShader = opengl.GLShader("""
780 uniform float cosAngle;
781 uniform mat3 rotMatrix;
782 varying float light_amount;
786 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
787 gl_FrontColor = gl_Color;
789 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
791 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
793 light_amount = -10.0;
797 varying float light_amount;
801 if (light_amount == -10.0)
803 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
805 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
809 self._objectLoadShader = opengl.GLShader("""
810 uniform float intensity;
812 varying float light_amount;
816 vec4 tmp = gl_Vertex;
817 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
818 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
819 gl_Position = gl_ModelViewProjectionMatrix * tmp;
820 gl_FrontColor = gl_Color;
822 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
826 uniform float intensity;
827 varying float light_amount;
831 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
834 if self._objectShader == None or not self._objectShader.isValid():
835 self._objectShader = opengl.GLFakeShader()
836 self._objectOverhangShader = opengl.GLFakeShader()
837 self._objectLoadShader = None
839 glTranslate(0,0,-self._zoom)
840 glRotate(-self._pitch, 1,0,0)
841 glRotate(self._yaw, 0,0,1)
842 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
844 self._viewport = glGetIntegerv(GL_VIEWPORT)
845 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
846 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
848 glClearColor(1,1,1,1)
849 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
851 if self.viewMode != 'gcode':
852 for n in xrange(0, len(self._scene.objects())):
853 obj = self._scene.objects()[n]
854 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
855 self._renderObject(obj)
857 if self._mouseX > -1:
859 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
860 if n < len(self._scene.objects()):
861 self._focusObj = self._scene.objects()[n]
863 self._focusObj = None
864 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
865 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
866 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
867 self._mouse3Dpos -= self._viewTarget
870 glTranslate(0,0,-self._zoom)
871 glRotate(-self._pitch, 1,0,0)
872 glRotate(self._yaw, 0,0,1)
873 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
875 if self.viewMode == 'gcode':
876 if self._gcode is not None and self._gcode.layerList is None:
877 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
878 self._gcodeLoadThread.daemon = True
879 self._gcodeLoadThread.start()
880 if self._gcode is not None and self._gcode.layerList is not None:
882 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
884 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
885 for n in xrange(0, drawUpTill):
886 c = 1.0 - float(drawUpTill - n) / 15
888 if len(self._gcodeVBOs) < n + 1:
889 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
890 if time.time() - t > 0.5:
893 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
894 if n == drawUpTill - 1:
895 if len(self._gcodeVBOs[n]) < 9:
896 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
898 self._gcodeVBOs[n][8].render(GL_QUADS)
900 self._gcodeVBOs[n][9].render(GL_QUADS)
902 self._gcodeVBOs[n][10].render(GL_QUADS)
904 self._gcodeVBOs[n][11].render(GL_QUADS)
907 self._gcodeVBOs[n][12].render(GL_QUADS)
908 glColor3f(c/2, c/2, 0.0)
909 self._gcodeVBOs[n][13].render(GL_QUADS)
911 self._gcodeVBOs[n][14].render(GL_QUADS)
912 self._gcodeVBOs[n][15].render(GL_QUADS)
914 self._gcodeVBOs[n][16].render(GL_LINES)
917 self._gcodeVBOs[n][0].render(GL_LINES)
919 self._gcodeVBOs[n][1].render(GL_LINES)
921 self._gcodeVBOs[n][2].render(GL_LINES)
923 self._gcodeVBOs[n][3].render(GL_LINES)
926 self._gcodeVBOs[n][4].render(GL_LINES)
927 glColor3f(c/2, c/2, 0.0)
928 self._gcodeVBOs[n][5].render(GL_LINES)
930 self._gcodeVBOs[n][6].render(GL_LINES)
931 self._gcodeVBOs[n][7].render(GL_LINES)
934 glStencilFunc(GL_ALWAYS, 1, 1)
935 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
937 if self.viewMode == 'overhang':
938 self._objectOverhangShader.bind()
939 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
941 self._objectShader.bind()
942 for obj in self._scene.objects():
943 if obj._loadAnim is not None:
944 if obj._loadAnim.isDone():
949 if self._focusObj == obj:
951 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
954 if self._selectedObj == obj or self._selectedObj is None:
955 #If we want transparent, then first render a solid black model to remove the printer size lines.
956 if self.viewMode == 'transparent':
957 glColor4f(0, 0, 0, 0)
958 self._renderObject(obj)
960 glBlendFunc(GL_ONE, GL_ONE)
961 glDisable(GL_DEPTH_TEST)
963 if self.viewMode == 'xray':
964 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
965 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
966 glEnable(GL_STENCIL_TEST)
968 if self.viewMode == 'overhang':
969 if self._selectedObj == obj and self.tempMatrix is not None:
970 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
972 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
974 if not self._scene.checkPlatform(obj):
975 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
976 self._renderObject(obj)
978 self._renderObject(obj, brightness)
979 glDisable(GL_STENCIL_TEST)
981 glEnable(GL_DEPTH_TEST)
982 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
984 if self.viewMode == 'xray':
987 glEnable(GL_STENCIL_TEST)
988 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
989 glDisable(GL_DEPTH_TEST)
990 for i in xrange(2, 15, 2):
991 glStencilFunc(GL_EQUAL, i, 0xFF)
992 glColor(float(i)/10, float(i)/10, float(i)/5)
994 glVertex3f(-1000,-1000,-10)
995 glVertex3f( 1000,-1000,-10)
996 glVertex3f( 1000, 1000,-10)
997 glVertex3f(-1000, 1000,-10)
999 for i in xrange(1, 15, 2):
1000 glStencilFunc(GL_EQUAL, i, 0xFF)
1001 glColor(float(i)/10, 0, 0)
1003 glVertex3f(-1000,-1000,-10)
1004 glVertex3f( 1000,-1000,-10)
1005 glVertex3f( 1000, 1000,-10)
1006 glVertex3f(-1000, 1000,-10)
1009 glDisable(GL_STENCIL_TEST)
1010 glEnable(GL_DEPTH_TEST)
1012 self._objectShader.unbind()
1014 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1016 if self._objectLoadShader is not None:
1017 self._objectLoadShader.bind()
1018 glColor4f(0.2, 0.6, 1.0, 1.0)
1019 for obj in self._scene.objects():
1020 if obj._loadAnim is None:
1022 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1023 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1024 self._renderObject(obj)
1025 self._objectLoadShader.unbind()
1030 if self.viewMode == 'gcode':
1031 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1032 glDisable(GL_DEPTH_TEST)
1035 glTranslate(0,-4,-10)
1036 glColor4ub(60,60,60,255)
1037 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1040 #Draw the object box-shadow, so you can see where it will collide with other objects.
1041 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1042 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1044 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1046 glEnable(GL_CULL_FACE)
1047 glColor4f(0,0,0,0.12)
1049 glVertex3f(-size[0], size[1], 0.1)
1050 glVertex3f(-size[0], -size[1], 0.1)
1051 glVertex3f( size[0], -size[1], 0.1)
1052 glVertex3f( size[0], size[1], 0.1)
1054 glDisable(GL_CULL_FACE)
1057 #Draw the outline of the selected object, on top of everything else except the GUI.
1058 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1059 glDisable(GL_DEPTH_TEST)
1060 glEnable(GL_CULL_FACE)
1061 glEnable(GL_STENCIL_TEST)
1063 glStencilFunc(GL_EQUAL, 0, 255)
1065 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1067 glColor4f(1,1,1,0.5)
1068 self._renderObject(self._selectedObj)
1069 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1071 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1072 glDisable(GL_STENCIL_TEST)
1073 glDisable(GL_CULL_FACE)
1074 glEnable(GL_DEPTH_TEST)
1076 if self._selectedObj is not None:
1078 pos = self.getObjectCenterPos()
1079 glTranslate(pos[0], pos[1], pos[2])
1082 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1083 glDisable(GL_DEPTH_TEST)
1086 glTranslate(0,-4,-10)
1087 glColor4ub(60,60,60,255)
1088 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1091 def _renderObject(self, obj, brightness = False, addSink = True):
1094 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1096 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1098 if self.tempMatrix is not None and obj == self._selectedObj:
1099 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1100 glMultMatrixf(tempMatrix)
1102 offset = obj.getDrawOffset()
1103 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1105 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1106 glMultMatrixf(tempMatrix)
1109 for m in obj._meshList:
1111 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1113 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1118 def _drawMachine(self):
1119 glEnable(GL_CULL_FACE)
1122 if profile.getPreference('machine_type') == 'ultimaker':
1123 glColor4f(1,1,1,0.5)
1124 self._objectShader.bind()
1125 self._renderObject(self._platformMesh, False, False)
1126 self._objectShader.unbind()
1128 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1129 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1130 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1131 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1132 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1133 v4 = [ size[0] / 2, size[1] / 2, 0]
1134 v5 = [ size[0] / 2,-size[1] / 2, 0]
1135 v6 = [-size[0] / 2, size[1] / 2, 0]
1136 v7 = [-size[0] / 2,-size[1] / 2, 0]
1138 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1139 glEnableClientState(GL_VERTEX_ARRAY)
1140 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1142 glColor4ub(5, 171, 231, 64)
1143 glDrawArrays(GL_QUADS, 0, 4)
1144 glColor4ub(5, 171, 231, 96)
1145 glDrawArrays(GL_QUADS, 4, 8)
1146 glColor4ub(5, 171, 231, 128)
1147 glDrawArrays(GL_QUADS, 12, 8)
1148 glDisableClientState(GL_VERTEX_ARRAY)
1150 sx = self._machineSize[0]
1151 sy = self._machineSize[1]
1152 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1153 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1158 x1 = max(min(x1, sx/2), -sx/2)
1159 y1 = max(min(y1, sy/2), -sy/2)
1160 x2 = max(min(x2, sx/2), -sx/2)
1161 y2 = max(min(y2, sy/2), -sy/2)
1162 if (x & 1) == (y & 1):
1163 glColor4ub(5, 171, 231, 127)
1165 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1167 glVertex3f(x1, y1, -0.02)
1168 glVertex3f(x2, y1, -0.02)
1169 glVertex3f(x2, y2, -0.02)
1170 glVertex3f(x1, y2, -0.02)
1174 glDisable(GL_CULL_FACE)
1176 def _generateGCodeVBOs(self, layer):
1178 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1179 if ':' in extrudeType:
1180 extruder = int(extrudeType[extrudeType.find(':')+1:])
1181 extrudeType = extrudeType[0:extrudeType.find(':')]
1184 pointList = numpy.zeros((0,3), numpy.float32)
1186 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1188 a = numpy.concatenate((a[:-1], a[1:]), 1)
1189 a = a.reshape((len(a) * 2, 3))
1190 pointList = numpy.concatenate((pointList, a))
1191 ret.append(opengl.GLVBO(pointList))
1194 def _generateGCodeVBOs2(self, layer):
1195 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1196 filamentArea = math.pi * filamentRadius * filamentRadius
1199 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1200 if ':' in extrudeType:
1201 extruder = int(extrudeType[extrudeType.find(':')+1:])
1202 extrudeType = extrudeType[0:extrudeType.find(':')]
1205 pointList = numpy.zeros((0,3), numpy.float32)
1207 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1209 if extrudeType == 'FILL':
1212 normal = a[1:] - a[:-1]
1213 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1214 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1217 ePerDist = path['extrusion'][1:] / lens
1218 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1220 normal[:,0] *= lineWidth
1221 normal[:,1] *= lineWidth
1223 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1224 b = numpy.concatenate((b, a[1:] + normal), 1)
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 = b.reshape((len(b) * 4, 3))
1231 normal2 = normal[:-1] + normal[1:]
1232 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1233 normal2[:,0] /= lens2
1234 normal2[:,1] /= lens2
1235 normal2[:,0] *= lineWidth[:-1]
1236 normal2[:,1] *= lineWidth[:-1]
1238 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1239 c = numpy.concatenate((c, a[1:-1]), 1)
1240 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1241 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1242 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1244 c = numpy.concatenate((c, a[1:-1]), 1)
1245 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1246 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1247 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1249 c = c.reshape((len(c) * 8, 3))
1251 pointList = numpy.concatenate((pointList, b, c))
1253 pointList = numpy.concatenate((pointList, b))
1254 ret.append(opengl.GLVBO(pointList))
1256 pointList = numpy.zeros((0,3), numpy.float32)
1258 if path['type'] == 'move':
1259 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1260 a = numpy.concatenate((a[:-1], a[1:]), 1)
1261 a = a.reshape((len(a) * 2, 3))
1262 pointList = numpy.concatenate((pointList, a))
1263 if path['type'] == 'retract':
1264 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1265 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1266 a = a.reshape((len(a) * 2, 3))
1267 pointList = numpy.concatenate((pointList, a))
1268 ret.append(opengl.GLVBO(pointList))
1272 def getObjectCenterPos(self):
1273 if self._selectedObj is None:
1274 return [0.0, 0.0, 0.0]
1275 pos = self._selectedObj.getPosition()
1276 size = self._selectedObj.getSize()
1277 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1279 def getObjectBoundaryCircle(self):
1280 if self._selectedObj is None:
1282 return self._selectedObj.getBoundaryCircle()
1284 def getObjectSize(self):
1285 if self._selectedObj is None:
1286 return [0.0, 0.0, 0.0]
1287 return self._selectedObj.getSize()
1289 def getObjectMatrix(self):
1290 if self._selectedObj is None:
1291 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1292 return self._selectedObj.getMatrix()
1294 class shaderEditor(wx.Dialog):
1295 def __init__(self, parent, callback, v, f):
1296 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1297 self._callback = callback
1298 s = wx.BoxSizer(wx.VERTICAL)
1300 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1301 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1302 s.Add(self._vertex, 1, flag=wx.EXPAND)
1303 s.Add(self._fragment, 1, flag=wx.EXPAND)
1305 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1306 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1308 self.SetPosition(self.GetParent().GetPosition())
1309 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1312 def OnText(self, e):
1313 self._callback(self._vertex.GetValue(), self._fragment.GetValue())