1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
11 import cStringIO as StringIO
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 pluginInfo
25 from Cura.util import removableStorage
26 from Cura.util import explorer
27 from Cura.util.printerConnection import printerConnectionManager
28 from Cura.gui.util import previewTools
29 from Cura.gui.util import openglHelpers
30 from Cura.gui.util import openglGui
31 from Cura.gui.util import engineResultView
32 from Cura.gui.tools import youmagineGui
33 from Cura.gui.tools import imageToMesh
35 class SceneView(openglGui.glGuiPanel):
36 def __init__(self, parent):
37 super(SceneView, self).__init__(parent)
42 self._scene = objectScene.Scene()
43 self._objectShader = None
44 self._objectLoadShader = None
46 self._selectedObj = None
47 self._objColors = [None,None,None,None]
50 self._mouseState = None
51 self._viewTarget = numpy.array([0,0,0], numpy.float32)
54 self._platformMesh = {}
55 self._platformTexture = None
56 self._isSimpleMode = True
57 self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager()
60 self._modelMatrix = None
61 self._projMatrix = None
62 self.tempMatrix = None
64 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
65 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
66 self.printButton.setDisabled(True)
69 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
70 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
71 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
73 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
74 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
76 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
77 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
79 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
80 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
81 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
83 self.rotateToolButton.setExpandArrow(True)
84 self.scaleToolButton.setExpandArrow(True)
85 self.mirrorToolButton.setExpandArrow(True)
87 self.scaleForm = openglGui.glFrame(self, (2, -2))
88 openglGui.glGuiLayoutGrid(self.scaleForm)
89 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
90 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
91 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
92 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
93 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
94 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
95 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
96 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
97 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
98 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
99 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
100 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
101 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
102 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
104 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
106 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
107 self.youMagineButton.setDisabled(True)
109 self.notification = openglGui.glNotification(self, (0, 0))
111 self._engine = sliceEngine.Engine(self._updateEngineProgress)
112 self._engineResultView = engineResultView.engineResultView(self)
113 self._sceneUpdateTimer = wx.Timer(self)
114 self.Bind(wx.EVT_TIMER, self._onRunEngine, self._sceneUpdateTimer)
115 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
116 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
120 self.updateToolButtons()
121 self.updateProfileToControls()
123 def loadGCodeFile(self, filename):
124 self.OnDeleteAll(None)
125 #Cheat the engine results to load a GCode file into it.
126 self._engine._result = sliceEngine.EngineResult()
127 with open(filename, "r") as f:
128 self._engine._result.setGCode(f.read())
129 self._engine._result.setFinished(True)
130 self._engineResultView.setResult(self._engine._result)
131 self.printButton.setBottomText('')
132 self.viewSelection.setValue(4)
133 self.printButton.setDisabled(False)
134 self.youMagineButton.setDisabled(True)
137 def loadSceneFiles(self, filenames):
138 self.youMagineButton.setDisabled(False)
139 #if self.viewSelection.getValue() == 4:
140 # self.viewSelection.setValue(0)
141 # self.OnViewChange()
142 self.loadScene(filenames)
144 def loadFiles(self, filenames):
145 mainWindow = self.GetParent().GetParent().GetParent()
146 # only one GCODE file can be active
147 # so if single gcode file, process this
148 # otherwise ignore all gcode files
150 if len(filenames) == 1:
151 filename = filenames[0]
152 ext = os.path.splitext(filename)[1].lower()
153 if ext == '.g' or ext == '.gcode':
154 gcodeFilename = filename
155 mainWindow.addToModelMRU(filename)
156 if gcodeFilename is not None:
157 self.loadGCodeFile(gcodeFilename)
159 # process directories and special file types
160 # and keep scene files for later processing
162 ignored_types = dict()
163 # use file list as queue
164 # pop first entry for processing and append new files at end
166 filename = filenames.pop(0)
167 if os.path.isdir(filename):
168 # directory: queue all included files and directories
169 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
171 ext = os.path.splitext(filename)[1].lower()
173 profile.loadProfile(filename)
174 mainWindow.addToProfileMRU(filename)
175 elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
176 scene_filenames.append(filename)
177 mainWindow.addToModelMRU(filename)
179 ignored_types[ext] = 1
181 ignored_types = ignored_types.keys()
183 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
184 mainWindow.updateProfileToAllControls()
185 # now process all the scene files
187 self.loadSceneFiles(scene_filenames)
188 self._selectObject(None)
190 newZoom = numpy.max(self._machineSize)
191 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
192 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
194 def reloadScene(self, e):
195 # Copy the list before DeleteAll clears it
197 for obj in self._scene.objects():
198 fileList.append(obj.getOriginFilename())
199 self.OnDeleteAll(None)
200 self.loadScene(fileList)
202 def showLoadModel(self, button = 1):
204 dlg=wx.FileDialog(self, _("Open 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
206 wildcardList = ';'.join(map(lambda s: '*' + s, meshLoader.loadSupportedExtensions() + imageToMesh.supportedExtensions() + ['.g', '.gcode']))
207 wildcardFilter = "All (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
208 wildcardList = ';'.join(map(lambda s: '*' + s, meshLoader.loadSupportedExtensions()))
209 wildcardFilter += "|Mesh files (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
210 wildcardList = ';'.join(map(lambda s: '*' + s, imageToMesh.supportedExtensions()))
211 wildcardFilter += "|Image files (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
212 wildcardList = ';'.join(map(lambda s: '*' + s, ['.g', '.gcode']))
213 wildcardFilter += "|GCode files (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
215 dlg.SetWildcard(wildcardFilter)
216 if dlg.ShowModal() != wx.ID_OK:
219 filenames = dlg.GetPaths()
221 if len(filenames) < 1:
223 profile.putPreference('lastFile', filenames[0])
224 self.loadFiles(filenames)
226 def showSaveModel(self):
227 if len(self._scene.objects()) < 1:
229 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
230 fileExtensions = meshLoader.saveSupportedExtensions()
231 wildcardList = ';'.join(map(lambda s: '*' + s, fileExtensions))
232 wildcardFilter = "Mesh files (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
233 dlg.SetWildcard(wildcardFilter)
234 if dlg.ShowModal() != wx.ID_OK:
237 filename = dlg.GetPath()
239 meshLoader.saveMeshes(filename, self._scene.objects())
241 def OnPrintButton(self, button):
243 connectionGroup = self._printerConnectionManager.getAvailableGroup()
244 if len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
245 drives = removableStorage.getPossibleSDcardDrives()
247 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))
248 if dlg.ShowModal() != wx.ID_OK:
251 drive = drives[dlg.GetSelection()]
255 filename = self._scene._objectList[0].getName() + '.gcode'
256 threading.Thread(target=self._saveGCode,args=(drive[1] + filename, drive[1])).start()
257 elif connectionGroup is not None:
258 connections = connectionGroup.getAvailableConnections()
259 if len(connections) < 2:
260 connection = connections[0]
262 dlg = wx.SingleChoiceDialog(self, "Select the %s connection to use" % (connectionGroup.getName()), "Multiple %s connections found" % (connectionGroup.getName()), map(lambda n: n.getName(), connections))
263 if dlg.ShowModal() != wx.ID_OK:
266 connection = connections[dlg.GetSelection()]
268 self._openPrintWindowForConnection(connection)
273 connections = self._printerConnectionManager.getAvailableConnections()
274 menu.connectionMap = {}
275 for connection in connections:
276 i = menu.Append(-1, _("Print with %s") % (connection.getName()))
277 menu.connectionMap[i.GetId()] = connection
278 self.Bind(wx.EVT_MENU, lambda e: self._openPrintWindowForConnection(e.GetEventObject().connectionMap[e.GetId()]), i)
279 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
280 self.Bind(wx.EVT_MENU, lambda e: self._showEngineLog(), menu.Append(-1, _("Slice engine log...")))
284 def _openPrintWindowForConnection(self, connection):
285 if connection.window is None or not connection.window:
286 connection.window = None
287 windowType = profile.getPreference('printing_window')
288 for p in pluginInfo.getPluginList('printwindow'):
289 if p.getName() == windowType:
290 connection.window = printWindow.printWindowPlugin(self, connection, p.getFullFilename())
292 if connection.window is None:
293 connection.window = printWindow.printWindowBasic(self, connection)
294 connection.window.Show()
295 connection.window.Raise()
296 if not connection.loadGCodeData(StringIO.StringIO(self._engine.getResult().getGCode())):
297 if connection.isPrinting():
298 self.notification.message("Cannot start print, because other print still running.")
300 self.notification.message("Failed to start print...")
302 def showSaveGCode(self):
303 if len(self._scene._objectList) < 1:
305 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
306 filename = self._scene._objectList[0].getName() + '.gcode'
307 dlg.SetFilename(filename)
308 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
309 if dlg.ShowModal() != wx.ID_OK:
312 filename = dlg.GetPath()
315 threading.Thread(target=self._saveGCode,args=(filename,)).start()
317 def _saveGCode(self, targetFilename, ejectDrive = False):
318 data = self._engine.getResult().getGCode()
320 size = float(len(data))
321 fsrc = StringIO.StringIO(data)
322 with open(targetFilename, 'wb') as fdst:
324 buf = fsrc.read(16*1024)
328 self.printButton.setProgressBar(float(fsrc.tell()) / size)
331 import sys, traceback
332 traceback.print_exc()
333 self.notification.message("Failed to save")
336 self.notification.message("Saved as %s" % (targetFilename), lambda : self._doEjectSD(ejectDrive), 31, 'Eject')
337 elif explorer.hasExplorer():
338 self.notification.message("Saved as %s" % (targetFilename), lambda : explorer.openExplorer(targetFilename), 4, 'Open folder')
340 self.notification.message("Saved as %s" % (targetFilename))
341 self.printButton.setProgressBar(None)
342 self._engine.getResult().submitInfoOnline()
344 def _doEjectSD(self, drive):
345 if removableStorage.ejectDrive(drive):
346 self.notification.message('You can now eject the card.')
348 self.notification.message('Safe remove failed...')
350 def _showEngineLog(self):
351 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._engine.getResult().getLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
355 def OnToolSelect(self, button):
356 if self.rotateToolButton.getSelected():
357 self.tool = previewTools.toolRotate(self)
358 elif self.scaleToolButton.getSelected():
359 self.tool = previewTools.toolScale(self)
360 elif self.mirrorToolButton.getSelected():
361 self.tool = previewTools.toolNone(self)
363 self.tool = previewTools.toolNone(self)
364 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
365 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
366 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
367 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
368 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
369 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
370 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
371 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
373 def updateToolButtons(self):
374 if self._selectedObj is None:
378 self.rotateToolButton.setHidden(hidden)
379 self.scaleToolButton.setHidden(hidden)
380 self.mirrorToolButton.setHidden(hidden)
382 self.rotateToolButton.setSelected(False)
383 self.scaleToolButton.setSelected(False)
384 self.mirrorToolButton.setSelected(False)
387 def OnViewChange(self):
388 if self.viewSelection.getValue() == 4:
389 self.viewMode = 'gcode'
390 self.tool = previewTools.toolNone(self)
391 elif self.viewSelection.getValue() == 1:
392 self.viewMode = 'overhang'
393 elif self.viewSelection.getValue() == 2:
394 self.viewMode = 'transparent'
395 elif self.viewSelection.getValue() == 3:
396 self.viewMode = 'xray'
398 self.viewMode = 'normal'
399 self._engineResultView.setEnabled(self.viewMode == 'gcode')
402 def OnRotateReset(self, button):
403 if self._selectedObj is None:
405 self._selectedObj.resetRotation()
406 self._scene.pushFree(self._selectedObj)
407 self._selectObject(self._selectedObj)
410 def OnLayFlat(self, button):
411 if self._selectedObj is None:
413 self._selectedObj.layFlat()
414 self._scene.pushFree(self._selectedObj)
415 self._selectObject(self._selectedObj)
418 def OnScaleReset(self, button):
419 if self._selectedObj is None:
421 self._selectedObj.resetScale()
422 self._selectObject(self._selectedObj)
423 self.updateProfileToControls()
426 def OnScaleMax(self, button):
427 if self._selectedObj is None:
429 machine = profile.getMachineSetting('machine_type')
430 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
431 self._scene.pushFree(self._selectedObj)
433 if machine == "ultimaker2":
434 #This is bad and Jaime should feel bad!
435 self._selectedObj.setPosition(numpy.array([0.0,-10.0]))
436 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
437 self._selectedObj.setPosition(numpy.array([0.0,0.0]))
438 self._scene.pushFree(self._selectedObj)
440 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
441 self._scene.pushFree(self._selectedObj)
442 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
443 self._scene.pushFree(self._selectedObj)
444 self._selectObject(self._selectedObj)
445 self.updateProfileToControls()
448 def OnMirror(self, axis):
449 if self._selectedObj is None:
451 self._selectedObj.mirror(axis)
454 def OnScaleEntry(self, value, axis):
455 if self._selectedObj is None:
461 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
462 self.updateProfileToControls()
463 self._scene.pushFree(self._selectedObj)
464 self._selectObject(self._selectedObj)
467 def OnScaleEntryMM(self, value, axis):
468 if self._selectedObj is None:
474 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
475 self.updateProfileToControls()
476 self._scene.pushFree(self._selectedObj)
477 self._selectObject(self._selectedObj)
480 def OnDeleteAll(self, e):
481 while len(self._scene.objects()) > 0:
482 self._deleteObject(self._scene.objects()[0])
483 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
484 self._engineResultView.setResult(None)
486 def OnMultiply(self, e):
487 if self._focusObj is None:
490 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
491 if dlg.ShowModal() != wx.ID_OK:
500 self._scene.add(newObj)
501 self._scene.centerAll()
502 if not self._scene.checkPlatform(newObj):
507 self.notification.message("Could not create more then %d items" % (n - 1))
508 self._scene.remove(newObj)
509 self._scene.centerAll()
512 def OnSplitObject(self, e):
513 if self._focusObj is None:
515 self._scene.remove(self._focusObj)
516 for obj in self._focusObj.split(self._splitCallback):
517 if numpy.max(obj.getSize()) > 2.0:
519 self._scene.centerAll()
520 self._selectObject(None)
523 def OnCenter(self, e):
524 if self._focusObj is None:
526 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
527 self._scene.pushFree(self._selectedObj)
528 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
529 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
532 def _splitCallback(self, progress):
535 def OnMergeObjects(self, e):
536 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
537 if len(self._scene.objects()) == 2:
538 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
541 self._scene.merge(self._selectedObj, self._focusObj)
544 def sceneUpdated(self):
545 self._sceneUpdateTimer.Start(500, True)
546 self._engine.abortEngine()
547 self._scene.updateSizeOffsets()
550 def _onRunEngine(self, e):
551 if self._isSimpleMode:
552 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
553 self._engine.runEngine(self._scene)
554 if self._isSimpleMode:
555 profile.resetTempOverride()
557 def _updateEngineProgress(self, progressValue):
558 result = self._engine.getResult()
559 finished = result is not None and result.isFinished()
561 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
563 self.printButton.setDisabled(not finished)
564 if progressValue >= 0.0:
565 self.printButton.setProgressBar(progressValue)
567 self.printButton.setProgressBar(None)
568 self._engineResultView.setResult(result)
570 self.printButton.setProgressBar(None)
571 text = '%s' % (result.getPrintTime())
572 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
573 amount = result.getFilamentAmount(e)
576 text += '\n%s' % (amount)
577 cost = result.getFilamentCost(e)
579 text += '\n%s' % (cost)
580 self.printButton.setBottomText(text)
582 self.printButton.setBottomText('')
585 def loadScene(self, fileList):
586 for filename in fileList:
588 ext = os.path.splitext(filename)[1].lower()
589 if ext in imageToMesh.supportedExtensions():
590 imageToMesh.convertImageDialog(self, filename).Show()
593 objList = meshLoader.loadMeshes(filename)
595 traceback.print_exc()
598 if self._objectLoadShader is not None:
599 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
603 if not self._scene.checkPlatform(obj):
604 self._scene.centerAll()
605 self._selectObject(obj)
606 if obj.getScale()[0] < 1.0:
607 self.notification.message("Warning: Object scaled down.")
610 def _deleteObject(self, obj):
611 if obj == self._selectedObj:
612 self._selectObject(None)
613 if obj == self._focusObj:
614 self._focusObj = None
615 self._scene.remove(obj)
616 for m in obj._meshList:
617 if m.vbo is not None and m.vbo.decRef():
618 self.glReleaseList.append(m.vbo)
619 if len(self._scene.objects()) == 0:
620 self._engineResultView.setResult(None)
625 def _selectObject(self, obj, zoom = True):
626 if obj != self._selectedObj:
627 self._selectedObj = obj
628 self.updateModelSettingsToControls()
629 self.updateToolButtons()
630 if zoom and obj is not None:
631 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
632 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
633 newZoom = obj.getBoundaryCircle() * 6
634 if newZoom > numpy.max(self._machineSize) * 3:
635 newZoom = numpy.max(self._machineSize) * 3
636 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
638 def updateProfileToControls(self):
639 oldSimpleMode = self._isSimpleMode
640 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
641 if self._isSimpleMode != oldSimpleMode:
642 self._scene.arrangeAll()
644 self._scene.updateSizeOffsets(True)
645 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
646 self._objColors[0] = profile.getPreferenceColour('model_colour')
647 self._objColors[1] = profile.getPreferenceColour('model_colour2')
648 self._objColors[2] = profile.getPreferenceColour('model_colour3')
649 self._objColors[3] = profile.getPreferenceColour('model_colour4')
650 self._scene.updateMachineDimensions()
651 self.updateModelSettingsToControls()
653 def updateModelSettingsToControls(self):
654 if self._selectedObj is not None:
655 scale = self._selectedObj.getScale()
656 size = self._selectedObj.getSize()
657 self.scaleXctrl.setValue(round(scale[0], 2))
658 self.scaleYctrl.setValue(round(scale[1], 2))
659 self.scaleZctrl.setValue(round(scale[2], 2))
660 self.scaleXmmctrl.setValue(round(size[0], 2))
661 self.scaleYmmctrl.setValue(round(size[1], 2))
662 self.scaleZmmctrl.setValue(round(size[2], 2))
664 def OnKeyChar(self, keyCode):
665 if self._engineResultView.OnKeyChar(keyCode):
667 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
668 if self._selectedObj is not None:
669 self._deleteObject(self._selectedObj)
671 if keyCode == wx.WXK_UP:
672 if wx.GetKeyState(wx.WXK_SHIFT):
679 elif keyCode == wx.WXK_DOWN:
680 if wx.GetKeyState(wx.WXK_SHIFT):
682 if self._zoom > numpy.max(self._machineSize) * 3:
683 self._zoom = numpy.max(self._machineSize) * 3
687 elif keyCode == wx.WXK_LEFT:
690 elif keyCode == wx.WXK_RIGHT:
693 elif keyCode == wx.WXK_NUMPAD_ADD or keyCode == wx.WXK_ADD or keyCode == ord('+') or keyCode == ord('='):
698 elif keyCode == wx.WXK_NUMPAD_SUBTRACT or keyCode == wx.WXK_SUBTRACT or keyCode == ord('-'):
700 if self._zoom > numpy.max(self._machineSize) * 3:
701 self._zoom = numpy.max(self._machineSize) * 3
703 elif keyCode == wx.WXK_HOME:
707 elif keyCode == wx.WXK_PAGEUP:
711 elif keyCode == wx.WXK_PAGEDOWN:
715 elif keyCode == wx.WXK_END:
720 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
721 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
722 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
723 from collections import defaultdict
724 from gc import get_objects
725 self._beforeLeakTest = defaultdict(int)
726 for i in get_objects():
727 self._beforeLeakTest[type(i)] += 1
728 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
729 from collections import defaultdict
730 from gc import get_objects
731 self._afterLeakTest = defaultdict(int)
732 for i in get_objects():
733 self._afterLeakTest[type(i)] += 1
734 for k in self._afterLeakTest:
735 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
736 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
738 def ShaderUpdate(self, v, f):
739 s = openglHelpers.GLShader(v, f)
741 self._objectLoadShader.release()
742 self._objectLoadShader = s
743 for obj in self._scene.objects():
744 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
747 def OnMouseDown(self,e):
748 self._mouseX = e.GetX()
749 self._mouseY = e.GetY()
750 self._mouseClick3DPos = self._mouse3Dpos
751 self._mouseClickFocus = self._focusObj
753 self._mouseState = 'doubleClick'
755 if self._mouseState == 'dragObject' and self._selectedObj is not None:
756 self._scene.pushFree(self._selectedObj)
758 self._mouseState = 'dragOrClick'
759 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
760 p0 -= self.getObjectCenterPos() - self._viewTarget
761 p1 -= self.getObjectCenterPos() - self._viewTarget
762 if self.tool.OnDragStart(p0, p1):
763 self._mouseState = 'tool'
764 if self._mouseState == 'dragOrClick':
765 if e.GetButton() == 1:
766 if self._focusObj is not None:
767 self._selectObject(self._focusObj, False)
770 def OnMouseUp(self, e):
771 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
773 if self._mouseState == 'dragOrClick':
774 if e.GetButton() == 1:
775 self._selectObject(self._focusObj)
776 if e.GetButton() == 3:
778 if self._focusObj is not None:
780 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
781 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
782 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
783 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
784 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:
785 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
786 if len(self._scene.objects()) > 0:
787 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
788 self.Bind(wx.EVT_MENU, self.reloadScene, menu.Append(-1, _("Reload all objects")))
789 if menu.MenuItemCount > 0:
792 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
793 self._scene.pushFree(self._selectedObj)
795 elif self._mouseState == 'tool':
796 if self.tempMatrix is not None and self._selectedObj is not None:
797 self._selectedObj.applyMatrix(self.tempMatrix)
798 self._scene.pushFree(self._selectedObj)
799 self._selectObject(self._selectedObj)
800 self.tempMatrix = None
801 self.tool.OnDragEnd()
803 self._mouseState = None
805 def OnMouseMotion(self,e):
806 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
807 p0 -= self.getObjectCenterPos() - self._viewTarget
808 p1 -= self.getObjectCenterPos() - self._viewTarget
810 if e.Dragging() and self._mouseState is not None:
811 if self._mouseState == 'tool':
812 self.tool.OnDrag(p0, p1)
813 elif not e.LeftIsDown() and e.RightIsDown():
814 self._mouseState = 'drag'
815 if wx.GetKeyState(wx.WXK_SHIFT):
816 a = math.cos(math.radians(self._yaw)) / 3.0
817 b = math.sin(math.radians(self._yaw)) / 3.0
818 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
819 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
820 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
821 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
823 self._yaw += e.GetX() - self._mouseX
824 self._pitch -= e.GetY() - self._mouseY
825 if self._pitch > 170:
829 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
830 self._mouseState = 'drag'
831 self._zoom += e.GetY() - self._mouseY
834 if self._zoom > numpy.max(self._machineSize) * 3:
835 self._zoom = numpy.max(self._machineSize) * 3
836 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
837 self._mouseState = 'dragObject'
838 z = max(0, self._mouseClick3DPos[2])
839 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
840 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
845 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
846 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
847 diff = cursorZ1 - cursorZ0
848 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
849 if not e.Dragging() or self._mouseState != 'tool':
850 self.tool.OnMouseMove(p0, p1)
852 self._mouseX = e.GetX()
853 self._mouseY = e.GetY()
855 def OnMouseWheel(self, e):
856 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
857 delta = max(min(delta,4),-4)
858 self._zoom *= 1.0 - delta / 10.0
861 if self._zoom > numpy.max(self._machineSize) * 3:
862 self._zoom = numpy.max(self._machineSize) * 3
865 def OnMouseLeave(self, e):
869 def getMouseRay(self, x, y):
870 if self._viewport is None:
871 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
872 p0 = openglHelpers.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
873 p1 = openglHelpers.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
874 p0 -= self._viewTarget
875 p1 -= self._viewTarget
878 def _init3DView(self):
879 # set viewing projection
880 size = self.GetSize()
881 glViewport(0, 0, size.GetWidth(), size.GetHeight())
884 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
886 glDisable(GL_RESCALE_NORMAL)
887 glDisable(GL_LIGHTING)
889 glEnable(GL_DEPTH_TEST)
890 glDisable(GL_CULL_FACE)
892 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
894 glClearColor(0.8, 0.8, 0.8, 1.0)
898 glMatrixMode(GL_PROJECTION)
900 aspect = float(size.GetWidth()) / float(size.GetHeight())
901 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
903 glMatrixMode(GL_MODELVIEW)
905 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
908 connectionGroup = self._printerConnectionManager.getAvailableGroup()
909 if len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
910 self.printButton._imageID = 2
911 self.printButton._tooltip = _("Toolpath to SD")
912 elif connectionGroup is not None:
913 self.printButton._imageID = connectionGroup.getIconID()
914 self.printButton._tooltip = _("Print with %s") % (connectionGroup.getName())
916 self.printButton._imageID = 3
917 self.printButton._tooltip = _("Save toolpath")
919 if self._animView is not None:
920 self._viewTarget = self._animView.getPosition()
921 if self._animView.isDone():
922 self._animView = None
923 if self._animZoom is not None:
924 self._zoom = self._animZoom.getPosition()
925 if self._animZoom.isDone():
926 self._animZoom = None
927 if self._objectShader is None: #TODO: add loading shaders from file(s)
928 if openglHelpers.hasShaderSupport():
929 self._objectShader = openglHelpers.GLShader("""
930 varying float light_amount;
934 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
935 gl_FrontColor = gl_Color;
937 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
941 varying float light_amount;
945 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
948 self._objectOverhangShader = openglHelpers.GLShader("""
949 uniform float cosAngle;
950 uniform mat3 rotMatrix;
951 varying float light_amount;
955 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
956 gl_FrontColor = gl_Color;
958 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
960 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
962 light_amount = -10.0;
966 varying float light_amount;
970 if (light_amount == -10.0)
972 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
974 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
978 self._objectLoadShader = openglHelpers.GLShader("""
979 uniform float intensity;
981 varying float light_amount;
985 vec4 tmp = gl_Vertex;
986 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
987 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
988 gl_Position = gl_ModelViewProjectionMatrix * tmp;
989 gl_FrontColor = gl_Color;
991 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
995 uniform float intensity;
996 varying float light_amount;
1000 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
1003 if self._objectShader is None or not self._objectShader.isValid(): #Could not make shader.
1004 self._objectShader = openglHelpers.GLFakeShader()
1005 self._objectOverhangShader = openglHelpers.GLFakeShader()
1006 self._objectLoadShader = None
1008 glTranslate(0,0,-self._zoom)
1009 glRotate(-self._pitch, 1,0,0)
1010 glRotate(self._yaw, 0,0,1)
1011 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
1013 self._viewport = glGetIntegerv(GL_VIEWPORT)
1014 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
1015 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
1017 glClearColor(1,1,1,1)
1018 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
1020 if self.viewMode != 'gcode':
1021 for n in xrange(0, len(self._scene.objects())):
1022 obj = self._scene.objects()[n]
1023 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
1024 self._renderObject(obj)
1026 if self._mouseX > -1: # mouse has not passed over the opengl window.
1028 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
1029 if n < len(self._scene.objects()):
1030 self._focusObj = self._scene.objects()[n]
1032 self._focusObj = None
1033 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
1034 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
1035 self._mouse3Dpos = openglHelpers.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
1036 self._mouse3Dpos -= self._viewTarget
1039 glTranslate(0,0,-self._zoom)
1040 glRotate(-self._pitch, 1,0,0)
1041 glRotate(self._yaw, 0,0,1)
1042 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
1044 self._objectShader.unbind()
1045 self._engineResultView.OnDraw()
1046 if self.viewMode != 'gcode':
1047 glStencilFunc(GL_ALWAYS, 1, 1)
1048 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1050 if self.viewMode == 'overhang':
1051 self._objectOverhangShader.bind()
1052 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - profile.getProfileSettingFloat('support_angle'))))
1054 self._objectShader.bind()
1055 for obj in self._scene.objects():
1056 if obj._loadAnim is not None:
1057 if obj._loadAnim.isDone():
1058 obj._loadAnim = None
1062 if self._focusObj == obj:
1064 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1067 if self._selectedObj == obj or self._selectedObj is None:
1068 #If we want transparent, then first render a solid black model to remove the printer size lines.
1069 if self.viewMode == 'transparent':
1070 glColor4f(0, 0, 0, 0)
1071 self._renderObject(obj)
1073 glBlendFunc(GL_ONE, GL_ONE)
1074 glDisable(GL_DEPTH_TEST)
1076 if self.viewMode == 'xray':
1077 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1078 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1079 glEnable(GL_STENCIL_TEST)
1081 if self.viewMode == 'overhang':
1082 if self._selectedObj == obj and self.tempMatrix is not None:
1083 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1085 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1087 if not self._scene.checkPlatform(obj):
1088 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1089 self._renderObject(obj)
1091 self._renderObject(obj, brightness)
1092 glDisable(GL_STENCIL_TEST)
1094 glEnable(GL_DEPTH_TEST)
1095 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1097 if self.viewMode == 'xray':
1100 glEnable(GL_STENCIL_TEST)
1101 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) #Keep values
1102 glDisable(GL_DEPTH_TEST)
1103 for i in xrange(2, 15, 2): #All even values
1104 glStencilFunc(GL_EQUAL, i, 0xFF)
1105 glColor(float(i)/10, float(i)/10, float(i)/5)
1107 glVertex3f(-1000,-1000,-10)
1108 glVertex3f( 1000,-1000,-10)
1109 glVertex3f( 1000, 1000,-10)
1110 glVertex3f(-1000, 1000,-10)
1112 for i in xrange(1, 15, 2): #All odd values
1113 glStencilFunc(GL_EQUAL, i, 0xFF)
1114 glColor(float(i)/10, 0, 0)
1116 glVertex3f(-1000,-1000,-10)
1117 glVertex3f( 1000,-1000,-10)
1118 glVertex3f( 1000, 1000,-10)
1119 glVertex3f(-1000, 1000,-10)
1122 glDisable(GL_STENCIL_TEST)
1123 glEnable(GL_DEPTH_TEST)
1125 self._objectShader.unbind()
1127 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1129 if self._objectLoadShader is not None:
1130 self._objectLoadShader.bind()
1131 glColor4f(0.2, 0.6, 1.0, 1.0)
1132 for obj in self._scene.objects():
1133 if obj._loadAnim is None:
1135 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1136 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1137 self._renderObject(obj)
1138 self._objectLoadShader.unbind()
1143 if self.viewMode != 'gcode':
1144 #Draw the object box-shadow, so you can see where it will collide with other objects.
1145 if self._selectedObj is not None:
1147 glEnable(GL_CULL_FACE)
1148 glColor4f(0,0,0,0.16)
1150 for obj in self._scene.objects():
1152 glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1153 glBegin(GL_TRIANGLE_FAN)
1154 for p in obj._boundaryHull[::-1]:
1155 glVertex3f(p[0], p[1], 0)
1158 if self._scene.isOneAtATime(): #Check print sequence mode.
1160 glColor4f(0,0,0,0.06)
1161 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1162 glBegin(GL_TRIANGLE_FAN)
1163 for p in self._selectedObj._printAreaHull[::-1]:
1164 glVertex3f(p[0], p[1], 0)
1166 glBegin(GL_TRIANGLE_FAN)
1167 for p in self._selectedObj._headAreaMinHull[::-1]:
1168 glVertex3f(p[0], p[1], 0)
1172 glDisable(GL_CULL_FACE)
1174 #Draw the outline of the selected object on top of everything else except the GUI.
1175 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1176 glDisable(GL_DEPTH_TEST)
1177 glEnable(GL_CULL_FACE)
1178 glEnable(GL_STENCIL_TEST)
1180 glStencilFunc(GL_EQUAL, 0, 255)
1182 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1184 glColor4f(1,1,1,0.5)
1185 self._renderObject(self._selectedObj)
1186 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1188 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1189 glDisable(GL_STENCIL_TEST)
1190 glDisable(GL_CULL_FACE)
1191 glEnable(GL_DEPTH_TEST)
1193 if self._selectedObj is not None:
1195 pos = self.getObjectCenterPos()
1196 glTranslate(pos[0], pos[1], pos[2])
1199 if self.viewMode == 'overhang' and not openglHelpers.hasShaderSupport():
1200 glDisable(GL_DEPTH_TEST)
1203 glTranslate(0,-4,-10)
1204 glColor4ub(60,60,60,255)
1205 openglHelpers.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1208 def _renderObject(self, obj, brightness = 0, addSink = True):
1211 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1213 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1215 if self.tempMatrix is not None and obj == self._selectedObj:
1216 glMultMatrixf(openglHelpers.convert3x3MatrixTo4x4(self.tempMatrix))
1218 offset = obj.getDrawOffset()
1219 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1221 glMultMatrixf(openglHelpers.convert3x3MatrixTo4x4(obj.getMatrix()))
1224 for m in obj._meshList:
1226 m.vbo = openglHelpers.GLVBO(GL_TRIANGLES, m.vertexes, m.normal)
1228 glColor4fv(map(lambda idx: idx * brightness, self._objColors[n]))
1233 def _drawMachine(self):
1234 glEnable(GL_CULL_FACE)
1237 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1239 machine = profile.getMachineSetting('machine_type')
1240 if machine.startswith('ultimaker'):
1241 if machine not in self._platformMesh:
1242 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1244 self._platformMesh[machine] = meshes[0]
1246 self._platformMesh[machine] = None
1247 if machine == 'ultimaker2':
1248 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1250 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1251 glColor4f(1,1,1,0.5)
1252 self._objectShader.bind()
1253 self._renderObject(self._platformMesh[machine], False, False)
1254 self._objectShader.unbind()
1256 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1257 if machine == 'ultimaker2':
1258 if not hasattr(self._platformMesh[machine], 'texture'):
1259 self._platformMesh[machine].texture = openglHelpers.loadGLTexture('Ultimaker2backplate.png')
1260 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1261 glEnable(GL_TEXTURE_2D)
1265 glTranslate(0,150,-5)
1270 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1273 glVertex3f( w, 0, h)
1275 glVertex3f(-w, 0, h)
1277 glVertex3f(-w, 0, 0)
1279 glVertex3f( w, 0, 0)
1282 glVertex3f(-w, d, h)
1284 glVertex3f( w, d, h)
1286 glVertex3f( w, d, 0)
1288 glVertex3f(-w, d, 0)
1290 glDisable(GL_TEXTURE_2D)
1291 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1297 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1298 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1299 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1300 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1301 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1302 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1307 polys = profile.getMachineSizePolygons()
1308 height = profile.getMachineSettingFloat('machine_height')
1309 circular = profile.getMachineSetting('machine_shape') == 'Circular'
1311 # Draw the sides of the build volume.
1312 for n in xrange(0, len(polys[0])):
1315 glColor4ub(5, 171, 231, 96)
1317 glColor4ub(5, 171, 231, 64)
1319 glColor4ub(5, 171, 231, 96)
1321 glVertex3f(polys[0][n][0], polys[0][n][1], height)
1322 glVertex3f(polys[0][n][0], polys[0][n][1], 0)
1323 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0)
1324 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height)
1327 #Draw top of build volume.
1328 glColor4ub(5, 171, 231, 128)
1329 glBegin(GL_TRIANGLE_FAN)
1330 for p in polys[0][::-1]:
1331 glVertex3f(p[0], p[1], height)
1335 if self._platformTexture is None:
1336 self._platformTexture = openglHelpers.loadGLTexture('checkerboard.png')
1337 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1338 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
1339 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
1340 glColor4f(1,1,1,0.5)
1341 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1342 glEnable(GL_TEXTURE_2D)
1343 glBegin(GL_TRIANGLE_FAN)
1345 glTexCoord2f(p[0]/20, p[1]/20)
1346 glVertex3f(p[0], p[1], 0)
1349 #Draw no-go zones. (clips in case of UM2)
1350 glDisable(GL_TEXTURE_2D)
1351 glColor4ub(127, 127, 127, 200)
1352 for poly in polys[1:]:
1353 glBegin(GL_TRIANGLE_FAN)
1355 glTexCoord2f(p[0]/20, p[1]/20)
1356 glVertex3f(p[0], p[1], 0)
1361 glDisable(GL_CULL_FACE)
1363 def getObjectCenterPos(self):
1364 if self._selectedObj is None:
1365 return [0.0, 0.0, 0.0]
1366 pos = self._selectedObj.getPosition()
1367 size = self._selectedObj.getSize()
1368 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1370 def getObjectBoundaryCircle(self):
1371 if self._selectedObj is None:
1373 return self._selectedObj.getBoundaryCircle()
1375 def getObjectSize(self):
1376 if self._selectedObj is None:
1377 return [0.0, 0.0, 0.0]
1378 return self._selectedObj.getSize()
1380 def getObjectMatrix(self):
1381 if self._selectedObj is None:
1382 return numpy.matrix(numpy.identity(3))
1383 return self._selectedObj.getMatrix()
1385 #TODO: Remove this or put it in a seperate file
1386 class shaderEditor(wx.Frame):
1387 def __init__(self, parent, callback, v, f):
1388 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1389 self._callback = callback
1390 s = wx.BoxSizer(wx.VERTICAL)
1392 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1393 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1394 s.Add(self._vertex, 1, flag=wx.EXPAND)
1395 s.Add(self._fragment, 1, flag=wx.EXPAND)
1397 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1398 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1400 self.SetPosition(self.GetParent().GetPosition())
1401 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1404 def OnText(self, e):
1405 self._callback(self._vertex.GetValue(), self._fragment.GetValue())