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
29 from Cura.gui.tools import youmagineGui
31 class SceneView(openglGui.glGuiPanel):
32 def __init__(self, parent):
33 super(SceneView, self).__init__(parent)
38 self._scene = objectScene.Scene()
41 self._gcodeFilename = None
42 self._gcodeLoadThread = None
43 self._objectShader = None
44 self._objectLoadShader = None
46 self._selectedObj = None
47 self._objColors = [None,None,None,None]
50 self._mouseState = None
51 self._viewTarget = numpy.array([0,0,0], numpy.float32)
54 self._platformMesh = None
55 self._isSimpleMode = True
56 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
59 self._modelMatrix = None
60 self._projMatrix = None
61 self.tempMatrix = None
63 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
64 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
65 self.printButton.setDisabled(True)
68 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
69 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
70 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
72 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
73 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
75 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
76 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
78 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
79 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
80 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
82 self.rotateToolButton.setExpandArrow(True)
83 self.scaleToolButton.setExpandArrow(True)
84 self.mirrorToolButton.setExpandArrow(True)
86 self.scaleForm = openglGui.glFrame(self, (2, -2))
87 openglGui.glGuiLayoutGrid(self.scaleForm)
88 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
89 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
90 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
91 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
92 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
93 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
94 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
95 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
96 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
97 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
98 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
99 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
100 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
101 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
103 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
104 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
106 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
107 self.youMagineButton.setDisabled(True)
109 self.notification = openglGui.glNotification(self, (0, 0))
111 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
112 self._sceneUpdateTimer = wx.Timer(self)
113 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
114 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
115 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
119 self.updateToolButtons()
120 self.updateProfileToControls()
122 def loadGCodeFile(self, filename):
123 self.OnDeleteAll(None)
124 if self._gcode is not None:
126 for layerVBOlist in self._gcodeVBOs:
127 for vbo in layerVBOlist:
128 self.glReleaseList.append(vbo)
130 self._gcode = gcodeInterpreter.gcode()
131 self._gcodeFilename = filename
132 self.printButton.setBottomText('')
133 self.viewSelection.setValue(4)
134 self.printButton.setDisabled(False)
135 self.youMagineButton.setDisabled(True)
138 def loadSceneFiles(self, filenames):
139 self.youMagineButton.setDisabled(False)
140 if self.viewSelection.getValue() == 4:
141 self.viewSelection.setValue(0)
143 self.loadScene(filenames)
145 def loadFiles(self, filenames):
146 print "load ", filenames
148 for filename in filenames:
149 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
150 ext = filename[filename.rfind('.')+1:].upper()
151 if ext == 'G' or ext == 'GCODE':
152 gcodeFilename = filename
153 if gcodeFilename is not None:
154 self.loadGCodeFile(gcodeFilename)
156 self.loadSceneFiles(filenames)
158 def showLoadModel(self, button = 1):
160 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)
161 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
162 if dlg.ShowModal() != wx.ID_OK:
165 filenames = dlg.GetPaths()
167 if len(filenames) < 1:
169 profile.putPreference('lastFile', filenames[0])
170 self.loadFiles(filenames)
172 def showSaveModel(self):
173 if len(self._scene.objects()) < 1:
175 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
176 dlg.SetWildcard(meshLoader.saveWildcardFilter())
177 if dlg.ShowModal() != wx.ID_OK:
180 filename = dlg.GetPath()
182 meshLoader.saveMeshes(filename, self._scene.objects())
184 def OnPrintButton(self, button):
186 if machineCom.machineIsConnected():
187 self.showPrintWindow()
188 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
189 drives = removableStorage.getPossibleSDcardDrives()
191 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))
192 if dlg.ShowModal() != wx.ID_OK:
195 drive = drives[dlg.GetSelection()]
199 filename = self._scene._objectList[0].getName() + '.gcode'
200 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
205 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
206 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
207 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
211 def showPrintWindow(self):
212 if self._gcodeFilename is None:
214 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
215 if self._gcodeFilename == self._slicer.getGCodeFilename():
216 self._slicer.submitSliceInfoOnline()
218 def showSaveGCode(self):
219 defPath = profile.getPreference('lastFile')
220 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
221 dlg=wx.FileDialog(self, _("Save toolpath"), defPath, style=wx.FD_SAVE)
222 dlg.SetFilename(self._scene._objectList[0].getName())
223 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
224 if dlg.ShowModal() != wx.ID_OK:
227 filename = dlg.GetPath()
230 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
232 def _copyFile(self, fileA, fileB, allowEject = False):
234 size = float(os.stat(fileA).st_size)
235 with open(fileA, 'rb') as fsrc:
236 with open(fileB, 'wb') as fdst:
238 buf = fsrc.read(16*1024)
242 self.printButton.setProgressBar(float(fsrc.tell()) / size)
247 self.notification.message("Failed to save")
250 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...'))
252 self.notification.message("Saved as %s" % (fileB))
253 self.printButton.setProgressBar(None)
254 if fileA == self._slicer.getGCodeFilename():
255 self._slicer.submitSliceInfoOnline()
257 def _showSliceLog(self):
258 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
262 def OnToolSelect(self, button):
263 if self.rotateToolButton.getSelected():
264 self.tool = previewTools.toolRotate(self)
265 elif self.scaleToolButton.getSelected():
266 self.tool = previewTools.toolScale(self)
267 elif self.mirrorToolButton.getSelected():
268 self.tool = previewTools.toolNone(self)
270 self.tool = previewTools.toolNone(self)
271 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
272 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
273 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
274 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
275 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
276 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
277 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
278 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
280 def updateToolButtons(self):
281 if self._selectedObj is None:
285 self.rotateToolButton.setHidden(hidden)
286 self.scaleToolButton.setHidden(hidden)
287 self.mirrorToolButton.setHidden(hidden)
289 self.rotateToolButton.setSelected(False)
290 self.scaleToolButton.setSelected(False)
291 self.mirrorToolButton.setSelected(False)
294 def OnViewChange(self):
295 if self.viewSelection.getValue() == 4:
296 self.viewMode = 'gcode'
297 if self._gcode is not None and self._gcode.layerList is not None:
298 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
299 self._selectObject(None)
300 elif self.viewSelection.getValue() == 1:
301 self.viewMode = 'overhang'
302 elif self.viewSelection.getValue() == 2:
303 self.viewMode = 'transparent'
304 elif self.viewSelection.getValue() == 3:
305 self.viewMode = 'xray'
307 self.viewMode = 'normal'
308 self.layerSelect.setHidden(self.viewMode != 'gcode')
311 def OnRotateReset(self, button):
312 if self._selectedObj is None:
314 self._selectedObj.resetRotation()
315 self._scene.pushFree()
316 self._selectObject(self._selectedObj)
319 def OnLayFlat(self, button):
320 if self._selectedObj is None:
322 self._selectedObj.layFlat()
323 self._scene.pushFree()
324 self._selectObject(self._selectedObj)
327 def OnScaleReset(self, button):
328 if self._selectedObj is None:
330 self._selectedObj.resetScale()
331 self._selectObject(self._selectedObj)
332 self.updateProfileToControls()
335 def OnScaleMax(self, button):
336 if self._selectedObj is None:
338 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
339 self._scene.pushFree()
340 self._selectObject(self._selectedObj)
341 self.updateProfileToControls()
344 def OnMirror(self, axis):
345 if self._selectedObj is None:
347 self._selectedObj.mirror(axis)
350 def OnScaleEntry(self, value, axis):
351 if self._selectedObj is None:
357 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
358 self.updateProfileToControls()
359 self._scene.pushFree()
360 self._selectObject(self._selectedObj)
363 def OnScaleEntryMM(self, value, axis):
364 if self._selectedObj is None:
370 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
371 self.updateProfileToControls()
372 self._scene.pushFree()
373 self._selectObject(self._selectedObj)
376 def OnDeleteAll(self, e):
377 while len(self._scene.objects()) > 0:
378 self._deleteObject(self._scene.objects()[0])
379 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
381 def OnMultiply(self, e):
382 if self._focusObj is None:
385 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
386 if dlg.ShowModal() != wx.ID_OK:
395 self._scene.add(newObj)
396 self._scene.centerAll()
397 if not self._scene.checkPlatform(newObj):
402 self.notification.message("Could not create more then %d items" % (n - 1))
403 self._scene.remove(newObj)
404 self._scene.centerAll()
407 def OnSplitObject(self, e):
408 if self._focusObj is None:
410 self._scene.remove(self._focusObj)
411 for obj in self._focusObj.split(self._splitCallback):
412 if numpy.max(obj.getSize()) > 2.0:
414 self._scene.centerAll()
415 self._selectObject(None)
418 def _splitCallback(self, progress):
421 def OnMergeObjects(self, e):
422 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
424 self._scene.merge(self._selectedObj, self._focusObj)
427 def sceneUpdated(self):
428 self._sceneUpdateTimer.Start(500, True)
429 self._slicer.abortSlicer()
430 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
433 def _onRunSlicer(self, e):
434 if self._isSimpleMode:
435 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
436 self._slicer.runSlicer(self._scene)
437 if self._isSimpleMode:
438 profile.resetTempOverride()
440 def _updateSliceProgress(self, progressValue, ready):
442 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
444 self.printButton.setDisabled(not ready)
445 if progressValue >= 0.0:
446 self.printButton.setProgressBar(progressValue)
448 self.printButton.setProgressBar(None)
449 if self._gcode is not None:
451 for layerVBOlist in self._gcodeVBOs:
452 for vbo in layerVBOlist:
453 self.glReleaseList.append(vbo)
456 self.printButton.setProgressBar(None)
457 cost = self._slicer.getFilamentCost()
459 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
461 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
462 self._gcode = gcodeInterpreter.gcode()
463 self._gcodeFilename = self._slicer.getGCodeFilename()
465 self.printButton.setBottomText('')
468 def _loadGCode(self):
469 self._gcode.progressCallback = self._gcodeLoadCallback
470 self._gcode.load(self._gcodeFilename)
472 def _gcodeLoadCallback(self, progress):
473 if self._gcode is None:
475 if len(self._gcode.layerList) % 15 == 0:
477 if self._gcode is None:
479 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
480 if self.viewMode == 'gcode':
484 def loadScene(self, fileList):
485 for filename in fileList:
487 objList = meshLoader.loadMeshes(filename)
489 traceback.print_exc()
492 if self._objectLoadShader is not None:
493 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
497 self._scene.centerAll()
498 self._selectObject(obj)
499 if obj.getScale()[0] < 1.0:
500 self.notification.message("Warning: Object scaled down.")
503 def _deleteObject(self, obj):
504 if obj == self._selectedObj:
505 self._selectObject(None)
506 if obj == self._focusObj:
507 self._focusObj = None
508 self._scene.remove(obj)
509 for m in obj._meshList:
510 if m.vbo is not None and m.vbo.decRef():
511 self.glReleaseList.append(m.vbo)
516 def _selectObject(self, obj, zoom = True):
517 if obj != self._selectedObj:
518 self._selectedObj = obj
519 self.updateProfileToControls()
520 self.updateToolButtons()
521 if zoom and obj is not None:
522 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
523 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
524 newZoom = obj.getBoundaryCircle() * 6
525 if newZoom > numpy.max(self._machineSize) * 3:
526 newZoom = numpy.max(self._machineSize) * 3
527 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
529 def updateProfileToControls(self):
530 oldSimpleMode = self._isSimpleMode
531 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
532 if self._isSimpleMode != oldSimpleMode:
533 self._scene.arrangeAll()
535 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
536 self._objColors[0] = profile.getPreferenceColour('model_colour')
537 self._objColors[1] = profile.getPreferenceColour('model_colour2')
538 self._objColors[2] = profile.getPreferenceColour('model_colour3')
539 self._objColors[3] = profile.getPreferenceColour('model_colour4')
540 self._scene.setMachineSize(self._machineSize)
541 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
542 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'))
544 if self._selectedObj is not None:
545 scale = self._selectedObj.getScale()
546 size = self._selectedObj.getSize()
547 self.scaleXctrl.setValue(round(scale[0], 2))
548 self.scaleYctrl.setValue(round(scale[1], 2))
549 self.scaleZctrl.setValue(round(scale[2], 2))
550 self.scaleXmmctrl.setValue(round(size[0], 2))
551 self.scaleYmmctrl.setValue(round(size[1], 2))
552 self.scaleZmmctrl.setValue(round(size[2], 2))
554 def OnKeyChar(self, keyCode):
555 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
556 if self._selectedObj is not None:
557 self._deleteObject(self._selectedObj)
559 if keyCode == wx.WXK_UP:
560 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
562 elif keyCode == wx.WXK_DOWN:
563 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
565 elif keyCode == wx.WXK_PAGEUP:
566 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
568 elif keyCode == wx.WXK_PAGEDOWN:
569 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
572 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
573 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
574 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
575 from collections import defaultdict
576 from gc import get_objects
577 self._beforeLeakTest = defaultdict(int)
578 for i in get_objects():
579 self._beforeLeakTest[type(i)] += 1
580 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
581 from collections import defaultdict
582 from gc import get_objects
583 self._afterLeakTest = defaultdict(int)
584 for i in get_objects():
585 self._afterLeakTest[type(i)] += 1
586 for k in self._afterLeakTest:
587 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
588 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
590 def ShaderUpdate(self, v, f):
591 s = opengl.GLShader(v, f)
593 self._objectLoadShader.release()
594 self._objectLoadShader = s
595 for obj in self._scene.objects():
596 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
599 def OnMouseDown(self,e):
600 self._mouseX = e.GetX()
601 self._mouseY = e.GetY()
602 self._mouseClick3DPos = self._mouse3Dpos
603 self._mouseClickFocus = self._focusObj
605 self._mouseState = 'doubleClick'
607 self._mouseState = 'dragOrClick'
608 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
609 p0 -= self.getObjectCenterPos() - self._viewTarget
610 p1 -= self.getObjectCenterPos() - self._viewTarget
611 if self.tool.OnDragStart(p0, p1):
612 self._mouseState = 'tool'
613 if self._mouseState == 'dragOrClick':
614 if e.GetButton() == 1:
615 if self._focusObj is not None:
616 self._selectObject(self._focusObj, False)
619 def OnMouseUp(self, e):
620 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
622 if self._mouseState == 'dragOrClick':
623 if e.GetButton() == 1:
624 self._selectObject(self._focusObj)
625 if e.GetButton() == 3:
627 if self._focusObj is not None:
628 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete")))
629 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply")))
630 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split")))
631 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
632 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
633 if len(self._scene.objects()) > 0:
634 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all")))
635 if menu.MenuItemCount > 0:
638 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
639 self._scene.pushFree()
641 elif self._mouseState == 'tool':
642 if self.tempMatrix is not None and self._selectedObj is not None:
643 self._selectedObj.applyMatrix(self.tempMatrix)
644 self._scene.pushFree()
645 self._selectObject(self._selectedObj)
646 self.tempMatrix = None
647 self.tool.OnDragEnd()
649 self._mouseState = None
651 def OnMouseMotion(self,e):
652 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
653 p0 -= self.getObjectCenterPos() - self._viewTarget
654 p1 -= self.getObjectCenterPos() - self._viewTarget
656 if e.Dragging() and self._mouseState is not None:
657 if self._mouseState == 'tool':
658 self.tool.OnDrag(p0, p1)
659 elif not e.LeftIsDown() and e.RightIsDown():
660 self._mouseState = 'drag'
661 if wx.GetKeyState(wx.WXK_SHIFT):
662 a = math.cos(math.radians(self._yaw)) / 3.0
663 b = math.sin(math.radians(self._yaw)) / 3.0
664 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
665 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
666 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
667 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
669 self._yaw += e.GetX() - self._mouseX
670 self._pitch -= e.GetY() - self._mouseY
671 if self._pitch > 170:
675 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
676 self._mouseState = 'drag'
677 self._zoom += e.GetY() - self._mouseY
680 if self._zoom > numpy.max(self._machineSize) * 3:
681 self._zoom = numpy.max(self._machineSize) * 3
682 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
683 self._mouseState = 'dragObject'
684 z = max(0, self._mouseClick3DPos[2])
685 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
686 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
691 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
692 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
693 diff = cursorZ1 - cursorZ0
694 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
695 if not e.Dragging() or self._mouseState != 'tool':
696 self.tool.OnMouseMove(p0, p1)
698 self._mouseX = e.GetX()
699 self._mouseY = e.GetY()
701 def OnMouseWheel(self, e):
702 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
703 delta = max(min(delta,4),-4)
704 self._zoom *= 1.0 - delta / 10.0
707 if self._zoom > numpy.max(self._machineSize) * 3:
708 self._zoom = numpy.max(self._machineSize) * 3
711 def OnMouseLeave(self, e):
715 def getMouseRay(self, x, y):
716 if self._viewport is None:
717 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
718 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
719 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
720 p0 -= self._viewTarget
721 p1 -= self._viewTarget
724 def _init3DView(self):
725 # set viewing projection
726 size = self.GetSize()
727 glViewport(0, 0, size.GetWidth(), size.GetHeight())
730 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
732 glDisable(GL_RESCALE_NORMAL)
733 glDisable(GL_LIGHTING)
735 glEnable(GL_DEPTH_TEST)
736 glDisable(GL_CULL_FACE)
738 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
740 glClearColor(0.8, 0.8, 0.8, 1.0)
744 glMatrixMode(GL_PROJECTION)
746 aspect = float(size.GetWidth()) / float(size.GetHeight())
747 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
749 glMatrixMode(GL_MODELVIEW)
751 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
754 if machineCom.machineIsConnected():
755 self.printButton._imageID = 6
756 self.printButton._tooltip = _("Print")
757 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
758 self.printButton._imageID = 2
759 self.printButton._tooltip = _("Toolpath to SD")
761 self.printButton._imageID = 3
762 self.printButton._tooltip = _("Save toolpath")
764 if self._animView is not None:
765 self._viewTarget = self._animView.getPosition()
766 if self._animView.isDone():
767 self._animView = None
768 if self._animZoom is not None:
769 self._zoom = self._animZoom.getPosition()
770 if self._animZoom.isDone():
771 self._animZoom = None
772 if self.viewMode == 'gcode' and self._gcode is not None:
774 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
777 if self._objectShader is None:
778 if opengl.hasShaderSupport():
779 self._objectShader = opengl.GLShader("""
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)));
791 varying float light_amount;
795 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
798 self._objectOverhangShader = opengl.GLShader("""
799 uniform float cosAngle;
800 uniform mat3 rotMatrix;
801 varying float light_amount;
805 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
806 gl_FrontColor = gl_Color;
808 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
810 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
812 light_amount = -10.0;
816 varying float light_amount;
820 if (light_amount == -10.0)
822 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
824 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
828 self._objectLoadShader = opengl.GLShader("""
829 uniform float intensity;
831 varying float light_amount;
835 vec4 tmp = gl_Vertex;
836 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
837 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
838 gl_Position = gl_ModelViewProjectionMatrix * tmp;
839 gl_FrontColor = gl_Color;
841 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
845 uniform float intensity;
846 varying float light_amount;
850 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
853 if self._objectShader == None or not self._objectShader.isValid():
854 self._objectShader = opengl.GLFakeShader()
855 self._objectOverhangShader = opengl.GLFakeShader()
856 self._objectLoadShader = None
858 glTranslate(0,0,-self._zoom)
859 glRotate(-self._pitch, 1,0,0)
860 glRotate(self._yaw, 0,0,1)
861 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
863 self._viewport = glGetIntegerv(GL_VIEWPORT)
864 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
865 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
867 glClearColor(1,1,1,1)
868 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
870 if self.viewMode != 'gcode':
871 for n in xrange(0, len(self._scene.objects())):
872 obj = self._scene.objects()[n]
873 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
874 self._renderObject(obj)
876 if self._mouseX > -1:
878 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
879 if n < len(self._scene.objects()):
880 self._focusObj = self._scene.objects()[n]
882 self._focusObj = None
883 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
884 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
885 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
886 self._mouse3Dpos -= self._viewTarget
889 glTranslate(0,0,-self._zoom)
890 glRotate(-self._pitch, 1,0,0)
891 glRotate(self._yaw, 0,0,1)
892 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
894 if self.viewMode == 'gcode':
895 if self._gcode is not None and self._gcode.layerList is None:
896 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
897 self._gcodeLoadThread.daemon = True
898 self._gcodeLoadThread.start()
899 if self._gcode is not None and self._gcode.layerList is not None:
901 if profile.getPreference('machine_center_is_zero') != 'True':
902 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
904 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
905 for n in xrange(0, drawUpTill):
906 c = 1.0 - float(drawUpTill - n) / 15
908 if len(self._gcodeVBOs) < n + 1:
909 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
910 if time.time() - t > 0.5:
913 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
914 if n == drawUpTill - 1:
915 if len(self._gcodeVBOs[n]) < 9:
916 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
918 self._gcodeVBOs[n][8].render(GL_QUADS)
920 self._gcodeVBOs[n][9].render(GL_QUADS)
922 self._gcodeVBOs[n][10].render(GL_QUADS)
924 self._gcodeVBOs[n][11].render(GL_QUADS)
927 self._gcodeVBOs[n][12].render(GL_QUADS)
928 glColor3f(c/2, c/2, 0.0)
929 self._gcodeVBOs[n][13].render(GL_QUADS)
931 self._gcodeVBOs[n][14].render(GL_QUADS)
932 self._gcodeVBOs[n][15].render(GL_QUADS)
934 self._gcodeVBOs[n][16].render(GL_LINES)
937 self._gcodeVBOs[n][0].render(GL_LINES)
939 self._gcodeVBOs[n][1].render(GL_LINES)
941 self._gcodeVBOs[n][2].render(GL_LINES)
943 self._gcodeVBOs[n][3].render(GL_LINES)
946 self._gcodeVBOs[n][4].render(GL_LINES)
947 glColor3f(c/2, c/2, 0.0)
948 self._gcodeVBOs[n][5].render(GL_LINES)
950 self._gcodeVBOs[n][6].render(GL_LINES)
951 self._gcodeVBOs[n][7].render(GL_LINES)
954 glStencilFunc(GL_ALWAYS, 1, 1)
955 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
957 if self.viewMode == 'overhang':
958 self._objectOverhangShader.bind()
959 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
961 self._objectShader.bind()
962 for obj in self._scene.objects():
963 if obj._loadAnim is not None:
964 if obj._loadAnim.isDone():
969 if self._focusObj == obj:
971 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
974 if self._selectedObj == obj or self._selectedObj is None:
975 #If we want transparent, then first render a solid black model to remove the printer size lines.
976 if self.viewMode == 'transparent':
977 glColor4f(0, 0, 0, 0)
978 self._renderObject(obj)
980 glBlendFunc(GL_ONE, GL_ONE)
981 glDisable(GL_DEPTH_TEST)
983 if self.viewMode == 'xray':
984 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
985 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
986 glEnable(GL_STENCIL_TEST)
988 if self.viewMode == 'overhang':
989 if self._selectedObj == obj and self.tempMatrix is not None:
990 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
992 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
994 if not self._scene.checkPlatform(obj):
995 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
996 self._renderObject(obj)
998 self._renderObject(obj, brightness)
999 glDisable(GL_STENCIL_TEST)
1001 glEnable(GL_DEPTH_TEST)
1002 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1004 if self.viewMode == 'xray':
1007 glEnable(GL_STENCIL_TEST)
1008 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1009 glDisable(GL_DEPTH_TEST)
1010 for i in xrange(2, 15, 2):
1011 glStencilFunc(GL_EQUAL, i, 0xFF)
1012 glColor(float(i)/10, float(i)/10, float(i)/5)
1014 glVertex3f(-1000,-1000,-10)
1015 glVertex3f( 1000,-1000,-10)
1016 glVertex3f( 1000, 1000,-10)
1017 glVertex3f(-1000, 1000,-10)
1019 for i in xrange(1, 15, 2):
1020 glStencilFunc(GL_EQUAL, i, 0xFF)
1021 glColor(float(i)/10, 0, 0)
1023 glVertex3f(-1000,-1000,-10)
1024 glVertex3f( 1000,-1000,-10)
1025 glVertex3f( 1000, 1000,-10)
1026 glVertex3f(-1000, 1000,-10)
1029 glDisable(GL_STENCIL_TEST)
1030 glEnable(GL_DEPTH_TEST)
1032 self._objectShader.unbind()
1034 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1036 if self._objectLoadShader is not None:
1037 self._objectLoadShader.bind()
1038 glColor4f(0.2, 0.6, 1.0, 1.0)
1039 for obj in self._scene.objects():
1040 if obj._loadAnim is None:
1042 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1043 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1044 self._renderObject(obj)
1045 self._objectLoadShader.unbind()
1050 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1052 z = self._usbPrintMonitor.getZ()
1053 size = self._machineSize
1054 glColor4ub(255,255,0,128)
1056 glVertex3f(-size[0]/2,-size[1]/2, z)
1057 glVertex3f( size[0]/2,-size[1]/2, z)
1058 glVertex3f( size[0]/2, size[1]/2, z)
1059 glVertex3f(-size[0]/2, size[1]/2, z)
1062 if self.viewMode == 'gcode':
1063 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1064 glDisable(GL_DEPTH_TEST)
1067 glTranslate(0,-4,-10)
1068 glColor4ub(60,60,60,255)
1069 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1072 #Draw the object box-shadow, so you can see where it will collide with other objects.
1073 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1074 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1076 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1078 glEnable(GL_CULL_FACE)
1079 glColor4f(0,0,0,0.12)
1081 glVertex3f(-size[0], size[1], 0.1)
1082 glVertex3f(-size[0], -size[1], 0.1)
1083 glVertex3f( size[0], -size[1], 0.1)
1084 glVertex3f( size[0], size[1], 0.1)
1086 glDisable(GL_CULL_FACE)
1089 #Draw the outline of the selected object, on top of everything else except the GUI.
1090 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1091 glDisable(GL_DEPTH_TEST)
1092 glEnable(GL_CULL_FACE)
1093 glEnable(GL_STENCIL_TEST)
1095 glStencilFunc(GL_EQUAL, 0, 255)
1097 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1099 glColor4f(1,1,1,0.5)
1100 self._renderObject(self._selectedObj)
1101 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1103 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1104 glDisable(GL_STENCIL_TEST)
1105 glDisable(GL_CULL_FACE)
1106 glEnable(GL_DEPTH_TEST)
1108 if self._selectedObj is not None:
1110 pos = self.getObjectCenterPos()
1111 glTranslate(pos[0], pos[1], pos[2])
1114 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1115 glDisable(GL_DEPTH_TEST)
1118 glTranslate(0,-4,-10)
1119 glColor4ub(60,60,60,255)
1120 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1123 def _renderObject(self, obj, brightness = False, addSink = True):
1126 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1128 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1130 if self.tempMatrix is not None and obj == self._selectedObj:
1131 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1132 glMultMatrixf(tempMatrix)
1134 offset = obj.getDrawOffset()
1135 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1137 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1138 glMultMatrixf(tempMatrix)
1141 for m in obj._meshList:
1143 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1145 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1150 def _drawMachine(self):
1151 glEnable(GL_CULL_FACE)
1154 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1156 if profile.getPreference('machine_type').startswith('ultimaker'):
1157 if self._platformMesh is None:
1158 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
1159 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1160 glColor4f(1,1,1,0.5)
1161 self._objectShader.bind()
1162 self._renderObject(self._platformMesh, False, False)
1163 self._objectShader.unbind()
1168 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1169 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1170 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1171 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1172 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1173 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1176 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1177 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1178 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1179 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1180 v4 = [ size[0] / 2, size[1] / 2, 0]
1181 v5 = [ size[0] / 2,-size[1] / 2, 0]
1182 v6 = [-size[0] / 2, size[1] / 2, 0]
1183 v7 = [-size[0] / 2,-size[1] / 2, 0]
1185 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1186 glEnableClientState(GL_VERTEX_ARRAY)
1187 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1189 glColor4ub(5, 171, 231, 64)
1190 glDrawArrays(GL_QUADS, 0, 4)
1191 glColor4ub(5, 171, 231, 96)
1192 glDrawArrays(GL_QUADS, 4, 8)
1193 glColor4ub(5, 171, 231, 128)
1194 glDrawArrays(GL_QUADS, 12, 8)
1195 glDisableClientState(GL_VERTEX_ARRAY)
1197 sx = self._machineSize[0]
1198 sy = self._machineSize[1]
1199 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1200 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1205 x1 = max(min(x1, sx/2), -sx/2)
1206 y1 = max(min(y1, sy/2), -sy/2)
1207 x2 = max(min(x2, sx/2), -sx/2)
1208 y2 = max(min(y2, sy/2), -sy/2)
1209 if (x & 1) == (y & 1):
1210 glColor4ub(5, 171, 231, 127)
1212 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1214 glVertex3f(x1, y1, -0.02)
1215 glVertex3f(x2, y1, -0.02)
1216 glVertex3f(x2, y2, -0.02)
1217 glVertex3f(x1, y2, -0.02)
1221 glDisable(GL_CULL_FACE)
1223 def _generateGCodeVBOs(self, layer):
1225 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1226 if ':' in extrudeType:
1227 extruder = int(extrudeType[extrudeType.find(':')+1:])
1228 extrudeType = extrudeType[0:extrudeType.find(':')]
1231 pointList = numpy.zeros((0,3), numpy.float32)
1233 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1235 a = numpy.concatenate((a[:-1], a[1:]), 1)
1236 a = a.reshape((len(a) * 2, 3))
1237 pointList = numpy.concatenate((pointList, a))
1238 ret.append(opengl.GLVBO(pointList))
1241 def _generateGCodeVBOs2(self, layer):
1242 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1243 filamentArea = math.pi * filamentRadius * filamentRadius
1244 useFilamentArea = profile.getPreference('gcode_flavor') == 'UltiGCode'
1247 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1248 if ':' in extrudeType:
1249 extruder = int(extrudeType[extrudeType.find(':')+1:])
1250 extrudeType = extrudeType[0:extrudeType.find(':')]
1253 pointList = numpy.zeros((0,3), numpy.float32)
1255 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1257 if extrudeType == 'FILL':
1260 normal = a[1:] - a[:-1]
1261 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1262 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1265 ePerDist = path['extrusion'][1:] / lens
1267 lineWidth = ePerDist / path['layerThickness'] / 2.0
1269 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1271 normal[:,0] *= lineWidth
1272 normal[:,1] *= lineWidth
1274 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1275 b = numpy.concatenate((b, a[1:] + normal), 1)
1276 b = numpy.concatenate((b, a[1:] - normal), 1)
1277 b = numpy.concatenate((b, a[:-1] - normal), 1)
1278 b = numpy.concatenate((b, a[:-1] + normal), 1)
1279 b = b.reshape((len(b) * 4, 3))
1282 normal2 = normal[:-1] + normal[1:]
1283 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1284 normal2[:,0] /= lens2
1285 normal2[:,1] /= lens2
1286 normal2[:,0] *= lineWidth[:-1]
1287 normal2[:,1] *= lineWidth[:-1]
1289 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1290 c = numpy.concatenate((c, a[1:-1]), 1)
1291 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1292 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1293 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1295 c = numpy.concatenate((c, a[1:-1]), 1)
1296 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1297 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1298 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1300 c = c.reshape((len(c) * 8, 3))
1302 pointList = numpy.concatenate((pointList, b, c))
1304 pointList = numpy.concatenate((pointList, b))
1305 ret.append(opengl.GLVBO(pointList))
1307 pointList = numpy.zeros((0,3), numpy.float32)
1309 if path['type'] == 'move':
1310 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1311 a = numpy.concatenate((a[:-1], a[1:]), 1)
1312 a = a.reshape((len(a) * 2, 3))
1313 pointList = numpy.concatenate((pointList, a))
1314 if path['type'] == 'retract':
1315 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1316 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1317 a = a.reshape((len(a) * 2, 3))
1318 pointList = numpy.concatenate((pointList, a))
1319 ret.append(opengl.GLVBO(pointList))
1323 def getObjectCenterPos(self):
1324 if self._selectedObj is None:
1325 return [0.0, 0.0, 0.0]
1326 pos = self._selectedObj.getPosition()
1327 size = self._selectedObj.getSize()
1328 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1330 def getObjectBoundaryCircle(self):
1331 if self._selectedObj is None:
1333 return self._selectedObj.getBoundaryCircle()
1335 def getObjectSize(self):
1336 if self._selectedObj is None:
1337 return [0.0, 0.0, 0.0]
1338 return self._selectedObj.getSize()
1340 def getObjectMatrix(self):
1341 if self._selectedObj is None:
1342 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1343 return self._selectedObj.getMatrix()
1345 class shaderEditor(wx.Dialog):
1346 def __init__(self, parent, callback, v, f):
1347 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1348 self._callback = callback
1349 s = wx.BoxSizer(wx.VERTICAL)
1351 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1352 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1353 s.Add(self._vertex, 1, flag=wx.EXPAND)
1354 s.Add(self._fragment, 1, flag=wx.EXPAND)
1356 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1357 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1359 self.SetPosition(self.GetParent().GetPosition())
1360 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1363 def OnText(self, e):
1364 self._callback(self._vertex.GetValue(), self._fragment.GetValue())