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.updateModelSettingsToControls()
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()
637 self.updateModelSettingsToControls()
639 def updateModelSettingsToControls(self):
640 if self._selectedObj is not None:
641 scale = self._selectedObj.getScale()
642 size = self._selectedObj.getSize()
643 self.scaleXctrl.setValue(round(scale[0], 2))
644 self.scaleYctrl.setValue(round(scale[1], 2))
645 self.scaleZctrl.setValue(round(scale[2], 2))
646 self.scaleXmmctrl.setValue(round(size[0], 2))
647 self.scaleYmmctrl.setValue(round(size[1], 2))
648 self.scaleZmmctrl.setValue(round(size[2], 2))
650 def OnKeyChar(self, keyCode):
651 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
652 if self._selectedObj is not None:
653 self._deleteObject(self._selectedObj)
655 if keyCode == wx.WXK_UP:
656 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
658 elif keyCode == wx.WXK_DOWN:
659 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
661 elif keyCode == wx.WXK_PAGEUP:
662 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
664 elif keyCode == wx.WXK_PAGEDOWN:
665 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
668 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
669 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
670 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
671 from collections import defaultdict
672 from gc import get_objects
673 self._beforeLeakTest = defaultdict(int)
674 for i in get_objects():
675 self._beforeLeakTest[type(i)] += 1
676 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
677 from collections import defaultdict
678 from gc import get_objects
679 self._afterLeakTest = defaultdict(int)
680 for i in get_objects():
681 self._afterLeakTest[type(i)] += 1
682 for k in self._afterLeakTest:
683 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
684 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
686 def ShaderUpdate(self, v, f):
687 s = opengl.GLShader(v, f)
689 self._objectLoadShader.release()
690 self._objectLoadShader = s
691 for obj in self._scene.objects():
692 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
695 def OnMouseDown(self,e):
696 self._mouseX = e.GetX()
697 self._mouseY = e.GetY()
698 self._mouseClick3DPos = self._mouse3Dpos
699 self._mouseClickFocus = self._focusObj
701 self._mouseState = 'doubleClick'
703 self._mouseState = 'dragOrClick'
704 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
705 p0 -= self.getObjectCenterPos() - self._viewTarget
706 p1 -= self.getObjectCenterPos() - self._viewTarget
707 if self.tool.OnDragStart(p0, p1):
708 self._mouseState = 'tool'
709 if self._mouseState == 'dragOrClick':
710 if e.GetButton() == 1:
711 if self._focusObj is not None:
712 self._selectObject(self._focusObj, False)
715 def OnMouseUp(self, e):
716 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
718 if self._mouseState == 'dragOrClick':
719 if e.GetButton() == 1:
720 self._selectObject(self._focusObj)
721 if e.GetButton() == 3:
723 if self._focusObj is not None:
724 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
725 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
726 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
727 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
728 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:
729 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
730 if len(self._scene.objects()) > 0:
731 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
732 if menu.MenuItemCount > 0:
735 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
736 self._scene.pushFree()
738 elif self._mouseState == 'tool':
739 if self.tempMatrix is not None and self._selectedObj is not None:
740 self._selectedObj.applyMatrix(self.tempMatrix)
741 self._scene.pushFree()
742 self._selectObject(self._selectedObj)
743 self.tempMatrix = None
744 self.tool.OnDragEnd()
746 self._mouseState = None
748 def OnMouseMotion(self,e):
749 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
750 p0 -= self.getObjectCenterPos() - self._viewTarget
751 p1 -= self.getObjectCenterPos() - self._viewTarget
753 if e.Dragging() and self._mouseState is not None:
754 if self._mouseState == 'tool':
755 self.tool.OnDrag(p0, p1)
756 elif not e.LeftIsDown() and e.RightIsDown():
757 self._mouseState = 'drag'
758 if wx.GetKeyState(wx.WXK_SHIFT):
759 a = math.cos(math.radians(self._yaw)) / 3.0
760 b = math.sin(math.radians(self._yaw)) / 3.0
761 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
762 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
763 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
764 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
766 self._yaw += e.GetX() - self._mouseX
767 self._pitch -= e.GetY() - self._mouseY
768 if self._pitch > 170:
772 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
773 self._mouseState = 'drag'
774 self._zoom += e.GetY() - self._mouseY
777 if self._zoom > numpy.max(self._machineSize) * 3:
778 self._zoom = numpy.max(self._machineSize) * 3
779 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
780 self._mouseState = 'dragObject'
781 z = max(0, self._mouseClick3DPos[2])
782 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
783 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
788 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
789 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
790 diff = cursorZ1 - cursorZ0
791 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
792 if not e.Dragging() or self._mouseState != 'tool':
793 self.tool.OnMouseMove(p0, p1)
795 self._mouseX = e.GetX()
796 self._mouseY = e.GetY()
798 def OnMouseWheel(self, e):
799 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
800 delta = max(min(delta,4),-4)
801 self._zoom *= 1.0 - delta / 10.0
804 if self._zoom > numpy.max(self._machineSize) * 3:
805 self._zoom = numpy.max(self._machineSize) * 3
808 def OnMouseLeave(self, e):
812 def getMouseRay(self, x, y):
813 if self._viewport is None:
814 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
815 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
816 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
817 p0 -= self._viewTarget
818 p1 -= self._viewTarget
821 def _init3DView(self):
822 # set viewing projection
823 size = self.GetSize()
824 glViewport(0, 0, size.GetWidth(), size.GetHeight())
827 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
829 glDisable(GL_RESCALE_NORMAL)
830 glDisable(GL_LIGHTING)
832 glEnable(GL_DEPTH_TEST)
833 glDisable(GL_CULL_FACE)
835 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
837 glClearColor(0.8, 0.8, 0.8, 1.0)
841 glMatrixMode(GL_PROJECTION)
843 aspect = float(size.GetWidth()) / float(size.GetHeight())
844 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
846 glMatrixMode(GL_MODELVIEW)
848 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
851 connectionEntry = self._printerConnectionManager.getAvailableConnection()
852 if machineCom.machineIsConnected():
853 self.printButton._imageID = 6
854 self.printButton._tooltip = _("Print")
855 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
856 self.printButton._imageID = 2
857 self.printButton._tooltip = _("Toolpath to SD")
858 elif connectionEntry is not None:
859 self.printButton._imageID = connectionEntry.icon
860 self.printButton._tooltip = _("Print with %s") % (connectionEntry.name)
862 self.printButton._imageID = 3
863 self.printButton._tooltip = _("Save toolpath")
865 if self._animView is not None:
866 self._viewTarget = self._animView.getPosition()
867 if self._animView.isDone():
868 self._animView = None
869 if self._animZoom is not None:
870 self._zoom = self._animZoom.getPosition()
871 if self._animZoom.isDone():
872 self._animZoom = None
873 if self.viewMode == 'gcode' and self._gcode is not None:
875 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
878 if self._objectShader is None:
879 if opengl.hasShaderSupport():
880 self._objectShader = opengl.GLShader("""
881 varying float light_amount;
885 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
886 gl_FrontColor = gl_Color;
888 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
892 varying float light_amount;
896 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
899 self._objectOverhangShader = opengl.GLShader("""
900 uniform float cosAngle;
901 uniform mat3 rotMatrix;
902 varying float light_amount;
906 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
907 gl_FrontColor = gl_Color;
909 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
911 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
913 light_amount = -10.0;
917 varying float light_amount;
921 if (light_amount == -10.0)
923 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
925 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
929 self._objectLoadShader = opengl.GLShader("""
930 uniform float intensity;
932 varying float light_amount;
936 vec4 tmp = gl_Vertex;
937 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
938 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
939 gl_Position = gl_ModelViewProjectionMatrix * tmp;
940 gl_FrontColor = gl_Color;
942 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
946 uniform float intensity;
947 varying float light_amount;
951 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
954 if self._objectShader is None or not self._objectShader.isValid():
955 self._objectShader = opengl.GLFakeShader()
956 self._objectOverhangShader = opengl.GLFakeShader()
957 self._objectLoadShader = None
959 glTranslate(0,0,-self._zoom)
960 glRotate(-self._pitch, 1,0,0)
961 glRotate(self._yaw, 0,0,1)
962 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
964 self._viewport = glGetIntegerv(GL_VIEWPORT)
965 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
966 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
968 glClearColor(1,1,1,1)
969 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
971 if self.viewMode != 'gcode':
972 for n in xrange(0, len(self._scene.objects())):
973 obj = self._scene.objects()[n]
974 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
975 self._renderObject(obj)
977 if self._mouseX > -1:
979 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
980 if n < len(self._scene.objects()):
981 self._focusObj = self._scene.objects()[n]
983 self._focusObj = None
984 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
985 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
986 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
987 self._mouse3Dpos -= self._viewTarget
990 glTranslate(0,0,-self._zoom)
991 glRotate(-self._pitch, 1,0,0)
992 glRotate(self._yaw, 0,0,1)
993 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
995 if self.viewMode == 'gcode':
996 if self._gcode is not None and self._gcode.layerList is None:
997 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
998 self._gcodeLoadThread.daemon = True
999 self._gcodeLoadThread.start()
1000 if self._gcode is not None and self._gcode.layerList is not None:
1002 if profile.getMachineSetting('machine_center_is_zero') != 'True':
1003 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
1005 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
1006 for n in xrange(0, drawUpTill):
1007 c = 1.0 - float(drawUpTill - n) / 15
1009 if len(self._gcodeVBOs) < n + 1:
1010 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
1011 if time.time() - t > 0.5:
1014 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
1015 if n == drawUpTill - 1:
1016 if len(self._gcodeVBOs[n]) < 9:
1017 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
1019 self._gcodeVBOs[n][8].render(GL_QUADS)
1020 glColor3f(c/2, 0, c)
1021 self._gcodeVBOs[n][9].render(GL_QUADS)
1022 glColor3f(0, c, c/2)
1023 self._gcodeVBOs[n][10].render(GL_QUADS)
1025 self._gcodeVBOs[n][11].render(GL_QUADS)
1028 self._gcodeVBOs[n][12].render(GL_QUADS)
1029 glColor3f(c/2, c/2, 0.0)
1030 self._gcodeVBOs[n][13].render(GL_QUADS)
1032 self._gcodeVBOs[n][14].render(GL_QUADS)
1033 self._gcodeVBOs[n][15].render(GL_QUADS)
1035 self._gcodeVBOs[n][16].render(GL_LINES)
1038 self._gcodeVBOs[n][0].render(GL_LINES)
1039 glColor3f(c/2, 0, c)
1040 self._gcodeVBOs[n][1].render(GL_LINES)
1041 glColor3f(0, c, c/2)
1042 self._gcodeVBOs[n][2].render(GL_LINES)
1044 self._gcodeVBOs[n][3].render(GL_LINES)
1047 self._gcodeVBOs[n][4].render(GL_LINES)
1048 glColor3f(c/2, c/2, 0.0)
1049 self._gcodeVBOs[n][5].render(GL_LINES)
1051 self._gcodeVBOs[n][6].render(GL_LINES)
1052 self._gcodeVBOs[n][7].render(GL_LINES)
1055 glStencilFunc(GL_ALWAYS, 1, 1)
1056 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1058 if self.viewMode == 'overhang':
1059 self._objectOverhangShader.bind()
1060 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1062 self._objectShader.bind()
1063 for obj in self._scene.objects():
1064 if obj._loadAnim is not None:
1065 if obj._loadAnim.isDone():
1066 obj._loadAnim = None
1070 if self._focusObj == obj:
1072 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1075 if self._selectedObj == obj or self._selectedObj is None:
1076 #If we want transparent, then first render a solid black model to remove the printer size lines.
1077 if self.viewMode == 'transparent':
1078 glColor4f(0, 0, 0, 0)
1079 self._renderObject(obj)
1081 glBlendFunc(GL_ONE, GL_ONE)
1082 glDisable(GL_DEPTH_TEST)
1084 if self.viewMode == 'xray':
1085 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1086 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1087 glEnable(GL_STENCIL_TEST)
1089 if self.viewMode == 'overhang':
1090 if self._selectedObj == obj and self.tempMatrix is not None:
1091 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1093 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1095 if not self._scene.checkPlatform(obj):
1096 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1097 self._renderObject(obj)
1099 self._renderObject(obj, brightness)
1100 glDisable(GL_STENCIL_TEST)
1102 glEnable(GL_DEPTH_TEST)
1103 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1105 if self.viewMode == 'xray':
1108 glEnable(GL_STENCIL_TEST)
1109 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1110 glDisable(GL_DEPTH_TEST)
1111 for i in xrange(2, 15, 2):
1112 glStencilFunc(GL_EQUAL, i, 0xFF)
1113 glColor(float(i)/10, float(i)/10, float(i)/5)
1115 glVertex3f(-1000,-1000,-10)
1116 glVertex3f( 1000,-1000,-10)
1117 glVertex3f( 1000, 1000,-10)
1118 glVertex3f(-1000, 1000,-10)
1120 for i in xrange(1, 15, 2):
1121 glStencilFunc(GL_EQUAL, i, 0xFF)
1122 glColor(float(i)/10, 0, 0)
1124 glVertex3f(-1000,-1000,-10)
1125 glVertex3f( 1000,-1000,-10)
1126 glVertex3f( 1000, 1000,-10)
1127 glVertex3f(-1000, 1000,-10)
1130 glDisable(GL_STENCIL_TEST)
1131 glEnable(GL_DEPTH_TEST)
1133 self._objectShader.unbind()
1135 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1137 if self._objectLoadShader is not None:
1138 self._objectLoadShader.bind()
1139 glColor4f(0.2, 0.6, 1.0, 1.0)
1140 for obj in self._scene.objects():
1141 if obj._loadAnim is None:
1143 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1144 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1145 self._renderObject(obj)
1146 self._objectLoadShader.unbind()
1151 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1153 z = self._usbPrintMonitor.getZ()
1154 size = self._machineSize
1155 glColor4ub(255,255,0,128)
1157 glVertex3f(-size[0]/2,-size[1]/2, z)
1158 glVertex3f( size[0]/2,-size[1]/2, z)
1159 glVertex3f( size[0]/2, size[1]/2, z)
1160 glVertex3f(-size[0]/2, size[1]/2, z)
1163 if self.viewMode == 'gcode':
1164 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1165 glDisable(GL_DEPTH_TEST)
1168 glTranslate(0,-4,-10)
1169 glColor4ub(60,60,60,255)
1170 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1173 #Draw the object box-shadow, so you can see where it will collide with other objects.
1174 if self._selectedObj is not None:
1176 glEnable(GL_CULL_FACE)
1177 glColor4f(0,0,0,0.16)
1179 for obj in self._scene.objects():
1181 glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1182 glBegin(GL_TRIANGLE_FAN)
1183 for p in obj._boundaryHull[::-1]:
1184 glVertex3f(p[0], p[1], 0)
1188 glColor4f(0,0,0,0.06)
1189 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1190 glBegin(GL_TRIANGLE_FAN)
1191 for p in self._selectedObj._printAreaHull[::-1]:
1192 glVertex3f(p[0], p[1], 0)
1194 glBegin(GL_TRIANGLE_FAN)
1195 for p in self._selectedObj._headAreaMinHull[::-1]:
1196 glVertex3f(p[0], p[1], 0)
1199 glDisable(GL_CULL_FACE)
1202 #Draw the outline of the selected object, on top of everything else except the GUI.
1203 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1204 glDisable(GL_DEPTH_TEST)
1205 glEnable(GL_CULL_FACE)
1206 glEnable(GL_STENCIL_TEST)
1208 glStencilFunc(GL_EQUAL, 0, 255)
1210 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1212 glColor4f(1,1,1,0.5)
1213 self._renderObject(self._selectedObj)
1214 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1216 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1217 glDisable(GL_STENCIL_TEST)
1218 glDisable(GL_CULL_FACE)
1219 glEnable(GL_DEPTH_TEST)
1221 if self._selectedObj is not None:
1223 pos = self.getObjectCenterPos()
1224 glTranslate(pos[0], pos[1], pos[2])
1227 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1228 glDisable(GL_DEPTH_TEST)
1231 glTranslate(0,-4,-10)
1232 glColor4ub(60,60,60,255)
1233 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1236 def _renderObject(self, obj, brightness = False, addSink = True):
1239 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1241 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1243 if self.tempMatrix is not None and obj == self._selectedObj:
1244 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1245 glMultMatrixf(tempMatrix)
1247 offset = obj.getDrawOffset()
1248 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1250 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1251 glMultMatrixf(tempMatrix)
1254 for m in obj._meshList:
1256 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1258 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1263 def _drawMachine(self):
1264 glEnable(GL_CULL_FACE)
1267 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1269 machine = profile.getMachineSetting('machine_type')
1270 if machine.startswith('ultimaker'):
1271 if machine not in self._platformMesh:
1272 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1274 self._platformMesh[machine] = meshes[0]
1276 self._platformMesh[machine] = None
1277 if machine == 'ultimaker2':
1278 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1280 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1281 glColor4f(1,1,1,0.5)
1282 self._objectShader.bind()
1283 self._renderObject(self._platformMesh[machine], False, False)
1284 self._objectShader.unbind()
1286 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1287 if machine == 'ultimaker2':
1288 if not hasattr(self._platformMesh[machine], 'texture'):
1289 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1290 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1291 glEnable(GL_TEXTURE_2D)
1295 glTranslate(0,150,-5)
1300 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1303 glVertex3f( w, 0, h)
1305 glVertex3f(-w, 0, h)
1307 glVertex3f(-w, 0, 0)
1309 glVertex3f( w, 0, 0)
1312 glVertex3f(-w, d, h)
1314 glVertex3f( w, d, h)
1316 glVertex3f( w, d, 0)
1318 glVertex3f(-w, d, 0)
1320 glDisable(GL_TEXTURE_2D)
1321 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1327 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1328 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1329 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1330 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1331 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1332 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1337 polys = profile.getMachineSizePolygons()
1338 height = profile.getMachineSettingFloat('machine_height')
1340 for n in xrange(0, len(polys[0])):
1342 glColor4ub(5, 171, 231, 96)
1344 glColor4ub(5, 171, 231, 64)
1345 glVertex3f(polys[0][n][0], polys[0][n][1], height)
1346 glVertex3f(polys[0][n][0], polys[0][n][1], 0)
1347 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0)
1348 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height)
1350 glColor4ub(5, 171, 231, 128)
1351 glBegin(GL_TRIANGLE_FAN)
1352 for p in polys[0][::-1]:
1353 glVertex3f(p[0], p[1], height)
1357 if self._platformTexture is None:
1358 self._platformTexture = opengl.loadGLTexture('checkerboard.png')
1359 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1360 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
1361 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
1362 glColor4f(1,1,1,0.5)
1363 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1364 glEnable(GL_TEXTURE_2D)
1365 glBegin(GL_TRIANGLE_FAN)
1367 glTexCoord2f(p[0]/20, p[1]/20)
1368 glVertex3f(p[0], p[1], 0)
1370 glDisable(GL_TEXTURE_2D)
1371 glColor4ub(127, 127, 127, 200)
1372 for poly in polys[1:]:
1373 glBegin(GL_TRIANGLE_FAN)
1375 glTexCoord2f(p[0]/20, p[1]/20)
1376 glVertex3f(p[0], p[1], 0)
1381 glDisable(GL_CULL_FACE)
1383 def _generateGCodeVBOs(self, layer):
1385 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1386 if ':' in extrudeType:
1387 extruder = int(extrudeType[extrudeType.find(':')+1:])
1388 extrudeType = extrudeType[0:extrudeType.find(':')]
1391 pointList = numpy.zeros((0,3), numpy.float32)
1393 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1395 a = numpy.concatenate((a[:-1], a[1:]), 1)
1396 a = a.reshape((len(a) * 2, 3))
1397 pointList = numpy.concatenate((pointList, a))
1398 ret.append(opengl.GLVBO(pointList))
1401 def _generateGCodeVBOs2(self, layer):
1402 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1403 filamentArea = math.pi * filamentRadius * filamentRadius
1404 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1407 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1408 if ':' in extrudeType:
1409 extruder = int(extrudeType[extrudeType.find(':')+1:])
1410 extrudeType = extrudeType[0:extrudeType.find(':')]
1413 pointList = numpy.zeros((0,3), numpy.float32)
1415 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1417 if extrudeType == 'FILL':
1420 normal = a[1:] - a[:-1]
1421 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1422 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1425 ePerDist = path['extrusion'][1:] / lens
1427 lineWidth = ePerDist / path['layerThickness'] / 2.0
1429 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1431 normal[:,0] *= lineWidth
1432 normal[:,1] *= lineWidth
1434 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1435 b = numpy.concatenate((b, a[1:] + normal), 1)
1436 b = numpy.concatenate((b, a[1:] - normal), 1)
1437 b = numpy.concatenate((b, a[:-1] - normal), 1)
1438 b = numpy.concatenate((b, a[:-1] + normal), 1)
1439 b = b.reshape((len(b) * 4, 3))
1442 normal2 = normal[:-1] + normal[1:]
1443 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1444 normal2[:,0] /= lens2
1445 normal2[:,1] /= lens2
1446 normal2[:,0] *= lineWidth[:-1]
1447 normal2[:,1] *= lineWidth[:-1]
1449 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1450 c = numpy.concatenate((c, a[1:-1]), 1)
1451 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1452 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1453 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1455 c = numpy.concatenate((c, a[1:-1]), 1)
1456 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1457 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1458 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1460 c = c.reshape((len(c) * 8, 3))
1462 pointList = numpy.concatenate((pointList, b, c))
1464 pointList = numpy.concatenate((pointList, b))
1465 ret.append(opengl.GLVBO(pointList))
1467 pointList = numpy.zeros((0,3), numpy.float32)
1469 if path['type'] == 'move':
1470 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1471 a = numpy.concatenate((a[:-1], a[1:]), 1)
1472 a = a.reshape((len(a) * 2, 3))
1473 pointList = numpy.concatenate((pointList, a))
1474 if path['type'] == 'retract':
1475 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1476 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1477 a = a.reshape((len(a) * 2, 3))
1478 pointList = numpy.concatenate((pointList, a))
1479 ret.append(opengl.GLVBO(pointList))
1483 def getObjectCenterPos(self):
1484 if self._selectedObj is None:
1485 return [0.0, 0.0, 0.0]
1486 pos = self._selectedObj.getPosition()
1487 size = self._selectedObj.getSize()
1488 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1490 def getObjectBoundaryCircle(self):
1491 if self._selectedObj is None:
1493 return self._selectedObj.getBoundaryCircle()
1495 def getObjectSize(self):
1496 if self._selectedObj is None:
1497 return [0.0, 0.0, 0.0]
1498 return self._selectedObj.getSize()
1500 def getObjectMatrix(self):
1501 if self._selectedObj is None:
1502 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1503 return self._selectedObj.getMatrix()
1505 class shaderEditor(wx.Dialog):
1506 def __init__(self, parent, callback, v, f):
1507 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1508 self._callback = callback
1509 s = wx.BoxSizer(wx.VERTICAL)
1511 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1512 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1513 s.Add(self._vertex, 1, flag=wx.EXPAND)
1514 s.Add(self._fragment, 1, flag=wx.EXPAND)
1516 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1517 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1519 self.SetPosition(self.GetParent().GetPosition())
1520 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1523 def OnText(self, e):
1524 self._callback(self._vertex.GetValue(), self._fragment.GetValue())