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 defPath = profile.getPreference('lastFile')
258 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
259 dlg=wx.FileDialog(self, _("Save toolpath"), defPath, style=wx.FD_SAVE)
260 dlg.SetFilename(self._scene._objectList[0].getName())
261 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
262 if dlg.ShowModal() != wx.ID_OK:
265 filename = dlg.GetPath()
268 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
270 def _copyFile(self, fileA, fileB, allowEject = False):
272 size = float(os.stat(fileA).st_size)
273 with open(fileA, 'rb') as fsrc:
274 with open(fileB, 'wb') as fdst:
276 buf = fsrc.read(16*1024)
280 self.printButton.setProgressBar(float(fsrc.tell()) / size)
285 self.notification.message("Failed to save")
288 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...'))
290 self.notification.message("Saved as %s" % (fileB))
291 self.printButton.setProgressBar(None)
292 if fileA == self._slicer.getGCodeFilename():
293 self._slicer.submitSliceInfoOnline()
295 def _showSliceLog(self):
296 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
300 def OnToolSelect(self, button):
301 if self.rotateToolButton.getSelected():
302 self.tool = previewTools.toolRotate(self)
303 elif self.scaleToolButton.getSelected():
304 self.tool = previewTools.toolScale(self)
305 elif self.mirrorToolButton.getSelected():
306 self.tool = previewTools.toolNone(self)
308 self.tool = previewTools.toolNone(self)
309 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
310 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
311 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
312 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
313 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
314 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
315 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
316 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
318 def updateToolButtons(self):
319 if self._selectedObj is None:
323 self.rotateToolButton.setHidden(hidden)
324 self.scaleToolButton.setHidden(hidden)
325 self.mirrorToolButton.setHidden(hidden)
327 self.rotateToolButton.setSelected(False)
328 self.scaleToolButton.setSelected(False)
329 self.mirrorToolButton.setSelected(False)
332 def OnViewChange(self):
333 if self.viewSelection.getValue() == 4:
334 self.viewMode = 'gcode'
335 if self._gcode is not None and self._gcode.layerList is not None:
336 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
337 self._selectObject(None)
338 elif self.viewSelection.getValue() == 1:
339 self.viewMode = 'overhang'
340 elif self.viewSelection.getValue() == 2:
341 self.viewMode = 'transparent'
342 elif self.viewSelection.getValue() == 3:
343 self.viewMode = 'xray'
345 self.viewMode = 'normal'
346 self.layerSelect.setHidden(self.viewMode != 'gcode')
349 def OnRotateReset(self, button):
350 if self._selectedObj is None:
352 self._selectedObj.resetRotation()
353 self._scene.pushFree()
354 self._selectObject(self._selectedObj)
357 def OnLayFlat(self, button):
358 if self._selectedObj is None:
360 self._selectedObj.layFlat()
361 self._scene.pushFree()
362 self._selectObject(self._selectedObj)
365 def OnScaleReset(self, button):
366 if self._selectedObj is None:
368 self._selectedObj.resetScale()
369 self._selectObject(self._selectedObj)
370 self.updateProfileToControls()
373 def OnScaleMax(self, button):
374 if self._selectedObj is None:
376 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
377 self._scene.pushFree()
378 self._selectObject(self._selectedObj)
379 self.updateProfileToControls()
382 def OnMirror(self, axis):
383 if self._selectedObj is None:
385 self._selectedObj.mirror(axis)
388 def OnScaleEntry(self, value, axis):
389 if self._selectedObj is None:
395 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
396 self.updateProfileToControls()
397 self._scene.pushFree()
398 self._selectObject(self._selectedObj)
401 def OnScaleEntryMM(self, value, axis):
402 if self._selectedObj is None:
408 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
409 self.updateProfileToControls()
410 self._scene.pushFree()
411 self._selectObject(self._selectedObj)
414 def OnDeleteAll(self, e):
415 while len(self._scene.objects()) > 0:
416 self._deleteObject(self._scene.objects()[0])
417 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
419 def OnMultiply(self, e):
420 if self._focusObj is None:
423 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
424 if dlg.ShowModal() != wx.ID_OK:
433 self._scene.add(newObj)
434 self._scene.centerAll()
435 if not self._scene.checkPlatform(newObj):
440 self.notification.message("Could not create more then %d items" % (n - 1))
441 self._scene.remove(newObj)
442 self._scene.centerAll()
445 def OnSplitObject(self, e):
446 if self._focusObj is None:
448 self._scene.remove(self._focusObj)
449 for obj in self._focusObj.split(self._splitCallback):
450 if numpy.max(obj.getSize()) > 2.0:
452 self._scene.centerAll()
453 self._selectObject(None)
456 def OnCenter(self, e):
457 if self._focusObj is None:
459 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
460 self._scene.pushFree()
462 def _splitCallback(self, progress):
465 def OnMergeObjects(self, e):
466 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
467 if len(self._scene.objects()) == 2:
468 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
471 self._scene.merge(self._selectedObj, self._focusObj)
474 def sceneUpdated(self):
475 self._sceneUpdateTimer.Start(500, True)
476 self._slicer.abortSlicer()
477 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
480 def _onRunSlicer(self, e):
481 if self._isSimpleMode:
482 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
483 self._slicer.runSlicer(self._scene)
484 if self._isSimpleMode:
485 profile.resetTempOverride()
487 def _updateSliceProgress(self, progressValue, ready):
489 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
491 self.printButton.setDisabled(not ready)
492 if progressValue >= 0.0:
493 self.printButton.setProgressBar(progressValue)
495 self.printButton.setProgressBar(None)
496 if self._gcode is not None:
498 for layerVBOlist in self._gcodeVBOs:
499 for vbo in layerVBOlist:
500 self.glReleaseList.append(vbo)
503 self.printButton.setProgressBar(None)
504 cost = self._slicer.getFilamentCost()
506 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
508 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
509 self._gcode = gcodeInterpreter.gcode()
510 self._gcodeFilename = self._slicer.getGCodeFilename()
512 self.printButton.setBottomText('')
515 def _loadGCode(self):
516 self._gcode.progressCallback = self._gcodeLoadCallback
517 self._gcode.load(self._gcodeFilename)
519 def _gcodeLoadCallback(self, progress):
520 if self._gcode is None:
522 if len(self._gcode.layerList) % 15 == 0:
524 if self._gcode is None:
526 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
527 if self.viewMode == 'gcode':
531 def loadScene(self, fileList):
532 for filename in fileList:
534 objList = meshLoader.loadMeshes(filename)
536 traceback.print_exc()
539 if self._objectLoadShader is not None:
540 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
544 self._scene.centerAll()
545 self._selectObject(obj)
546 if obj.getScale()[0] < 1.0:
547 self.notification.message("Warning: Object scaled down.")
550 def _deleteObject(self, obj):
551 if obj == self._selectedObj:
552 self._selectObject(None)
553 if obj == self._focusObj:
554 self._focusObj = None
555 self._scene.remove(obj)
556 for m in obj._meshList:
557 if m.vbo is not None and m.vbo.decRef():
558 self.glReleaseList.append(m.vbo)
563 def _selectObject(self, obj, zoom = True):
564 if obj != self._selectedObj:
565 self._selectedObj = obj
566 self.updateProfileToControls()
567 self.updateToolButtons()
568 if zoom and obj is not None:
569 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
570 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
571 newZoom = obj.getBoundaryCircle() * 6
572 if newZoom > numpy.max(self._machineSize) * 3:
573 newZoom = numpy.max(self._machineSize) * 3
574 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
576 def updateProfileToControls(self):
577 oldSimpleMode = self._isSimpleMode
578 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
579 if self._isSimpleMode != oldSimpleMode:
580 self._scene.arrangeAll()
582 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
583 self._objColors[0] = profile.getPreferenceColour('model_colour')
584 self._objColors[1] = profile.getPreferenceColour('model_colour2')
585 self._objColors[2] = profile.getPreferenceColour('model_colour3')
586 self._objColors[3] = profile.getPreferenceColour('model_colour4')
587 self._scene.setMachineSize(self._machineSize)
588 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
589 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'))
591 if self._selectedObj is not None:
592 scale = self._selectedObj.getScale()
593 size = self._selectedObj.getSize()
594 self.scaleXctrl.setValue(round(scale[0], 2))
595 self.scaleYctrl.setValue(round(scale[1], 2))
596 self.scaleZctrl.setValue(round(scale[2], 2))
597 self.scaleXmmctrl.setValue(round(size[0], 2))
598 self.scaleYmmctrl.setValue(round(size[1], 2))
599 self.scaleZmmctrl.setValue(round(size[2], 2))
601 def OnKeyChar(self, keyCode):
602 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
603 if self._selectedObj is not None:
604 self._deleteObject(self._selectedObj)
606 if keyCode == wx.WXK_UP:
607 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
609 elif keyCode == wx.WXK_DOWN:
610 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
612 elif keyCode == wx.WXK_PAGEUP:
613 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
615 elif keyCode == wx.WXK_PAGEDOWN:
616 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
619 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
620 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
621 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
622 from collections import defaultdict
623 from gc import get_objects
624 self._beforeLeakTest = defaultdict(int)
625 for i in get_objects():
626 self._beforeLeakTest[type(i)] += 1
627 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
628 from collections import defaultdict
629 from gc import get_objects
630 self._afterLeakTest = defaultdict(int)
631 for i in get_objects():
632 self._afterLeakTest[type(i)] += 1
633 for k in self._afterLeakTest:
634 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
635 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
637 def ShaderUpdate(self, v, f):
638 s = opengl.GLShader(v, f)
640 self._objectLoadShader.release()
641 self._objectLoadShader = s
642 for obj in self._scene.objects():
643 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
646 def OnMouseDown(self,e):
647 self._mouseX = e.GetX()
648 self._mouseY = e.GetY()
649 self._mouseClick3DPos = self._mouse3Dpos
650 self._mouseClickFocus = self._focusObj
652 self._mouseState = 'doubleClick'
654 self._mouseState = 'dragOrClick'
655 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
656 p0 -= self.getObjectCenterPos() - self._viewTarget
657 p1 -= self.getObjectCenterPos() - self._viewTarget
658 if self.tool.OnDragStart(p0, p1):
659 self._mouseState = 'tool'
660 if self._mouseState == 'dragOrClick':
661 if e.GetButton() == 1:
662 if self._focusObj is not None:
663 self._selectObject(self._focusObj, False)
666 def OnMouseUp(self, e):
667 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
669 if self._mouseState == 'dragOrClick':
670 if e.GetButton() == 1:
671 self._selectObject(self._focusObj)
672 if e.GetButton() == 3:
674 if self._focusObj is not None:
675 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
676 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
677 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
678 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
679 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:
680 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
681 if len(self._scene.objects()) > 0:
682 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
683 if menu.MenuItemCount > 0:
686 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
687 self._scene.pushFree()
689 elif self._mouseState == 'tool':
690 if self.tempMatrix is not None and self._selectedObj is not None:
691 self._selectedObj.applyMatrix(self.tempMatrix)
692 self._scene.pushFree()
693 self._selectObject(self._selectedObj)
694 self.tempMatrix = None
695 self.tool.OnDragEnd()
697 self._mouseState = None
699 def OnMouseMotion(self,e):
700 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
701 p0 -= self.getObjectCenterPos() - self._viewTarget
702 p1 -= self.getObjectCenterPos() - self._viewTarget
704 if e.Dragging() and self._mouseState is not None:
705 if self._mouseState == 'tool':
706 self.tool.OnDrag(p0, p1)
707 elif not e.LeftIsDown() and e.RightIsDown():
708 self._mouseState = 'drag'
709 if wx.GetKeyState(wx.WXK_SHIFT):
710 a = math.cos(math.radians(self._yaw)) / 3.0
711 b = math.sin(math.radians(self._yaw)) / 3.0
712 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
713 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
714 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
715 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
717 self._yaw += e.GetX() - self._mouseX
718 self._pitch -= e.GetY() - self._mouseY
719 if self._pitch > 170:
723 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
724 self._mouseState = 'drag'
725 self._zoom += e.GetY() - self._mouseY
728 if self._zoom > numpy.max(self._machineSize) * 3:
729 self._zoom = numpy.max(self._machineSize) * 3
730 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
731 self._mouseState = 'dragObject'
732 z = max(0, self._mouseClick3DPos[2])
733 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
734 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
739 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
740 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
741 diff = cursorZ1 - cursorZ0
742 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
743 if not e.Dragging() or self._mouseState != 'tool':
744 self.tool.OnMouseMove(p0, p1)
746 self._mouseX = e.GetX()
747 self._mouseY = e.GetY()
749 def OnMouseWheel(self, e):
750 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
751 delta = max(min(delta,4),-4)
752 self._zoom *= 1.0 - delta / 10.0
755 if self._zoom > numpy.max(self._machineSize) * 3:
756 self._zoom = numpy.max(self._machineSize) * 3
759 def OnMouseLeave(self, e):
763 def getMouseRay(self, x, y):
764 if self._viewport is None:
765 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
766 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
767 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
768 p0 -= self._viewTarget
769 p1 -= self._viewTarget
772 def _init3DView(self):
773 # set viewing projection
774 size = self.GetSize()
775 glViewport(0, 0, size.GetWidth(), size.GetHeight())
778 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
780 glDisable(GL_RESCALE_NORMAL)
781 glDisable(GL_LIGHTING)
783 glEnable(GL_DEPTH_TEST)
784 glDisable(GL_CULL_FACE)
786 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
788 glClearColor(0.8, 0.8, 0.8, 1.0)
792 glMatrixMode(GL_PROJECTION)
794 aspect = float(size.GetWidth()) / float(size.GetHeight())
795 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
797 glMatrixMode(GL_MODELVIEW)
799 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
802 if machineCom.machineIsConnected():
803 self.printButton._imageID = 6
804 self.printButton._tooltip = _("Print")
805 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
806 self.printButton._imageID = 2
807 self.printButton._tooltip = _("Toolpath to SD")
809 self.printButton._imageID = 3
810 self.printButton._tooltip = _("Save toolpath")
812 if self._animView is not None:
813 self._viewTarget = self._animView.getPosition()
814 if self._animView.isDone():
815 self._animView = None
816 if self._animZoom is not None:
817 self._zoom = self._animZoom.getPosition()
818 if self._animZoom.isDone():
819 self._animZoom = None
820 if self.viewMode == 'gcode' and self._gcode is not None:
822 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
825 if self._objectShader is None:
826 if opengl.hasShaderSupport():
827 self._objectShader = opengl.GLShader("""
828 varying float light_amount;
832 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
833 gl_FrontColor = gl_Color;
835 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
839 varying float light_amount;
843 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
846 self._objectOverhangShader = opengl.GLShader("""
847 uniform float cosAngle;
848 uniform mat3 rotMatrix;
849 varying float light_amount;
853 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
854 gl_FrontColor = gl_Color;
856 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
858 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
860 light_amount = -10.0;
864 varying float light_amount;
868 if (light_amount == -10.0)
870 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
872 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
876 self._objectLoadShader = opengl.GLShader("""
877 uniform float intensity;
879 varying float light_amount;
883 vec4 tmp = gl_Vertex;
884 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
885 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
886 gl_Position = gl_ModelViewProjectionMatrix * tmp;
887 gl_FrontColor = gl_Color;
889 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
893 uniform float intensity;
894 varying float light_amount;
898 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
901 if self._objectShader == None or not self._objectShader.isValid():
902 self._objectShader = opengl.GLFakeShader()
903 self._objectOverhangShader = opengl.GLFakeShader()
904 self._objectLoadShader = None
906 glTranslate(0,0,-self._zoom)
907 glRotate(-self._pitch, 1,0,0)
908 glRotate(self._yaw, 0,0,1)
909 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
911 self._viewport = glGetIntegerv(GL_VIEWPORT)
912 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
913 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
915 glClearColor(1,1,1,1)
916 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
918 if self.viewMode != 'gcode':
919 for n in xrange(0, len(self._scene.objects())):
920 obj = self._scene.objects()[n]
921 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
922 self._renderObject(obj)
924 if self._mouseX > -1:
926 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
927 if n < len(self._scene.objects()):
928 self._focusObj = self._scene.objects()[n]
930 self._focusObj = None
931 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
932 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
933 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
934 self._mouse3Dpos -= self._viewTarget
937 glTranslate(0,0,-self._zoom)
938 glRotate(-self._pitch, 1,0,0)
939 glRotate(self._yaw, 0,0,1)
940 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
942 if self.viewMode == 'gcode':
943 if self._gcode is not None and self._gcode.layerList is None:
944 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
945 self._gcodeLoadThread.daemon = True
946 self._gcodeLoadThread.start()
947 if self._gcode is not None and self._gcode.layerList is not None:
949 if profile.getMachineSetting('machine_center_is_zero') != 'True':
950 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
952 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
953 for n in xrange(0, drawUpTill):
954 c = 1.0 - float(drawUpTill - n) / 15
956 if len(self._gcodeVBOs) < n + 1:
957 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
958 if time.time() - t > 0.5:
961 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
962 if n == drawUpTill - 1:
963 if len(self._gcodeVBOs[n]) < 9:
964 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
966 self._gcodeVBOs[n][8].render(GL_QUADS)
968 self._gcodeVBOs[n][9].render(GL_QUADS)
970 self._gcodeVBOs[n][10].render(GL_QUADS)
972 self._gcodeVBOs[n][11].render(GL_QUADS)
975 self._gcodeVBOs[n][12].render(GL_QUADS)
976 glColor3f(c/2, c/2, 0.0)
977 self._gcodeVBOs[n][13].render(GL_QUADS)
979 self._gcodeVBOs[n][14].render(GL_QUADS)
980 self._gcodeVBOs[n][15].render(GL_QUADS)
982 self._gcodeVBOs[n][16].render(GL_LINES)
985 self._gcodeVBOs[n][0].render(GL_LINES)
987 self._gcodeVBOs[n][1].render(GL_LINES)
989 self._gcodeVBOs[n][2].render(GL_LINES)
991 self._gcodeVBOs[n][3].render(GL_LINES)
994 self._gcodeVBOs[n][4].render(GL_LINES)
995 glColor3f(c/2, c/2, 0.0)
996 self._gcodeVBOs[n][5].render(GL_LINES)
998 self._gcodeVBOs[n][6].render(GL_LINES)
999 self._gcodeVBOs[n][7].render(GL_LINES)
1002 glStencilFunc(GL_ALWAYS, 1, 1)
1003 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1005 if self.viewMode == 'overhang':
1006 self._objectOverhangShader.bind()
1007 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1009 self._objectShader.bind()
1010 for obj in self._scene.objects():
1011 if obj._loadAnim is not None:
1012 if obj._loadAnim.isDone():
1013 obj._loadAnim = None
1017 if self._focusObj == obj:
1019 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1022 if self._selectedObj == obj or self._selectedObj is None:
1023 #If we want transparent, then first render a solid black model to remove the printer size lines.
1024 if self.viewMode == 'transparent':
1025 glColor4f(0, 0, 0, 0)
1026 self._renderObject(obj)
1028 glBlendFunc(GL_ONE, GL_ONE)
1029 glDisable(GL_DEPTH_TEST)
1031 if self.viewMode == 'xray':
1032 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1033 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1034 glEnable(GL_STENCIL_TEST)
1036 if self.viewMode == 'overhang':
1037 if self._selectedObj == obj and self.tempMatrix is not None:
1038 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1040 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1042 if not self._scene.checkPlatform(obj):
1043 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1044 self._renderObject(obj)
1046 self._renderObject(obj, brightness)
1047 glDisable(GL_STENCIL_TEST)
1049 glEnable(GL_DEPTH_TEST)
1050 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1052 if self.viewMode == 'xray':
1055 glEnable(GL_STENCIL_TEST)
1056 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1057 glDisable(GL_DEPTH_TEST)
1058 for i in xrange(2, 15, 2):
1059 glStencilFunc(GL_EQUAL, i, 0xFF)
1060 glColor(float(i)/10, float(i)/10, float(i)/5)
1062 glVertex3f(-1000,-1000,-10)
1063 glVertex3f( 1000,-1000,-10)
1064 glVertex3f( 1000, 1000,-10)
1065 glVertex3f(-1000, 1000,-10)
1067 for i in xrange(1, 15, 2):
1068 glStencilFunc(GL_EQUAL, i, 0xFF)
1069 glColor(float(i)/10, 0, 0)
1071 glVertex3f(-1000,-1000,-10)
1072 glVertex3f( 1000,-1000,-10)
1073 glVertex3f( 1000, 1000,-10)
1074 glVertex3f(-1000, 1000,-10)
1077 glDisable(GL_STENCIL_TEST)
1078 glEnable(GL_DEPTH_TEST)
1080 self._objectShader.unbind()
1082 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1084 if self._objectLoadShader is not None:
1085 self._objectLoadShader.bind()
1086 glColor4f(0.2, 0.6, 1.0, 1.0)
1087 for obj in self._scene.objects():
1088 if obj._loadAnim is None:
1090 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1091 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1092 self._renderObject(obj)
1093 self._objectLoadShader.unbind()
1098 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1100 z = self._usbPrintMonitor.getZ()
1101 size = self._machineSize
1102 glColor4ub(255,255,0,128)
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)
1107 glVertex3f(-size[0]/2, size[1]/2, z)
1110 if self.viewMode == 'gcode':
1111 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1112 glDisable(GL_DEPTH_TEST)
1115 glTranslate(0,-4,-10)
1116 glColor4ub(60,60,60,255)
1117 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1120 #Draw the object box-shadow, so you can see where it will collide with other objects.
1121 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1122 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1124 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1126 glEnable(GL_CULL_FACE)
1127 glColor4f(0,0,0,0.12)
1129 glVertex3f(-size[0], size[1], 0.1)
1130 glVertex3f(-size[0], -size[1], 0.1)
1131 glVertex3f( size[0], -size[1], 0.1)
1132 glVertex3f( size[0], size[1], 0.1)
1134 glDisable(GL_CULL_FACE)
1137 #Draw the outline of the selected object, on top of everything else except the GUI.
1138 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1139 glDisable(GL_DEPTH_TEST)
1140 glEnable(GL_CULL_FACE)
1141 glEnable(GL_STENCIL_TEST)
1143 glStencilFunc(GL_EQUAL, 0, 255)
1145 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1147 glColor4f(1,1,1,0.5)
1148 self._renderObject(self._selectedObj)
1149 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1151 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1152 glDisable(GL_STENCIL_TEST)
1153 glDisable(GL_CULL_FACE)
1154 glEnable(GL_DEPTH_TEST)
1156 if self._selectedObj is not None:
1158 pos = self.getObjectCenterPos()
1159 glTranslate(pos[0], pos[1], pos[2])
1162 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1163 glDisable(GL_DEPTH_TEST)
1166 glTranslate(0,-4,-10)
1167 glColor4ub(60,60,60,255)
1168 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1171 def _renderObject(self, obj, brightness = False, addSink = True):
1174 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1176 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1178 if self.tempMatrix is not None and obj == self._selectedObj:
1179 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1180 glMultMatrixf(tempMatrix)
1182 offset = obj.getDrawOffset()
1183 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1185 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1186 glMultMatrixf(tempMatrix)
1189 for m in obj._meshList:
1191 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1193 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1198 def _drawMachine(self):
1199 glEnable(GL_CULL_FACE)
1202 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1204 machine = profile.getMachineSetting('machine_type')
1205 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1206 if machine not in self._platformMesh:
1207 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1209 self._platformMesh[machine] = meshes[0]
1211 self._platformMesh[machine] = None
1212 if machine == 'ultimaker2':
1213 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1215 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1216 glColor4f(1,1,1,0.5)
1217 self._objectShader.bind()
1218 self._renderObject(self._platformMesh[machine], False, False)
1219 self._objectShader.unbind()
1221 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1222 if machine == 'ultimaker2':
1223 if not hasattr(self._platformMesh[machine], 'texture'):
1224 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1225 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1226 glEnable(GL_TEXTURE_2D)
1230 glTranslate(0,150,-5)
1235 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1238 glVertex3f( w, 0, h)
1240 glVertex3f(-w, 0, h)
1242 glVertex3f(-w, 0, 0)
1244 glVertex3f( w, 0, 0)
1247 glVertex3f(-w, d, h)
1249 glVertex3f( w, d, h)
1251 glVertex3f( w, d, 0)
1253 glVertex3f(-w, d, 0)
1255 glDisable(GL_TEXTURE_2D)
1256 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1262 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1263 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1264 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1265 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1266 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1267 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1270 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1271 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1272 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1273 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1274 v4 = [ size[0] / 2, size[1] / 2, 0]
1275 v5 = [ size[0] / 2,-size[1] / 2, 0]
1276 v6 = [-size[0] / 2, size[1] / 2, 0]
1277 v7 = [-size[0] / 2,-size[1] / 2, 0]
1279 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1280 glEnableClientState(GL_VERTEX_ARRAY)
1281 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1283 glColor4ub(5, 171, 231, 64)
1284 glDrawArrays(GL_QUADS, 0, 4)
1285 glColor4ub(5, 171, 231, 96)
1286 glDrawArrays(GL_QUADS, 4, 8)
1287 glColor4ub(5, 171, 231, 128)
1288 glDrawArrays(GL_QUADS, 12, 8)
1289 glDisableClientState(GL_VERTEX_ARRAY)
1291 sx = self._machineSize[0]
1292 sy = self._machineSize[1]
1293 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1294 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1299 x1 = max(min(x1, sx/2), -sx/2)
1300 y1 = max(min(y1, sy/2), -sy/2)
1301 x2 = max(min(x2, sx/2), -sx/2)
1302 y2 = max(min(y2, sy/2), -sy/2)
1303 if (x & 1) == (y & 1):
1304 glColor4ub(5, 171, 231, 127)
1306 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1308 glVertex3f(x1, y1, -0.02)
1309 glVertex3f(x2, y1, -0.02)
1310 glVertex3f(x2, y2, -0.02)
1311 glVertex3f(x1, y2, -0.02)
1315 glDisable(GL_CULL_FACE)
1317 def _generateGCodeVBOs(self, layer):
1319 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1320 if ':' in extrudeType:
1321 extruder = int(extrudeType[extrudeType.find(':')+1:])
1322 extrudeType = extrudeType[0:extrudeType.find(':')]
1325 pointList = numpy.zeros((0,3), numpy.float32)
1327 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1329 a = numpy.concatenate((a[:-1], a[1:]), 1)
1330 a = a.reshape((len(a) * 2, 3))
1331 pointList = numpy.concatenate((pointList, a))
1332 ret.append(opengl.GLVBO(pointList))
1335 def _generateGCodeVBOs2(self, layer):
1336 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1337 filamentArea = math.pi * filamentRadius * filamentRadius
1338 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1341 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1342 if ':' in extrudeType:
1343 extruder = int(extrudeType[extrudeType.find(':')+1:])
1344 extrudeType = extrudeType[0:extrudeType.find(':')]
1347 pointList = numpy.zeros((0,3), numpy.float32)
1349 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1351 if extrudeType == 'FILL':
1354 normal = a[1:] - a[:-1]
1355 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1356 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1359 ePerDist = path['extrusion'][1:] / lens
1361 lineWidth = ePerDist / path['layerThickness'] / 2.0
1363 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1365 normal[:,0] *= lineWidth
1366 normal[:,1] *= lineWidth
1368 b = numpy.zeros((len(a)-1, 0), numpy.float32)
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 = numpy.concatenate((b, a[:-1] + normal), 1)
1373 b = b.reshape((len(b) * 4, 3))
1376 normal2 = normal[:-1] + normal[1:]
1377 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1378 normal2[:,0] /= lens2
1379 normal2[:,1] /= lens2
1380 normal2[:,0] *= lineWidth[:-1]
1381 normal2[:,1] *= lineWidth[:-1]
1383 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1384 c = numpy.concatenate((c, a[1:-1]), 1)
1385 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1386 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1387 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1389 c = numpy.concatenate((c, a[1:-1]), 1)
1390 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1391 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1392 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1394 c = c.reshape((len(c) * 8, 3))
1396 pointList = numpy.concatenate((pointList, b, c))
1398 pointList = numpy.concatenate((pointList, b))
1399 ret.append(opengl.GLVBO(pointList))
1401 pointList = numpy.zeros((0,3), numpy.float32)
1403 if path['type'] == 'move':
1404 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1405 a = numpy.concatenate((a[:-1], a[1:]), 1)
1406 a = a.reshape((len(a) * 2, 3))
1407 pointList = numpy.concatenate((pointList, a))
1408 if path['type'] == 'retract':
1409 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1410 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1411 a = a.reshape((len(a) * 2, 3))
1412 pointList = numpy.concatenate((pointList, a))
1413 ret.append(opengl.GLVBO(pointList))
1417 def getObjectCenterPos(self):
1418 if self._selectedObj is None:
1419 return [0.0, 0.0, 0.0]
1420 pos = self._selectedObj.getPosition()
1421 size = self._selectedObj.getSize()
1422 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1424 def getObjectBoundaryCircle(self):
1425 if self._selectedObj is None:
1427 return self._selectedObj.getBoundaryCircle()
1429 def getObjectSize(self):
1430 if self._selectedObj is None:
1431 return [0.0, 0.0, 0.0]
1432 return self._selectedObj.getSize()
1434 def getObjectMatrix(self):
1435 if self._selectedObj is None:
1436 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1437 return self._selectedObj.getMatrix()
1439 class shaderEditor(wx.Dialog):
1440 def __init__(self, parent, callback, v, f):
1441 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1442 self._callback = callback
1443 s = wx.BoxSizer(wx.VERTICAL)
1445 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1446 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1447 s.Add(self._vertex, 1, flag=wx.EXPAND)
1448 s.Add(self._fragment, 1, flag=wx.EXPAND)
1450 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1451 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1453 self.SetPosition(self.GetParent().GetPosition())
1454 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1457 def OnText(self, e):
1458 self._callback(self._vertex.GetValue(), self._fragment.GetValue())