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()
463 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
464 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
467 def _splitCallback(self, progress):
470 def OnMergeObjects(self, e):
471 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
472 if len(self._scene.objects()) == 2:
473 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
476 self._scene.merge(self._selectedObj, self._focusObj)
479 def sceneUpdated(self):
480 self._sceneUpdateTimer.Start(500, True)
481 self._slicer.abortSlicer()
482 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
485 def _onRunSlicer(self, e):
486 if self._isSimpleMode:
487 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
488 self._slicer.runSlicer(self._scene)
489 if self._isSimpleMode:
490 profile.resetTempOverride()
492 def _updateSliceProgress(self, progressValue, ready):
494 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
496 self.printButton.setDisabled(not ready)
497 if progressValue >= 0.0:
498 self.printButton.setProgressBar(progressValue)
500 self.printButton.setProgressBar(None)
501 if self._gcode is not None:
503 for layerVBOlist in self._gcodeVBOs:
504 for vbo in layerVBOlist:
505 self.glReleaseList.append(vbo)
508 self.printButton.setProgressBar(None)
509 cost = self._slicer.getFilamentCost()
511 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
513 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
514 self._gcode = gcodeInterpreter.gcode()
515 self._gcodeFilename = self._slicer.getGCodeFilename()
517 self.printButton.setBottomText('')
520 def _loadGCode(self):
521 self._gcode.progressCallback = self._gcodeLoadCallback
522 self._gcode.load(self._gcodeFilename)
524 def _gcodeLoadCallback(self, progress):
525 if not self or self._gcode is None:
527 if len(self._gcode.layerList) % 15 == 0:
529 if self._gcode is None:
531 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
532 if self.viewMode == 'gcode':
536 def loadScene(self, fileList):
537 for filename in fileList:
539 ext = os.path.splitext(filename)[1].lower()
540 if ext in imageToMesh.supportedExtensions():
541 imageToMesh.convertImageDialog(self, filename).Show()
544 objList = meshLoader.loadMeshes(filename)
546 traceback.print_exc()
549 if self._objectLoadShader is not None:
550 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
554 self._scene.centerAll()
555 self._selectObject(obj)
556 if obj.getScale()[0] < 1.0:
557 self.notification.message("Warning: Object scaled down.")
560 def _deleteObject(self, obj):
561 if obj == self._selectedObj:
562 self._selectObject(None)
563 if obj == self._focusObj:
564 self._focusObj = None
565 self._scene.remove(obj)
566 for m in obj._meshList:
567 if m.vbo is not None and m.vbo.decRef():
568 self.glReleaseList.append(m.vbo)
573 def _selectObject(self, obj, zoom = True):
574 if obj != self._selectedObj:
575 self._selectedObj = obj
576 self.updateProfileToControls()
577 self.updateToolButtons()
578 if zoom and obj is not None:
579 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
580 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
581 newZoom = obj.getBoundaryCircle() * 6
582 if newZoom > numpy.max(self._machineSize) * 3:
583 newZoom = numpy.max(self._machineSize) * 3
584 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
586 def updateProfileToControls(self):
587 oldSimpleMode = self._isSimpleMode
588 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
589 if self._isSimpleMode != oldSimpleMode:
590 self._scene.arrangeAll()
592 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
593 self._objColors[0] = profile.getPreferenceColour('model_colour')
594 self._objColors[1] = profile.getPreferenceColour('model_colour2')
595 self._objColors[2] = profile.getPreferenceColour('model_colour3')
596 self._objColors[3] = profile.getPreferenceColour('model_colour4')
597 self._scene.setMachineSize(self._machineSize)
598 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
599 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'))
601 if self._selectedObj is not None:
602 scale = self._selectedObj.getScale()
603 size = self._selectedObj.getSize()
604 self.scaleXctrl.setValue(round(scale[0], 2))
605 self.scaleYctrl.setValue(round(scale[1], 2))
606 self.scaleZctrl.setValue(round(scale[2], 2))
607 self.scaleXmmctrl.setValue(round(size[0], 2))
608 self.scaleYmmctrl.setValue(round(size[1], 2))
609 self.scaleZmmctrl.setValue(round(size[2], 2))
611 def OnKeyChar(self, keyCode):
612 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
613 if self._selectedObj is not None:
614 self._deleteObject(self._selectedObj)
616 if keyCode == wx.WXK_UP:
617 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
619 elif keyCode == wx.WXK_DOWN:
620 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
622 elif keyCode == wx.WXK_PAGEUP:
623 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
625 elif keyCode == wx.WXK_PAGEDOWN:
626 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
629 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
630 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
631 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
632 from collections import defaultdict
633 from gc import get_objects
634 self._beforeLeakTest = defaultdict(int)
635 for i in get_objects():
636 self._beforeLeakTest[type(i)] += 1
637 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
638 from collections import defaultdict
639 from gc import get_objects
640 self._afterLeakTest = defaultdict(int)
641 for i in get_objects():
642 self._afterLeakTest[type(i)] += 1
643 for k in self._afterLeakTest:
644 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
645 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
647 def ShaderUpdate(self, v, f):
648 s = opengl.GLShader(v, f)
650 self._objectLoadShader.release()
651 self._objectLoadShader = s
652 for obj in self._scene.objects():
653 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
656 def OnMouseDown(self,e):
657 self._mouseX = e.GetX()
658 self._mouseY = e.GetY()
659 self._mouseClick3DPos = self._mouse3Dpos
660 self._mouseClickFocus = self._focusObj
662 self._mouseState = 'doubleClick'
664 self._mouseState = 'dragOrClick'
665 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
666 p0 -= self.getObjectCenterPos() - self._viewTarget
667 p1 -= self.getObjectCenterPos() - self._viewTarget
668 if self.tool.OnDragStart(p0, p1):
669 self._mouseState = 'tool'
670 if self._mouseState == 'dragOrClick':
671 if e.GetButton() == 1:
672 if self._focusObj is not None:
673 self._selectObject(self._focusObj, False)
676 def OnMouseUp(self, e):
677 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
679 if self._mouseState == 'dragOrClick':
680 if e.GetButton() == 1:
681 self._selectObject(self._focusObj)
682 if e.GetButton() == 3:
684 if self._focusObj is not None:
685 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
686 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
687 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
688 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
689 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:
690 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
691 if len(self._scene.objects()) > 0:
692 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
693 if menu.MenuItemCount > 0:
696 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
697 self._scene.pushFree()
699 elif self._mouseState == 'tool':
700 if self.tempMatrix is not None and self._selectedObj is not None:
701 self._selectedObj.applyMatrix(self.tempMatrix)
702 self._scene.pushFree()
703 self._selectObject(self._selectedObj)
704 self.tempMatrix = None
705 self.tool.OnDragEnd()
707 self._mouseState = None
709 def OnMouseMotion(self,e):
710 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
711 p0 -= self.getObjectCenterPos() - self._viewTarget
712 p1 -= self.getObjectCenterPos() - self._viewTarget
714 if e.Dragging() and self._mouseState is not None:
715 if self._mouseState == 'tool':
716 self.tool.OnDrag(p0, p1)
717 elif not e.LeftIsDown() and e.RightIsDown():
718 self._mouseState = 'drag'
719 if wx.GetKeyState(wx.WXK_SHIFT):
720 a = math.cos(math.radians(self._yaw)) / 3.0
721 b = math.sin(math.radians(self._yaw)) / 3.0
722 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
723 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
724 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
725 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
727 self._yaw += e.GetX() - self._mouseX
728 self._pitch -= e.GetY() - self._mouseY
729 if self._pitch > 170:
733 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
734 self._mouseState = 'drag'
735 self._zoom += e.GetY() - self._mouseY
738 if self._zoom > numpy.max(self._machineSize) * 3:
739 self._zoom = numpy.max(self._machineSize) * 3
740 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
741 self._mouseState = 'dragObject'
742 z = max(0, self._mouseClick3DPos[2])
743 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
744 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
749 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
750 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
751 diff = cursorZ1 - cursorZ0
752 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
753 if not e.Dragging() or self._mouseState != 'tool':
754 self.tool.OnMouseMove(p0, p1)
756 self._mouseX = e.GetX()
757 self._mouseY = e.GetY()
759 def OnMouseWheel(self, e):
760 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
761 delta = max(min(delta,4),-4)
762 self._zoom *= 1.0 - delta / 10.0
765 if self._zoom > numpy.max(self._machineSize) * 3:
766 self._zoom = numpy.max(self._machineSize) * 3
769 def OnMouseLeave(self, e):
773 def getMouseRay(self, x, y):
774 if self._viewport is None:
775 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
776 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
777 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
778 p0 -= self._viewTarget
779 p1 -= self._viewTarget
782 def _init3DView(self):
783 # set viewing projection
784 size = self.GetSize()
785 glViewport(0, 0, size.GetWidth(), size.GetHeight())
788 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
790 glDisable(GL_RESCALE_NORMAL)
791 glDisable(GL_LIGHTING)
793 glEnable(GL_DEPTH_TEST)
794 glDisable(GL_CULL_FACE)
796 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
798 glClearColor(0.8, 0.8, 0.8, 1.0)
802 glMatrixMode(GL_PROJECTION)
804 aspect = float(size.GetWidth()) / float(size.GetHeight())
805 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
807 glMatrixMode(GL_MODELVIEW)
809 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
812 if machineCom.machineIsConnected():
813 self.printButton._imageID = 6
814 self.printButton._tooltip = _("Print")
815 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
816 self.printButton._imageID = 2
817 self.printButton._tooltip = _("Toolpath to SD")
819 self.printButton._imageID = 3
820 self.printButton._tooltip = _("Save toolpath")
822 if self._animView is not None:
823 self._viewTarget = self._animView.getPosition()
824 if self._animView.isDone():
825 self._animView = None
826 if self._animZoom is not None:
827 self._zoom = self._animZoom.getPosition()
828 if self._animZoom.isDone():
829 self._animZoom = None
830 if self.viewMode == 'gcode' and self._gcode is not None:
832 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
835 if self._objectShader is None:
836 if opengl.hasShaderSupport():
837 self._objectShader = opengl.GLShader("""
838 varying float light_amount;
842 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
843 gl_FrontColor = gl_Color;
845 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
849 varying float light_amount;
853 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
856 self._objectOverhangShader = opengl.GLShader("""
857 uniform float cosAngle;
858 uniform mat3 rotMatrix;
859 varying float light_amount;
863 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
864 gl_FrontColor = gl_Color;
866 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
868 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
870 light_amount = -10.0;
874 varying float light_amount;
878 if (light_amount == -10.0)
880 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
882 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
886 self._objectLoadShader = opengl.GLShader("""
887 uniform float intensity;
889 varying float light_amount;
893 vec4 tmp = gl_Vertex;
894 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
895 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
896 gl_Position = gl_ModelViewProjectionMatrix * tmp;
897 gl_FrontColor = gl_Color;
899 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
903 uniform float intensity;
904 varying float light_amount;
908 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
911 if self._objectShader is None or not self._objectShader.isValid():
912 self._objectShader = opengl.GLFakeShader()
913 self._objectOverhangShader = opengl.GLFakeShader()
914 self._objectLoadShader = None
916 glTranslate(0,0,-self._zoom)
917 glRotate(-self._pitch, 1,0,0)
918 glRotate(self._yaw, 0,0,1)
919 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
921 self._viewport = glGetIntegerv(GL_VIEWPORT)
922 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
923 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
925 glClearColor(1,1,1,1)
926 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
928 if self.viewMode != 'gcode':
929 for n in xrange(0, len(self._scene.objects())):
930 obj = self._scene.objects()[n]
931 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
932 self._renderObject(obj)
934 if self._mouseX > -1:
936 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
937 if n < len(self._scene.objects()):
938 self._focusObj = self._scene.objects()[n]
940 self._focusObj = None
941 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
942 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
943 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
944 self._mouse3Dpos -= self._viewTarget
947 glTranslate(0,0,-self._zoom)
948 glRotate(-self._pitch, 1,0,0)
949 glRotate(self._yaw, 0,0,1)
950 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
952 if self.viewMode == 'gcode':
953 if self._gcode is not None and self._gcode.layerList is None:
954 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
955 self._gcodeLoadThread.daemon = True
956 self._gcodeLoadThread.start()
957 if self._gcode is not None and self._gcode.layerList is not None:
959 if profile.getMachineSetting('machine_center_is_zero') != 'True':
960 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
962 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
963 for n in xrange(0, drawUpTill):
964 c = 1.0 - float(drawUpTill - n) / 15
966 if len(self._gcodeVBOs) < n + 1:
967 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
968 if time.time() - t > 0.5:
971 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
972 if n == drawUpTill - 1:
973 if len(self._gcodeVBOs[n]) < 9:
974 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
976 self._gcodeVBOs[n][8].render(GL_QUADS)
978 self._gcodeVBOs[n][9].render(GL_QUADS)
980 self._gcodeVBOs[n][10].render(GL_QUADS)
982 self._gcodeVBOs[n][11].render(GL_QUADS)
985 self._gcodeVBOs[n][12].render(GL_QUADS)
986 glColor3f(c/2, c/2, 0.0)
987 self._gcodeVBOs[n][13].render(GL_QUADS)
989 self._gcodeVBOs[n][14].render(GL_QUADS)
990 self._gcodeVBOs[n][15].render(GL_QUADS)
992 self._gcodeVBOs[n][16].render(GL_LINES)
995 self._gcodeVBOs[n][0].render(GL_LINES)
997 self._gcodeVBOs[n][1].render(GL_LINES)
999 self._gcodeVBOs[n][2].render(GL_LINES)
1001 self._gcodeVBOs[n][3].render(GL_LINES)
1004 self._gcodeVBOs[n][4].render(GL_LINES)
1005 glColor3f(c/2, c/2, 0.0)
1006 self._gcodeVBOs[n][5].render(GL_LINES)
1008 self._gcodeVBOs[n][6].render(GL_LINES)
1009 self._gcodeVBOs[n][7].render(GL_LINES)
1012 glStencilFunc(GL_ALWAYS, 1, 1)
1013 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1015 if self.viewMode == 'overhang':
1016 self._objectOverhangShader.bind()
1017 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1019 self._objectShader.bind()
1020 for obj in self._scene.objects():
1021 if obj._loadAnim is not None:
1022 if obj._loadAnim.isDone():
1023 obj._loadAnim = None
1027 if self._focusObj == obj:
1029 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1032 if self._selectedObj == obj or self._selectedObj is None:
1033 #If we want transparent, then first render a solid black model to remove the printer size lines.
1034 if self.viewMode == 'transparent':
1035 glColor4f(0, 0, 0, 0)
1036 self._renderObject(obj)
1038 glBlendFunc(GL_ONE, GL_ONE)
1039 glDisable(GL_DEPTH_TEST)
1041 if self.viewMode == 'xray':
1042 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1043 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1044 glEnable(GL_STENCIL_TEST)
1046 if self.viewMode == 'overhang':
1047 if self._selectedObj == obj and self.tempMatrix is not None:
1048 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1050 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1052 if not self._scene.checkPlatform(obj):
1053 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1054 self._renderObject(obj)
1056 self._renderObject(obj, brightness)
1057 glDisable(GL_STENCIL_TEST)
1059 glEnable(GL_DEPTH_TEST)
1060 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1062 if self.viewMode == 'xray':
1065 glEnable(GL_STENCIL_TEST)
1066 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1067 glDisable(GL_DEPTH_TEST)
1068 for i in xrange(2, 15, 2):
1069 glStencilFunc(GL_EQUAL, i, 0xFF)
1070 glColor(float(i)/10, float(i)/10, float(i)/5)
1072 glVertex3f(-1000,-1000,-10)
1073 glVertex3f( 1000,-1000,-10)
1074 glVertex3f( 1000, 1000,-10)
1075 glVertex3f(-1000, 1000,-10)
1077 for i in xrange(1, 15, 2):
1078 glStencilFunc(GL_EQUAL, i, 0xFF)
1079 glColor(float(i)/10, 0, 0)
1081 glVertex3f(-1000,-1000,-10)
1082 glVertex3f( 1000,-1000,-10)
1083 glVertex3f( 1000, 1000,-10)
1084 glVertex3f(-1000, 1000,-10)
1087 glDisable(GL_STENCIL_TEST)
1088 glEnable(GL_DEPTH_TEST)
1090 self._objectShader.unbind()
1092 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1094 if self._objectLoadShader is not None:
1095 self._objectLoadShader.bind()
1096 glColor4f(0.2, 0.6, 1.0, 1.0)
1097 for obj in self._scene.objects():
1098 if obj._loadAnim is None:
1100 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1101 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1102 self._renderObject(obj)
1103 self._objectLoadShader.unbind()
1108 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1110 z = self._usbPrintMonitor.getZ()
1111 size = self._machineSize
1112 glColor4ub(255,255,0,128)
1114 glVertex3f(-size[0]/2,-size[1]/2, z)
1115 glVertex3f( size[0]/2,-size[1]/2, z)
1116 glVertex3f( size[0]/2, size[1]/2, z)
1117 glVertex3f(-size[0]/2, size[1]/2, z)
1120 if self.viewMode == 'gcode':
1121 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1122 glDisable(GL_DEPTH_TEST)
1125 glTranslate(0,-4,-10)
1126 glColor4ub(60,60,60,255)
1127 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1130 #Draw the object box-shadow, so you can see where it will collide with other objects.
1131 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1132 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1134 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1136 glEnable(GL_CULL_FACE)
1137 glColor4f(0,0,0,0.12)
1139 glVertex3f(-size[0], size[1], 0.1)
1140 glVertex3f(-size[0], -size[1], 0.1)
1141 glVertex3f( size[0], -size[1], 0.1)
1142 glVertex3f( size[0], size[1], 0.1)
1144 glDisable(GL_CULL_FACE)
1147 #Draw the outline of the selected object, on top of everything else except the GUI.
1148 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1149 glDisable(GL_DEPTH_TEST)
1150 glEnable(GL_CULL_FACE)
1151 glEnable(GL_STENCIL_TEST)
1153 glStencilFunc(GL_EQUAL, 0, 255)
1155 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1157 glColor4f(1,1,1,0.5)
1158 self._renderObject(self._selectedObj)
1159 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1161 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1162 glDisable(GL_STENCIL_TEST)
1163 glDisable(GL_CULL_FACE)
1164 glEnable(GL_DEPTH_TEST)
1166 if self._selectedObj is not None:
1168 pos = self.getObjectCenterPos()
1169 glTranslate(pos[0], pos[1], pos[2])
1172 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1173 glDisable(GL_DEPTH_TEST)
1176 glTranslate(0,-4,-10)
1177 glColor4ub(60,60,60,255)
1178 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1181 def _renderObject(self, obj, brightness = False, addSink = True):
1184 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1186 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1188 if self.tempMatrix is not None and obj == self._selectedObj:
1189 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1190 glMultMatrixf(tempMatrix)
1192 offset = obj.getDrawOffset()
1193 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1195 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1196 glMultMatrixf(tempMatrix)
1199 for m in obj._meshList:
1201 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1203 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1208 def _drawMachine(self):
1209 glEnable(GL_CULL_FACE)
1212 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1214 machine = profile.getMachineSetting('machine_type')
1215 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1216 if machine not in self._platformMesh:
1217 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1219 self._platformMesh[machine] = meshes[0]
1221 self._platformMesh[machine] = None
1222 if machine == 'ultimaker2':
1223 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1225 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1226 glColor4f(1,1,1,0.5)
1227 self._objectShader.bind()
1228 self._renderObject(self._platformMesh[machine], False, False)
1229 self._objectShader.unbind()
1231 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1232 if machine == 'ultimaker2':
1233 if not hasattr(self._platformMesh[machine], 'texture'):
1234 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1235 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1236 glEnable(GL_TEXTURE_2D)
1240 glTranslate(0,150,-5)
1245 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1248 glVertex3f( w, 0, h)
1250 glVertex3f(-w, 0, h)
1252 glVertex3f(-w, 0, 0)
1254 glVertex3f( w, 0, 0)
1257 glVertex3f(-w, d, h)
1259 glVertex3f( w, d, h)
1261 glVertex3f( w, d, 0)
1263 glVertex3f(-w, d, 0)
1265 glDisable(GL_TEXTURE_2D)
1266 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1272 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1273 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1274 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1275 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1276 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1277 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1280 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1281 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1282 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1283 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1284 v4 = [ size[0] / 2, size[1] / 2, 0]
1285 v5 = [ size[0] / 2,-size[1] / 2, 0]
1286 v6 = [-size[0] / 2, size[1] / 2, 0]
1287 v7 = [-size[0] / 2,-size[1] / 2, 0]
1289 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1290 glEnableClientState(GL_VERTEX_ARRAY)
1291 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1293 glColor4ub(5, 171, 231, 64)
1294 glDrawArrays(GL_QUADS, 0, 4)
1295 glColor4ub(5, 171, 231, 96)
1296 glDrawArrays(GL_QUADS, 4, 8)
1297 glColor4ub(5, 171, 231, 128)
1298 glDrawArrays(GL_QUADS, 12, 8)
1299 glDisableClientState(GL_VERTEX_ARRAY)
1301 sx = self._machineSize[0]
1302 sy = self._machineSize[1]
1303 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1304 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1309 x1 = max(min(x1, sx/2), -sx/2)
1310 y1 = max(min(y1, sy/2), -sy/2)
1311 x2 = max(min(x2, sx/2), -sx/2)
1312 y2 = max(min(y2, sy/2), -sy/2)
1313 if (x & 1) == (y & 1):
1314 glColor4ub(5, 171, 231, 127)
1316 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1318 glVertex3f(x1, y1, -0.02)
1319 glVertex3f(x2, y1, -0.02)
1320 glVertex3f(x2, y2, -0.02)
1321 glVertex3f(x1, y2, -0.02)
1325 glDisable(GL_CULL_FACE)
1327 def _generateGCodeVBOs(self, layer):
1329 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1330 if ':' in extrudeType:
1331 extruder = int(extrudeType[extrudeType.find(':')+1:])
1332 extrudeType = extrudeType[0:extrudeType.find(':')]
1335 pointList = numpy.zeros((0,3), numpy.float32)
1337 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1339 a = numpy.concatenate((a[:-1], a[1:]), 1)
1340 a = a.reshape((len(a) * 2, 3))
1341 pointList = numpy.concatenate((pointList, a))
1342 ret.append(opengl.GLVBO(pointList))
1345 def _generateGCodeVBOs2(self, layer):
1346 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1347 filamentArea = math.pi * filamentRadius * filamentRadius
1348 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1351 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1352 if ':' in extrudeType:
1353 extruder = int(extrudeType[extrudeType.find(':')+1:])
1354 extrudeType = extrudeType[0:extrudeType.find(':')]
1357 pointList = numpy.zeros((0,3), numpy.float32)
1359 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1361 if extrudeType == 'FILL':
1364 normal = a[1:] - a[:-1]
1365 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1366 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1369 ePerDist = path['extrusion'][1:] / lens
1371 lineWidth = ePerDist / path['layerThickness'] / 2.0
1373 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1375 normal[:,0] *= lineWidth
1376 normal[:,1] *= lineWidth
1378 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1379 b = numpy.concatenate((b, a[1:] + normal), 1)
1380 b = numpy.concatenate((b, a[1:] - normal), 1)
1381 b = numpy.concatenate((b, a[:-1] - normal), 1)
1382 b = numpy.concatenate((b, a[:-1] + normal), 1)
1383 b = b.reshape((len(b) * 4, 3))
1386 normal2 = normal[:-1] + normal[1:]
1387 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1388 normal2[:,0] /= lens2
1389 normal2[:,1] /= lens2
1390 normal2[:,0] *= lineWidth[:-1]
1391 normal2[:,1] *= lineWidth[:-1]
1393 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1394 c = numpy.concatenate((c, a[1:-1]), 1)
1395 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1396 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1397 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1399 c = numpy.concatenate((c, a[1:-1]), 1)
1400 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1401 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1402 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1404 c = c.reshape((len(c) * 8, 3))
1406 pointList = numpy.concatenate((pointList, b, c))
1408 pointList = numpy.concatenate((pointList, b))
1409 ret.append(opengl.GLVBO(pointList))
1411 pointList = numpy.zeros((0,3), numpy.float32)
1413 if path['type'] == 'move':
1414 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1415 a = numpy.concatenate((a[:-1], a[1:]), 1)
1416 a = a.reshape((len(a) * 2, 3))
1417 pointList = numpy.concatenate((pointList, a))
1418 if path['type'] == 'retract':
1419 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1420 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1421 a = a.reshape((len(a) * 2, 3))
1422 pointList = numpy.concatenate((pointList, a))
1423 ret.append(opengl.GLVBO(pointList))
1427 def getObjectCenterPos(self):
1428 if self._selectedObj is None:
1429 return [0.0, 0.0, 0.0]
1430 pos = self._selectedObj.getPosition()
1431 size = self._selectedObj.getSize()
1432 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1434 def getObjectBoundaryCircle(self):
1435 if self._selectedObj is None:
1437 return self._selectedObj.getBoundaryCircle()
1439 def getObjectSize(self):
1440 if self._selectedObj is None:
1441 return [0.0, 0.0, 0.0]
1442 return self._selectedObj.getSize()
1444 def getObjectMatrix(self):
1445 if self._selectedObj is None:
1446 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1447 return self._selectedObj.getMatrix()
1449 class shaderEditor(wx.Dialog):
1450 def __init__(self, parent, callback, v, f):
1451 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1452 self._callback = callback
1453 s = wx.BoxSizer(wx.VERTICAL)
1455 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1456 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1457 s.Add(self._vertex, 1, flag=wx.EXPAND)
1458 s.Add(self._fragment, 1, flag=wx.EXPAND)
1460 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1461 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1463 self.SetPosition(self.GetParent().GetPosition())
1464 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1467 def OnText(self, e):
1468 self._callback(self._vertex.GetValue(), self._fragment.GetValue())