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 = {}
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):
147 for filename in filenames:
148 self.GetParent().GetParent().GetParent().addToModelMRU(filename) #??? only Model files?
149 ext = filename[filename.rfind('.')+1:].upper()
150 if ext == 'G' or ext == 'GCODE':
151 gcodeFilename = filename
152 if gcodeFilename is not None:
153 self.loadGCodeFile(gcodeFilename)
155 profileFilename = None
156 for filename in filenames:
157 ext = filename[filename.rfind('.')+1:].upper()
159 profileFilename = filename
160 if profileFilename is not None:
161 profile.loadProfile(profileFilename)
162 self.GetParent().GetParent().GetParent().updateProfileToAllControls()
164 self.loadSceneFiles(filenames)
166 def showLoadModel(self, button = 1):
168 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)
169 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
170 if dlg.ShowModal() != wx.ID_OK:
173 filenames = dlg.GetPaths()
175 if len(filenames) < 1:
177 profile.putPreference('lastFile', filenames[0])
178 self.loadFiles(filenames)
180 def showSaveModel(self):
181 if len(self._scene.objects()) < 1:
183 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
184 dlg.SetWildcard(meshLoader.saveWildcardFilter())
185 if dlg.ShowModal() != wx.ID_OK:
188 filename = dlg.GetPath()
190 meshLoader.saveMeshes(filename, self._scene.objects())
192 def OnPrintButton(self, button):
194 if machineCom.machineIsConnected():
195 self.showPrintWindow()
196 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
197 drives = removableStorage.getPossibleSDcardDrives()
199 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))
200 if dlg.ShowModal() != wx.ID_OK:
203 drive = drives[dlg.GetSelection()]
207 filename = self._scene._objectList[0].getName() + '.gcode'
208 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
213 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
214 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
215 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
219 def showPrintWindow(self):
220 if self._gcodeFilename is None:
222 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
223 if self._gcodeFilename == self._slicer.getGCodeFilename():
224 self._slicer.submitSliceInfoOnline()
226 def showSaveGCode(self):
227 defPath = profile.getPreference('lastFile')
228 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
229 dlg=wx.FileDialog(self, _("Save toolpath"), defPath, style=wx.FD_SAVE)
230 dlg.SetFilename(self._scene._objectList[0].getName())
231 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
232 if dlg.ShowModal() != wx.ID_OK:
235 filename = dlg.GetPath()
238 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
240 def _copyFile(self, fileA, fileB, allowEject = False):
242 size = float(os.stat(fileA).st_size)
243 with open(fileA, 'rb') as fsrc:
244 with open(fileB, 'wb') as fdst:
246 buf = fsrc.read(16*1024)
250 self.printButton.setProgressBar(float(fsrc.tell()) / size)
255 self.notification.message("Failed to save")
258 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...'))
260 self.notification.message("Saved as %s" % (fileB))
261 self.printButton.setProgressBar(None)
262 if fileA == self._slicer.getGCodeFilename():
263 self._slicer.submitSliceInfoOnline()
265 def _showSliceLog(self):
266 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
270 def OnToolSelect(self, button):
271 if self.rotateToolButton.getSelected():
272 self.tool = previewTools.toolRotate(self)
273 elif self.scaleToolButton.getSelected():
274 self.tool = previewTools.toolScale(self)
275 elif self.mirrorToolButton.getSelected():
276 self.tool = previewTools.toolNone(self)
278 self.tool = previewTools.toolNone(self)
279 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
280 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
281 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
282 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
283 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
284 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
285 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
286 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
288 def updateToolButtons(self):
289 if self._selectedObj is None:
293 self.rotateToolButton.setHidden(hidden)
294 self.scaleToolButton.setHidden(hidden)
295 self.mirrorToolButton.setHidden(hidden)
297 self.rotateToolButton.setSelected(False)
298 self.scaleToolButton.setSelected(False)
299 self.mirrorToolButton.setSelected(False)
302 def OnViewChange(self):
303 if self.viewSelection.getValue() == 4:
304 self.viewMode = 'gcode'
305 if self._gcode is not None and self._gcode.layerList is not None:
306 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
307 self._selectObject(None)
308 elif self.viewSelection.getValue() == 1:
309 self.viewMode = 'overhang'
310 elif self.viewSelection.getValue() == 2:
311 self.viewMode = 'transparent'
312 elif self.viewSelection.getValue() == 3:
313 self.viewMode = 'xray'
315 self.viewMode = 'normal'
316 self.layerSelect.setHidden(self.viewMode != 'gcode')
319 def OnRotateReset(self, button):
320 if self._selectedObj is None:
322 self._selectedObj.resetRotation()
323 self._scene.pushFree()
324 self._selectObject(self._selectedObj)
327 def OnLayFlat(self, button):
328 if self._selectedObj is None:
330 self._selectedObj.layFlat()
331 self._scene.pushFree()
332 self._selectObject(self._selectedObj)
335 def OnScaleReset(self, button):
336 if self._selectedObj is None:
338 self._selectedObj.resetScale()
339 self._selectObject(self._selectedObj)
340 self.updateProfileToControls()
343 def OnScaleMax(self, button):
344 if self._selectedObj is None:
346 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
347 self._scene.pushFree()
348 self._selectObject(self._selectedObj)
349 self.updateProfileToControls()
352 def OnMirror(self, axis):
353 if self._selectedObj is None:
355 self._selectedObj.mirror(axis)
358 def OnScaleEntry(self, value, axis):
359 if self._selectedObj is None:
365 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
366 self.updateProfileToControls()
367 self._scene.pushFree()
368 self._selectObject(self._selectedObj)
371 def OnScaleEntryMM(self, value, axis):
372 if self._selectedObj is None:
378 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
379 self.updateProfileToControls()
380 self._scene.pushFree()
381 self._selectObject(self._selectedObj)
384 def OnDeleteAll(self, e):
385 while len(self._scene.objects()) > 0:
386 self._deleteObject(self._scene.objects()[0])
387 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
389 def OnMultiply(self, e):
390 if self._focusObj is None:
393 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
394 if dlg.ShowModal() != wx.ID_OK:
403 self._scene.add(newObj)
404 self._scene.centerAll()
405 if not self._scene.checkPlatform(newObj):
410 self.notification.message("Could not create more then %d items" % (n - 1))
411 self._scene.remove(newObj)
412 self._scene.centerAll()
415 def OnSplitObject(self, e):
416 if self._focusObj is None:
418 self._scene.remove(self._focusObj)
419 for obj in self._focusObj.split(self._splitCallback):
420 if numpy.max(obj.getSize()) > 2.0:
422 self._scene.centerAll()
423 self._selectObject(None)
426 def OnCenter(self, e):
427 if self._focusObj is None:
429 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
430 self._scene.pushFree()
432 def _splitCallback(self, progress):
435 def OnMergeObjects(self, e):
436 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
437 if len(self._scene.objects()) == 2:
438 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
441 self._scene.merge(self._selectedObj, self._focusObj)
444 def sceneUpdated(self):
445 self._sceneUpdateTimer.Start(500, True)
446 self._slicer.abortSlicer()
447 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
450 def _onRunSlicer(self, e):
451 if self._isSimpleMode:
452 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
453 self._slicer.runSlicer(self._scene)
454 if self._isSimpleMode:
455 profile.resetTempOverride()
457 def _updateSliceProgress(self, progressValue, ready):
459 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
461 self.printButton.setDisabled(not ready)
462 if progressValue >= 0.0:
463 self.printButton.setProgressBar(progressValue)
465 self.printButton.setProgressBar(None)
466 if self._gcode is not None:
468 for layerVBOlist in self._gcodeVBOs:
469 for vbo in layerVBOlist:
470 self.glReleaseList.append(vbo)
473 self.printButton.setProgressBar(None)
474 cost = self._slicer.getFilamentCost()
476 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
478 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
479 self._gcode = gcodeInterpreter.gcode()
480 self._gcodeFilename = self._slicer.getGCodeFilename()
482 self.printButton.setBottomText('')
485 def _loadGCode(self):
486 self._gcode.progressCallback = self._gcodeLoadCallback
487 self._gcode.load(self._gcodeFilename)
489 def _gcodeLoadCallback(self, progress):
490 if self._gcode is None:
492 if len(self._gcode.layerList) % 15 == 0:
494 if self._gcode is None:
496 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
497 if self.viewMode == 'gcode':
501 def loadScene(self, fileList):
502 for filename in fileList:
504 objList = meshLoader.loadMeshes(filename)
506 traceback.print_exc()
509 if self._objectLoadShader is not None:
510 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
514 self._scene.centerAll()
515 self._selectObject(obj)
516 if obj.getScale()[0] < 1.0:
517 self.notification.message("Warning: Object scaled down.")
520 def _deleteObject(self, obj):
521 if obj == self._selectedObj:
522 self._selectObject(None)
523 if obj == self._focusObj:
524 self._focusObj = None
525 self._scene.remove(obj)
526 for m in obj._meshList:
527 if m.vbo is not None and m.vbo.decRef():
528 self.glReleaseList.append(m.vbo)
533 def _selectObject(self, obj, zoom = True):
534 if obj != self._selectedObj:
535 self._selectedObj = obj
536 self.updateProfileToControls()
537 self.updateToolButtons()
538 if zoom and obj is not None:
539 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
540 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
541 newZoom = obj.getBoundaryCircle() * 6
542 if newZoom > numpy.max(self._machineSize) * 3:
543 newZoom = numpy.max(self._machineSize) * 3
544 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
546 def updateProfileToControls(self):
547 oldSimpleMode = self._isSimpleMode
548 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
549 if self._isSimpleMode != oldSimpleMode:
550 self._scene.arrangeAll()
552 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
553 self._objColors[0] = profile.getPreferenceColour('model_colour')
554 self._objColors[1] = profile.getPreferenceColour('model_colour2')
555 self._objColors[2] = profile.getPreferenceColour('model_colour3')
556 self._objColors[3] = profile.getPreferenceColour('model_colour4')
557 self._scene.setMachineSize(self._machineSize)
558 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
559 self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height'))
561 if self._selectedObj is not None:
562 scale = self._selectedObj.getScale()
563 size = self._selectedObj.getSize()
564 self.scaleXctrl.setValue(round(scale[0], 2))
565 self.scaleYctrl.setValue(round(scale[1], 2))
566 self.scaleZctrl.setValue(round(scale[2], 2))
567 self.scaleXmmctrl.setValue(round(size[0], 2))
568 self.scaleYmmctrl.setValue(round(size[1], 2))
569 self.scaleZmmctrl.setValue(round(size[2], 2))
571 def OnKeyChar(self, keyCode):
572 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
573 if self._selectedObj is not None:
574 self._deleteObject(self._selectedObj)
576 if keyCode == wx.WXK_UP:
577 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
579 elif keyCode == wx.WXK_DOWN:
580 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
582 elif keyCode == wx.WXK_PAGEUP:
583 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
585 elif keyCode == wx.WXK_PAGEDOWN:
586 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
589 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
590 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
591 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
592 from collections import defaultdict
593 from gc import get_objects
594 self._beforeLeakTest = defaultdict(int)
595 for i in get_objects():
596 self._beforeLeakTest[type(i)] += 1
597 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
598 from collections import defaultdict
599 from gc import get_objects
600 self._afterLeakTest = defaultdict(int)
601 for i in get_objects():
602 self._afterLeakTest[type(i)] += 1
603 for k in self._afterLeakTest:
604 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
605 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
607 def ShaderUpdate(self, v, f):
608 s = opengl.GLShader(v, f)
610 self._objectLoadShader.release()
611 self._objectLoadShader = s
612 for obj in self._scene.objects():
613 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
616 def OnMouseDown(self,e):
617 self._mouseX = e.GetX()
618 self._mouseY = e.GetY()
619 self._mouseClick3DPos = self._mouse3Dpos
620 self._mouseClickFocus = self._focusObj
622 self._mouseState = 'doubleClick'
624 self._mouseState = 'dragOrClick'
625 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
626 p0 -= self.getObjectCenterPos() - self._viewTarget
627 p1 -= self.getObjectCenterPos() - self._viewTarget
628 if self.tool.OnDragStart(p0, p1):
629 self._mouseState = 'tool'
630 if self._mouseState == 'dragOrClick':
631 if e.GetButton() == 1:
632 if self._focusObj is not None:
633 self._selectObject(self._focusObj, False)
636 def OnMouseUp(self, e):
637 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
639 if self._mouseState == 'dragOrClick':
640 if e.GetButton() == 1:
641 self._selectObject(self._focusObj)
642 if e.GetButton() == 3:
644 if self._focusObj is not None:
645 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
646 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
647 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
648 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
649 if ((self._selectedObj != self._focusObj and self._focusObj is not None and self._selectedObj is not None) or len(self._scene.objects()) == 2) and int(profile.getMachineSetting('extruder_amount')) > 1:
650 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
651 if len(self._scene.objects()) > 0:
652 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
653 if menu.MenuItemCount > 0:
656 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
657 self._scene.pushFree()
659 elif self._mouseState == 'tool':
660 if self.tempMatrix is not None and self._selectedObj is not None:
661 self._selectedObj.applyMatrix(self.tempMatrix)
662 self._scene.pushFree()
663 self._selectObject(self._selectedObj)
664 self.tempMatrix = None
665 self.tool.OnDragEnd()
667 self._mouseState = None
669 def OnMouseMotion(self,e):
670 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
671 p0 -= self.getObjectCenterPos() - self._viewTarget
672 p1 -= self.getObjectCenterPos() - self._viewTarget
674 if e.Dragging() and self._mouseState is not None:
675 if self._mouseState == 'tool':
676 self.tool.OnDrag(p0, p1)
677 elif not e.LeftIsDown() and e.RightIsDown():
678 self._mouseState = 'drag'
679 if wx.GetKeyState(wx.WXK_SHIFT):
680 a = math.cos(math.radians(self._yaw)) / 3.0
681 b = math.sin(math.radians(self._yaw)) / 3.0
682 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
683 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
684 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
685 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
687 self._yaw += e.GetX() - self._mouseX
688 self._pitch -= e.GetY() - self._mouseY
689 if self._pitch > 170:
693 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
694 self._mouseState = 'drag'
695 self._zoom += e.GetY() - self._mouseY
698 if self._zoom > numpy.max(self._machineSize) * 3:
699 self._zoom = numpy.max(self._machineSize) * 3
700 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
701 self._mouseState = 'dragObject'
702 z = max(0, self._mouseClick3DPos[2])
703 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
704 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
709 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
710 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
711 diff = cursorZ1 - cursorZ0
712 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
713 if not e.Dragging() or self._mouseState != 'tool':
714 self.tool.OnMouseMove(p0, p1)
716 self._mouseX = e.GetX()
717 self._mouseY = e.GetY()
719 def OnMouseWheel(self, e):
720 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
721 delta = max(min(delta,4),-4)
722 self._zoom *= 1.0 - delta / 10.0
725 if self._zoom > numpy.max(self._machineSize) * 3:
726 self._zoom = numpy.max(self._machineSize) * 3
729 def OnMouseLeave(self, e):
733 def getMouseRay(self, x, y):
734 if self._viewport is None:
735 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
736 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
737 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
738 p0 -= self._viewTarget
739 p1 -= self._viewTarget
742 def _init3DView(self):
743 # set viewing projection
744 size = self.GetSize()
745 glViewport(0, 0, size.GetWidth(), size.GetHeight())
748 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
750 glDisable(GL_RESCALE_NORMAL)
751 glDisable(GL_LIGHTING)
753 glEnable(GL_DEPTH_TEST)
754 glDisable(GL_CULL_FACE)
756 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
758 glClearColor(0.8, 0.8, 0.8, 1.0)
762 glMatrixMode(GL_PROJECTION)
764 aspect = float(size.GetWidth()) / float(size.GetHeight())
765 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
767 glMatrixMode(GL_MODELVIEW)
769 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
772 if machineCom.machineIsConnected():
773 self.printButton._imageID = 6
774 self.printButton._tooltip = _("Print")
775 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
776 self.printButton._imageID = 2
777 self.printButton._tooltip = _("Toolpath to SD")
779 self.printButton._imageID = 3
780 self.printButton._tooltip = _("Save toolpath")
782 if self._animView is not None:
783 self._viewTarget = self._animView.getPosition()
784 if self._animView.isDone():
785 self._animView = None
786 if self._animZoom is not None:
787 self._zoom = self._animZoom.getPosition()
788 if self._animZoom.isDone():
789 self._animZoom = None
790 if self.viewMode == 'gcode' and self._gcode is not None:
792 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
795 if self._objectShader is None:
796 if opengl.hasShaderSupport():
797 self._objectShader = opengl.GLShader("""
798 varying float light_amount;
802 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
803 gl_FrontColor = gl_Color;
805 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
809 varying float light_amount;
813 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
816 self._objectOverhangShader = opengl.GLShader("""
817 uniform float cosAngle;
818 uniform mat3 rotMatrix;
819 varying float light_amount;
823 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
824 gl_FrontColor = gl_Color;
826 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
828 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
830 light_amount = -10.0;
834 varying float light_amount;
838 if (light_amount == -10.0)
840 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
842 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
846 self._objectLoadShader = opengl.GLShader("""
847 uniform float intensity;
849 varying float light_amount;
853 vec4 tmp = gl_Vertex;
854 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
855 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
856 gl_Position = gl_ModelViewProjectionMatrix * tmp;
857 gl_FrontColor = gl_Color;
859 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
863 uniform float intensity;
864 varying float light_amount;
868 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
871 if self._objectShader == None or not self._objectShader.isValid():
872 self._objectShader = opengl.GLFakeShader()
873 self._objectOverhangShader = opengl.GLFakeShader()
874 self._objectLoadShader = None
876 glTranslate(0,0,-self._zoom)
877 glRotate(-self._pitch, 1,0,0)
878 glRotate(self._yaw, 0,0,1)
879 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
881 self._viewport = glGetIntegerv(GL_VIEWPORT)
882 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
883 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
885 glClearColor(1,1,1,1)
886 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
888 if self.viewMode != 'gcode':
889 for n in xrange(0, len(self._scene.objects())):
890 obj = self._scene.objects()[n]
891 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
892 self._renderObject(obj)
894 if self._mouseX > -1:
896 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
897 if n < len(self._scene.objects()):
898 self._focusObj = self._scene.objects()[n]
900 self._focusObj = None
901 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
902 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
903 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
904 self._mouse3Dpos -= self._viewTarget
907 glTranslate(0,0,-self._zoom)
908 glRotate(-self._pitch, 1,0,0)
909 glRotate(self._yaw, 0,0,1)
910 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
912 if self.viewMode == 'gcode':
913 if self._gcode is not None and self._gcode.layerList is None:
914 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
915 self._gcodeLoadThread.daemon = True
916 self._gcodeLoadThread.start()
917 if self._gcode is not None and self._gcode.layerList is not None:
919 if profile.getMachineSetting('machine_center_is_zero') != 'True':
920 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
922 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
923 for n in xrange(0, drawUpTill):
924 c = 1.0 - float(drawUpTill - n) / 15
926 if len(self._gcodeVBOs) < n + 1:
927 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
928 if time.time() - t > 0.5:
931 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
932 if n == drawUpTill - 1:
933 if len(self._gcodeVBOs[n]) < 9:
934 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
936 self._gcodeVBOs[n][8].render(GL_QUADS)
938 self._gcodeVBOs[n][9].render(GL_QUADS)
940 self._gcodeVBOs[n][10].render(GL_QUADS)
942 self._gcodeVBOs[n][11].render(GL_QUADS)
945 self._gcodeVBOs[n][12].render(GL_QUADS)
946 glColor3f(c/2, c/2, 0.0)
947 self._gcodeVBOs[n][13].render(GL_QUADS)
949 self._gcodeVBOs[n][14].render(GL_QUADS)
950 self._gcodeVBOs[n][15].render(GL_QUADS)
952 self._gcodeVBOs[n][16].render(GL_LINES)
955 self._gcodeVBOs[n][0].render(GL_LINES)
957 self._gcodeVBOs[n][1].render(GL_LINES)
959 self._gcodeVBOs[n][2].render(GL_LINES)
961 self._gcodeVBOs[n][3].render(GL_LINES)
964 self._gcodeVBOs[n][4].render(GL_LINES)
965 glColor3f(c/2, c/2, 0.0)
966 self._gcodeVBOs[n][5].render(GL_LINES)
968 self._gcodeVBOs[n][6].render(GL_LINES)
969 self._gcodeVBOs[n][7].render(GL_LINES)
972 glStencilFunc(GL_ALWAYS, 1, 1)
973 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
975 if self.viewMode == 'overhang':
976 self._objectOverhangShader.bind()
977 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
979 self._objectShader.bind()
980 for obj in self._scene.objects():
981 if obj._loadAnim is not None:
982 if obj._loadAnim.isDone():
987 if self._focusObj == obj:
989 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
992 if self._selectedObj == obj or self._selectedObj is None:
993 #If we want transparent, then first render a solid black model to remove the printer size lines.
994 if self.viewMode == 'transparent':
995 glColor4f(0, 0, 0, 0)
996 self._renderObject(obj)
998 glBlendFunc(GL_ONE, GL_ONE)
999 glDisable(GL_DEPTH_TEST)
1001 if self.viewMode == 'xray':
1002 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1003 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1004 glEnable(GL_STENCIL_TEST)
1006 if self.viewMode == 'overhang':
1007 if self._selectedObj == obj and self.tempMatrix is not None:
1008 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1010 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1012 if not self._scene.checkPlatform(obj):
1013 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1014 self._renderObject(obj)
1016 self._renderObject(obj, brightness)
1017 glDisable(GL_STENCIL_TEST)
1019 glEnable(GL_DEPTH_TEST)
1020 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1022 if self.viewMode == 'xray':
1025 glEnable(GL_STENCIL_TEST)
1026 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1027 glDisable(GL_DEPTH_TEST)
1028 for i in xrange(2, 15, 2):
1029 glStencilFunc(GL_EQUAL, i, 0xFF)
1030 glColor(float(i)/10, float(i)/10, float(i)/5)
1032 glVertex3f(-1000,-1000,-10)
1033 glVertex3f( 1000,-1000,-10)
1034 glVertex3f( 1000, 1000,-10)
1035 glVertex3f(-1000, 1000,-10)
1037 for i in xrange(1, 15, 2):
1038 glStencilFunc(GL_EQUAL, i, 0xFF)
1039 glColor(float(i)/10, 0, 0)
1041 glVertex3f(-1000,-1000,-10)
1042 glVertex3f( 1000,-1000,-10)
1043 glVertex3f( 1000, 1000,-10)
1044 glVertex3f(-1000, 1000,-10)
1047 glDisable(GL_STENCIL_TEST)
1048 glEnable(GL_DEPTH_TEST)
1050 self._objectShader.unbind()
1052 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1054 if self._objectLoadShader is not None:
1055 self._objectLoadShader.bind()
1056 glColor4f(0.2, 0.6, 1.0, 1.0)
1057 for obj in self._scene.objects():
1058 if obj._loadAnim is None:
1060 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1061 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1062 self._renderObject(obj)
1063 self._objectLoadShader.unbind()
1068 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1070 z = self._usbPrintMonitor.getZ()
1071 size = self._machineSize
1072 glColor4ub(255,255,0,128)
1074 glVertex3f(-size[0]/2,-size[1]/2, z)
1075 glVertex3f( size[0]/2,-size[1]/2, z)
1076 glVertex3f( size[0]/2, size[1]/2, z)
1077 glVertex3f(-size[0]/2, size[1]/2, z)
1080 if self.viewMode == 'gcode':
1081 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1082 glDisable(GL_DEPTH_TEST)
1085 glTranslate(0,-4,-10)
1086 glColor4ub(60,60,60,255)
1087 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1090 #Draw the object box-shadow, so you can see where it will collide with other objects.
1091 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1092 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1094 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1096 glEnable(GL_CULL_FACE)
1097 glColor4f(0,0,0,0.12)
1099 glVertex3f(-size[0], size[1], 0.1)
1100 glVertex3f(-size[0], -size[1], 0.1)
1101 glVertex3f( size[0], -size[1], 0.1)
1102 glVertex3f( size[0], size[1], 0.1)
1104 glDisable(GL_CULL_FACE)
1107 #Draw the outline of the selected object, on top of everything else except the GUI.
1108 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1109 glDisable(GL_DEPTH_TEST)
1110 glEnable(GL_CULL_FACE)
1111 glEnable(GL_STENCIL_TEST)
1113 glStencilFunc(GL_EQUAL, 0, 255)
1115 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1117 glColor4f(1,1,1,0.5)
1118 self._renderObject(self._selectedObj)
1119 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1121 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1122 glDisable(GL_STENCIL_TEST)
1123 glDisable(GL_CULL_FACE)
1124 glEnable(GL_DEPTH_TEST)
1126 if self._selectedObj is not None:
1128 pos = self.getObjectCenterPos()
1129 glTranslate(pos[0], pos[1], pos[2])
1132 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1133 glDisable(GL_DEPTH_TEST)
1136 glTranslate(0,-4,-10)
1137 glColor4ub(60,60,60,255)
1138 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1141 def _renderObject(self, obj, brightness = False, addSink = True):
1144 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1146 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1148 if self.tempMatrix is not None and obj == self._selectedObj:
1149 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1150 glMultMatrixf(tempMatrix)
1152 offset = obj.getDrawOffset()
1153 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1155 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1156 glMultMatrixf(tempMatrix)
1159 for m in obj._meshList:
1161 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1163 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1168 def _drawMachine(self):
1169 glEnable(GL_CULL_FACE)
1172 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1174 machine = profile.getMachineSetting('machine_type')
1175 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1176 if machine not in self._platformMesh:
1177 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1179 self._platformMesh[machine] = meshes[0]
1181 self._platformMesh[machine] = None
1182 if machine == 'ultimaker2':
1183 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1185 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1186 glColor4f(1,1,1,0.5)
1187 self._objectShader.bind()
1188 self._renderObject(self._platformMesh[machine], False, False)
1189 self._objectShader.unbind()
1191 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1192 if machine == 'ultimaker2':
1193 if not hasattr(self._platformMesh[machine], 'texture'):
1194 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1195 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1196 glEnable(GL_TEXTURE_2D)
1200 glTranslate(0,150,-5)
1205 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1208 glVertex3f( w, 0, h)
1210 glVertex3f(-w, 0, h)
1212 glVertex3f(-w, 0, 0)
1214 glVertex3f( w, 0, 0)
1217 glVertex3f(-w, d, h)
1219 glVertex3f( w, d, h)
1221 glVertex3f( w, d, 0)
1223 glVertex3f(-w, d, 0)
1225 glDisable(GL_TEXTURE_2D)
1226 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1232 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1233 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1234 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1235 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1236 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1237 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1240 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1241 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1242 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1243 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1244 v4 = [ size[0] / 2, size[1] / 2, 0]
1245 v5 = [ size[0] / 2,-size[1] / 2, 0]
1246 v6 = [-size[0] / 2, size[1] / 2, 0]
1247 v7 = [-size[0] / 2,-size[1] / 2, 0]
1249 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1250 glEnableClientState(GL_VERTEX_ARRAY)
1251 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1253 glColor4ub(5, 171, 231, 64)
1254 glDrawArrays(GL_QUADS, 0, 4)
1255 glColor4ub(5, 171, 231, 96)
1256 glDrawArrays(GL_QUADS, 4, 8)
1257 glColor4ub(5, 171, 231, 128)
1258 glDrawArrays(GL_QUADS, 12, 8)
1259 glDisableClientState(GL_VERTEX_ARRAY)
1261 sx = self._machineSize[0]
1262 sy = self._machineSize[1]
1263 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1264 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1269 x1 = max(min(x1, sx/2), -sx/2)
1270 y1 = max(min(y1, sy/2), -sy/2)
1271 x2 = max(min(x2, sx/2), -sx/2)
1272 y2 = max(min(y2, sy/2), -sy/2)
1273 if (x & 1) == (y & 1):
1274 glColor4ub(5, 171, 231, 127)
1276 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1278 glVertex3f(x1, y1, -0.02)
1279 glVertex3f(x2, y1, -0.02)
1280 glVertex3f(x2, y2, -0.02)
1281 glVertex3f(x1, y2, -0.02)
1285 glDisable(GL_CULL_FACE)
1287 def _generateGCodeVBOs(self, layer):
1289 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1290 if ':' in extrudeType:
1291 extruder = int(extrudeType[extrudeType.find(':')+1:])
1292 extrudeType = extrudeType[0:extrudeType.find(':')]
1295 pointList = numpy.zeros((0,3), numpy.float32)
1297 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1299 a = numpy.concatenate((a[:-1], a[1:]), 1)
1300 a = a.reshape((len(a) * 2, 3))
1301 pointList = numpy.concatenate((pointList, a))
1302 ret.append(opengl.GLVBO(pointList))
1305 def _generateGCodeVBOs2(self, layer):
1306 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1307 filamentArea = math.pi * filamentRadius * filamentRadius
1308 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1311 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1312 if ':' in extrudeType:
1313 extruder = int(extrudeType[extrudeType.find(':')+1:])
1314 extrudeType = extrudeType[0:extrudeType.find(':')]
1317 pointList = numpy.zeros((0,3), numpy.float32)
1319 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1321 if extrudeType == 'FILL':
1324 normal = a[1:] - a[:-1]
1325 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1326 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1329 ePerDist = path['extrusion'][1:] / lens
1331 lineWidth = ePerDist / path['layerThickness'] / 2.0
1333 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1335 normal[:,0] *= lineWidth
1336 normal[:,1] *= lineWidth
1338 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1339 b = numpy.concatenate((b, a[1:] + normal), 1)
1340 b = numpy.concatenate((b, a[1:] - normal), 1)
1341 b = numpy.concatenate((b, a[:-1] - normal), 1)
1342 b = numpy.concatenate((b, a[:-1] + normal), 1)
1343 b = b.reshape((len(b) * 4, 3))
1346 normal2 = normal[:-1] + normal[1:]
1347 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1348 normal2[:,0] /= lens2
1349 normal2[:,1] /= lens2
1350 normal2[:,0] *= lineWidth[:-1]
1351 normal2[:,1] *= lineWidth[:-1]
1353 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1354 c = numpy.concatenate((c, a[1:-1]), 1)
1355 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1356 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1357 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1359 c = numpy.concatenate((c, a[1:-1]), 1)
1360 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1361 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1362 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1364 c = c.reshape((len(c) * 8, 3))
1366 pointList = numpy.concatenate((pointList, b, c))
1368 pointList = numpy.concatenate((pointList, b))
1369 ret.append(opengl.GLVBO(pointList))
1371 pointList = numpy.zeros((0,3), numpy.float32)
1373 if path['type'] == 'move':
1374 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1375 a = numpy.concatenate((a[:-1], a[1:]), 1)
1376 a = a.reshape((len(a) * 2, 3))
1377 pointList = numpy.concatenate((pointList, a))
1378 if path['type'] == 'retract':
1379 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1380 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1381 a = a.reshape((len(a) * 2, 3))
1382 pointList = numpy.concatenate((pointList, a))
1383 ret.append(opengl.GLVBO(pointList))
1387 def getObjectCenterPos(self):
1388 if self._selectedObj is None:
1389 return [0.0, 0.0, 0.0]
1390 pos = self._selectedObj.getPosition()
1391 size = self._selectedObj.getSize()
1392 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1394 def getObjectBoundaryCircle(self):
1395 if self._selectedObj is None:
1397 return self._selectedObj.getBoundaryCircle()
1399 def getObjectSize(self):
1400 if self._selectedObj is None:
1401 return [0.0, 0.0, 0.0]
1402 return self._selectedObj.getSize()
1404 def getObjectMatrix(self):
1405 if self._selectedObj is None:
1406 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1407 return self._selectedObj.getMatrix()
1409 class shaderEditor(wx.Dialog):
1410 def __init__(self, parent, callback, v, f):
1411 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1412 self._callback = callback
1413 s = wx.BoxSizer(wx.VERTICAL)
1415 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1416 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1417 s.Add(self._vertex, 1, flag=wx.EXPAND)
1418 s.Add(self._fragment, 1, flag=wx.EXPAND)
1420 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1421 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1423 self.SetPosition(self.GetParent().GetPosition())
1424 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1427 def OnText(self, e):
1428 self._callback(self._vertex.GetValue(), self._fragment.GetValue())