1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
14 OpenGL.ERROR_CHECKING = False
15 from OpenGL.GLU import *
16 from OpenGL.GL import *
18 from Cura.gui import printWindow
19 from Cura.util import profile
20 from Cura.util import meshLoader
21 from Cura.util import objectScene
22 from Cura.util import resources
23 from Cura.util import sliceEngine
24 from Cura.util import machineCom
25 from Cura.util import removableStorage
26 from Cura.util import gcodeInterpreter
27 from Cura.util.printerConnection import printerConnectionManager
28 from Cura.gui.util import previewTools
29 from Cura.gui.util import opengl
30 from Cura.gui.util import openglGui
31 from Cura.gui.tools import youmagineGui
32 from Cura.gui.tools import imageToMesh
34 class SceneView(openglGui.glGuiPanel):
35 def __init__(self, parent):
36 super(SceneView, self).__init__(parent)
41 self._scene = objectScene.Scene()
44 self._gcodeFilename = None
45 self._gcodeLoadThread = None
46 self._objectShader = None
47 self._objectLoadShader = None
49 self._selectedObj = None
50 self._objColors = [None,None,None,None]
53 self._mouseState = None
54 self._viewTarget = numpy.array([0,0,0], numpy.float32)
57 self._platformMesh = {}
58 self._isSimpleMode = True
59 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
60 self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager()
63 self._modelMatrix = None
64 self._projMatrix = None
65 self.tempMatrix = None
67 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
68 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
69 self.printButton.setDisabled(True)
72 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
73 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
74 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
76 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
77 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
79 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
80 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
82 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
83 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
84 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
86 self.rotateToolButton.setExpandArrow(True)
87 self.scaleToolButton.setExpandArrow(True)
88 self.mirrorToolButton.setExpandArrow(True)
90 self.scaleForm = openglGui.glFrame(self, (2, -2))
91 openglGui.glGuiLayoutGrid(self.scaleForm)
92 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
93 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
94 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
95 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
96 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
97 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
98 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
99 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
100 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
101 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
102 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
103 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
104 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
105 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
107 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
108 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
110 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
111 self.youMagineButton.setDisabled(True)
113 self.notification = openglGui.glNotification(self, (0, 0))
115 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
116 self._sceneUpdateTimer = wx.Timer(self)
117 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
118 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
119 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
123 self.updateToolButtons()
124 self.updateProfileToControls()
126 def loadGCodeFile(self, filename):
127 self.OnDeleteAll(None)
128 if self._gcode is not None:
130 for layerVBOlist in self._gcodeVBOs:
131 for vbo in layerVBOlist:
132 self.glReleaseList.append(vbo)
134 self._gcode = gcodeInterpreter.gcode()
135 self._gcodeFilename = filename
136 self.printButton.setBottomText('')
137 self.viewSelection.setValue(4)
138 self.printButton.setDisabled(False)
139 self.youMagineButton.setDisabled(True)
142 def loadSceneFiles(self, filenames):
143 self.youMagineButton.setDisabled(False)
144 #if self.viewSelection.getValue() == 4:
145 # self.viewSelection.setValue(0)
146 # self.OnViewChange()
147 self.loadScene(filenames)
149 def loadFiles(self, filenames):
150 mainWindow = self.GetParent().GetParent().GetParent()
151 # only one GCODE file can be active
152 # so if single gcode file, process this
153 # otherwise ignore all gcode files
155 if len(filenames) == 1:
156 filename = filenames[0]
157 ext = os.path.splitext(filename)[1].lower()
158 if ext == '.g' or ext == '.gcode':
159 gcodeFilename = filename
160 mainWindow.addToModelMRU(filename)
161 if gcodeFilename is not None:
162 self.loadGCodeFile(gcodeFilename)
164 # process directories and special file types
165 # and keep scene files for later processing
167 ignored_types = dict()
168 # use file list as queue
169 # pop first entry for processing and append new files at end
171 filename = filenames.pop(0)
172 if os.path.isdir(filename):
173 # directory: queue all included files and directories
174 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
176 ext = os.path.splitext(filename)[1].lower()
178 profile.loadProfile(filename)
179 mainWindow.addToProfileMRU(filename)
180 elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
181 scene_filenames.append(filename)
182 mainWindow.addToModelMRU(filename)
184 ignored_types[ext] = 1
186 ignored_types = ignored_types.keys()
188 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
189 mainWindow.updateProfileToAllControls()
190 # now process all the scene files
192 self.loadSceneFiles(scene_filenames)
193 self._selectObject(None)
195 newZoom = numpy.max(self._machineSize)
196 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
197 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
199 def showLoadModel(self, button = 1):
201 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)
202 dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
203 if dlg.ShowModal() != wx.ID_OK:
206 filenames = dlg.GetPaths()
208 if len(filenames) < 1:
210 profile.putPreference('lastFile', filenames[0])
211 self.loadFiles(filenames)
213 def showSaveModel(self):
214 if len(self._scene.objects()) < 1:
216 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
217 dlg.SetWildcard(meshLoader.saveWildcardFilter())
218 if dlg.ShowModal() != wx.ID_OK:
221 filename = dlg.GetPath()
223 meshLoader.saveMeshes(filename, self._scene.objects())
225 def OnPrintButton(self, button):
228 connectionEntry = self._printerConnectionManager.getAvailableConnection()
229 if machineCom.machineIsConnected():
230 self.showPrintWindow()
231 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
232 drives = removableStorage.getPossibleSDcardDrives()
234 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))
235 if dlg.ShowModal() != wx.ID_OK:
238 drive = drives[dlg.GetSelection()]
242 filename = self._scene._objectList[0].getName() + '.gcode'
243 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
244 elif connectionEntry is not None:
245 connectionEntry.connection.loadFile(self._gcodeFilename)
246 connectionEntry.connection.startPrint()
251 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
252 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
253 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
257 def showPrintWindow(self):
258 if self._gcodeFilename is None:
260 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
261 wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING)
263 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
264 if self._gcodeFilename == self._slicer.getGCodeFilename():
265 self._slicer.submitSliceInfoOnline()
267 def showSaveGCode(self):
268 if len(self._scene._objectList) < 1:
270 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
271 filename = self._scene._objectList[0].getName() + '.gcode'
272 dlg.SetFilename(filename)
273 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
274 if dlg.ShowModal() != wx.ID_OK:
277 filename = dlg.GetPath()
280 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
282 def _copyFile(self, fileA, fileB, allowEject = False):
284 size = float(os.stat(fileA).st_size)
285 with open(fileA, 'rb') as fsrc:
286 with open(fileB, 'wb') as fdst:
288 buf = fsrc.read(16*1024)
292 self.printButton.setProgressBar(float(fsrc.tell()) / size)
297 self.notification.message("Failed to save")
300 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...'))
302 self.notification.message("Saved as %s" % (fileB))
303 self.printButton.setProgressBar(None)
304 if fileA == self._slicer.getGCodeFilename():
305 self._slicer.submitSliceInfoOnline()
307 def _showSliceLog(self):
308 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
312 def OnToolSelect(self, button):
313 if self.rotateToolButton.getSelected():
314 self.tool = previewTools.toolRotate(self)
315 elif self.scaleToolButton.getSelected():
316 self.tool = previewTools.toolScale(self)
317 elif self.mirrorToolButton.getSelected():
318 self.tool = previewTools.toolNone(self)
320 self.tool = previewTools.toolNone(self)
321 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
322 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
323 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
324 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
325 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
326 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
327 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
328 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
330 def updateToolButtons(self):
331 if self._selectedObj is None:
335 self.rotateToolButton.setHidden(hidden)
336 self.scaleToolButton.setHidden(hidden)
337 self.mirrorToolButton.setHidden(hidden)
339 self.rotateToolButton.setSelected(False)
340 self.scaleToolButton.setSelected(False)
341 self.mirrorToolButton.setSelected(False)
344 def OnViewChange(self):
345 if self.viewSelection.getValue() == 4:
346 self.viewMode = 'gcode'
347 if self._gcode is not None and self._gcode.layerList is not None:
348 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
349 self._selectObject(None)
350 elif self.viewSelection.getValue() == 1:
351 self.viewMode = 'overhang'
352 elif self.viewSelection.getValue() == 2:
353 self.viewMode = 'transparent'
354 elif self.viewSelection.getValue() == 3:
355 self.viewMode = 'xray'
357 self.viewMode = 'normal'
358 self.layerSelect.setHidden(self.viewMode != 'gcode')
361 def OnRotateReset(self, button):
362 if self._selectedObj is None:
364 self._selectedObj.resetRotation()
365 self._scene.pushFree()
366 self._selectObject(self._selectedObj)
369 def OnLayFlat(self, button):
370 if self._selectedObj is None:
372 self._selectedObj.layFlat()
373 self._scene.pushFree()
374 self._selectObject(self._selectedObj)
377 def OnScaleReset(self, button):
378 if self._selectedObj is None:
380 self._selectedObj.resetScale()
381 self._selectObject(self._selectedObj)
382 self.updateProfileToControls()
385 def OnScaleMax(self, button):
386 if self._selectedObj is None:
388 machine = profile.getMachineSetting('machine_type')
389 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
390 self._scene.pushFree()
392 if machine == "ultimaker2":
393 #This is bad and Jaime should feel bad!
394 self._selectedObj.setPosition(numpy.array([0.0,-10.0]))
395 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
396 self._selectedObj.setPosition(numpy.array([0.0,0.0]))
397 self._scene.pushFree()
399 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
400 self._scene.pushFree()
401 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
402 self._scene.pushFree()
403 self._selectObject(self._selectedObj)
404 self.updateProfileToControls()
407 def OnMirror(self, axis):
408 if self._selectedObj is None:
410 self._selectedObj.mirror(axis)
413 def OnScaleEntry(self, value, axis):
414 if self._selectedObj is None:
420 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
421 self.updateProfileToControls()
422 self._scene.pushFree()
423 self._selectObject(self._selectedObj)
426 def OnScaleEntryMM(self, value, axis):
427 if self._selectedObj is None:
433 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
434 self.updateProfileToControls()
435 self._scene.pushFree()
436 self._selectObject(self._selectedObj)
439 def OnDeleteAll(self, e):
440 while len(self._scene.objects()) > 0:
441 self._deleteObject(self._scene.objects()[0])
442 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
444 def OnMultiply(self, e):
445 if self._focusObj is None:
448 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
449 if dlg.ShowModal() != wx.ID_OK:
458 self._scene.add(newObj)
459 self._scene.centerAll()
460 if not self._scene.checkPlatform(newObj):
465 self.notification.message("Could not create more then %d items" % (n - 1))
466 self._scene.remove(newObj)
467 self._scene.centerAll()
470 def OnSplitObject(self, e):
471 if self._focusObj is None:
473 self._scene.remove(self._focusObj)
474 for obj in self._focusObj.split(self._splitCallback):
475 if numpy.max(obj.getSize()) > 2.0:
477 self._scene.centerAll()
478 self._selectObject(None)
481 def OnCenter(self, e):
482 if self._focusObj is None:
484 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
485 self._scene.pushFree()
486 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
487 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
490 def _splitCallback(self, progress):
493 def OnMergeObjects(self, e):
494 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
495 if len(self._scene.objects()) == 2:
496 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
499 self._scene.merge(self._selectedObj, self._focusObj)
502 def sceneUpdated(self):
503 self._sceneUpdateTimer.Start(500, True)
504 self._slicer.abortSlicer()
505 self._scene.updateSizeOffsets()
508 def _onRunSlicer(self, e):
509 if self._isSimpleMode:
510 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
511 self._slicer.runSlicer(self._scene)
512 if self._isSimpleMode:
513 profile.resetTempOverride()
515 def _updateSliceProgress(self, progressValue, ready):
517 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
519 self.printButton.setDisabled(not ready)
520 if progressValue >= 0.0:
521 self.printButton.setProgressBar(progressValue)
523 self.printButton.setProgressBar(None)
524 if self._gcode is not None:
526 for layerVBOlist in self._gcodeVBOs:
527 for vbo in layerVBOlist:
528 self.glReleaseList.append(vbo)
531 self.printButton.setProgressBar(None)
532 text = '%s' % (self._slicer.getPrintTime())
533 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
534 amount = self._slicer.getFilamentAmount(e)
537 text += '\n%s' % (amount)
538 cost = self._slicer.getFilamentCost(e)
540 text += '\n%s' % (cost)
541 self.printButton.setBottomText(text)
542 self._gcode = gcodeInterpreter.gcode()
543 self._gcodeFilename = self._slicer.getGCodeFilename()
545 self.printButton.setBottomText('')
548 def _loadGCode(self):
549 self._gcode.progressCallback = self._gcodeLoadCallback
550 self._gcode.load(self._gcodeFilename)
552 def _gcodeLoadCallback(self, progress):
553 if not self or self._gcode is None:
555 if len(self._gcode.layerList) % 15 == 0:
557 if self._gcode is None:
559 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
560 if self.viewMode == 'gcode':
564 def loadScene(self, fileList):
565 for filename in fileList:
567 ext = os.path.splitext(filename)[1].lower()
568 if ext in imageToMesh.supportedExtensions():
569 imageToMesh.convertImageDialog(self, filename).Show()
572 objList = meshLoader.loadMeshes(filename)
574 traceback.print_exc()
577 if self._objectLoadShader is not None:
578 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
582 self._scene.centerAll()
583 self._selectObject(obj)
584 if obj.getScale()[0] < 1.0:
585 self.notification.message("Warning: Object scaled down.")
588 def _deleteObject(self, obj):
589 if obj == self._selectedObj:
590 self._selectObject(None)
591 if obj == self._focusObj:
592 self._focusObj = None
593 self._scene.remove(obj)
594 for m in obj._meshList:
595 if m.vbo is not None and m.vbo.decRef():
596 self.glReleaseList.append(m.vbo)
601 def _selectObject(self, obj, zoom = True):
602 if obj != self._selectedObj:
603 self._selectedObj = obj
604 self.updateProfileToControls()
605 self.updateToolButtons()
606 if zoom and obj is not None:
607 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
608 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
609 newZoom = obj.getBoundaryCircle() * 6
610 if newZoom > numpy.max(self._machineSize) * 3:
611 newZoom = numpy.max(self._machineSize) * 3
612 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
614 def updateProfileToControls(self):
615 oldSimpleMode = self._isSimpleMode
616 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
617 if self._isSimpleMode != oldSimpleMode:
618 self._scene.arrangeAll()
620 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
621 self._objColors[0] = profile.getPreferenceColour('model_colour')
622 self._objColors[1] = profile.getPreferenceColour('model_colour2')
623 self._objColors[2] = profile.getPreferenceColour('model_colour3')
624 self._objColors[3] = profile.getPreferenceColour('model_colour4')
625 self._scene.setMachineSize(self._machineSize)
626 self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height'))
628 if self._selectedObj is not None:
629 scale = self._selectedObj.getScale()
630 size = self._selectedObj.getSize()
631 self.scaleXctrl.setValue(round(scale[0], 2))
632 self.scaleYctrl.setValue(round(scale[1], 2))
633 self.scaleZctrl.setValue(round(scale[2], 2))
634 self.scaleXmmctrl.setValue(round(size[0], 2))
635 self.scaleYmmctrl.setValue(round(size[1], 2))
636 self.scaleZmmctrl.setValue(round(size[2], 2))
638 def OnKeyChar(self, keyCode):
639 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
640 if self._selectedObj is not None:
641 self._deleteObject(self._selectedObj)
643 if keyCode == wx.WXK_UP:
644 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
646 elif keyCode == wx.WXK_DOWN:
647 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
649 elif keyCode == wx.WXK_PAGEUP:
650 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
652 elif keyCode == wx.WXK_PAGEDOWN:
653 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
656 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
657 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
658 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
659 from collections import defaultdict
660 from gc import get_objects
661 self._beforeLeakTest = defaultdict(int)
662 for i in get_objects():
663 self._beforeLeakTest[type(i)] += 1
664 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
665 from collections import defaultdict
666 from gc import get_objects
667 self._afterLeakTest = defaultdict(int)
668 for i in get_objects():
669 self._afterLeakTest[type(i)] += 1
670 for k in self._afterLeakTest:
671 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
672 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
674 def ShaderUpdate(self, v, f):
675 s = opengl.GLShader(v, f)
677 self._objectLoadShader.release()
678 self._objectLoadShader = s
679 for obj in self._scene.objects():
680 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
683 def OnMouseDown(self,e):
684 self._mouseX = e.GetX()
685 self._mouseY = e.GetY()
686 self._mouseClick3DPos = self._mouse3Dpos
687 self._mouseClickFocus = self._focusObj
689 self._mouseState = 'doubleClick'
691 self._mouseState = 'dragOrClick'
692 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
693 p0 -= self.getObjectCenterPos() - self._viewTarget
694 p1 -= self.getObjectCenterPos() - self._viewTarget
695 if self.tool.OnDragStart(p0, p1):
696 self._mouseState = 'tool'
697 if self._mouseState == 'dragOrClick':
698 if e.GetButton() == 1:
699 if self._focusObj is not None:
700 self._selectObject(self._focusObj, False)
703 def OnMouseUp(self, e):
704 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
706 if self._mouseState == 'dragOrClick':
707 if e.GetButton() == 1:
708 self._selectObject(self._focusObj)
709 if e.GetButton() == 3:
711 if self._focusObj is not None:
712 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
713 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
714 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
715 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
716 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:
717 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
718 if len(self._scene.objects()) > 0:
719 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
720 if menu.MenuItemCount > 0:
723 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
724 self._scene.pushFree()
726 elif self._mouseState == 'tool':
727 if self.tempMatrix is not None and self._selectedObj is not None:
728 self._selectedObj.applyMatrix(self.tempMatrix)
729 self._scene.pushFree()
730 self._selectObject(self._selectedObj)
731 self.tempMatrix = None
732 self.tool.OnDragEnd()
734 self._mouseState = None
736 def OnMouseMotion(self,e):
737 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
738 p0 -= self.getObjectCenterPos() - self._viewTarget
739 p1 -= self.getObjectCenterPos() - self._viewTarget
741 if e.Dragging() and self._mouseState is not None:
742 if self._mouseState == 'tool':
743 self.tool.OnDrag(p0, p1)
744 elif not e.LeftIsDown() and e.RightIsDown():
745 self._mouseState = 'drag'
746 if wx.GetKeyState(wx.WXK_SHIFT):
747 a = math.cos(math.radians(self._yaw)) / 3.0
748 b = math.sin(math.radians(self._yaw)) / 3.0
749 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
750 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
751 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
752 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
754 self._yaw += e.GetX() - self._mouseX
755 self._pitch -= e.GetY() - self._mouseY
756 if self._pitch > 170:
760 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
761 self._mouseState = 'drag'
762 self._zoom += e.GetY() - self._mouseY
765 if self._zoom > numpy.max(self._machineSize) * 3:
766 self._zoom = numpy.max(self._machineSize) * 3
767 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
768 self._mouseState = 'dragObject'
769 z = max(0, self._mouseClick3DPos[2])
770 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
771 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
776 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
777 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
778 diff = cursorZ1 - cursorZ0
779 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
780 if not e.Dragging() or self._mouseState != 'tool':
781 self.tool.OnMouseMove(p0, p1)
783 self._mouseX = e.GetX()
784 self._mouseY = e.GetY()
786 def OnMouseWheel(self, e):
787 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
788 delta = max(min(delta,4),-4)
789 self._zoom *= 1.0 - delta / 10.0
792 if self._zoom > numpy.max(self._machineSize) * 3:
793 self._zoom = numpy.max(self._machineSize) * 3
796 def OnMouseLeave(self, e):
800 def getMouseRay(self, x, y):
801 if self._viewport is None:
802 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
803 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
804 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
805 p0 -= self._viewTarget
806 p1 -= self._viewTarget
809 def _init3DView(self):
810 # set viewing projection
811 size = self.GetSize()
812 glViewport(0, 0, size.GetWidth(), size.GetHeight())
815 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
817 glDisable(GL_RESCALE_NORMAL)
818 glDisable(GL_LIGHTING)
820 glEnable(GL_DEPTH_TEST)
821 glDisable(GL_CULL_FACE)
823 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
825 glClearColor(0.8, 0.8, 0.8, 1.0)
829 glMatrixMode(GL_PROJECTION)
831 aspect = float(size.GetWidth()) / float(size.GetHeight())
832 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
834 glMatrixMode(GL_MODELVIEW)
836 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
839 connectionEntry = self._printerConnectionManager.getAvailableConnection()
840 if machineCom.machineIsConnected():
841 self.printButton._imageID = 6
842 self.printButton._tooltip = _("Print")
843 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0):
844 self.printButton._imageID = 2
845 self.printButton._tooltip = _("Toolpath to SD")
846 elif connectionEntry is not None:
847 self.printButton._imageID = connectionEntry.icon
848 self.printButton._tooltip = _("Print with %s") % (connectionEntry.name)
850 self.printButton._imageID = 3
851 self.printButton._tooltip = _("Save toolpath")
853 if self._animView is not None:
854 self._viewTarget = self._animView.getPosition()
855 if self._animView.isDone():
856 self._animView = None
857 if self._animZoom is not None:
858 self._zoom = self._animZoom.getPosition()
859 if self._animZoom.isDone():
860 self._animZoom = None
861 if self.viewMode == 'gcode' and self._gcode is not None:
863 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
866 if self._objectShader is None:
867 if opengl.hasShaderSupport():
868 self._objectShader = opengl.GLShader("""
869 varying float light_amount;
873 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
874 gl_FrontColor = gl_Color;
876 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
880 varying float light_amount;
884 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
887 self._objectOverhangShader = opengl.GLShader("""
888 uniform float cosAngle;
889 uniform mat3 rotMatrix;
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)));
899 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
901 light_amount = -10.0;
905 varying float light_amount;
909 if (light_amount == -10.0)
911 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
913 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
917 self._objectLoadShader = opengl.GLShader("""
918 uniform float intensity;
920 varying float light_amount;
924 vec4 tmp = gl_Vertex;
925 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
926 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
927 gl_Position = gl_ModelViewProjectionMatrix * tmp;
928 gl_FrontColor = gl_Color;
930 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
934 uniform float intensity;
935 varying float light_amount;
939 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
942 if self._objectShader is None or not self._objectShader.isValid():
943 self._objectShader = opengl.GLFakeShader()
944 self._objectOverhangShader = opengl.GLFakeShader()
945 self._objectLoadShader = None
947 glTranslate(0,0,-self._zoom)
948 glRotate(-self._pitch, 1,0,0)
949 glRotate(self._yaw, 0,0,1)
950 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
952 self._viewport = glGetIntegerv(GL_VIEWPORT)
953 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
954 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
956 glClearColor(1,1,1,1)
957 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
959 if self.viewMode != 'gcode':
960 for n in xrange(0, len(self._scene.objects())):
961 obj = self._scene.objects()[n]
962 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
963 self._renderObject(obj)
965 if self._mouseX > -1:
967 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
968 if n < len(self._scene.objects()):
969 self._focusObj = self._scene.objects()[n]
971 self._focusObj = None
972 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
973 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
974 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
975 self._mouse3Dpos -= self._viewTarget
978 glTranslate(0,0,-self._zoom)
979 glRotate(-self._pitch, 1,0,0)
980 glRotate(self._yaw, 0,0,1)
981 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
983 if self.viewMode == 'gcode':
984 if self._gcode is not None and self._gcode.layerList is None:
985 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
986 self._gcodeLoadThread.daemon = True
987 self._gcodeLoadThread.start()
988 if self._gcode is not None and self._gcode.layerList is not None:
990 if profile.getMachineSetting('machine_center_is_zero') != 'True':
991 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
993 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
994 for n in xrange(0, drawUpTill):
995 c = 1.0 - float(drawUpTill - n) / 15
997 if len(self._gcodeVBOs) < n + 1:
998 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
999 if time.time() - t > 0.5:
1002 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
1003 if n == drawUpTill - 1:
1004 if len(self._gcodeVBOs[n]) < 9:
1005 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
1007 self._gcodeVBOs[n][8].render(GL_QUADS)
1008 glColor3f(c/2, 0, c)
1009 self._gcodeVBOs[n][9].render(GL_QUADS)
1010 glColor3f(0, c, c/2)
1011 self._gcodeVBOs[n][10].render(GL_QUADS)
1013 self._gcodeVBOs[n][11].render(GL_QUADS)
1016 self._gcodeVBOs[n][12].render(GL_QUADS)
1017 glColor3f(c/2, c/2, 0.0)
1018 self._gcodeVBOs[n][13].render(GL_QUADS)
1020 self._gcodeVBOs[n][14].render(GL_QUADS)
1021 self._gcodeVBOs[n][15].render(GL_QUADS)
1023 self._gcodeVBOs[n][16].render(GL_LINES)
1026 self._gcodeVBOs[n][0].render(GL_LINES)
1027 glColor3f(c/2, 0, c)
1028 self._gcodeVBOs[n][1].render(GL_LINES)
1029 glColor3f(0, c, c/2)
1030 self._gcodeVBOs[n][2].render(GL_LINES)
1032 self._gcodeVBOs[n][3].render(GL_LINES)
1035 self._gcodeVBOs[n][4].render(GL_LINES)
1036 glColor3f(c/2, c/2, 0.0)
1037 self._gcodeVBOs[n][5].render(GL_LINES)
1039 self._gcodeVBOs[n][6].render(GL_LINES)
1040 self._gcodeVBOs[n][7].render(GL_LINES)
1043 glStencilFunc(GL_ALWAYS, 1, 1)
1044 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1046 if self.viewMode == 'overhang':
1047 self._objectOverhangShader.bind()
1048 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1050 self._objectShader.bind()
1051 for obj in self._scene.objects():
1052 if obj._loadAnim is not None:
1053 if obj._loadAnim.isDone():
1054 obj._loadAnim = None
1058 if self._focusObj == obj:
1060 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1063 if self._selectedObj == obj or self._selectedObj is None:
1064 #If we want transparent, then first render a solid black model to remove the printer size lines.
1065 if self.viewMode == 'transparent':
1066 glColor4f(0, 0, 0, 0)
1067 self._renderObject(obj)
1069 glBlendFunc(GL_ONE, GL_ONE)
1070 glDisable(GL_DEPTH_TEST)
1072 if self.viewMode == 'xray':
1073 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1074 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1075 glEnable(GL_STENCIL_TEST)
1077 if self.viewMode == 'overhang':
1078 if self._selectedObj == obj and self.tempMatrix is not None:
1079 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1081 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1083 if not self._scene.checkPlatform(obj):
1084 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1085 self._renderObject(obj)
1087 self._renderObject(obj, brightness)
1088 glDisable(GL_STENCIL_TEST)
1090 glEnable(GL_DEPTH_TEST)
1091 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1093 if self.viewMode == 'xray':
1096 glEnable(GL_STENCIL_TEST)
1097 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1098 glDisable(GL_DEPTH_TEST)
1099 for i in xrange(2, 15, 2):
1100 glStencilFunc(GL_EQUAL, i, 0xFF)
1101 glColor(float(i)/10, float(i)/10, float(i)/5)
1103 glVertex3f(-1000,-1000,-10)
1104 glVertex3f( 1000,-1000,-10)
1105 glVertex3f( 1000, 1000,-10)
1106 glVertex3f(-1000, 1000,-10)
1108 for i in xrange(1, 15, 2):
1109 glStencilFunc(GL_EQUAL, i, 0xFF)
1110 glColor(float(i)/10, 0, 0)
1112 glVertex3f(-1000,-1000,-10)
1113 glVertex3f( 1000,-1000,-10)
1114 glVertex3f( 1000, 1000,-10)
1115 glVertex3f(-1000, 1000,-10)
1118 glDisable(GL_STENCIL_TEST)
1119 glEnable(GL_DEPTH_TEST)
1121 self._objectShader.unbind()
1123 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1125 if self._objectLoadShader is not None:
1126 self._objectLoadShader.bind()
1127 glColor4f(0.2, 0.6, 1.0, 1.0)
1128 for obj in self._scene.objects():
1129 if obj._loadAnim is None:
1131 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1132 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1133 self._renderObject(obj)
1134 self._objectLoadShader.unbind()
1139 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1141 z = self._usbPrintMonitor.getZ()
1142 size = self._machineSize
1143 glColor4ub(255,255,0,128)
1145 glVertex3f(-size[0]/2,-size[1]/2, z)
1146 glVertex3f( size[0]/2,-size[1]/2, z)
1147 glVertex3f( size[0]/2, size[1]/2, z)
1148 glVertex3f(-size[0]/2, size[1]/2, z)
1151 if self.viewMode == 'gcode':
1152 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1153 glDisable(GL_DEPTH_TEST)
1156 glTranslate(0,-4,-10)
1157 glColor4ub(60,60,60,255)
1158 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1161 #Draw the object box-shadow, so you can see where it will collide with other objects.
1162 if self._selectedObj is not None:
1164 glEnable(GL_CULL_FACE)
1165 glColor4f(0,0,0,0.16)
1167 for obj in self._scene.objects():
1169 glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1170 glBegin(GL_TRIANGLE_FAN)
1171 for p in obj._boundaryHull[::-1]:
1172 glVertex3f(p[0], p[1], 0)
1176 glColor4f(0,0,0,0.06)
1177 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1178 glBegin(GL_TRIANGLE_FAN)
1179 for p in self._selectedObj._printAreaHull[::-1]:
1180 glVertex3f(p[0], p[1], 0)
1182 glBegin(GL_TRIANGLE_FAN)
1183 for p in self._selectedObj._headAreaHull[::-1]:
1184 glVertex3f(p[0], p[1], 0)
1187 glDisable(GL_CULL_FACE)
1190 #Draw the outline of the selected object, on top of everything else except the GUI.
1191 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1192 glDisable(GL_DEPTH_TEST)
1193 glEnable(GL_CULL_FACE)
1194 glEnable(GL_STENCIL_TEST)
1196 glStencilFunc(GL_EQUAL, 0, 255)
1198 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1200 glColor4f(1,1,1,0.5)
1201 self._renderObject(self._selectedObj)
1202 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1204 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1205 glDisable(GL_STENCIL_TEST)
1206 glDisable(GL_CULL_FACE)
1207 glEnable(GL_DEPTH_TEST)
1209 if self._selectedObj is not None:
1211 pos = self.getObjectCenterPos()
1212 glTranslate(pos[0], pos[1], pos[2])
1215 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1216 glDisable(GL_DEPTH_TEST)
1219 glTranslate(0,-4,-10)
1220 glColor4ub(60,60,60,255)
1221 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1224 def _renderObject(self, obj, brightness = False, addSink = True):
1227 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1229 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1231 if self.tempMatrix is not None and obj == self._selectedObj:
1232 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1233 glMultMatrixf(tempMatrix)
1235 offset = obj.getDrawOffset()
1236 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1238 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1239 glMultMatrixf(tempMatrix)
1242 for m in obj._meshList:
1244 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1246 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1251 def _drawMachine(self):
1252 glEnable(GL_CULL_FACE)
1255 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1257 machine = profile.getMachineSetting('machine_type')
1258 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1259 if machine not in self._platformMesh:
1260 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1262 self._platformMesh[machine] = meshes[0]
1264 self._platformMesh[machine] = None
1265 if machine == 'ultimaker2':
1266 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1268 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1269 glColor4f(1,1,1,0.5)
1270 self._objectShader.bind()
1271 self._renderObject(self._platformMesh[machine], False, False)
1272 self._objectShader.unbind()
1274 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1275 if machine == 'ultimaker2':
1276 if not hasattr(self._platformMesh[machine], 'texture'):
1277 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1278 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1279 glEnable(GL_TEXTURE_2D)
1283 glTranslate(0,150,-5)
1288 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1291 glVertex3f( w, 0, h)
1293 glVertex3f(-w, 0, h)
1295 glVertex3f(-w, 0, 0)
1297 glVertex3f( w, 0, 0)
1300 glVertex3f(-w, d, h)
1302 glVertex3f( w, d, h)
1304 glVertex3f( w, d, 0)
1306 glVertex3f(-w, d, 0)
1308 glDisable(GL_TEXTURE_2D)
1309 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1315 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1316 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1317 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1318 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1319 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1320 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1323 #Cornerpoints for big blue square
1324 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1325 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1326 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1327 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1328 v4 = [ size[0] / 2, size[1] / 2, 0]
1329 v5 = [ size[0] / 2,-size[1] / 2, 0]
1330 v6 = [-size[0] / 2, size[1] / 2, 0]
1331 v7 = [-size[0] / 2,-size[1] / 2, 0]
1333 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1334 glEnableClientState(GL_VERTEX_ARRAY)
1335 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1338 glColor4ub(5, 171, 231, 64)
1339 glDrawArrays(GL_QUADS, 0, 4)
1340 glColor4ub(5, 171, 231, 96)
1341 glDrawArrays(GL_QUADS, 4, 8)
1342 glColor4ub(5, 171, 231, 128)
1343 glDrawArrays(GL_QUADS, 12, 8)
1344 glDisableClientState(GL_VERTEX_ARRAY)
1347 sx = self._machineSize[0]
1348 sy = self._machineSize[1]
1349 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1350 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1355 x1 = max(min(x1, sx/2), -sx/2)
1356 y1 = max(min(y1, sy/2), -sy/2)
1357 x2 = max(min(x2, sx/2), -sx/2)
1358 y2 = max(min(y2, sy/2), -sy/2)
1359 #Black or "white" checker
1360 if (x & 1) == (y & 1):
1361 glColor4ub(5, 171, 231, 127)
1363 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1365 glVertex3f(x1, y1, 0)
1366 glVertex3f(x2, y1, 0)
1367 glVertex3f(x2, y2, 0)
1368 glVertex3f(x1, y2, 0)
1371 if machine == 'ultimaker2':
1373 glColor4ub(127, 127, 127, 200)
1374 #if UM2, draw bat-area zone for head. THe head can't stop there, because its bat-area.
1378 posX = sx / 2 - clipWidth
1379 posY = sy / 2 - clipHeight
1381 glVertex3f(posX, posY, 0)
1382 glVertex3f(posX+clipWidth, posY, 0)
1383 glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1384 glVertex3f(posX, posY+clipHeight, 0)
1390 posY = sy / 2 - clipHeight
1392 glVertex3f(posX, posY, 0)
1393 glVertex3f(posX+clipWidth, posY, 0)
1394 glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1395 glVertex3f(posX, posY+clipHeight, 0)
1400 posX = sx / 2 - clipWidth
1403 glVertex3f(posX, posY, 0)
1404 glVertex3f(posX+clipWidth, posY, 0)
1405 glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1406 glVertex3f(posX, posY+clipHeight, 0)
1414 glVertex3f(posX, posY, 0)
1415 glVertex3f(posX+clipWidth, posY, 0)
1416 glVertex3f(posX+clipWidth, posY+clipHeight, 0)
1417 glVertex3f(posX, posY+clipHeight, 0)
1422 glDisable(GL_CULL_FACE)
1424 def _generateGCodeVBOs(self, layer):
1426 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1427 if ':' in extrudeType:
1428 extruder = int(extrudeType[extrudeType.find(':')+1:])
1429 extrudeType = extrudeType[0:extrudeType.find(':')]
1432 pointList = numpy.zeros((0,3), numpy.float32)
1434 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1436 a = numpy.concatenate((a[:-1], a[1:]), 1)
1437 a = a.reshape((len(a) * 2, 3))
1438 pointList = numpy.concatenate((pointList, a))
1439 ret.append(opengl.GLVBO(pointList))
1442 def _generateGCodeVBOs2(self, layer):
1443 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1444 filamentArea = math.pi * filamentRadius * filamentRadius
1445 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1448 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1449 if ':' in extrudeType:
1450 extruder = int(extrudeType[extrudeType.find(':')+1:])
1451 extrudeType = extrudeType[0:extrudeType.find(':')]
1454 pointList = numpy.zeros((0,3), numpy.float32)
1456 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1458 if extrudeType == 'FILL':
1461 normal = a[1:] - a[:-1]
1462 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1463 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1466 ePerDist = path['extrusion'][1:] / lens
1468 lineWidth = ePerDist / path['layerThickness'] / 2.0
1470 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1472 normal[:,0] *= lineWidth
1473 normal[:,1] *= lineWidth
1475 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1476 b = numpy.concatenate((b, a[1:] + normal), 1)
1477 b = numpy.concatenate((b, a[1:] - normal), 1)
1478 b = numpy.concatenate((b, a[:-1] - normal), 1)
1479 b = numpy.concatenate((b, a[:-1] + normal), 1)
1480 b = b.reshape((len(b) * 4, 3))
1483 normal2 = normal[:-1] + normal[1:]
1484 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1485 normal2[:,0] /= lens2
1486 normal2[:,1] /= lens2
1487 normal2[:,0] *= lineWidth[:-1]
1488 normal2[:,1] *= lineWidth[:-1]
1490 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1491 c = numpy.concatenate((c, a[1:-1]), 1)
1492 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1493 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1494 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1496 c = numpy.concatenate((c, a[1:-1]), 1)
1497 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1498 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1499 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1501 c = c.reshape((len(c) * 8, 3))
1503 pointList = numpy.concatenate((pointList, b, c))
1505 pointList = numpy.concatenate((pointList, b))
1506 ret.append(opengl.GLVBO(pointList))
1508 pointList = numpy.zeros((0,3), numpy.float32)
1510 if path['type'] == 'move':
1511 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1512 a = numpy.concatenate((a[:-1], a[1:]), 1)
1513 a = a.reshape((len(a) * 2, 3))
1514 pointList = numpy.concatenate((pointList, a))
1515 if path['type'] == 'retract':
1516 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1517 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1518 a = a.reshape((len(a) * 2, 3))
1519 pointList = numpy.concatenate((pointList, a))
1520 ret.append(opengl.GLVBO(pointList))
1524 def getObjectCenterPos(self):
1525 if self._selectedObj is None:
1526 return [0.0, 0.0, 0.0]
1527 pos = self._selectedObj.getPosition()
1528 size = self._selectedObj.getSize()
1529 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1531 def getObjectBoundaryCircle(self):
1532 if self._selectedObj is None:
1534 return self._selectedObj.getBoundaryCircle()
1536 def getObjectSize(self):
1537 if self._selectedObj is None:
1538 return [0.0, 0.0, 0.0]
1539 return self._selectedObj.getSize()
1541 def getObjectMatrix(self):
1542 if self._selectedObj is None:
1543 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1544 return self._selectedObj.getMatrix()
1546 class shaderEditor(wx.Dialog):
1547 def __init__(self, parent, callback, v, f):
1548 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1549 self._callback = callback
1550 s = wx.BoxSizer(wx.VERTICAL)
1552 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1553 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1554 s.Add(self._vertex, 1, flag=wx.EXPAND)
1555 s.Add(self._fragment, 1, flag=wx.EXPAND)
1557 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1558 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1560 self.SetPosition(self.GetParent().GetPosition())
1561 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1564 def OnText(self, e):
1565 self._callback(self._vertex.GetValue(), self._fragment.GetValue())