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 if not connection.loadGCodeData(StringIO.StringIO(self._engine.getResult().getGCode())):
271 if connection.isPrinting():
272 self.notification.message("Cannot start print, because other print still running.")
274 self.notification.message("Failed to start print...")
276 def showPrintWindow(self):
277 if self._gcodeFilename is None:
279 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
280 wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING)
282 #TODO: Fix for _engine.getResult
283 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._engine.getID())
284 if self._gcodeFilename is None:
285 self._engine.submitInfoOnline()
287 def showSaveGCode(self):
288 if len(self._scene._objectList) < 1:
290 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
291 filename = self._scene._objectList[0].getName() + '.gcode'
292 dlg.SetFilename(filename)
293 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
294 if dlg.ShowModal() != wx.ID_OK:
297 filename = dlg.GetPath()
300 threading.Thread(target=self._saveGCode,args=(filename,)).start()
302 def _saveGCode(self, targetFilename, ejectDrive = False):
303 data = self._engine.getResult().getGCode()
305 size = float(len(data))
306 fsrc = StringIO.StringIO(data)
307 with open(targetFilename, 'wb') as fdst:
309 buf = fsrc.read(16*1024)
313 self.printButton.setProgressBar(float(fsrc.tell()) / size)
316 import sys, traceback
317 traceback.print_exc()
318 self.notification.message("Failed to save")
321 self.notification.message("Saved as %s" % (targetFilename), lambda : self._doEjectSD(ejectDrive), 31, 'Eject')
322 elif explorer.hasExplorer():
323 self.notification.message("Saved as %s" % (targetFilename), lambda : explorer.openExplorer(targetFilename), 4, 'Open folder')
325 self.notification.message("Saved as %s" % (targetFilename))
326 self.printButton.setProgressBar(None)
327 self._engine.getResult().submitInfoOnline()
329 def _doEjectSD(self, drive):
330 if removableStorage.ejectDrive(drive):
331 self.notification.message('You can now eject the card.')
333 self.notification.message('Safe remove failed...')
335 def _showEngineLog(self):
336 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)
340 def OnToolSelect(self, button):
341 if self.rotateToolButton.getSelected():
342 self.tool = previewTools.toolRotate(self)
343 elif self.scaleToolButton.getSelected():
344 self.tool = previewTools.toolScale(self)
345 elif self.mirrorToolButton.getSelected():
346 self.tool = previewTools.toolNone(self)
348 self.tool = previewTools.toolNone(self)
349 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
350 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
351 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
352 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
353 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
354 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
355 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
356 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
358 def updateToolButtons(self):
359 if self._selectedObj is None:
363 self.rotateToolButton.setHidden(hidden)
364 self.scaleToolButton.setHidden(hidden)
365 self.mirrorToolButton.setHidden(hidden)
367 self.rotateToolButton.setSelected(False)
368 self.scaleToolButton.setSelected(False)
369 self.mirrorToolButton.setSelected(False)
372 def OnViewChange(self):
373 if self.viewSelection.getValue() == 4:
374 self.viewMode = 'gcode'
375 elif self.viewSelection.getValue() == 1:
376 self.viewMode = 'overhang'
377 elif self.viewSelection.getValue() == 2:
378 self.viewMode = 'transparent'
379 elif self.viewSelection.getValue() == 3:
380 self.viewMode = 'xray'
382 self.viewMode = 'normal'
383 self._engineResultView.setEnabled(self.viewMode == 'gcode')
386 def OnRotateReset(self, button):
387 if self._selectedObj is None:
389 self._selectedObj.resetRotation()
390 self._scene.pushFree(self._selectedObj)
391 self._selectObject(self._selectedObj)
394 def OnLayFlat(self, button):
395 if self._selectedObj is None:
397 self._selectedObj.layFlat()
398 self._scene.pushFree(self._selectedObj)
399 self._selectObject(self._selectedObj)
402 def OnScaleReset(self, button):
403 if self._selectedObj is None:
405 self._selectedObj.resetScale()
406 self._selectObject(self._selectedObj)
407 self.updateProfileToControls()
410 def OnScaleMax(self, button):
411 if self._selectedObj is None:
413 machine = profile.getMachineSetting('machine_type')
414 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
415 self._scene.pushFree(self._selectedObj)
417 if machine == "ultimaker2":
418 #This is bad and Jaime should feel bad!
419 self._selectedObj.setPosition(numpy.array([0.0,-10.0]))
420 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
421 self._selectedObj.setPosition(numpy.array([0.0,0.0]))
422 self._scene.pushFree(self._selectedObj)
424 self._selectedObj.setPosition(numpy.array([0.0, 0.0]))
425 self._scene.pushFree(self._selectedObj)
426 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
427 self._scene.pushFree(self._selectedObj)
428 self._selectObject(self._selectedObj)
429 self.updateProfileToControls()
432 def OnMirror(self, axis):
433 if self._selectedObj is None:
435 self._selectedObj.mirror(axis)
438 def OnScaleEntry(self, value, axis):
439 if self._selectedObj is None:
445 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
446 self.updateProfileToControls()
447 self._scene.pushFree(self._selectedObj)
448 self._selectObject(self._selectedObj)
451 def OnScaleEntryMM(self, value, axis):
452 if self._selectedObj is None:
458 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
459 self.updateProfileToControls()
460 self._scene.pushFree(self._selectedObj)
461 self._selectObject(self._selectedObj)
464 def OnDeleteAll(self, e):
465 while len(self._scene.objects()) > 0:
466 self._deleteObject(self._scene.objects()[0])
467 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
469 def OnMultiply(self, e):
470 if self._focusObj is None:
473 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
474 if dlg.ShowModal() != wx.ID_OK:
483 self._scene.add(newObj)
484 self._scene.centerAll()
485 if not self._scene.checkPlatform(newObj):
490 self.notification.message("Could not create more then %d items" % (n - 1))
491 self._scene.remove(newObj)
492 self._scene.centerAll()
495 def OnSplitObject(self, e):
496 if self._focusObj is None:
498 self._scene.remove(self._focusObj)
499 for obj in self._focusObj.split(self._splitCallback):
500 if numpy.max(obj.getSize()) > 2.0:
502 self._scene.centerAll()
503 self._selectObject(None)
506 def OnCenter(self, e):
507 if self._focusObj is None:
509 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
510 self._scene.pushFree(self._selectedObj)
511 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
512 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
515 def _splitCallback(self, progress):
518 def OnMergeObjects(self, e):
519 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
520 if len(self._scene.objects()) == 2:
521 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
524 self._scene.merge(self._selectedObj, self._focusObj)
527 def sceneUpdated(self):
528 self._sceneUpdateTimer.Start(500, True)
529 self._engine.abortEngine()
530 self._scene.updateSizeOffsets()
533 def _onRunEngine(self, e):
534 if self._isSimpleMode:
535 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
536 self._engine.runEngine(self._scene)
537 if self._isSimpleMode:
538 profile.resetTempOverride()
540 def _updateEngineProgress(self, progressValue):
541 result = self._engine.getResult()
542 finished = result is not None and result.isFinished()
544 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
546 self.printButton.setDisabled(not finished)
547 if progressValue >= 0.0:
548 self.printButton.setProgressBar(progressValue)
550 self.printButton.setProgressBar(None)
551 self._engineResultView.setResult(result)
553 self.printButton.setProgressBar(None)
554 text = '%s' % (result.getPrintTime())
555 for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))):
556 amount = result.getFilamentAmount(e)
559 text += '\n%s' % (amount)
560 cost = result.getFilamentCost(e)
562 text += '\n%s' % (cost)
563 self.printButton.setBottomText(text)
565 self.printButton.setBottomText('')
568 def loadScene(self, fileList):
569 for filename in fileList:
571 ext = os.path.splitext(filename)[1].lower()
572 if ext in imageToMesh.supportedExtensions():
573 imageToMesh.convertImageDialog(self, filename).Show()
576 objList = meshLoader.loadMeshes(filename)
578 traceback.print_exc()
581 if self._objectLoadShader is not None:
582 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
586 if not self._scene.checkPlatform(obj):
587 self._scene.centerAll()
588 self._selectObject(obj)
589 if obj.getScale()[0] < 1.0:
590 self.notification.message("Warning: Object scaled down.")
593 def _deleteObject(self, obj):
594 if obj == self._selectedObj:
595 self._selectObject(None)
596 if obj == self._focusObj:
597 self._focusObj = None
598 self._scene.remove(obj)
599 for m in obj._meshList:
600 if m.vbo is not None and m.vbo.decRef():
601 self.glReleaseList.append(m.vbo)
606 def _selectObject(self, obj, zoom = True):
607 if obj != self._selectedObj:
608 self._selectedObj = obj
609 self.updateModelSettingsToControls()
610 self.updateToolButtons()
611 if zoom and obj is not None:
612 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
613 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
614 newZoom = obj.getBoundaryCircle() * 6
615 if newZoom > numpy.max(self._machineSize) * 3:
616 newZoom = numpy.max(self._machineSize) * 3
617 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
619 def updateProfileToControls(self):
620 oldSimpleMode = self._isSimpleMode
621 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
622 if self._isSimpleMode != oldSimpleMode:
623 self._scene.arrangeAll()
625 self._scene.updateSizeOffsets(True)
626 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
627 self._objColors[0] = profile.getPreferenceColour('model_colour')
628 self._objColors[1] = profile.getPreferenceColour('model_colour2')
629 self._objColors[2] = profile.getPreferenceColour('model_colour3')
630 self._objColors[3] = profile.getPreferenceColour('model_colour4')
631 self._scene.updateMachineDimensions()
632 self.updateModelSettingsToControls()
634 def updateModelSettingsToControls(self):
635 if self._selectedObj is not None:
636 scale = self._selectedObj.getScale()
637 size = self._selectedObj.getSize()
638 self.scaleXctrl.setValue(round(scale[0], 2))
639 self.scaleYctrl.setValue(round(scale[1], 2))
640 self.scaleZctrl.setValue(round(scale[2], 2))
641 self.scaleXmmctrl.setValue(round(size[0], 2))
642 self.scaleYmmctrl.setValue(round(size[1], 2))
643 self.scaleZmmctrl.setValue(round(size[2], 2))
645 def OnKeyChar(self, keyCode):
646 if self._engineResultView.OnKeyChar(keyCode):
648 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
649 if self._selectedObj is not None:
650 self._deleteObject(self._selectedObj)
652 if keyCode == wx.WXK_UP:
653 if wx.GetKeyState(wx.WXK_SHIFT):
660 elif keyCode == wx.WXK_DOWN:
661 if wx.GetKeyState(wx.WXK_SHIFT):
663 if self._zoom > numpy.max(self._machineSize) * 3:
664 self._zoom = numpy.max(self._machineSize) * 3
668 elif keyCode == wx.WXK_LEFT:
671 elif keyCode == wx.WXK_RIGHT:
674 elif keyCode == wx.WXK_NUMPAD_ADD or keyCode == wx.WXK_ADD or keyCode == ord('+') or keyCode == ord('='):
679 elif keyCode == wx.WXK_NUMPAD_SUBTRACT or keyCode == wx.WXK_SUBTRACT or keyCode == ord('-'):
681 if self._zoom > numpy.max(self._machineSize) * 3:
682 self._zoom = numpy.max(self._machineSize) * 3
684 elif keyCode == wx.WXK_HOME:
688 elif keyCode == wx.WXK_PAGEUP:
692 elif keyCode == wx.WXK_PAGEDOWN:
696 elif keyCode == wx.WXK_END:
701 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
702 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
703 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
704 from collections import defaultdict
705 from gc import get_objects
706 self._beforeLeakTest = defaultdict(int)
707 for i in get_objects():
708 self._beforeLeakTest[type(i)] += 1
709 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
710 from collections import defaultdict
711 from gc import get_objects
712 self._afterLeakTest = defaultdict(int)
713 for i in get_objects():
714 self._afterLeakTest[type(i)] += 1
715 for k in self._afterLeakTest:
716 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
717 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
719 def ShaderUpdate(self, v, f):
720 s = opengl.GLShader(v, f)
722 self._objectLoadShader.release()
723 self._objectLoadShader = s
724 for obj in self._scene.objects():
725 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
728 def OnMouseDown(self,e):
729 self._mouseX = e.GetX()
730 self._mouseY = e.GetY()
731 self._mouseClick3DPos = self._mouse3Dpos
732 self._mouseClickFocus = self._focusObj
734 self._mouseState = 'doubleClick'
736 self._mouseState = 'dragOrClick'
737 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
738 p0 -= self.getObjectCenterPos() - self._viewTarget
739 p1 -= self.getObjectCenterPos() - self._viewTarget
740 if self.tool.OnDragStart(p0, p1):
741 self._mouseState = 'tool'
742 if self._mouseState == 'dragOrClick':
743 if e.GetButton() == 1:
744 if self._focusObj is not None:
745 self._selectObject(self._focusObj, False)
748 def OnMouseUp(self, e):
749 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
751 if self._mouseState == 'dragOrClick':
752 if e.GetButton() == 1:
753 self._selectObject(self._focusObj)
754 if e.GetButton() == 3:
756 if self._focusObj is not None:
757 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
758 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
759 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
760 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
761 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:
762 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
763 if len(self._scene.objects()) > 0:
764 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
765 if menu.MenuItemCount > 0:
768 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
769 self._scene.pushFree(self._selectedObj)
771 elif self._mouseState == 'tool':
772 if self.tempMatrix is not None and self._selectedObj is not None:
773 self._selectedObj.applyMatrix(self.tempMatrix)
774 self._scene.pushFree(self._selectedObj)
775 self._selectObject(self._selectedObj)
776 self.tempMatrix = None
777 self.tool.OnDragEnd()
779 self._mouseState = None
781 def OnMouseMotion(self,e):
782 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
783 p0 -= self.getObjectCenterPos() - self._viewTarget
784 p1 -= self.getObjectCenterPos() - self._viewTarget
786 if e.Dragging() and self._mouseState is not None:
787 if self._mouseState == 'tool':
788 self.tool.OnDrag(p0, p1)
789 elif not e.LeftIsDown() and e.RightIsDown():
790 self._mouseState = 'drag'
791 if wx.GetKeyState(wx.WXK_SHIFT):
792 a = math.cos(math.radians(self._yaw)) / 3.0
793 b = math.sin(math.radians(self._yaw)) / 3.0
794 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
795 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
796 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
797 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
799 self._yaw += e.GetX() - self._mouseX
800 self._pitch -= e.GetY() - self._mouseY
801 if self._pitch > 170:
805 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
806 self._mouseState = 'drag'
807 self._zoom += e.GetY() - self._mouseY
810 if self._zoom > numpy.max(self._machineSize) * 3:
811 self._zoom = numpy.max(self._machineSize) * 3
812 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
813 self._mouseState = 'dragObject'
814 z = max(0, self._mouseClick3DPos[2])
815 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
816 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
821 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
822 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
823 diff = cursorZ1 - cursorZ0
824 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
825 if not e.Dragging() or self._mouseState != 'tool':
826 self.tool.OnMouseMove(p0, p1)
828 self._mouseX = e.GetX()
829 self._mouseY = e.GetY()
831 def OnMouseWheel(self, e):
832 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
833 delta = max(min(delta,4),-4)
834 self._zoom *= 1.0 - delta / 10.0
837 if self._zoom > numpy.max(self._machineSize) * 3:
838 self._zoom = numpy.max(self._machineSize) * 3
841 def OnMouseLeave(self, e):
845 def getMouseRay(self, x, y):
846 if self._viewport is None:
847 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
848 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
849 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
850 p0 -= self._viewTarget
851 p1 -= self._viewTarget
854 def _init3DView(self):
855 # set viewing projection
856 size = self.GetSize()
857 glViewport(0, 0, size.GetWidth(), size.GetHeight())
860 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
862 glDisable(GL_RESCALE_NORMAL)
863 glDisable(GL_LIGHTING)
865 glEnable(GL_DEPTH_TEST)
866 glDisable(GL_CULL_FACE)
868 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
870 glClearColor(0.8, 0.8, 0.8, 1.0)
874 glMatrixMode(GL_PROJECTION)
876 aspect = float(size.GetWidth()) / float(size.GetHeight())
877 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
879 glMatrixMode(GL_MODELVIEW)
881 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
884 connectionGroup = self._printerConnectionManager.getAvailableGroup()
885 if machineCom.machineIsConnected():
886 self.printButton._imageID = 6
887 self.printButton._tooltip = _("Print")
888 elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
889 self.printButton._imageID = 2
890 self.printButton._tooltip = _("Toolpath to SD")
891 elif connectionGroup is not None:
892 self.printButton._imageID = connectionGroup.getIconID()
893 self.printButton._tooltip = _("Print with %s") % (connectionGroup.getName())
895 self.printButton._imageID = 3
896 self.printButton._tooltip = _("Save toolpath")
898 if self._animView is not None:
899 self._viewTarget = self._animView.getPosition()
900 if self._animView.isDone():
901 self._animView = None
902 if self._animZoom is not None:
903 self._zoom = self._animZoom.getPosition()
904 if self._animZoom.isDone():
905 self._animZoom = None
906 if self._objectShader is None:
907 if opengl.hasShaderSupport():
908 self._objectShader = opengl.GLShader("""
909 varying float light_amount;
913 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
914 gl_FrontColor = gl_Color;
916 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
920 varying float light_amount;
924 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
927 self._objectOverhangShader = opengl.GLShader("""
928 uniform float cosAngle;
929 uniform mat3 rotMatrix;
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)));
939 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
941 light_amount = -10.0;
945 varying float light_amount;
949 if (light_amount == -10.0)
951 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
953 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
957 self._objectLoadShader = opengl.GLShader("""
958 uniform float intensity;
960 varying float light_amount;
964 vec4 tmp = gl_Vertex;
965 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
966 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
967 gl_Position = gl_ModelViewProjectionMatrix * tmp;
968 gl_FrontColor = gl_Color;
970 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
974 uniform float intensity;
975 varying float light_amount;
979 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
982 if self._objectShader is None or not self._objectShader.isValid():
983 self._objectShader = opengl.GLFakeShader()
984 self._objectOverhangShader = opengl.GLFakeShader()
985 self._objectLoadShader = None
987 glTranslate(0,0,-self._zoom)
988 glRotate(-self._pitch, 1,0,0)
989 glRotate(self._yaw, 0,0,1)
990 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
992 self._viewport = glGetIntegerv(GL_VIEWPORT)
993 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
994 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
996 glClearColor(1,1,1,1)
997 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
999 if self.viewMode != 'gcode':
1000 for n in xrange(0, len(self._scene.objects())):
1001 obj = self._scene.objects()[n]
1002 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
1003 self._renderObject(obj)
1005 if self._mouseX > -1:
1007 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
1008 if n < len(self._scene.objects()):
1009 self._focusObj = self._scene.objects()[n]
1011 self._focusObj = None
1012 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
1013 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
1014 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
1015 self._mouse3Dpos -= self._viewTarget
1018 glTranslate(0,0,-self._zoom)
1019 glRotate(-self._pitch, 1,0,0)
1020 glRotate(self._yaw, 0,0,1)
1021 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
1023 self._objectShader.unbind()
1024 self._engineResultView.OnDraw()
1025 if self.viewMode != 'gcode':
1026 glStencilFunc(GL_ALWAYS, 1, 1)
1027 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1029 if self.viewMode == 'overhang':
1030 self._objectOverhangShader.bind()
1031 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - profile.getProfileSettingFloat('support_angle'))))
1033 self._objectShader.bind()
1034 for obj in self._scene.objects():
1035 if obj._loadAnim is not None:
1036 if obj._loadAnim.isDone():
1037 obj._loadAnim = None
1041 if self._focusObj == obj:
1043 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1046 if self._selectedObj == obj or self._selectedObj is None:
1047 #If we want transparent, then first render a solid black model to remove the printer size lines.
1048 if self.viewMode == 'transparent':
1049 glColor4f(0, 0, 0, 0)
1050 self._renderObject(obj)
1052 glBlendFunc(GL_ONE, GL_ONE)
1053 glDisable(GL_DEPTH_TEST)
1055 if self.viewMode == 'xray':
1056 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1057 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1058 glEnable(GL_STENCIL_TEST)
1060 if self.viewMode == 'overhang':
1061 if self._selectedObj == obj and self.tempMatrix is not None:
1062 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1064 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1066 if not self._scene.checkPlatform(obj):
1067 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1068 self._renderObject(obj)
1070 self._renderObject(obj, brightness)
1071 glDisable(GL_STENCIL_TEST)
1073 glEnable(GL_DEPTH_TEST)
1074 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1076 if self.viewMode == 'xray':
1079 glEnable(GL_STENCIL_TEST)
1080 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1081 glDisable(GL_DEPTH_TEST)
1082 for i in xrange(2, 15, 2):
1083 glStencilFunc(GL_EQUAL, i, 0xFF)
1084 glColor(float(i)/10, float(i)/10, float(i)/5)
1086 glVertex3f(-1000,-1000,-10)
1087 glVertex3f( 1000,-1000,-10)
1088 glVertex3f( 1000, 1000,-10)
1089 glVertex3f(-1000, 1000,-10)
1091 for i in xrange(1, 15, 2):
1092 glStencilFunc(GL_EQUAL, i, 0xFF)
1093 glColor(float(i)/10, 0, 0)
1095 glVertex3f(-1000,-1000,-10)
1096 glVertex3f( 1000,-1000,-10)
1097 glVertex3f( 1000, 1000,-10)
1098 glVertex3f(-1000, 1000,-10)
1101 glDisable(GL_STENCIL_TEST)
1102 glEnable(GL_DEPTH_TEST)
1104 self._objectShader.unbind()
1106 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1108 if self._objectLoadShader is not None:
1109 self._objectLoadShader.bind()
1110 glColor4f(0.2, 0.6, 1.0, 1.0)
1111 for obj in self._scene.objects():
1112 if obj._loadAnim is None:
1114 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1115 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1116 self._renderObject(obj)
1117 self._objectLoadShader.unbind()
1122 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._engine.getID():
1123 z = self._usbPrintMonitor.getZ()
1124 if self.viewMode == 'gcode':
1125 layer_height = profile.getProfileSettingFloat('layer_height')
1126 layer1_height = profile.getProfileSettingFloat('bottom_thickness')
1127 if layer_height > 0:
1128 if layer1_height > 0:
1129 layer = int((z - layer1_height) / layer_height) + 1
1131 layer = int(z / layer_height)
1134 self.layerSelect.setValue(layer)
1136 size = self._machineSize
1138 glColor4ub(255,255,0,128)
1140 glVertex3f(-size[0]/2,-size[1]/2, z)
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)
1146 if self.viewMode != 'gcode':
1147 #Draw the object box-shadow, so you can see where it will collide with other objects.
1148 if self._selectedObj is not None:
1150 glEnable(GL_CULL_FACE)
1151 glColor4f(0,0,0,0.16)
1153 for obj in self._scene.objects():
1155 glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0)
1156 glBegin(GL_TRIANGLE_FAN)
1157 for p in obj._boundaryHull[::-1]:
1158 glVertex3f(p[0], p[1], 0)
1161 if self._scene.isOneAtATime():
1163 glColor4f(0,0,0,0.06)
1164 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1165 glBegin(GL_TRIANGLE_FAN)
1166 for p in self._selectedObj._printAreaHull[::-1]:
1167 glVertex3f(p[0], p[1], 0)
1169 glBegin(GL_TRIANGLE_FAN)
1170 for p in self._selectedObj._headAreaMinHull[::-1]:
1171 glVertex3f(p[0], p[1], 0)
1175 glDisable(GL_CULL_FACE)
1177 #Draw the outline of the selected object, on top of everything else except the GUI.
1178 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1179 glDisable(GL_DEPTH_TEST)
1180 glEnable(GL_CULL_FACE)
1181 glEnable(GL_STENCIL_TEST)
1183 glStencilFunc(GL_EQUAL, 0, 255)
1185 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1187 glColor4f(1,1,1,0.5)
1188 self._renderObject(self._selectedObj)
1189 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1191 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1192 glDisable(GL_STENCIL_TEST)
1193 glDisable(GL_CULL_FACE)
1194 glEnable(GL_DEPTH_TEST)
1196 if self._selectedObj is not None:
1198 pos = self.getObjectCenterPos()
1199 glTranslate(pos[0], pos[1], pos[2])
1202 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1203 glDisable(GL_DEPTH_TEST)
1206 glTranslate(0,-4,-10)
1207 glColor4ub(60,60,60,255)
1208 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1211 def _renderObject(self, obj, brightness = False, addSink = True):
1214 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1216 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1218 if self.tempMatrix is not None and obj == self._selectedObj:
1219 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1220 glMultMatrixf(tempMatrix)
1222 offset = obj.getDrawOffset()
1223 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1225 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1226 glMultMatrixf(tempMatrix)
1229 for m in obj._meshList:
1231 m.vbo = opengl.GLVBO(GL_TRIANGLES, m.vertexes, m.normal)
1233 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1238 def _drawMachine(self):
1239 glEnable(GL_CULL_FACE)
1242 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1244 machine = profile.getMachineSetting('machine_type')
1245 if machine.startswith('ultimaker'):
1246 if machine not in self._platformMesh:
1247 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1249 self._platformMesh[machine] = meshes[0]
1251 self._platformMesh[machine] = None
1252 if machine == 'ultimaker2':
1253 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1255 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1256 glColor4f(1,1,1,0.5)
1257 self._objectShader.bind()
1258 self._renderObject(self._platformMesh[machine], False, False)
1259 self._objectShader.unbind()
1261 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1262 if machine == 'ultimaker2':
1263 if not hasattr(self._platformMesh[machine], 'texture'):
1264 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1265 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1266 glEnable(GL_TEXTURE_2D)
1270 glTranslate(0,150,-5)
1275 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1278 glVertex3f( w, 0, h)
1280 glVertex3f(-w, 0, h)
1282 glVertex3f(-w, 0, 0)
1284 glVertex3f( w, 0, 0)
1287 glVertex3f(-w, d, h)
1289 glVertex3f( w, d, h)
1291 glVertex3f( w, d, 0)
1293 glVertex3f(-w, d, 0)
1295 glDisable(GL_TEXTURE_2D)
1296 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1302 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1303 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1304 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1305 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1306 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1307 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1312 polys = profile.getMachineSizePolygons()
1313 height = profile.getMachineSettingFloat('machine_height')
1314 circular = profile.getMachineSetting('machine_shape') == 'Circular'
1316 for n in xrange(0, len(polys[0])):
1319 glColor4ub(5, 171, 231, 96)
1321 glColor4ub(5, 171, 231, 64)
1323 glColor4ub(5, 171, 231, 96)
1325 glVertex3f(polys[0][n][0], polys[0][n][1], height)
1326 glVertex3f(polys[0][n][0], polys[0][n][1], 0)
1327 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0)
1328 glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height)
1330 glColor4ub(5, 171, 231, 128)
1331 glBegin(GL_TRIANGLE_FAN)
1332 for p in polys[0][::-1]:
1333 glVertex3f(p[0], p[1], height)
1337 if self._platformTexture is None:
1338 self._platformTexture = opengl.loadGLTexture('checkerboard.png')
1339 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1340 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
1341 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
1342 glColor4f(1,1,1,0.5)
1343 glBindTexture(GL_TEXTURE_2D, self._platformTexture)
1344 glEnable(GL_TEXTURE_2D)
1345 glBegin(GL_TRIANGLE_FAN)
1347 glTexCoord2f(p[0]/20, p[1]/20)
1348 glVertex3f(p[0], p[1], 0)
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([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1383 return self._selectedObj.getMatrix()
1385 class shaderEditor(wx.Dialog):
1386 def __init__(self, parent, callback, v, f):
1387 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1388 self._callback = callback
1389 s = wx.BoxSizer(wx.VERTICAL)
1391 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1392 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1393 s.Add(self._vertex, 1, flag=wx.EXPAND)
1394 s.Add(self._fragment, 1, flag=wx.EXPAND)
1396 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1397 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1399 self.SetPosition(self.GetParent().GetPosition())
1400 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1403 def OnText(self, e):
1404 self._callback(self._vertex.GetValue(), self._fragment.GetValue())