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 import explorer
29 from Cura.util.printerConnection import printerConnectionManager
30 from Cura.gui.util import previewTools
31 from Cura.gui.util import opengl
32 from Cura.gui.util import openglGui
33 from Cura.gui.tools import youmagineGui
34 from Cura.gui.tools import imageToMesh
36 class SceneView(openglGui.glGuiPanel):
37 def __init__(self, parent):
38 super(SceneView, self).__init__(parent)
43 self._scene = objectScene.Scene()
46 self._gcodeFilename = None
47 self._gcodeLoadThread = None
48 self._objectShader = None
49 self._objectLoadShader = None
51 self._selectedObj = None
52 self._objColors = [None,None,None,None]
55 self._mouseState = None
56 self._viewTarget = numpy.array([0,0,0], numpy.float32)
59 self._platformMesh = {}
60 self._platformTexture = None
61 self._isSimpleMode = True
62 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
63 self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager()
66 self._modelMatrix = None
67 self._projMatrix = None
68 self.tempMatrix = None
70 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
71 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
72 self.printButton.setDisabled(True)
75 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
76 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
77 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
79 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
80 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
82 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
83 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
85 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
86 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
87 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
89 self.rotateToolButton.setExpandArrow(True)
90 self.scaleToolButton.setExpandArrow(True)
91 self.mirrorToolButton.setExpandArrow(True)
93 self.scaleForm = openglGui.glFrame(self, (2, -2))
94 openglGui.glGuiLayoutGrid(self.scaleForm)
95 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
96 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
97 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
98 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
99 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
100 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
101 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
102 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
103 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
104 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
105 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
106 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
107 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
108 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
110 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
111 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
113 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
114 self.youMagineButton.setDisabled(True)
116 self.notification = openglGui.glNotification(self, (0, 0))
118 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
119 self._sceneUpdateTimer = wx.Timer(self)
120 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
121 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
122 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
126 self.updateToolButtons()
127 self.updateProfileToControls()
129 def loadGCodeFile(self, filename):
130 self.OnDeleteAll(None)
131 if self._gcode is not None:
133 for layerVBOlist in self._gcodeVBOs:
134 for vbo in layerVBOlist:
135 self.glReleaseList.append(vbo)
137 self._gcode = gcodeInterpreter.gcode()
138 self._gcodeFilename = filename
139 self.printButton.setBottomText('')
140 self.viewSelection.setValue(4)
141 self.printButton.setDisabled(False)
142 self.youMagineButton.setDisabled(True)
145 def loadSceneFiles(self, filenames):
146 self.youMagineButton.setDisabled(False)
147 #if self.viewSelection.getValue() == 4:
148 # self.viewSelection.setValue(0)
149 # self.OnViewChange()
150 self.loadScene(filenames)
152 def loadFiles(self, filenames):
153 mainWindow = self.GetParent().GetParent().GetParent()
154 # only one GCODE file can be active
155 # so if single gcode file, process this
156 # otherwise ignore all gcode files
158 if len(filenames) == 1:
159 filename = filenames[0]
160 ext = os.path.splitext(filename)[1].lower()
161 if ext == '.g' or ext == '.gcode':
162 gcodeFilename = filename
163 mainWindow.addToModelMRU(filename)
164 if gcodeFilename is not None:
165 self.loadGCodeFile(gcodeFilename)
167 # process directories and special file types
168 # and keep scene files for later processing
170 ignored_types = dict()
171 # use file list as queue
172 # pop first entry for processing and append new files at end
174 filename = filenames.pop(0)
175 if os.path.isdir(filename):
176 # directory: queue all included files and directories
177 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
179 ext = os.path.splitext(filename)[1].lower()
181 profile.loadProfile(filename)
182 mainWindow.addToProfileMRU(filename)
183 elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
184 scene_filenames.append(filename)
185 mainWindow.addToModelMRU(filename)
187 ignored_types[ext] = 1
189 ignored_types = ignored_types.keys()
191 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
192 mainWindow.updateProfileToAllControls()
193 # now process all the scene files
195 self.loadSceneFiles(scene_filenames)
196 self._selectObject(None)
198 newZoom = numpy.max(self._machineSize)
199 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
200 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
202 def showLoadModel(self, button = 1):
204 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)
205 dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
206 if dlg.ShowModal() != wx.ID_OK:
209 filenames = dlg.GetPaths()
211 if len(filenames) < 1:
213 profile.putPreference('lastFile', filenames[0])
214 self.loadFiles(filenames)
216 def showSaveModel(self):
217 if len(self._scene.objects()) < 1:
219 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
220 dlg.SetWildcard(meshLoader.saveWildcardFilter())
221 if dlg.ShowModal() != wx.ID_OK:
224 filename = dlg.GetPath()
226 meshLoader.saveMeshes(filename, self._scene.objects())
228 def OnPrintButton(self, button):
230 connectionEntry = self._printerConnectionManager.getAvailableConnection()
231 if machineCom.machineIsConnected():
232 self.showPrintWindow()
233 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
234 drives = removableStorage.getPossibleSDcardDrives()
236 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))
237 if dlg.ShowModal() != wx.ID_OK:
240 drive = drives[dlg.GetSelection()]
244 filename = self._scene._objectList[0].getName() + '.gcode'
245 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
246 elif connectionEntry is not None:
247 connection = connectionEntry.connection
248 if connectionEntry.window is None or not connectionEntry.window:
249 connectionEntry.window = printWindow2.printWindow(connection)
250 connectionEntry.window.Show()
251 connectionEntry.window.Raise()
252 if not connection.loadFile(self._gcodeFilename):
253 if connection.isPrinting():
254 self.notification.message("Cannot start print, because other print still running.")
256 self.notification.message("Failed to start print...")
261 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
262 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
263 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
267 def showPrintWindow(self):
268 if self._gcodeFilename is None:
270 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
271 wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING)
273 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
274 if self._gcodeFilename == self._slicer.getGCodeFilename():
275 self._slicer.submitSliceInfoOnline()
277 def showSaveGCode(self):
278 if len(self._scene._objectList) < 1:
280 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
281 filename = self._scene._objectList[0].getName() + '.gcode'
282 dlg.SetFilename(filename)
283 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
284 if dlg.ShowModal() != wx.ID_OK:
287 filename = dlg.GetPath()
290 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
292 def _copyFile(self, fileA, fileB, allowEject = False):
294 size = float(os.stat(fileA).st_size)
295 with open(fileA, 'rb') as fsrc:
296 with open(fileB, 'wb') as fdst:
298 buf = fsrc.read(16*1024)
302 self.printButton.setProgressBar(float(fsrc.tell()) / size)
307 self.notification.message("Failed to save")
310 self.notification.message("Saved as %s" % (fileB), lambda : self._doEjectSD(allowEject), 31, 'Eject')
311 elif explorer.hasExplorer():
312 self.notification.message("Saved as %s" % (fileB), lambda : explorer.openExplorer(fileB), 4, 'Open folder')
314 self.notification.message("Saved as %s" % (fileB))
315 self.printButton.setProgressBar(None)
316 if fileA == self._slicer.getGCodeFilename():
317 self._slicer.submitSliceInfoOnline()
319 def _doEjectSD(self, drive):
320 if removableStorage.ejectDrive(drive):
321 self.notification.message('You can now eject the card.')
323 self.notification.message('Safe remove failed...')
325 def _showSliceLog(self):
326 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
330 def OnToolSelect(self, button):
331 if self.rotateToolButton.getSelected():
332 self.tool = previewTools.toolRotate(self)
333 elif self.scaleToolButton.getSelected():
334 self.tool = previewTools.toolScale(self)
335 elif self.mirrorToolButton.getSelected():
336 self.tool = previewTools.toolNone(self)
338 self.tool = previewTools.toolNone(self)
339 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
340 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
341 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
342 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
343 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
344 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
345 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
346 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
348 def updateToolButtons(self):
349 if self._selectedObj is None:
353 self.rotateToolButton.setHidden(hidden)
354 self.scaleToolButton.setHidden(hidden)
355 self.mirrorToolButton.setHidden(hidden)
357 self.rotateToolButton.setSelected(False)
358 self.scaleToolButton.setSelected(False)
359 self.mirrorToolButton.setSelected(False)
362 def OnViewChange(self):
363 if self.viewSelection.getValue() == 4:
364 self.viewMode = 'gcode'
365 if self._gcode is not None and self._gcode.layerList is not None:
366 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
367 self._selectObject(None)
368 elif self.viewSelection.getValue() == 1:
369 self.viewMode = 'overhang'
370 elif self.viewSelection.getValue() == 2:
371 self.viewMode = 'transparent'
372 elif self.viewSelection.getValue() == 3:
373 self.viewMode = 'xray'
375 self.viewMode = 'normal'
376 self.layerSelect.setHidden(self.viewMode != 'gcode')
379 def OnRotateReset(self, button):
380 if self._selectedObj is None:
382 self._selectedObj.resetRotation()
383 self._scene.pushFree()
384 self._selectObject(self._selectedObj)
387 def OnLayFlat(self, button):
388 if self._selectedObj is None:
390 self._selectedObj.layFlat()
391 self._scene.pushFree()
392 self._selectObject(self._selectedObj)
395 def OnScaleReset(self, button):
396 if self._selectedObj is None:
398 self._selectedObj.resetScale()
399 self._selectObject(self._selectedObj)
400 self.updateProfileToControls()
403 def OnScaleMax(self, button):
404 if self._selectedObj is None:
406 machine = profile.getMachineSetting('machine_type')
407 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
408 self._scene.pushFree()
410 if machine == "ultimaker2":
411 #This is bad and Jaime should feel bad!
412 self._selectedObj.setPosition(numpy.array([0.0,-10.0]))
413 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
414 self._selectedObj.setPosition(numpy.array([0.0,0.0]))
415 self._scene.pushFree()
417 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
418 self._scene.pushFree()
419 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
420 self._scene.pushFree()
421 self._selectObject(self._selectedObj)
422 self.updateProfileToControls()
425 def OnMirror(self, axis):
426 if self._selectedObj is None:
428 self._selectedObj.mirror(axis)
431 def OnScaleEntry(self, value, axis):
432 if self._selectedObj is None:
438 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
439 self.updateProfileToControls()
440 self._scene.pushFree()
441 self._selectObject(self._selectedObj)
444 def OnScaleEntryMM(self, value, axis):
445 if self._selectedObj is None:
451 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
452 self.updateProfileToControls()
453 self._scene.pushFree()
454 self._selectObject(self._selectedObj)
457 def OnDeleteAll(self, e):
458 while len(self._scene.objects()) > 0:
459 self._deleteObject(self._scene.objects()[0])
460 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
462 def OnMultiply(self, e):
463 if self._focusObj is None:
466 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
467 if dlg.ShowModal() != wx.ID_OK:
476 self._scene.add(newObj)
477 self._scene.centerAll()
478 if not self._scene.checkPlatform(newObj):
483 self.notification.message("Could not create more then %d items" % (n - 1))
484 self._scene.remove(newObj)
485 self._scene.centerAll()
488 def OnSplitObject(self, e):
489 if self._focusObj is None:
491 self._scene.remove(self._focusObj)
492 for obj in self._focusObj.split(self._splitCallback):
493 if numpy.max(obj.getSize()) > 2.0:
495 self._scene.centerAll()
496 self._selectObject(None)
499 def OnCenter(self, e):
500 if self._focusObj is None:
502 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
503 self._scene.pushFree()
504 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
505 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
508 def _splitCallback(self, progress):
511 def OnMergeObjects(self, e):
512 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
513 if len(self._scene.objects()) == 2:
514 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
517 self._scene.merge(self._selectedObj, self._focusObj)
520 def sceneUpdated(self):
521 self._sceneUpdateTimer.Start(500, True)
522 self._slicer.abortSlicer()
523 self._scene.updateSizeOffsets()
526 def _onRunSlicer(self, e):
527 if self._isSimpleMode:
528 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
529 self._slicer.runSlicer(self._scene)
530 if self._isSimpleMode:
531 profile.resetTempOverride()
533 def _updateSliceProgress(self, progressValue, ready):
535 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
537 self.printButton.setDisabled(not ready)
538 if progressValue >= 0.0:
539 self.printButton.setProgressBar(progressValue)
541 self.printButton.setProgressBar(None)
542 if self._gcode is not None:
544 for layerVBOlist in self._gcodeVBOs:
545 for vbo in layerVBOlist:
546 self.glReleaseList.append(vbo)
549 self.printButton.setProgressBar(None)
550 text = '%s' % (self._slicer.getPrintTime())
551 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
552 amount = self._slicer.getFilamentAmount(e)
555 text += '\n%s' % (amount)
556 cost = self._slicer.getFilamentCost(e)
558 text += '\n%s' % (cost)
559 self.printButton.setBottomText(text)
560 self._gcode = gcodeInterpreter.gcode()
561 self._gcodeFilename = self._slicer.getGCodeFilename()
563 self.printButton.setBottomText('')
566 def _loadGCode(self):
567 self._gcode.progressCallback = self._gcodeLoadCallback
568 self._gcode.load(self._gcodeFilename)
570 def _gcodeLoadCallback(self, progress):
571 if not self or self._gcode is None:
573 if len(self._gcode.layerList) % 15 == 0:
575 if self._gcode is None:
577 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
578 if self.viewMode == 'gcode':
582 def loadScene(self, fileList):
583 for filename in fileList:
585 ext = os.path.splitext(filename)[1].lower()
586 if ext in imageToMesh.supportedExtensions():
587 imageToMesh.convertImageDialog(self, filename).Show()
590 objList = meshLoader.loadMeshes(filename)
592 traceback.print_exc()
595 if self._objectLoadShader is not None:
596 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
600 if not self._scene.checkPlatform(obj):
601 self._scene.centerAll()
602 self._selectObject(obj)
603 if obj.getScale()[0] < 1.0:
604 self.notification.message("Warning: Object scaled down.")
607 def _deleteObject(self, obj):
608 if obj == self._selectedObj:
609 self._selectObject(None)
610 if obj == self._focusObj:
611 self._focusObj = None
612 self._scene.remove(obj)
613 for m in obj._meshList:
614 if m.vbo is not None and m.vbo.decRef():
615 self.glReleaseList.append(m.vbo)
620 def _selectObject(self, obj, zoom = True):
621 if obj != self._selectedObj:
622 self._selectedObj = obj
623 self.updateModelSettingsToControls()
624 self.updateToolButtons()
625 if zoom and obj is not None:
626 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
627 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
628 newZoom = obj.getBoundaryCircle() * 6
629 if newZoom > numpy.max(self._machineSize) * 3:
630 newZoom = numpy.max(self._machineSize) * 3
631 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
633 def updateProfileToControls(self):
634 oldSimpleMode = self._isSimpleMode
635 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
636 if self._isSimpleMode != oldSimpleMode:
637 self._scene.arrangeAll()
639 self._scene.updateSizeOffsets(True)
640 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
641 self._objColors[0] = profile.getPreferenceColour('model_colour')
642 self._objColors[1] = profile.getPreferenceColour('model_colour2')
643 self._objColors[2] = profile.getPreferenceColour('model_colour3')
644 self._objColors[3] = profile.getPreferenceColour('model_colour4')
645 self._scene.updateMachineDimensions()
646 self.updateModelSettingsToControls()
648 def updateModelSettingsToControls(self):
649 if self._selectedObj is not None:
650 scale = self._selectedObj.getScale()
651 size = self._selectedObj.getSize()
652 self.scaleXctrl.setValue(round(scale[0], 2))
653 self.scaleYctrl.setValue(round(scale[1], 2))
654 self.scaleZctrl.setValue(round(scale[2], 2))
655 self.scaleXmmctrl.setValue(round(size[0], 2))
656 self.scaleYmmctrl.setValue(round(size[1], 2))
657 self.scaleZmmctrl.setValue(round(size[2], 2))
659 def OnKeyChar(self, keyCode):
660 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
661 if self._selectedObj is not None:
662 self._deleteObject(self._selectedObj)
664 if keyCode == wx.WXK_UP:
665 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
667 elif keyCode == wx.WXK_DOWN:
668 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
670 elif keyCode == wx.WXK_PAGEUP:
671 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
673 elif keyCode == wx.WXK_PAGEDOWN:
674 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
677 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
678 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
679 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
680 from collections import defaultdict
681 from gc import get_objects
682 self._beforeLeakTest = defaultdict(int)
683 for i in get_objects():
684 self._beforeLeakTest[type(i)] += 1
685 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
686 from collections import defaultdict
687 from gc import get_objects
688 self._afterLeakTest = defaultdict(int)
689 for i in get_objects():
690 self._afterLeakTest[type(i)] += 1
691 for k in self._afterLeakTest:
692 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
693 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
695 def ShaderUpdate(self, v, f):
696 s = opengl.GLShader(v, f)
698 self._objectLoadShader.release()
699 self._objectLoadShader = s
700 for obj in self._scene.objects():
701 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
704 def OnMouseDown(self,e):
705 self._mouseX = e.GetX()
706 self._mouseY = e.GetY()
707 self._mouseClick3DPos = self._mouse3Dpos
708 self._mouseClickFocus = self._focusObj
710 self._mouseState = 'doubleClick'
712 self._mouseState = 'dragOrClick'
713 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
714 p0 -= self.getObjectCenterPos() - self._viewTarget
715 p1 -= self.getObjectCenterPos() - self._viewTarget
716 if self.tool.OnDragStart(p0, p1):
717 self._mouseState = 'tool'
718 if self._mouseState == 'dragOrClick':
719 if e.GetButton() == 1:
720 if self._focusObj is not None:
721 self._selectObject(self._focusObj, False)
724 def OnMouseUp(self, e):
725 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
727 if self._mouseState == 'dragOrClick':
728 if e.GetButton() == 1:
729 self._selectObject(self._focusObj)
730 if e.GetButton() == 3:
732 if self._focusObj is not None:
733 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
734 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
735 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
736 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
737 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:
738 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
739 if len(self._scene.objects()) > 0:
740 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
741 if menu.MenuItemCount > 0:
744 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
745 self._scene.pushFree()
747 elif self._mouseState == 'tool':
748 if self.tempMatrix is not None and self._selectedObj is not None:
749 self._selectedObj.applyMatrix(self.tempMatrix)
750 self._scene.pushFree()
751 self._selectObject(self._selectedObj)
752 self.tempMatrix = None
753 self.tool.OnDragEnd()
755 self._mouseState = None
757 def OnMouseMotion(self,e):
758 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
759 p0 -= self.getObjectCenterPos() - self._viewTarget
760 p1 -= self.getObjectCenterPos() - self._viewTarget
762 if e.Dragging() and self._mouseState is not None:
763 if self._mouseState == 'tool':
764 self.tool.OnDrag(p0, p1)
765 elif not e.LeftIsDown() and e.RightIsDown():
766 self._mouseState = 'drag'
767 if wx.GetKeyState(wx.WXK_SHIFT):
768 a = math.cos(math.radians(self._yaw)) / 3.0
769 b = math.sin(math.radians(self._yaw)) / 3.0
770 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
771 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
772 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
773 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
775 self._yaw += e.GetX() - self._mouseX
776 self._pitch -= e.GetY() - self._mouseY
777 if self._pitch > 170:
781 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
782 self._mouseState = 'drag'
783 self._zoom += e.GetY() - self._mouseY
786 if self._zoom > numpy.max(self._machineSize) * 3:
787 self._zoom = numpy.max(self._machineSize) * 3
788 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
789 self._mouseState = 'dragObject'
790 z = max(0, self._mouseClick3DPos[2])
791 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
792 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
797 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
798 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
799 diff = cursorZ1 - cursorZ0
800 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
801 if not e.Dragging() or self._mouseState != 'tool':
802 self.tool.OnMouseMove(p0, p1)
804 self._mouseX = e.GetX()
805 self._mouseY = e.GetY()
807 def OnMouseWheel(self, e):
808 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
809 delta = max(min(delta,4),-4)
810 self._zoom *= 1.0 - delta / 10.0
813 if self._zoom > numpy.max(self._machineSize) * 3:
814 self._zoom = numpy.max(self._machineSize) * 3
817 def OnMouseLeave(self, e):
821 def getMouseRay(self, x, y):
822 if self._viewport is None:
823 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
824 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
825 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
826 p0 -= self._viewTarget
827 p1 -= self._viewTarget
830 def _init3DView(self):
831 # set viewing projection
832 size = self.GetSize()
833 glViewport(0, 0, size.GetWidth(), size.GetHeight())
836 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
838 glDisable(GL_RESCALE_NORMAL)
839 glDisable(GL_LIGHTING)
841 glEnable(GL_DEPTH_TEST)
842 glDisable(GL_CULL_FACE)
844 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
846 glClearColor(0.8, 0.8, 0.8, 1.0)
850 glMatrixMode(GL_PROJECTION)
852 aspect = float(size.GetWidth()) / float(size.GetHeight())
853 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
855 glMatrixMode(GL_MODELVIEW)
857 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
860 connectionEntry = self._printerConnectionManager.getAvailableConnection()
861 if machineCom.machineIsConnected():
862 self.printButton._imageID = 6
863 self.printButton._tooltip = _("Print")
864 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
865 self.printButton._imageID = 2
866 self.printButton._tooltip = _("Toolpath to SD")
867 elif connectionEntry is not None:
868 self.printButton._imageID = connectionEntry.icon
869 self.printButton._tooltip = _("Print with %s") % (connectionEntry.name)
871 self.printButton._imageID = 3
872 self.printButton._tooltip = _("Save toolpath")
874 if self._animView is not None:
875 self._viewTarget = self._animView.getPosition()
876 if self._animView.isDone():
877 self._animView = None
878 if self._animZoom is not None:
879 self._zoom = self._animZoom.getPosition()
880 if self._animZoom.isDone():
881 self._animZoom = None
882 if self.viewMode == 'gcode' and self._gcode is not None:
884 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
887 if self._objectShader is None:
888 if opengl.hasShaderSupport():
889 self._objectShader = opengl.GLShader("""
890 varying float light_amount;
894 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
895 gl_FrontColor = gl_Color;
897 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
901 varying float light_amount;
905 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
908 self._objectOverhangShader = opengl.GLShader("""
909 uniform float cosAngle;
910 uniform mat3 rotMatrix;
911 varying float light_amount;
915 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
916 gl_FrontColor = gl_Color;
918 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
920 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
922 light_amount = -10.0;
926 varying float light_amount;
930 if (light_amount == -10.0)
932 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
934 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
938 self._objectLoadShader = opengl.GLShader("""
939 uniform float intensity;
941 varying float light_amount;
945 vec4 tmp = gl_Vertex;
946 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
947 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
948 gl_Position = gl_ModelViewProjectionMatrix * tmp;
949 gl_FrontColor = gl_Color;
951 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
955 uniform float intensity;
956 varying float light_amount;
960 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
963 if self._objectShader is None or not self._objectShader.isValid():
964 self._objectShader = opengl.GLFakeShader()
965 self._objectOverhangShader = opengl.GLFakeShader()
966 self._objectLoadShader = None
968 glTranslate(0,0,-self._zoom)
969 glRotate(-self._pitch, 1,0,0)
970 glRotate(self._yaw, 0,0,1)
971 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
973 self._viewport = glGetIntegerv(GL_VIEWPORT)
974 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
975 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
977 glClearColor(1,1,1,1)
978 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
980 if self.viewMode != 'gcode':
981 for n in xrange(0, len(self._scene.objects())):
982 obj = self._scene.objects()[n]
983 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
984 self._renderObject(obj)
986 if self._mouseX > -1:
988 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
989 if n < len(self._scene.objects()):
990 self._focusObj = self._scene.objects()[n]
992 self._focusObj = None
993 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
994 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
995 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
996 self._mouse3Dpos -= self._viewTarget
999 glTranslate(0,0,-self._zoom)
1000 glRotate(-self._pitch, 1,0,0)
1001 glRotate(self._yaw, 0,0,1)
1002 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
1004 if self.viewMode == 'gcode':
1005 if self._gcode is not None and self._gcode.layerList is None:
1006 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
1007 self._gcodeLoadThread.daemon = True
1008 self._gcodeLoadThread.start()
1009 if self._gcode is not None and self._gcode.layerList is not None:
1011 if profile.getMachineSetting('machine_center_is_zero') != 'True':
1012 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
1014 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
1015 for n in xrange(0, drawUpTill):
1016 c = 1.0 - float(drawUpTill - n) / 15
1018 if len(self._gcodeVBOs) < n + 1:
1019 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
1020 if time.time() - t > 0.5:
1023 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
1024 if n == drawUpTill - 1:
1025 if len(self._gcodeVBOs[n]) < 9:
1026 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
1028 self._gcodeVBOs[n][8].render(GL_QUADS)
1029 glColor3f(c/2, 0, c)
1030 self._gcodeVBOs[n][9].render(GL_QUADS)
1031 glColor3f(0, c, c/2)
1032 self._gcodeVBOs[n][10].render(GL_QUADS)
1034 self._gcodeVBOs[n][11].render(GL_QUADS)
1037 self._gcodeVBOs[n][12].render(GL_QUADS)
1038 glColor3f(c/2, c/2, 0.0)
1039 self._gcodeVBOs[n][13].render(GL_QUADS)
1041 self._gcodeVBOs[n][14].render(GL_QUADS)
1042 self._gcodeVBOs[n][15].render(GL_QUADS)
1044 self._gcodeVBOs[n][16].render(GL_LINES)
1047 self._gcodeVBOs[n][0].render(GL_LINES)
1048 glColor3f(c/2, 0, c)
1049 self._gcodeVBOs[n][1].render(GL_LINES)
1050 glColor3f(0, c, c/2)
1051 self._gcodeVBOs[n][2].render(GL_LINES)
1053 self._gcodeVBOs[n][3].render(GL_LINES)
1056 self._gcodeVBOs[n][4].render(GL_LINES)
1057 glColor3f(c/2, c/2, 0.0)
1058 self._gcodeVBOs[n][5].render(GL_LINES)
1060 self._gcodeVBOs[n][6].render(GL_LINES)
1061 self._gcodeVBOs[n][7].render(GL_LINES)
1064 glStencilFunc(GL_ALWAYS, 1, 1)
1065 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1067 if self.viewMode == 'overhang':
1068 self._objectOverhangShader.bind()
1069 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1071 self._objectShader.bind()
1072 for obj in self._scene.objects():
1073 if obj._loadAnim is not None:
1074 if obj._loadAnim.isDone():
1075 obj._loadAnim = None
1079 if self._focusObj == obj:
1081 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1084 if self._selectedObj == obj or self._selectedObj is None:
1085 #If we want transparent, then first render a solid black model to remove the printer size lines.
1086 if self.viewMode == 'transparent':
1087 glColor4f(0, 0, 0, 0)
1088 self._renderObject(obj)
1090 glBlendFunc(GL_ONE, GL_ONE)
1091 glDisable(GL_DEPTH_TEST)
1093 if self.viewMode == 'xray':
1094 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1095 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1096 glEnable(GL_STENCIL_TEST)
1098 if self.viewMode == 'overhang':
1099 if self._selectedObj == obj and self.tempMatrix is not None:
1100 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1102 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1104 if not self._scene.checkPlatform(obj):
1105 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1106 self._renderObject(obj)
1108 self._renderObject(obj, brightness)
1109 glDisable(GL_STENCIL_TEST)
1111 glEnable(GL_DEPTH_TEST)
1112 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1114 if self.viewMode == 'xray':
1117 glEnable(GL_STENCIL_TEST)
1118 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1119 glDisable(GL_DEPTH_TEST)
1120 for i in xrange(2, 15, 2):
1121 glStencilFunc(GL_EQUAL, i, 0xFF)
1122 glColor(float(i)/10, float(i)/10, float(i)/5)
1124 glVertex3f(-1000,-1000,-10)
1125 glVertex3f( 1000,-1000,-10)
1126 glVertex3f( 1000, 1000,-10)
1127 glVertex3f(-1000, 1000,-10)
1129 for i in xrange(1, 15, 2):
1130 glStencilFunc(GL_EQUAL, i, 0xFF)
1131 glColor(float(i)/10, 0, 0)
1133 glVertex3f(-1000,-1000,-10)
1134 glVertex3f( 1000,-1000,-10)
1135 glVertex3f( 1000, 1000,-10)
1136 glVertex3f(-1000, 1000,-10)
1139 glDisable(GL_STENCIL_TEST)
1140 glEnable(GL_DEPTH_TEST)
1142 self._objectShader.unbind()
1144 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1146 if self._objectLoadShader is not None:
1147 self._objectLoadShader.bind()
1148 glColor4f(0.2, 0.6, 1.0, 1.0)
1149 for obj in self._scene.objects():
1150 if obj._loadAnim is None:
1152 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1153 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1154 self._renderObject(obj)
1155 self._objectLoadShader.unbind()
1160 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1162 z = self._usbPrintMonitor.getZ()
1163 size = self._machineSize
1164 glColor4ub(255,255,0,128)
1166 glVertex3f(-size[0]/2,-size[1]/2, z)
1167 glVertex3f( size[0]/2,-size[1]/2, z)
1168 glVertex3f( size[0]/2, size[1]/2, z)
1169 glVertex3f(-size[0]/2, size[1]/2, z)
1172 if self.viewMode == 'gcode':
1173 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1174 glDisable(GL_DEPTH_TEST)
1177 glTranslate(0,-4,-10)
1178 glColor4ub(60,60,60,255)
1179 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1182 #Draw the object box-shadow, so you can see where it will collide with other objects.
1183 if self._selectedObj is not None:
1185 glEnable(GL_CULL_FACE)
1186 glColor4f(0,0,0,0.16)
1188 for obj in self._scene.objects():
1190 glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1191 glBegin(GL_TRIANGLE_FAN)
1192 for p in obj._boundaryHull[::-1]:
1193 glVertex3f(p[0], p[1], 0)
1197 glColor4f(0,0,0,0.06)
1198 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1199 glBegin(GL_TRIANGLE_FAN)
1200 for p in self._selectedObj._printAreaHull[::-1]:
1201 glVertex3f(p[0], p[1], 0)
1203 glBegin(GL_TRIANGLE_FAN)
1204 for p in self._selectedObj._headAreaMinHull[::-1]:
1205 glVertex3f(p[0], p[1], 0)
1208 glDisable(GL_CULL_FACE)
1211 #Draw the outline of the selected object, on top of everything else except the GUI.
1212 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1213 glDisable(GL_DEPTH_TEST)
1214 glEnable(GL_CULL_FACE)
1215 glEnable(GL_STENCIL_TEST)
1217 glStencilFunc(GL_EQUAL, 0, 255)
1219 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1221 glColor4f(1,1,1,0.5)
1222 self._renderObject(self._selectedObj)
1223 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1225 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1226 glDisable(GL_STENCIL_TEST)
1227 glDisable(GL_CULL_FACE)
1228 glEnable(GL_DEPTH_TEST)
1230 if self._selectedObj is not None:
1232 pos = self.getObjectCenterPos()
1233 glTranslate(pos[0], pos[1], pos[2])
1236 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1237 glDisable(GL_DEPTH_TEST)
1240 glTranslate(0,-4,-10)
1241 glColor4ub(60,60,60,255)
1242 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1245 def _renderObject(self, obj, brightness = False, addSink = True):
1248 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1250 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1252 if self.tempMatrix is not None and obj == self._selectedObj:
1253 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1254 glMultMatrixf(tempMatrix)
1256 offset = obj.getDrawOffset()
1257 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1259 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1260 glMultMatrixf(tempMatrix)
1263 for m in obj._meshList:
1265 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1267 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1272 def _drawMachine(self):
1273 glEnable(GL_CULL_FACE)
1276 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1278 machine = profile.getMachineSetting('machine_type')
1279 if machine.startswith('ultimaker'):
1280 if machine not in self._platformMesh:
1281 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1283 self._platformMesh[machine] = meshes[0]
1285 self._platformMesh[machine] = None
1286 if machine == 'ultimaker2':
1287 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1289 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1290 glColor4f(1,1,1,0.5)
1291 self._objectShader.bind()
1292 self._renderObject(self._platformMesh[machine], False, False)
1293 self._objectShader.unbind()
1295 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1296 if machine == 'ultimaker2':
1297 if not hasattr(self._platformMesh[machine], 'texture'):
1298 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1299 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1300 glEnable(GL_TEXTURE_2D)
1304 glTranslate(0,150,-5)
1309 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1312 glVertex3f( w, 0, h)
1314 glVertex3f(-w, 0, h)
1316 glVertex3f(-w, 0, 0)
1318 glVertex3f( w, 0, 0)
1321 glVertex3f(-w, d, h)
1323 glVertex3f( w, d, h)
1325 glVertex3f( w, d, 0)
1327 glVertex3f(-w, d, 0)
1329 glDisable(GL_TEXTURE_2D)
1330 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1336 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1337 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1338 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1339 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1340 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1341 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1346 polys = profile.getMachineSizePolygons()
1347 height = profile.getMachineSettingFloat('machine_height')
1349 for n in xrange(0, len(polys[0])):
1351 glColor4ub(5, 171, 231, 96)
1353 glColor4ub(5, 171, 231, 64)
1354 glVertex3f(polys[0][n][0], polys[0][n][1], height)
1355 glVertex3f(polys[0][n][0], polys[0][n][1], 0)
1356 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0)
1357 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height)
1359 glColor4ub(5, 171, 231, 128)
1360 glBegin(GL_TRIANGLE_FAN)
1361 for p in polys[0][::-1]:
1362 glVertex3f(p[0], p[1], height)
1366 if self._platformTexture is None:
1367 self._platformTexture = opengl.loadGLTexture('checkerboard.png')
1368 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1369 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
1370 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
1371 glColor4f(1,1,1,0.5)
1372 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1373 glEnable(GL_TEXTURE_2D)
1374 glBegin(GL_TRIANGLE_FAN)
1376 glTexCoord2f(p[0]/20, p[1]/20)
1377 glVertex3f(p[0], p[1], 0)
1379 glDisable(GL_TEXTURE_2D)
1380 glColor4ub(127, 127, 127, 200)
1381 for poly in polys[1:]:
1382 glBegin(GL_TRIANGLE_FAN)
1384 glTexCoord2f(p[0]/20, p[1]/20)
1385 glVertex3f(p[0], p[1], 0)
1390 glDisable(GL_CULL_FACE)
1392 def _generateGCodeVBOs(self, layer):
1394 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1395 if ':' in extrudeType:
1396 extruder = int(extrudeType[extrudeType.find(':')+1:])
1397 extrudeType = extrudeType[0:extrudeType.find(':')]
1400 pointList = numpy.zeros((0,3), numpy.float32)
1402 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1404 a = numpy.concatenate((a[:-1], a[1:]), 1)
1405 a = a.reshape((len(a) * 2, 3))
1406 pointList = numpy.concatenate((pointList, a))
1407 ret.append(opengl.GLVBO(pointList))
1410 def _generateGCodeVBOs2(self, layer):
1411 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1412 filamentArea = math.pi * filamentRadius * filamentRadius
1413 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1416 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1417 if ':' in extrudeType:
1418 extruder = int(extrudeType[extrudeType.find(':')+1:])
1419 extrudeType = extrudeType[0:extrudeType.find(':')]
1422 pointList = numpy.zeros((0,3), numpy.float32)
1424 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1426 if extrudeType == 'FILL':
1429 normal = a[1:] - a[:-1]
1430 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1431 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1434 ePerDist = path['extrusion'][1:] / lens
1436 lineWidth = ePerDist / path['layerThickness'] / 2.0
1438 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1440 normal[:,0] *= lineWidth
1441 normal[:,1] *= lineWidth
1443 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1444 b = numpy.concatenate((b, a[1:] + normal), 1)
1445 b = numpy.concatenate((b, a[1:] - normal), 1)
1446 b = numpy.concatenate((b, a[:-1] - normal), 1)
1447 b = numpy.concatenate((b, a[:-1] + normal), 1)
1448 b = b.reshape((len(b) * 4, 3))
1451 normal2 = normal[:-1] + normal[1:]
1452 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1453 normal2[:,0] /= lens2
1454 normal2[:,1] /= lens2
1455 normal2[:,0] *= lineWidth[:-1]
1456 normal2[:,1] *= lineWidth[:-1]
1458 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1459 c = numpy.concatenate((c, a[1:-1]), 1)
1460 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1461 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1462 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1464 c = numpy.concatenate((c, a[1:-1]), 1)
1465 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1466 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1467 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1469 c = c.reshape((len(c) * 8, 3))
1471 pointList = numpy.concatenate((pointList, b, c))
1473 pointList = numpy.concatenate((pointList, b))
1474 ret.append(opengl.GLVBO(pointList))
1476 pointList = numpy.zeros((0,3), numpy.float32)
1478 if path['type'] == 'move':
1479 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1480 a = numpy.concatenate((a[:-1], a[1:]), 1)
1481 a = a.reshape((len(a) * 2, 3))
1482 pointList = numpy.concatenate((pointList, a))
1483 if path['type'] == 'retract':
1484 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1485 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1486 a = a.reshape((len(a) * 2, 3))
1487 pointList = numpy.concatenate((pointList, a))
1488 ret.append(opengl.GLVBO(pointList))
1492 def getObjectCenterPos(self):
1493 if self._selectedObj is None:
1494 return [0.0, 0.0, 0.0]
1495 pos = self._selectedObj.getPosition()
1496 size = self._selectedObj.getSize()
1497 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1499 def getObjectBoundaryCircle(self):
1500 if self._selectedObj is None:
1502 return self._selectedObj.getBoundaryCircle()
1504 def getObjectSize(self):
1505 if self._selectedObj is None:
1506 return [0.0, 0.0, 0.0]
1507 return self._selectedObj.getSize()
1509 def getObjectMatrix(self):
1510 if self._selectedObj is None:
1511 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1512 return self._selectedObj.getMatrix()
1514 class shaderEditor(wx.Dialog):
1515 def __init__(self, parent, callback, v, f):
1516 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1517 self._callback = callback
1518 s = wx.BoxSizer(wx.VERTICAL)
1520 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1521 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1522 s.Add(self._vertex, 1, flag=wx.EXPAND)
1523 s.Add(self._fragment, 1, flag=wx.EXPAND)
1525 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1526 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1528 self.SetPosition(self.GetParent().GetPosition())
1529 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1532 def OnText(self, e):
1533 self._callback(self._vertex.GetValue(), self._fragment.GetValue())