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)
466 def _splitCallback(self, progress):
469 def OnMergeObjects(self, e):
470 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
471 if len(self._scene.objects()) == 2:
472 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
475 self._scene.merge(self._selectedObj, self._focusObj)
478 def sceneUpdated(self):
479 self._sceneUpdateTimer.Start(500, True)
480 self._slicer.abortSlicer()
481 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
484 def _onRunSlicer(self, e):
485 if self._isSimpleMode:
486 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
487 self._slicer.runSlicer(self._scene)
488 if self._isSimpleMode:
489 profile.resetTempOverride()
491 def _updateSliceProgress(self, progressValue, ready):
493 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
495 self.printButton.setDisabled(not ready)
496 if progressValue >= 0.0:
497 self.printButton.setProgressBar(progressValue)
499 self.printButton.setProgressBar(None)
500 if self._gcode is not None:
502 for layerVBOlist in self._gcodeVBOs:
503 for vbo in layerVBOlist:
504 self.glReleaseList.append(vbo)
507 self.printButton.setProgressBar(None)
508 cost = self._slicer.getFilamentCost()
510 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
512 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
513 self._gcode = gcodeInterpreter.gcode()
514 self._gcodeFilename = self._slicer.getGCodeFilename()
516 self.printButton.setBottomText('')
519 def _loadGCode(self):
520 self._gcode.progressCallback = self._gcodeLoadCallback
521 self._gcode.load(self._gcodeFilename)
523 def _gcodeLoadCallback(self, progress):
524 if self._gcode is None:
526 if len(self._gcode.layerList) % 15 == 0:
528 if self._gcode is None:
530 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
531 if self.viewMode == 'gcode':
535 def loadScene(self, fileList):
536 for filename in fileList:
538 ext = os.path.splitext(filename)[1].lower()
539 if ext in imageToMesh.supportedExtensions():
540 imageToMesh.convertImageDialog(self, filename).Show()
543 objList = meshLoader.loadMeshes(filename)
545 traceback.print_exc()
548 if self._objectLoadShader is not None:
549 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
553 self._scene.centerAll()
554 self._selectObject(obj)
555 if obj.getScale()[0] < 1.0:
556 self.notification.message("Warning: Object scaled down.")
559 def _deleteObject(self, obj):
560 if obj == self._selectedObj:
561 self._selectObject(None)
562 if obj == self._focusObj:
563 self._focusObj = None
564 self._scene.remove(obj)
565 for m in obj._meshList:
566 if m.vbo is not None and m.vbo.decRef():
567 self.glReleaseList.append(m.vbo)
572 def _selectObject(self, obj, zoom = True):
573 if obj != self._selectedObj:
574 self._selectedObj = obj
575 self.updateProfileToControls()
576 self.updateToolButtons()
577 if zoom and obj is not None:
578 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
579 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
580 newZoom = obj.getBoundaryCircle() * 6
581 if newZoom > numpy.max(self._machineSize) * 3:
582 newZoom = numpy.max(self._machineSize) * 3
583 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
585 def updateProfileToControls(self):
586 oldSimpleMode = self._isSimpleMode
587 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
588 if self._isSimpleMode != oldSimpleMode:
589 self._scene.arrangeAll()
591 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
592 self._objColors[0] = profile.getPreferenceColour('model_colour')
593 self._objColors[1] = profile.getPreferenceColour('model_colour2')
594 self._objColors[2] = profile.getPreferenceColour('model_colour3')
595 self._objColors[3] = profile.getPreferenceColour('model_colour4')
596 self._scene.setMachineSize(self._machineSize)
597 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
598 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'))
600 if self._selectedObj is not None:
601 scale = self._selectedObj.getScale()
602 size = self._selectedObj.getSize()
603 self.scaleXctrl.setValue(round(scale[0], 2))
604 self.scaleYctrl.setValue(round(scale[1], 2))
605 self.scaleZctrl.setValue(round(scale[2], 2))
606 self.scaleXmmctrl.setValue(round(size[0], 2))
607 self.scaleYmmctrl.setValue(round(size[1], 2))
608 self.scaleZmmctrl.setValue(round(size[2], 2))
610 def OnKeyChar(self, keyCode):
611 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
612 if self._selectedObj is not None:
613 self._deleteObject(self._selectedObj)
615 if keyCode == wx.WXK_UP:
616 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
618 elif keyCode == wx.WXK_DOWN:
619 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
621 elif keyCode == wx.WXK_PAGEUP:
622 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
624 elif keyCode == wx.WXK_PAGEDOWN:
625 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
628 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
629 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
630 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
631 from collections import defaultdict
632 from gc import get_objects
633 self._beforeLeakTest = defaultdict(int)
634 for i in get_objects():
635 self._beforeLeakTest[type(i)] += 1
636 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
637 from collections import defaultdict
638 from gc import get_objects
639 self._afterLeakTest = defaultdict(int)
640 for i in get_objects():
641 self._afterLeakTest[type(i)] += 1
642 for k in self._afterLeakTest:
643 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
644 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
646 def ShaderUpdate(self, v, f):
647 s = opengl.GLShader(v, f)
649 self._objectLoadShader.release()
650 self._objectLoadShader = s
651 for obj in self._scene.objects():
652 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
655 def OnMouseDown(self,e):
656 self._mouseX = e.GetX()
657 self._mouseY = e.GetY()
658 self._mouseClick3DPos = self._mouse3Dpos
659 self._mouseClickFocus = self._focusObj
661 self._mouseState = 'doubleClick'
663 self._mouseState = 'dragOrClick'
664 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
665 p0 -= self.getObjectCenterPos() - self._viewTarget
666 p1 -= self.getObjectCenterPos() - self._viewTarget
667 if self.tool.OnDragStart(p0, p1):
668 self._mouseState = 'tool'
669 if self._mouseState == 'dragOrClick':
670 if e.GetButton() == 1:
671 if self._focusObj is not None:
672 self._selectObject(self._focusObj, False)
675 def OnMouseUp(self, e):
676 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
678 if self._mouseState == 'dragOrClick':
679 if e.GetButton() == 1:
680 self._selectObject(self._focusObj)
681 if e.GetButton() == 3:
683 if self._focusObj is not None:
684 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
685 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
686 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
687 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
688 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:
689 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
690 if len(self._scene.objects()) > 0:
691 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
692 if menu.MenuItemCount > 0:
695 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
696 self._scene.pushFree()
698 elif self._mouseState == 'tool':
699 if self.tempMatrix is not None and self._selectedObj is not None:
700 self._selectedObj.applyMatrix(self.tempMatrix)
701 self._scene.pushFree()
702 self._selectObject(self._selectedObj)
703 self.tempMatrix = None
704 self.tool.OnDragEnd()
706 self._mouseState = None
708 def OnMouseMotion(self,e):
709 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
710 p0 -= self.getObjectCenterPos() - self._viewTarget
711 p1 -= self.getObjectCenterPos() - self._viewTarget
713 if e.Dragging() and self._mouseState is not None:
714 if self._mouseState == 'tool':
715 self.tool.OnDrag(p0, p1)
716 elif not e.LeftIsDown() and e.RightIsDown():
717 self._mouseState = 'drag'
718 if wx.GetKeyState(wx.WXK_SHIFT):
719 a = math.cos(math.radians(self._yaw)) / 3.0
720 b = math.sin(math.radians(self._yaw)) / 3.0
721 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
722 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
723 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
724 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
726 self._yaw += e.GetX() - self._mouseX
727 self._pitch -= e.GetY() - self._mouseY
728 if self._pitch > 170:
732 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
733 self._mouseState = 'drag'
734 self._zoom += e.GetY() - self._mouseY
737 if self._zoom > numpy.max(self._machineSize) * 3:
738 self._zoom = numpy.max(self._machineSize) * 3
739 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
740 self._mouseState = 'dragObject'
741 z = max(0, self._mouseClick3DPos[2])
742 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
743 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
748 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
749 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
750 diff = cursorZ1 - cursorZ0
751 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
752 if not e.Dragging() or self._mouseState != 'tool':
753 self.tool.OnMouseMove(p0, p1)
755 self._mouseX = e.GetX()
756 self._mouseY = e.GetY()
758 def OnMouseWheel(self, e):
759 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
760 delta = max(min(delta,4),-4)
761 self._zoom *= 1.0 - delta / 10.0
764 if self._zoom > numpy.max(self._machineSize) * 3:
765 self._zoom = numpy.max(self._machineSize) * 3
768 def OnMouseLeave(self, e):
772 def getMouseRay(self, x, y):
773 if self._viewport is None:
774 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
775 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
776 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
777 p0 -= self._viewTarget
778 p1 -= self._viewTarget
781 def _init3DView(self):
782 # set viewing projection
783 size = self.GetSize()
784 glViewport(0, 0, size.GetWidth(), size.GetHeight())
787 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
789 glDisable(GL_RESCALE_NORMAL)
790 glDisable(GL_LIGHTING)
792 glEnable(GL_DEPTH_TEST)
793 glDisable(GL_CULL_FACE)
795 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
797 glClearColor(0.8, 0.8, 0.8, 1.0)
801 glMatrixMode(GL_PROJECTION)
803 aspect = float(size.GetWidth()) / float(size.GetHeight())
804 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
806 glMatrixMode(GL_MODELVIEW)
808 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
811 if machineCom.machineIsConnected():
812 self.printButton._imageID = 6
813 self.printButton._tooltip = _("Print")
814 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
815 self.printButton._imageID = 2
816 self.printButton._tooltip = _("Toolpath to SD")
818 self.printButton._imageID = 3
819 self.printButton._tooltip = _("Save toolpath")
821 if self._animView is not None:
822 self._viewTarget = self._animView.getPosition()
823 if self._animView.isDone():
824 self._animView = None
825 if self._animZoom is not None:
826 self._zoom = self._animZoom.getPosition()
827 if self._animZoom.isDone():
828 self._animZoom = None
829 if self.viewMode == 'gcode' and self._gcode is not None:
831 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
834 if self._objectShader is None:
835 if opengl.hasShaderSupport():
836 self._objectShader = opengl.GLShader("""
837 varying float light_amount;
841 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
842 gl_FrontColor = gl_Color;
844 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
848 varying float light_amount;
852 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
855 self._objectOverhangShader = opengl.GLShader("""
856 uniform float cosAngle;
857 uniform mat3 rotMatrix;
858 varying float light_amount;
862 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
863 gl_FrontColor = gl_Color;
865 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
867 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
869 light_amount = -10.0;
873 varying float light_amount;
877 if (light_amount == -10.0)
879 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
881 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
885 self._objectLoadShader = opengl.GLShader("""
886 uniform float intensity;
888 varying float light_amount;
892 vec4 tmp = gl_Vertex;
893 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
894 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
895 gl_Position = gl_ModelViewProjectionMatrix * tmp;
896 gl_FrontColor = gl_Color;
898 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
902 uniform float intensity;
903 varying float light_amount;
907 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
910 if self._objectShader is None or not self._objectShader.isValid():
911 self._objectShader = opengl.GLFakeShader()
912 self._objectOverhangShader = opengl.GLFakeShader()
913 self._objectLoadShader = None
915 glTranslate(0,0,-self._zoom)
916 glRotate(-self._pitch, 1,0,0)
917 glRotate(self._yaw, 0,0,1)
918 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
920 self._viewport = glGetIntegerv(GL_VIEWPORT)
921 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
922 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
924 glClearColor(1,1,1,1)
925 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
927 if self.viewMode != 'gcode':
928 for n in xrange(0, len(self._scene.objects())):
929 obj = self._scene.objects()[n]
930 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
931 self._renderObject(obj)
933 if self._mouseX > -1:
935 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
936 if n < len(self._scene.objects()):
937 self._focusObj = self._scene.objects()[n]
939 self._focusObj = None
940 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
941 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
942 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
943 self._mouse3Dpos -= self._viewTarget
946 glTranslate(0,0,-self._zoom)
947 glRotate(-self._pitch, 1,0,0)
948 glRotate(self._yaw, 0,0,1)
949 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
951 if self.viewMode == 'gcode':
952 if self._gcode is not None and self._gcode.layerList is None:
953 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
954 self._gcodeLoadThread.daemon = True
955 self._gcodeLoadThread.start()
956 if self._gcode is not None and self._gcode.layerList is not None:
958 if profile.getMachineSetting('machine_center_is_zero') != 'True':
959 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
961 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
962 for n in xrange(0, drawUpTill):
963 c = 1.0 - float(drawUpTill - n) / 15
965 if len(self._gcodeVBOs) < n + 1:
966 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
967 if time.time() - t > 0.5:
970 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
971 if n == drawUpTill - 1:
972 if len(self._gcodeVBOs[n]) < 9:
973 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
975 self._gcodeVBOs[n][8].render(GL_QUADS)
977 self._gcodeVBOs[n][9].render(GL_QUADS)
979 self._gcodeVBOs[n][10].render(GL_QUADS)
981 self._gcodeVBOs[n][11].render(GL_QUADS)
984 self._gcodeVBOs[n][12].render(GL_QUADS)
985 glColor3f(c/2, c/2, 0.0)
986 self._gcodeVBOs[n][13].render(GL_QUADS)
988 self._gcodeVBOs[n][14].render(GL_QUADS)
989 self._gcodeVBOs[n][15].render(GL_QUADS)
991 self._gcodeVBOs[n][16].render(GL_LINES)
994 self._gcodeVBOs[n][0].render(GL_LINES)
996 self._gcodeVBOs[n][1].render(GL_LINES)
998 self._gcodeVBOs[n][2].render(GL_LINES)
1000 self._gcodeVBOs[n][3].render(GL_LINES)
1003 self._gcodeVBOs[n][4].render(GL_LINES)
1004 glColor3f(c/2, c/2, 0.0)
1005 self._gcodeVBOs[n][5].render(GL_LINES)
1007 self._gcodeVBOs[n][6].render(GL_LINES)
1008 self._gcodeVBOs[n][7].render(GL_LINES)
1011 glStencilFunc(GL_ALWAYS, 1, 1)
1012 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1014 if self.viewMode == 'overhang':
1015 self._objectOverhangShader.bind()
1016 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1018 self._objectShader.bind()
1019 for obj in self._scene.objects():
1020 if obj._loadAnim is not None:
1021 if obj._loadAnim.isDone():
1022 obj._loadAnim = None
1026 if self._focusObj == obj:
1028 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1031 if self._selectedObj == obj or self._selectedObj is None:
1032 #If we want transparent, then first render a solid black model to remove the printer size lines.
1033 if self.viewMode == 'transparent':
1034 glColor4f(0, 0, 0, 0)
1035 self._renderObject(obj)
1037 glBlendFunc(GL_ONE, GL_ONE)
1038 glDisable(GL_DEPTH_TEST)
1040 if self.viewMode == 'xray':
1041 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1042 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1043 glEnable(GL_STENCIL_TEST)
1045 if self.viewMode == 'overhang':
1046 if self._selectedObj == obj and self.tempMatrix is not None:
1047 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1049 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1051 if not self._scene.checkPlatform(obj):
1052 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1053 self._renderObject(obj)
1055 self._renderObject(obj, brightness)
1056 glDisable(GL_STENCIL_TEST)
1058 glEnable(GL_DEPTH_TEST)
1059 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1061 if self.viewMode == 'xray':
1064 glEnable(GL_STENCIL_TEST)
1065 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1066 glDisable(GL_DEPTH_TEST)
1067 for i in xrange(2, 15, 2):
1068 glStencilFunc(GL_EQUAL, i, 0xFF)
1069 glColor(float(i)/10, float(i)/10, float(i)/5)
1071 glVertex3f(-1000,-1000,-10)
1072 glVertex3f( 1000,-1000,-10)
1073 glVertex3f( 1000, 1000,-10)
1074 glVertex3f(-1000, 1000,-10)
1076 for i in xrange(1, 15, 2):
1077 glStencilFunc(GL_EQUAL, i, 0xFF)
1078 glColor(float(i)/10, 0, 0)
1080 glVertex3f(-1000,-1000,-10)
1081 glVertex3f( 1000,-1000,-10)
1082 glVertex3f( 1000, 1000,-10)
1083 glVertex3f(-1000, 1000,-10)
1086 glDisable(GL_STENCIL_TEST)
1087 glEnable(GL_DEPTH_TEST)
1089 self._objectShader.unbind()
1091 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1093 if self._objectLoadShader is not None:
1094 self._objectLoadShader.bind()
1095 glColor4f(0.2, 0.6, 1.0, 1.0)
1096 for obj in self._scene.objects():
1097 if obj._loadAnim is None:
1099 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1100 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1101 self._renderObject(obj)
1102 self._objectLoadShader.unbind()
1107 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1109 z = self._usbPrintMonitor.getZ()
1110 size = self._machineSize
1111 glColor4ub(255,255,0,128)
1113 glVertex3f(-size[0]/2,-size[1]/2, z)
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)
1119 if self.viewMode == 'gcode':
1120 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1121 glDisable(GL_DEPTH_TEST)
1124 glTranslate(0,-4,-10)
1125 glColor4ub(60,60,60,255)
1126 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1129 #Draw the object box-shadow, so you can see where it will collide with other objects.
1130 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1131 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1133 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1135 glEnable(GL_CULL_FACE)
1136 glColor4f(0,0,0,0.12)
1138 glVertex3f(-size[0], size[1], 0.1)
1139 glVertex3f(-size[0], -size[1], 0.1)
1140 glVertex3f( size[0], -size[1], 0.1)
1141 glVertex3f( size[0], size[1], 0.1)
1143 glDisable(GL_CULL_FACE)
1146 #Draw the outline of the selected object, on top of everything else except the GUI.
1147 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1148 glDisable(GL_DEPTH_TEST)
1149 glEnable(GL_CULL_FACE)
1150 glEnable(GL_STENCIL_TEST)
1152 glStencilFunc(GL_EQUAL, 0, 255)
1154 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1156 glColor4f(1,1,1,0.5)
1157 self._renderObject(self._selectedObj)
1158 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1160 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1161 glDisable(GL_STENCIL_TEST)
1162 glDisable(GL_CULL_FACE)
1163 glEnable(GL_DEPTH_TEST)
1165 if self._selectedObj is not None:
1167 pos = self.getObjectCenterPos()
1168 glTranslate(pos[0], pos[1], pos[2])
1171 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1172 glDisable(GL_DEPTH_TEST)
1175 glTranslate(0,-4,-10)
1176 glColor4ub(60,60,60,255)
1177 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1180 def _renderObject(self, obj, brightness = False, addSink = True):
1183 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1185 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1187 if self.tempMatrix is not None and obj == self._selectedObj:
1188 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1189 glMultMatrixf(tempMatrix)
1191 offset = obj.getDrawOffset()
1192 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1194 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1195 glMultMatrixf(tempMatrix)
1198 for m in obj._meshList:
1200 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1202 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1207 def _drawMachine(self):
1208 glEnable(GL_CULL_FACE)
1211 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1213 machine = profile.getMachineSetting('machine_type')
1214 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1215 if machine not in self._platformMesh:
1216 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1218 self._platformMesh[machine] = meshes[0]
1220 self._platformMesh[machine] = None
1221 if machine == 'ultimaker2':
1222 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1224 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1225 glColor4f(1,1,1,0.5)
1226 self._objectShader.bind()
1227 self._renderObject(self._platformMesh[machine], False, False)
1228 self._objectShader.unbind()
1230 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1231 if machine == 'ultimaker2':
1232 if not hasattr(self._platformMesh[machine], 'texture'):
1233 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1234 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1235 glEnable(GL_TEXTURE_2D)
1239 glTranslate(0,150,-5)
1244 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1247 glVertex3f( w, 0, h)
1249 glVertex3f(-w, 0, h)
1251 glVertex3f(-w, 0, 0)
1253 glVertex3f( w, 0, 0)
1256 glVertex3f(-w, d, h)
1258 glVertex3f( w, d, h)
1260 glVertex3f( w, d, 0)
1262 glVertex3f(-w, d, 0)
1264 glDisable(GL_TEXTURE_2D)
1265 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1271 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1272 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1273 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1274 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1275 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1276 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1279 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1280 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1281 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1282 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1283 v4 = [ size[0] / 2, size[1] / 2, 0]
1284 v5 = [ size[0] / 2,-size[1] / 2, 0]
1285 v6 = [-size[0] / 2, size[1] / 2, 0]
1286 v7 = [-size[0] / 2,-size[1] / 2, 0]
1288 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1289 glEnableClientState(GL_VERTEX_ARRAY)
1290 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1292 glColor4ub(5, 171, 231, 64)
1293 glDrawArrays(GL_QUADS, 0, 4)
1294 glColor4ub(5, 171, 231, 96)
1295 glDrawArrays(GL_QUADS, 4, 8)
1296 glColor4ub(5, 171, 231, 128)
1297 glDrawArrays(GL_QUADS, 12, 8)
1298 glDisableClientState(GL_VERTEX_ARRAY)
1300 sx = self._machineSize[0]
1301 sy = self._machineSize[1]
1302 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1303 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1308 x1 = max(min(x1, sx/2), -sx/2)
1309 y1 = max(min(y1, sy/2), -sy/2)
1310 x2 = max(min(x2, sx/2), -sx/2)
1311 y2 = max(min(y2, sy/2), -sy/2)
1312 if (x & 1) == (y & 1):
1313 glColor4ub(5, 171, 231, 127)
1315 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1317 glVertex3f(x1, y1, -0.02)
1318 glVertex3f(x2, y1, -0.02)
1319 glVertex3f(x2, y2, -0.02)
1320 glVertex3f(x1, y2, -0.02)
1324 glDisable(GL_CULL_FACE)
1326 def _generateGCodeVBOs(self, layer):
1328 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1329 if ':' in extrudeType:
1330 extruder = int(extrudeType[extrudeType.find(':')+1:])
1331 extrudeType = extrudeType[0:extrudeType.find(':')]
1334 pointList = numpy.zeros((0,3), numpy.float32)
1336 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1338 a = numpy.concatenate((a[:-1], a[1:]), 1)
1339 a = a.reshape((len(a) * 2, 3))
1340 pointList = numpy.concatenate((pointList, a))
1341 ret.append(opengl.GLVBO(pointList))
1344 def _generateGCodeVBOs2(self, layer):
1345 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1346 filamentArea = math.pi * filamentRadius * filamentRadius
1347 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1350 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1351 if ':' in extrudeType:
1352 extruder = int(extrudeType[extrudeType.find(':')+1:])
1353 extrudeType = extrudeType[0:extrudeType.find(':')]
1356 pointList = numpy.zeros((0,3), numpy.float32)
1358 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1360 if extrudeType == 'FILL':
1363 normal = a[1:] - a[:-1]
1364 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1365 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1368 ePerDist = path['extrusion'][1:] / lens
1370 lineWidth = ePerDist / path['layerThickness'] / 2.0
1372 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1374 normal[:,0] *= lineWidth
1375 normal[:,1] *= lineWidth
1377 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1378 b = numpy.concatenate((b, a[1:] + normal), 1)
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 = b.reshape((len(b) * 4, 3))
1385 normal2 = normal[:-1] + normal[1:]
1386 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1387 normal2[:,0] /= lens2
1388 normal2[:,1] /= lens2
1389 normal2[:,0] *= lineWidth[:-1]
1390 normal2[:,1] *= lineWidth[:-1]
1392 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1393 c = numpy.concatenate((c, a[1:-1]), 1)
1394 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1395 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1396 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1398 c = numpy.concatenate((c, a[1:-1]), 1)
1399 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1400 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1401 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1403 c = c.reshape((len(c) * 8, 3))
1405 pointList = numpy.concatenate((pointList, b, c))
1407 pointList = numpy.concatenate((pointList, b))
1408 ret.append(opengl.GLVBO(pointList))
1410 pointList = numpy.zeros((0,3), numpy.float32)
1412 if path['type'] == 'move':
1413 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1414 a = numpy.concatenate((a[:-1], a[1:]), 1)
1415 a = a.reshape((len(a) * 2, 3))
1416 pointList = numpy.concatenate((pointList, a))
1417 if path['type'] == 'retract':
1418 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1419 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1420 a = a.reshape((len(a) * 2, 3))
1421 pointList = numpy.concatenate((pointList, a))
1422 ret.append(opengl.GLVBO(pointList))
1426 def getObjectCenterPos(self):
1427 if self._selectedObj is None:
1428 return [0.0, 0.0, 0.0]
1429 pos = self._selectedObj.getPosition()
1430 size = self._selectedObj.getSize()
1431 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1433 def getObjectBoundaryCircle(self):
1434 if self._selectedObj is None:
1436 return self._selectedObj.getBoundaryCircle()
1438 def getObjectSize(self):
1439 if self._selectedObj is None:
1440 return [0.0, 0.0, 0.0]
1441 return self._selectedObj.getSize()
1443 def getObjectMatrix(self):
1444 if self._selectedObj is None:
1445 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1446 return self._selectedObj.getMatrix()
1448 class shaderEditor(wx.Dialog):
1449 def __init__(self, parent, callback, v, f):
1450 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1451 self._callback = callback
1452 s = wx.BoxSizer(wx.VERTICAL)
1454 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1455 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1456 s.Add(self._vertex, 1, flag=wx.EXPAND)
1457 s.Add(self._fragment, 1, flag=wx.EXPAND)
1459 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1460 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1462 self.SetPosition(self.GetParent().GetPosition())
1463 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1466 def OnText(self, e):
1467 self._callback(self._vertex.GetValue(), self._fragment.GetValue())