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.gui import printWindow2
20 from Cura.util import profile
21 from Cura.util import meshLoader
22 from Cura.util import objectScene
23 from Cura.util import resources
24 from Cura.util import sliceEngine
25 from Cura.util import machineCom
26 from Cura.util import removableStorage
27 from Cura.util import gcodeInterpreter
28 from Cura.util.printerConnection import printerConnectionManager
29 from Cura.gui.util import previewTools
30 from Cura.gui.util import opengl
31 from Cura.gui.util import openglGui
32 from Cura.gui.tools import youmagineGui
33 from Cura.gui.tools import imageToMesh
35 class SceneView(openglGui.glGuiPanel):
36 def __init__(self, parent):
37 super(SceneView, self).__init__(parent)
42 self._scene = objectScene.Scene()
45 self._gcodeFilename = None
46 self._gcodeLoadThread = None
47 self._objectShader = None
48 self._objectLoadShader = None
50 self._selectedObj = None
51 self._objColors = [None,None,None,None]
54 self._mouseState = None
55 self._viewTarget = numpy.array([0,0,0], numpy.float32)
58 self._platformMesh = {}
59 self._platformTexture = None
60 self._isSimpleMode = True
61 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
62 self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager()
65 self._modelMatrix = None
66 self._projMatrix = None
67 self.tempMatrix = None
69 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
70 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
71 self.printButton.setDisabled(True)
74 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
75 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
76 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
78 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
79 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
81 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
82 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
84 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
85 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
86 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
88 self.rotateToolButton.setExpandArrow(True)
89 self.scaleToolButton.setExpandArrow(True)
90 self.mirrorToolButton.setExpandArrow(True)
92 self.scaleForm = openglGui.glFrame(self, (2, -2))
93 openglGui.glGuiLayoutGrid(self.scaleForm)
94 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
95 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
96 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
97 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
98 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
99 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
100 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
101 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
102 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
103 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
104 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
105 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
106 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
107 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
109 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
110 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
112 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
113 self.youMagineButton.setDisabled(True)
115 self.notification = openglGui.glNotification(self, (0, 0))
117 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
118 self._sceneUpdateTimer = wx.Timer(self)
119 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
120 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
121 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
125 self.updateToolButtons()
126 self.updateProfileToControls()
128 def loadGCodeFile(self, filename):
129 self.OnDeleteAll(None)
130 if self._gcode is not None:
132 for layerVBOlist in self._gcodeVBOs:
133 for vbo in layerVBOlist:
134 self.glReleaseList.append(vbo)
136 self._gcode = gcodeInterpreter.gcode()
137 self._gcodeFilename = filename
138 self.printButton.setBottomText('')
139 self.viewSelection.setValue(4)
140 self.printButton.setDisabled(False)
141 self.youMagineButton.setDisabled(True)
144 def loadSceneFiles(self, filenames):
145 self.youMagineButton.setDisabled(False)
146 #if self.viewSelection.getValue() == 4:
147 # self.viewSelection.setValue(0)
148 # self.OnViewChange()
149 self.loadScene(filenames)
151 def loadFiles(self, filenames):
152 mainWindow = self.GetParent().GetParent().GetParent()
153 # only one GCODE file can be active
154 # so if single gcode file, process this
155 # otherwise ignore all gcode files
157 if len(filenames) == 1:
158 filename = filenames[0]
159 ext = os.path.splitext(filename)[1].lower()
160 if ext == '.g' or ext == '.gcode':
161 gcodeFilename = filename
162 mainWindow.addToModelMRU(filename)
163 if gcodeFilename is not None:
164 self.loadGCodeFile(gcodeFilename)
166 # process directories and special file types
167 # and keep scene files for later processing
169 ignored_types = dict()
170 # use file list as queue
171 # pop first entry for processing and append new files at end
173 filename = filenames.pop(0)
174 if os.path.isdir(filename):
175 # directory: queue all included files and directories
176 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
178 ext = os.path.splitext(filename)[1].lower()
180 profile.loadProfile(filename)
181 mainWindow.addToProfileMRU(filename)
182 elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
183 scene_filenames.append(filename)
184 mainWindow.addToModelMRU(filename)
186 ignored_types[ext] = 1
188 ignored_types = ignored_types.keys()
190 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
191 mainWindow.updateProfileToAllControls()
192 # now process all the scene files
194 self.loadSceneFiles(scene_filenames)
195 self._selectObject(None)
197 newZoom = numpy.max(self._machineSize)
198 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
199 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
201 def showLoadModel(self, button = 1):
203 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)
204 dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
205 if dlg.ShowModal() != wx.ID_OK:
208 filenames = dlg.GetPaths()
210 if len(filenames) < 1:
212 profile.putPreference('lastFile', filenames[0])
213 self.loadFiles(filenames)
215 def showSaveModel(self):
216 if len(self._scene.objects()) < 1:
218 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
219 dlg.SetWildcard(meshLoader.saveWildcardFilter())
220 if dlg.ShowModal() != wx.ID_OK:
223 filename = dlg.GetPath()
225 meshLoader.saveMeshes(filename, self._scene.objects())
227 def OnPrintButton(self, button):
229 connectionEntry = self._printerConnectionManager.getAvailableConnection()
230 if machineCom.machineIsConnected():
231 self.showPrintWindow()
232 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
233 drives = removableStorage.getPossibleSDcardDrives()
235 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))
236 if dlg.ShowModal() != wx.ID_OK:
239 drive = drives[dlg.GetSelection()]
243 filename = self._scene._objectList[0].getName() + '.gcode'
244 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
245 elif connectionEntry is not None:
246 connection = connectionEntry.connection
247 if connectionEntry.window is None or not connectionEntry.window:
248 connectionEntry.window = printWindow2.printWindow(connection)
249 connectionEntry.window.Show()
250 connectionEntry.window.Raise()
251 if not connection.loadFile(self._gcodeFilename):
252 if connection.isPrinting():
253 self.notification.message("Cannot start print, because other print still running.")
255 self.notification.message("Failed to start print...")
260 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
261 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
262 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
266 def showPrintWindow(self):
267 if self._gcodeFilename is None:
269 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
270 wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING)
272 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
273 if self._gcodeFilename == self._slicer.getGCodeFilename():
274 self._slicer.submitSliceInfoOnline()
276 def showSaveGCode(self):
277 if len(self._scene._objectList) < 1:
279 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
280 filename = self._scene._objectList[0].getName() + '.gcode'
281 dlg.SetFilename(filename)
282 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
283 if dlg.ShowModal() != wx.ID_OK:
286 filename = dlg.GetPath()
289 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
291 def _copyFile(self, fileA, fileB, allowEject = False):
293 size = float(os.stat(fileA).st_size)
294 with open(fileA, 'rb') as fsrc:
295 with open(fileB, 'wb') as fdst:
297 buf = fsrc.read(16*1024)
301 self.printButton.setProgressBar(float(fsrc.tell()) / size)
306 self.notification.message("Failed to save")
309 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...'))
311 self.notification.message("Saved as %s" % (fileB))
312 self.printButton.setProgressBar(None)
313 if fileA == self._slicer.getGCodeFilename():
314 self._slicer.submitSliceInfoOnline()
316 def _showSliceLog(self):
317 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
321 def OnToolSelect(self, button):
322 if self.rotateToolButton.getSelected():
323 self.tool = previewTools.toolRotate(self)
324 elif self.scaleToolButton.getSelected():
325 self.tool = previewTools.toolScale(self)
326 elif self.mirrorToolButton.getSelected():
327 self.tool = previewTools.toolNone(self)
329 self.tool = previewTools.toolNone(self)
330 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
331 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
332 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
333 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
334 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
335 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
336 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
337 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
339 def updateToolButtons(self):
340 if self._selectedObj is None:
344 self.rotateToolButton.setHidden(hidden)
345 self.scaleToolButton.setHidden(hidden)
346 self.mirrorToolButton.setHidden(hidden)
348 self.rotateToolButton.setSelected(False)
349 self.scaleToolButton.setSelected(False)
350 self.mirrorToolButton.setSelected(False)
353 def OnViewChange(self):
354 if self.viewSelection.getValue() == 4:
355 self.viewMode = 'gcode'
356 if self._gcode is not None and self._gcode.layerList is not None:
357 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
358 self._selectObject(None)
359 elif self.viewSelection.getValue() == 1:
360 self.viewMode = 'overhang'
361 elif self.viewSelection.getValue() == 2:
362 self.viewMode = 'transparent'
363 elif self.viewSelection.getValue() == 3:
364 self.viewMode = 'xray'
366 self.viewMode = 'normal'
367 self.layerSelect.setHidden(self.viewMode != 'gcode')
370 def OnRotateReset(self, button):
371 if self._selectedObj is None:
373 self._selectedObj.resetRotation()
374 self._scene.pushFree()
375 self._selectObject(self._selectedObj)
378 def OnLayFlat(self, button):
379 if self._selectedObj is None:
381 self._selectedObj.layFlat()
382 self._scene.pushFree()
383 self._selectObject(self._selectedObj)
386 def OnScaleReset(self, button):
387 if self._selectedObj is None:
389 self._selectedObj.resetScale()
390 self._selectObject(self._selectedObj)
391 self.updateProfileToControls()
394 def OnScaleMax(self, button):
395 if self._selectedObj is None:
397 machine = profile.getMachineSetting('machine_type')
398 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
399 self._scene.pushFree()
401 if machine == "ultimaker2":
402 #This is bad and Jaime should feel bad!
403 self._selectedObj.setPosition(numpy.array([0.0,-10.0]))
404 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
405 self._selectedObj.setPosition(numpy.array([0.0,0.0]))
406 self._scene.pushFree()
408 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
409 self._scene.pushFree()
410 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
411 self._scene.pushFree()
412 self._selectObject(self._selectedObj)
413 self.updateProfileToControls()
416 def OnMirror(self, axis):
417 if self._selectedObj is None:
419 self._selectedObj.mirror(axis)
422 def OnScaleEntry(self, value, axis):
423 if self._selectedObj is None:
429 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
430 self.updateProfileToControls()
431 self._scene.pushFree()
432 self._selectObject(self._selectedObj)
435 def OnScaleEntryMM(self, value, axis):
436 if self._selectedObj is None:
442 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
443 self.updateProfileToControls()
444 self._scene.pushFree()
445 self._selectObject(self._selectedObj)
448 def OnDeleteAll(self, e):
449 while len(self._scene.objects()) > 0:
450 self._deleteObject(self._scene.objects()[0])
451 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
453 def OnMultiply(self, e):
454 if self._focusObj is None:
457 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
458 if dlg.ShowModal() != wx.ID_OK:
467 self._scene.add(newObj)
468 self._scene.centerAll()
469 if not self._scene.checkPlatform(newObj):
474 self.notification.message("Could not create more then %d items" % (n - 1))
475 self._scene.remove(newObj)
476 self._scene.centerAll()
479 def OnSplitObject(self, e):
480 if self._focusObj is None:
482 self._scene.remove(self._focusObj)
483 for obj in self._focusObj.split(self._splitCallback):
484 if numpy.max(obj.getSize()) > 2.0:
486 self._scene.centerAll()
487 self._selectObject(None)
490 def OnCenter(self, e):
491 if self._focusObj is None:
493 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
494 self._scene.pushFree()
495 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
496 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
499 def _splitCallback(self, progress):
502 def OnMergeObjects(self, e):
503 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
504 if len(self._scene.objects()) == 2:
505 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
508 self._scene.merge(self._selectedObj, self._focusObj)
511 def sceneUpdated(self):
512 self._sceneUpdateTimer.Start(500, True)
513 self._slicer.abortSlicer()
514 self._scene.updateSizeOffsets()
517 def _onRunSlicer(self, e):
518 if self._isSimpleMode:
519 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
520 self._slicer.runSlicer(self._scene)
521 if self._isSimpleMode:
522 profile.resetTempOverride()
524 def _updateSliceProgress(self, progressValue, ready):
526 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
528 self.printButton.setDisabled(not ready)
529 if progressValue >= 0.0:
530 self.printButton.setProgressBar(progressValue)
532 self.printButton.setProgressBar(None)
533 if self._gcode is not None:
535 for layerVBOlist in self._gcodeVBOs:
536 for vbo in layerVBOlist:
537 self.glReleaseList.append(vbo)
540 self.printButton.setProgressBar(None)
541 text = '%s' % (self._slicer.getPrintTime())
542 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
543 amount = self._slicer.getFilamentAmount(e)
546 text += '\n%s' % (amount)
547 cost = self._slicer.getFilamentCost(e)
549 text += '\n%s' % (cost)
550 self.printButton.setBottomText(text)
551 self._gcode = gcodeInterpreter.gcode()
552 self._gcodeFilename = self._slicer.getGCodeFilename()
554 self.printButton.setBottomText('')
557 def _loadGCode(self):
558 self._gcode.progressCallback = self._gcodeLoadCallback
559 self._gcode.load(self._gcodeFilename)
561 def _gcodeLoadCallback(self, progress):
562 if not self or self._gcode is None:
564 if len(self._gcode.layerList) % 15 == 0:
566 if self._gcode is None:
568 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
569 if self.viewMode == 'gcode':
573 def loadScene(self, fileList):
574 for filename in fileList:
576 ext = os.path.splitext(filename)[1].lower()
577 if ext in imageToMesh.supportedExtensions():
578 imageToMesh.convertImageDialog(self, filename).Show()
581 objList = meshLoader.loadMeshes(filename)
583 traceback.print_exc()
586 if self._objectLoadShader is not None:
587 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
591 if not self._scene.checkPlatform(obj):
592 self._scene.centerAll()
593 self._selectObject(obj)
594 if obj.getScale()[0] < 1.0:
595 self.notification.message("Warning: Object scaled down.")
598 def _deleteObject(self, obj):
599 if obj == self._selectedObj:
600 self._selectObject(None)
601 if obj == self._focusObj:
602 self._focusObj = None
603 self._scene.remove(obj)
604 for m in obj._meshList:
605 if m.vbo is not None and m.vbo.decRef():
606 self.glReleaseList.append(m.vbo)
611 def _selectObject(self, obj, zoom = True):
612 if obj != self._selectedObj:
613 self._selectedObj = obj
614 self.updateProfileToControls()
615 self.updateToolButtons()
616 if zoom and obj is not None:
617 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
618 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
619 newZoom = obj.getBoundaryCircle() * 6
620 if newZoom > numpy.max(self._machineSize) * 3:
621 newZoom = numpy.max(self._machineSize) * 3
622 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
624 def updateProfileToControls(self):
625 oldSimpleMode = self._isSimpleMode
626 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
627 if self._isSimpleMode != oldSimpleMode:
628 self._scene.arrangeAll()
630 self._scene.updateSizeOffsets(True)
631 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
632 self._objColors[0] = profile.getPreferenceColour('model_colour')
633 self._objColors[1] = profile.getPreferenceColour('model_colour2')
634 self._objColors[2] = profile.getPreferenceColour('model_colour3')
635 self._objColors[3] = profile.getPreferenceColour('model_colour4')
636 self._scene.updateMachineDimensions()
638 if self._selectedObj is not None:
639 scale = self._selectedObj.getScale()
640 size = self._selectedObj.getSize()
641 self.scaleXctrl.setValue(round(scale[0], 2))
642 self.scaleYctrl.setValue(round(scale[1], 2))
643 self.scaleZctrl.setValue(round(scale[2], 2))
644 self.scaleXmmctrl.setValue(round(size[0], 2))
645 self.scaleYmmctrl.setValue(round(size[1], 2))
646 self.scaleZmmctrl.setValue(round(size[2], 2))
648 def OnKeyChar(self, keyCode):
649 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
650 if self._selectedObj is not None:
651 self._deleteObject(self._selectedObj)
653 if keyCode == wx.WXK_UP:
654 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
656 elif keyCode == wx.WXK_DOWN:
657 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
659 elif keyCode == wx.WXK_PAGEUP:
660 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
662 elif keyCode == wx.WXK_PAGEDOWN:
663 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
666 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
667 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
668 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
669 from collections import defaultdict
670 from gc import get_objects
671 self._beforeLeakTest = defaultdict(int)
672 for i in get_objects():
673 self._beforeLeakTest[type(i)] += 1
674 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
675 from collections import defaultdict
676 from gc import get_objects
677 self._afterLeakTest = defaultdict(int)
678 for i in get_objects():
679 self._afterLeakTest[type(i)] += 1
680 for k in self._afterLeakTest:
681 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
682 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
684 def ShaderUpdate(self, v, f):
685 s = opengl.GLShader(v, f)
687 self._objectLoadShader.release()
688 self._objectLoadShader = s
689 for obj in self._scene.objects():
690 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
693 def OnMouseDown(self,e):
694 self._mouseX = e.GetX()
695 self._mouseY = e.GetY()
696 self._mouseClick3DPos = self._mouse3Dpos
697 self._mouseClickFocus = self._focusObj
699 self._mouseState = 'doubleClick'
701 self._mouseState = 'dragOrClick'
702 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
703 p0 -= self.getObjectCenterPos() - self._viewTarget
704 p1 -= self.getObjectCenterPos() - self._viewTarget
705 if self.tool.OnDragStart(p0, p1):
706 self._mouseState = 'tool'
707 if self._mouseState == 'dragOrClick':
708 if e.GetButton() == 1:
709 if self._focusObj is not None:
710 self._selectObject(self._focusObj, False)
713 def OnMouseUp(self, e):
714 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
716 if self._mouseState == 'dragOrClick':
717 if e.GetButton() == 1:
718 self._selectObject(self._focusObj)
719 if e.GetButton() == 3:
721 if self._focusObj is not None:
722 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
723 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
724 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
725 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
726 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:
727 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
728 if len(self._scene.objects()) > 0:
729 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
730 if menu.MenuItemCount > 0:
733 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
734 self._scene.pushFree()
736 elif self._mouseState == 'tool':
737 if self.tempMatrix is not None and self._selectedObj is not None:
738 self._selectedObj.applyMatrix(self.tempMatrix)
739 self._scene.pushFree()
740 self._selectObject(self._selectedObj)
741 self.tempMatrix = None
742 self.tool.OnDragEnd()
744 self._mouseState = None
746 def OnMouseMotion(self,e):
747 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
748 p0 -= self.getObjectCenterPos() - self._viewTarget
749 p1 -= self.getObjectCenterPos() - self._viewTarget
751 if e.Dragging() and self._mouseState is not None:
752 if self._mouseState == 'tool':
753 self.tool.OnDrag(p0, p1)
754 elif not e.LeftIsDown() and e.RightIsDown():
755 self._mouseState = 'drag'
756 if wx.GetKeyState(wx.WXK_SHIFT):
757 a = math.cos(math.radians(self._yaw)) / 3.0
758 b = math.sin(math.radians(self._yaw)) / 3.0
759 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
760 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
761 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
762 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
764 self._yaw += e.GetX() - self._mouseX
765 self._pitch -= e.GetY() - self._mouseY
766 if self._pitch > 170:
770 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
771 self._mouseState = 'drag'
772 self._zoom += e.GetY() - self._mouseY
775 if self._zoom > numpy.max(self._machineSize) * 3:
776 self._zoom = numpy.max(self._machineSize) * 3
777 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
778 self._mouseState = 'dragObject'
779 z = max(0, self._mouseClick3DPos[2])
780 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
781 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
786 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
787 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
788 diff = cursorZ1 - cursorZ0
789 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
790 if not e.Dragging() or self._mouseState != 'tool':
791 self.tool.OnMouseMove(p0, p1)
793 self._mouseX = e.GetX()
794 self._mouseY = e.GetY()
796 def OnMouseWheel(self, e):
797 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
798 delta = max(min(delta,4),-4)
799 self._zoom *= 1.0 - delta / 10.0
802 if self._zoom > numpy.max(self._machineSize) * 3:
803 self._zoom = numpy.max(self._machineSize) * 3
806 def OnMouseLeave(self, e):
810 def getMouseRay(self, x, y):
811 if self._viewport is None:
812 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
813 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
814 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
815 p0 -= self._viewTarget
816 p1 -= self._viewTarget
819 def _init3DView(self):
820 # set viewing projection
821 size = self.GetSize()
822 glViewport(0, 0, size.GetWidth(), size.GetHeight())
825 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
827 glDisable(GL_RESCALE_NORMAL)
828 glDisable(GL_LIGHTING)
830 glEnable(GL_DEPTH_TEST)
831 glDisable(GL_CULL_FACE)
833 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
835 glClearColor(0.8, 0.8, 0.8, 1.0)
839 glMatrixMode(GL_PROJECTION)
841 aspect = float(size.GetWidth()) / float(size.GetHeight())
842 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
844 glMatrixMode(GL_MODELVIEW)
846 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
849 connectionEntry = self._printerConnectionManager.getAvailableConnection()
850 if machineCom.machineIsConnected():
851 self.printButton._imageID = 6
852 self.printButton._tooltip = _("Print")
853 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
854 self.printButton._imageID = 2
855 self.printButton._tooltip = _("Toolpath to SD")
856 elif connectionEntry is not None:
857 self.printButton._imageID = connectionEntry.icon
858 self.printButton._tooltip = _("Print with %s") % (connectionEntry.name)
860 self.printButton._imageID = 3
861 self.printButton._tooltip = _("Save toolpath")
863 if self._animView is not None:
864 self._viewTarget = self._animView.getPosition()
865 if self._animView.isDone():
866 self._animView = None
867 if self._animZoom is not None:
868 self._zoom = self._animZoom.getPosition()
869 if self._animZoom.isDone():
870 self._animZoom = None
871 if self.viewMode == 'gcode' and self._gcode is not None:
873 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
876 if self._objectShader is None:
877 if opengl.hasShaderSupport():
878 self._objectShader = opengl.GLShader("""
879 varying float light_amount;
883 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
884 gl_FrontColor = gl_Color;
886 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
890 varying float light_amount;
894 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
897 self._objectOverhangShader = opengl.GLShader("""
898 uniform float cosAngle;
899 uniform mat3 rotMatrix;
900 varying float light_amount;
904 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
905 gl_FrontColor = gl_Color;
907 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
909 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
911 light_amount = -10.0;
915 varying float light_amount;
919 if (light_amount == -10.0)
921 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
923 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
927 self._objectLoadShader = opengl.GLShader("""
928 uniform float intensity;
930 varying float light_amount;
934 vec4 tmp = gl_Vertex;
935 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
936 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
937 gl_Position = gl_ModelViewProjectionMatrix * tmp;
938 gl_FrontColor = gl_Color;
940 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
944 uniform float intensity;
945 varying float light_amount;
949 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
952 if self._objectShader is None or not self._objectShader.isValid():
953 self._objectShader = opengl.GLFakeShader()
954 self._objectOverhangShader = opengl.GLFakeShader()
955 self._objectLoadShader = None
957 glTranslate(0,0,-self._zoom)
958 glRotate(-self._pitch, 1,0,0)
959 glRotate(self._yaw, 0,0,1)
960 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
962 self._viewport = glGetIntegerv(GL_VIEWPORT)
963 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
964 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
966 glClearColor(1,1,1,1)
967 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
969 if self.viewMode != 'gcode':
970 for n in xrange(0, len(self._scene.objects())):
971 obj = self._scene.objects()[n]
972 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
973 self._renderObject(obj)
975 if self._mouseX > -1:
977 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
978 if n < len(self._scene.objects()):
979 self._focusObj = self._scene.objects()[n]
981 self._focusObj = None
982 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
983 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
984 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
985 self._mouse3Dpos -= self._viewTarget
988 glTranslate(0,0,-self._zoom)
989 glRotate(-self._pitch, 1,0,0)
990 glRotate(self._yaw, 0,0,1)
991 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
993 if self.viewMode == 'gcode':
994 if self._gcode is not None and self._gcode.layerList is None:
995 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
996 self._gcodeLoadThread.daemon = True
997 self._gcodeLoadThread.start()
998 if self._gcode is not None and self._gcode.layerList is not None:
1000 if profile.getMachineSetting('machine_center_is_zero') != 'True':
1001 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
1003 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
1004 for n in xrange(0, drawUpTill):
1005 c = 1.0 - float(drawUpTill - n) / 15
1007 if len(self._gcodeVBOs) < n + 1:
1008 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
1009 if time.time() - t > 0.5:
1012 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
1013 if n == drawUpTill - 1:
1014 if len(self._gcodeVBOs[n]) < 9:
1015 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
1017 self._gcodeVBOs[n][8].render(GL_QUADS)
1018 glColor3f(c/2, 0, c)
1019 self._gcodeVBOs[n][9].render(GL_QUADS)
1020 glColor3f(0, c, c/2)
1021 self._gcodeVBOs[n][10].render(GL_QUADS)
1023 self._gcodeVBOs[n][11].render(GL_QUADS)
1026 self._gcodeVBOs[n][12].render(GL_QUADS)
1027 glColor3f(c/2, c/2, 0.0)
1028 self._gcodeVBOs[n][13].render(GL_QUADS)
1030 self._gcodeVBOs[n][14].render(GL_QUADS)
1031 self._gcodeVBOs[n][15].render(GL_QUADS)
1033 self._gcodeVBOs[n][16].render(GL_LINES)
1036 self._gcodeVBOs[n][0].render(GL_LINES)
1037 glColor3f(c/2, 0, c)
1038 self._gcodeVBOs[n][1].render(GL_LINES)
1039 glColor3f(0, c, c/2)
1040 self._gcodeVBOs[n][2].render(GL_LINES)
1042 self._gcodeVBOs[n][3].render(GL_LINES)
1045 self._gcodeVBOs[n][4].render(GL_LINES)
1046 glColor3f(c/2, c/2, 0.0)
1047 self._gcodeVBOs[n][5].render(GL_LINES)
1049 self._gcodeVBOs[n][6].render(GL_LINES)
1050 self._gcodeVBOs[n][7].render(GL_LINES)
1053 glStencilFunc(GL_ALWAYS, 1, 1)
1054 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1056 if self.viewMode == 'overhang':
1057 self._objectOverhangShader.bind()
1058 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1060 self._objectShader.bind()
1061 for obj in self._scene.objects():
1062 if obj._loadAnim is not None:
1063 if obj._loadAnim.isDone():
1064 obj._loadAnim = None
1068 if self._focusObj == obj:
1070 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1073 if self._selectedObj == obj or self._selectedObj is None:
1074 #If we want transparent, then first render a solid black model to remove the printer size lines.
1075 if self.viewMode == 'transparent':
1076 glColor4f(0, 0, 0, 0)
1077 self._renderObject(obj)
1079 glBlendFunc(GL_ONE, GL_ONE)
1080 glDisable(GL_DEPTH_TEST)
1082 if self.viewMode == 'xray':
1083 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1084 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1085 glEnable(GL_STENCIL_TEST)
1087 if self.viewMode == 'overhang':
1088 if self._selectedObj == obj and self.tempMatrix is not None:
1089 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1091 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1093 if not self._scene.checkPlatform(obj):
1094 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1095 self._renderObject(obj)
1097 self._renderObject(obj, brightness)
1098 glDisable(GL_STENCIL_TEST)
1100 glEnable(GL_DEPTH_TEST)
1101 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1103 if self.viewMode == 'xray':
1106 glEnable(GL_STENCIL_TEST)
1107 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1108 glDisable(GL_DEPTH_TEST)
1109 for i in xrange(2, 15, 2):
1110 glStencilFunc(GL_EQUAL, i, 0xFF)
1111 glColor(float(i)/10, float(i)/10, float(i)/5)
1113 glVertex3f(-1000,-1000,-10)
1114 glVertex3f( 1000,-1000,-10)
1115 glVertex3f( 1000, 1000,-10)
1116 glVertex3f(-1000, 1000,-10)
1118 for i in xrange(1, 15, 2):
1119 glStencilFunc(GL_EQUAL, i, 0xFF)
1120 glColor(float(i)/10, 0, 0)
1122 glVertex3f(-1000,-1000,-10)
1123 glVertex3f( 1000,-1000,-10)
1124 glVertex3f( 1000, 1000,-10)
1125 glVertex3f(-1000, 1000,-10)
1128 glDisable(GL_STENCIL_TEST)
1129 glEnable(GL_DEPTH_TEST)
1131 self._objectShader.unbind()
1133 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1135 if self._objectLoadShader is not None:
1136 self._objectLoadShader.bind()
1137 glColor4f(0.2, 0.6, 1.0, 1.0)
1138 for obj in self._scene.objects():
1139 if obj._loadAnim is None:
1141 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1142 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1143 self._renderObject(obj)
1144 self._objectLoadShader.unbind()
1149 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1151 z = self._usbPrintMonitor.getZ()
1152 size = self._machineSize
1153 glColor4ub(255,255,0,128)
1155 glVertex3f(-size[0]/2,-size[1]/2, z)
1156 glVertex3f( size[0]/2,-size[1]/2, z)
1157 glVertex3f( size[0]/2, size[1]/2, z)
1158 glVertex3f(-size[0]/2, size[1]/2, z)
1161 if self.viewMode == 'gcode':
1162 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1163 glDisable(GL_DEPTH_TEST)
1166 glTranslate(0,-4,-10)
1167 glColor4ub(60,60,60,255)
1168 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1171 #Draw the object box-shadow, so you can see where it will collide with other objects.
1172 if self._selectedObj is not None:
1174 glEnable(GL_CULL_FACE)
1175 glColor4f(0,0,0,0.16)
1177 for obj in self._scene.objects():
1179 glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1180 glBegin(GL_TRIANGLE_FAN)
1181 for p in obj._boundaryHull[::-1]:
1182 glVertex3f(p[0], p[1], 0)
1186 glColor4f(0,0,0,0.06)
1187 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1188 glBegin(GL_TRIANGLE_FAN)
1189 for p in self._selectedObj._printAreaHull[::-1]:
1190 glVertex3f(p[0], p[1], 0)
1192 glBegin(GL_TRIANGLE_FAN)
1193 for p in self._selectedObj._headAreaMinHull[::-1]:
1194 glVertex3f(p[0], p[1], 0)
1197 glDisable(GL_CULL_FACE)
1200 #Draw the outline of the selected object, on top of everything else except the GUI.
1201 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1202 glDisable(GL_DEPTH_TEST)
1203 glEnable(GL_CULL_FACE)
1204 glEnable(GL_STENCIL_TEST)
1206 glStencilFunc(GL_EQUAL, 0, 255)
1208 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1210 glColor4f(1,1,1,0.5)
1211 self._renderObject(self._selectedObj)
1212 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1214 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1215 glDisable(GL_STENCIL_TEST)
1216 glDisable(GL_CULL_FACE)
1217 glEnable(GL_DEPTH_TEST)
1219 if self._selectedObj is not None:
1221 pos = self.getObjectCenterPos()
1222 glTranslate(pos[0], pos[1], pos[2])
1225 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1226 glDisable(GL_DEPTH_TEST)
1229 glTranslate(0,-4,-10)
1230 glColor4ub(60,60,60,255)
1231 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1234 def _renderObject(self, obj, brightness = False, addSink = True):
1237 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1239 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1241 if self.tempMatrix is not None and obj == self._selectedObj:
1242 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1243 glMultMatrixf(tempMatrix)
1245 offset = obj.getDrawOffset()
1246 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1248 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1249 glMultMatrixf(tempMatrix)
1252 for m in obj._meshList:
1254 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1256 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1261 def _drawMachine(self):
1262 glEnable(GL_CULL_FACE)
1265 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1267 machine = profile.getMachineSetting('machine_type')
1268 if machine.startswith('ultimaker'):
1269 if machine not in self._platformMesh:
1270 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1272 self._platformMesh[machine] = meshes[0]
1274 self._platformMesh[machine] = None
1275 if machine == 'ultimaker2':
1276 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1278 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1279 glColor4f(1,1,1,0.5)
1280 self._objectShader.bind()
1281 self._renderObject(self._platformMesh[machine], False, False)
1282 self._objectShader.unbind()
1284 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1285 if machine == 'ultimaker2':
1286 if not hasattr(self._platformMesh[machine], 'texture'):
1287 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1288 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1289 glEnable(GL_TEXTURE_2D)
1293 glTranslate(0,150,-5)
1298 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1301 glVertex3f( w, 0, h)
1303 glVertex3f(-w, 0, h)
1305 glVertex3f(-w, 0, 0)
1307 glVertex3f( w, 0, 0)
1310 glVertex3f(-w, d, h)
1312 glVertex3f( w, d, h)
1314 glVertex3f( w, d, 0)
1316 glVertex3f(-w, d, 0)
1318 glDisable(GL_TEXTURE_2D)
1319 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1325 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1326 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1327 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1328 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1329 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1330 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1335 polys = profile.getMachineSizePolygons()
1336 height = profile.getMachineSettingFloat('machine_height')
1338 for n in xrange(0, len(polys[0])):
1340 glColor4ub(5, 171, 231, 96)
1342 glColor4ub(5, 171, 231, 64)
1343 glVertex3f(polys[0][n][0], polys[0][n][1], height)
1344 glVertex3f(polys[0][n][0], polys[0][n][1], 0)
1345 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0)
1346 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height)
1348 glColor4ub(5, 171, 231, 128)
1349 glBegin(GL_TRIANGLE_FAN)
1350 for p in polys[0][::-1]:
1351 glVertex3f(p[0], p[1], height)
1355 if self._platformTexture is None:
1356 self._platformTexture = opengl.loadGLTexture('checkerboard.png')
1357 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1358 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
1359 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
1360 glColor4f(1,1,1,0.5)
1361 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1362 glEnable(GL_TEXTURE_2D)
1363 glBegin(GL_TRIANGLE_FAN)
1365 glTexCoord2f(p[0]/20, p[1]/20)
1366 glVertex3f(p[0], p[1], 0)
1368 glDisable(GL_TEXTURE_2D)
1369 glColor4ub(127, 127, 127, 200)
1370 for poly in polys[1:]:
1371 glBegin(GL_TRIANGLE_FAN)
1373 glTexCoord2f(p[0]/20, p[1]/20)
1374 glVertex3f(p[0], p[1], 0)
1379 glDisable(GL_CULL_FACE)
1381 def _generateGCodeVBOs(self, layer):
1383 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1384 if ':' in extrudeType:
1385 extruder = int(extrudeType[extrudeType.find(':')+1:])
1386 extrudeType = extrudeType[0:extrudeType.find(':')]
1389 pointList = numpy.zeros((0,3), numpy.float32)
1391 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1393 a = numpy.concatenate((a[:-1], a[1:]), 1)
1394 a = a.reshape((len(a) * 2, 3))
1395 pointList = numpy.concatenate((pointList, a))
1396 ret.append(opengl.GLVBO(pointList))
1399 def _generateGCodeVBOs2(self, layer):
1400 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1401 filamentArea = math.pi * filamentRadius * filamentRadius
1402 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1405 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1406 if ':' in extrudeType:
1407 extruder = int(extrudeType[extrudeType.find(':')+1:])
1408 extrudeType = extrudeType[0:extrudeType.find(':')]
1411 pointList = numpy.zeros((0,3), numpy.float32)
1413 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1415 if extrudeType == 'FILL':
1418 normal = a[1:] - a[:-1]
1419 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1420 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1423 ePerDist = path['extrusion'][1:] / lens
1425 lineWidth = ePerDist / path['layerThickness'] / 2.0
1427 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1429 normal[:,0] *= lineWidth
1430 normal[:,1] *= lineWidth
1432 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1433 b = numpy.concatenate((b, a[1:] + normal), 1)
1434 b = numpy.concatenate((b, a[1:] - normal), 1)
1435 b = numpy.concatenate((b, a[:-1] - normal), 1)
1436 b = numpy.concatenate((b, a[:-1] + normal), 1)
1437 b = b.reshape((len(b) * 4, 3))
1440 normal2 = normal[:-1] + normal[1:]
1441 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1442 normal2[:,0] /= lens2
1443 normal2[:,1] /= lens2
1444 normal2[:,0] *= lineWidth[:-1]
1445 normal2[:,1] *= lineWidth[:-1]
1447 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1448 c = numpy.concatenate((c, a[1:-1]), 1)
1449 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1450 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1451 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1453 c = numpy.concatenate((c, a[1:-1]), 1)
1454 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1455 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1456 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1458 c = c.reshape((len(c) * 8, 3))
1460 pointList = numpy.concatenate((pointList, b, c))
1462 pointList = numpy.concatenate((pointList, b))
1463 ret.append(opengl.GLVBO(pointList))
1465 pointList = numpy.zeros((0,3), numpy.float32)
1467 if path['type'] == 'move':
1468 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1469 a = numpy.concatenate((a[:-1], a[1:]), 1)
1470 a = a.reshape((len(a) * 2, 3))
1471 pointList = numpy.concatenate((pointList, a))
1472 if path['type'] == 'retract':
1473 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1474 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1475 a = a.reshape((len(a) * 2, 3))
1476 pointList = numpy.concatenate((pointList, a))
1477 ret.append(opengl.GLVBO(pointList))
1481 def getObjectCenterPos(self):
1482 if self._selectedObj is None:
1483 return [0.0, 0.0, 0.0]
1484 pos = self._selectedObj.getPosition()
1485 size = self._selectedObj.getSize()
1486 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1488 def getObjectBoundaryCircle(self):
1489 if self._selectedObj is None:
1491 return self._selectedObj.getBoundaryCircle()
1493 def getObjectSize(self):
1494 if self._selectedObj is None:
1495 return [0.0, 0.0, 0.0]
1496 return self._selectedObj.getSize()
1498 def getObjectMatrix(self):
1499 if self._selectedObj is None:
1500 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1501 return self._selectedObj.getMatrix()
1503 class shaderEditor(wx.Dialog):
1504 def __init__(self, parent, callback, v, f):
1505 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1506 self._callback = callback
1507 s = wx.BoxSizer(wx.VERTICAL)
1509 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1510 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1511 s.Add(self._vertex, 1, flag=wx.EXPAND)
1512 s.Add(self._fragment, 1, flag=wx.EXPAND)
1514 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1515 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1517 self.SetPosition(self.GetParent().GetPosition())
1518 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1521 def OnText(self, e):
1522 self._callback(self._vertex.GetValue(), self._fragment.GetValue())