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 = os.path.splitext(filename)[1].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 = os.path.splitext(filename)[1].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 filename = self._scene._objectList[0].getName() + '.gcode'
259 dlg.SetFilename(filename)
260 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
261 if dlg.ShowModal() != wx.ID_OK:
264 filename = dlg.GetPath()
267 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
269 def _copyFile(self, fileA, fileB, allowEject = False):
271 size = float(os.stat(fileA).st_size)
272 with open(fileA, 'rb') as fsrc:
273 with open(fileB, 'wb') as fdst:
275 buf = fsrc.read(16*1024)
279 self.printButton.setProgressBar(float(fsrc.tell()) / size)
284 self.notification.message("Failed to save")
287 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...'))
289 self.notification.message("Saved as %s" % (fileB))
290 self.printButton.setProgressBar(None)
291 if fileA == self._slicer.getGCodeFilename():
292 self._slicer.submitSliceInfoOnline()
294 def _showSliceLog(self):
295 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
299 def OnToolSelect(self, button):
300 if self.rotateToolButton.getSelected():
301 self.tool = previewTools.toolRotate(self)
302 elif self.scaleToolButton.getSelected():
303 self.tool = previewTools.toolScale(self)
304 elif self.mirrorToolButton.getSelected():
305 self.tool = previewTools.toolNone(self)
307 self.tool = previewTools.toolNone(self)
308 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
309 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
310 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
311 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
312 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
313 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
314 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
315 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
317 def updateToolButtons(self):
318 if self._selectedObj is None:
322 self.rotateToolButton.setHidden(hidden)
323 self.scaleToolButton.setHidden(hidden)
324 self.mirrorToolButton.setHidden(hidden)
326 self.rotateToolButton.setSelected(False)
327 self.scaleToolButton.setSelected(False)
328 self.mirrorToolButton.setSelected(False)
331 def OnViewChange(self):
332 if self.viewSelection.getValue() == 4:
333 self.viewMode = 'gcode'
334 if self._gcode is not None and self._gcode.layerList is not None:
335 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
336 self._selectObject(None)
337 elif self.viewSelection.getValue() == 1:
338 self.viewMode = 'overhang'
339 elif self.viewSelection.getValue() == 2:
340 self.viewMode = 'transparent'
341 elif self.viewSelection.getValue() == 3:
342 self.viewMode = 'xray'
344 self.viewMode = 'normal'
345 self.layerSelect.setHidden(self.viewMode != 'gcode')
348 def OnRotateReset(self, button):
349 if self._selectedObj is None:
351 self._selectedObj.resetRotation()
352 self._scene.pushFree()
353 self._selectObject(self._selectedObj)
356 def OnLayFlat(self, button):
357 if self._selectedObj is None:
359 self._selectedObj.layFlat()
360 self._scene.pushFree()
361 self._selectObject(self._selectedObj)
364 def OnScaleReset(self, button):
365 if self._selectedObj is None:
367 self._selectedObj.resetScale()
368 self._selectObject(self._selectedObj)
369 self.updateProfileToControls()
372 def OnScaleMax(self, button):
373 if self._selectedObj is None:
375 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
376 self._scene.pushFree()
377 self._selectObject(self._selectedObj)
378 self.updateProfileToControls()
381 def OnMirror(self, axis):
382 if self._selectedObj is None:
384 self._selectedObj.mirror(axis)
387 def OnScaleEntry(self, value, axis):
388 if self._selectedObj is None:
394 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
395 self.updateProfileToControls()
396 self._scene.pushFree()
397 self._selectObject(self._selectedObj)
400 def OnScaleEntryMM(self, value, axis):
401 if self._selectedObj is None:
407 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
408 self.updateProfileToControls()
409 self._scene.pushFree()
410 self._selectObject(self._selectedObj)
413 def OnDeleteAll(self, e):
414 while len(self._scene.objects()) > 0:
415 self._deleteObject(self._scene.objects()[0])
416 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
418 def OnMultiply(self, e):
419 if self._focusObj is None:
422 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
423 if dlg.ShowModal() != wx.ID_OK:
432 self._scene.add(newObj)
433 self._scene.centerAll()
434 if not self._scene.checkPlatform(newObj):
439 self.notification.message("Could not create more then %d items" % (n - 1))
440 self._scene.remove(newObj)
441 self._scene.centerAll()
444 def OnSplitObject(self, e):
445 if self._focusObj is None:
447 self._scene.remove(self._focusObj)
448 for obj in self._focusObj.split(self._splitCallback):
449 if numpy.max(obj.getSize()) > 2.0:
451 self._scene.centerAll()
452 self._selectObject(None)
455 def OnCenter(self, e):
456 if self._focusObj is None:
458 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
459 self._scene.pushFree()
461 def _splitCallback(self, progress):
464 def OnMergeObjects(self, e):
465 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
466 if len(self._scene.objects()) == 2:
467 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
470 self._scene.merge(self._selectedObj, self._focusObj)
473 def sceneUpdated(self):
474 self._sceneUpdateTimer.Start(500, True)
475 self._slicer.abortSlicer()
476 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
479 def _onRunSlicer(self, e):
480 if self._isSimpleMode:
481 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
482 self._slicer.runSlicer(self._scene)
483 if self._isSimpleMode:
484 profile.resetTempOverride()
486 def _updateSliceProgress(self, progressValue, ready):
488 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
490 self.printButton.setDisabled(not ready)
491 if progressValue >= 0.0:
492 self.printButton.setProgressBar(progressValue)
494 self.printButton.setProgressBar(None)
495 if self._gcode is not None:
497 for layerVBOlist in self._gcodeVBOs:
498 for vbo in layerVBOlist:
499 self.glReleaseList.append(vbo)
502 self.printButton.setProgressBar(None)
503 cost = self._slicer.getFilamentCost()
505 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
507 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
508 self._gcode = gcodeInterpreter.gcode()
509 self._gcodeFilename = self._slicer.getGCodeFilename()
511 self.printButton.setBottomText('')
514 def _loadGCode(self):
515 self._gcode.progressCallback = self._gcodeLoadCallback
516 self._gcode.load(self._gcodeFilename)
518 def _gcodeLoadCallback(self, progress):
519 if self._gcode is None:
521 if len(self._gcode.layerList) % 15 == 0:
523 if self._gcode is None:
525 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
526 if self.viewMode == 'gcode':
530 def loadScene(self, fileList):
531 for filename in fileList:
533 objList = meshLoader.loadMeshes(filename)
535 traceback.print_exc()
538 if self._objectLoadShader is not None:
539 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
543 self._scene.centerAll()
544 self._selectObject(obj)
545 if obj.getScale()[0] < 1.0:
546 self.notification.message("Warning: Object scaled down.")
549 def _deleteObject(self, obj):
550 if obj == self._selectedObj:
551 self._selectObject(None)
552 if obj == self._focusObj:
553 self._focusObj = None
554 self._scene.remove(obj)
555 for m in obj._meshList:
556 if m.vbo is not None and m.vbo.decRef():
557 self.glReleaseList.append(m.vbo)
562 def _selectObject(self, obj, zoom = True):
563 if obj != self._selectedObj:
564 self._selectedObj = obj
565 self.updateProfileToControls()
566 self.updateToolButtons()
567 if zoom and obj is not None:
568 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
569 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
570 newZoom = obj.getBoundaryCircle() * 6
571 if newZoom > numpy.max(self._machineSize) * 3:
572 newZoom = numpy.max(self._machineSize) * 3
573 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
575 def updateProfileToControls(self):
576 oldSimpleMode = self._isSimpleMode
577 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
578 if self._isSimpleMode != oldSimpleMode:
579 self._scene.arrangeAll()
581 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
582 self._objColors[0] = profile.getPreferenceColour('model_colour')
583 self._objColors[1] = profile.getPreferenceColour('model_colour2')
584 self._objColors[2] = profile.getPreferenceColour('model_colour3')
585 self._objColors[3] = profile.getPreferenceColour('model_colour4')
586 self._scene.setMachineSize(self._machineSize)
587 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
588 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'))
590 if self._selectedObj is not None:
591 scale = self._selectedObj.getScale()
592 size = self._selectedObj.getSize()
593 self.scaleXctrl.setValue(round(scale[0], 2))
594 self.scaleYctrl.setValue(round(scale[1], 2))
595 self.scaleZctrl.setValue(round(scale[2], 2))
596 self.scaleXmmctrl.setValue(round(size[0], 2))
597 self.scaleYmmctrl.setValue(round(size[1], 2))
598 self.scaleZmmctrl.setValue(round(size[2], 2))
600 def OnKeyChar(self, keyCode):
601 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
602 if self._selectedObj is not None:
603 self._deleteObject(self._selectedObj)
605 if keyCode == wx.WXK_UP:
606 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
608 elif keyCode == wx.WXK_DOWN:
609 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
611 elif keyCode == wx.WXK_PAGEUP:
612 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
614 elif keyCode == wx.WXK_PAGEDOWN:
615 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
618 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
619 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
620 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
621 from collections import defaultdict
622 from gc import get_objects
623 self._beforeLeakTest = defaultdict(int)
624 for i in get_objects():
625 self._beforeLeakTest[type(i)] += 1
626 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
627 from collections import defaultdict
628 from gc import get_objects
629 self._afterLeakTest = defaultdict(int)
630 for i in get_objects():
631 self._afterLeakTest[type(i)] += 1
632 for k in self._afterLeakTest:
633 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
634 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
636 def ShaderUpdate(self, v, f):
637 s = opengl.GLShader(v, f)
639 self._objectLoadShader.release()
640 self._objectLoadShader = s
641 for obj in self._scene.objects():
642 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
645 def OnMouseDown(self,e):
646 self._mouseX = e.GetX()
647 self._mouseY = e.GetY()
648 self._mouseClick3DPos = self._mouse3Dpos
649 self._mouseClickFocus = self._focusObj
651 self._mouseState = 'doubleClick'
653 self._mouseState = 'dragOrClick'
654 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
655 p0 -= self.getObjectCenterPos() - self._viewTarget
656 p1 -= self.getObjectCenterPos() - self._viewTarget
657 if self.tool.OnDragStart(p0, p1):
658 self._mouseState = 'tool'
659 if self._mouseState == 'dragOrClick':
660 if e.GetButton() == 1:
661 if self._focusObj is not None:
662 self._selectObject(self._focusObj, False)
665 def OnMouseUp(self, e):
666 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
668 if self._mouseState == 'dragOrClick':
669 if e.GetButton() == 1:
670 self._selectObject(self._focusObj)
671 if e.GetButton() == 3:
673 if self._focusObj is not None:
674 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
675 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
676 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
677 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
678 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:
679 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
680 if len(self._scene.objects()) > 0:
681 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
682 if menu.MenuItemCount > 0:
685 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
686 self._scene.pushFree()
688 elif self._mouseState == 'tool':
689 if self.tempMatrix is not None and self._selectedObj is not None:
690 self._selectedObj.applyMatrix(self.tempMatrix)
691 self._scene.pushFree()
692 self._selectObject(self._selectedObj)
693 self.tempMatrix = None
694 self.tool.OnDragEnd()
696 self._mouseState = None
698 def OnMouseMotion(self,e):
699 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
700 p0 -= self.getObjectCenterPos() - self._viewTarget
701 p1 -= self.getObjectCenterPos() - self._viewTarget
703 if e.Dragging() and self._mouseState is not None:
704 if self._mouseState == 'tool':
705 self.tool.OnDrag(p0, p1)
706 elif not e.LeftIsDown() and e.RightIsDown():
707 self._mouseState = 'drag'
708 if wx.GetKeyState(wx.WXK_SHIFT):
709 a = math.cos(math.radians(self._yaw)) / 3.0
710 b = math.sin(math.radians(self._yaw)) / 3.0
711 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
712 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
713 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
714 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
716 self._yaw += e.GetX() - self._mouseX
717 self._pitch -= e.GetY() - self._mouseY
718 if self._pitch > 170:
722 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
723 self._mouseState = 'drag'
724 self._zoom += e.GetY() - self._mouseY
727 if self._zoom > numpy.max(self._machineSize) * 3:
728 self._zoom = numpy.max(self._machineSize) * 3
729 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
730 self._mouseState = 'dragObject'
731 z = max(0, self._mouseClick3DPos[2])
732 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
733 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
738 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
739 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
740 diff = cursorZ1 - cursorZ0
741 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
742 if not e.Dragging() or self._mouseState != 'tool':
743 self.tool.OnMouseMove(p0, p1)
745 self._mouseX = e.GetX()
746 self._mouseY = e.GetY()
748 def OnMouseWheel(self, e):
749 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
750 delta = max(min(delta,4),-4)
751 self._zoom *= 1.0 - delta / 10.0
754 if self._zoom > numpy.max(self._machineSize) * 3:
755 self._zoom = numpy.max(self._machineSize) * 3
758 def OnMouseLeave(self, e):
762 def getMouseRay(self, x, y):
763 if self._viewport is None:
764 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
765 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
766 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
767 p0 -= self._viewTarget
768 p1 -= self._viewTarget
771 def _init3DView(self):
772 # set viewing projection
773 size = self.GetSize()
774 glViewport(0, 0, size.GetWidth(), size.GetHeight())
777 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
779 glDisable(GL_RESCALE_NORMAL)
780 glDisable(GL_LIGHTING)
782 glEnable(GL_DEPTH_TEST)
783 glDisable(GL_CULL_FACE)
785 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
787 glClearColor(0.8, 0.8, 0.8, 1.0)
791 glMatrixMode(GL_PROJECTION)
793 aspect = float(size.GetWidth()) / float(size.GetHeight())
794 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
796 glMatrixMode(GL_MODELVIEW)
798 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
801 if machineCom.machineIsConnected():
802 self.printButton._imageID = 6
803 self.printButton._tooltip = _("Print")
804 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
805 self.printButton._imageID = 2
806 self.printButton._tooltip = _("Toolpath to SD")
808 self.printButton._imageID = 3
809 self.printButton._tooltip = _("Save toolpath")
811 if self._animView is not None:
812 self._viewTarget = self._animView.getPosition()
813 if self._animView.isDone():
814 self._animView = None
815 if self._animZoom is not None:
816 self._zoom = self._animZoom.getPosition()
817 if self._animZoom.isDone():
818 self._animZoom = None
819 if self.viewMode == 'gcode' and self._gcode is not None:
821 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
824 if self._objectShader is None:
825 if opengl.hasShaderSupport():
826 self._objectShader = opengl.GLShader("""
827 varying float light_amount;
831 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
832 gl_FrontColor = gl_Color;
834 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
838 varying float light_amount;
842 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
845 self._objectOverhangShader = opengl.GLShader("""
846 uniform float cosAngle;
847 uniform mat3 rotMatrix;
848 varying float light_amount;
852 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
853 gl_FrontColor = gl_Color;
855 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
857 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
859 light_amount = -10.0;
863 varying float light_amount;
867 if (light_amount == -10.0)
869 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
871 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
875 self._objectLoadShader = opengl.GLShader("""
876 uniform float intensity;
878 varying float light_amount;
882 vec4 tmp = gl_Vertex;
883 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
884 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
885 gl_Position = gl_ModelViewProjectionMatrix * tmp;
886 gl_FrontColor = gl_Color;
888 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
892 uniform float intensity;
893 varying float light_amount;
897 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
900 if self._objectShader == None or not self._objectShader.isValid():
901 self._objectShader = opengl.GLFakeShader()
902 self._objectOverhangShader = opengl.GLFakeShader()
903 self._objectLoadShader = None
905 glTranslate(0,0,-self._zoom)
906 glRotate(-self._pitch, 1,0,0)
907 glRotate(self._yaw, 0,0,1)
908 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
910 self._viewport = glGetIntegerv(GL_VIEWPORT)
911 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
912 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
914 glClearColor(1,1,1,1)
915 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
917 if self.viewMode != 'gcode':
918 for n in xrange(0, len(self._scene.objects())):
919 obj = self._scene.objects()[n]
920 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
921 self._renderObject(obj)
923 if self._mouseX > -1:
925 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
926 if n < len(self._scene.objects()):
927 self._focusObj = self._scene.objects()[n]
929 self._focusObj = None
930 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
931 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
932 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
933 self._mouse3Dpos -= self._viewTarget
936 glTranslate(0,0,-self._zoom)
937 glRotate(-self._pitch, 1,0,0)
938 glRotate(self._yaw, 0,0,1)
939 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
941 if self.viewMode == 'gcode':
942 if self._gcode is not None and self._gcode.layerList is None:
943 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
944 self._gcodeLoadThread.daemon = True
945 self._gcodeLoadThread.start()
946 if self._gcode is not None and self._gcode.layerList is not None:
948 if profile.getMachineSetting('machine_center_is_zero') != 'True':
949 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
951 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
952 for n in xrange(0, drawUpTill):
953 c = 1.0 - float(drawUpTill - n) / 15
955 if len(self._gcodeVBOs) < n + 1:
956 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
957 if time.time() - t > 0.5:
960 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
961 if n == drawUpTill - 1:
962 if len(self._gcodeVBOs[n]) < 9:
963 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
965 self._gcodeVBOs[n][8].render(GL_QUADS)
967 self._gcodeVBOs[n][9].render(GL_QUADS)
969 self._gcodeVBOs[n][10].render(GL_QUADS)
971 self._gcodeVBOs[n][11].render(GL_QUADS)
974 self._gcodeVBOs[n][12].render(GL_QUADS)
975 glColor3f(c/2, c/2, 0.0)
976 self._gcodeVBOs[n][13].render(GL_QUADS)
978 self._gcodeVBOs[n][14].render(GL_QUADS)
979 self._gcodeVBOs[n][15].render(GL_QUADS)
981 self._gcodeVBOs[n][16].render(GL_LINES)
984 self._gcodeVBOs[n][0].render(GL_LINES)
986 self._gcodeVBOs[n][1].render(GL_LINES)
988 self._gcodeVBOs[n][2].render(GL_LINES)
990 self._gcodeVBOs[n][3].render(GL_LINES)
993 self._gcodeVBOs[n][4].render(GL_LINES)
994 glColor3f(c/2, c/2, 0.0)
995 self._gcodeVBOs[n][5].render(GL_LINES)
997 self._gcodeVBOs[n][6].render(GL_LINES)
998 self._gcodeVBOs[n][7].render(GL_LINES)
1001 glStencilFunc(GL_ALWAYS, 1, 1)
1002 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1004 if self.viewMode == 'overhang':
1005 self._objectOverhangShader.bind()
1006 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1008 self._objectShader.bind()
1009 for obj in self._scene.objects():
1010 if obj._loadAnim is not None:
1011 if obj._loadAnim.isDone():
1012 obj._loadAnim = None
1016 if self._focusObj == obj:
1018 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1021 if self._selectedObj == obj or self._selectedObj is None:
1022 #If we want transparent, then first render a solid black model to remove the printer size lines.
1023 if self.viewMode == 'transparent':
1024 glColor4f(0, 0, 0, 0)
1025 self._renderObject(obj)
1027 glBlendFunc(GL_ONE, GL_ONE)
1028 glDisable(GL_DEPTH_TEST)
1030 if self.viewMode == 'xray':
1031 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1032 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1033 glEnable(GL_STENCIL_TEST)
1035 if self.viewMode == 'overhang':
1036 if self._selectedObj == obj and self.tempMatrix is not None:
1037 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1039 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1041 if not self._scene.checkPlatform(obj):
1042 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1043 self._renderObject(obj)
1045 self._renderObject(obj, brightness)
1046 glDisable(GL_STENCIL_TEST)
1048 glEnable(GL_DEPTH_TEST)
1049 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1051 if self.viewMode == 'xray':
1054 glEnable(GL_STENCIL_TEST)
1055 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1056 glDisable(GL_DEPTH_TEST)
1057 for i in xrange(2, 15, 2):
1058 glStencilFunc(GL_EQUAL, i, 0xFF)
1059 glColor(float(i)/10, float(i)/10, float(i)/5)
1061 glVertex3f(-1000,-1000,-10)
1062 glVertex3f( 1000,-1000,-10)
1063 glVertex3f( 1000, 1000,-10)
1064 glVertex3f(-1000, 1000,-10)
1066 for i in xrange(1, 15, 2):
1067 glStencilFunc(GL_EQUAL, i, 0xFF)
1068 glColor(float(i)/10, 0, 0)
1070 glVertex3f(-1000,-1000,-10)
1071 glVertex3f( 1000,-1000,-10)
1072 glVertex3f( 1000, 1000,-10)
1073 glVertex3f(-1000, 1000,-10)
1076 glDisable(GL_STENCIL_TEST)
1077 glEnable(GL_DEPTH_TEST)
1079 self._objectShader.unbind()
1081 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1083 if self._objectLoadShader is not None:
1084 self._objectLoadShader.bind()
1085 glColor4f(0.2, 0.6, 1.0, 1.0)
1086 for obj in self._scene.objects():
1087 if obj._loadAnim is None:
1089 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1090 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1091 self._renderObject(obj)
1092 self._objectLoadShader.unbind()
1097 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1099 z = self._usbPrintMonitor.getZ()
1100 size = self._machineSize
1101 glColor4ub(255,255,0,128)
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)
1106 glVertex3f(-size[0]/2, size[1]/2, z)
1109 if self.viewMode == 'gcode':
1110 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1111 glDisable(GL_DEPTH_TEST)
1114 glTranslate(0,-4,-10)
1115 glColor4ub(60,60,60,255)
1116 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1119 #Draw the object box-shadow, so you can see where it will collide with other objects.
1120 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1121 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1123 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1125 glEnable(GL_CULL_FACE)
1126 glColor4f(0,0,0,0.12)
1128 glVertex3f(-size[0], size[1], 0.1)
1129 glVertex3f(-size[0], -size[1], 0.1)
1130 glVertex3f( size[0], -size[1], 0.1)
1131 glVertex3f( size[0], size[1], 0.1)
1133 glDisable(GL_CULL_FACE)
1136 #Draw the outline of the selected object, on top of everything else except the GUI.
1137 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1138 glDisable(GL_DEPTH_TEST)
1139 glEnable(GL_CULL_FACE)
1140 glEnable(GL_STENCIL_TEST)
1142 glStencilFunc(GL_EQUAL, 0, 255)
1144 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1146 glColor4f(1,1,1,0.5)
1147 self._renderObject(self._selectedObj)
1148 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1150 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1151 glDisable(GL_STENCIL_TEST)
1152 glDisable(GL_CULL_FACE)
1153 glEnable(GL_DEPTH_TEST)
1155 if self._selectedObj is not None:
1157 pos = self.getObjectCenterPos()
1158 glTranslate(pos[0], pos[1], pos[2])
1161 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1162 glDisable(GL_DEPTH_TEST)
1165 glTranslate(0,-4,-10)
1166 glColor4ub(60,60,60,255)
1167 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1170 def _renderObject(self, obj, brightness = False, addSink = True):
1173 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1175 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1177 if self.tempMatrix is not None and obj == self._selectedObj:
1178 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1179 glMultMatrixf(tempMatrix)
1181 offset = obj.getDrawOffset()
1182 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1184 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1185 glMultMatrixf(tempMatrix)
1188 for m in obj._meshList:
1190 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1192 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1197 def _drawMachine(self):
1198 glEnable(GL_CULL_FACE)
1201 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1203 machine = profile.getMachineSetting('machine_type')
1204 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1205 if machine not in self._platformMesh:
1206 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1208 self._platformMesh[machine] = meshes[0]
1210 self._platformMesh[machine] = None
1211 if machine == 'ultimaker2':
1212 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1214 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1215 glColor4f(1,1,1,0.5)
1216 self._objectShader.bind()
1217 self._renderObject(self._platformMesh[machine], False, False)
1218 self._objectShader.unbind()
1220 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1221 if machine == 'ultimaker2':
1222 if not hasattr(self._platformMesh[machine], 'texture'):
1223 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1224 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1225 glEnable(GL_TEXTURE_2D)
1229 glTranslate(0,150,-5)
1234 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1237 glVertex3f( w, 0, h)
1239 glVertex3f(-w, 0, h)
1241 glVertex3f(-w, 0, 0)
1243 glVertex3f( w, 0, 0)
1246 glVertex3f(-w, d, h)
1248 glVertex3f( w, d, h)
1250 glVertex3f( w, d, 0)
1252 glVertex3f(-w, d, 0)
1254 glDisable(GL_TEXTURE_2D)
1255 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1261 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1262 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1263 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1264 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1265 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1266 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1269 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1270 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1271 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1272 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1273 v4 = [ size[0] / 2, size[1] / 2, 0]
1274 v5 = [ size[0] / 2,-size[1] / 2, 0]
1275 v6 = [-size[0] / 2, size[1] / 2, 0]
1276 v7 = [-size[0] / 2,-size[1] / 2, 0]
1278 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1279 glEnableClientState(GL_VERTEX_ARRAY)
1280 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1282 glColor4ub(5, 171, 231, 64)
1283 glDrawArrays(GL_QUADS, 0, 4)
1284 glColor4ub(5, 171, 231, 96)
1285 glDrawArrays(GL_QUADS, 4, 8)
1286 glColor4ub(5, 171, 231, 128)
1287 glDrawArrays(GL_QUADS, 12, 8)
1288 glDisableClientState(GL_VERTEX_ARRAY)
1290 sx = self._machineSize[0]
1291 sy = self._machineSize[1]
1292 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1293 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1298 x1 = max(min(x1, sx/2), -sx/2)
1299 y1 = max(min(y1, sy/2), -sy/2)
1300 x2 = max(min(x2, sx/2), -sx/2)
1301 y2 = max(min(y2, sy/2), -sy/2)
1302 if (x & 1) == (y & 1):
1303 glColor4ub(5, 171, 231, 127)
1305 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1307 glVertex3f(x1, y1, -0.02)
1308 glVertex3f(x2, y1, -0.02)
1309 glVertex3f(x2, y2, -0.02)
1310 glVertex3f(x1, y2, -0.02)
1314 glDisable(GL_CULL_FACE)
1316 def _generateGCodeVBOs(self, layer):
1318 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1319 if ':' in extrudeType:
1320 extruder = int(extrudeType[extrudeType.find(':')+1:])
1321 extrudeType = extrudeType[0:extrudeType.find(':')]
1324 pointList = numpy.zeros((0,3), numpy.float32)
1326 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1328 a = numpy.concatenate((a[:-1], a[1:]), 1)
1329 a = a.reshape((len(a) * 2, 3))
1330 pointList = numpy.concatenate((pointList, a))
1331 ret.append(opengl.GLVBO(pointList))
1334 def _generateGCodeVBOs2(self, layer):
1335 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1336 filamentArea = math.pi * filamentRadius * filamentRadius
1337 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1340 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1341 if ':' in extrudeType:
1342 extruder = int(extrudeType[extrudeType.find(':')+1:])
1343 extrudeType = extrudeType[0:extrudeType.find(':')]
1346 pointList = numpy.zeros((0,3), numpy.float32)
1348 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1350 if extrudeType == 'FILL':
1353 normal = a[1:] - a[:-1]
1354 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1355 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1358 ePerDist = path['extrusion'][1:] / lens
1360 lineWidth = ePerDist / path['layerThickness'] / 2.0
1362 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1364 normal[:,0] *= lineWidth
1365 normal[:,1] *= lineWidth
1367 b = numpy.zeros((len(a)-1, 0), numpy.float32)
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 = numpy.concatenate((b, a[:-1] + normal), 1)
1372 b = b.reshape((len(b) * 4, 3))
1375 normal2 = normal[:-1] + normal[1:]
1376 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1377 normal2[:,0] /= lens2
1378 normal2[:,1] /= lens2
1379 normal2[:,0] *= lineWidth[:-1]
1380 normal2[:,1] *= lineWidth[:-1]
1382 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1383 c = numpy.concatenate((c, a[1:-1]), 1)
1384 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1385 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1386 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1388 c = numpy.concatenate((c, a[1:-1]), 1)
1389 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1390 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1391 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1393 c = c.reshape((len(c) * 8, 3))
1395 pointList = numpy.concatenate((pointList, b, c))
1397 pointList = numpy.concatenate((pointList, b))
1398 ret.append(opengl.GLVBO(pointList))
1400 pointList = numpy.zeros((0,3), numpy.float32)
1402 if path['type'] == 'move':
1403 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1404 a = numpy.concatenate((a[:-1], a[1:]), 1)
1405 a = a.reshape((len(a) * 2, 3))
1406 pointList = numpy.concatenate((pointList, a))
1407 if path['type'] == 'retract':
1408 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1409 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1410 a = a.reshape((len(a) * 2, 3))
1411 pointList = numpy.concatenate((pointList, a))
1412 ret.append(opengl.GLVBO(pointList))
1416 def getObjectCenterPos(self):
1417 if self._selectedObj is None:
1418 return [0.0, 0.0, 0.0]
1419 pos = self._selectedObj.getPosition()
1420 size = self._selectedObj.getSize()
1421 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1423 def getObjectBoundaryCircle(self):
1424 if self._selectedObj is None:
1426 return self._selectedObj.getBoundaryCircle()
1428 def getObjectSize(self):
1429 if self._selectedObj is None:
1430 return [0.0, 0.0, 0.0]
1431 return self._selectedObj.getSize()
1433 def getObjectMatrix(self):
1434 if self._selectedObj is None:
1435 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1436 return self._selectedObj.getMatrix()
1438 class shaderEditor(wx.Dialog):
1439 def __init__(self, parent, callback, v, f):
1440 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1441 self._callback = callback
1442 s = wx.BoxSizer(wx.VERTICAL)
1444 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1445 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1446 s.Add(self._vertex, 1, flag=wx.EXPAND)
1447 s.Add(self._fragment, 1, flag=wx.EXPAND)
1449 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1450 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1452 self.SetPosition(self.GetParent().GetPosition())
1453 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1456 def OnText(self, e):
1457 self._callback(self._vertex.GetValue(), self._fragment.GetValue())