1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
12 import cStringIO as StringIO
15 #OpenGL.ERROR_CHECKING = False
16 from OpenGL.GLU import *
17 from OpenGL.GL import *
19 from Cura.gui import printWindow
20 from Cura.gui import printWindow2
21 from Cura.util import profile
22 from Cura.util import meshLoader
23 from Cura.util import objectScene
24 from Cura.util import resources
25 from Cura.util import sliceEngine
26 from Cura.util import machineCom
27 from Cura.util import removableStorage
28 from Cura.util import explorer
29 from Cura.util.printerConnection import printerConnectionManager
30 from Cura.gui.util import previewTools
31 from Cura.gui.util import opengl
32 from Cura.gui.util import openglGui
33 from Cura.gui.util import engineResultView
34 from Cura.gui.tools import youmagineGui
35 from Cura.gui.tools import imageToMesh
37 class SceneView(openglGui.glGuiPanel):
38 def __init__(self, parent):
39 super(SceneView, self).__init__(parent)
44 self._scene = objectScene.Scene()
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._platformTexture = None
58 self._isSimpleMode = True
59 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
60 self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager()
63 self._modelMatrix = None
64 self._projMatrix = None
65 self.tempMatrix = None
67 self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
68 self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
69 self.printButton.setDisabled(True)
72 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
73 self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
74 self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
76 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
77 self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
79 self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
80 self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
82 self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
83 self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
84 self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
86 self.rotateToolButton.setExpandArrow(True)
87 self.scaleToolButton.setExpandArrow(True)
88 self.mirrorToolButton.setExpandArrow(True)
90 self.scaleForm = openglGui.glFrame(self, (2, -2))
91 openglGui.glGuiLayoutGrid(self.scaleForm)
92 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
93 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
94 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
95 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
96 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
97 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
98 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
99 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
100 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
101 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
102 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
103 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
104 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
105 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
107 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
109 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
110 self.youMagineButton.setDisabled(True)
112 self.notification = openglGui.glNotification(self, (0, 0))
114 self._engine = sliceEngine.Engine(self._updateEngineProgress)
115 self._engineResultView = engineResultView.engineResultView(self)
116 self._sceneUpdateTimer = wx.Timer(self)
117 self.Bind(wx.EVT_TIMER, self._onRunEngine, self._sceneUpdateTimer)
118 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
119 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
123 self.updateToolButtons()
124 self.updateProfileToControls()
126 def loadGCodeFile(self, filename):
127 self.OnDeleteAll(None)
128 #TODO: Load straight GCodeFile
129 self.printButton.setBottomText('')
130 self.viewSelection.setValue(4)
131 self.printButton.setDisabled(False)
132 self.youMagineButton.setDisabled(True)
135 def loadSceneFiles(self, filenames):
136 self.youMagineButton.setDisabled(False)
137 #if self.viewSelection.getValue() == 4:
138 # self.viewSelection.setValue(0)
139 # self.OnViewChange()
140 self.loadScene(filenames)
142 def loadFiles(self, filenames):
143 mainWindow = self.GetParent().GetParent().GetParent()
144 # only one GCODE file can be active
145 # so if single gcode file, process this
146 # otherwise ignore all gcode files
148 if len(filenames) == 1:
149 filename = filenames[0]
150 ext = os.path.splitext(filename)[1].lower()
151 if ext == '.g' or ext == '.gcode':
152 gcodeFilename = filename
153 mainWindow.addToModelMRU(filename)
154 if gcodeFilename is not None:
155 self.loadGCodeFile(gcodeFilename)
157 # process directories and special file types
158 # and keep scene files for later processing
160 ignored_types = dict()
161 # use file list as queue
162 # pop first entry for processing and append new files at end
164 filename = filenames.pop(0)
165 if os.path.isdir(filename):
166 # directory: queue all included files and directories
167 filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
169 ext = os.path.splitext(filename)[1].lower()
171 profile.loadProfile(filename)
172 mainWindow.addToProfileMRU(filename)
173 elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
174 scene_filenames.append(filename)
175 mainWindow.addToModelMRU(filename)
177 ignored_types[ext] = 1
179 ignored_types = ignored_types.keys()
181 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
182 mainWindow.updateProfileToAllControls()
183 # now process all the scene files
185 self.loadSceneFiles(scene_filenames)
186 self._selectObject(None)
188 newZoom = numpy.max(self._machineSize)
189 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
190 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
192 def showLoadModel(self, button = 1):
194 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)
195 dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
196 if dlg.ShowModal() != wx.ID_OK:
199 filenames = dlg.GetPaths()
201 if len(filenames) < 1:
203 profile.putPreference('lastFile', filenames[0])
204 self.loadFiles(filenames)
206 def showSaveModel(self):
207 if len(self._scene.objects()) < 1:
209 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
210 dlg.SetWildcard(meshLoader.saveWildcardFilter())
211 if dlg.ShowModal() != wx.ID_OK:
214 filename = dlg.GetPath()
216 meshLoader.saveMeshes(filename, self._scene.objects())
218 def OnPrintButton(self, button):
220 connectionGroup = self._printerConnectionManager.getAvailableGroup()
221 if machineCom.machineIsConnected():
222 self.showPrintWindow()
223 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
224 drives = removableStorage.getPossibleSDcardDrives()
226 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))
227 if dlg.ShowModal() != wx.ID_OK:
230 drive = drives[dlg.GetSelection()]
234 filename = self._scene._objectList[0].getName() + '.gcode'
235 threading.Thread(target=self._saveGCode,args=(drive[1] + filename, drive[1])).start()
236 elif connectionGroup is not None:
237 connections = connectionGroup.getAvailableConnections()
238 if len(connections) < 2:
239 connection = connections[0]
241 dlg = wx.SingleChoiceDialog(self, "Select the %s connection to use" % (connectionGroup.getName()), "Multiple %s connections found" % (connectionGroup.getName()), map(lambda n: n.getName(), connections))
242 if dlg.ShowModal() != wx.ID_OK:
245 connection = connections[dlg.GetSelection()]
247 self._openPrintWindowForConnection(connection)
252 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
253 connections = self._printerConnectionManager.getAvailableConnections()
254 menu.connectionMap = {}
255 for connection in connections:
256 i = menu.Append(-1, _("Print with %s") % (connection.getName()))
257 menu.connectionMap[i.GetId()] = connection
258 self.Bind(wx.EVT_MENU, lambda e: self._openPrintWindowForConnection(e.GetEventObject().connectionMap[e.GetId()]), i)
259 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
260 self.Bind(wx.EVT_MENU, lambda e: self._showEngineLog(), menu.Append(-1, _("Slice engine log...")))
264 def _openPrintWindowForConnection(self, connection):
265 print '_openPrintWindowForConnection', connection.getName()
266 if connection.window is None or not connection.window:
267 connection.window = printWindow2.printWindow(connection)
268 connection.window.Show()
269 connection.window.Raise()
270 #TODO: Fix for _engine.getResult
271 if not connection.loadFile(self._gcodeFilename):
272 if connection.isPrinting():
273 self.notification.message("Cannot start print, because other print still running.")
275 self.notification.message("Failed to start print...")
277 def showPrintWindow(self):
278 if self._gcodeFilename is None:
280 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
281 wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING)
283 #TODO: Fix for _engine.getResult
284 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._engine.getID())
285 if self._gcodeFilename is None:
286 self._engine.submitInfoOnline()
288 def showSaveGCode(self):
289 if len(self._scene._objectList) < 1:
291 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
292 filename = self._scene._objectList[0].getName() + '.gcode'
293 dlg.SetFilename(filename)
294 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
295 if dlg.ShowModal() != wx.ID_OK:
298 filename = dlg.GetPath()
301 threading.Thread(target=self._saveGCode,args=(filename,)).start()
303 def _saveGCode(self, targetFilename, ejectDrive = False):
304 data = self._engine.getResult().getGCode()
306 size = float(len(data))
307 fsrc = StringIO.StringIO(data)
308 with open(targetFilename, 'wb') as fdst:
310 buf = fsrc.read(16*1024)
314 self.printButton.setProgressBar(float(fsrc.tell()) / size)
317 import sys, traceback
318 traceback.print_exc()
319 self.notification.message("Failed to save")
322 self.notification.message("Saved as %s" % (targetFilename), lambda : self._doEjectSD(ejectDrive), 31, 'Eject')
323 elif explorer.hasExplorer():
324 self.notification.message("Saved as %s" % (targetFilename), lambda : explorer.openExplorer(targetFilename), 4, 'Open folder')
326 self.notification.message("Saved as %s" % (targetFilename))
327 self.printButton.setProgressBar(None)
328 self._engine.getResult().submitInfoOnline()
330 def _doEjectSD(self, drive):
331 if removableStorage.ejectDrive(drive):
332 self.notification.message('You can now eject the card.')
334 self.notification.message('Safe remove failed...')
336 def _showEngineLog(self):
337 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)
341 def OnToolSelect(self, button):
342 if self.rotateToolButton.getSelected():
343 self.tool = previewTools.toolRotate(self)
344 elif self.scaleToolButton.getSelected():
345 self.tool = previewTools.toolScale(self)
346 elif self.mirrorToolButton.getSelected():
347 self.tool = previewTools.toolNone(self)
349 self.tool = previewTools.toolNone(self)
350 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
351 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
352 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
353 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
354 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
355 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
356 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
357 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
359 def updateToolButtons(self):
360 if self._selectedObj is None:
364 self.rotateToolButton.setHidden(hidden)
365 self.scaleToolButton.setHidden(hidden)
366 self.mirrorToolButton.setHidden(hidden)
368 self.rotateToolButton.setSelected(False)
369 self.scaleToolButton.setSelected(False)
370 self.mirrorToolButton.setSelected(False)
373 def OnViewChange(self):
374 if self.viewSelection.getValue() == 4:
375 self.viewMode = 'gcode'
376 elif self.viewSelection.getValue() == 1:
377 self.viewMode = 'overhang'
378 elif self.viewSelection.getValue() == 2:
379 self.viewMode = 'transparent'
380 elif self.viewSelection.getValue() == 3:
381 self.viewMode = 'xray'
383 self.viewMode = 'normal'
384 self._engineResultView.setEnabled(self.viewMode == 'gcode')
387 def OnRotateReset(self, button):
388 if self._selectedObj is None:
390 self._selectedObj.resetRotation()
391 self._scene.pushFree(self._selectedObj)
392 self._selectObject(self._selectedObj)
395 def OnLayFlat(self, button):
396 if self._selectedObj is None:
398 self._selectedObj.layFlat()
399 self._scene.pushFree(self._selectedObj)
400 self._selectObject(self._selectedObj)
403 def OnScaleReset(self, button):
404 if self._selectedObj is None:
406 self._selectedObj.resetScale()
407 self._selectObject(self._selectedObj)
408 self.updateProfileToControls()
411 def OnScaleMax(self, button):
412 if self._selectedObj is None:
414 machine = profile.getMachineSetting('machine_type')
415 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
416 self._scene.pushFree(self._selectedObj)
418 if machine == "ultimaker2":
419 #This is bad and Jaime should feel bad!
420 self._selectedObj.setPosition(numpy.array([0.0,-10.0]))
421 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
422 self._selectedObj.setPosition(numpy.array([0.0,0.0]))
423 self._scene.pushFree(self._selectedObj)
425 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
426 self._scene.pushFree(self._selectedObj)
427 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
428 self._scene.pushFree(self._selectedObj)
429 self._selectObject(self._selectedObj)
430 self.updateProfileToControls()
433 def OnMirror(self, axis):
434 if self._selectedObj is None:
436 self._selectedObj.mirror(axis)
439 def OnScaleEntry(self, value, axis):
440 if self._selectedObj is None:
446 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
447 self.updateProfileToControls()
448 self._scene.pushFree(self._selectedObj)
449 self._selectObject(self._selectedObj)
452 def OnScaleEntryMM(self, value, axis):
453 if self._selectedObj is None:
459 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
460 self.updateProfileToControls()
461 self._scene.pushFree(self._selectedObj)
462 self._selectObject(self._selectedObj)
465 def OnDeleteAll(self, e):
466 while len(self._scene.objects()) > 0:
467 self._deleteObject(self._scene.objects()[0])
468 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
470 def OnMultiply(self, e):
471 if self._focusObj is None:
474 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
475 if dlg.ShowModal() != wx.ID_OK:
484 self._scene.add(newObj)
485 self._scene.centerAll()
486 if not self._scene.checkPlatform(newObj):
491 self.notification.message("Could not create more then %d items" % (n - 1))
492 self._scene.remove(newObj)
493 self._scene.centerAll()
496 def OnSplitObject(self, e):
497 if self._focusObj is None:
499 self._scene.remove(self._focusObj)
500 for obj in self._focusObj.split(self._splitCallback):
501 if numpy.max(obj.getSize()) > 2.0:
503 self._scene.centerAll()
504 self._selectObject(None)
507 def OnCenter(self, e):
508 if self._focusObj is None:
510 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
511 self._scene.pushFree(self._selectedObj)
512 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
513 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
516 def _splitCallback(self, progress):
519 def OnMergeObjects(self, e):
520 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
521 if len(self._scene.objects()) == 2:
522 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
525 self._scene.merge(self._selectedObj, self._focusObj)
528 def sceneUpdated(self):
529 self._sceneUpdateTimer.Start(500, True)
530 self._engine.abortEngine()
531 self._scene.updateSizeOffsets()
534 def _onRunEngine(self, e):
535 if self._isSimpleMode:
536 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
537 self._engine.runEngine(self._scene)
538 if self._isSimpleMode:
539 profile.resetTempOverride()
541 def _updateEngineProgress(self, progressValue):
542 result = self._engine.getResult()
543 finished = result is not None and result.isFinished()
545 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
547 self.printButton.setDisabled(not finished)
548 if progressValue >= 0.0:
549 self.printButton.setProgressBar(progressValue)
551 self.printButton.setProgressBar(None)
552 self._engineResultView.setResult(result)
554 self.printButton.setProgressBar(None)
555 text = '%s' % (result.getPrintTime())
556 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
557 amount = result.getFilamentAmount(e)
560 text += '\n%s' % (amount)
561 cost = result.getFilamentCost(e)
563 text += '\n%s' % (cost)
564 self.printButton.setBottomText(text)
566 self.printButton.setBottomText('')
569 def loadScene(self, fileList):
570 for filename in fileList:
572 ext = os.path.splitext(filename)[1].lower()
573 if ext in imageToMesh.supportedExtensions():
574 imageToMesh.convertImageDialog(self, filename).Show()
577 objList = meshLoader.loadMeshes(filename)
579 traceback.print_exc()
582 if self._objectLoadShader is not None:
583 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
587 if not self._scene.checkPlatform(obj):
588 self._scene.centerAll()
589 self._selectObject(obj)
590 if obj.getScale()[0] < 1.0:
591 self.notification.message("Warning: Object scaled down.")
594 def _deleteObject(self, obj):
595 if obj == self._selectedObj:
596 self._selectObject(None)
597 if obj == self._focusObj:
598 self._focusObj = None
599 self._scene.remove(obj)
600 for m in obj._meshList:
601 if m.vbo is not None and m.vbo.decRef():
602 self.glReleaseList.append(m.vbo)
607 def _selectObject(self, obj, zoom = True):
608 if obj != self._selectedObj:
609 self._selectedObj = obj
610 self.updateModelSettingsToControls()
611 self.updateToolButtons()
612 if zoom and obj is not None:
613 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
614 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
615 newZoom = obj.getBoundaryCircle() * 6
616 if newZoom > numpy.max(self._machineSize) * 3:
617 newZoom = numpy.max(self._machineSize) * 3
618 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
620 def updateProfileToControls(self):
621 oldSimpleMode = self._isSimpleMode
622 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
623 if self._isSimpleMode != oldSimpleMode:
624 self._scene.arrangeAll()
626 self._scene.updateSizeOffsets(True)
627 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
628 self._objColors[0] = profile.getPreferenceColour('model_colour')
629 self._objColors[1] = profile.getPreferenceColour('model_colour2')
630 self._objColors[2] = profile.getPreferenceColour('model_colour3')
631 self._objColors[3] = profile.getPreferenceColour('model_colour4')
632 self._scene.updateMachineDimensions()
633 self.updateModelSettingsToControls()
635 def updateModelSettingsToControls(self):
636 if self._selectedObj is not None:
637 scale = self._selectedObj.getScale()
638 size = self._selectedObj.getSize()
639 self.scaleXctrl.setValue(round(scale[0], 2))
640 self.scaleYctrl.setValue(round(scale[1], 2))
641 self.scaleZctrl.setValue(round(scale[2], 2))
642 self.scaleXmmctrl.setValue(round(size[0], 2))
643 self.scaleYmmctrl.setValue(round(size[1], 2))
644 self.scaleZmmctrl.setValue(round(size[2], 2))
646 def OnKeyChar(self, keyCode):
647 if self._engineResultView.OnKeyChar(keyCode):
649 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
650 if self._selectedObj is not None:
651 self._deleteObject(self._selectedObj)
653 if keyCode == wx.WXK_UP:
654 if wx.GetKeyState(wx.WXK_SHIFT):
661 elif keyCode == wx.WXK_DOWN:
662 if wx.GetKeyState(wx.WXK_SHIFT):
664 if self._zoom > numpy.max(self._machineSize) * 3:
665 self._zoom = numpy.max(self._machineSize) * 3
669 elif keyCode == wx.WXK_LEFT:
672 elif keyCode == wx.WXK_RIGHT:
675 elif keyCode == wx.WXK_NUMPAD_ADD or keyCode == wx.WXK_ADD or keyCode == ord('+') or keyCode == ord('='):
680 elif keyCode == wx.WXK_NUMPAD_SUBTRACT or keyCode == wx.WXK_SUBTRACT or keyCode == ord('-'):
682 if self._zoom > numpy.max(self._machineSize) * 3:
683 self._zoom = numpy.max(self._machineSize) * 3
685 elif keyCode == wx.WXK_HOME:
689 elif keyCode == wx.WXK_PAGEUP:
693 elif keyCode == wx.WXK_PAGEDOWN:
697 elif keyCode == wx.WXK_END:
702 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
703 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
704 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
705 from collections import defaultdict
706 from gc import get_objects
707 self._beforeLeakTest = defaultdict(int)
708 for i in get_objects():
709 self._beforeLeakTest[type(i)] += 1
710 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
711 from collections import defaultdict
712 from gc import get_objects
713 self._afterLeakTest = defaultdict(int)
714 for i in get_objects():
715 self._afterLeakTest[type(i)] += 1
716 for k in self._afterLeakTest:
717 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
718 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
720 def ShaderUpdate(self, v, f):
721 s = opengl.GLShader(v, f)
723 self._objectLoadShader.release()
724 self._objectLoadShader = s
725 for obj in self._scene.objects():
726 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
729 def OnMouseDown(self,e):
730 self._mouseX = e.GetX()
731 self._mouseY = e.GetY()
732 self._mouseClick3DPos = self._mouse3Dpos
733 self._mouseClickFocus = self._focusObj
735 self._mouseState = 'doubleClick'
737 self._mouseState = 'dragOrClick'
738 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
739 p0 -= self.getObjectCenterPos() - self._viewTarget
740 p1 -= self.getObjectCenterPos() - self._viewTarget
741 if self.tool.OnDragStart(p0, p1):
742 self._mouseState = 'tool'
743 if self._mouseState == 'dragOrClick':
744 if e.GetButton() == 1:
745 if self._focusObj is not None:
746 self._selectObject(self._focusObj, False)
749 def OnMouseUp(self, e):
750 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
752 if self._mouseState == 'dragOrClick':
753 if e.GetButton() == 1:
754 self._selectObject(self._focusObj)
755 if e.GetButton() == 3:
757 if self._focusObj is not None:
758 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
759 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
760 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
761 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
762 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:
763 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
764 if len(self._scene.objects()) > 0:
765 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
766 if menu.MenuItemCount > 0:
769 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
770 self._scene.pushFree(self._selectedObj)
772 elif self._mouseState == 'tool':
773 if self.tempMatrix is not None and self._selectedObj is not None:
774 self._selectedObj.applyMatrix(self.tempMatrix)
775 self._scene.pushFree(self._selectedObj)
776 self._selectObject(self._selectedObj)
777 self.tempMatrix = None
778 self.tool.OnDragEnd()
780 self._mouseState = None
782 def OnMouseMotion(self,e):
783 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
784 p0 -= self.getObjectCenterPos() - self._viewTarget
785 p1 -= self.getObjectCenterPos() - self._viewTarget
787 if e.Dragging() and self._mouseState is not None:
788 if self._mouseState == 'tool':
789 self.tool.OnDrag(p0, p1)
790 elif not e.LeftIsDown() and e.RightIsDown():
791 self._mouseState = 'drag'
792 if wx.GetKeyState(wx.WXK_SHIFT):
793 a = math.cos(math.radians(self._yaw)) / 3.0
794 b = math.sin(math.radians(self._yaw)) / 3.0
795 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
796 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
797 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
798 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
800 self._yaw += e.GetX() - self._mouseX
801 self._pitch -= e.GetY() - self._mouseY
802 if self._pitch > 170:
806 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
807 self._mouseState = 'drag'
808 self._zoom += e.GetY() - self._mouseY
811 if self._zoom > numpy.max(self._machineSize) * 3:
812 self._zoom = numpy.max(self._machineSize) * 3
813 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
814 self._mouseState = 'dragObject'
815 z = max(0, self._mouseClick3DPos[2])
816 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
817 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
822 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
823 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
824 diff = cursorZ1 - cursorZ0
825 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
826 if not e.Dragging() or self._mouseState != 'tool':
827 self.tool.OnMouseMove(p0, p1)
829 self._mouseX = e.GetX()
830 self._mouseY = e.GetY()
832 def OnMouseWheel(self, e):
833 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
834 delta = max(min(delta,4),-4)
835 self._zoom *= 1.0 - delta / 10.0
838 if self._zoom > numpy.max(self._machineSize) * 3:
839 self._zoom = numpy.max(self._machineSize) * 3
842 def OnMouseLeave(self, e):
846 def getMouseRay(self, x, y):
847 if self._viewport is None:
848 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
849 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
850 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
851 p0 -= self._viewTarget
852 p1 -= self._viewTarget
855 def _init3DView(self):
856 # set viewing projection
857 size = self.GetSize()
858 glViewport(0, 0, size.GetWidth(), size.GetHeight())
861 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
863 glDisable(GL_RESCALE_NORMAL)
864 glDisable(GL_LIGHTING)
866 glEnable(GL_DEPTH_TEST)
867 glDisable(GL_CULL_FACE)
869 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
871 glClearColor(0.8, 0.8, 0.8, 1.0)
875 glMatrixMode(GL_PROJECTION)
877 aspect = float(size.GetWidth()) / float(size.GetHeight())
878 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
880 glMatrixMode(GL_MODELVIEW)
882 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
885 connectionGroup = self._printerConnectionManager.getAvailableGroup()
886 if machineCom.machineIsConnected():
887 self.printButton._imageID = 6
888 self.printButton._tooltip = _("Print")
889 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
890 self.printButton._imageID = 2
891 self.printButton._tooltip = _("Toolpath to SD")
892 elif connectionGroup is not None:
893 self.printButton._imageID = connectionGroup.getIconID()
894 self.printButton._tooltip = _("Print with %s") % (connectionGroup.getName())
896 self.printButton._imageID = 3
897 self.printButton._tooltip = _("Save toolpath")
899 if self._animView is not None:
900 self._viewTarget = self._animView.getPosition()
901 if self._animView.isDone():
902 self._animView = None
903 if self._animZoom is not None:
904 self._zoom = self._animZoom.getPosition()
905 if self._animZoom.isDone():
906 self._animZoom = None
907 if self._objectShader is None:
908 if opengl.hasShaderSupport():
909 self._objectShader = opengl.GLShader("""
910 varying float light_amount;
914 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
915 gl_FrontColor = gl_Color;
917 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
921 varying float light_amount;
925 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
928 self._objectOverhangShader = opengl.GLShader("""
929 uniform float cosAngle;
930 uniform mat3 rotMatrix;
931 varying float light_amount;
935 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
936 gl_FrontColor = gl_Color;
938 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
940 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
942 light_amount = -10.0;
946 varying float light_amount;
950 if (light_amount == -10.0)
952 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
954 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
958 self._objectLoadShader = opengl.GLShader("""
959 uniform float intensity;
961 varying float light_amount;
965 vec4 tmp = gl_Vertex;
966 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
967 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
968 gl_Position = gl_ModelViewProjectionMatrix * tmp;
969 gl_FrontColor = gl_Color;
971 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
975 uniform float intensity;
976 varying float light_amount;
980 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
983 if self._objectShader is None or not self._objectShader.isValid():
984 self._objectShader = opengl.GLFakeShader()
985 self._objectOverhangShader = opengl.GLFakeShader()
986 self._objectLoadShader = None
988 glTranslate(0,0,-self._zoom)
989 glRotate(-self._pitch, 1,0,0)
990 glRotate(self._yaw, 0,0,1)
991 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
993 self._viewport = glGetIntegerv(GL_VIEWPORT)
994 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
995 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
997 glClearColor(1,1,1,1)
998 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
1000 if self.viewMode != 'gcode':
1001 for n in xrange(0, len(self._scene.objects())):
1002 obj = self._scene.objects()[n]
1003 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
1004 self._renderObject(obj)
1006 if self._mouseX > -1:
1008 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
1009 if n < len(self._scene.objects()):
1010 self._focusObj = self._scene.objects()[n]
1012 self._focusObj = None
1013 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
1014 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
1015 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
1016 self._mouse3Dpos -= self._viewTarget
1019 glTranslate(0,0,-self._zoom)
1020 glRotate(-self._pitch, 1,0,0)
1021 glRotate(self._yaw, 0,0,1)
1022 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
1024 self._objectShader.unbind()
1025 self._engineResultView.OnDraw()
1026 if self.viewMode != 'gcode':
1027 glStencilFunc(GL_ALWAYS, 1, 1)
1028 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1030 if self.viewMode == 'overhang':
1031 self._objectOverhangShader.bind()
1032 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - profile.getProfileSettingFloat('support_angle'))))
1034 self._objectShader.bind()
1035 for obj in self._scene.objects():
1036 if obj._loadAnim is not None:
1037 if obj._loadAnim.isDone():
1038 obj._loadAnim = None
1042 if self._focusObj == obj:
1044 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1047 if self._selectedObj == obj or self._selectedObj is None:
1048 #If we want transparent, then first render a solid black model to remove the printer size lines.
1049 if self.viewMode == 'transparent':
1050 glColor4f(0, 0, 0, 0)
1051 self._renderObject(obj)
1053 glBlendFunc(GL_ONE, GL_ONE)
1054 glDisable(GL_DEPTH_TEST)
1056 if self.viewMode == 'xray':
1057 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1058 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1059 glEnable(GL_STENCIL_TEST)
1061 if self.viewMode == 'overhang':
1062 if self._selectedObj == obj and self.tempMatrix is not None:
1063 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1065 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1067 if not self._scene.checkPlatform(obj):
1068 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1069 self._renderObject(obj)
1071 self._renderObject(obj, brightness)
1072 glDisable(GL_STENCIL_TEST)
1074 glEnable(GL_DEPTH_TEST)
1075 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1077 if self.viewMode == 'xray':
1080 glEnable(GL_STENCIL_TEST)
1081 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1082 glDisable(GL_DEPTH_TEST)
1083 for i in xrange(2, 15, 2):
1084 glStencilFunc(GL_EQUAL, i, 0xFF)
1085 glColor(float(i)/10, float(i)/10, float(i)/5)
1087 glVertex3f(-1000,-1000,-10)
1088 glVertex3f( 1000,-1000,-10)
1089 glVertex3f( 1000, 1000,-10)
1090 glVertex3f(-1000, 1000,-10)
1092 for i in xrange(1, 15, 2):
1093 glStencilFunc(GL_EQUAL, i, 0xFF)
1094 glColor(float(i)/10, 0, 0)
1096 glVertex3f(-1000,-1000,-10)
1097 glVertex3f( 1000,-1000,-10)
1098 glVertex3f( 1000, 1000,-10)
1099 glVertex3f(-1000, 1000,-10)
1102 glDisable(GL_STENCIL_TEST)
1103 glEnable(GL_DEPTH_TEST)
1105 self._objectShader.unbind()
1107 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1109 if self._objectLoadShader is not None:
1110 self._objectLoadShader.bind()
1111 glColor4f(0.2, 0.6, 1.0, 1.0)
1112 for obj in self._scene.objects():
1113 if obj._loadAnim is None:
1115 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1116 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1117 self._renderObject(obj)
1118 self._objectLoadShader.unbind()
1123 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._engine.getID():
1124 z = self._usbPrintMonitor.getZ()
1125 if self.viewMode == 'gcode':
1126 layer_height = profile.getProfileSettingFloat('layer_height')
1127 layer1_height = profile.getProfileSettingFloat('bottom_thickness')
1128 if layer_height > 0:
1129 if layer1_height > 0:
1130 layer = int((z - layer1_height) / layer_height) + 1
1132 layer = int(z / layer_height)
1135 self.layerSelect.setValue(layer)
1137 size = self._machineSize
1139 glColor4ub(255,255,0,128)
1141 glVertex3f(-size[0]/2,-size[1]/2, z)
1142 glVertex3f( size[0]/2,-size[1]/2, z)
1143 glVertex3f( size[0]/2, size[1]/2, z)
1144 glVertex3f(-size[0]/2, size[1]/2, z)
1147 if self.viewMode != 'gcode':
1148 #Draw the object box-shadow, so you can see where it will collide with other objects.
1149 if self._selectedObj is not None:
1151 glEnable(GL_CULL_FACE)
1152 glColor4f(0,0,0,0.16)
1154 for obj in self._scene.objects():
1156 glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1157 glBegin(GL_TRIANGLE_FAN)
1158 for p in obj._boundaryHull[::-1]:
1159 glVertex3f(p[0], p[1], 0)
1162 if self._scene.isOneAtATime():
1164 glColor4f(0,0,0,0.06)
1165 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1166 glBegin(GL_TRIANGLE_FAN)
1167 for p in self._selectedObj._printAreaHull[::-1]:
1168 glVertex3f(p[0], p[1], 0)
1170 glBegin(GL_TRIANGLE_FAN)
1171 for p in self._selectedObj._headAreaMinHull[::-1]:
1172 glVertex3f(p[0], p[1], 0)
1176 glDisable(GL_CULL_FACE)
1178 #Draw the outline of the selected object, on top of everything else except the GUI.
1179 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1180 glDisable(GL_DEPTH_TEST)
1181 glEnable(GL_CULL_FACE)
1182 glEnable(GL_STENCIL_TEST)
1184 glStencilFunc(GL_EQUAL, 0, 255)
1186 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1188 glColor4f(1,1,1,0.5)
1189 self._renderObject(self._selectedObj)
1190 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1192 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1193 glDisable(GL_STENCIL_TEST)
1194 glDisable(GL_CULL_FACE)
1195 glEnable(GL_DEPTH_TEST)
1197 if self._selectedObj is not None:
1199 pos = self.getObjectCenterPos()
1200 glTranslate(pos[0], pos[1], pos[2])
1203 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1204 glDisable(GL_DEPTH_TEST)
1207 glTranslate(0,-4,-10)
1208 glColor4ub(60,60,60,255)
1209 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1212 def _renderObject(self, obj, brightness = False, addSink = True):
1215 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1217 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1219 if self.tempMatrix is not None and obj == self._selectedObj:
1220 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1221 glMultMatrixf(tempMatrix)
1223 offset = obj.getDrawOffset()
1224 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1226 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1227 glMultMatrixf(tempMatrix)
1230 for m in obj._meshList:
1232 m.vbo = opengl.GLVBO(GL_TRIANGLES, m.vertexes, m.normal)
1234 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1239 def _drawMachine(self):
1240 glEnable(GL_CULL_FACE)
1243 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1245 machine = profile.getMachineSetting('machine_type')
1246 if machine.startswith('ultimaker'):
1247 if machine not in self._platformMesh:
1248 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1250 self._platformMesh[machine] = meshes[0]
1252 self._platformMesh[machine] = None
1253 if machine == 'ultimaker2':
1254 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1256 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1257 glColor4f(1,1,1,0.5)
1258 self._objectShader.bind()
1259 self._renderObject(self._platformMesh[machine], False, False)
1260 self._objectShader.unbind()
1262 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1263 if machine == 'ultimaker2':
1264 if not hasattr(self._platformMesh[machine], 'texture'):
1265 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1266 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1267 glEnable(GL_TEXTURE_2D)
1271 glTranslate(0,150,-5)
1276 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1279 glVertex3f( w, 0, h)
1281 glVertex3f(-w, 0, h)
1283 glVertex3f(-w, 0, 0)
1285 glVertex3f( w, 0, 0)
1288 glVertex3f(-w, d, h)
1290 glVertex3f( w, d, h)
1292 glVertex3f( w, d, 0)
1294 glVertex3f(-w, d, 0)
1296 glDisable(GL_TEXTURE_2D)
1297 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1303 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1304 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1305 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1306 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1307 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1308 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1313 polys = profile.getMachineSizePolygons()
1314 height = profile.getMachineSettingFloat('machine_height')
1315 circular = profile.getMachineSetting('machine_shape') == 'Circular'
1317 for n in xrange(0, len(polys[0])):
1320 glColor4ub(5, 171, 231, 96)
1322 glColor4ub(5, 171, 231, 64)
1324 glColor4ub(5, 171, 231, 96)
1326 glVertex3f(polys[0][n][0], polys[0][n][1], height)
1327 glVertex3f(polys[0][n][0], polys[0][n][1], 0)
1328 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0)
1329 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height)
1331 glColor4ub(5, 171, 231, 128)
1332 glBegin(GL_TRIANGLE_FAN)
1333 for p in polys[0][::-1]:
1334 glVertex3f(p[0], p[1], height)
1338 if self._platformTexture is None:
1339 self._platformTexture = opengl.loadGLTexture('checkerboard.png')
1340 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1341 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
1342 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
1343 glColor4f(1,1,1,0.5)
1344 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1345 glEnable(GL_TEXTURE_2D)
1346 glBegin(GL_TRIANGLE_FAN)
1348 glTexCoord2f(p[0]/20, p[1]/20)
1349 glVertex3f(p[0], p[1], 0)
1351 glDisable(GL_TEXTURE_2D)
1352 glColor4ub(127, 127, 127, 200)
1353 for poly in polys[1:]:
1354 glBegin(GL_TRIANGLE_FAN)
1356 glTexCoord2f(p[0]/20, p[1]/20)
1357 glVertex3f(p[0], p[1], 0)
1362 glDisable(GL_CULL_FACE)
1364 def getObjectCenterPos(self):
1365 if self._selectedObj is None:
1366 return [0.0, 0.0, 0.0]
1367 pos = self._selectedObj.getPosition()
1368 size = self._selectedObj.getSize()
1369 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1371 def getObjectBoundaryCircle(self):
1372 if self._selectedObj is None:
1374 return self._selectedObj.getBoundaryCircle()
1376 def getObjectSize(self):
1377 if self._selectedObj is None:
1378 return [0.0, 0.0, 0.0]
1379 return self._selectedObj.getSize()
1381 def getObjectMatrix(self):
1382 if self._selectedObj is None:
1383 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1384 return self._selectedObj.getMatrix()
1386 class shaderEditor(wx.Dialog):
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())