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 reloadScene(self, e):
198 # Copy the list before DeleteAll clears it
200 for obj in self._scene.objects():
201 fileList.append(obj.getOriginFilename())
202 self.OnDeleteAll(None)
203 self.loadScene(fileList)
205 def showLoadModel(self, button = 1):
207 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)
208 dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
209 if dlg.ShowModal() != wx.ID_OK:
212 filenames = dlg.GetPaths()
214 if len(filenames) < 1:
216 profile.putPreference('lastFile', filenames[0])
217 self.loadFiles(filenames)
219 def showSaveModel(self):
220 if len(self._scene.objects()) < 1:
222 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
223 dlg.SetWildcard(meshLoader.saveWildcardFilter())
224 if dlg.ShowModal() != wx.ID_OK:
227 filename = dlg.GetPath()
229 meshLoader.saveMeshes(filename, self._scene.objects())
231 def OnPrintButton(self, button):
233 if machineCom.machineIsConnected():
234 self.showPrintWindow()
235 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
236 drives = removableStorage.getPossibleSDcardDrives()
238 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))
239 if dlg.ShowModal() != wx.ID_OK:
242 drive = drives[dlg.GetSelection()]
246 filename = self._scene._objectList[0].getName() + '.gcode'
247 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
252 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
253 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
254 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
258 def showPrintWindow(self):
259 if self._gcodeFilename is None:
261 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
262 if self._gcodeFilename == self._slicer.getGCodeFilename():
263 self._slicer.submitSliceInfoOnline()
265 def showSaveGCode(self):
266 if len(self._scene._objectList) < 1:
268 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
269 filename = self._scene._objectList[0].getName() + '.gcode'
270 dlg.SetFilename(filename)
271 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
272 if dlg.ShowModal() != wx.ID_OK:
275 filename = dlg.GetPath()
278 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
280 def _copyFile(self, fileA, fileB, allowEject = False):
282 size = float(os.stat(fileA).st_size)
283 with open(fileA, 'rb') as fsrc:
284 with open(fileB, 'wb') as fdst:
286 buf = fsrc.read(16*1024)
290 self.printButton.setProgressBar(float(fsrc.tell()) / size)
295 self.notification.message("Failed to save")
298 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...'))
300 self.notification.message("Saved as %s" % (fileB))
301 self.printButton.setProgressBar(None)
302 if fileA == self._slicer.getGCodeFilename():
303 self._slicer.submitSliceInfoOnline()
305 def _showSliceLog(self):
306 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
310 def OnToolSelect(self, button):
311 if self.rotateToolButton.getSelected():
312 self.tool = previewTools.toolRotate(self)
313 elif self.scaleToolButton.getSelected():
314 self.tool = previewTools.toolScale(self)
315 elif self.mirrorToolButton.getSelected():
316 self.tool = previewTools.toolNone(self)
318 self.tool = previewTools.toolNone(self)
319 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
320 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
321 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
322 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
323 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
324 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
325 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
326 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
328 def updateToolButtons(self):
329 if self._selectedObj is None:
333 self.rotateToolButton.setHidden(hidden)
334 self.scaleToolButton.setHidden(hidden)
335 self.mirrorToolButton.setHidden(hidden)
337 self.rotateToolButton.setSelected(False)
338 self.scaleToolButton.setSelected(False)
339 self.mirrorToolButton.setSelected(False)
342 def OnViewChange(self):
343 if self.viewSelection.getValue() == 4:
344 self.viewMode = 'gcode'
345 if self._gcode is not None and self._gcode.layerList is not None:
346 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
347 self._selectObject(None)
348 elif self.viewSelection.getValue() == 1:
349 self.viewMode = 'overhang'
350 elif self.viewSelection.getValue() == 2:
351 self.viewMode = 'transparent'
352 elif self.viewSelection.getValue() == 3:
353 self.viewMode = 'xray'
355 self.viewMode = 'normal'
356 self.layerSelect.setHidden(self.viewMode != 'gcode')
359 def OnRotateReset(self, button):
360 if self._selectedObj is None:
362 self._selectedObj.resetRotation()
363 self._scene.pushFree()
364 self._selectObject(self._selectedObj)
367 def OnLayFlat(self, button):
368 if self._selectedObj is None:
370 self._selectedObj.layFlat()
371 self._scene.pushFree()
372 self._selectObject(self._selectedObj)
375 def OnScaleReset(self, button):
376 if self._selectedObj is None:
378 self._selectedObj.resetScale()
379 self._selectObject(self._selectedObj)
380 self.updateProfileToControls()
383 def OnScaleMax(self, button):
384 if self._selectedObj is None:
386 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
387 self._scene.pushFree()
388 self._selectObject(self._selectedObj)
389 self.updateProfileToControls()
392 def OnMirror(self, axis):
393 if self._selectedObj is None:
395 self._selectedObj.mirror(axis)
398 def OnScaleEntry(self, value, axis):
399 if self._selectedObj is None:
405 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
406 self.updateProfileToControls()
407 self._scene.pushFree()
408 self._selectObject(self._selectedObj)
411 def OnScaleEntryMM(self, value, axis):
412 if self._selectedObj is None:
418 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
419 self.updateProfileToControls()
420 self._scene.pushFree()
421 self._selectObject(self._selectedObj)
424 def OnDeleteAll(self, e):
425 while len(self._scene.objects()) > 0:
426 self._deleteObject(self._scene.objects()[0])
427 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
429 def OnMultiply(self, e):
430 if self._focusObj is None:
433 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
434 if dlg.ShowModal() != wx.ID_OK:
443 self._scene.add(newObj)
444 self._scene.centerAll()
445 if not self._scene.checkPlatform(newObj):
450 self.notification.message("Could not create more then %d items" % (n - 1))
451 self._scene.remove(newObj)
452 self._scene.centerAll()
455 def OnSplitObject(self, e):
456 if self._focusObj is None:
458 self._scene.remove(self._focusObj)
459 for obj in self._focusObj.split(self._splitCallback):
460 if numpy.max(obj.getSize()) > 2.0:
462 self._scene.centerAll()
463 self._selectObject(None)
466 def OnCenter(self, e):
467 if self._focusObj is None:
469 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
470 self._scene.pushFree()
472 def _splitCallback(self, progress):
475 def OnMergeObjects(self, e):
476 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
477 if len(self._scene.objects()) == 2:
478 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
481 self._scene.merge(self._selectedObj, self._focusObj)
484 def sceneUpdated(self):
485 self._sceneUpdateTimer.Start(500, True)
486 self._slicer.abortSlicer()
487 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
490 def _onRunSlicer(self, e):
491 if self._isSimpleMode:
492 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
493 self._slicer.runSlicer(self._scene)
494 if self._isSimpleMode:
495 profile.resetTempOverride()
497 def _updateSliceProgress(self, progressValue, ready):
499 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
501 self.printButton.setDisabled(not ready)
502 if progressValue >= 0.0:
503 self.printButton.setProgressBar(progressValue)
505 self.printButton.setProgressBar(None)
506 if self._gcode is not None:
508 for layerVBOlist in self._gcodeVBOs:
509 for vbo in layerVBOlist:
510 self.glReleaseList.append(vbo)
513 self.printButton.setProgressBar(None)
514 cost = self._slicer.getFilamentCost()
516 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
518 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
519 self._gcode = gcodeInterpreter.gcode()
520 self._gcodeFilename = self._slicer.getGCodeFilename()
522 self.printButton.setBottomText('')
525 def _loadGCode(self):
526 self._gcode.progressCallback = self._gcodeLoadCallback
527 self._gcode.load(self._gcodeFilename)
529 def _gcodeLoadCallback(self, progress):
530 if self._gcode is None:
532 if len(self._gcode.layerList) % 15 == 0:
534 if self._gcode is None:
536 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
537 if self.viewMode == 'gcode':
541 def loadScene(self, fileList):
542 for filename in fileList:
544 ext = os.path.splitext(filename)[1].lower()
545 if ext in imageToMesh.supportedExtensions():
546 imageToMesh.convertImageDialog(self, filename).Show()
549 objList = meshLoader.loadMeshes(filename)
551 traceback.print_exc()
554 if self._objectLoadShader is not None:
555 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
559 self._scene.centerAll()
560 self._selectObject(obj)
561 if obj.getScale()[0] < 1.0:
562 self.notification.message("Warning: Object scaled down.")
565 def _deleteObject(self, obj):
566 if obj == self._selectedObj:
567 self._selectObject(None)
568 if obj == self._focusObj:
569 self._focusObj = None
570 self._scene.remove(obj)
571 for m in obj._meshList:
572 if m.vbo is not None and m.vbo.decRef():
573 self.glReleaseList.append(m.vbo)
578 def _selectObject(self, obj, zoom = True):
579 if obj != self._selectedObj:
580 self._selectedObj = obj
581 self.updateProfileToControls()
582 self.updateToolButtons()
583 if zoom and obj is not None:
584 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
585 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
586 newZoom = obj.getBoundaryCircle() * 6
587 if newZoom > numpy.max(self._machineSize) * 3:
588 newZoom = numpy.max(self._machineSize) * 3
589 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
591 def updateProfileToControls(self):
592 oldSimpleMode = self._isSimpleMode
593 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
594 if self._isSimpleMode != oldSimpleMode:
595 self._scene.arrangeAll()
597 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
598 self._objColors[0] = profile.getPreferenceColour('model_colour')
599 self._objColors[1] = profile.getPreferenceColour('model_colour2')
600 self._objColors[2] = profile.getPreferenceColour('model_colour3')
601 self._objColors[3] = profile.getPreferenceColour('model_colour4')
602 self._scene.setMachineSize(self._machineSize)
603 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
604 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'))
606 if self._selectedObj is not None:
607 scale = self._selectedObj.getScale()
608 size = self._selectedObj.getSize()
609 self.scaleXctrl.setValue(round(scale[0], 2))
610 self.scaleYctrl.setValue(round(scale[1], 2))
611 self.scaleZctrl.setValue(round(scale[2], 2))
612 self.scaleXmmctrl.setValue(round(size[0], 2))
613 self.scaleYmmctrl.setValue(round(size[1], 2))
614 self.scaleZmmctrl.setValue(round(size[2], 2))
616 def OnKeyChar(self, keyCode):
617 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
618 if self._selectedObj is not None:
619 self._deleteObject(self._selectedObj)
621 if keyCode == wx.WXK_UP:
622 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
624 elif keyCode == wx.WXK_DOWN:
625 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
627 elif keyCode == wx.WXK_PAGEUP:
628 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
630 elif keyCode == wx.WXK_PAGEDOWN:
631 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
634 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
635 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
636 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
637 from collections import defaultdict
638 from gc import get_objects
639 self._beforeLeakTest = defaultdict(int)
640 for i in get_objects():
641 self._beforeLeakTest[type(i)] += 1
642 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
643 from collections import defaultdict
644 from gc import get_objects
645 self._afterLeakTest = defaultdict(int)
646 for i in get_objects():
647 self._afterLeakTest[type(i)] += 1
648 for k in self._afterLeakTest:
649 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
650 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
652 def ShaderUpdate(self, v, f):
653 s = opengl.GLShader(v, f)
655 self._objectLoadShader.release()
656 self._objectLoadShader = s
657 for obj in self._scene.objects():
658 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
661 def OnMouseDown(self,e):
662 self._mouseX = e.GetX()
663 self._mouseY = e.GetY()
664 self._mouseClick3DPos = self._mouse3Dpos
665 self._mouseClickFocus = self._focusObj
667 self._mouseState = 'doubleClick'
669 self._mouseState = 'dragOrClick'
670 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
671 p0 -= self.getObjectCenterPos() - self._viewTarget
672 p1 -= self.getObjectCenterPos() - self._viewTarget
673 if self.tool.OnDragStart(p0, p1):
674 self._mouseState = 'tool'
675 if self._mouseState == 'dragOrClick':
676 if e.GetButton() == 1:
677 if self._focusObj is not None:
678 self._selectObject(self._focusObj, False)
681 def OnMouseUp(self, e):
682 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
684 if self._mouseState == 'dragOrClick':
685 if e.GetButton() == 1:
686 self._selectObject(self._focusObj)
687 if e.GetButton() == 3:
689 if self._focusObj is not None:
690 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
691 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
692 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
693 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
694 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:
695 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
696 if len(self._scene.objects()) > 0:
697 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
698 self.Bind(wx.EVT_MENU, self.reloadScene, menu.Append(-1, _("Reload all objects")))
699 if menu.MenuItemCount > 0:
702 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
703 self._scene.pushFree()
705 elif self._mouseState == 'tool':
706 if self.tempMatrix is not None and self._selectedObj is not None:
707 self._selectedObj.applyMatrix(self.tempMatrix)
708 self._scene.pushFree()
709 self._selectObject(self._selectedObj)
710 self.tempMatrix = None
711 self.tool.OnDragEnd()
713 self._mouseState = None
715 def OnMouseMotion(self,e):
716 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
717 p0 -= self.getObjectCenterPos() - self._viewTarget
718 p1 -= self.getObjectCenterPos() - self._viewTarget
720 if e.Dragging() and self._mouseState is not None:
721 if self._mouseState == 'tool':
722 self.tool.OnDrag(p0, p1)
723 elif not e.LeftIsDown() and e.RightIsDown():
724 self._mouseState = 'drag'
725 if wx.GetKeyState(wx.WXK_SHIFT):
726 a = math.cos(math.radians(self._yaw)) / 3.0
727 b = math.sin(math.radians(self._yaw)) / 3.0
728 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
729 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
730 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
731 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
733 self._yaw += e.GetX() - self._mouseX
734 self._pitch -= e.GetY() - self._mouseY
735 if self._pitch > 170:
739 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
740 self._mouseState = 'drag'
741 self._zoom += e.GetY() - self._mouseY
744 if self._zoom > numpy.max(self._machineSize) * 3:
745 self._zoom = numpy.max(self._machineSize) * 3
746 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
747 self._mouseState = 'dragObject'
748 z = max(0, self._mouseClick3DPos[2])
749 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
750 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
755 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
756 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
757 diff = cursorZ1 - cursorZ0
758 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
759 if not e.Dragging() or self._mouseState != 'tool':
760 self.tool.OnMouseMove(p0, p1)
762 self._mouseX = e.GetX()
763 self._mouseY = e.GetY()
765 def OnMouseWheel(self, e):
766 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
767 delta = max(min(delta,4),-4)
768 self._zoom *= 1.0 - delta / 10.0
771 if self._zoom > numpy.max(self._machineSize) * 3:
772 self._zoom = numpy.max(self._machineSize) * 3
775 def OnMouseLeave(self, e):
779 def getMouseRay(self, x, y):
780 if self._viewport is None:
781 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
782 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
783 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
784 p0 -= self._viewTarget
785 p1 -= self._viewTarget
788 def _init3DView(self):
789 # set viewing projection
790 size = self.GetSize()
791 glViewport(0, 0, size.GetWidth(), size.GetHeight())
794 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
796 glDisable(GL_RESCALE_NORMAL)
797 glDisable(GL_LIGHTING)
799 glEnable(GL_DEPTH_TEST)
800 glDisable(GL_CULL_FACE)
802 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
804 glClearColor(0.8, 0.8, 0.8, 1.0)
808 glMatrixMode(GL_PROJECTION)
810 aspect = float(size.GetWidth()) / float(size.GetHeight())
811 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
813 glMatrixMode(GL_MODELVIEW)
815 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
818 if machineCom.machineIsConnected():
819 self.printButton._imageID = 6
820 self.printButton._tooltip = _("Print")
821 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
822 self.printButton._imageID = 2
823 self.printButton._tooltip = _("Toolpath to SD")
825 self.printButton._imageID = 3
826 self.printButton._tooltip = _("Save toolpath")
828 if self._animView is not None:
829 self._viewTarget = self._animView.getPosition()
830 if self._animView.isDone():
831 self._animView = None
832 if self._animZoom is not None:
833 self._zoom = self._animZoom.getPosition()
834 if self._animZoom.isDone():
835 self._animZoom = None
836 if self.viewMode == 'gcode' and self._gcode is not None:
838 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
841 if self._objectShader is None:
842 if opengl.hasShaderSupport():
843 self._objectShader = opengl.GLShader("""
844 varying float light_amount;
848 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
849 gl_FrontColor = gl_Color;
851 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
855 varying float light_amount;
859 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
862 self._objectOverhangShader = opengl.GLShader("""
863 uniform float cosAngle;
864 uniform mat3 rotMatrix;
865 varying float light_amount;
869 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
870 gl_FrontColor = gl_Color;
872 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
874 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
876 light_amount = -10.0;
880 varying float light_amount;
884 if (light_amount == -10.0)
886 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
888 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
892 self._objectLoadShader = opengl.GLShader("""
893 uniform float intensity;
895 varying float light_amount;
899 vec4 tmp = gl_Vertex;
900 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
901 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
902 gl_Position = gl_ModelViewProjectionMatrix * tmp;
903 gl_FrontColor = gl_Color;
905 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
909 uniform float intensity;
910 varying float light_amount;
914 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
917 if self._objectShader is None or not self._objectShader.isValid():
918 self._objectShader = opengl.GLFakeShader()
919 self._objectOverhangShader = opengl.GLFakeShader()
920 self._objectLoadShader = None
922 glTranslate(0,0,-self._zoom)
923 glRotate(-self._pitch, 1,0,0)
924 glRotate(self._yaw, 0,0,1)
925 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
927 self._viewport = glGetIntegerv(GL_VIEWPORT)
928 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
929 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
931 glClearColor(1,1,1,1)
932 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
934 if self.viewMode != 'gcode':
935 for n in xrange(0, len(self._scene.objects())):
936 obj = self._scene.objects()[n]
937 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
938 self._renderObject(obj)
940 if self._mouseX > -1:
942 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
943 if n < len(self._scene.objects()):
944 self._focusObj = self._scene.objects()[n]
946 self._focusObj = None
947 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
948 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
949 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
950 self._mouse3Dpos -= self._viewTarget
953 glTranslate(0,0,-self._zoom)
954 glRotate(-self._pitch, 1,0,0)
955 glRotate(self._yaw, 0,0,1)
956 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
958 if self.viewMode == 'gcode':
959 if self._gcode is not None and self._gcode.layerList is None:
960 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
961 self._gcodeLoadThread.daemon = True
962 self._gcodeLoadThread.start()
963 if self._gcode is not None and self._gcode.layerList is not None:
965 if profile.getMachineSetting('machine_center_is_zero') != 'True':
966 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
968 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
969 for n in xrange(0, drawUpTill):
970 c = 1.0 - float(drawUpTill - n) / 15
972 if len(self._gcodeVBOs) < n + 1:
973 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
974 if time.time() - t > 0.5:
977 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
978 if n == drawUpTill - 1:
979 if len(self._gcodeVBOs[n]) < 9:
980 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
982 self._gcodeVBOs[n][8].render(GL_QUADS)
984 self._gcodeVBOs[n][9].render(GL_QUADS)
986 self._gcodeVBOs[n][10].render(GL_QUADS)
988 self._gcodeVBOs[n][11].render(GL_QUADS)
991 self._gcodeVBOs[n][12].render(GL_QUADS)
992 glColor3f(c/2, c/2, 0.0)
993 self._gcodeVBOs[n][13].render(GL_QUADS)
995 self._gcodeVBOs[n][14].render(GL_QUADS)
996 self._gcodeVBOs[n][15].render(GL_QUADS)
998 self._gcodeVBOs[n][16].render(GL_LINES)
1001 self._gcodeVBOs[n][0].render(GL_LINES)
1002 glColor3f(c/2, 0, c)
1003 self._gcodeVBOs[n][1].render(GL_LINES)
1004 glColor3f(0, c, c/2)
1005 self._gcodeVBOs[n][2].render(GL_LINES)
1007 self._gcodeVBOs[n][3].render(GL_LINES)
1010 self._gcodeVBOs[n][4].render(GL_LINES)
1011 glColor3f(c/2, c/2, 0.0)
1012 self._gcodeVBOs[n][5].render(GL_LINES)
1014 self._gcodeVBOs[n][6].render(GL_LINES)
1015 self._gcodeVBOs[n][7].render(GL_LINES)
1018 glStencilFunc(GL_ALWAYS, 1, 1)
1019 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1021 if self.viewMode == 'overhang':
1022 self._objectOverhangShader.bind()
1023 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1025 self._objectShader.bind()
1026 for obj in self._scene.objects():
1027 if obj._loadAnim is not None:
1028 if obj._loadAnim.isDone():
1029 obj._loadAnim = None
1033 if self._focusObj == obj:
1035 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1038 if self._selectedObj == obj or self._selectedObj is None:
1039 #If we want transparent, then first render a solid black model to remove the printer size lines.
1040 if self.viewMode == 'transparent':
1041 glColor4f(0, 0, 0, 0)
1042 self._renderObject(obj)
1044 glBlendFunc(GL_ONE, GL_ONE)
1045 glDisable(GL_DEPTH_TEST)
1047 if self.viewMode == 'xray':
1048 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1049 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1050 glEnable(GL_STENCIL_TEST)
1052 if self.viewMode == 'overhang':
1053 if self._selectedObj == obj and self.tempMatrix is not None:
1054 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1056 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1058 if not self._scene.checkPlatform(obj):
1059 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1060 self._renderObject(obj)
1062 self._renderObject(obj, brightness)
1063 glDisable(GL_STENCIL_TEST)
1065 glEnable(GL_DEPTH_TEST)
1066 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1068 if self.viewMode == 'xray':
1071 glEnable(GL_STENCIL_TEST)
1072 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1073 glDisable(GL_DEPTH_TEST)
1074 for i in xrange(2, 15, 2):
1075 glStencilFunc(GL_EQUAL, i, 0xFF)
1076 glColor(float(i)/10, float(i)/10, float(i)/5)
1078 glVertex3f(-1000,-1000,-10)
1079 glVertex3f( 1000,-1000,-10)
1080 glVertex3f( 1000, 1000,-10)
1081 glVertex3f(-1000, 1000,-10)
1083 for i in xrange(1, 15, 2):
1084 glStencilFunc(GL_EQUAL, i, 0xFF)
1085 glColor(float(i)/10, 0, 0)
1087 glVertex3f(-1000,-1000,-10)
1088 glVertex3f( 1000,-1000,-10)
1089 glVertex3f( 1000, 1000,-10)
1090 glVertex3f(-1000, 1000,-10)
1093 glDisable(GL_STENCIL_TEST)
1094 glEnable(GL_DEPTH_TEST)
1096 self._objectShader.unbind()
1098 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1100 if self._objectLoadShader is not None:
1101 self._objectLoadShader.bind()
1102 glColor4f(0.2, 0.6, 1.0, 1.0)
1103 for obj in self._scene.objects():
1104 if obj._loadAnim is None:
1106 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1107 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1108 self._renderObject(obj)
1109 self._objectLoadShader.unbind()
1114 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1116 z = self._usbPrintMonitor.getZ()
1117 size = self._machineSize
1118 glColor4ub(255,255,0,128)
1120 glVertex3f(-size[0]/2,-size[1]/2, z)
1121 glVertex3f( size[0]/2,-size[1]/2, z)
1122 glVertex3f( size[0]/2, size[1]/2, z)
1123 glVertex3f(-size[0]/2, size[1]/2, z)
1126 if self.viewMode == 'gcode':
1127 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1128 glDisable(GL_DEPTH_TEST)
1131 glTranslate(0,-4,-10)
1132 glColor4ub(60,60,60,255)
1133 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1136 #Draw the object box-shadow, so you can see where it will collide with other objects.
1137 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1138 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1140 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1142 glEnable(GL_CULL_FACE)
1143 glColor4f(0,0,0,0.12)
1145 glVertex3f(-size[0], size[1], 0.1)
1146 glVertex3f(-size[0], -size[1], 0.1)
1147 glVertex3f( size[0], -size[1], 0.1)
1148 glVertex3f( size[0], size[1], 0.1)
1150 glDisable(GL_CULL_FACE)
1153 #Draw the outline of the selected object, on top of everything else except the GUI.
1154 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1155 glDisable(GL_DEPTH_TEST)
1156 glEnable(GL_CULL_FACE)
1157 glEnable(GL_STENCIL_TEST)
1159 glStencilFunc(GL_EQUAL, 0, 255)
1161 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1163 glColor4f(1,1,1,0.5)
1164 self._renderObject(self._selectedObj)
1165 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1167 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1168 glDisable(GL_STENCIL_TEST)
1169 glDisable(GL_CULL_FACE)
1170 glEnable(GL_DEPTH_TEST)
1172 if self._selectedObj is not None:
1174 pos = self.getObjectCenterPos()
1175 glTranslate(pos[0], pos[1], pos[2])
1178 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1179 glDisable(GL_DEPTH_TEST)
1182 glTranslate(0,-4,-10)
1183 glColor4ub(60,60,60,255)
1184 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1187 def _renderObject(self, obj, brightness = False, addSink = True):
1190 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1192 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1194 if self.tempMatrix is not None and obj == self._selectedObj:
1195 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1196 glMultMatrixf(tempMatrix)
1198 offset = obj.getDrawOffset()
1199 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1201 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1202 glMultMatrixf(tempMatrix)
1205 for m in obj._meshList:
1207 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1209 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1214 def _drawMachine(self):
1215 glEnable(GL_CULL_FACE)
1218 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1220 machine = profile.getMachineSetting('machine_type')
1221 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1222 if machine not in self._platformMesh:
1223 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1225 self._platformMesh[machine] = meshes[0]
1227 self._platformMesh[machine] = None
1228 if machine == 'ultimaker2':
1229 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1231 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1232 glColor4f(1,1,1,0.5)
1233 self._objectShader.bind()
1234 self._renderObject(self._platformMesh[machine], False, False)
1235 self._objectShader.unbind()
1237 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1238 if machine == 'ultimaker2':
1239 if not hasattr(self._platformMesh[machine], 'texture'):
1240 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1241 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1242 glEnable(GL_TEXTURE_2D)
1246 glTranslate(0,150,-5)
1251 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1254 glVertex3f( w, 0, h)
1256 glVertex3f(-w, 0, h)
1258 glVertex3f(-w, 0, 0)
1260 glVertex3f( w, 0, 0)
1263 glVertex3f(-w, d, h)
1265 glVertex3f( w, d, h)
1267 glVertex3f( w, d, 0)
1269 glVertex3f(-w, d, 0)
1271 glDisable(GL_TEXTURE_2D)
1272 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1278 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1279 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1280 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1281 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1282 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1283 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1286 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1287 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1288 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1289 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1290 v4 = [ size[0] / 2, size[1] / 2, 0]
1291 v5 = [ size[0] / 2,-size[1] / 2, 0]
1292 v6 = [-size[0] / 2, size[1] / 2, 0]
1293 v7 = [-size[0] / 2,-size[1] / 2, 0]
1295 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1296 glEnableClientState(GL_VERTEX_ARRAY)
1297 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1299 glColor4ub(5, 171, 231, 64)
1300 glDrawArrays(GL_QUADS, 0, 4)
1301 glColor4ub(5, 171, 231, 96)
1302 glDrawArrays(GL_QUADS, 4, 8)
1303 glColor4ub(5, 171, 231, 128)
1304 glDrawArrays(GL_QUADS, 12, 8)
1305 glDisableClientState(GL_VERTEX_ARRAY)
1307 sx = self._machineSize[0]
1308 sy = self._machineSize[1]
1309 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1310 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1315 x1 = max(min(x1, sx/2), -sx/2)
1316 y1 = max(min(y1, sy/2), -sy/2)
1317 x2 = max(min(x2, sx/2), -sx/2)
1318 y2 = max(min(y2, sy/2), -sy/2)
1319 if (x & 1) == (y & 1):
1320 glColor4ub(5, 171, 231, 127)
1322 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1324 glVertex3f(x1, y1, -0.02)
1325 glVertex3f(x2, y1, -0.02)
1326 glVertex3f(x2, y2, -0.02)
1327 glVertex3f(x1, y2, -0.02)
1331 glDisable(GL_CULL_FACE)
1333 def _generateGCodeVBOs(self, layer):
1335 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1336 if ':' in extrudeType:
1337 extruder = int(extrudeType[extrudeType.find(':')+1:])
1338 extrudeType = extrudeType[0:extrudeType.find(':')]
1341 pointList = numpy.zeros((0,3), numpy.float32)
1343 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1345 a = numpy.concatenate((a[:-1], a[1:]), 1)
1346 a = a.reshape((len(a) * 2, 3))
1347 pointList = numpy.concatenate((pointList, a))
1348 ret.append(opengl.GLVBO(pointList))
1351 def _generateGCodeVBOs2(self, layer):
1352 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1353 filamentArea = math.pi * filamentRadius * filamentRadius
1354 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1357 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1358 if ':' in extrudeType:
1359 extruder = int(extrudeType[extrudeType.find(':')+1:])
1360 extrudeType = extrudeType[0:extrudeType.find(':')]
1363 pointList = numpy.zeros((0,3), numpy.float32)
1365 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1367 if extrudeType == 'FILL':
1370 normal = a[1:] - a[:-1]
1371 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1372 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1375 ePerDist = path['extrusion'][1:] / lens
1377 lineWidth = ePerDist / path['layerThickness'] / 2.0
1379 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1381 normal[:,0] *= lineWidth
1382 normal[:,1] *= lineWidth
1384 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1385 b = numpy.concatenate((b, a[1:] + normal), 1)
1386 b = numpy.concatenate((b, a[1:] - normal), 1)
1387 b = numpy.concatenate((b, a[:-1] - normal), 1)
1388 b = numpy.concatenate((b, a[:-1] + normal), 1)
1389 b = b.reshape((len(b) * 4, 3))
1392 normal2 = normal[:-1] + normal[1:]
1393 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1394 normal2[:,0] /= lens2
1395 normal2[:,1] /= lens2
1396 normal2[:,0] *= lineWidth[:-1]
1397 normal2[:,1] *= lineWidth[:-1]
1399 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1400 c = numpy.concatenate((c, a[1:-1]), 1)
1401 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1402 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1403 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1405 c = numpy.concatenate((c, a[1:-1]), 1)
1406 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1407 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1408 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1410 c = c.reshape((len(c) * 8, 3))
1412 pointList = numpy.concatenate((pointList, b, c))
1414 pointList = numpy.concatenate((pointList, b))
1415 ret.append(opengl.GLVBO(pointList))
1417 pointList = numpy.zeros((0,3), numpy.float32)
1419 if path['type'] == 'move':
1420 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1421 a = numpy.concatenate((a[:-1], a[1:]), 1)
1422 a = a.reshape((len(a) * 2, 3))
1423 pointList = numpy.concatenate((pointList, a))
1424 if path['type'] == 'retract':
1425 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1426 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1427 a = a.reshape((len(a) * 2, 3))
1428 pointList = numpy.concatenate((pointList, a))
1429 ret.append(opengl.GLVBO(pointList))
1433 def getObjectCenterPos(self):
1434 if self._selectedObj is None:
1435 return [0.0, 0.0, 0.0]
1436 pos = self._selectedObj.getPosition()
1437 size = self._selectedObj.getSize()
1438 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1440 def getObjectBoundaryCircle(self):
1441 if self._selectedObj is None:
1443 return self._selectedObj.getBoundaryCircle()
1445 def getObjectSize(self):
1446 if self._selectedObj is None:
1447 return [0.0, 0.0, 0.0]
1448 return self._selectedObj.getSize()
1450 def getObjectMatrix(self):
1451 if self._selectedObj is None:
1452 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1453 return self._selectedObj.getMatrix()
1455 class shaderEditor(wx.Dialog):
1456 def __init__(self, parent, callback, v, f):
1457 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1458 self._callback = callback
1459 s = wx.BoxSizer(wx.VERTICAL)
1461 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1462 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1463 s.Add(self._vertex, 1, flag=wx.EXPAND)
1464 s.Add(self._fragment, 1, flag=wx.EXPAND)
1466 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1467 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1469 self.SetPosition(self.GetParent().GetPosition())
1470 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1473 def OnText(self, e):
1474 self._callback(self._vertex.GetValue(), self._fragment.GetValue())