1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
13 OpenGL.ERROR_CHECKING = False
14 from OpenGL.GLU import *
15 from OpenGL.GL import *
17 from Cura.gui import printWindow
18 from Cura.util import profile
19 from Cura.util import meshLoader
20 from Cura.util import objectScene
21 from Cura.util import resources
22 from Cura.util import sliceEngine
23 from Cura.util import machineCom
24 from Cura.util import removableStorage
25 from Cura.util import gcodeInterpreter
26 from Cura.gui.util import previewTools
27 from Cura.gui.util import opengl
28 from Cura.gui.util import openglGui
30 class SceneView(openglGui.glGuiPanel):
31 def __init__(self, parent):
32 super(SceneView, self).__init__(parent)
37 self._scene = objectScene.Scene()
40 self._gcodeLoadThread = None
41 self._objectShader = None
42 self._objectLoadShader = None
44 self._selectedObj = None
45 self._objColors = [None,None,None,None]
48 self._mouseState = None
49 self._viewTarget = numpy.array([0,0,0], numpy.float32)
52 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
53 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
54 self._isSimpleMode = True
57 self._modelMatrix = None
58 self._projMatrix = None
59 self.tempMatrix = None
61 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
62 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.showPrintWindow)
63 self.printButton.setDisabled(True)
66 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
67 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
68 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
70 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
71 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
73 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
74 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
76 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
77 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
78 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
80 self.rotateToolButton.setExpandArrow(True)
81 self.scaleToolButton.setExpandArrow(True)
82 self.mirrorToolButton.setExpandArrow(True)
84 self.scaleForm = openglGui.glFrame(self, (2, -2))
85 openglGui.glGuiLayoutGrid(self.scaleForm)
86 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
87 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
88 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
89 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
90 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
91 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
92 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
93 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
94 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
95 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
96 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
97 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
98 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
99 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
101 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
102 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
104 self.notification = openglGui.glNotification(self, (0, 0))
106 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
107 self._sceneUpdateTimer = wx.Timer(self)
108 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
109 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
110 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
114 self.updateToolButtons()
115 self.updateProfileToControls()
117 def showLoadModel(self, button = 1):
119 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)
120 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
121 if dlg.ShowModal() != wx.ID_OK:
124 filenames = dlg.GetPaths()
126 if len(filenames) < 1:
128 profile.putPreference('lastFile', filenames[0])
130 for filename in filenames:
131 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
132 ext = filename[filename.rfind('.')+1:].upper()
133 if ext == 'G' or ext == 'GCODE':
134 gcodeFilename = filename
135 if gcodeFilename is not None:
136 if self._gcode is not None:
138 for layerVBOlist in self._gcodeVBOs:
139 for vbo in layerVBOlist:
140 self.glReleaseList.append(vbo)
142 self._gcode = gcodeInterpreter.gcode()
143 self._gcodeFilename = gcodeFilename
144 self.printButton.setBottomText('')
145 self.viewSelection.setValue(4)
146 self.printButton.setDisabled(False)
149 if self.viewSelection.getValue() == 4:
150 self.viewSelection.setValue(0)
152 self.loadScene(filenames)
154 def showSaveModel(self):
155 if len(self._scene.objects()) < 1:
157 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
158 dlg.SetWildcard(meshLoader.saveWildcardFilter())
159 if dlg.ShowModal() != wx.ID_OK:
162 filename = dlg.GetPath()
164 meshLoader.saveMeshes(filename, self._scene.objects())
166 def showPrintWindow(self, button):
168 if machineCom.machineIsConnected():
169 printWindow.printFile(self._gcodeFilename)
170 if self._gcodeFilename == self._slicer.getGCodeFilename():
171 self._slicer.submitSliceInfoOnline()
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: printWindow.printFile(self._gcodeFilename), 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 showSaveGCode(self):
196 defPath = profile.getPreference('lastFile')
197 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
198 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
199 dlg.SetFilename(self._scene._objectList[0].getName())
200 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
201 if dlg.ShowModal() != wx.ID_OK:
204 filename = dlg.GetPath()
207 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
209 def _copyFile(self, fileA, fileB, allowEject = False):
211 size = float(os.stat(fileA).st_size)
212 with open(fileA, 'rb') as fsrc:
213 with open(fileB, 'wb') as fdst:
215 buf = fsrc.read(16*1024)
219 self.printButton.setProgressBar(float(fsrc.tell()) / size)
224 self.notification.message("Failed to save")
227 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...'))
229 self.notification.message("Saved as %s" % (fileB))
230 self.printButton.setProgressBar(None)
231 if fileA == self._slicer.getGCodeFilename():
232 self._slicer.submitSliceInfoOnline()
234 def _showSliceLog(self):
235 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
239 def OnToolSelect(self, button):
240 if self.rotateToolButton.getSelected():
241 self.tool = previewTools.toolRotate(self)
242 elif self.scaleToolButton.getSelected():
243 self.tool = previewTools.toolScale(self)
244 elif self.mirrorToolButton.getSelected():
245 self.tool = previewTools.toolNone(self)
247 self.tool = previewTools.toolNone(self)
248 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
249 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
250 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
251 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
252 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
253 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
254 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
255 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
257 def updateToolButtons(self):
258 if self._selectedObj is None:
262 self.rotateToolButton.setHidden(hidden)
263 self.scaleToolButton.setHidden(hidden)
264 self.mirrorToolButton.setHidden(hidden)
266 self.rotateToolButton.setSelected(False)
267 self.scaleToolButton.setSelected(False)
268 self.mirrorToolButton.setSelected(False)
271 def OnViewChange(self):
272 if self.viewSelection.getValue() == 4:
273 self.viewMode = 'gcode'
274 if self._gcode is not None and self._gcode.layerList is not None:
275 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
276 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
277 self._selectObject(None)
278 elif self.viewSelection.getValue() == 1:
279 self.viewMode = 'overhang'
280 elif self.viewSelection.getValue() == 2:
281 self.viewMode = 'transparent'
282 elif self.viewSelection.getValue() == 3:
283 self.viewMode = 'xray'
285 self.viewMode = 'normal'
286 self.layerSelect.setHidden(self.viewMode != 'gcode')
289 def OnRotateReset(self, button):
290 if self._selectedObj is None:
292 self._selectedObj.resetRotation()
293 self._scene.pushFree()
294 self._selectObject(self._selectedObj)
297 def OnLayFlat(self, button):
298 if self._selectedObj is None:
300 self._selectedObj.layFlat()
301 self._scene.pushFree()
302 self._selectObject(self._selectedObj)
305 def OnScaleReset(self, button):
306 if self._selectedObj is None:
308 self._selectedObj.resetScale()
309 self._selectObject(self._selectedObj)
310 self.updateProfileToControls()
313 def OnScaleMax(self, button):
314 if self._selectedObj is None:
316 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
317 self._scene.pushFree()
318 self._selectObject(self._selectedObj)
319 self.updateProfileToControls()
322 def OnMirror(self, axis):
323 if self._selectedObj is None:
325 self._selectedObj.mirror(axis)
328 def OnScaleEntry(self, value, axis):
329 if self._selectedObj is None:
335 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
336 self.updateProfileToControls()
337 self._scene.pushFree()
338 self._selectObject(self._selectedObj)
341 def OnScaleEntryMM(self, value, axis):
342 if self._selectedObj is None:
348 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
349 self.updateProfileToControls()
350 self._scene.pushFree()
351 self._selectObject(self._selectedObj)
354 def OnDeleteAll(self, e):
355 while len(self._scene.objects()) > 0:
356 self._deleteObject(self._scene.objects()[0])
357 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
359 def OnMultiply(self, e):
360 if self._focusObj is None:
363 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
364 if dlg.ShowModal() != wx.ID_OK:
373 self._scene.add(newObj)
374 self._scene.centerAll()
375 if not self._scene.checkPlatform(newObj):
380 self.notification.message("Could not create more then %d items" % (n - 1))
381 self._scene.remove(newObj)
382 self._scene.centerAll()
385 def OnSplitObject(self, e):
386 if self._focusObj is None:
388 self._scene.remove(self._focusObj)
389 for obj in self._focusObj.split(self._splitCallback):
390 if numpy.max(obj.getSize()) > 2.0:
392 self._scene.centerAll()
393 self._selectObject(None)
396 def _splitCallback(self, progress):
399 def OnMergeObjects(self, e):
400 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
402 self._scene.merge(self._selectedObj, self._focusObj)
405 def sceneUpdated(self):
406 self._sceneUpdateTimer.Start(500, True)
407 self._slicer.abortSlicer()
408 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
411 def _onRunSlicer(self, e):
412 if self._isSimpleMode:
413 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
414 self._slicer.runSlicer(self._scene)
415 if self._isSimpleMode:
416 profile.resetTempOverride()
418 def _updateSliceProgress(self, progressValue, ready):
420 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
422 self.printButton.setDisabled(not ready)
423 if progressValue >= 0.0:
424 self.printButton.setProgressBar(progressValue)
426 self.printButton.setProgressBar(None)
427 if self._gcode is not None:
429 for layerVBOlist in self._gcodeVBOs:
430 for vbo in layerVBOlist:
431 self.glReleaseList.append(vbo)
434 self.printButton.setProgressBar(None)
435 cost = self._slicer.getFilamentCost()
437 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
439 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
440 self._gcode = gcodeInterpreter.gcode()
441 self._gcodeFilename = self._slicer.getGCodeFilename()
443 self.printButton.setBottomText('')
446 def _loadGCode(self):
447 self._gcode.progressCallback = self._gcodeLoadCallback
448 self._gcode.load(self._gcodeFilename)
450 def _gcodeLoadCallback(self, progress):
451 if self._gcode is None:
453 if len(self._gcode.layerList) % 15 == 0:
455 if self._gcode is None:
457 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
458 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
459 self.layerSelect.setValue(self.layerSelect.getMaxValue())
461 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
462 if self.viewMode == 'gcode':
466 def loadScene(self, fileList):
467 for filename in fileList:
469 objList = meshLoader.loadMeshes(filename)
471 traceback.print_exc()
474 if self._objectLoadShader is not None:
475 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
479 self._scene.centerAll()
480 self._selectObject(obj)
483 def _deleteObject(self, obj):
484 if obj == self._selectedObj:
485 self._selectObject(None)
486 if obj == self._focusObj:
487 self._focusObj = None
488 self._scene.remove(obj)
489 for m in obj._meshList:
490 if m.vbo is not None and m.vbo.decRef():
491 self.glReleaseList.append(m.vbo)
496 def _selectObject(self, obj, zoom = True):
497 if obj != self._selectedObj:
498 self._selectedObj = obj
499 self.updateProfileToControls()
500 self.updateToolButtons()
501 if zoom and obj is not None:
502 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
503 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
504 newZoom = obj.getBoundaryCircle() * 6
505 if newZoom > numpy.max(self._machineSize) * 3:
506 newZoom = numpy.max(self._machineSize) * 3
507 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
509 def updateProfileToControls(self):
510 oldSimpleMode = self._isSimpleMode
511 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
512 if self._isSimpleMode and not oldSimpleMode:
513 self._scene.arrangeAll()
515 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
516 self._objColors[0] = profile.getPreferenceColour('model_colour')
517 self._objColors[1] = profile.getPreferenceColour('model_colour2')
518 self._objColors[2] = profile.getPreferenceColour('model_colour3')
519 self._objColors[3] = profile.getPreferenceColour('model_colour4')
520 self._scene.setMachineSize(self._machineSize)
521 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
522 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'))
524 if self._selectedObj is not None:
525 scale = self._selectedObj.getScale()
526 size = self._selectedObj.getSize()
527 self.scaleXctrl.setValue(round(scale[0], 2))
528 self.scaleYctrl.setValue(round(scale[1], 2))
529 self.scaleZctrl.setValue(round(scale[2], 2))
530 self.scaleXmmctrl.setValue(round(size[0], 2))
531 self.scaleYmmctrl.setValue(round(size[1], 2))
532 self.scaleZmmctrl.setValue(round(size[2], 2))
534 def OnKeyChar(self, keyCode):
535 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
536 if self._selectedObj is not None:
537 self._deleteObject(self._selectedObj)
539 if keyCode == wx.WXK_UP:
540 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
542 elif keyCode == wx.WXK_DOWN:
543 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
545 elif keyCode == wx.WXK_PAGEUP:
546 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
548 elif keyCode == wx.WXK_PAGEDOWN:
549 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
552 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
553 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
554 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
555 from collections import defaultdict
556 from gc import get_objects
557 self._beforeLeakTest = defaultdict(int)
558 for i in get_objects():
559 self._beforeLeakTest[type(i)] += 1
560 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
561 from collections import defaultdict
562 from gc import get_objects
563 self._afterLeakTest = defaultdict(int)
564 for i in get_objects():
565 self._afterLeakTest[type(i)] += 1
566 for k in self._afterLeakTest:
567 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
568 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
570 def ShaderUpdate(self, v, f):
571 s = opengl.GLShader(v, f)
573 self._objectLoadShader.release()
574 self._objectLoadShader = s
575 for obj in self._scene.objects():
576 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
579 def OnMouseDown(self,e):
580 self._mouseX = e.GetX()
581 self._mouseY = e.GetY()
582 self._mouseClick3DPos = self._mouse3Dpos
583 self._mouseClickFocus = self._focusObj
585 self._mouseState = 'doubleClick'
587 self._mouseState = 'dragOrClick'
588 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
589 p0 -= self.getObjectCenterPos() - self._viewTarget
590 p1 -= self.getObjectCenterPos() - self._viewTarget
591 if self.tool.OnDragStart(p0, p1):
592 self._mouseState = 'tool'
593 if self._mouseState == 'dragOrClick':
594 if e.GetButton() == 1:
595 if self._focusObj is not None:
596 self._selectObject(self._focusObj, False)
599 def OnMouseUp(self, e):
600 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
602 if self._mouseState == 'dragOrClick':
603 if e.GetButton() == 1:
604 self._selectObject(self._focusObj)
605 if e.GetButton() == 3:
607 if self._focusObj is not None:
608 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
609 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
610 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
611 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
612 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
613 if len(self._scene.objects()) > 0:
614 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
615 if menu.MenuItemCount > 0:
618 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
619 self._scene.pushFree()
621 elif self._mouseState == 'tool':
622 if self.tempMatrix is not None and self._selectedObj is not None:
623 self._selectedObj.applyMatrix(self.tempMatrix)
624 self._scene.pushFree()
625 self._selectObject(self._selectedObj)
626 self.tempMatrix = None
627 self.tool.OnDragEnd()
629 self._mouseState = None
631 def OnMouseMotion(self,e):
632 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
633 p0 -= self.getObjectCenterPos() - self._viewTarget
634 p1 -= self.getObjectCenterPos() - self._viewTarget
636 if e.Dragging() and self._mouseState is not None:
637 if self._mouseState == 'tool':
638 self.tool.OnDrag(p0, p1)
639 elif not e.LeftIsDown() and e.RightIsDown():
640 self._mouseState = 'drag'
641 if wx.GetKeyState(wx.WXK_SHIFT):
642 a = math.cos(math.radians(self._yaw)) / 3.0
643 b = math.sin(math.radians(self._yaw)) / 3.0
644 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
645 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
646 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
647 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
649 self._yaw += e.GetX() - self._mouseX
650 self._pitch -= e.GetY() - self._mouseY
651 if self._pitch > 170:
655 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
656 self._mouseState = 'drag'
657 self._zoom += e.GetY() - self._mouseY
660 if self._zoom > numpy.max(self._machineSize) * 3:
661 self._zoom = numpy.max(self._machineSize) * 3
662 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
663 self._mouseState = 'dragObject'
664 z = max(0, self._mouseClick3DPos[2])
665 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
666 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
671 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
672 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
673 diff = cursorZ1 - cursorZ0
674 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
675 if not e.Dragging() or self._mouseState != 'tool':
676 self.tool.OnMouseMove(p0, p1)
678 self._mouseX = e.GetX()
679 self._mouseY = e.GetY()
681 def OnMouseWheel(self, e):
682 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
683 delta = max(min(delta,4),-4)
684 self._zoom *= 1.0 - delta / 10.0
687 if self._zoom > numpy.max(self._machineSize) * 3:
688 self._zoom = numpy.max(self._machineSize) * 3
691 def OnMouseLeave(self, e):
694 def getMouseRay(self, x, y):
695 if self._viewport is None:
696 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
697 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
698 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
699 p0 -= self._viewTarget
700 p1 -= self._viewTarget
703 def _init3DView(self):
704 # set viewing projection
705 size = self.GetSize()
706 glViewport(0, 0, size.GetWidth(), size.GetHeight())
709 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
711 glDisable(GL_RESCALE_NORMAL)
712 glDisable(GL_LIGHTING)
714 glEnable(GL_DEPTH_TEST)
715 glDisable(GL_CULL_FACE)
717 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
719 glClearColor(0.8, 0.8, 0.8, 1.0)
723 glMatrixMode(GL_PROJECTION)
725 aspect = float(size.GetWidth()) / float(size.GetHeight())
726 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
728 glMatrixMode(GL_MODELVIEW)
730 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
733 if machineCom.machineIsConnected():
734 self.printButton._imageID = 6
735 self.printButton._tooltip = 'Print'
736 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
737 self.printButton._imageID = 2
738 self.printButton._tooltip = 'Toolpath to SD'
740 self.printButton._imageID = 3
741 self.printButton._tooltip = 'Save toolpath'
743 if self._animView is not None:
744 self._viewTarget = self._animView.getPosition()
745 if self._animView.isDone():
746 self._animView = None
747 if self._animZoom is not None:
748 self._zoom = self._animZoom.getPosition()
749 if self._animZoom.isDone():
750 self._animZoom = None
751 if self.viewMode == 'gcode' and self._gcode is not None:
753 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
756 if self._objectShader is None:
757 if opengl.hasShaderSupport():
758 self._objectShader = opengl.GLShader("""
759 varying float light_amount;
763 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
764 gl_FrontColor = gl_Color;
766 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
770 varying float light_amount;
774 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
777 self._objectOverhangShader = opengl.GLShader("""
778 uniform float cosAngle;
779 uniform mat3 rotMatrix;
780 varying float light_amount;
784 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
785 gl_FrontColor = gl_Color;
787 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
789 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
791 light_amount = -10.0;
795 varying float light_amount;
799 if (light_amount == -10.0)
801 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
803 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
807 self._objectLoadShader = opengl.GLShader("""
808 uniform float intensity;
810 varying float light_amount;
814 vec4 tmp = gl_Vertex;
815 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
816 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
817 gl_Position = gl_ModelViewProjectionMatrix * tmp;
818 gl_FrontColor = gl_Color;
820 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
824 uniform float intensity;
825 varying float light_amount;
829 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
832 if self._objectShader == None or not self._objectShader.isValid():
833 self._objectShader = opengl.GLFakeShader()
834 self._objectOverhangShader = opengl.GLFakeShader()
835 self._objectLoadShader = None
837 glTranslate(0,0,-self._zoom)
838 glRotate(-self._pitch, 1,0,0)
839 glRotate(self._yaw, 0,0,1)
840 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
842 self._viewport = glGetIntegerv(GL_VIEWPORT)
843 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
844 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
846 glClearColor(1,1,1,1)
847 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
849 if self.viewMode != 'gcode':
850 for n in xrange(0, len(self._scene.objects())):
851 obj = self._scene.objects()[n]
852 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
853 self._renderObject(obj)
855 if self._mouseX > -1:
857 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
858 if n < len(self._scene.objects()):
859 self._focusObj = self._scene.objects()[n]
861 self._focusObj = None
862 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
863 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
864 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
865 self._mouse3Dpos -= self._viewTarget
868 glTranslate(0,0,-self._zoom)
869 glRotate(-self._pitch, 1,0,0)
870 glRotate(self._yaw, 0,0,1)
871 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
873 if self.viewMode == 'gcode':
874 if self._gcode is not None and self._gcode.layerList is None:
875 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
876 self._gcodeLoadThread.daemon = True
877 self._gcodeLoadThread.start()
878 if self._gcode is not None and self._gcode.layerList is not None:
880 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
882 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
883 for n in xrange(0, drawUpTill):
884 c = 1.0 - float(drawUpTill - n) / 15
886 if len(self._gcodeVBOs) < n + 1:
887 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
888 if time.time() - t > 0.5:
891 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
892 if n == drawUpTill - 1:
893 if len(self._gcodeVBOs[n]) < 9:
894 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
896 self._gcodeVBOs[n][8].render(GL_QUADS)
898 self._gcodeVBOs[n][9].render(GL_QUADS)
900 self._gcodeVBOs[n][10].render(GL_QUADS)
902 self._gcodeVBOs[n][11].render(GL_QUADS)
905 self._gcodeVBOs[n][12].render(GL_QUADS)
906 glColor3f(c/2, c/2, 0.0)
907 self._gcodeVBOs[n][13].render(GL_QUADS)
909 self._gcodeVBOs[n][14].render(GL_QUADS)
910 self._gcodeVBOs[n][15].render(GL_QUADS)
912 self._gcodeVBOs[n][16].render(GL_LINES)
915 self._gcodeVBOs[n][0].render(GL_LINES)
917 self._gcodeVBOs[n][1].render(GL_LINES)
919 self._gcodeVBOs[n][2].render(GL_LINES)
921 self._gcodeVBOs[n][3].render(GL_LINES)
924 self._gcodeVBOs[n][4].render(GL_LINES)
925 glColor3f(c/2, c/2, 0.0)
926 self._gcodeVBOs[n][5].render(GL_LINES)
928 self._gcodeVBOs[n][6].render(GL_LINES)
929 self._gcodeVBOs[n][7].render(GL_LINES)
932 glStencilFunc(GL_ALWAYS, 1, 1)
933 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
935 if self.viewMode == 'overhang':
936 self._objectOverhangShader.bind()
937 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
939 self._objectShader.bind()
940 for obj in self._scene.objects():
941 if obj._loadAnim is not None:
942 if obj._loadAnim.isDone():
947 if self._focusObj == obj:
949 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
952 if self._selectedObj == obj or self._selectedObj is None:
953 #If we want transparent, then first render a solid black model to remove the printer size lines.
954 if self.viewMode == 'transparent':
955 glColor4f(0, 0, 0, 0)
956 self._renderObject(obj)
958 glBlendFunc(GL_ONE, GL_ONE)
959 glDisable(GL_DEPTH_TEST)
961 if self.viewMode == 'xray':
962 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
963 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
964 glEnable(GL_STENCIL_TEST)
966 if self.viewMode == 'overhang':
967 if self._selectedObj == obj and self.tempMatrix is not None:
968 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
970 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
972 if not self._scene.checkPlatform(obj):
973 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
974 self._renderObject(obj)
976 self._renderObject(obj, brightness)
977 glDisable(GL_STENCIL_TEST)
979 glEnable(GL_DEPTH_TEST)
980 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
982 if self.viewMode == 'xray':
985 glEnable(GL_STENCIL_TEST)
986 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
987 glDisable(GL_DEPTH_TEST)
988 for i in xrange(2, 15, 2):
989 glStencilFunc(GL_EQUAL, i, 0xFF)
990 glColor(float(i)/10, float(i)/10, float(i)/5)
992 glVertex3f(-1000,-1000,-10)
993 glVertex3f( 1000,-1000,-10)
994 glVertex3f( 1000, 1000,-10)
995 glVertex3f(-1000, 1000,-10)
997 for i in xrange(1, 15, 2):
998 glStencilFunc(GL_EQUAL, i, 0xFF)
999 glColor(float(i)/10, 0, 0)
1001 glVertex3f(-1000,-1000,-10)
1002 glVertex3f( 1000,-1000,-10)
1003 glVertex3f( 1000, 1000,-10)
1004 glVertex3f(-1000, 1000,-10)
1007 glDisable(GL_STENCIL_TEST)
1008 glEnable(GL_DEPTH_TEST)
1010 self._objectShader.unbind()
1012 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1014 if self._objectLoadShader is not None:
1015 self._objectLoadShader.bind()
1016 glColor4f(0.2, 0.6, 1.0, 1.0)
1017 for obj in self._scene.objects():
1018 if obj._loadAnim is None:
1020 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1021 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1022 self._renderObject(obj)
1023 self._objectLoadShader.unbind()
1028 if self.viewMode == 'gcode':
1029 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1030 glDisable(GL_DEPTH_TEST)
1033 glTranslate(0,-4,-10)
1034 glColor4ub(60,60,60,255)
1035 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1038 #Draw the object box-shadow, so you can see where it will collide with other objects.
1039 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1040 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1042 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1044 glEnable(GL_CULL_FACE)
1045 glColor4f(0,0,0,0.12)
1047 glVertex3f(-size[0], size[1], 0.1)
1048 glVertex3f(-size[0], -size[1], 0.1)
1049 glVertex3f( size[0], -size[1], 0.1)
1050 glVertex3f( size[0], size[1], 0.1)
1052 glDisable(GL_CULL_FACE)
1055 #Draw the outline of the selected object, on top of everything else except the GUI.
1056 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1057 glDisable(GL_DEPTH_TEST)
1058 glEnable(GL_CULL_FACE)
1059 glEnable(GL_STENCIL_TEST)
1061 glStencilFunc(GL_EQUAL, 0, 255)
1063 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1065 glColor4f(1,1,1,0.5)
1066 self._renderObject(self._selectedObj)
1067 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1069 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1070 glDisable(GL_STENCIL_TEST)
1071 glDisable(GL_CULL_FACE)
1072 glEnable(GL_DEPTH_TEST)
1074 if self._selectedObj is not None:
1076 pos = self.getObjectCenterPos()
1077 glTranslate(pos[0], pos[1], pos[2])
1080 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1081 glDisable(GL_DEPTH_TEST)
1084 glTranslate(0,-4,-10)
1085 glColor4ub(60,60,60,255)
1086 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1089 def _renderObject(self, obj, brightness = False, addSink = True):
1092 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1094 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1096 if self.tempMatrix is not None and obj == self._selectedObj:
1097 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1098 glMultMatrixf(tempMatrix)
1100 offset = obj.getDrawOffset()
1101 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1103 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1104 glMultMatrixf(tempMatrix)
1107 for m in obj._meshList:
1109 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1111 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1116 def _drawMachine(self):
1117 glEnable(GL_CULL_FACE)
1120 if profile.getPreference('machine_type') == 'ultimaker':
1121 glColor4f(1,1,1,0.5)
1122 self._objectShader.bind()
1123 self._renderObject(self._platformMesh, False, False)
1124 self._objectShader.unbind()
1126 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1127 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1128 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1129 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1130 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1131 v4 = [ size[0] / 2, size[1] / 2, 0]
1132 v5 = [ size[0] / 2,-size[1] / 2, 0]
1133 v6 = [-size[0] / 2, size[1] / 2, 0]
1134 v7 = [-size[0] / 2,-size[1] / 2, 0]
1136 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1137 glEnableClientState(GL_VERTEX_ARRAY)
1138 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1140 glColor4ub(5, 171, 231, 64)
1141 glDrawArrays(GL_QUADS, 0, 4)
1142 glColor4ub(5, 171, 231, 96)
1143 glDrawArrays(GL_QUADS, 4, 8)
1144 glColor4ub(5, 171, 231, 128)
1145 glDrawArrays(GL_QUADS, 12, 8)
1146 glDisableClientState(GL_VERTEX_ARRAY)
1148 sx = self._machineSize[0]
1149 sy = self._machineSize[1]
1150 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1151 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1156 x1 = max(min(x1, sx/2), -sx/2)
1157 y1 = max(min(y1, sy/2), -sy/2)
1158 x2 = max(min(x2, sx/2), -sx/2)
1159 y2 = max(min(y2, sy/2), -sy/2)
1160 if (x & 1) == (y & 1):
1161 glColor4ub(5, 171, 231, 127)
1163 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1165 glVertex3f(x1, y1, -0.02)
1166 glVertex3f(x2, y1, -0.02)
1167 glVertex3f(x2, y2, -0.02)
1168 glVertex3f(x1, y2, -0.02)
1172 glDisable(GL_CULL_FACE)
1174 def _generateGCodeVBOs(self, layer):
1176 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1177 if ':' in extrudeType:
1178 extruder = int(extrudeType[extrudeType.find(':')+1:])
1179 extrudeType = extrudeType[0:extrudeType.find(':')]
1182 pointList = numpy.zeros((0,3), numpy.float32)
1184 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1186 a = numpy.concatenate((a[:-1], a[1:]), 1)
1187 a = a.reshape((len(a) * 2, 3))
1188 pointList = numpy.concatenate((pointList, a))
1189 ret.append(opengl.GLVBO(pointList))
1192 def _generateGCodeVBOs2(self, layer):
1193 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1194 filamentArea = math.pi * filamentRadius * filamentRadius
1197 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1198 if ':' in extrudeType:
1199 extruder = int(extrudeType[extrudeType.find(':')+1:])
1200 extrudeType = extrudeType[0:extrudeType.find(':')]
1203 pointList = numpy.zeros((0,3), numpy.float32)
1205 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1207 if extrudeType == 'FILL':
1210 normal = a[1:] - a[:-1]
1211 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1212 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1215 ePerDist = path['extrusion'][1:] / lens
1216 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1218 normal[:,0] *= lineWidth
1219 normal[:,1] *= lineWidth
1221 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1222 b = numpy.concatenate((b, a[1:] + normal), 1)
1223 b = numpy.concatenate((b, a[1:] - normal), 1)
1224 b = numpy.concatenate((b, a[:-1] - normal), 1)
1225 b = numpy.concatenate((b, a[:-1] + normal), 1)
1226 b = b.reshape((len(b) * 4, 3))
1229 normal2 = normal[:-1] + normal[1:]
1230 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1231 normal2[:,0] /= lens2
1232 normal2[:,1] /= lens2
1233 normal2[:,0] *= lineWidth[:-1]
1234 normal2[:,1] *= lineWidth[:-1]
1236 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1237 c = numpy.concatenate((c, a[1:-1]), 1)
1238 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1239 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1240 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1242 c = numpy.concatenate((c, a[1:-1]), 1)
1243 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1244 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1245 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1247 c = c.reshape((len(c) * 8, 3))
1249 pointList = numpy.concatenate((pointList, b, c))
1251 pointList = numpy.concatenate((pointList, b))
1252 ret.append(opengl.GLVBO(pointList))
1254 pointList = numpy.zeros((0,3), numpy.float32)
1256 if path['type'] == 'move':
1257 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1258 a = numpy.concatenate((a[:-1], a[1:]), 1)
1259 a = a.reshape((len(a) * 2, 3))
1260 pointList = numpy.concatenate((pointList, a))
1261 if path['type'] == 'retract':
1262 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1263 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1264 a = a.reshape((len(a) * 2, 3))
1265 pointList = numpy.concatenate((pointList, a))
1266 ret.append(opengl.GLVBO(pointList))
1270 def getObjectCenterPos(self):
1271 if self._selectedObj is None:
1272 return [0.0, 0.0, 0.0]
1273 pos = self._selectedObj.getPosition()
1274 size = self._selectedObj.getSize()
1275 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1277 def getObjectBoundaryCircle(self):
1278 if self._selectedObj is None:
1280 return self._selectedObj.getBoundaryCircle()
1282 def getObjectSize(self):
1283 if self._selectedObj is None:
1284 return [0.0, 0.0, 0.0]
1285 return self._selectedObj.getSize()
1287 def getObjectMatrix(self):
1288 if self._selectedObj is None:
1289 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1290 return self._selectedObj.getMatrix()
1292 class shaderEditor(wx.Dialog):
1293 def __init__(self, parent, callback, v, f):
1294 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1295 self._callback = callback
1296 s = wx.BoxSizer(wx.VERTICAL)
1298 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1299 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1300 s.Add(self._vertex, 1, flag=wx.EXPAND)
1301 s.Add(self._fragment, 1, flag=wx.EXPAND)
1303 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1304 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1306 self.SetPosition(self.GetParent().GetPosition())
1307 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1310 def OnText(self, e):
1311 self._callback(self._vertex.GetValue(), self._fragment.GetValue())