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 text = '%s' % (self._slicer.getPrintTime())
510 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
511 amount = self._slicer.getFilamentAmount(e)
514 text += '\n%s' % (amount)
515 cost = self._slicer.getFilamentCost(e)
517 text += '\n%s' % (cost)
518 self.printButton.setBottomText(text)
519 self._gcode = gcodeInterpreter.gcode()
520 self._gcodeFilename = self._slicer.getGCodeFilename()
522 self.printButton.setBottomText('')
525 def _loadGCode(self):
526 self._gcode.progressCallback = self._gcodeLoadCallback
527 self._gcode.load(self._gcodeFilename)
529 def _gcodeLoadCallback(self, progress):
530 if not self or self._gcode is None:
532 if len(self._gcode.layerList) % 15 == 0:
534 if self._gcode is None:
536 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
537 if self.viewMode == 'gcode':
541 def loadScene(self, fileList):
542 for filename in fileList:
544 ext = os.path.splitext(filename)[1].lower()
545 if ext in imageToMesh.supportedExtensions():
546 imageToMesh.convertImageDialog(self, filename).Show()
549 objList = meshLoader.loadMeshes(filename)
551 traceback.print_exc()
554 if self._objectLoadShader is not None:
555 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
559 self._scene.centerAll()
560 self._selectObject(obj)
561 if obj.getScale()[0] < 1.0:
562 self.notification.message("Warning: Object scaled down.")
565 def _deleteObject(self, obj):
566 if obj == self._selectedObj:
567 self._selectObject(None)
568 if obj == self._focusObj:
569 self._focusObj = None
570 self._scene.remove(obj)
571 for m in obj._meshList:
572 if m.vbo is not None and m.vbo.decRef():
573 self.glReleaseList.append(m.vbo)
578 def _selectObject(self, obj, zoom = True):
579 if obj != self._selectedObj:
580 self._selectedObj = obj
581 self.updateProfileToControls()
582 self.updateToolButtons()
583 if zoom and obj is not None:
584 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
585 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
586 newZoom = obj.getBoundaryCircle() * 6
587 if newZoom > numpy.max(self._machineSize) * 3:
588 newZoom = numpy.max(self._machineSize) * 3
589 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
591 def updateProfileToControls(self):
592 oldSimpleMode = self._isSimpleMode
593 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
594 if self._isSimpleMode != oldSimpleMode:
595 self._scene.arrangeAll()
597 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
598 self._objColors[0] = profile.getPreferenceColour('model_colour')
599 self._objColors[1] = profile.getPreferenceColour('model_colour2')
600 self._objColors[2] = profile.getPreferenceColour('model_colour3')
601 self._objColors[3] = profile.getPreferenceColour('model_colour4')
602 self._scene.setMachineSize(self._machineSize)
603 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
604 self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height'))
606 if self._selectedObj is not None:
607 scale = self._selectedObj.getScale()
608 size = self._selectedObj.getSize()
609 self.scaleXctrl.setValue(round(scale[0], 2))
610 self.scaleYctrl.setValue(round(scale[1], 2))
611 self.scaleZctrl.setValue(round(scale[2], 2))
612 self.scaleXmmctrl.setValue(round(size[0], 2))
613 self.scaleYmmctrl.setValue(round(size[1], 2))
614 self.scaleZmmctrl.setValue(round(size[2], 2))
616 def OnKeyChar(self, keyCode):
617 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
618 if self._selectedObj is not None:
619 self._deleteObject(self._selectedObj)
621 if keyCode == wx.WXK_UP:
622 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
624 elif keyCode == wx.WXK_DOWN:
625 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
627 elif keyCode == wx.WXK_PAGEUP:
628 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
630 elif keyCode == wx.WXK_PAGEDOWN:
631 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
634 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
635 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
636 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
637 from collections import defaultdict
638 from gc import get_objects
639 self._beforeLeakTest = defaultdict(int)
640 for i in get_objects():
641 self._beforeLeakTest[type(i)] += 1
642 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
643 from collections import defaultdict
644 from gc import get_objects
645 self._afterLeakTest = defaultdict(int)
646 for i in get_objects():
647 self._afterLeakTest[type(i)] += 1
648 for k in self._afterLeakTest:
649 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
650 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
652 def ShaderUpdate(self, v, f):
653 s = opengl.GLShader(v, f)
655 self._objectLoadShader.release()
656 self._objectLoadShader = s
657 for obj in self._scene.objects():
658 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
661 def OnMouseDown(self,e):
662 self._mouseX = e.GetX()
663 self._mouseY = e.GetY()
664 self._mouseClick3DPos = self._mouse3Dpos
665 self._mouseClickFocus = self._focusObj
667 self._mouseState = 'doubleClick'
669 self._mouseState = 'dragOrClick'
670 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
671 p0 -= self.getObjectCenterPos() - self._viewTarget
672 p1 -= self.getObjectCenterPos() - self._viewTarget
673 if self.tool.OnDragStart(p0, p1):
674 self._mouseState = 'tool'
675 if self._mouseState == 'dragOrClick':
676 if e.GetButton() == 1:
677 if self._focusObj is not None:
678 self._selectObject(self._focusObj, False)
681 def OnMouseUp(self, e):
682 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
684 if self._mouseState == 'dragOrClick':
685 if e.GetButton() == 1:
686 self._selectObject(self._focusObj)
687 if e.GetButton() == 3:
689 if self._focusObj is not None:
690 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
691 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
692 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
693 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
694 if ((self._selectedObj != self._focusObj and self._focusObj is not None and self._selectedObj is not None) or len(self._scene.objects()) == 2) and int(profile.getMachineSetting('extruder_amount')) > 1:
695 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
696 if len(self._scene.objects()) > 0:
697 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
698 if menu.MenuItemCount > 0:
701 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
702 self._scene.pushFree()
704 elif self._mouseState == 'tool':
705 if self.tempMatrix is not None and self._selectedObj is not None:
706 self._selectedObj.applyMatrix(self.tempMatrix)
707 self._scene.pushFree()
708 self._selectObject(self._selectedObj)
709 self.tempMatrix = None
710 self.tool.OnDragEnd()
712 self._mouseState = None
714 def OnMouseMotion(self,e):
715 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
716 p0 -= self.getObjectCenterPos() - self._viewTarget
717 p1 -= self.getObjectCenterPos() - self._viewTarget
719 if e.Dragging() and self._mouseState is not None:
720 if self._mouseState == 'tool':
721 self.tool.OnDrag(p0, p1)
722 elif not e.LeftIsDown() and e.RightIsDown():
723 self._mouseState = 'drag'
724 if wx.GetKeyState(wx.WXK_SHIFT):
725 a = math.cos(math.radians(self._yaw)) / 3.0
726 b = math.sin(math.radians(self._yaw)) / 3.0
727 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
728 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
729 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
730 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
732 self._yaw += e.GetX() - self._mouseX
733 self._pitch -= e.GetY() - self._mouseY
734 if self._pitch > 170:
738 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
739 self._mouseState = 'drag'
740 self._zoom += e.GetY() - self._mouseY
743 if self._zoom > numpy.max(self._machineSize) * 3:
744 self._zoom = numpy.max(self._machineSize) * 3
745 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
746 self._mouseState = 'dragObject'
747 z = max(0, self._mouseClick3DPos[2])
748 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
749 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
754 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
755 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
756 diff = cursorZ1 - cursorZ0
757 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
758 if not e.Dragging() or self._mouseState != 'tool':
759 self.tool.OnMouseMove(p0, p1)
761 self._mouseX = e.GetX()
762 self._mouseY = e.GetY()
764 def OnMouseWheel(self, e):
765 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
766 delta = max(min(delta,4),-4)
767 self._zoom *= 1.0 - delta / 10.0
770 if self._zoom > numpy.max(self._machineSize) * 3:
771 self._zoom = numpy.max(self._machineSize) * 3
774 def OnMouseLeave(self, e):
778 def getMouseRay(self, x, y):
779 if self._viewport is None:
780 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
781 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
782 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
783 p0 -= self._viewTarget
784 p1 -= self._viewTarget
787 def _init3DView(self):
788 # set viewing projection
789 size = self.GetSize()
790 glViewport(0, 0, size.GetWidth(), size.GetHeight())
793 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
795 glDisable(GL_RESCALE_NORMAL)
796 glDisable(GL_LIGHTING)
798 glEnable(GL_DEPTH_TEST)
799 glDisable(GL_CULL_FACE)
801 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
803 glClearColor(0.8, 0.8, 0.8, 1.0)
807 glMatrixMode(GL_PROJECTION)
809 aspect = float(size.GetWidth()) / float(size.GetHeight())
810 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
812 glMatrixMode(GL_MODELVIEW)
814 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
817 if machineCom.machineIsConnected():
818 self.printButton._imageID = 6
819 self.printButton._tooltip = _("Print")
820 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
821 self.printButton._imageID = 2
822 self.printButton._tooltip = _("Toolpath to SD")
824 self.printButton._imageID = 3
825 self.printButton._tooltip = _("Save toolpath")
827 if self._animView is not None:
828 self._viewTarget = self._animView.getPosition()
829 if self._animView.isDone():
830 self._animView = None
831 if self._animZoom is not None:
832 self._zoom = self._animZoom.getPosition()
833 if self._animZoom.isDone():
834 self._animZoom = None
835 if self.viewMode == 'gcode' and self._gcode is not None:
837 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
840 if self._objectShader is None:
841 if opengl.hasShaderSupport():
842 self._objectShader = opengl.GLShader("""
843 varying float light_amount;
847 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
848 gl_FrontColor = gl_Color;
850 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
854 varying float light_amount;
858 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
861 self._objectOverhangShader = opengl.GLShader("""
862 uniform float cosAngle;
863 uniform mat3 rotMatrix;
864 varying float light_amount;
868 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
869 gl_FrontColor = gl_Color;
871 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
873 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
875 light_amount = -10.0;
879 varying float light_amount;
883 if (light_amount == -10.0)
885 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
887 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
891 self._objectLoadShader = opengl.GLShader("""
892 uniform float intensity;
894 varying float light_amount;
898 vec4 tmp = gl_Vertex;
899 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
900 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
901 gl_Position = gl_ModelViewProjectionMatrix * tmp;
902 gl_FrontColor = gl_Color;
904 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
908 uniform float intensity;
909 varying float light_amount;
913 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
916 if self._objectShader is None or not self._objectShader.isValid():
917 self._objectShader = opengl.GLFakeShader()
918 self._objectOverhangShader = opengl.GLFakeShader()
919 self._objectLoadShader = None
921 glTranslate(0,0,-self._zoom)
922 glRotate(-self._pitch, 1,0,0)
923 glRotate(self._yaw, 0,0,1)
924 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
926 self._viewport = glGetIntegerv(GL_VIEWPORT)
927 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
928 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
930 glClearColor(1,1,1,1)
931 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
933 if self.viewMode != 'gcode':
934 for n in xrange(0, len(self._scene.objects())):
935 obj = self._scene.objects()[n]
936 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
937 self._renderObject(obj)
939 if self._mouseX > -1:
941 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
942 if n < len(self._scene.objects()):
943 self._focusObj = self._scene.objects()[n]
945 self._focusObj = None
946 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
947 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
948 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
949 self._mouse3Dpos -= self._viewTarget
952 glTranslate(0,0,-self._zoom)
953 glRotate(-self._pitch, 1,0,0)
954 glRotate(self._yaw, 0,0,1)
955 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
957 if self.viewMode == 'gcode':
958 if self._gcode is not None and self._gcode.layerList is None:
959 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
960 self._gcodeLoadThread.daemon = True
961 self._gcodeLoadThread.start()
962 if self._gcode is not None and self._gcode.layerList is not None:
964 if profile.getMachineSetting('machine_center_is_zero') != 'True':
965 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
967 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
968 for n in xrange(0, drawUpTill):
969 c = 1.0 - float(drawUpTill - n) / 15
971 if len(self._gcodeVBOs) < n + 1:
972 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
973 if time.time() - t > 0.5:
976 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
977 if n == drawUpTill - 1:
978 if len(self._gcodeVBOs[n]) < 9:
979 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
981 self._gcodeVBOs[n][8].render(GL_QUADS)
983 self._gcodeVBOs[n][9].render(GL_QUADS)
985 self._gcodeVBOs[n][10].render(GL_QUADS)
987 self._gcodeVBOs[n][11].render(GL_QUADS)
990 self._gcodeVBOs[n][12].render(GL_QUADS)
991 glColor3f(c/2, c/2, 0.0)
992 self._gcodeVBOs[n][13].render(GL_QUADS)
994 self._gcodeVBOs[n][14].render(GL_QUADS)
995 self._gcodeVBOs[n][15].render(GL_QUADS)
997 self._gcodeVBOs[n][16].render(GL_LINES)
1000 self._gcodeVBOs[n][0].render(GL_LINES)
1001 glColor3f(c/2, 0, c)
1002 self._gcodeVBOs[n][1].render(GL_LINES)
1003 glColor3f(0, c, c/2)
1004 self._gcodeVBOs[n][2].render(GL_LINES)
1006 self._gcodeVBOs[n][3].render(GL_LINES)
1009 self._gcodeVBOs[n][4].render(GL_LINES)
1010 glColor3f(c/2, c/2, 0.0)
1011 self._gcodeVBOs[n][5].render(GL_LINES)
1013 self._gcodeVBOs[n][6].render(GL_LINES)
1014 self._gcodeVBOs[n][7].render(GL_LINES)
1017 glStencilFunc(GL_ALWAYS, 1, 1)
1018 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1020 if self.viewMode == 'overhang':
1021 self._objectOverhangShader.bind()
1022 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1024 self._objectShader.bind()
1025 for obj in self._scene.objects():
1026 if obj._loadAnim is not None:
1027 if obj._loadAnim.isDone():
1028 obj._loadAnim = None
1032 if self._focusObj == obj:
1034 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1037 if self._selectedObj == obj or self._selectedObj is None:
1038 #If we want transparent, then first render a solid black model to remove the printer size lines.
1039 if self.viewMode == 'transparent':
1040 glColor4f(0, 0, 0, 0)
1041 self._renderObject(obj)
1043 glBlendFunc(GL_ONE, GL_ONE)
1044 glDisable(GL_DEPTH_TEST)
1046 if self.viewMode == 'xray':
1047 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1048 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1049 glEnable(GL_STENCIL_TEST)
1051 if self.viewMode == 'overhang':
1052 if self._selectedObj == obj and self.tempMatrix is not None:
1053 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1055 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1057 if not self._scene.checkPlatform(obj):
1058 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1059 self._renderObject(obj)
1061 self._renderObject(obj, brightness)
1062 glDisable(GL_STENCIL_TEST)
1064 glEnable(GL_DEPTH_TEST)
1065 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1067 if self.viewMode == 'xray':
1070 glEnable(GL_STENCIL_TEST)
1071 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1072 glDisable(GL_DEPTH_TEST)
1073 for i in xrange(2, 15, 2):
1074 glStencilFunc(GL_EQUAL, i, 0xFF)
1075 glColor(float(i)/10, float(i)/10, float(i)/5)
1077 glVertex3f(-1000,-1000,-10)
1078 glVertex3f( 1000,-1000,-10)
1079 glVertex3f( 1000, 1000,-10)
1080 glVertex3f(-1000, 1000,-10)
1082 for i in xrange(1, 15, 2):
1083 glStencilFunc(GL_EQUAL, i, 0xFF)
1084 glColor(float(i)/10, 0, 0)
1086 glVertex3f(-1000,-1000,-10)
1087 glVertex3f( 1000,-1000,-10)
1088 glVertex3f( 1000, 1000,-10)
1089 glVertex3f(-1000, 1000,-10)
1092 glDisable(GL_STENCIL_TEST)
1093 glEnable(GL_DEPTH_TEST)
1095 self._objectShader.unbind()
1097 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1099 if self._objectLoadShader is not None:
1100 self._objectLoadShader.bind()
1101 glColor4f(0.2, 0.6, 1.0, 1.0)
1102 for obj in self._scene.objects():
1103 if obj._loadAnim is None:
1105 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1106 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1107 self._renderObject(obj)
1108 self._objectLoadShader.unbind()
1113 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1115 z = self._usbPrintMonitor.getZ()
1116 size = self._machineSize
1117 glColor4ub(255,255,0,128)
1119 glVertex3f(-size[0]/2,-size[1]/2, z)
1120 glVertex3f( size[0]/2,-size[1]/2, z)
1121 glVertex3f( size[0]/2, size[1]/2, z)
1122 glVertex3f(-size[0]/2, size[1]/2, z)
1125 if self.viewMode == 'gcode':
1126 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1127 glDisable(GL_DEPTH_TEST)
1130 glTranslate(0,-4,-10)
1131 glColor4ub(60,60,60,255)
1132 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1135 #Draw the object box-shadow, so you can see where it will collide with other objects.
1136 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1137 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1139 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1141 glEnable(GL_CULL_FACE)
1142 glColor4f(0,0,0,0.12)
1144 glVertex3f(-size[0], size[1], 0.1)
1145 glVertex3f(-size[0], -size[1], 0.1)
1146 glVertex3f( size[0], -size[1], 0.1)
1147 glVertex3f( size[0], size[1], 0.1)
1149 glDisable(GL_CULL_FACE)
1152 #Draw the outline of the selected object, on top of everything else except the GUI.
1153 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1154 glDisable(GL_DEPTH_TEST)
1155 glEnable(GL_CULL_FACE)
1156 glEnable(GL_STENCIL_TEST)
1158 glStencilFunc(GL_EQUAL, 0, 255)
1160 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1162 glColor4f(1,1,1,0.5)
1163 self._renderObject(self._selectedObj)
1164 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1166 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1167 glDisable(GL_STENCIL_TEST)
1168 glDisable(GL_CULL_FACE)
1169 glEnable(GL_DEPTH_TEST)
1171 if self._selectedObj is not None:
1173 pos = self.getObjectCenterPos()
1174 glTranslate(pos[0], pos[1], pos[2])
1177 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1178 glDisable(GL_DEPTH_TEST)
1181 glTranslate(0,-4,-10)
1182 glColor4ub(60,60,60,255)
1183 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1186 def _renderObject(self, obj, brightness = False, addSink = True):
1189 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1191 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1193 if self.tempMatrix is not None and obj == self._selectedObj:
1194 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1195 glMultMatrixf(tempMatrix)
1197 offset = obj.getDrawOffset()
1198 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1200 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1201 glMultMatrixf(tempMatrix)
1204 for m in obj._meshList:
1206 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1208 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1213 def _drawMachine(self):
1214 glEnable(GL_CULL_FACE)
1217 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1219 machine = profile.getMachineSetting('machine_type')
1220 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1221 if machine not in self._platformMesh:
1222 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1224 self._platformMesh[machine] = meshes[0]
1226 self._platformMesh[machine] = None
1227 if machine == 'ultimaker2':
1228 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1230 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1231 glColor4f(1,1,1,0.5)
1232 self._objectShader.bind()
1233 self._renderObject(self._platformMesh[machine], False, False)
1234 self._objectShader.unbind()
1236 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1237 if machine == 'ultimaker2':
1238 if not hasattr(self._platformMesh[machine], 'texture'):
1239 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1240 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1241 glEnable(GL_TEXTURE_2D)
1245 glTranslate(0,150,-5)
1250 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1253 glVertex3f( w, 0, h)
1255 glVertex3f(-w, 0, h)
1257 glVertex3f(-w, 0, 0)
1259 glVertex3f( w, 0, 0)
1262 glVertex3f(-w, d, h)
1264 glVertex3f( w, d, h)
1266 glVertex3f( w, d, 0)
1268 glVertex3f(-w, d, 0)
1270 glDisable(GL_TEXTURE_2D)
1271 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1277 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1278 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1279 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1280 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1281 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1282 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1285 #Cornerpoints for big blue square
1286 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1287 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1288 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1289 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1290 v4 = [ size[0] / 2, size[1] / 2, 0]
1291 v5 = [ size[0] / 2,-size[1] / 2, 0]
1292 v6 = [-size[0] / 2, size[1] / 2, 0]
1293 v7 = [-size[0] / 2,-size[1] / 2, 0]
1295 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1296 glEnableClientState(GL_VERTEX_ARRAY)
1297 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1299 glColor4ub(5, 171, 231, 64)
1300 glDrawArrays(GL_QUADS, 0, 4)
1301 glColor4ub(5, 171, 231, 96)
1302 glDrawArrays(GL_QUADS, 4, 8)
1303 glColor4ub(5, 171, 231, 128)
1304 glDrawArrays(GL_QUADS, 12, 8)
1305 glDisableClientState(GL_VERTEX_ARRAY)
1308 sx = self._machineSize[0]
1309 sy = self._machineSize[1]
1310 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1311 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1316 x1 = max(min(x1, sx/2), -sx/2)
1317 y1 = max(min(y1, sy/2), -sy/2)
1318 x2 = max(min(x2, sx/2), -sx/2)
1319 y2 = max(min(y2, sy/2), -sy/2)
1320 #Black or "white" checker
1321 if (x & 1) == (y & 1):
1322 glColor4ub(5, 171, 231, 127)
1324 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1326 glVertex3f(x1, y1, -0.02) #Draw bit below z0 to prevent zfighting.
1327 glVertex3f(x2, y1, -0.02)
1328 glVertex3f(x2, y2, -0.02)
1329 glVertex3f(x1, y2, -0.02)
1332 if machine == 'ultimaker2':
1334 glColor4ub(127, 127, 127, 200)
1335 #if UM2, draw bat-area zone for head. THe head can't stop there, because its bat-area.
1339 posX = sx / 2 - clipWidth;
1340 posY = sy / 2 - clipHeight;
1342 glVertex3f(posX, posY, 0.1)
1343 glVertex3f(posX+clipWidth, posY, 0.1)
1344 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1345 glVertex3f(posX, posY+clipHeight, 0.1)
1351 posY = sy / 2 - clipHeight;
1353 glVertex3f(posX, posY, 0.1)
1354 glVertex3f(posX+clipWidth, posY, 0.1)
1355 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1356 glVertex3f(posX, posY+clipHeight, 0.1)
1361 posX = sx / 2 - clipWidth;
1364 glVertex3f(posX, posY, 0.1)
1365 glVertex3f(posX+clipWidth, posY, 0.1)
1366 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1367 glVertex3f(posX, posY+clipHeight, 0.1)
1375 glVertex3f(posX, posY, 0.1)
1376 glVertex3f(posX+clipWidth, posY, 0.1)
1377 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1378 glVertex3f(posX, posY+clipHeight, 0.1)
1384 glDisable(GL_CULL_FACE)
1386 def _generateGCodeVBOs(self, layer):
1388 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1389 if ':' in extrudeType:
1390 extruder = int(extrudeType[extrudeType.find(':')+1:])
1391 extrudeType = extrudeType[0:extrudeType.find(':')]
1394 pointList = numpy.zeros((0,3), numpy.float32)
1396 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1398 a = numpy.concatenate((a[:-1], a[1:]), 1)
1399 a = a.reshape((len(a) * 2, 3))
1400 pointList = numpy.concatenate((pointList, a))
1401 ret.append(opengl.GLVBO(pointList))
1404 def _generateGCodeVBOs2(self, layer):
1405 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1406 filamentArea = math.pi * filamentRadius * filamentRadius
1407 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1410 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1411 if ':' in extrudeType:
1412 extruder = int(extrudeType[extrudeType.find(':')+1:])
1413 extrudeType = extrudeType[0:extrudeType.find(':')]
1416 pointList = numpy.zeros((0,3), numpy.float32)
1418 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1420 if extrudeType == 'FILL':
1423 normal = a[1:] - a[:-1]
1424 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1425 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1428 ePerDist = path['extrusion'][1:] / lens
1430 lineWidth = ePerDist / path['layerThickness'] / 2.0
1432 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1434 normal[:,0] *= lineWidth
1435 normal[:,1] *= lineWidth
1437 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1438 b = numpy.concatenate((b, a[1:] + normal), 1)
1439 b = numpy.concatenate((b, a[1:] - normal), 1)
1440 b = numpy.concatenate((b, a[:-1] - normal), 1)
1441 b = numpy.concatenate((b, a[:-1] + normal), 1)
1442 b = b.reshape((len(b) * 4, 3))
1445 normal2 = normal[:-1] + normal[1:]
1446 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1447 normal2[:,0] /= lens2
1448 normal2[:,1] /= lens2
1449 normal2[:,0] *= lineWidth[:-1]
1450 normal2[:,1] *= lineWidth[:-1]
1452 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1453 c = numpy.concatenate((c, a[1:-1]), 1)
1454 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1455 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1456 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1458 c = numpy.concatenate((c, a[1:-1]), 1)
1459 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1460 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1461 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1463 c = c.reshape((len(c) * 8, 3))
1465 pointList = numpy.concatenate((pointList, b, c))
1467 pointList = numpy.concatenate((pointList, b))
1468 ret.append(opengl.GLVBO(pointList))
1470 pointList = numpy.zeros((0,3), numpy.float32)
1472 if path['type'] == 'move':
1473 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1474 a = numpy.concatenate((a[:-1], a[1:]), 1)
1475 a = a.reshape((len(a) * 2, 3))
1476 pointList = numpy.concatenate((pointList, a))
1477 if path['type'] == 'retract':
1478 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1479 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1480 a = a.reshape((len(a) * 2, 3))
1481 pointList = numpy.concatenate((pointList, a))
1482 ret.append(opengl.GLVBO(pointList))
1486 def getObjectCenterPos(self):
1487 if self._selectedObj is None:
1488 return [0.0, 0.0, 0.0]
1489 pos = self._selectedObj.getPosition()
1490 size = self._selectedObj.getSize()
1491 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1493 def getObjectBoundaryCircle(self):
1494 if self._selectedObj is None:
1496 return self._selectedObj.getBoundaryCircle()
1498 def getObjectSize(self):
1499 if self._selectedObj is None:
1500 return [0.0, 0.0, 0.0]
1501 return self._selectedObj.getSize()
1503 def getObjectMatrix(self):
1504 if self._selectedObj is None:
1505 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1506 return self._selectedObj.getMatrix()
1508 class shaderEditor(wx.Dialog):
1509 def __init__(self, parent, callback, v, f):
1510 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1511 self._callback = callback
1512 s = wx.BoxSizer(wx.VERTICAL)
1514 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1515 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1516 s.Add(self._vertex, 1, flag=wx.EXPAND)
1517 s.Add(self._fragment, 1, flag=wx.EXPAND)
1519 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1520 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1522 self.SetPosition(self.GetParent().GetPosition())
1523 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1526 def OnText(self, e):
1527 self._callback(self._vertex.GetValue(), self._fragment.GetValue())