1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
14 OpenGL.ERROR_CHECKING = False
15 from OpenGL.GLU import *
16 from OpenGL.GL import *
18 from Cura.gui import printWindow
19 from Cura.util import profile
20 from Cura.util import meshLoader
21 from Cura.util import objectScene
22 from Cura.util import resources
23 from Cura.util import sliceEngine
24 from Cura.util import machineCom
25 from Cura.util import removableStorage
26 from Cura.util import gcodeInterpreter
27 from Cura.gui.util import previewTools
28 from Cura.gui.util import opengl
29 from Cura.gui.util import openglGui
30 from Cura.gui.tools import youmagineGui
31 from Cura.gui.tools import imageToMesh
33 class SceneView(openglGui.glGuiPanel):
34 def __init__(self, parent):
35 super(SceneView, self).__init__(parent)
40 self._scene = objectScene.Scene()
43 self._gcodeFilename = None
44 self._gcodeLoadThread = None
45 self._objectShader = None
46 self._objectLoadShader = None
48 self._selectedObj = None
49 self._objColors = [None,None,None,None]
52 self._mouseState = None
53 self._viewTarget = numpy.array([0,0,0], numpy.float32)
56 self._platformMesh = {}
57 self._isSimpleMode = True
58 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
61 self._modelMatrix = None
62 self._projMatrix = None
63 self.tempMatrix = None
65 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
66 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
67 self.printButton.setDisabled(True)
70 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
71 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
72 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
74 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
75 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
77 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
78 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
80 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
81 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
82 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
84 self.rotateToolButton.setExpandArrow(True)
85 self.scaleToolButton.setExpandArrow(True)
86 self.mirrorToolButton.setExpandArrow(True)
88 self.scaleForm = openglGui.glFrame(self, (2, -2))
89 openglGui.glGuiLayoutGrid(self.scaleForm)
90 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
91 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
92 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
93 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
94 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
95 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
96 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
97 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
98 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
99 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
100 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
101 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
102 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
103 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
105 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
106 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
108 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
109 self.youMagineButton.setDisabled(True)
111 self.notification = openglGui.glNotification(self, (0, 0))
113 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
114 self._sceneUpdateTimer = wx.Timer(self)
115 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
116 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
117 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
121 self.updateToolButtons()
122 self.updateProfileToControls()
124 def loadGCodeFile(self, filename):
125 self.OnDeleteAll(None)
126 if self._gcode is not None:
128 for layerVBOlist in self._gcodeVBOs:
129 for vbo in layerVBOlist:
130 self.glReleaseList.append(vbo)
132 self._gcode = gcodeInterpreter.gcode()
133 self._gcodeFilename = filename
134 self.printButton.setBottomText('')
135 self.viewSelection.setValue(4)
136 self.printButton.setDisabled(False)
137 self.youMagineButton.setDisabled(True)
140 def loadSceneFiles(self, filenames):
141 self.youMagineButton.setDisabled(False)
142 #if self.viewSelection.getValue() == 4:
143 # self.viewSelection.setValue(0)
144 # self.OnViewChange()
145 self.loadScene(filenames)
147 def loadFiles(self, filenames):
148 mainWindow = self.GetParent().GetParent().GetParent()
149 # only one GCODE file can be active
150 # so if single gcode file, process this
151 # otherwise ignore all gcode files
153 if len(filenames) == 1:
154 filename = filenames[0]
155 ext = os.path.splitext(filename)[1].lower()
156 if ext == '.g' or ext == '.gcode':
157 gcodeFilename = filename
158 mainWindow.addToModelMRU(filename)
159 if gcodeFilename is not None:
160 self.loadGCodeFile(gcodeFilename)
162 # process directories and special file types
163 # and keep scene files for later processing
165 ignored_types = dict()
166 # use file list as queue
167 # pop first entry for processing and append new files at end
169 filename = filenames.pop(0)
170 if os.path.isdir(filename):
171 # directory: queue all included files and directories
172 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
174 ext = os.path.splitext(filename)[1].lower()
176 profile.loadProfile(filename)
177 mainWindow.addToProfileMRU(filename)
178 elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
179 scene_filenames.append(filename)
180 mainWindow.addToModelMRU(filename)
182 ignored_types[ext] = 1
184 ignored_types = ignored_types.keys()
186 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
187 mainWindow.updateProfileToAllControls()
188 # now process all the scene files
190 self.loadSceneFiles(scene_filenames)
191 self._selectObject(None)
193 newZoom = numpy.max(self._machineSize)
194 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
195 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
197 def showLoadModel(self, button = 1):
199 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)
200 dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
201 if dlg.ShowModal() != wx.ID_OK:
204 filenames = dlg.GetPaths()
206 if len(filenames) < 1:
208 profile.putPreference('lastFile', filenames[0])
209 self.loadFiles(filenames)
211 def showSaveModel(self):
212 if len(self._scene.objects()) < 1:
214 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
215 dlg.SetWildcard(meshLoader.saveWildcardFilter())
216 if dlg.ShowModal() != wx.ID_OK:
219 filename = dlg.GetPath()
221 meshLoader.saveMeshes(filename, self._scene.objects())
223 def OnPrintButton(self, button):
225 if machineCom.machineIsConnected():
226 self.showPrintWindow()
227 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
228 drives = removableStorage.getPossibleSDcardDrives()
230 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))
231 if dlg.ShowModal() != wx.ID_OK:
234 drive = drives[dlg.GetSelection()]
238 filename = self._scene._objectList[0].getName() + '.gcode'
239 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
244 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
245 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
246 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
250 def showPrintWindow(self):
251 if self._gcodeFilename is None:
253 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
254 if self._gcodeFilename == self._slicer.getGCodeFilename():
255 self._slicer.submitSliceInfoOnline()
257 def showSaveGCode(self):
258 if len(self._scene._objectList) < 1:
260 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
261 filename = self._scene._objectList[0].getName() + '.gcode'
262 dlg.SetFilename(filename)
263 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
264 if dlg.ShowModal() != wx.ID_OK:
267 filename = dlg.GetPath()
270 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
272 def _copyFile(self, fileA, fileB, allowEject = False):
274 size = float(os.stat(fileA).st_size)
275 with open(fileA, 'rb') as fsrc:
276 with open(fileB, 'wb') as fdst:
278 buf = fsrc.read(16*1024)
282 self.printButton.setProgressBar(float(fsrc.tell()) / size)
287 self.notification.message("Failed to save")
290 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...'))
292 self.notification.message("Saved as %s" % (fileB))
293 self.printButton.setProgressBar(None)
294 if fileA == self._slicer.getGCodeFilename():
295 self._slicer.submitSliceInfoOnline()
297 def _showSliceLog(self):
298 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
302 def OnToolSelect(self, button):
303 if self.rotateToolButton.getSelected():
304 self.tool = previewTools.toolRotate(self)
305 elif self.scaleToolButton.getSelected():
306 self.tool = previewTools.toolScale(self)
307 elif self.mirrorToolButton.getSelected():
308 self.tool = previewTools.toolNone(self)
310 self.tool = previewTools.toolNone(self)
311 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
312 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
313 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
314 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
315 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
316 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
317 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
318 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
320 def updateToolButtons(self):
321 if self._selectedObj is None:
325 self.rotateToolButton.setHidden(hidden)
326 self.scaleToolButton.setHidden(hidden)
327 self.mirrorToolButton.setHidden(hidden)
329 self.rotateToolButton.setSelected(False)
330 self.scaleToolButton.setSelected(False)
331 self.mirrorToolButton.setSelected(False)
334 def OnViewChange(self):
335 if self.viewSelection.getValue() == 4:
336 self.viewMode = 'gcode'
337 if self._gcode is not None and self._gcode.layerList is not None:
338 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
339 self._selectObject(None)
340 elif self.viewSelection.getValue() == 1:
341 self.viewMode = 'overhang'
342 elif self.viewSelection.getValue() == 2:
343 self.viewMode = 'transparent'
344 elif self.viewSelection.getValue() == 3:
345 self.viewMode = 'xray'
347 self.viewMode = 'normal'
348 self.layerSelect.setHidden(self.viewMode != 'gcode')
351 def OnRotateReset(self, button):
352 if self._selectedObj is None:
354 self._selectedObj.resetRotation()
355 self._scene.pushFree()
356 self._selectObject(self._selectedObj)
359 def OnLayFlat(self, button):
360 if self._selectedObj is None:
362 self._selectedObj.layFlat()
363 self._scene.pushFree()
364 self._selectObject(self._selectedObj)
367 def OnScaleReset(self, button):
368 if self._selectedObj is None:
370 self._selectedObj.resetScale()
371 self._selectObject(self._selectedObj)
372 self.updateProfileToControls()
375 def OnScaleMax(self, button):
376 if self._selectedObj is None:
378 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
379 self._scene.pushFree()
380 self._selectObject(self._selectedObj)
381 self.updateProfileToControls()
384 def OnMirror(self, axis):
385 if self._selectedObj is None:
387 self._selectedObj.mirror(axis)
390 def OnScaleEntry(self, value, axis):
391 if self._selectedObj is None:
397 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
398 self.updateProfileToControls()
399 self._scene.pushFree()
400 self._selectObject(self._selectedObj)
403 def OnScaleEntryMM(self, value, axis):
404 if self._selectedObj is None:
410 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
411 self.updateProfileToControls()
412 self._scene.pushFree()
413 self._selectObject(self._selectedObj)
416 def OnDeleteAll(self, e):
417 while len(self._scene.objects()) > 0:
418 self._deleteObject(self._scene.objects()[0])
419 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
421 def OnMultiply(self, e):
422 if self._focusObj is None:
425 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
426 if dlg.ShowModal() != wx.ID_OK:
435 self._scene.add(newObj)
436 self._scene.centerAll()
437 if not self._scene.checkPlatform(newObj):
442 self.notification.message("Could not create more then %d items" % (n - 1))
443 self._scene.remove(newObj)
444 self._scene.centerAll()
447 def OnSplitObject(self, e):
448 if self._focusObj is None:
450 self._scene.remove(self._focusObj)
451 for obj in self._focusObj.split(self._splitCallback):
452 if numpy.max(obj.getSize()) > 2.0:
454 self._scene.centerAll()
455 self._selectObject(None)
458 def OnCenter(self, e):
459 if self._focusObj is None:
461 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
462 self._scene.pushFree()
464 def _splitCallback(self, progress):
467 def OnMergeObjects(self, e):
468 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
469 if len(self._scene.objects()) == 2:
470 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
473 self._scene.merge(self._selectedObj, self._focusObj)
476 def sceneUpdated(self):
477 self._sceneUpdateTimer.Start(500, True)
478 self._slicer.abortSlicer()
479 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
482 def _onRunSlicer(self, e):
483 if self._isSimpleMode:
484 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
485 self._slicer.runSlicer(self._scene)
486 if self._isSimpleMode:
487 profile.resetTempOverride()
489 def _updateSliceProgress(self, progressValue, ready):
491 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
493 self.printButton.setDisabled(not ready)
494 if progressValue >= 0.0:
495 self.printButton.setProgressBar(progressValue)
497 self.printButton.setProgressBar(None)
498 if self._gcode is not None:
500 for layerVBOlist in self._gcodeVBOs:
501 for vbo in layerVBOlist:
502 self.glReleaseList.append(vbo)
505 self.printButton.setProgressBar(None)
506 cost = self._slicer.getFilamentCost()
508 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
510 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
511 self._gcode = gcodeInterpreter.gcode()
512 self._gcodeFilename = self._slicer.getGCodeFilename()
514 self.printButton.setBottomText('')
517 def _loadGCode(self):
518 self._gcode.progressCallback = self._gcodeLoadCallback
519 self._gcode.load(self._gcodeFilename)
521 def _gcodeLoadCallback(self, progress):
522 if self._gcode is None:
524 if len(self._gcode.layerList) % 15 == 0:
526 if self._gcode is None:
528 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
529 if self.viewMode == 'gcode':
533 def loadScene(self, fileList):
534 for filename in fileList:
536 ext = os.path.splitext(filename)[1].lower()
537 if ext in imageToMesh.supportedExtensions():
538 imageToMesh.convertImageDialog(self, filename).Show()
541 objList = meshLoader.loadMeshes(filename)
543 traceback.print_exc()
546 if self._objectLoadShader is not None:
547 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
551 self._scene.centerAll()
552 self._selectObject(obj)
553 if obj.getScale()[0] < 1.0:
554 self.notification.message("Warning: Object scaled down.")
557 def _deleteObject(self, obj):
558 if obj == self._selectedObj:
559 self._selectObject(None)
560 if obj == self._focusObj:
561 self._focusObj = None
562 self._scene.remove(obj)
563 for m in obj._meshList:
564 if m.vbo is not None and m.vbo.decRef():
565 self.glReleaseList.append(m.vbo)
570 def _selectObject(self, obj, zoom = True):
571 if obj != self._selectedObj:
572 self._selectedObj = obj
573 self.updateProfileToControls()
574 self.updateToolButtons()
575 if zoom and obj is not None:
576 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
577 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
578 newZoom = obj.getBoundaryCircle() * 6
579 if newZoom > numpy.max(self._machineSize) * 3:
580 newZoom = numpy.max(self._machineSize) * 3
581 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
583 def updateProfileToControls(self):
584 oldSimpleMode = self._isSimpleMode
585 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
586 if self._isSimpleMode != oldSimpleMode:
587 self._scene.arrangeAll()
589 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
590 self._objColors[0] = profile.getPreferenceColour('model_colour')
591 self._objColors[1] = profile.getPreferenceColour('model_colour2')
592 self._objColors[2] = profile.getPreferenceColour('model_colour3')
593 self._objColors[3] = profile.getPreferenceColour('model_colour4')
594 self._scene.setMachineSize(self._machineSize)
595 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
596 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'))
598 if self._selectedObj is not None:
599 scale = self._selectedObj.getScale()
600 size = self._selectedObj.getSize()
601 self.scaleXctrl.setValue(round(scale[0], 2))
602 self.scaleYctrl.setValue(round(scale[1], 2))
603 self.scaleZctrl.setValue(round(scale[2], 2))
604 self.scaleXmmctrl.setValue(round(size[0], 2))
605 self.scaleYmmctrl.setValue(round(size[1], 2))
606 self.scaleZmmctrl.setValue(round(size[2], 2))
608 def OnKeyChar(self, keyCode):
609 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
610 if self._selectedObj is not None:
611 self._deleteObject(self._selectedObj)
613 if keyCode == wx.WXK_UP:
614 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
616 elif keyCode == wx.WXK_DOWN:
617 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
619 elif keyCode == wx.WXK_PAGEUP:
620 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
622 elif keyCode == wx.WXK_PAGEDOWN:
623 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
626 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
627 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
628 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
629 from collections import defaultdict
630 from gc import get_objects
631 self._beforeLeakTest = defaultdict(int)
632 for i in get_objects():
633 self._beforeLeakTest[type(i)] += 1
634 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
635 from collections import defaultdict
636 from gc import get_objects
637 self._afterLeakTest = defaultdict(int)
638 for i in get_objects():
639 self._afterLeakTest[type(i)] += 1
640 for k in self._afterLeakTest:
641 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
642 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
644 def ShaderUpdate(self, v, f):
645 s = opengl.GLShader(v, f)
647 self._objectLoadShader.release()
648 self._objectLoadShader = s
649 for obj in self._scene.objects():
650 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
653 def OnMouseDown(self,e):
654 self._mouseX = e.GetX()
655 self._mouseY = e.GetY()
656 self._mouseClick3DPos = self._mouse3Dpos
657 self._mouseClickFocus = self._focusObj
659 self._mouseState = 'doubleClick'
661 self._mouseState = 'dragOrClick'
662 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
663 p0 -= self.getObjectCenterPos() - self._viewTarget
664 p1 -= self.getObjectCenterPos() - self._viewTarget
665 if self.tool.OnDragStart(p0, p1):
666 self._mouseState = 'tool'
667 if self._mouseState == 'dragOrClick':
668 if e.GetButton() == 1:
669 if self._focusObj is not None:
670 self._selectObject(self._focusObj, False)
673 def OnMouseUp(self, e):
674 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
676 if self._mouseState == 'dragOrClick':
677 if e.GetButton() == 1:
678 self._selectObject(self._focusObj)
679 if e.GetButton() == 3:
681 if self._focusObj is not None:
682 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
683 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
684 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
685 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
686 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:
687 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
688 if len(self._scene.objects()) > 0:
689 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
690 if menu.MenuItemCount > 0:
693 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
694 self._scene.pushFree()
696 elif self._mouseState == 'tool':
697 if self.tempMatrix is not None and self._selectedObj is not None:
698 self._selectedObj.applyMatrix(self.tempMatrix)
699 self._scene.pushFree()
700 self._selectObject(self._selectedObj)
701 self.tempMatrix = None
702 self.tool.OnDragEnd()
704 self._mouseState = None
706 def OnMouseMotion(self,e):
707 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
708 p0 -= self.getObjectCenterPos() - self._viewTarget
709 p1 -= self.getObjectCenterPos() - self._viewTarget
711 if e.Dragging() and self._mouseState is not None:
712 if self._mouseState == 'tool':
713 self.tool.OnDrag(p0, p1)
714 elif not e.LeftIsDown() and e.RightIsDown():
715 self._mouseState = 'drag'
716 if wx.GetKeyState(wx.WXK_SHIFT):
717 a = math.cos(math.radians(self._yaw)) / 3.0
718 b = math.sin(math.radians(self._yaw)) / 3.0
719 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
720 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
721 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
722 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
724 self._yaw += e.GetX() - self._mouseX
725 self._pitch -= e.GetY() - self._mouseY
726 if self._pitch > 170:
730 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
731 self._mouseState = 'drag'
732 self._zoom += e.GetY() - self._mouseY
735 if self._zoom > numpy.max(self._machineSize) * 3:
736 self._zoom = numpy.max(self._machineSize) * 3
737 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
738 self._mouseState = 'dragObject'
739 z = max(0, self._mouseClick3DPos[2])
740 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
741 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
746 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
747 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
748 diff = cursorZ1 - cursorZ0
749 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
750 if not e.Dragging() or self._mouseState != 'tool':
751 self.tool.OnMouseMove(p0, p1)
753 self._mouseX = e.GetX()
754 self._mouseY = e.GetY()
756 def OnMouseWheel(self, e):
757 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
758 delta = max(min(delta,4),-4)
759 self._zoom *= 1.0 - delta / 10.0
762 if self._zoom > numpy.max(self._machineSize) * 3:
763 self._zoom = numpy.max(self._machineSize) * 3
766 def OnMouseLeave(self, e):
770 def getMouseRay(self, x, y):
771 if self._viewport is None:
772 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
773 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
774 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
775 p0 -= self._viewTarget
776 p1 -= self._viewTarget
779 def _init3DView(self):
780 # set viewing projection
781 size = self.GetSize()
782 glViewport(0, 0, size.GetWidth(), size.GetHeight())
785 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
787 glDisable(GL_RESCALE_NORMAL)
788 glDisable(GL_LIGHTING)
790 glEnable(GL_DEPTH_TEST)
791 glDisable(GL_CULL_FACE)
793 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
795 glClearColor(0.8, 0.8, 0.8, 1.0)
799 glMatrixMode(GL_PROJECTION)
801 aspect = float(size.GetWidth()) / float(size.GetHeight())
802 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
804 glMatrixMode(GL_MODELVIEW)
806 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
809 if machineCom.machineIsConnected():
810 self.printButton._imageID = 6
811 self.printButton._tooltip = _("Print")
812 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
813 self.printButton._imageID = 2
814 self.printButton._tooltip = _("Toolpath to SD")
816 self.printButton._imageID = 3
817 self.printButton._tooltip = _("Save toolpath")
819 if self._animView is not None:
820 self._viewTarget = self._animView.getPosition()
821 if self._animView.isDone():
822 self._animView = None
823 if self._animZoom is not None:
824 self._zoom = self._animZoom.getPosition()
825 if self._animZoom.isDone():
826 self._animZoom = None
827 if self.viewMode == 'gcode' and self._gcode is not None:
829 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
832 if self._objectShader is None:
833 if opengl.hasShaderSupport():
834 self._objectShader = opengl.GLShader("""
835 varying float light_amount;
839 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
840 gl_FrontColor = gl_Color;
842 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
846 varying float light_amount;
850 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
853 self._objectOverhangShader = opengl.GLShader("""
854 uniform float cosAngle;
855 uniform mat3 rotMatrix;
856 varying float light_amount;
860 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
861 gl_FrontColor = gl_Color;
863 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
865 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
867 light_amount = -10.0;
871 varying float light_amount;
875 if (light_amount == -10.0)
877 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
879 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
883 self._objectLoadShader = opengl.GLShader("""
884 uniform float intensity;
886 varying float light_amount;
890 vec4 tmp = gl_Vertex;
891 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
892 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
893 gl_Position = gl_ModelViewProjectionMatrix * tmp;
894 gl_FrontColor = gl_Color;
896 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
900 uniform float intensity;
901 varying float light_amount;
905 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
908 if self._objectShader is None or not self._objectShader.isValid():
909 self._objectShader = opengl.GLFakeShader()
910 self._objectOverhangShader = opengl.GLFakeShader()
911 self._objectLoadShader = None
913 glTranslate(0,0,-self._zoom)
914 glRotate(-self._pitch, 1,0,0)
915 glRotate(self._yaw, 0,0,1)
916 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
918 self._viewport = glGetIntegerv(GL_VIEWPORT)
919 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
920 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
922 glClearColor(1,1,1,1)
923 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
925 if self.viewMode != 'gcode':
926 for n in xrange(0, len(self._scene.objects())):
927 obj = self._scene.objects()[n]
928 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
929 self._renderObject(obj)
931 if self._mouseX > -1:
933 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
934 if n < len(self._scene.objects()):
935 self._focusObj = self._scene.objects()[n]
937 self._focusObj = None
938 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
939 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
940 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
941 self._mouse3Dpos -= self._viewTarget
944 glTranslate(0,0,-self._zoom)
945 glRotate(-self._pitch, 1,0,0)
946 glRotate(self._yaw, 0,0,1)
947 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
949 if self.viewMode == 'gcode':
950 if self._gcode is not None and self._gcode.layerList is None:
951 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
952 self._gcodeLoadThread.daemon = True
953 self._gcodeLoadThread.start()
954 if self._gcode is not None and self._gcode.layerList is not None:
956 if profile.getMachineSetting('machine_center_is_zero') != 'True':
957 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
959 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
960 for n in xrange(0, drawUpTill):
961 c = 1.0 - float(drawUpTill - n) / 15
963 if len(self._gcodeVBOs) < n + 1:
964 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
965 if time.time() - t > 0.5:
968 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
969 if n == drawUpTill - 1:
970 if len(self._gcodeVBOs[n]) < 9:
971 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
973 self._gcodeVBOs[n][8].render(GL_QUADS)
975 self._gcodeVBOs[n][9].render(GL_QUADS)
977 self._gcodeVBOs[n][10].render(GL_QUADS)
979 self._gcodeVBOs[n][11].render(GL_QUADS)
982 self._gcodeVBOs[n][12].render(GL_QUADS)
983 glColor3f(c/2, c/2, 0.0)
984 self._gcodeVBOs[n][13].render(GL_QUADS)
986 self._gcodeVBOs[n][14].render(GL_QUADS)
987 self._gcodeVBOs[n][15].render(GL_QUADS)
989 self._gcodeVBOs[n][16].render(GL_LINES)
992 self._gcodeVBOs[n][0].render(GL_LINES)
994 self._gcodeVBOs[n][1].render(GL_LINES)
996 self._gcodeVBOs[n][2].render(GL_LINES)
998 self._gcodeVBOs[n][3].render(GL_LINES)
1001 self._gcodeVBOs[n][4].render(GL_LINES)
1002 glColor3f(c/2, c/2, 0.0)
1003 self._gcodeVBOs[n][5].render(GL_LINES)
1005 self._gcodeVBOs[n][6].render(GL_LINES)
1006 self._gcodeVBOs[n][7].render(GL_LINES)
1009 glStencilFunc(GL_ALWAYS, 1, 1)
1010 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1012 if self.viewMode == 'overhang':
1013 self._objectOverhangShader.bind()
1014 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1016 self._objectShader.bind()
1017 for obj in self._scene.objects():
1018 if obj._loadAnim is not None:
1019 if obj._loadAnim.isDone():
1020 obj._loadAnim = None
1024 if self._focusObj == obj:
1026 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1029 if self._selectedObj == obj or self._selectedObj is None:
1030 #If we want transparent, then first render a solid black model to remove the printer size lines.
1031 if self.viewMode == 'transparent':
1032 glColor4f(0, 0, 0, 0)
1033 self._renderObject(obj)
1035 glBlendFunc(GL_ONE, GL_ONE)
1036 glDisable(GL_DEPTH_TEST)
1038 if self.viewMode == 'xray':
1039 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1040 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1041 glEnable(GL_STENCIL_TEST)
1043 if self.viewMode == 'overhang':
1044 if self._selectedObj == obj and self.tempMatrix is not None:
1045 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1047 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1049 if not self._scene.checkPlatform(obj):
1050 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1051 self._renderObject(obj)
1053 self._renderObject(obj, brightness)
1054 glDisable(GL_STENCIL_TEST)
1056 glEnable(GL_DEPTH_TEST)
1057 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1059 if self.viewMode == 'xray':
1062 glEnable(GL_STENCIL_TEST)
1063 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1064 glDisable(GL_DEPTH_TEST)
1065 for i in xrange(2, 15, 2):
1066 glStencilFunc(GL_EQUAL, i, 0xFF)
1067 glColor(float(i)/10, float(i)/10, float(i)/5)
1069 glVertex3f(-1000,-1000,-10)
1070 glVertex3f( 1000,-1000,-10)
1071 glVertex3f( 1000, 1000,-10)
1072 glVertex3f(-1000, 1000,-10)
1074 for i in xrange(1, 15, 2):
1075 glStencilFunc(GL_EQUAL, i, 0xFF)
1076 glColor(float(i)/10, 0, 0)
1078 glVertex3f(-1000,-1000,-10)
1079 glVertex3f( 1000,-1000,-10)
1080 glVertex3f( 1000, 1000,-10)
1081 glVertex3f(-1000, 1000,-10)
1084 glDisable(GL_STENCIL_TEST)
1085 glEnable(GL_DEPTH_TEST)
1087 self._objectShader.unbind()
1089 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1091 if self._objectLoadShader is not None:
1092 self._objectLoadShader.bind()
1093 glColor4f(0.2, 0.6, 1.0, 1.0)
1094 for obj in self._scene.objects():
1095 if obj._loadAnim is None:
1097 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1098 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1099 self._renderObject(obj)
1100 self._objectLoadShader.unbind()
1105 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1107 z = self._usbPrintMonitor.getZ()
1108 size = self._machineSize
1109 glColor4ub(255,255,0,128)
1111 glVertex3f(-size[0]/2,-size[1]/2, z)
1112 glVertex3f( size[0]/2,-size[1]/2, z)
1113 glVertex3f( size[0]/2, size[1]/2, z)
1114 glVertex3f(-size[0]/2, size[1]/2, z)
1117 if self.viewMode == 'gcode':
1118 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1119 glDisable(GL_DEPTH_TEST)
1122 glTranslate(0,-4,-10)
1123 glColor4ub(60,60,60,255)
1124 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1127 #Draw the object box-shadow, so you can see where it will collide with other objects.
1128 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1129 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1131 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1133 glEnable(GL_CULL_FACE)
1134 glColor4f(0,0,0,0.12)
1136 glVertex3f(-size[0], size[1], 0.1)
1137 glVertex3f(-size[0], -size[1], 0.1)
1138 glVertex3f( size[0], -size[1], 0.1)
1139 glVertex3f( size[0], size[1], 0.1)
1141 glDisable(GL_CULL_FACE)
1144 #Draw the outline of the selected object, on top of everything else except the GUI.
1145 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1146 glDisable(GL_DEPTH_TEST)
1147 glEnable(GL_CULL_FACE)
1148 glEnable(GL_STENCIL_TEST)
1150 glStencilFunc(GL_EQUAL, 0, 255)
1152 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1154 glColor4f(1,1,1,0.5)
1155 self._renderObject(self._selectedObj)
1156 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1158 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1159 glDisable(GL_STENCIL_TEST)
1160 glDisable(GL_CULL_FACE)
1161 glEnable(GL_DEPTH_TEST)
1163 if self._selectedObj is not None:
1165 pos = self.getObjectCenterPos()
1166 glTranslate(pos[0], pos[1], pos[2])
1169 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1170 glDisable(GL_DEPTH_TEST)
1173 glTranslate(0,-4,-10)
1174 glColor4ub(60,60,60,255)
1175 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1178 def _renderObject(self, obj, brightness = False, addSink = True):
1181 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1183 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1185 if self.tempMatrix is not None and obj == self._selectedObj:
1186 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1187 glMultMatrixf(tempMatrix)
1189 offset = obj.getDrawOffset()
1190 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1192 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1193 glMultMatrixf(tempMatrix)
1196 for m in obj._meshList:
1198 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1200 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1205 def _drawMachine(self):
1206 glEnable(GL_CULL_FACE)
1209 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1211 machine = profile.getMachineSetting('machine_type')
1212 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1213 if machine not in self._platformMesh:
1214 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1216 self._platformMesh[machine] = meshes[0]
1218 self._platformMesh[machine] = None
1219 if machine == 'ultimaker2':
1220 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1222 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1223 glColor4f(1,1,1,0.5)
1224 self._objectShader.bind()
1225 self._renderObject(self._platformMesh[machine], False, False)
1226 self._objectShader.unbind()
1228 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1229 if machine == 'ultimaker2':
1230 if not hasattr(self._platformMesh[machine], 'texture'):
1231 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1232 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1233 glEnable(GL_TEXTURE_2D)
1237 glTranslate(0,150,-5)
1242 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1245 glVertex3f( w, 0, h)
1247 glVertex3f(-w, 0, h)
1249 glVertex3f(-w, 0, 0)
1251 glVertex3f( w, 0, 0)
1254 glVertex3f(-w, d, h)
1256 glVertex3f( w, d, h)
1258 glVertex3f( w, d, 0)
1260 glVertex3f(-w, d, 0)
1262 glDisable(GL_TEXTURE_2D)
1263 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1269 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1270 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1271 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1272 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1273 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1274 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1277 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1278 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1279 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1280 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1281 v4 = [ size[0] / 2, size[1] / 2, 0]
1282 v5 = [ size[0] / 2,-size[1] / 2, 0]
1283 v6 = [-size[0] / 2, size[1] / 2, 0]
1284 v7 = [-size[0] / 2,-size[1] / 2, 0]
1286 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1287 glEnableClientState(GL_VERTEX_ARRAY)
1288 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1290 glColor4ub(5, 171, 231, 64)
1291 glDrawArrays(GL_QUADS, 0, 4)
1292 glColor4ub(5, 171, 231, 96)
1293 glDrawArrays(GL_QUADS, 4, 8)
1294 glColor4ub(5, 171, 231, 128)
1295 glDrawArrays(GL_QUADS, 12, 8)
1296 glDisableClientState(GL_VERTEX_ARRAY)
1298 sx = self._machineSize[0]
1299 sy = self._machineSize[1]
1300 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1301 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1306 x1 = max(min(x1, sx/2), -sx/2)
1307 y1 = max(min(y1, sy/2), -sy/2)
1308 x2 = max(min(x2, sx/2), -sx/2)
1309 y2 = max(min(y2, sy/2), -sy/2)
1310 if (x & 1) == (y & 1):
1311 glColor4ub(5, 171, 231, 127)
1313 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1315 glVertex3f(x1, y1, -0.02)
1316 glVertex3f(x2, y1, -0.02)
1317 glVertex3f(x2, y2, -0.02)
1318 glVertex3f(x1, y2, -0.02)
1322 glDisable(GL_CULL_FACE)
1324 def _generateGCodeVBOs(self, layer):
1326 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1327 if ':' in extrudeType:
1328 extruder = int(extrudeType[extrudeType.find(':')+1:])
1329 extrudeType = extrudeType[0:extrudeType.find(':')]
1332 pointList = numpy.zeros((0,3), numpy.float32)
1334 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1336 a = numpy.concatenate((a[:-1], a[1:]), 1)
1337 a = a.reshape((len(a) * 2, 3))
1338 pointList = numpy.concatenate((pointList, a))
1339 ret.append(opengl.GLVBO(pointList))
1342 def _generateGCodeVBOs2(self, layer):
1343 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1344 filamentArea = math.pi * filamentRadius * filamentRadius
1345 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1348 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1349 if ':' in extrudeType:
1350 extruder = int(extrudeType[extrudeType.find(':')+1:])
1351 extrudeType = extrudeType[0:extrudeType.find(':')]
1354 pointList = numpy.zeros((0,3), numpy.float32)
1356 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1358 if extrudeType == 'FILL':
1361 normal = a[1:] - a[:-1]
1362 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1363 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1366 ePerDist = path['extrusion'][1:] / lens
1368 lineWidth = ePerDist / path['layerThickness'] / 2.0
1370 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1372 normal[:,0] *= lineWidth
1373 normal[:,1] *= lineWidth
1375 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1376 b = numpy.concatenate((b, a[1:] + normal), 1)
1377 b = numpy.concatenate((b, a[1:] - normal), 1)
1378 b = numpy.concatenate((b, a[:-1] - normal), 1)
1379 b = numpy.concatenate((b, a[:-1] + normal), 1)
1380 b = b.reshape((len(b) * 4, 3))
1383 normal2 = normal[:-1] + normal[1:]
1384 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1385 normal2[:,0] /= lens2
1386 normal2[:,1] /= lens2
1387 normal2[:,0] *= lineWidth[:-1]
1388 normal2[:,1] *= lineWidth[:-1]
1390 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1391 c = numpy.concatenate((c, a[1:-1]), 1)
1392 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1393 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1394 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1396 c = numpy.concatenate((c, a[1:-1]), 1)
1397 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1398 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1399 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1401 c = c.reshape((len(c) * 8, 3))
1403 pointList = numpy.concatenate((pointList, b, c))
1405 pointList = numpy.concatenate((pointList, b))
1406 ret.append(opengl.GLVBO(pointList))
1408 pointList = numpy.zeros((0,3), numpy.float32)
1410 if path['type'] == 'move':
1411 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1412 a = numpy.concatenate((a[:-1], a[1:]), 1)
1413 a = a.reshape((len(a) * 2, 3))
1414 pointList = numpy.concatenate((pointList, a))
1415 if path['type'] == 'retract':
1416 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1417 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1418 a = a.reshape((len(a) * 2, 3))
1419 pointList = numpy.concatenate((pointList, a))
1420 ret.append(opengl.GLVBO(pointList))
1424 def getObjectCenterPos(self):
1425 if self._selectedObj is None:
1426 return [0.0, 0.0, 0.0]
1427 pos = self._selectedObj.getPosition()
1428 size = self._selectedObj.getSize()
1429 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1431 def getObjectBoundaryCircle(self):
1432 if self._selectedObj is None:
1434 return self._selectedObj.getBoundaryCircle()
1436 def getObjectSize(self):
1437 if self._selectedObj is None:
1438 return [0.0, 0.0, 0.0]
1439 return self._selectedObj.getSize()
1441 def getObjectMatrix(self):
1442 if self._selectedObj is None:
1443 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1444 return self._selectedObj.getMatrix()
1446 class shaderEditor(wx.Dialog):
1447 def __init__(self, parent, callback, v, f):
1448 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1449 self._callback = callback
1450 s = wx.BoxSizer(wx.VERTICAL)
1452 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1453 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1454 s.Add(self._vertex, 1, flag=wx.EXPAND)
1455 s.Add(self._fragment, 1, flag=wx.EXPAND)
1457 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1458 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1460 self.SetPosition(self.GetParent().GetPosition())
1461 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1464 def OnText(self, e):
1465 self._callback(self._vertex.GetValue(), self._fragment.GetValue())