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
32 class SceneView(openglGui.glGuiPanel):
33 def __init__(self, parent):
34 super(SceneView, self).__init__(parent)
39 self._scene = objectScene.Scene()
42 self._gcodeFilename = None
43 self._gcodeLoadThread = None
44 self._objectShader = None
45 self._objectLoadShader = None
47 self._selectedObj = None
48 self._objColors = [None,None,None,None]
51 self._mouseState = None
52 self._viewTarget = numpy.array([0,0,0], numpy.float32)
55 self._platformMesh = {}
56 self._isSimpleMode = True
57 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
60 self._modelMatrix = None
61 self._projMatrix = None
62 self.tempMatrix = None
64 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
65 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
66 self.printButton.setDisabled(True)
69 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
70 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
71 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
73 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
74 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
76 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
77 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
79 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
80 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
81 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
83 self.rotateToolButton.setExpandArrow(True)
84 self.scaleToolButton.setExpandArrow(True)
85 self.mirrorToolButton.setExpandArrow(True)
87 self.scaleForm = openglGui.glFrame(self, (2, -2))
88 openglGui.glGuiLayoutGrid(self.scaleForm)
89 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
90 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
91 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
92 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
93 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
94 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
95 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
96 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
97 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
98 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
99 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
100 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
101 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
102 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
104 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
105 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
107 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
108 self.youMagineButton.setDisabled(True)
110 self.notification = openglGui.glNotification(self, (0, 0))
112 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
113 self._sceneUpdateTimer = wx.Timer(self)
114 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
115 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
116 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
120 self.updateToolButtons()
121 self.updateProfileToControls()
123 def loadGCodeFile(self, filename):
124 self.OnDeleteAll(None)
125 if self._gcode is not None:
127 for layerVBOlist in self._gcodeVBOs:
128 for vbo in layerVBOlist:
129 self.glReleaseList.append(vbo)
131 self._gcode = gcodeInterpreter.gcode()
132 self._gcodeFilename = filename
133 self.printButton.setBottomText('')
134 self.viewSelection.setValue(4)
135 self.printButton.setDisabled(False)
136 self.youMagineButton.setDisabled(True)
139 def loadSceneFiles(self, filenames):
140 self.youMagineButton.setDisabled(False)
141 #if self.viewSelection.getValue() == 4:
142 # self.viewSelection.setValue(0)
143 # self.OnViewChange()
144 self.loadScene(filenames)
146 def loadFiles(self, filenames):
147 mainWindow = self.GetParent().GetParent().GetParent()
148 # only one GCODE file can be active
149 # so if single gcode file, process this
150 # otherwise ignore all gcode files
152 if len(filenames) == 1:
153 filename = filenames[0]
154 ext = filename[filename.rfind('.'):].lower()
155 if ext == '.g' or ext == '.gcode':
156 gcodeFilename = filename
157 mainWindow.addToModelMRU(filename)
158 if gcodeFilename is not None:
159 self.loadGCodeFile(gcodeFilename)
161 # process directories and special file types
162 # and keep scene files for later processing
164 ignored_types = dict()
165 # use file list as queue
166 # pop first entry for processing and append new files at end
168 filename = filenames.pop(0)
169 if os.path.isdir(filename):
170 # directory: queue all included files and directories
171 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
173 ext = filename[filename.rfind('.'):].lower()
175 profile.loadProfile(filename)
176 mainWindow.addToProfileMRU(filename)
177 elif ext in meshLoader.loadSupportedExtensions():
178 scene_filenames.append(filename)
179 mainWindow.addToModelMRU(filename)
181 ignored_types[ext] = 1
183 ignored_types = ignored_types.keys()
185 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
186 mainWindow.updateProfileToAllControls()
187 # now process all the scene files
189 self.loadSceneFiles(scene_filenames)
190 self._selectObject(None)
192 newZoom = numpy.max(self._machineSize)
193 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
194 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
196 def showLoadModel(self, button = 1):
198 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)
199 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
200 if dlg.ShowModal() != wx.ID_OK:
203 filenames = dlg.GetPaths()
205 if len(filenames) < 1:
207 profile.putPreference('lastFile', filenames[0])
208 self.loadFiles(filenames)
210 def showSaveModel(self):
211 if len(self._scene.objects()) < 1:
213 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
214 dlg.SetWildcard(meshLoader.saveWildcardFilter())
215 if dlg.ShowModal() != wx.ID_OK:
218 filename = dlg.GetPath()
220 meshLoader.saveMeshes(filename, self._scene.objects())
222 def OnPrintButton(self, button):
224 if machineCom.machineIsConnected():
225 self.showPrintWindow()
226 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
227 drives = removableStorage.getPossibleSDcardDrives()
229 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))
230 if dlg.ShowModal() != wx.ID_OK:
233 drive = drives[dlg.GetSelection()]
237 filename = self._scene._objectList[0].getName() + '.gcode'
238 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
243 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
244 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
245 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
249 def showPrintWindow(self):
250 if self._gcodeFilename is None:
252 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
253 if self._gcodeFilename == self._slicer.getGCodeFilename():
254 self._slicer.submitSliceInfoOnline()
256 def showSaveGCode(self):
257 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
258 dlg.SetFilename(self._scene._objectList[0].getName()+'.gcode')
259 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
260 if dlg.ShowModal() != wx.ID_OK:
263 filename = dlg.GetPath()
266 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
268 def _copyFile(self, fileA, fileB, allowEject = False):
270 size = float(os.stat(fileA).st_size)
271 with open(fileA, 'rb') as fsrc:
272 with open(fileB, 'wb') as fdst:
274 buf = fsrc.read(16*1024)
278 self.printButton.setProgressBar(float(fsrc.tell()) / size)
283 self.notification.message("Failed to save")
286 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...'))
288 self.notification.message("Saved as %s" % (fileB))
289 self.printButton.setProgressBar(None)
290 if fileA == self._slicer.getGCodeFilename():
291 self._slicer.submitSliceInfoOnline()
293 def _showSliceLog(self):
294 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
298 def OnToolSelect(self, button):
299 if self.rotateToolButton.getSelected():
300 self.tool = previewTools.toolRotate(self)
301 elif self.scaleToolButton.getSelected():
302 self.tool = previewTools.toolScale(self)
303 elif self.mirrorToolButton.getSelected():
304 self.tool = previewTools.toolNone(self)
306 self.tool = previewTools.toolNone(self)
307 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
308 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
309 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
310 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
311 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
312 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
313 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
314 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
316 def updateToolButtons(self):
317 if self._selectedObj is None:
321 self.rotateToolButton.setHidden(hidden)
322 self.scaleToolButton.setHidden(hidden)
323 self.mirrorToolButton.setHidden(hidden)
325 self.rotateToolButton.setSelected(False)
326 self.scaleToolButton.setSelected(False)
327 self.mirrorToolButton.setSelected(False)
330 def OnViewChange(self):
331 if self.viewSelection.getValue() == 4:
332 self.viewMode = 'gcode'
333 if self._gcode is not None and self._gcode.layerList is not None:
334 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
335 self._selectObject(None)
336 elif self.viewSelection.getValue() == 1:
337 self.viewMode = 'overhang'
338 elif self.viewSelection.getValue() == 2:
339 self.viewMode = 'transparent'
340 elif self.viewSelection.getValue() == 3:
341 self.viewMode = 'xray'
343 self.viewMode = 'normal'
344 self.layerSelect.setHidden(self.viewMode != 'gcode')
347 def OnRotateReset(self, button):
348 if self._selectedObj is None:
350 self._selectedObj.resetRotation()
351 self._scene.pushFree()
352 self._selectObject(self._selectedObj)
355 def OnLayFlat(self, button):
356 if self._selectedObj is None:
358 self._selectedObj.layFlat()
359 self._scene.pushFree()
360 self._selectObject(self._selectedObj)
363 def OnScaleReset(self, button):
364 if self._selectedObj is None:
366 self._selectedObj.resetScale()
367 self._selectObject(self._selectedObj)
368 self.updateProfileToControls()
371 def OnScaleMax(self, button):
372 if self._selectedObj is None:
374 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
375 self._scene.pushFree()
376 self._selectObject(self._selectedObj)
377 self.updateProfileToControls()
380 def OnMirror(self, axis):
381 if self._selectedObj is None:
383 self._selectedObj.mirror(axis)
386 def OnScaleEntry(self, value, axis):
387 if self._selectedObj is None:
393 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
394 self.updateProfileToControls()
395 self._scene.pushFree()
396 self._selectObject(self._selectedObj)
399 def OnScaleEntryMM(self, value, axis):
400 if self._selectedObj is None:
406 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
407 self.updateProfileToControls()
408 self._scene.pushFree()
409 self._selectObject(self._selectedObj)
412 def OnDeleteAll(self, e):
413 while len(self._scene.objects()) > 0:
414 self._deleteObject(self._scene.objects()[0])
415 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
417 def OnMultiply(self, e):
418 if self._focusObj is None:
421 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
422 if dlg.ShowModal() != wx.ID_OK:
431 self._scene.add(newObj)
432 self._scene.centerAll()
433 if not self._scene.checkPlatform(newObj):
438 self.notification.message("Could not create more then %d items" % (n - 1))
439 self._scene.remove(newObj)
440 self._scene.centerAll()
443 def OnSplitObject(self, e):
444 if self._focusObj is None:
446 self._scene.remove(self._focusObj)
447 for obj in self._focusObj.split(self._splitCallback):
448 if numpy.max(obj.getSize()) > 2.0:
450 self._scene.centerAll()
451 self._selectObject(None)
454 def OnCenter(self, e):
455 if self._focusObj is None:
457 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
458 self._scene.pushFree()
460 def _splitCallback(self, progress):
463 def OnMergeObjects(self, e):
464 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
465 if len(self._scene.objects()) == 2:
466 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
469 self._scene.merge(self._selectedObj, self._focusObj)
472 def sceneUpdated(self):
473 self._sceneUpdateTimer.Start(500, True)
474 self._slicer.abortSlicer()
475 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
478 def _onRunSlicer(self, e):
479 if self._isSimpleMode:
480 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
481 self._slicer.runSlicer(self._scene)
482 if self._isSimpleMode:
483 profile.resetTempOverride()
485 def _updateSliceProgress(self, progressValue, ready):
487 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
489 self.printButton.setDisabled(not ready)
490 if progressValue >= 0.0:
491 self.printButton.setProgressBar(progressValue)
493 self.printButton.setProgressBar(None)
494 if self._gcode is not None:
496 for layerVBOlist in self._gcodeVBOs:
497 for vbo in layerVBOlist:
498 self.glReleaseList.append(vbo)
501 self.printButton.setProgressBar(None)
502 cost = self._slicer.getFilamentCost()
504 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
506 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
507 self._gcode = gcodeInterpreter.gcode()
508 self._gcodeFilename = self._slicer.getGCodeFilename()
510 self.printButton.setBottomText('')
513 def _loadGCode(self):
514 self._gcode.progressCallback = self._gcodeLoadCallback
515 self._gcode.load(self._gcodeFilename)
517 def _gcodeLoadCallback(self, progress):
518 if self._gcode is None:
520 if len(self._gcode.layerList) % 15 == 0:
522 if self._gcode is None:
524 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
525 if self.viewMode == 'gcode':
529 def loadScene(self, fileList):
530 for filename in fileList:
532 objList = meshLoader.loadMeshes(filename)
534 traceback.print_exc()
537 if self._objectLoadShader is not None:
538 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
542 self._scene.centerAll()
543 self._selectObject(obj)
544 if obj.getScale()[0] < 1.0:
545 self.notification.message("Warning: Object scaled down.")
548 def _deleteObject(self, obj):
549 if obj == self._selectedObj:
550 self._selectObject(None)
551 if obj == self._focusObj:
552 self._focusObj = None
553 self._scene.remove(obj)
554 for m in obj._meshList:
555 if m.vbo is not None and m.vbo.decRef():
556 self.glReleaseList.append(m.vbo)
561 def _selectObject(self, obj, zoom = True):
562 if obj != self._selectedObj:
563 self._selectedObj = obj
564 self.updateProfileToControls()
565 self.updateToolButtons()
566 if zoom and obj is not None:
567 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
568 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
569 newZoom = obj.getBoundaryCircle() * 6
570 if newZoom > numpy.max(self._machineSize) * 3:
571 newZoom = numpy.max(self._machineSize) * 3
572 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
574 def updateProfileToControls(self):
575 oldSimpleMode = self._isSimpleMode
576 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
577 if self._isSimpleMode != oldSimpleMode:
578 self._scene.arrangeAll()
580 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
581 self._objColors[0] = profile.getPreferenceColour('model_colour')
582 self._objColors[1] = profile.getPreferenceColour('model_colour2')
583 self._objColors[2] = profile.getPreferenceColour('model_colour3')
584 self._objColors[3] = profile.getPreferenceColour('model_colour4')
585 self._scene.setMachineSize(self._machineSize)
586 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
587 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'))
589 if self._selectedObj is not None:
590 scale = self._selectedObj.getScale()
591 size = self._selectedObj.getSize()
592 self.scaleXctrl.setValue(round(scale[0], 2))
593 self.scaleYctrl.setValue(round(scale[1], 2))
594 self.scaleZctrl.setValue(round(scale[2], 2))
595 self.scaleXmmctrl.setValue(round(size[0], 2))
596 self.scaleYmmctrl.setValue(round(size[1], 2))
597 self.scaleZmmctrl.setValue(round(size[2], 2))
599 def OnKeyChar(self, keyCode):
600 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
601 if self._selectedObj is not None:
602 self._deleteObject(self._selectedObj)
604 if keyCode == wx.WXK_UP:
605 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
607 elif keyCode == wx.WXK_DOWN:
608 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
610 elif keyCode == wx.WXK_PAGEUP:
611 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
613 elif keyCode == wx.WXK_PAGEDOWN:
614 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
617 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
618 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
619 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
620 from collections import defaultdict
621 from gc import get_objects
622 self._beforeLeakTest = defaultdict(int)
623 for i in get_objects():
624 self._beforeLeakTest[type(i)] += 1
625 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
626 from collections import defaultdict
627 from gc import get_objects
628 self._afterLeakTest = defaultdict(int)
629 for i in get_objects():
630 self._afterLeakTest[type(i)] += 1
631 for k in self._afterLeakTest:
632 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
633 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
635 def ShaderUpdate(self, v, f):
636 s = opengl.GLShader(v, f)
638 self._objectLoadShader.release()
639 self._objectLoadShader = s
640 for obj in self._scene.objects():
641 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
644 def OnMouseDown(self,e):
645 self._mouseX = e.GetX()
646 self._mouseY = e.GetY()
647 self._mouseClick3DPos = self._mouse3Dpos
648 self._mouseClickFocus = self._focusObj
650 self._mouseState = 'doubleClick'
652 self._mouseState = 'dragOrClick'
653 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
654 p0 -= self.getObjectCenterPos() - self._viewTarget
655 p1 -= self.getObjectCenterPos() - self._viewTarget
656 if self.tool.OnDragStart(p0, p1):
657 self._mouseState = 'tool'
658 if self._mouseState == 'dragOrClick':
659 if e.GetButton() == 1:
660 if self._focusObj is not None:
661 self._selectObject(self._focusObj, False)
664 def OnMouseUp(self, e):
665 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
667 if self._mouseState == 'dragOrClick':
668 if e.GetButton() == 1:
669 self._selectObject(self._focusObj)
670 if e.GetButton() == 3:
672 if self._focusObj is not None:
673 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
674 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
675 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
676 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
677 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:
678 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
679 if len(self._scene.objects()) > 0:
680 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
681 if menu.MenuItemCount > 0:
684 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
685 self._scene.pushFree()
687 elif self._mouseState == 'tool':
688 if self.tempMatrix is not None and self._selectedObj is not None:
689 self._selectedObj.applyMatrix(self.tempMatrix)
690 self._scene.pushFree()
691 self._selectObject(self._selectedObj)
692 self.tempMatrix = None
693 self.tool.OnDragEnd()
695 self._mouseState = None
697 def OnMouseMotion(self,e):
698 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
699 p0 -= self.getObjectCenterPos() - self._viewTarget
700 p1 -= self.getObjectCenterPos() - self._viewTarget
702 if e.Dragging() and self._mouseState is not None:
703 if self._mouseState == 'tool':
704 self.tool.OnDrag(p0, p1)
705 elif not e.LeftIsDown() and e.RightIsDown():
706 self._mouseState = 'drag'
707 if wx.GetKeyState(wx.WXK_SHIFT):
708 a = math.cos(math.radians(self._yaw)) / 3.0
709 b = math.sin(math.radians(self._yaw)) / 3.0
710 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
711 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
712 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
713 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
715 self._yaw += e.GetX() - self._mouseX
716 self._pitch -= e.GetY() - self._mouseY
717 if self._pitch > 170:
721 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
722 self._mouseState = 'drag'
723 self._zoom += e.GetY() - self._mouseY
726 if self._zoom > numpy.max(self._machineSize) * 3:
727 self._zoom = numpy.max(self._machineSize) * 3
728 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
729 self._mouseState = 'dragObject'
730 z = max(0, self._mouseClick3DPos[2])
731 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
732 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
737 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
738 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
739 diff = cursorZ1 - cursorZ0
740 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
741 if not e.Dragging() or self._mouseState != 'tool':
742 self.tool.OnMouseMove(p0, p1)
744 self._mouseX = e.GetX()
745 self._mouseY = e.GetY()
747 def OnMouseWheel(self, e):
748 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
749 delta = max(min(delta,4),-4)
750 self._zoom *= 1.0 - delta / 10.0
753 if self._zoom > numpy.max(self._machineSize) * 3:
754 self._zoom = numpy.max(self._machineSize) * 3
757 def OnMouseLeave(self, e):
761 def getMouseRay(self, x, y):
762 if self._viewport is None:
763 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
764 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
765 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
766 p0 -= self._viewTarget
767 p1 -= self._viewTarget
770 def _init3DView(self):
771 # set viewing projection
772 size = self.GetSize()
773 glViewport(0, 0, size.GetWidth(), size.GetHeight())
776 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
778 glDisable(GL_RESCALE_NORMAL)
779 glDisable(GL_LIGHTING)
781 glEnable(GL_DEPTH_TEST)
782 glDisable(GL_CULL_FACE)
784 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
786 glClearColor(0.8, 0.8, 0.8, 1.0)
790 glMatrixMode(GL_PROJECTION)
792 aspect = float(size.GetWidth()) / float(size.GetHeight())
793 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
795 glMatrixMode(GL_MODELVIEW)
797 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
800 if machineCom.machineIsConnected():
801 self.printButton._imageID = 6
802 self.printButton._tooltip = _("Print")
803 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
804 self.printButton._imageID = 2
805 self.printButton._tooltip = _("Toolpath to SD")
807 self.printButton._imageID = 3
808 self.printButton._tooltip = _("Save toolpath")
810 if self._animView is not None:
811 self._viewTarget = self._animView.getPosition()
812 if self._animView.isDone():
813 self._animView = None
814 if self._animZoom is not None:
815 self._zoom = self._animZoom.getPosition()
816 if self._animZoom.isDone():
817 self._animZoom = None
818 if self.viewMode == 'gcode' and self._gcode is not None:
820 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
823 if self._objectShader is None:
824 if opengl.hasShaderSupport():
825 self._objectShader = opengl.GLShader("""
826 varying float light_amount;
830 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
831 gl_FrontColor = gl_Color;
833 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
837 varying float light_amount;
841 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
844 self._objectOverhangShader = opengl.GLShader("""
845 uniform float cosAngle;
846 uniform mat3 rotMatrix;
847 varying float light_amount;
851 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
852 gl_FrontColor = gl_Color;
854 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
856 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
858 light_amount = -10.0;
862 varying float light_amount;
866 if (light_amount == -10.0)
868 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
870 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
874 self._objectLoadShader = opengl.GLShader("""
875 uniform float intensity;
877 varying float light_amount;
881 vec4 tmp = gl_Vertex;
882 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
883 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
884 gl_Position = gl_ModelViewProjectionMatrix * tmp;
885 gl_FrontColor = gl_Color;
887 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
891 uniform float intensity;
892 varying float light_amount;
896 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
899 if self._objectShader == None or not self._objectShader.isValid():
900 self._objectShader = opengl.GLFakeShader()
901 self._objectOverhangShader = opengl.GLFakeShader()
902 self._objectLoadShader = None
904 glTranslate(0,0,-self._zoom)
905 glRotate(-self._pitch, 1,0,0)
906 glRotate(self._yaw, 0,0,1)
907 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
909 self._viewport = glGetIntegerv(GL_VIEWPORT)
910 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
911 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
913 glClearColor(1,1,1,1)
914 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
916 if self.viewMode != 'gcode':
917 for n in xrange(0, len(self._scene.objects())):
918 obj = self._scene.objects()[n]
919 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
920 self._renderObject(obj)
922 if self._mouseX > -1:
924 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
925 if n < len(self._scene.objects()):
926 self._focusObj = self._scene.objects()[n]
928 self._focusObj = None
929 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
930 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
931 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
932 self._mouse3Dpos -= self._viewTarget
935 glTranslate(0,0,-self._zoom)
936 glRotate(-self._pitch, 1,0,0)
937 glRotate(self._yaw, 0,0,1)
938 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
940 if self.viewMode == 'gcode':
941 if self._gcode is not None and self._gcode.layerList is None:
942 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
943 self._gcodeLoadThread.daemon = True
944 self._gcodeLoadThread.start()
945 if self._gcode is not None and self._gcode.layerList is not None:
947 if profile.getMachineSetting('machine_center_is_zero') != 'True':
948 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
950 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
951 for n in xrange(0, drawUpTill):
952 c = 1.0 - float(drawUpTill - n) / 15
954 if len(self._gcodeVBOs) < n + 1:
955 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
956 if time.time() - t > 0.5:
959 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
960 if n == drawUpTill - 1:
961 if len(self._gcodeVBOs[n]) < 9:
962 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
964 self._gcodeVBOs[n][8].render(GL_QUADS)
966 self._gcodeVBOs[n][9].render(GL_QUADS)
968 self._gcodeVBOs[n][10].render(GL_QUADS)
970 self._gcodeVBOs[n][11].render(GL_QUADS)
973 self._gcodeVBOs[n][12].render(GL_QUADS)
974 glColor3f(c/2, c/2, 0.0)
975 self._gcodeVBOs[n][13].render(GL_QUADS)
977 self._gcodeVBOs[n][14].render(GL_QUADS)
978 self._gcodeVBOs[n][15].render(GL_QUADS)
980 self._gcodeVBOs[n][16].render(GL_LINES)
983 self._gcodeVBOs[n][0].render(GL_LINES)
985 self._gcodeVBOs[n][1].render(GL_LINES)
987 self._gcodeVBOs[n][2].render(GL_LINES)
989 self._gcodeVBOs[n][3].render(GL_LINES)
992 self._gcodeVBOs[n][4].render(GL_LINES)
993 glColor3f(c/2, c/2, 0.0)
994 self._gcodeVBOs[n][5].render(GL_LINES)
996 self._gcodeVBOs[n][6].render(GL_LINES)
997 self._gcodeVBOs[n][7].render(GL_LINES)
1000 glStencilFunc(GL_ALWAYS, 1, 1)
1001 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1003 if self.viewMode == 'overhang':
1004 self._objectOverhangShader.bind()
1005 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1007 self._objectShader.bind()
1008 for obj in self._scene.objects():
1009 if obj._loadAnim is not None:
1010 if obj._loadAnim.isDone():
1011 obj._loadAnim = None
1015 if self._focusObj == obj:
1017 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1020 if self._selectedObj == obj or self._selectedObj is None:
1021 #If we want transparent, then first render a solid black model to remove the printer size lines.
1022 if self.viewMode == 'transparent':
1023 glColor4f(0, 0, 0, 0)
1024 self._renderObject(obj)
1026 glBlendFunc(GL_ONE, GL_ONE)
1027 glDisable(GL_DEPTH_TEST)
1029 if self.viewMode == 'xray':
1030 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1031 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1032 glEnable(GL_STENCIL_TEST)
1034 if self.viewMode == 'overhang':
1035 if self._selectedObj == obj and self.tempMatrix is not None:
1036 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1038 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1040 if not self._scene.checkPlatform(obj):
1041 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1042 self._renderObject(obj)
1044 self._renderObject(obj, brightness)
1045 glDisable(GL_STENCIL_TEST)
1047 glEnable(GL_DEPTH_TEST)
1048 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1050 if self.viewMode == 'xray':
1053 glEnable(GL_STENCIL_TEST)
1054 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1055 glDisable(GL_DEPTH_TEST)
1056 for i in xrange(2, 15, 2):
1057 glStencilFunc(GL_EQUAL, i, 0xFF)
1058 glColor(float(i)/10, float(i)/10, float(i)/5)
1060 glVertex3f(-1000,-1000,-10)
1061 glVertex3f( 1000,-1000,-10)
1062 glVertex3f( 1000, 1000,-10)
1063 glVertex3f(-1000, 1000,-10)
1065 for i in xrange(1, 15, 2):
1066 glStencilFunc(GL_EQUAL, i, 0xFF)
1067 glColor(float(i)/10, 0, 0)
1069 glVertex3f(-1000,-1000,-10)
1070 glVertex3f( 1000,-1000,-10)
1071 glVertex3f( 1000, 1000,-10)
1072 glVertex3f(-1000, 1000,-10)
1075 glDisable(GL_STENCIL_TEST)
1076 glEnable(GL_DEPTH_TEST)
1078 self._objectShader.unbind()
1080 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1082 if self._objectLoadShader is not None:
1083 self._objectLoadShader.bind()
1084 glColor4f(0.2, 0.6, 1.0, 1.0)
1085 for obj in self._scene.objects():
1086 if obj._loadAnim is None:
1088 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1089 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1090 self._renderObject(obj)
1091 self._objectLoadShader.unbind()
1096 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1098 z = self._usbPrintMonitor.getZ()
1099 size = self._machineSize
1100 glColor4ub(255,255,0,128)
1102 glVertex3f(-size[0]/2,-size[1]/2, z)
1103 glVertex3f( size[0]/2,-size[1]/2, z)
1104 glVertex3f( size[0]/2, size[1]/2, z)
1105 glVertex3f(-size[0]/2, size[1]/2, z)
1108 if self.viewMode == 'gcode':
1109 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1110 glDisable(GL_DEPTH_TEST)
1113 glTranslate(0,-4,-10)
1114 glColor4ub(60,60,60,255)
1115 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1118 #Draw the object box-shadow, so you can see where it will collide with other objects.
1119 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1120 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1122 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1124 glEnable(GL_CULL_FACE)
1125 glColor4f(0,0,0,0.12)
1127 glVertex3f(-size[0], size[1], 0.1)
1128 glVertex3f(-size[0], -size[1], 0.1)
1129 glVertex3f( size[0], -size[1], 0.1)
1130 glVertex3f( size[0], size[1], 0.1)
1132 glDisable(GL_CULL_FACE)
1135 #Draw the outline of the selected object, on top of everything else except the GUI.
1136 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1137 glDisable(GL_DEPTH_TEST)
1138 glEnable(GL_CULL_FACE)
1139 glEnable(GL_STENCIL_TEST)
1141 glStencilFunc(GL_EQUAL, 0, 255)
1143 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1145 glColor4f(1,1,1,0.5)
1146 self._renderObject(self._selectedObj)
1147 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1149 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1150 glDisable(GL_STENCIL_TEST)
1151 glDisable(GL_CULL_FACE)
1152 glEnable(GL_DEPTH_TEST)
1154 if self._selectedObj is not None:
1156 pos = self.getObjectCenterPos()
1157 glTranslate(pos[0], pos[1], pos[2])
1160 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1161 glDisable(GL_DEPTH_TEST)
1164 glTranslate(0,-4,-10)
1165 glColor4ub(60,60,60,255)
1166 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1169 def _renderObject(self, obj, brightness = False, addSink = True):
1172 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1174 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1176 if self.tempMatrix is not None and obj == self._selectedObj:
1177 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1178 glMultMatrixf(tempMatrix)
1180 offset = obj.getDrawOffset()
1181 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1183 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1184 glMultMatrixf(tempMatrix)
1187 for m in obj._meshList:
1189 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1191 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1196 def _drawMachine(self):
1197 glEnable(GL_CULL_FACE)
1200 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1202 machine = profile.getMachineSetting('machine_type')
1203 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1204 if machine not in self._platformMesh:
1205 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1207 self._platformMesh[machine] = meshes[0]
1209 self._platformMesh[machine] = None
1210 if machine == 'ultimaker2':
1211 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1213 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1214 glColor4f(1,1,1,0.5)
1215 self._objectShader.bind()
1216 self._renderObject(self._platformMesh[machine], False, False)
1217 self._objectShader.unbind()
1219 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1220 if machine == 'ultimaker2':
1221 if not hasattr(self._platformMesh[machine], 'texture'):
1222 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1223 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1224 glEnable(GL_TEXTURE_2D)
1228 glTranslate(0,150,-5)
1233 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1236 glVertex3f( w, 0, h)
1238 glVertex3f(-w, 0, h)
1240 glVertex3f(-w, 0, 0)
1242 glVertex3f( w, 0, 0)
1245 glVertex3f(-w, d, h)
1247 glVertex3f( w, d, h)
1249 glVertex3f( w, d, 0)
1251 glVertex3f(-w, d, 0)
1253 glDisable(GL_TEXTURE_2D)
1254 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1260 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1261 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1262 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1263 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1264 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1265 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1268 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1269 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1270 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1271 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1272 v4 = [ size[0] / 2, size[1] / 2, 0]
1273 v5 = [ size[0] / 2,-size[1] / 2, 0]
1274 v6 = [-size[0] / 2, size[1] / 2, 0]
1275 v7 = [-size[0] / 2,-size[1] / 2, 0]
1277 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1278 glEnableClientState(GL_VERTEX_ARRAY)
1279 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1281 glColor4ub(5, 171, 231, 64)
1282 glDrawArrays(GL_QUADS, 0, 4)
1283 glColor4ub(5, 171, 231, 96)
1284 glDrawArrays(GL_QUADS, 4, 8)
1285 glColor4ub(5, 171, 231, 128)
1286 glDrawArrays(GL_QUADS, 12, 8)
1287 glDisableClientState(GL_VERTEX_ARRAY)
1289 sx = self._machineSize[0]
1290 sy = self._machineSize[1]
1291 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1292 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1297 x1 = max(min(x1, sx/2), -sx/2)
1298 y1 = max(min(y1, sy/2), -sy/2)
1299 x2 = max(min(x2, sx/2), -sx/2)
1300 y2 = max(min(y2, sy/2), -sy/2)
1301 if (x & 1) == (y & 1):
1302 glColor4ub(5, 171, 231, 127)
1304 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1306 glVertex3f(x1, y1, -0.02)
1307 glVertex3f(x2, y1, -0.02)
1308 glVertex3f(x2, y2, -0.02)
1309 glVertex3f(x1, y2, -0.02)
1313 glDisable(GL_CULL_FACE)
1315 def _generateGCodeVBOs(self, layer):
1317 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1318 if ':' in extrudeType:
1319 extruder = int(extrudeType[extrudeType.find(':')+1:])
1320 extrudeType = extrudeType[0:extrudeType.find(':')]
1323 pointList = numpy.zeros((0,3), numpy.float32)
1325 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1327 a = numpy.concatenate((a[:-1], a[1:]), 1)
1328 a = a.reshape((len(a) * 2, 3))
1329 pointList = numpy.concatenate((pointList, a))
1330 ret.append(opengl.GLVBO(pointList))
1333 def _generateGCodeVBOs2(self, layer):
1334 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1335 filamentArea = math.pi * filamentRadius * filamentRadius
1336 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1339 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1340 if ':' in extrudeType:
1341 extruder = int(extrudeType[extrudeType.find(':')+1:])
1342 extrudeType = extrudeType[0:extrudeType.find(':')]
1345 pointList = numpy.zeros((0,3), numpy.float32)
1347 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1349 if extrudeType == 'FILL':
1352 normal = a[1:] - a[:-1]
1353 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1354 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1357 ePerDist = path['extrusion'][1:] / lens
1359 lineWidth = ePerDist / path['layerThickness'] / 2.0
1361 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1363 normal[:,0] *= lineWidth
1364 normal[:,1] *= lineWidth
1366 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1367 b = numpy.concatenate((b, a[1:] + normal), 1)
1368 b = numpy.concatenate((b, a[1:] - normal), 1)
1369 b = numpy.concatenate((b, a[:-1] - normal), 1)
1370 b = numpy.concatenate((b, a[:-1] + normal), 1)
1371 b = b.reshape((len(b) * 4, 3))
1374 normal2 = normal[:-1] + normal[1:]
1375 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1376 normal2[:,0] /= lens2
1377 normal2[:,1] /= lens2
1378 normal2[:,0] *= lineWidth[:-1]
1379 normal2[:,1] *= lineWidth[:-1]
1381 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1382 c = numpy.concatenate((c, a[1:-1]), 1)
1383 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1384 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1385 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1387 c = numpy.concatenate((c, a[1:-1]), 1)
1388 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1389 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1390 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1392 c = c.reshape((len(c) * 8, 3))
1394 pointList = numpy.concatenate((pointList, b, c))
1396 pointList = numpy.concatenate((pointList, b))
1397 ret.append(opengl.GLVBO(pointList))
1399 pointList = numpy.zeros((0,3), numpy.float32)
1401 if path['type'] == 'move':
1402 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1403 a = numpy.concatenate((a[:-1], a[1:]), 1)
1404 a = a.reshape((len(a) * 2, 3))
1405 pointList = numpy.concatenate((pointList, a))
1406 if path['type'] == 'retract':
1407 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1408 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1409 a = a.reshape((len(a) * 2, 3))
1410 pointList = numpy.concatenate((pointList, a))
1411 ret.append(opengl.GLVBO(pointList))
1415 def getObjectCenterPos(self):
1416 if self._selectedObj is None:
1417 return [0.0, 0.0, 0.0]
1418 pos = self._selectedObj.getPosition()
1419 size = self._selectedObj.getSize()
1420 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1422 def getObjectBoundaryCircle(self):
1423 if self._selectedObj is None:
1425 return self._selectedObj.getBoundaryCircle()
1427 def getObjectSize(self):
1428 if self._selectedObj is None:
1429 return [0.0, 0.0, 0.0]
1430 return self._selectedObj.getSize()
1432 def getObjectMatrix(self):
1433 if self._selectedObj is None:
1434 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1435 return self._selectedObj.getMatrix()
1437 class shaderEditor(wx.Dialog):
1438 def __init__(self, parent, callback, v, f):
1439 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1440 self._callback = callback
1441 s = wx.BoxSizer(wx.VERTICAL)
1443 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1444 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1445 s.Add(self._vertex, 1, flag=wx.EXPAND)
1446 s.Add(self._fragment, 1, flag=wx.EXPAND)
1448 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1449 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1451 self.SetPosition(self.GetParent().GetPosition())
1452 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1455 def OnText(self, e):
1456 self._callback(self._vertex.GetValue(), self._fragment.GetValue())