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._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
631 self._objColors[0] = profile.getPreferenceColour('model_colour')
632 self._objColors[1] = profile.getPreferenceColour('model_colour2')
633 self._objColors[2] = profile.getPreferenceColour('model_colour3')
634 self._objColors[3] = profile.getPreferenceColour('model_colour4')
635 self._scene.updateMachineDimensions()
637 if self._selectedObj is not None:
638 scale = self._selectedObj.getScale()
639 size = self._selectedObj.getSize()
640 self.scaleXctrl.setValue(round(scale[0], 2))
641 self.scaleYctrl.setValue(round(scale[1], 2))
642 self.scaleZctrl.setValue(round(scale[2], 2))
643 self.scaleXmmctrl.setValue(round(size[0], 2))
644 self.scaleYmmctrl.setValue(round(size[1], 2))
645 self.scaleZmmctrl.setValue(round(size[2], 2))
647 def OnKeyChar(self, keyCode):
648 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
649 if self._selectedObj is not None:
650 self._deleteObject(self._selectedObj)
652 if keyCode == wx.WXK_UP:
653 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
655 elif keyCode == wx.WXK_DOWN:
656 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
658 elif keyCode == wx.WXK_PAGEUP:
659 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
661 elif keyCode == wx.WXK_PAGEDOWN:
662 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
665 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
666 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
667 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
668 from collections import defaultdict
669 from gc import get_objects
670 self._beforeLeakTest = defaultdict(int)
671 for i in get_objects():
672 self._beforeLeakTest[type(i)] += 1
673 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
674 from collections import defaultdict
675 from gc import get_objects
676 self._afterLeakTest = defaultdict(int)
677 for i in get_objects():
678 self._afterLeakTest[type(i)] += 1
679 for k in self._afterLeakTest:
680 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
681 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
683 def ShaderUpdate(self, v, f):
684 s = opengl.GLShader(v, f)
686 self._objectLoadShader.release()
687 self._objectLoadShader = s
688 for obj in self._scene.objects():
689 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
692 def OnMouseDown(self,e):
693 self._mouseX = e.GetX()
694 self._mouseY = e.GetY()
695 self._mouseClick3DPos = self._mouse3Dpos
696 self._mouseClickFocus = self._focusObj
698 self._mouseState = 'doubleClick'
700 self._mouseState = 'dragOrClick'
701 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
702 p0 -= self.getObjectCenterPos() - self._viewTarget
703 p1 -= self.getObjectCenterPos() - self._viewTarget
704 if self.tool.OnDragStart(p0, p1):
705 self._mouseState = 'tool'
706 if self._mouseState == 'dragOrClick':
707 if e.GetButton() == 1:
708 if self._focusObj is not None:
709 self._selectObject(self._focusObj, False)
712 def OnMouseUp(self, e):
713 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
715 if self._mouseState == 'dragOrClick':
716 if e.GetButton() == 1:
717 self._selectObject(self._focusObj)
718 if e.GetButton() == 3:
720 if self._focusObj is not None:
721 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
722 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
723 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
724 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
725 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:
726 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
727 if len(self._scene.objects()) > 0:
728 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
729 if menu.MenuItemCount > 0:
732 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
733 self._scene.pushFree()
735 elif self._mouseState == 'tool':
736 if self.tempMatrix is not None and self._selectedObj is not None:
737 self._selectedObj.applyMatrix(self.tempMatrix)
738 self._scene.pushFree()
739 self._selectObject(self._selectedObj)
740 self.tempMatrix = None
741 self.tool.OnDragEnd()
743 self._mouseState = None
745 def OnMouseMotion(self,e):
746 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
747 p0 -= self.getObjectCenterPos() - self._viewTarget
748 p1 -= self.getObjectCenterPos() - self._viewTarget
750 if e.Dragging() and self._mouseState is not None:
751 if self._mouseState == 'tool':
752 self.tool.OnDrag(p0, p1)
753 elif not e.LeftIsDown() and e.RightIsDown():
754 self._mouseState = 'drag'
755 if wx.GetKeyState(wx.WXK_SHIFT):
756 a = math.cos(math.radians(self._yaw)) / 3.0
757 b = math.sin(math.radians(self._yaw)) / 3.0
758 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
759 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
760 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
761 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
763 self._yaw += e.GetX() - self._mouseX
764 self._pitch -= e.GetY() - self._mouseY
765 if self._pitch > 170:
769 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
770 self._mouseState = 'drag'
771 self._zoom += e.GetY() - self._mouseY
774 if self._zoom > numpy.max(self._machineSize) * 3:
775 self._zoom = numpy.max(self._machineSize) * 3
776 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
777 self._mouseState = 'dragObject'
778 z = max(0, self._mouseClick3DPos[2])
779 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
780 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
785 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
786 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
787 diff = cursorZ1 - cursorZ0
788 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
789 if not e.Dragging() or self._mouseState != 'tool':
790 self.tool.OnMouseMove(p0, p1)
792 self._mouseX = e.GetX()
793 self._mouseY = e.GetY()
795 def OnMouseWheel(self, e):
796 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
797 delta = max(min(delta,4),-4)
798 self._zoom *= 1.0 - delta / 10.0
801 if self._zoom > numpy.max(self._machineSize) * 3:
802 self._zoom = numpy.max(self._machineSize) * 3
805 def OnMouseLeave(self, e):
809 def getMouseRay(self, x, y):
810 if self._viewport is None:
811 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
812 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
813 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
814 p0 -= self._viewTarget
815 p1 -= self._viewTarget
818 def _init3DView(self):
819 # set viewing projection
820 size = self.GetSize()
821 glViewport(0, 0, size.GetWidth(), size.GetHeight())
824 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
826 glDisable(GL_RESCALE_NORMAL)
827 glDisable(GL_LIGHTING)
829 glEnable(GL_DEPTH_TEST)
830 glDisable(GL_CULL_FACE)
832 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
834 glClearColor(0.8, 0.8, 0.8, 1.0)
838 glMatrixMode(GL_PROJECTION)
840 aspect = float(size.GetWidth()) / float(size.GetHeight())
841 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
843 glMatrixMode(GL_MODELVIEW)
845 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
848 connectionEntry = self._printerConnectionManager.getAvailableConnection()
849 if machineCom.machineIsConnected():
850 self.printButton._imageID = 6
851 self.printButton._tooltip = _("Print")
852 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
853 self.printButton._imageID = 2
854 self.printButton._tooltip = _("Toolpath to SD")
855 elif connectionEntry is not None:
856 self.printButton._imageID = connectionEntry.icon
857 self.printButton._tooltip = _("Print with %s") % (connectionEntry.name)
859 self.printButton._imageID = 3
860 self.printButton._tooltip = _("Save toolpath")
862 if self._animView is not None:
863 self._viewTarget = self._animView.getPosition()
864 if self._animView.isDone():
865 self._animView = None
866 if self._animZoom is not None:
867 self._zoom = self._animZoom.getPosition()
868 if self._animZoom.isDone():
869 self._animZoom = None
870 if self.viewMode == 'gcode' and self._gcode is not None:
872 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
875 if self._objectShader is None:
876 if opengl.hasShaderSupport():
877 self._objectShader = opengl.GLShader("""
878 varying float light_amount;
882 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
883 gl_FrontColor = gl_Color;
885 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
889 varying float light_amount;
893 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
896 self._objectOverhangShader = opengl.GLShader("""
897 uniform float cosAngle;
898 uniform mat3 rotMatrix;
899 varying float light_amount;
903 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
904 gl_FrontColor = gl_Color;
906 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
908 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
910 light_amount = -10.0;
914 varying float light_amount;
918 if (light_amount == -10.0)
920 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
922 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
926 self._objectLoadShader = opengl.GLShader("""
927 uniform float intensity;
929 varying float light_amount;
933 vec4 tmp = gl_Vertex;
934 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
935 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
936 gl_Position = gl_ModelViewProjectionMatrix * tmp;
937 gl_FrontColor = gl_Color;
939 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
943 uniform float intensity;
944 varying float light_amount;
948 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
951 if self._objectShader is None or not self._objectShader.isValid():
952 self._objectShader = opengl.GLFakeShader()
953 self._objectOverhangShader = opengl.GLFakeShader()
954 self._objectLoadShader = None
956 glTranslate(0,0,-self._zoom)
957 glRotate(-self._pitch, 1,0,0)
958 glRotate(self._yaw, 0,0,1)
959 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
961 self._viewport = glGetIntegerv(GL_VIEWPORT)
962 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
963 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
965 glClearColor(1,1,1,1)
966 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
968 if self.viewMode != 'gcode':
969 for n in xrange(0, len(self._scene.objects())):
970 obj = self._scene.objects()[n]
971 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
972 self._renderObject(obj)
974 if self._mouseX > -1:
976 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
977 if n < len(self._scene.objects()):
978 self._focusObj = self._scene.objects()[n]
980 self._focusObj = None
981 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
982 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
983 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
984 self._mouse3Dpos -= self._viewTarget
987 glTranslate(0,0,-self._zoom)
988 glRotate(-self._pitch, 1,0,0)
989 glRotate(self._yaw, 0,0,1)
990 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
992 if self.viewMode == 'gcode':
993 if self._gcode is not None and self._gcode.layerList is None:
994 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
995 self._gcodeLoadThread.daemon = True
996 self._gcodeLoadThread.start()
997 if self._gcode is not None and self._gcode.layerList is not None:
999 if profile.getMachineSetting('machine_center_is_zero') != 'True':
1000 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
1002 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
1003 for n in xrange(0, drawUpTill):
1004 c = 1.0 - float(drawUpTill - n) / 15
1006 if len(self._gcodeVBOs) < n + 1:
1007 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
1008 if time.time() - t > 0.5:
1011 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
1012 if n == drawUpTill - 1:
1013 if len(self._gcodeVBOs[n]) < 9:
1014 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
1016 self._gcodeVBOs[n][8].render(GL_QUADS)
1017 glColor3f(c/2, 0, c)
1018 self._gcodeVBOs[n][9].render(GL_QUADS)
1019 glColor3f(0, c, c/2)
1020 self._gcodeVBOs[n][10].render(GL_QUADS)
1022 self._gcodeVBOs[n][11].render(GL_QUADS)
1025 self._gcodeVBOs[n][12].render(GL_QUADS)
1026 glColor3f(c/2, c/2, 0.0)
1027 self._gcodeVBOs[n][13].render(GL_QUADS)
1029 self._gcodeVBOs[n][14].render(GL_QUADS)
1030 self._gcodeVBOs[n][15].render(GL_QUADS)
1032 self._gcodeVBOs[n][16].render(GL_LINES)
1035 self._gcodeVBOs[n][0].render(GL_LINES)
1036 glColor3f(c/2, 0, c)
1037 self._gcodeVBOs[n][1].render(GL_LINES)
1038 glColor3f(0, c, c/2)
1039 self._gcodeVBOs[n][2].render(GL_LINES)
1041 self._gcodeVBOs[n][3].render(GL_LINES)
1044 self._gcodeVBOs[n][4].render(GL_LINES)
1045 glColor3f(c/2, c/2, 0.0)
1046 self._gcodeVBOs[n][5].render(GL_LINES)
1048 self._gcodeVBOs[n][6].render(GL_LINES)
1049 self._gcodeVBOs[n][7].render(GL_LINES)
1052 glStencilFunc(GL_ALWAYS, 1, 1)
1053 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1055 if self.viewMode == 'overhang':
1056 self._objectOverhangShader.bind()
1057 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1059 self._objectShader.bind()
1060 for obj in self._scene.objects():
1061 if obj._loadAnim is not None:
1062 if obj._loadAnim.isDone():
1063 obj._loadAnim = None
1067 if self._focusObj == obj:
1069 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1072 if self._selectedObj == obj or self._selectedObj is None:
1073 #If we want transparent, then first render a solid black model to remove the printer size lines.
1074 if self.viewMode == 'transparent':
1075 glColor4f(0, 0, 0, 0)
1076 self._renderObject(obj)
1078 glBlendFunc(GL_ONE, GL_ONE)
1079 glDisable(GL_DEPTH_TEST)
1081 if self.viewMode == 'xray':
1082 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1083 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1084 glEnable(GL_STENCIL_TEST)
1086 if self.viewMode == 'overhang':
1087 if self._selectedObj == obj and self.tempMatrix is not None:
1088 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1090 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1092 if not self._scene.checkPlatform(obj):
1093 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1094 self._renderObject(obj)
1096 self._renderObject(obj, brightness)
1097 glDisable(GL_STENCIL_TEST)
1099 glEnable(GL_DEPTH_TEST)
1100 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1102 if self.viewMode == 'xray':
1105 glEnable(GL_STENCIL_TEST)
1106 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1107 glDisable(GL_DEPTH_TEST)
1108 for i in xrange(2, 15, 2):
1109 glStencilFunc(GL_EQUAL, i, 0xFF)
1110 glColor(float(i)/10, float(i)/10, float(i)/5)
1112 glVertex3f(-1000,-1000,-10)
1113 glVertex3f( 1000,-1000,-10)
1114 glVertex3f( 1000, 1000,-10)
1115 glVertex3f(-1000, 1000,-10)
1117 for i in xrange(1, 15, 2):
1118 glStencilFunc(GL_EQUAL, i, 0xFF)
1119 glColor(float(i)/10, 0, 0)
1121 glVertex3f(-1000,-1000,-10)
1122 glVertex3f( 1000,-1000,-10)
1123 glVertex3f( 1000, 1000,-10)
1124 glVertex3f(-1000, 1000,-10)
1127 glDisable(GL_STENCIL_TEST)
1128 glEnable(GL_DEPTH_TEST)
1130 self._objectShader.unbind()
1132 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1134 if self._objectLoadShader is not None:
1135 self._objectLoadShader.bind()
1136 glColor4f(0.2, 0.6, 1.0, 1.0)
1137 for obj in self._scene.objects():
1138 if obj._loadAnim is None:
1140 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1141 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1142 self._renderObject(obj)
1143 self._objectLoadShader.unbind()
1148 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1150 z = self._usbPrintMonitor.getZ()
1151 size = self._machineSize
1152 glColor4ub(255,255,0,128)
1154 glVertex3f(-size[0]/2,-size[1]/2, z)
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)
1160 if self.viewMode == 'gcode':
1161 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1162 glDisable(GL_DEPTH_TEST)
1165 glTranslate(0,-4,-10)
1166 glColor4ub(60,60,60,255)
1167 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1170 #Draw the object box-shadow, so you can see where it will collide with other objects.
1171 if self._selectedObj is not None:
1173 glEnable(GL_CULL_FACE)
1174 glColor4f(0,0,0,0.16)
1176 for obj in self._scene.objects():
1178 glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1179 glBegin(GL_TRIANGLE_FAN)
1180 for p in obj._boundaryHull[::-1]:
1181 glVertex3f(p[0], p[1], 0)
1185 glColor4f(0,0,0,0.06)
1186 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1187 glBegin(GL_TRIANGLE_FAN)
1188 for p in self._selectedObj._printAreaHull[::-1]:
1189 glVertex3f(p[0], p[1], 0)
1191 glBegin(GL_TRIANGLE_FAN)
1192 for p in self._selectedObj._headAreaMinHull[::-1]:
1193 glVertex3f(p[0], p[1], 0)
1196 glDisable(GL_CULL_FACE)
1199 #Draw the outline of the selected object, on top of everything else except the GUI.
1200 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1201 glDisable(GL_DEPTH_TEST)
1202 glEnable(GL_CULL_FACE)
1203 glEnable(GL_STENCIL_TEST)
1205 glStencilFunc(GL_EQUAL, 0, 255)
1207 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1209 glColor4f(1,1,1,0.5)
1210 self._renderObject(self._selectedObj)
1211 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1213 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1214 glDisable(GL_STENCIL_TEST)
1215 glDisable(GL_CULL_FACE)
1216 glEnable(GL_DEPTH_TEST)
1218 if self._selectedObj is not None:
1220 pos = self.getObjectCenterPos()
1221 glTranslate(pos[0], pos[1], pos[2])
1224 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1225 glDisable(GL_DEPTH_TEST)
1228 glTranslate(0,-4,-10)
1229 glColor4ub(60,60,60,255)
1230 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1233 def _renderObject(self, obj, brightness = False, addSink = True):
1236 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1238 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1240 if self.tempMatrix is not None and obj == self._selectedObj:
1241 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1242 glMultMatrixf(tempMatrix)
1244 offset = obj.getDrawOffset()
1245 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1247 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1248 glMultMatrixf(tempMatrix)
1251 for m in obj._meshList:
1253 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1255 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1260 def _drawMachine(self):
1261 glEnable(GL_CULL_FACE)
1264 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1266 machine = profile.getMachineSetting('machine_type')
1267 if machine.startswith('ultimaker'):
1268 if machine not in self._platformMesh:
1269 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1271 self._platformMesh[machine] = meshes[0]
1273 self._platformMesh[machine] = None
1274 if machine == 'ultimaker2':
1275 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1277 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1278 glColor4f(1,1,1,0.5)
1279 self._objectShader.bind()
1280 self._renderObject(self._platformMesh[machine], False, False)
1281 self._objectShader.unbind()
1283 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1284 if machine == 'ultimaker2':
1285 if not hasattr(self._platformMesh[machine], 'texture'):
1286 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1287 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1288 glEnable(GL_TEXTURE_2D)
1292 glTranslate(0,150,-5)
1297 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1300 glVertex3f( w, 0, h)
1302 glVertex3f(-w, 0, h)
1304 glVertex3f(-w, 0, 0)
1306 glVertex3f( w, 0, 0)
1309 glVertex3f(-w, d, h)
1311 glVertex3f( w, d, h)
1313 glVertex3f( w, d, 0)
1315 glVertex3f(-w, d, 0)
1317 glDisable(GL_TEXTURE_2D)
1318 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1324 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1325 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1326 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1327 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1328 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1329 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1332 #Cornerpoints for big blue square
1333 # v0 = [ size[0] / 2, size[1] / 2, size[2]]
1334 # v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1335 # v2 = [-size[0] / 2, size[1] / 2, size[2]]
1336 # v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1337 # v4 = [ size[0] / 2, size[1] / 2, 0]
1338 # v5 = [ size[0] / 2,-size[1] / 2, 0]
1339 # v6 = [-size[0] / 2, size[1] / 2, 0]
1340 # v7 = [-size[0] / 2,-size[1] / 2, 0]
1342 # vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1343 # glEnableClientState(GL_VERTEX_ARRAY)
1344 # glVertexPointer(3, GL_FLOAT, 3*4, vList)
1347 # glColor4ub(5, 171, 231, 64)
1348 # glDrawArrays(GL_QUADS, 0, 4)
1349 # glColor4ub(5, 171, 231, 96)
1350 # glDrawArrays(GL_QUADS, 4, 8)
1351 # glColor4ub(5, 171, 231, 128)
1352 # glDrawArrays(GL_QUADS, 12, 8)
1353 # glDisableClientState(GL_VERTEX_ARRAY)
1355 polys = profile.getMachineSizePolygons()
1356 height = profile.getMachineSettingFloat('machine_height')
1358 for n in xrange(0, len(polys[0])):
1360 glColor4ub(5, 171, 231, 96)
1362 glColor4ub(5, 171, 231, 64)
1363 glVertex3f(polys[0][n][0], polys[0][n][1], height)
1364 glVertex3f(polys[0][n][0], polys[0][n][1], 0)
1365 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0)
1366 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height)
1368 glColor4ub(5, 171, 231, 128)
1369 glBegin(GL_TRIANGLE_FAN)
1370 for p in polys[0][::-1]:
1371 glVertex3f(p[0], p[1], height)
1375 if self._platformTexture is None:
1376 self._platformTexture = opengl.loadGLTexture('checkerboard.png')
1377 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1378 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
1379 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
1380 glColor4f(1,1,1,0.5)
1381 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1382 glEnable(GL_TEXTURE_2D)
1383 glBegin(GL_TRIANGLE_FAN)
1385 glTexCoord2f(p[0]/20, p[1]/20)
1386 glVertex3f(p[0], p[1], 0)
1388 glDisable(GL_TEXTURE_2D)
1389 glColor4ub(127, 127, 127, 200)
1390 for poly in polys[1:]:
1391 glBegin(GL_TRIANGLE_FAN)
1393 glTexCoord2f(p[0]/20, p[1]/20)
1394 glVertex3f(p[0], p[1], 0)
1397 if machine == 'ultimaker2x':
1399 glColor4ub(127, 127, 127, 200)
1400 #if UM2, draw bat-area zone for head. THe head can't stop there, because its bat-area.
1404 posX = size[0] / 2 - clipWidth
1405 posY = size[1] / 2 - clipHeight
1407 glVertex3f(posX, posY, 0)
1408 glVertex3f(posX+clipWidth, posY, 0)
1409 glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1410 glVertex3f(posX, posY+clipHeight, 0)
1416 posY = size[1] / 2 - clipHeight
1418 glVertex3f(posX, posY, 0)
1419 glVertex3f(posX+clipWidth, posY, 0)
1420 glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1421 glVertex3f(posX, posY+clipHeight, 0)
1426 posX = size[0] / 2 - clipWidth
1429 glVertex3f(posX, posY, 0)
1430 glVertex3f(posX+clipWidth, posY, 0)
1431 glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1432 glVertex3f(posX, posY+clipHeight, 0)
1440 glVertex3f(posX, posY, 0)
1441 glVertex3f(posX+clipWidth, posY, 0)
1442 glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1443 glVertex3f(posX, posY+clipHeight, 0)
1448 glDisable(GL_CULL_FACE)
1450 def _generateGCodeVBOs(self, layer):
1452 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1453 if ':' in extrudeType:
1454 extruder = int(extrudeType[extrudeType.find(':')+1:])
1455 extrudeType = extrudeType[0:extrudeType.find(':')]
1458 pointList = numpy.zeros((0,3), numpy.float32)
1460 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1462 a = numpy.concatenate((a[:-1], a[1:]), 1)
1463 a = a.reshape((len(a) * 2, 3))
1464 pointList = numpy.concatenate((pointList, a))
1465 ret.append(opengl.GLVBO(pointList))
1468 def _generateGCodeVBOs2(self, layer):
1469 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1470 filamentArea = math.pi * filamentRadius * filamentRadius
1471 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1474 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1475 if ':' in extrudeType:
1476 extruder = int(extrudeType[extrudeType.find(':')+1:])
1477 extrudeType = extrudeType[0:extrudeType.find(':')]
1480 pointList = numpy.zeros((0,3), numpy.float32)
1482 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1484 if extrudeType == 'FILL':
1487 normal = a[1:] - a[:-1]
1488 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1489 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1492 ePerDist = path['extrusion'][1:] / lens
1494 lineWidth = ePerDist / path['layerThickness'] / 2.0
1496 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1498 normal[:,0] *= lineWidth
1499 normal[:,1] *= lineWidth
1501 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1502 b = numpy.concatenate((b, a[1:] + normal), 1)
1503 b = numpy.concatenate((b, a[1:] - normal), 1)
1504 b = numpy.concatenate((b, a[:-1] - normal), 1)
1505 b = numpy.concatenate((b, a[:-1] + normal), 1)
1506 b = b.reshape((len(b) * 4, 3))
1509 normal2 = normal[:-1] + normal[1:]
1510 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1511 normal2[:,0] /= lens2
1512 normal2[:,1] /= lens2
1513 normal2[:,0] *= lineWidth[:-1]
1514 normal2[:,1] *= lineWidth[:-1]
1516 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1517 c = numpy.concatenate((c, a[1:-1]), 1)
1518 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1519 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1520 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1522 c = numpy.concatenate((c, a[1:-1]), 1)
1523 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1524 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1525 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1527 c = c.reshape((len(c) * 8, 3))
1529 pointList = numpy.concatenate((pointList, b, c))
1531 pointList = numpy.concatenate((pointList, b))
1532 ret.append(opengl.GLVBO(pointList))
1534 pointList = numpy.zeros((0,3), numpy.float32)
1536 if path['type'] == 'move':
1537 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1538 a = numpy.concatenate((a[:-1], a[1:]), 1)
1539 a = a.reshape((len(a) * 2, 3))
1540 pointList = numpy.concatenate((pointList, a))
1541 if path['type'] == 'retract':
1542 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1543 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1544 a = a.reshape((len(a) * 2, 3))
1545 pointList = numpy.concatenate((pointList, a))
1546 ret.append(opengl.GLVBO(pointList))
1550 def getObjectCenterPos(self):
1551 if self._selectedObj is None:
1552 return [0.0, 0.0, 0.0]
1553 pos = self._selectedObj.getPosition()
1554 size = self._selectedObj.getSize()
1555 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1557 def getObjectBoundaryCircle(self):
1558 if self._selectedObj is None:
1560 return self._selectedObj.getBoundaryCircle()
1562 def getObjectSize(self):
1563 if self._selectedObj is None:
1564 return [0.0, 0.0, 0.0]
1565 return self._selectedObj.getSize()
1567 def getObjectMatrix(self):
1568 if self._selectedObj is None:
1569 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1570 return self._selectedObj.getMatrix()
1572 class shaderEditor(wx.Dialog):
1573 def __init__(self, parent, callback, v, f):
1574 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1575 self._callback = callback
1576 s = wx.BoxSizer(wx.VERTICAL)
1578 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1579 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1580 s.Add(self._vertex, 1, flag=wx.EXPAND)
1581 s.Add(self._fragment, 1, flag=wx.EXPAND)
1583 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1584 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1586 self.SetPosition(self.GetParent().GetPosition())
1587 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1590 def OnText(self, e):
1591 self._callback(self._vertex.GetValue(), self._fragment.GetValue())