1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
14 OpenGL.ERROR_CHECKING = False
15 from OpenGL.GLU import *
16 from OpenGL.GL import *
18 from Cura.gui import printWindow
19 from Cura.util import profile
20 from Cura.util import meshLoader
21 from Cura.util import objectScene
22 from Cura.util import resources
23 from Cura.util import sliceEngine
24 from Cura.util import machineCom
25 from Cura.util import removableStorage
26 from Cura.util import gcodeInterpreter
27 from Cura.gui.util import previewTools
28 from Cura.gui.util import opengl
29 from Cura.gui.util import openglGui
30 from Cura.gui.tools import youmagineGui
31 from Cura.gui.tools import imageToMesh
33 class SceneView(openglGui.glGuiPanel):
34 def __init__(self, parent):
35 super(SceneView, self).__init__(parent)
40 self._scene = objectScene.Scene()
43 self._gcodeFilename = None
44 self._gcodeLoadThread = None
45 self._objectShader = None
46 self._objectLoadShader = None
48 self._selectedObj = None
49 self._objColors = [None,None,None,None]
52 self._mouseState = None
53 self._viewTarget = numpy.array([0,0,0], numpy.float32)
56 self._platformMesh = {}
57 self._isSimpleMode = True
58 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
61 self._modelMatrix = None
62 self._projMatrix = None
63 self.tempMatrix = None
65 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
66 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
67 self.printButton.setDisabled(True)
70 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
71 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
72 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
74 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
75 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
77 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
78 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
80 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
81 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
82 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
84 self.rotateToolButton.setExpandArrow(True)
85 self.scaleToolButton.setExpandArrow(True)
86 self.mirrorToolButton.setExpandArrow(True)
88 self.scaleForm = openglGui.glFrame(self, (2, -2))
89 openglGui.glGuiLayoutGrid(self.scaleForm)
90 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
91 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
92 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
93 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
94 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
95 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
96 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
97 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
98 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
99 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
100 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
101 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
102 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
103 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
105 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
106 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
108 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
109 self.youMagineButton.setDisabled(True)
111 self.notification = openglGui.glNotification(self, (0, 0))
113 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
114 self._sceneUpdateTimer = wx.Timer(self)
115 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
116 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
117 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
121 self.updateToolButtons()
122 self.updateProfileToControls()
124 def loadGCodeFile(self, filename):
125 self.OnDeleteAll(None)
126 if self._gcode is not None:
128 for layerVBOlist in self._gcodeVBOs:
129 for vbo in layerVBOlist:
130 self.glReleaseList.append(vbo)
132 self._gcode = gcodeInterpreter.gcode()
133 self._gcodeFilename = filename
134 self.printButton.setBottomText('')
135 self.viewSelection.setValue(4)
136 self.printButton.setDisabled(False)
137 self.youMagineButton.setDisabled(True)
140 def loadSceneFiles(self, filenames):
141 self.youMagineButton.setDisabled(False)
142 #if self.viewSelection.getValue() == 4:
143 # self.viewSelection.setValue(0)
144 # self.OnViewChange()
145 self.loadScene(filenames)
147 def loadFiles(self, filenames):
148 mainWindow = self.GetParent().GetParent().GetParent()
149 # only one GCODE file can be active
150 # so if single gcode file, process this
151 # otherwise ignore all gcode files
153 if len(filenames) == 1:
154 filename = filenames[0]
155 ext = os.path.splitext(filename)[1].lower()
156 if ext == '.g' or ext == '.gcode':
157 gcodeFilename = filename
158 mainWindow.addToModelMRU(filename)
159 if gcodeFilename is not None:
160 self.loadGCodeFile(gcodeFilename)
162 # process directories and special file types
163 # and keep scene files for later processing
165 ignored_types = dict()
166 # use file list as queue
167 # pop first entry for processing and append new files at end
169 filename = filenames.pop(0)
170 if os.path.isdir(filename):
171 # directory: queue all included files and directories
172 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
174 ext = os.path.splitext(filename)[1].lower()
176 profile.loadProfile(filename)
177 mainWindow.addToProfileMRU(filename)
178 elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
179 scene_filenames.append(filename)
180 mainWindow.addToModelMRU(filename)
182 ignored_types[ext] = 1
184 ignored_types = ignored_types.keys()
186 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
187 mainWindow.updateProfileToAllControls()
188 # now process all the scene files
190 self.loadSceneFiles(scene_filenames)
191 self._selectObject(None)
193 newZoom = numpy.max(self._machineSize)
194 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
195 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
197 def showLoadModel(self, button = 1):
199 dlg=wx.FileDialog(self, _("Open 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
200 dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
201 if dlg.ShowModal() != wx.ID_OK:
204 filenames = dlg.GetPaths()
206 if len(filenames) < 1:
208 profile.putPreference('lastFile', filenames[0])
209 self.loadFiles(filenames)
211 def showSaveModel(self):
212 if len(self._scene.objects()) < 1:
214 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
215 dlg.SetWildcard(meshLoader.saveWildcardFilter())
216 if dlg.ShowModal() != wx.ID_OK:
219 filename = dlg.GetPath()
221 meshLoader.saveMeshes(filename, self._scene.objects())
223 def OnPrintButton(self, button):
225 if machineCom.machineIsConnected():
226 self.showPrintWindow()
227 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
228 drives = removableStorage.getPossibleSDcardDrives()
230 dlg = wx.SingleChoiceDialog(self, "Select SD drive", "Multiple removable drives have been found,\nplease select your SD card drive", map(lambda n: n[0], drives))
231 if dlg.ShowModal() != wx.ID_OK:
234 drive = drives[dlg.GetSelection()]
238 filename = self._scene._objectList[0].getName() + '.gcode'
239 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
244 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
245 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
246 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
250 def showPrintWindow(self):
251 if self._gcodeFilename is None:
253 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
254 wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING)
256 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
257 if self._gcodeFilename == self._slicer.getGCodeFilename():
258 self._slicer.submitSliceInfoOnline()
260 def showSaveGCode(self):
261 if len(self._scene._objectList) < 1:
263 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
264 filename = self._scene._objectList[0].getName() + '.gcode'
265 dlg.SetFilename(filename)
266 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
267 if dlg.ShowModal() != wx.ID_OK:
270 filename = dlg.GetPath()
273 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
275 def _copyFile(self, fileA, fileB, allowEject = False):
277 size = float(os.stat(fileA).st_size)
278 with open(fileA, 'rb') as fsrc:
279 with open(fileB, 'wb') as fdst:
281 buf = fsrc.read(16*1024)
285 self.printButton.setProgressBar(float(fsrc.tell()) / size)
290 self.notification.message("Failed to save")
293 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...'))
295 self.notification.message("Saved as %s" % (fileB))
296 self.printButton.setProgressBar(None)
297 if fileA == self._slicer.getGCodeFilename():
298 self._slicer.submitSliceInfoOnline()
300 def _showSliceLog(self):
301 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
305 def OnToolSelect(self, button):
306 if self.rotateToolButton.getSelected():
307 self.tool = previewTools.toolRotate(self)
308 elif self.scaleToolButton.getSelected():
309 self.tool = previewTools.toolScale(self)
310 elif self.mirrorToolButton.getSelected():
311 self.tool = previewTools.toolNone(self)
313 self.tool = previewTools.toolNone(self)
314 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
315 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
316 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
317 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
318 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
319 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
320 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
321 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
323 def updateToolButtons(self):
324 if self._selectedObj is None:
328 self.rotateToolButton.setHidden(hidden)
329 self.scaleToolButton.setHidden(hidden)
330 self.mirrorToolButton.setHidden(hidden)
332 self.rotateToolButton.setSelected(False)
333 self.scaleToolButton.setSelected(False)
334 self.mirrorToolButton.setSelected(False)
337 def OnViewChange(self):
338 if self.viewSelection.getValue() == 4:
339 self.viewMode = 'gcode'
340 if self._gcode is not None and self._gcode.layerList is not None:
341 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
342 self._selectObject(None)
343 elif self.viewSelection.getValue() == 1:
344 self.viewMode = 'overhang'
345 elif self.viewSelection.getValue() == 2:
346 self.viewMode = 'transparent'
347 elif self.viewSelection.getValue() == 3:
348 self.viewMode = 'xray'
350 self.viewMode = 'normal'
351 self.layerSelect.setHidden(self.viewMode != 'gcode')
354 def OnRotateReset(self, button):
355 if self._selectedObj is None:
357 self._selectedObj.resetRotation()
358 self._scene.pushFree()
359 self._selectObject(self._selectedObj)
362 def OnLayFlat(self, button):
363 if self._selectedObj is None:
365 self._selectedObj.layFlat()
366 self._scene.pushFree()
367 self._selectObject(self._selectedObj)
370 def OnScaleReset(self, button):
371 if self._selectedObj is None:
373 self._selectedObj.resetScale()
374 self._selectObject(self._selectedObj)
375 self.updateProfileToControls()
378 def OnScaleMax(self, button):
379 if self._selectedObj is None:
381 machine = profile.getMachineSetting('machine_type')
382 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
383 self._scene.pushFree()
385 if machine == "ultimaker2":
386 #This is bad and Jaime should feel bad!
387 self._selectedObj.setPosition(numpy.array([0.0,-10.0]))
388 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
389 self._selectedObj.setPosition(numpy.array([0.0,0.0]))
390 self._scene.pushFree()
392 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
393 self._scene.pushFree()
394 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
395 self._scene.pushFree()
396 self._selectObject(self._selectedObj)
397 self.updateProfileToControls()
400 def OnMirror(self, axis):
401 if self._selectedObj is None:
403 self._selectedObj.mirror(axis)
406 def OnScaleEntry(self, value, axis):
407 if self._selectedObj is None:
413 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
414 self.updateProfileToControls()
415 self._scene.pushFree()
416 self._selectObject(self._selectedObj)
419 def OnScaleEntryMM(self, value, axis):
420 if self._selectedObj is None:
426 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
427 self.updateProfileToControls()
428 self._scene.pushFree()
429 self._selectObject(self._selectedObj)
432 def OnDeleteAll(self, e):
433 while len(self._scene.objects()) > 0:
434 self._deleteObject(self._scene.objects()[0])
435 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
437 def OnMultiply(self, e):
438 if self._focusObj is None:
441 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
442 if dlg.ShowModal() != wx.ID_OK:
451 self._scene.add(newObj)
452 self._scene.centerAll()
453 if not self._scene.checkPlatform(newObj):
458 self.notification.message("Could not create more then %d items" % (n - 1))
459 self._scene.remove(newObj)
460 self._scene.centerAll()
463 def OnSplitObject(self, e):
464 if self._focusObj is None:
466 self._scene.remove(self._focusObj)
467 for obj in self._focusObj.split(self._splitCallback):
468 if numpy.max(obj.getSize()) > 2.0:
470 self._scene.centerAll()
471 self._selectObject(None)
474 def OnCenter(self, e):
475 if self._focusObj is None:
477 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
478 self._scene.pushFree()
479 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
480 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
483 def _splitCallback(self, progress):
486 def OnMergeObjects(self, e):
487 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
488 if len(self._scene.objects()) == 2:
489 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
492 self._scene.merge(self._selectedObj, self._focusObj)
495 def sceneUpdated(self):
496 self._sceneUpdateTimer.Start(500, True)
497 self._slicer.abortSlicer()
498 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
501 def _onRunSlicer(self, e):
502 if self._isSimpleMode:
503 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
504 self._slicer.runSlicer(self._scene)
505 if self._isSimpleMode:
506 profile.resetTempOverride()
508 def _updateSliceProgress(self, progressValue, ready):
510 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
512 self.printButton.setDisabled(not ready)
513 if progressValue >= 0.0:
514 self.printButton.setProgressBar(progressValue)
516 self.printButton.setProgressBar(None)
517 if self._gcode is not None:
519 for layerVBOlist in self._gcodeVBOs:
520 for vbo in layerVBOlist:
521 self.glReleaseList.append(vbo)
524 self.printButton.setProgressBar(None)
525 text = '%s' % (self._slicer.getPrintTime())
526 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
527 amount = self._slicer.getFilamentAmount(e)
530 text += '\n%s' % (amount)
531 cost = self._slicer.getFilamentCost(e)
533 text += '\n%s' % (cost)
534 self.printButton.setBottomText(text)
535 self._gcode = gcodeInterpreter.gcode()
536 self._gcodeFilename = self._slicer.getGCodeFilename()
538 self.printButton.setBottomText('')
541 def _loadGCode(self):
542 self._gcode.progressCallback = self._gcodeLoadCallback
543 self._gcode.load(self._gcodeFilename)
545 def _gcodeLoadCallback(self, progress):
546 if not self or self._gcode is None:
548 if len(self._gcode.layerList) % 15 == 0:
550 if self._gcode is None:
552 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
553 if self.viewMode == 'gcode':
557 def loadScene(self, fileList):
558 for filename in fileList:
560 ext = os.path.splitext(filename)[1].lower()
561 if ext in imageToMesh.supportedExtensions():
562 imageToMesh.convertImageDialog(self, filename).Show()
565 objList = meshLoader.loadMeshes(filename)
567 traceback.print_exc()
570 if self._objectLoadShader is not None:
571 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
575 self._scene.centerAll()
576 self._selectObject(obj)
577 if obj.getScale()[0] < 1.0:
578 self.notification.message("Warning: Object scaled down.")
581 def _deleteObject(self, obj):
582 if obj == self._selectedObj:
583 self._selectObject(None)
584 if obj == self._focusObj:
585 self._focusObj = None
586 self._scene.remove(obj)
587 for m in obj._meshList:
588 if m.vbo is not None and m.vbo.decRef():
589 self.glReleaseList.append(m.vbo)
594 def _selectObject(self, obj, zoom = True):
595 if obj != self._selectedObj:
596 self._selectedObj = obj
597 self.updateProfileToControls()
598 self.updateToolButtons()
599 if zoom and obj is not None:
600 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
601 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
602 newZoom = obj.getBoundaryCircle() * 6
603 if newZoom > numpy.max(self._machineSize) * 3:
604 newZoom = numpy.max(self._machineSize) * 3
605 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
607 def updateProfileToControls(self):
608 oldSimpleMode = self._isSimpleMode
609 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
610 if self._isSimpleMode != oldSimpleMode:
611 self._scene.arrangeAll()
613 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
614 self._objColors[0] = profile.getPreferenceColour('model_colour')
615 self._objColors[1] = profile.getPreferenceColour('model_colour2')
616 self._objColors[2] = profile.getPreferenceColour('model_colour3')
617 self._objColors[3] = profile.getPreferenceColour('model_colour4')
618 self._scene.setMachineSize(self._machineSize)
619 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
620 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'))
622 if self._selectedObj is not None:
623 scale = self._selectedObj.getScale()
624 size = self._selectedObj.getSize()
625 self.scaleXctrl.setValue(round(scale[0], 2))
626 self.scaleYctrl.setValue(round(scale[1], 2))
627 self.scaleZctrl.setValue(round(scale[2], 2))
628 self.scaleXmmctrl.setValue(round(size[0], 2))
629 self.scaleYmmctrl.setValue(round(size[1], 2))
630 self.scaleZmmctrl.setValue(round(size[2], 2))
632 def OnKeyChar(self, keyCode):
633 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
634 if self._selectedObj is not None:
635 self._deleteObject(self._selectedObj)
637 if keyCode == wx.WXK_UP:
638 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
640 elif keyCode == wx.WXK_DOWN:
641 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
643 elif keyCode == wx.WXK_PAGEUP:
644 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
646 elif keyCode == wx.WXK_PAGEDOWN:
647 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
650 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
651 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
652 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
653 from collections import defaultdict
654 from gc import get_objects
655 self._beforeLeakTest = defaultdict(int)
656 for i in get_objects():
657 self._beforeLeakTest[type(i)] += 1
658 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
659 from collections import defaultdict
660 from gc import get_objects
661 self._afterLeakTest = defaultdict(int)
662 for i in get_objects():
663 self._afterLeakTest[type(i)] += 1
664 for k in self._afterLeakTest:
665 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
666 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
668 def ShaderUpdate(self, v, f):
669 s = opengl.GLShader(v, f)
671 self._objectLoadShader.release()
672 self._objectLoadShader = s
673 for obj in self._scene.objects():
674 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
677 def OnMouseDown(self,e):
678 self._mouseX = e.GetX()
679 self._mouseY = e.GetY()
680 self._mouseClick3DPos = self._mouse3Dpos
681 self._mouseClickFocus = self._focusObj
683 self._mouseState = 'doubleClick'
685 self._mouseState = 'dragOrClick'
686 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
687 p0 -= self.getObjectCenterPos() - self._viewTarget
688 p1 -= self.getObjectCenterPos() - self._viewTarget
689 if self.tool.OnDragStart(p0, p1):
690 self._mouseState = 'tool'
691 if self._mouseState == 'dragOrClick':
692 if e.GetButton() == 1:
693 if self._focusObj is not None:
694 self._selectObject(self._focusObj, False)
697 def OnMouseUp(self, e):
698 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
700 if self._mouseState == 'dragOrClick':
701 if e.GetButton() == 1:
702 self._selectObject(self._focusObj)
703 if e.GetButton() == 3:
705 if self._focusObj is not None:
706 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
707 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
708 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
709 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
710 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:
711 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
712 if len(self._scene.objects()) > 0:
713 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
714 if menu.MenuItemCount > 0:
717 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
718 self._scene.pushFree()
720 elif self._mouseState == 'tool':
721 if self.tempMatrix is not None and self._selectedObj is not None:
722 self._selectedObj.applyMatrix(self.tempMatrix)
723 self._scene.pushFree()
724 self._selectObject(self._selectedObj)
725 self.tempMatrix = None
726 self.tool.OnDragEnd()
728 self._mouseState = None
730 def OnMouseMotion(self,e):
731 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
732 p0 -= self.getObjectCenterPos() - self._viewTarget
733 p1 -= self.getObjectCenterPos() - self._viewTarget
735 if e.Dragging() and self._mouseState is not None:
736 if self._mouseState == 'tool':
737 self.tool.OnDrag(p0, p1)
738 elif not e.LeftIsDown() and e.RightIsDown():
739 self._mouseState = 'drag'
740 if wx.GetKeyState(wx.WXK_SHIFT):
741 a = math.cos(math.radians(self._yaw)) / 3.0
742 b = math.sin(math.radians(self._yaw)) / 3.0
743 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
744 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
745 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
746 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
748 self._yaw += e.GetX() - self._mouseX
749 self._pitch -= e.GetY() - self._mouseY
750 if self._pitch > 170:
754 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
755 self._mouseState = 'drag'
756 self._zoom += e.GetY() - self._mouseY
759 if self._zoom > numpy.max(self._machineSize) * 3:
760 self._zoom = numpy.max(self._machineSize) * 3
761 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
762 self._mouseState = 'dragObject'
763 z = max(0, self._mouseClick3DPos[2])
764 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
765 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
770 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
771 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
772 diff = cursorZ1 - cursorZ0
773 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
774 if not e.Dragging() or self._mouseState != 'tool':
775 self.tool.OnMouseMove(p0, p1)
777 self._mouseX = e.GetX()
778 self._mouseY = e.GetY()
780 def OnMouseWheel(self, e):
781 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
782 delta = max(min(delta,4),-4)
783 self._zoom *= 1.0 - delta / 10.0
786 if self._zoom > numpy.max(self._machineSize) * 3:
787 self._zoom = numpy.max(self._machineSize) * 3
790 def OnMouseLeave(self, e):
794 def getMouseRay(self, x, y):
795 if self._viewport is None:
796 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
797 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
798 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
799 p0 -= self._viewTarget
800 p1 -= self._viewTarget
803 def _init3DView(self):
804 # set viewing projection
805 size = self.GetSize()
806 glViewport(0, 0, size.GetWidth(), size.GetHeight())
809 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
811 glDisable(GL_RESCALE_NORMAL)
812 glDisable(GL_LIGHTING)
814 glEnable(GL_DEPTH_TEST)
815 glDisable(GL_CULL_FACE)
817 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
819 glClearColor(0.8, 0.8, 0.8, 1.0)
823 glMatrixMode(GL_PROJECTION)
825 aspect = float(size.GetWidth()) / float(size.GetHeight())
826 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
828 glMatrixMode(GL_MODELVIEW)
830 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
833 if machineCom.machineIsConnected():
834 self.printButton._imageID = 6
835 self.printButton._tooltip = _("Print")
836 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
837 self.printButton._imageID = 2
838 self.printButton._tooltip = _("Toolpath to SD")
840 self.printButton._imageID = 3
841 self.printButton._tooltip = _("Save toolpath")
843 if self._animView is not None:
844 self._viewTarget = self._animView.getPosition()
845 if self._animView.isDone():
846 self._animView = None
847 if self._animZoom is not None:
848 self._zoom = self._animZoom.getPosition()
849 if self._animZoom.isDone():
850 self._animZoom = None
851 if self.viewMode == 'gcode' and self._gcode is not None:
853 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
856 if self._objectShader is None:
857 if opengl.hasShaderSupport():
858 self._objectShader = opengl.GLShader("""
859 varying float light_amount;
863 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
864 gl_FrontColor = gl_Color;
866 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
870 varying float light_amount;
874 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
877 self._objectOverhangShader = opengl.GLShader("""
878 uniform float cosAngle;
879 uniform mat3 rotMatrix;
880 varying float light_amount;
884 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
885 gl_FrontColor = gl_Color;
887 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
889 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
891 light_amount = -10.0;
895 varying float light_amount;
899 if (light_amount == -10.0)
901 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
903 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
907 self._objectLoadShader = opengl.GLShader("""
908 uniform float intensity;
910 varying float light_amount;
914 vec4 tmp = gl_Vertex;
915 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
916 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
917 gl_Position = gl_ModelViewProjectionMatrix * tmp;
918 gl_FrontColor = gl_Color;
920 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
924 uniform float intensity;
925 varying float light_amount;
929 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
932 if self._objectShader is None or not self._objectShader.isValid():
933 self._objectShader = opengl.GLFakeShader()
934 self._objectOverhangShader = opengl.GLFakeShader()
935 self._objectLoadShader = None
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 self._viewport = glGetIntegerv(GL_VIEWPORT)
943 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
944 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
946 glClearColor(1,1,1,1)
947 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
949 if self.viewMode != 'gcode':
950 for n in xrange(0, len(self._scene.objects())):
951 obj = self._scene.objects()[n]
952 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
953 self._renderObject(obj)
955 if self._mouseX > -1:
957 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
958 if n < len(self._scene.objects()):
959 self._focusObj = self._scene.objects()[n]
961 self._focusObj = None
962 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
963 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
964 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
965 self._mouse3Dpos -= self._viewTarget
968 glTranslate(0,0,-self._zoom)
969 glRotate(-self._pitch, 1,0,0)
970 glRotate(self._yaw, 0,0,1)
971 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
973 if self.viewMode == 'gcode':
974 if self._gcode is not None and self._gcode.layerList is None:
975 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
976 self._gcodeLoadThread.daemon = True
977 self._gcodeLoadThread.start()
978 if self._gcode is not None and self._gcode.layerList is not None:
980 if profile.getMachineSetting('machine_center_is_zero') != 'True':
981 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
983 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
984 for n in xrange(0, drawUpTill):
985 c = 1.0 - float(drawUpTill - n) / 15
987 if len(self._gcodeVBOs) < n + 1:
988 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
989 if time.time() - t > 0.5:
992 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
993 if n == drawUpTill - 1:
994 if len(self._gcodeVBOs[n]) < 9:
995 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
997 self._gcodeVBOs[n][8].render(GL_QUADS)
999 self._gcodeVBOs[n][9].render(GL_QUADS)
1000 glColor3f(0, c, c/2)
1001 self._gcodeVBOs[n][10].render(GL_QUADS)
1003 self._gcodeVBOs[n][11].render(GL_QUADS)
1006 self._gcodeVBOs[n][12].render(GL_QUADS)
1007 glColor3f(c/2, c/2, 0.0)
1008 self._gcodeVBOs[n][13].render(GL_QUADS)
1010 self._gcodeVBOs[n][14].render(GL_QUADS)
1011 self._gcodeVBOs[n][15].render(GL_QUADS)
1013 self._gcodeVBOs[n][16].render(GL_LINES)
1016 self._gcodeVBOs[n][0].render(GL_LINES)
1017 glColor3f(c/2, 0, c)
1018 self._gcodeVBOs[n][1].render(GL_LINES)
1019 glColor3f(0, c, c/2)
1020 self._gcodeVBOs[n][2].render(GL_LINES)
1022 self._gcodeVBOs[n][3].render(GL_LINES)
1025 self._gcodeVBOs[n][4].render(GL_LINES)
1026 glColor3f(c/2, c/2, 0.0)
1027 self._gcodeVBOs[n][5].render(GL_LINES)
1029 self._gcodeVBOs[n][6].render(GL_LINES)
1030 self._gcodeVBOs[n][7].render(GL_LINES)
1033 glStencilFunc(GL_ALWAYS, 1, 1)
1034 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1036 if self.viewMode == 'overhang':
1037 self._objectOverhangShader.bind()
1038 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1040 self._objectShader.bind()
1041 for obj in self._scene.objects():
1042 if obj._loadAnim is not None:
1043 if obj._loadAnim.isDone():
1044 obj._loadAnim = None
1048 if self._focusObj == obj:
1050 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1053 if self._selectedObj == obj or self._selectedObj is None:
1054 #If we want transparent, then first render a solid black model to remove the printer size lines.
1055 if self.viewMode == 'transparent':
1056 glColor4f(0, 0, 0, 0)
1057 self._renderObject(obj)
1059 glBlendFunc(GL_ONE, GL_ONE)
1060 glDisable(GL_DEPTH_TEST)
1062 if self.viewMode == 'xray':
1063 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1064 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1065 glEnable(GL_STENCIL_TEST)
1067 if self.viewMode == 'overhang':
1068 if self._selectedObj == obj and self.tempMatrix is not None:
1069 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1071 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1073 if not self._scene.checkPlatform(obj):
1074 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1075 self._renderObject(obj)
1077 self._renderObject(obj, brightness)
1078 glDisable(GL_STENCIL_TEST)
1080 glEnable(GL_DEPTH_TEST)
1081 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1083 if self.viewMode == 'xray':
1086 glEnable(GL_STENCIL_TEST)
1087 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1088 glDisable(GL_DEPTH_TEST)
1089 for i in xrange(2, 15, 2):
1090 glStencilFunc(GL_EQUAL, i, 0xFF)
1091 glColor(float(i)/10, float(i)/10, float(i)/5)
1093 glVertex3f(-1000,-1000,-10)
1094 glVertex3f( 1000,-1000,-10)
1095 glVertex3f( 1000, 1000,-10)
1096 glVertex3f(-1000, 1000,-10)
1098 for i in xrange(1, 15, 2):
1099 glStencilFunc(GL_EQUAL, i, 0xFF)
1100 glColor(float(i)/10, 0, 0)
1102 glVertex3f(-1000,-1000,-10)
1103 glVertex3f( 1000,-1000,-10)
1104 glVertex3f( 1000, 1000,-10)
1105 glVertex3f(-1000, 1000,-10)
1108 glDisable(GL_STENCIL_TEST)
1109 glEnable(GL_DEPTH_TEST)
1111 self._objectShader.unbind()
1113 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1115 if self._objectLoadShader is not None:
1116 self._objectLoadShader.bind()
1117 glColor4f(0.2, 0.6, 1.0, 1.0)
1118 for obj in self._scene.objects():
1119 if obj._loadAnim is None:
1121 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1122 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1123 self._renderObject(obj)
1124 self._objectLoadShader.unbind()
1129 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1131 z = self._usbPrintMonitor.getZ()
1132 size = self._machineSize
1133 glColor4ub(255,255,0,128)
1135 glVertex3f(-size[0]/2,-size[1]/2, z)
1136 glVertex3f( size[0]/2,-size[1]/2, z)
1137 glVertex3f( size[0]/2, size[1]/2, z)
1138 glVertex3f(-size[0]/2, size[1]/2, z)
1141 if self.viewMode == 'gcode':
1142 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1143 glDisable(GL_DEPTH_TEST)
1146 glTranslate(0,-4,-10)
1147 glColor4ub(60,60,60,255)
1148 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1151 #Draw the object box-shadow, so you can see where it will collide with other objects.
1152 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1153 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1155 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1157 glEnable(GL_CULL_FACE)
1158 glColor4f(0,0,0,0.12)
1160 glVertex3f(-size[0], size[1], 0.1)
1161 glVertex3f(-size[0], -size[1], 0.1)
1162 glVertex3f( size[0], -size[1], 0.1)
1163 glVertex3f( size[0], size[1], 0.1)
1165 glDisable(GL_CULL_FACE)
1168 #Draw the outline of the selected object, on top of everything else except the GUI.
1169 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1170 glDisable(GL_DEPTH_TEST)
1171 glEnable(GL_CULL_FACE)
1172 glEnable(GL_STENCIL_TEST)
1174 glStencilFunc(GL_EQUAL, 0, 255)
1176 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1178 glColor4f(1,1,1,0.5)
1179 self._renderObject(self._selectedObj)
1180 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1182 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1183 glDisable(GL_STENCIL_TEST)
1184 glDisable(GL_CULL_FACE)
1185 glEnable(GL_DEPTH_TEST)
1187 if self._selectedObj is not None:
1189 pos = self.getObjectCenterPos()
1190 glTranslate(pos[0], pos[1], pos[2])
1193 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1194 glDisable(GL_DEPTH_TEST)
1197 glTranslate(0,-4,-10)
1198 glColor4ub(60,60,60,255)
1199 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1202 def _renderObject(self, obj, brightness = False, addSink = True):
1205 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1207 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1209 if self.tempMatrix is not None and obj == self._selectedObj:
1210 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1211 glMultMatrixf(tempMatrix)
1213 offset = obj.getDrawOffset()
1214 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1216 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1217 glMultMatrixf(tempMatrix)
1220 for m in obj._meshList:
1222 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1224 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1229 def _drawMachine(self):
1230 glEnable(GL_CULL_FACE)
1233 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1235 machine = profile.getMachineSetting('machine_type')
1236 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1237 if machine not in self._platformMesh:
1238 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1240 self._platformMesh[machine] = meshes[0]
1242 self._platformMesh[machine] = None
1243 if machine == 'ultimaker2':
1244 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1246 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1247 glColor4f(1,1,1,0.5)
1248 self._objectShader.bind()
1249 self._renderObject(self._platformMesh[machine], False, False)
1250 self._objectShader.unbind()
1252 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1253 if machine == 'ultimaker2':
1254 if not hasattr(self._platformMesh[machine], 'texture'):
1255 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1256 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1257 glEnable(GL_TEXTURE_2D)
1261 glTranslate(0,150,-5)
1266 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1269 glVertex3f( w, 0, h)
1271 glVertex3f(-w, 0, h)
1273 glVertex3f(-w, 0, 0)
1275 glVertex3f( w, 0, 0)
1278 glVertex3f(-w, d, h)
1280 glVertex3f( w, d, h)
1282 glVertex3f( w, d, 0)
1284 glVertex3f(-w, d, 0)
1286 glDisable(GL_TEXTURE_2D)
1287 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1293 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1294 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1295 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1296 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1297 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1298 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1301 #Cornerpoints for big blue square
1302 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1303 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1304 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1305 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1306 v4 = [ size[0] / 2, size[1] / 2, 0]
1307 v5 = [ size[0] / 2,-size[1] / 2, 0]
1308 v6 = [-size[0] / 2, size[1] / 2, 0]
1309 v7 = [-size[0] / 2,-size[1] / 2, 0]
1311 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1312 glEnableClientState(GL_VERTEX_ARRAY)
1313 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1315 glColor4ub(5, 171, 231, 64)
1316 glDrawArrays(GL_QUADS, 0, 4)
1317 glColor4ub(5, 171, 231, 96)
1318 glDrawArrays(GL_QUADS, 4, 8)
1319 glColor4ub(5, 171, 231, 128)
1320 glDrawArrays(GL_QUADS, 12, 8)
1321 glDisableClientState(GL_VERTEX_ARRAY)
1324 sx = self._machineSize[0]
1325 sy = self._machineSize[1]
1326 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1327 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1332 x1 = max(min(x1, sx/2), -sx/2)
1333 y1 = max(min(y1, sy/2), -sy/2)
1334 x2 = max(min(x2, sx/2), -sx/2)
1335 y2 = max(min(y2, sy/2), -sy/2)
1336 #Black or "white" checker
1337 if (x & 1) == (y & 1):
1338 glColor4ub(5, 171, 231, 127)
1340 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1342 glVertex3f(x1, y1, -0.02) #Draw bit below z0 to prevent zfighting.
1343 glVertex3f(x2, y1, -0.02)
1344 glVertex3f(x2, y2, -0.02)
1345 glVertex3f(x1, y2, -0.02)
1348 if machine == 'ultimaker2':
1350 glColor4ub(127, 127, 127, 200)
1351 #if UM2, draw bat-area zone for head. THe head can't stop there, because its bat-area.
1355 posX = sx / 2 - clipWidth
1356 posY = sy / 2 - clipHeight
1358 glVertex3f(posX, posY, 0.1)
1359 glVertex3f(posX+clipWidth, posY, 0.1)
1360 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1361 glVertex3f(posX, posY+clipHeight, 0.1)
1367 posY = sy / 2 - clipHeight
1369 glVertex3f(posX, posY, 0.1)
1370 glVertex3f(posX+clipWidth, posY, 0.1)
1371 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1372 glVertex3f(posX, posY+clipHeight, 0.1)
1377 posX = sx / 2 - clipWidth
1380 glVertex3f(posX, posY, 0.1)
1381 glVertex3f(posX+clipWidth, posY, 0.1)
1382 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1383 glVertex3f(posX, posY+clipHeight, 0.1)
1391 glVertex3f(posX, posY, 0.1)
1392 glVertex3f(posX+clipWidth, posY, 0.1)
1393 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1394 glVertex3f(posX, posY+clipHeight, 0.1)
1398 glDisable(GL_CULL_FACE)
1400 def _generateGCodeVBOs(self, layer):
1402 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1403 if ':' in extrudeType:
1404 extruder = int(extrudeType[extrudeType.find(':')+1:])
1405 extrudeType = extrudeType[0:extrudeType.find(':')]
1408 pointList = numpy.zeros((0,3), numpy.float32)
1410 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1412 a = numpy.concatenate((a[:-1], a[1:]), 1)
1413 a = a.reshape((len(a) * 2, 3))
1414 pointList = numpy.concatenate((pointList, a))
1415 ret.append(opengl.GLVBO(pointList))
1418 def _generateGCodeVBOs2(self, layer):
1419 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1420 filamentArea = math.pi * filamentRadius * filamentRadius
1421 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1424 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1425 if ':' in extrudeType:
1426 extruder = int(extrudeType[extrudeType.find(':')+1:])
1427 extrudeType = extrudeType[0:extrudeType.find(':')]
1430 pointList = numpy.zeros((0,3), numpy.float32)
1432 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1434 if extrudeType == 'FILL':
1437 normal = a[1:] - a[:-1]
1438 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1439 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1442 ePerDist = path['extrusion'][1:] / lens
1444 lineWidth = ePerDist / path['layerThickness'] / 2.0
1446 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1448 normal[:,0] *= lineWidth
1449 normal[:,1] *= lineWidth
1451 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1452 b = numpy.concatenate((b, a[1:] + normal), 1)
1453 b = numpy.concatenate((b, a[1:] - normal), 1)
1454 b = numpy.concatenate((b, a[:-1] - normal), 1)
1455 b = numpy.concatenate((b, a[:-1] + normal), 1)
1456 b = b.reshape((len(b) * 4, 3))
1459 normal2 = normal[:-1] + normal[1:]
1460 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1461 normal2[:,0] /= lens2
1462 normal2[:,1] /= lens2
1463 normal2[:,0] *= lineWidth[:-1]
1464 normal2[:,1] *= lineWidth[:-1]
1466 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1467 c = numpy.concatenate((c, a[1:-1]), 1)
1468 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1469 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1470 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1472 c = numpy.concatenate((c, a[1:-1]), 1)
1473 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1474 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1475 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1477 c = c.reshape((len(c) * 8, 3))
1479 pointList = numpy.concatenate((pointList, b, c))
1481 pointList = numpy.concatenate((pointList, b))
1482 ret.append(opengl.GLVBO(pointList))
1484 pointList = numpy.zeros((0,3), numpy.float32)
1486 if path['type'] == 'move':
1487 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1488 a = numpy.concatenate((a[:-1], a[1:]), 1)
1489 a = a.reshape((len(a) * 2, 3))
1490 pointList = numpy.concatenate((pointList, a))
1491 if path['type'] == 'retract':
1492 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1493 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1494 a = a.reshape((len(a) * 2, 3))
1495 pointList = numpy.concatenate((pointList, a))
1496 ret.append(opengl.GLVBO(pointList))
1500 def getObjectCenterPos(self):
1501 if self._selectedObj is None:
1502 return [0.0, 0.0, 0.0]
1503 pos = self._selectedObj.getPosition()
1504 size = self._selectedObj.getSize()
1505 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1507 def getObjectBoundaryCircle(self):
1508 if self._selectedObj is None:
1510 return self._selectedObj.getBoundaryCircle()
1512 def getObjectSize(self):
1513 if self._selectedObj is None:
1514 return [0.0, 0.0, 0.0]
1515 return self._selectedObj.getSize()
1517 def getObjectMatrix(self):
1518 if self._selectedObj is None:
1519 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1520 return self._selectedObj.getMatrix()
1522 class shaderEditor(wx.Dialog):
1523 def __init__(self, parent, callback, v, f):
1524 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1525 self._callback = callback
1526 s = wx.BoxSizer(wx.VERTICAL)
1528 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1529 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1530 s.Add(self._vertex, 1, flag=wx.EXPAND)
1531 s.Add(self._fragment, 1, flag=wx.EXPAND)
1533 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1534 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1536 self.SetPosition(self.GetParent().GetPosition())
1537 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1540 def OnText(self, e):
1541 self._callback(self._vertex.GetValue(), self._fragment.GetValue())