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.gui.util import previewTools
28 from Cura.gui.util import opengl
29 from Cura.gui.util import openglGui
30 from Cura.gui.tools import youmagineGui
31 from Cura.gui.tools import imageToMesh
33 class SceneView(openglGui.glGuiPanel):
34 def __init__(self, parent):
35 super(SceneView, self).__init__(parent)
40 self._scene = objectScene.Scene()
43 self._gcodeFilename = None
44 self._gcodeLoadThread = None
45 self._objectShader = None
46 self._objectLoadShader = None
48 self._selectedObj = None
49 self._objColors = [None,None,None,None]
52 self._mouseState = None
53 self._viewTarget = numpy.array([0,0,0], numpy.float32)
56 self._platformMesh = {}
57 self._isSimpleMode = True
58 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
61 self._modelMatrix = None
62 self._projMatrix = None
63 self.tempMatrix = None
65 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
66 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
67 self.printButton.setDisabled(True)
70 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
71 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
72 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
74 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
75 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
77 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
78 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
80 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
81 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
82 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
84 self.rotateToolButton.setExpandArrow(True)
85 self.scaleToolButton.setExpandArrow(True)
86 self.mirrorToolButton.setExpandArrow(True)
88 self.scaleForm = openglGui.glFrame(self, (2, -2))
89 openglGui.glGuiLayoutGrid(self.scaleForm)
90 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
91 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
92 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
93 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
94 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
95 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
96 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
97 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
98 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
99 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
100 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
101 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
102 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
103 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
105 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
106 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
108 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
109 self.youMagineButton.setDisabled(True)
111 self.notification = openglGui.glNotification(self, (0, 0))
113 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
114 self._sceneUpdateTimer = wx.Timer(self)
115 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
116 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
117 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
121 self.updateToolButtons()
122 self.updateProfileToControls()
124 def loadGCodeFile(self, filename):
125 self.OnDeleteAll(None)
126 if self._gcode is not None:
128 for layerVBOlist in self._gcodeVBOs:
129 for vbo in layerVBOlist:
130 self.glReleaseList.append(vbo)
132 self._gcode = gcodeInterpreter.gcode()
133 self._gcodeFilename = filename
134 self.printButton.setBottomText('')
135 self.viewSelection.setValue(4)
136 self.printButton.setDisabled(False)
137 self.youMagineButton.setDisabled(True)
140 def loadSceneFiles(self, filenames):
141 self.youMagineButton.setDisabled(False)
142 #if self.viewSelection.getValue() == 4:
143 # self.viewSelection.setValue(0)
144 # self.OnViewChange()
145 self.loadScene(filenames)
147 def loadFiles(self, filenames):
148 mainWindow = self.GetParent().GetParent().GetParent()
149 # only one GCODE file can be active
150 # so if single gcode file, process this
151 # otherwise ignore all gcode files
153 if len(filenames) == 1:
154 filename = filenames[0]
155 ext = os.path.splitext(filename)[1].lower()
156 if ext == '.g' or ext == '.gcode':
157 gcodeFilename = filename
158 mainWindow.addToModelMRU(filename)
159 if gcodeFilename is not None:
160 self.loadGCodeFile(gcodeFilename)
162 # process directories and special file types
163 # and keep scene files for later processing
165 ignored_types = dict()
166 # use file list as queue
167 # pop first entry for processing and append new files at end
169 filename = filenames.pop(0)
170 if os.path.isdir(filename):
171 # directory: queue all included files and directories
172 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
174 ext = os.path.splitext(filename)[1].lower()
176 profile.loadProfile(filename)
177 mainWindow.addToProfileMRU(filename)
178 elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
179 scene_filenames.append(filename)
180 mainWindow.addToModelMRU(filename)
182 ignored_types[ext] = 1
184 ignored_types = ignored_types.keys()
186 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
187 mainWindow.updateProfileToAllControls()
188 # now process all the scene files
190 self.loadSceneFiles(scene_filenames)
191 self._selectObject(None)
193 newZoom = numpy.max(self._machineSize)
194 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
195 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
197 def showLoadModel(self, button = 1):
199 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)
200 dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
201 if dlg.ShowModal() != wx.ID_OK:
204 filenames = dlg.GetPaths()
206 if len(filenames) < 1:
208 profile.putPreference('lastFile', filenames[0])
209 self.loadFiles(filenames)
211 def showSaveModel(self):
212 if len(self._scene.objects()) < 1:
214 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
215 dlg.SetWildcard(meshLoader.saveWildcardFilter())
216 if dlg.ShowModal() != wx.ID_OK:
219 filename = dlg.GetPath()
221 meshLoader.saveMeshes(filename, self._scene.objects())
223 def OnPrintButton(self, button):
225 if machineCom.machineIsConnected():
226 self.showPrintWindow()
227 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
228 drives = removableStorage.getPossibleSDcardDrives()
230 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))
231 if dlg.ShowModal() != wx.ID_OK:
234 drive = drives[dlg.GetSelection()]
238 filename = self._scene._objectList[0].getName() + '.gcode'
239 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
244 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
245 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
246 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
250 def showPrintWindow(self):
251 if self._gcodeFilename is None:
253 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
254 if self._gcodeFilename == self._slicer.getGCodeFilename():
255 self._slicer.submitSliceInfoOnline()
257 def showSaveGCode(self):
258 if len(self._scene._objectList) < 1:
260 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
261 filename = self._scene._objectList[0].getName() + '.gcode'
262 dlg.SetFilename(filename)
263 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
264 if dlg.ShowModal() != wx.ID_OK:
267 filename = dlg.GetPath()
270 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
272 def _copyFile(self, fileA, fileB, allowEject = False):
274 size = float(os.stat(fileA).st_size)
275 with open(fileA, 'rb') as fsrc:
276 with open(fileB, 'wb') as fdst:
278 buf = fsrc.read(16*1024)
282 self.printButton.setProgressBar(float(fsrc.tell()) / size)
287 self.notification.message("Failed to save")
290 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...'))
292 self.notification.message("Saved as %s" % (fileB))
293 self.printButton.setProgressBar(None)
294 if fileA == self._slicer.getGCodeFilename():
295 self._slicer.submitSliceInfoOnline()
297 def _showSliceLog(self):
298 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
302 def OnToolSelect(self, button):
303 if self.rotateToolButton.getSelected():
304 self.tool = previewTools.toolRotate(self)
305 elif self.scaleToolButton.getSelected():
306 self.tool = previewTools.toolScale(self)
307 elif self.mirrorToolButton.getSelected():
308 self.tool = previewTools.toolNone(self)
310 self.tool = previewTools.toolNone(self)
311 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
312 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
313 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
314 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
315 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
316 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
317 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
318 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
320 def updateToolButtons(self):
321 if self._selectedObj is None:
325 self.rotateToolButton.setHidden(hidden)
326 self.scaleToolButton.setHidden(hidden)
327 self.mirrorToolButton.setHidden(hidden)
329 self.rotateToolButton.setSelected(False)
330 self.scaleToolButton.setSelected(False)
331 self.mirrorToolButton.setSelected(False)
334 def OnViewChange(self):
335 if self.viewSelection.getValue() == 4:
336 self.viewMode = 'gcode'
337 if self._gcode is not None and self._gcode.layerList is not None:
338 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
339 self._selectObject(None)
340 elif self.viewSelection.getValue() == 1:
341 self.viewMode = 'overhang'
342 elif self.viewSelection.getValue() == 2:
343 self.viewMode = 'transparent'
344 elif self.viewSelection.getValue() == 3:
345 self.viewMode = 'xray'
347 self.viewMode = 'normal'
348 self.layerSelect.setHidden(self.viewMode != 'gcode')
351 def OnRotateReset(self, button):
352 if self._selectedObj is None:
354 self._selectedObj.resetRotation()
355 self._scene.pushFree()
356 self._selectObject(self._selectedObj)
359 def OnLayFlat(self, button):
360 if self._selectedObj is None:
362 self._selectedObj.layFlat()
363 self._scene.pushFree()
364 self._selectObject(self._selectedObj)
367 def OnScaleReset(self, button):
368 if self._selectedObj is None:
370 self._selectedObj.resetScale()
371 self._selectObject(self._selectedObj)
372 self.updateProfileToControls()
375 def OnScaleMax(self, button):
376 if self._selectedObj is None:
378 machine = profile.getMachineSetting('machine_type')
379 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
380 self._scene.pushFree()
382 if machine == "ultimaker2":
383 self._selectedObj.setPosition(numpy.array([0.0,-22.5]))
385 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
386 self._selectedObj.setPosition(numpy.array([0.0,-15]))
387 self._scene.pushFree()
389 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
390 self._scene.pushFree()
391 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
392 self._scene.pushFree()
393 self._selectObject(self._selectedObj)
394 self.updateProfileToControls()
397 def OnMirror(self, axis):
398 if self._selectedObj is None:
400 self._selectedObj.mirror(axis)
403 def OnScaleEntry(self, value, axis):
404 if self._selectedObj is None:
410 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
411 self.updateProfileToControls()
412 self._scene.pushFree()
413 self._selectObject(self._selectedObj)
416 def OnScaleEntryMM(self, value, axis):
417 if self._selectedObj is None:
423 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
424 self.updateProfileToControls()
425 self._scene.pushFree()
426 self._selectObject(self._selectedObj)
429 def OnDeleteAll(self, e):
430 while len(self._scene.objects()) > 0:
431 self._deleteObject(self._scene.objects()[0])
432 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
434 def OnMultiply(self, e):
435 if self._focusObj is None:
438 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
439 if dlg.ShowModal() != wx.ID_OK:
448 self._scene.add(newObj)
449 self._scene.centerAll()
450 if not self._scene.checkPlatform(newObj):
455 self.notification.message("Could not create more then %d items" % (n - 1))
456 self._scene.remove(newObj)
457 self._scene.centerAll()
460 def OnSplitObject(self, e):
461 if self._focusObj is None:
463 self._scene.remove(self._focusObj)
464 for obj in self._focusObj.split(self._splitCallback):
465 if numpy.max(obj.getSize()) > 2.0:
467 self._scene.centerAll()
468 self._selectObject(None)
471 def OnCenter(self, e):
472 if self._focusObj is None:
474 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
475 self._scene.pushFree()
476 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
477 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
480 def _splitCallback(self, progress):
483 def OnMergeObjects(self, e):
484 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
485 if len(self._scene.objects()) == 2:
486 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
489 self._scene.merge(self._selectedObj, self._focusObj)
492 def sceneUpdated(self):
493 self._sceneUpdateTimer.Start(500, True)
494 self._slicer.abortSlicer()
495 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
498 def _onRunSlicer(self, e):
499 if self._isSimpleMode:
500 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
501 self._slicer.runSlicer(self._scene)
502 if self._isSimpleMode:
503 profile.resetTempOverride()
505 def _updateSliceProgress(self, progressValue, ready):
507 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
509 self.printButton.setDisabled(not ready)
510 if progressValue >= 0.0:
511 self.printButton.setProgressBar(progressValue)
513 self.printButton.setProgressBar(None)
514 if self._gcode is not None:
516 for layerVBOlist in self._gcodeVBOs:
517 for vbo in layerVBOlist:
518 self.glReleaseList.append(vbo)
521 self.printButton.setProgressBar(None)
522 text = '%s' % (self._slicer.getPrintTime())
523 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
524 amount = self._slicer.getFilamentAmount(e)
527 text += '\n%s' % (amount)
528 cost = self._slicer.getFilamentCost(e)
530 text += '\n%s' % (cost)
531 self.printButton.setBottomText(text)
532 self._gcode = gcodeInterpreter.gcode()
533 self._gcodeFilename = self._slicer.getGCodeFilename()
535 self.printButton.setBottomText('')
538 def _loadGCode(self):
539 self._gcode.progressCallback = self._gcodeLoadCallback
540 self._gcode.load(self._gcodeFilename)
542 def _gcodeLoadCallback(self, progress):
543 if not self or self._gcode is None:
545 if len(self._gcode.layerList) % 15 == 0:
547 if self._gcode is None:
549 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
550 if self.viewMode == 'gcode':
554 def loadScene(self, fileList):
555 for filename in fileList:
557 ext = os.path.splitext(filename)[1].lower()
558 if ext in imageToMesh.supportedExtensions():
559 imageToMesh.convertImageDialog(self, filename).Show()
562 objList = meshLoader.loadMeshes(filename)
564 traceback.print_exc()
567 if self._objectLoadShader is not None:
568 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
572 self._scene.centerAll()
573 self._selectObject(obj)
574 if obj.getScale()[0] < 1.0:
575 self.notification.message("Warning: Object scaled down.")
578 def _deleteObject(self, obj):
579 if obj == self._selectedObj:
580 self._selectObject(None)
581 if obj == self._focusObj:
582 self._focusObj = None
583 self._scene.remove(obj)
584 for m in obj._meshList:
585 if m.vbo is not None and m.vbo.decRef():
586 self.glReleaseList.append(m.vbo)
591 def _selectObject(self, obj, zoom = True):
592 if obj != self._selectedObj:
593 self._selectedObj = obj
594 self.updateProfileToControls()
595 self.updateToolButtons()
596 if zoom and obj is not None:
597 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
598 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
599 newZoom = obj.getBoundaryCircle() * 6
600 if newZoom > numpy.max(self._machineSize) * 3:
601 newZoom = numpy.max(self._machineSize) * 3
602 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
604 def updateProfileToControls(self):
605 oldSimpleMode = self._isSimpleMode
606 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
607 if self._isSimpleMode != oldSimpleMode:
608 self._scene.arrangeAll()
610 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
611 self._objColors[0] = profile.getPreferenceColour('model_colour')
612 self._objColors[1] = profile.getPreferenceColour('model_colour2')
613 self._objColors[2] = profile.getPreferenceColour('model_colour3')
614 self._objColors[3] = profile.getPreferenceColour('model_colour4')
615 self._scene.setMachineSize(self._machineSize)
616 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
617 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'))
619 if self._selectedObj is not None:
620 scale = self._selectedObj.getScale()
621 size = self._selectedObj.getSize()
622 self.scaleXctrl.setValue(round(scale[0], 2))
623 self.scaleYctrl.setValue(round(scale[1], 2))
624 self.scaleZctrl.setValue(round(scale[2], 2))
625 self.scaleXmmctrl.setValue(round(size[0], 2))
626 self.scaleYmmctrl.setValue(round(size[1], 2))
627 self.scaleZmmctrl.setValue(round(size[2], 2))
629 def OnKeyChar(self, keyCode):
630 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
631 if self._selectedObj is not None:
632 self._deleteObject(self._selectedObj)
634 if keyCode == wx.WXK_UP:
635 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
637 elif keyCode == wx.WXK_DOWN:
638 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
640 elif keyCode == wx.WXK_PAGEUP:
641 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
643 elif keyCode == wx.WXK_PAGEDOWN:
644 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
647 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
648 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
649 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
650 from collections import defaultdict
651 from gc import get_objects
652 self._beforeLeakTest = defaultdict(int)
653 for i in get_objects():
654 self._beforeLeakTest[type(i)] += 1
655 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
656 from collections import defaultdict
657 from gc import get_objects
658 self._afterLeakTest = defaultdict(int)
659 for i in get_objects():
660 self._afterLeakTest[type(i)] += 1
661 for k in self._afterLeakTest:
662 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
663 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
665 def ShaderUpdate(self, v, f):
666 s = opengl.GLShader(v, f)
668 self._objectLoadShader.release()
669 self._objectLoadShader = s
670 for obj in self._scene.objects():
671 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
674 def OnMouseDown(self,e):
675 self._mouseX = e.GetX()
676 self._mouseY = e.GetY()
677 self._mouseClick3DPos = self._mouse3Dpos
678 self._mouseClickFocus = self._focusObj
680 self._mouseState = 'doubleClick'
682 self._mouseState = 'dragOrClick'
683 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
684 p0 -= self.getObjectCenterPos() - self._viewTarget
685 p1 -= self.getObjectCenterPos() - self._viewTarget
686 if self.tool.OnDragStart(p0, p1):
687 self._mouseState = 'tool'
688 if self._mouseState == 'dragOrClick':
689 if e.GetButton() == 1:
690 if self._focusObj is not None:
691 self._selectObject(self._focusObj, False)
694 def OnMouseUp(self, e):
695 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
697 if self._mouseState == 'dragOrClick':
698 if e.GetButton() == 1:
699 self._selectObject(self._focusObj)
700 if e.GetButton() == 3:
702 if self._focusObj is not None:
703 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
704 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
705 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
706 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
707 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:
708 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
709 if len(self._scene.objects()) > 0:
710 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
711 if menu.MenuItemCount > 0:
714 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
715 self._scene.pushFree()
717 elif self._mouseState == 'tool':
718 if self.tempMatrix is not None and self._selectedObj is not None:
719 self._selectedObj.applyMatrix(self.tempMatrix)
720 self._scene.pushFree()
721 self._selectObject(self._selectedObj)
722 self.tempMatrix = None
723 self.tool.OnDragEnd()
725 self._mouseState = None
727 def OnMouseMotion(self,e):
728 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
729 p0 -= self.getObjectCenterPos() - self._viewTarget
730 p1 -= self.getObjectCenterPos() - self._viewTarget
732 if e.Dragging() and self._mouseState is not None:
733 if self._mouseState == 'tool':
734 self.tool.OnDrag(p0, p1)
735 elif not e.LeftIsDown() and e.RightIsDown():
736 self._mouseState = 'drag'
737 if wx.GetKeyState(wx.WXK_SHIFT):
738 a = math.cos(math.radians(self._yaw)) / 3.0
739 b = math.sin(math.radians(self._yaw)) / 3.0
740 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
741 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
742 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
743 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
745 self._yaw += e.GetX() - self._mouseX
746 self._pitch -= e.GetY() - self._mouseY
747 if self._pitch > 170:
751 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
752 self._mouseState = 'drag'
753 self._zoom += e.GetY() - self._mouseY
756 if self._zoom > numpy.max(self._machineSize) * 3:
757 self._zoom = numpy.max(self._machineSize) * 3
758 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
759 self._mouseState = 'dragObject'
760 z = max(0, self._mouseClick3DPos[2])
761 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
762 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
767 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
768 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
769 diff = cursorZ1 - cursorZ0
770 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
771 if not e.Dragging() or self._mouseState != 'tool':
772 self.tool.OnMouseMove(p0, p1)
774 self._mouseX = e.GetX()
775 self._mouseY = e.GetY()
777 def OnMouseWheel(self, e):
778 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
779 delta = max(min(delta,4),-4)
780 self._zoom *= 1.0 - delta / 10.0
783 if self._zoom > numpy.max(self._machineSize) * 3:
784 self._zoom = numpy.max(self._machineSize) * 3
787 def OnMouseLeave(self, e):
791 def getMouseRay(self, x, y):
792 if self._viewport is None:
793 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
794 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
795 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
796 p0 -= self._viewTarget
797 p1 -= self._viewTarget
800 def _init3DView(self):
801 # set viewing projection
802 size = self.GetSize()
803 glViewport(0, 0, size.GetWidth(), size.GetHeight())
806 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
808 glDisable(GL_RESCALE_NORMAL)
809 glDisable(GL_LIGHTING)
811 glEnable(GL_DEPTH_TEST)
812 glDisable(GL_CULL_FACE)
814 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
816 glClearColor(0.8, 0.8, 0.8, 1.0)
820 glMatrixMode(GL_PROJECTION)
822 aspect = float(size.GetWidth()) / float(size.GetHeight())
823 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
825 glMatrixMode(GL_MODELVIEW)
827 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
830 if machineCom.machineIsConnected():
831 self.printButton._imageID = 6
832 self.printButton._tooltip = _("Print")
833 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
834 self.printButton._imageID = 2
835 self.printButton._tooltip = _("Toolpath to SD")
837 self.printButton._imageID = 3
838 self.printButton._tooltip = _("Save toolpath")
840 if self._animView is not None:
841 self._viewTarget = self._animView.getPosition()
842 if self._animView.isDone():
843 self._animView = None
844 if self._animZoom is not None:
845 self._zoom = self._animZoom.getPosition()
846 if self._animZoom.isDone():
847 self._animZoom = None
848 if self.viewMode == 'gcode' and self._gcode is not None:
850 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
853 if self._objectShader is None:
854 if opengl.hasShaderSupport():
855 self._objectShader = opengl.GLShader("""
856 varying float light_amount;
860 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
861 gl_FrontColor = gl_Color;
863 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
867 varying float light_amount;
871 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
874 self._objectOverhangShader = opengl.GLShader("""
875 uniform float cosAngle;
876 uniform mat3 rotMatrix;
877 varying float light_amount;
881 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
882 gl_FrontColor = gl_Color;
884 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
886 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
888 light_amount = -10.0;
892 varying float light_amount;
896 if (light_amount == -10.0)
898 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
900 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
904 self._objectLoadShader = opengl.GLShader("""
905 uniform float intensity;
907 varying float light_amount;
911 vec4 tmp = gl_Vertex;
912 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
913 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
914 gl_Position = gl_ModelViewProjectionMatrix * tmp;
915 gl_FrontColor = gl_Color;
917 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
921 uniform float intensity;
922 varying float light_amount;
926 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
929 if self._objectShader is None or not self._objectShader.isValid():
930 self._objectShader = opengl.GLFakeShader()
931 self._objectOverhangShader = opengl.GLFakeShader()
932 self._objectLoadShader = None
934 glTranslate(0,0,-self._zoom)
935 glRotate(-self._pitch, 1,0,0)
936 glRotate(self._yaw, 0,0,1)
937 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
939 self._viewport = glGetIntegerv(GL_VIEWPORT)
940 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
941 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
943 glClearColor(1,1,1,1)
944 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
946 if self.viewMode != 'gcode':
947 for n in xrange(0, len(self._scene.objects())):
948 obj = self._scene.objects()[n]
949 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
950 self._renderObject(obj)
952 if self._mouseX > -1:
954 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
955 if n < len(self._scene.objects()):
956 self._focusObj = self._scene.objects()[n]
958 self._focusObj = None
959 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
960 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
961 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
962 self._mouse3Dpos -= self._viewTarget
965 glTranslate(0,0,-self._zoom)
966 glRotate(-self._pitch, 1,0,0)
967 glRotate(self._yaw, 0,0,1)
968 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
970 if self.viewMode == 'gcode':
971 if self._gcode is not None and self._gcode.layerList is None:
972 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
973 self._gcodeLoadThread.daemon = True
974 self._gcodeLoadThread.start()
975 if self._gcode is not None and self._gcode.layerList is not None:
977 if profile.getMachineSetting('machine_center_is_zero') != 'True':
978 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
980 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
981 for n in xrange(0, drawUpTill):
982 c = 1.0 - float(drawUpTill - n) / 15
984 if len(self._gcodeVBOs) < n + 1:
985 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
986 if time.time() - t > 0.5:
989 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
990 if n == drawUpTill - 1:
991 if len(self._gcodeVBOs[n]) < 9:
992 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
994 self._gcodeVBOs[n][8].render(GL_QUADS)
996 self._gcodeVBOs[n][9].render(GL_QUADS)
998 self._gcodeVBOs[n][10].render(GL_QUADS)
1000 self._gcodeVBOs[n][11].render(GL_QUADS)
1003 self._gcodeVBOs[n][12].render(GL_QUADS)
1004 glColor3f(c/2, c/2, 0.0)
1005 self._gcodeVBOs[n][13].render(GL_QUADS)
1007 self._gcodeVBOs[n][14].render(GL_QUADS)
1008 self._gcodeVBOs[n][15].render(GL_QUADS)
1010 self._gcodeVBOs[n][16].render(GL_LINES)
1013 self._gcodeVBOs[n][0].render(GL_LINES)
1014 glColor3f(c/2, 0, c)
1015 self._gcodeVBOs[n][1].render(GL_LINES)
1016 glColor3f(0, c, c/2)
1017 self._gcodeVBOs[n][2].render(GL_LINES)
1019 self._gcodeVBOs[n][3].render(GL_LINES)
1022 self._gcodeVBOs[n][4].render(GL_LINES)
1023 glColor3f(c/2, c/2, 0.0)
1024 self._gcodeVBOs[n][5].render(GL_LINES)
1026 self._gcodeVBOs[n][6].render(GL_LINES)
1027 self._gcodeVBOs[n][7].render(GL_LINES)
1030 glStencilFunc(GL_ALWAYS, 1, 1)
1031 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1033 if self.viewMode == 'overhang':
1034 self._objectOverhangShader.bind()
1035 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1037 self._objectShader.bind()
1038 for obj in self._scene.objects():
1039 if obj._loadAnim is not None:
1040 if obj._loadAnim.isDone():
1041 obj._loadAnim = None
1045 if self._focusObj == obj:
1047 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1050 if self._selectedObj == obj or self._selectedObj is None:
1051 #If we want transparent, then first render a solid black model to remove the printer size lines.
1052 if self.viewMode == 'transparent':
1053 glColor4f(0, 0, 0, 0)
1054 self._renderObject(obj)
1056 glBlendFunc(GL_ONE, GL_ONE)
1057 glDisable(GL_DEPTH_TEST)
1059 if self.viewMode == 'xray':
1060 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1061 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1062 glEnable(GL_STENCIL_TEST)
1064 if self.viewMode == 'overhang':
1065 if self._selectedObj == obj and self.tempMatrix is not None:
1066 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1068 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1070 if not self._scene.checkPlatform(obj):
1071 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1072 self._renderObject(obj)
1074 self._renderObject(obj, brightness)
1075 glDisable(GL_STENCIL_TEST)
1077 glEnable(GL_DEPTH_TEST)
1078 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1080 if self.viewMode == 'xray':
1083 glEnable(GL_STENCIL_TEST)
1084 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1085 glDisable(GL_DEPTH_TEST)
1086 for i in xrange(2, 15, 2):
1087 glStencilFunc(GL_EQUAL, i, 0xFF)
1088 glColor(float(i)/10, float(i)/10, float(i)/5)
1090 glVertex3f(-1000,-1000,-10)
1091 glVertex3f( 1000,-1000,-10)
1092 glVertex3f( 1000, 1000,-10)
1093 glVertex3f(-1000, 1000,-10)
1095 for i in xrange(1, 15, 2):
1096 glStencilFunc(GL_EQUAL, i, 0xFF)
1097 glColor(float(i)/10, 0, 0)
1099 glVertex3f(-1000,-1000,-10)
1100 glVertex3f( 1000,-1000,-10)
1101 glVertex3f( 1000, 1000,-10)
1102 glVertex3f(-1000, 1000,-10)
1105 glDisable(GL_STENCIL_TEST)
1106 glEnable(GL_DEPTH_TEST)
1108 self._objectShader.unbind()
1110 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1112 if self._objectLoadShader is not None:
1113 self._objectLoadShader.bind()
1114 glColor4f(0.2, 0.6, 1.0, 1.0)
1115 for obj in self._scene.objects():
1116 if obj._loadAnim is None:
1118 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1119 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1120 self._renderObject(obj)
1121 self._objectLoadShader.unbind()
1126 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1128 z = self._usbPrintMonitor.getZ()
1129 size = self._machineSize
1130 glColor4ub(255,255,0,128)
1132 glVertex3f(-size[0]/2,-size[1]/2, z)
1133 glVertex3f( size[0]/2,-size[1]/2, z)
1134 glVertex3f( size[0]/2, size[1]/2, z)
1135 glVertex3f(-size[0]/2, size[1]/2, z)
1138 if self.viewMode == 'gcode':
1139 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1140 glDisable(GL_DEPTH_TEST)
1143 glTranslate(0,-4,-10)
1144 glColor4ub(60,60,60,255)
1145 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1148 #Draw the object box-shadow, so you can see where it will collide with other objects.
1149 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1150 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1152 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1154 glEnable(GL_CULL_FACE)
1155 glColor4f(0,0,0,0.12)
1157 glVertex3f(-size[0], size[1], 0.1)
1158 glVertex3f(-size[0], -size[1], 0.1)
1159 glVertex3f( size[0], -size[1], 0.1)
1160 glVertex3f( size[0], size[1], 0.1)
1162 glDisable(GL_CULL_FACE)
1165 #Draw the outline of the selected object, on top of everything else except the GUI.
1166 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1167 glDisable(GL_DEPTH_TEST)
1168 glEnable(GL_CULL_FACE)
1169 glEnable(GL_STENCIL_TEST)
1171 glStencilFunc(GL_EQUAL, 0, 255)
1173 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1175 glColor4f(1,1,1,0.5)
1176 self._renderObject(self._selectedObj)
1177 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1179 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1180 glDisable(GL_STENCIL_TEST)
1181 glDisable(GL_CULL_FACE)
1182 glEnable(GL_DEPTH_TEST)
1184 if self._selectedObj is not None:
1186 pos = self.getObjectCenterPos()
1187 glTranslate(pos[0], pos[1], pos[2])
1190 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1191 glDisable(GL_DEPTH_TEST)
1194 glTranslate(0,-4,-10)
1195 glColor4ub(60,60,60,255)
1196 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1199 def _renderObject(self, obj, brightness = False, addSink = True):
1202 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1204 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1206 if self.tempMatrix is not None and obj == self._selectedObj:
1207 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1208 glMultMatrixf(tempMatrix)
1210 offset = obj.getDrawOffset()
1211 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1213 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1214 glMultMatrixf(tempMatrix)
1217 for m in obj._meshList:
1219 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1221 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1226 def _drawMachine(self):
1227 glEnable(GL_CULL_FACE)
1230 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1232 machine = profile.getMachineSetting('machine_type')
1233 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1234 if machine not in self._platformMesh:
1235 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1237 self._platformMesh[machine] = meshes[0]
1239 self._platformMesh[machine] = None
1240 if machine == 'ultimaker2':
1241 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1243 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1244 glColor4f(1,1,1,0.5)
1245 self._objectShader.bind()
1246 self._renderObject(self._platformMesh[machine], False, False)
1247 self._objectShader.unbind()
1249 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1250 if machine == 'ultimaker2':
1251 if not hasattr(self._platformMesh[machine], 'texture'):
1252 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1253 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1254 glEnable(GL_TEXTURE_2D)
1258 glTranslate(0,150,-5)
1263 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1266 glVertex3f( w, 0, h)
1268 glVertex3f(-w, 0, h)
1270 glVertex3f(-w, 0, 0)
1272 glVertex3f( w, 0, 0)
1275 glVertex3f(-w, d, h)
1277 glVertex3f( w, d, h)
1279 glVertex3f( w, d, 0)
1281 glVertex3f(-w, d, 0)
1283 glDisable(GL_TEXTURE_2D)
1284 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1290 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1291 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1292 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1293 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1294 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1295 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1298 #Cornerpoints for big blue square
1299 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1300 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1301 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1302 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1303 v4 = [ size[0] / 2, size[1] / 2, 0]
1304 v5 = [ size[0] / 2,-size[1] / 2, 0]
1305 v6 = [-size[0] / 2, size[1] / 2, 0]
1306 v7 = [-size[0] / 2,-size[1] / 2, 0]
1308 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1309 glEnableClientState(GL_VERTEX_ARRAY)
1310 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1312 glColor4ub(5, 171, 231, 64)
1313 glDrawArrays(GL_QUADS, 0, 4)
1314 glColor4ub(5, 171, 231, 96)
1315 glDrawArrays(GL_QUADS, 4, 8)
1316 glColor4ub(5, 171, 231, 128)
1317 glDrawArrays(GL_QUADS, 12, 8)
1318 glDisableClientState(GL_VERTEX_ARRAY)
1321 sx = self._machineSize[0]
1322 sy = self._machineSize[1]
1323 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1324 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1329 x1 = max(min(x1, sx/2), -sx/2)
1330 y1 = max(min(y1, sy/2), -sy/2)
1331 x2 = max(min(x2, sx/2), -sx/2)
1332 y2 = max(min(y2, sy/2), -sy/2)
1333 #Black or "white" checker
1334 if (x & 1) == (y & 1):
1335 glColor4ub(5, 171, 231, 127)
1337 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1339 glVertex3f(x1, y1, -0.02) #Draw bit below z0 to prevent zfighting.
1340 glVertex3f(x2, y1, -0.02)
1341 glVertex3f(x2, y2, -0.02)
1342 glVertex3f(x1, y2, -0.02)
1345 if machine == 'ultimaker2':
1347 glColor4ub(127, 127, 127, 200)
1348 #if UM2, draw bat-area zone for head. THe head can't stop there, because its bat-area.
1352 posX = sx / 2 - clipWidth;
1353 posY = sy / 2 - clipHeight;
1355 glVertex3f(posX, posY, 0.1)
1356 glVertex3f(posX+clipWidth, posY, 0.1)
1357 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1358 glVertex3f(posX, posY+clipHeight, 0.1)
1364 posY = sy / 2 - clipHeight;
1366 glVertex3f(posX, posY, 0.1)
1367 glVertex3f(posX+clipWidth, posY, 0.1)
1368 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1369 glVertex3f(posX, posY+clipHeight, 0.1)
1374 posX = sx / 2 - clipWidth;
1377 glVertex3f(posX, posY, 0.1)
1378 glVertex3f(posX+clipWidth, posY, 0.1)
1379 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1380 glVertex3f(posX, posY+clipHeight, 0.1)
1388 glVertex3f(posX, posY, 0.1)
1389 glVertex3f(posX+clipWidth, posY, 0.1)
1390 glVertex3f(posX+clipWidth, posY+clipHeight, 0.1)
1391 glVertex3f(posX, posY+clipHeight, 0.1)
1397 glDisable(GL_CULL_FACE)
1399 def _generateGCodeVBOs(self, layer):
1401 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1402 if ':' in extrudeType:
1403 extruder = int(extrudeType[extrudeType.find(':')+1:])
1404 extrudeType = extrudeType[0:extrudeType.find(':')]
1407 pointList = numpy.zeros((0,3), numpy.float32)
1409 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1411 a = numpy.concatenate((a[:-1], a[1:]), 1)
1412 a = a.reshape((len(a) * 2, 3))
1413 pointList = numpy.concatenate((pointList, a))
1414 ret.append(opengl.GLVBO(pointList))
1417 def _generateGCodeVBOs2(self, layer):
1418 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1419 filamentArea = math.pi * filamentRadius * filamentRadius
1420 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1423 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1424 if ':' in extrudeType:
1425 extruder = int(extrudeType[extrudeType.find(':')+1:])
1426 extrudeType = extrudeType[0:extrudeType.find(':')]
1429 pointList = numpy.zeros((0,3), numpy.float32)
1431 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1433 if extrudeType == 'FILL':
1436 normal = a[1:] - a[:-1]
1437 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1438 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1441 ePerDist = path['extrusion'][1:] / lens
1443 lineWidth = ePerDist / path['layerThickness'] / 2.0
1445 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1447 normal[:,0] *= lineWidth
1448 normal[:,1] *= lineWidth
1450 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1451 b = numpy.concatenate((b, a[1:] + normal), 1)
1452 b = numpy.concatenate((b, a[1:] - normal), 1)
1453 b = numpy.concatenate((b, a[:-1] - normal), 1)
1454 b = numpy.concatenate((b, a[:-1] + normal), 1)
1455 b = b.reshape((len(b) * 4, 3))
1458 normal2 = normal[:-1] + normal[1:]
1459 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1460 normal2[:,0] /= lens2
1461 normal2[:,1] /= lens2
1462 normal2[:,0] *= lineWidth[:-1]
1463 normal2[:,1] *= lineWidth[:-1]
1465 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1466 c = numpy.concatenate((c, a[1:-1]), 1)
1467 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1468 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1469 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1471 c = numpy.concatenate((c, a[1:-1]), 1)
1472 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1473 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1474 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1476 c = c.reshape((len(c) * 8, 3))
1478 pointList = numpy.concatenate((pointList, b, c))
1480 pointList = numpy.concatenate((pointList, b))
1481 ret.append(opengl.GLVBO(pointList))
1483 pointList = numpy.zeros((0,3), numpy.float32)
1485 if path['type'] == 'move':
1486 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1487 a = numpy.concatenate((a[:-1], a[1:]), 1)
1488 a = a.reshape((len(a) * 2, 3))
1489 pointList = numpy.concatenate((pointList, a))
1490 if path['type'] == 'retract':
1491 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1492 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1493 a = a.reshape((len(a) * 2, 3))
1494 pointList = numpy.concatenate((pointList, a))
1495 ret.append(opengl.GLVBO(pointList))
1499 def getObjectCenterPos(self):
1500 if self._selectedObj is None:
1501 return [0.0, 0.0, 0.0]
1502 pos = self._selectedObj.getPosition()
1503 size = self._selectedObj.getSize()
1504 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1506 def getObjectBoundaryCircle(self):
1507 if self._selectedObj is None:
1509 return self._selectedObj.getBoundaryCircle()
1511 def getObjectSize(self):
1512 if self._selectedObj is None:
1513 return [0.0, 0.0, 0.0]
1514 return self._selectedObj.getSize()
1516 def getObjectMatrix(self):
1517 if self._selectedObj is None:
1518 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1519 return self._selectedObj.getMatrix()
1521 class shaderEditor(wx.Dialog):
1522 def __init__(self, parent, callback, v, f):
1523 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1524 self._callback = callback
1525 s = wx.BoxSizer(wx.VERTICAL)
1527 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1528 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1529 s.Add(self._vertex, 1, flag=wx.EXPAND)
1530 s.Add(self._fragment, 1, flag=wx.EXPAND)
1532 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1533 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1535 self.SetPosition(self.GetParent().GetPosition())
1536 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1539 def OnText(self, e):
1540 self._callback(self._vertex.GetValue(), self._fragment.GetValue())