X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=Cura%2Fgui%2FsceneView.py;h=337a18835306c54f4c2ba590ac2cbcabc99519b5;hb=6208201bafdeaf82883181471c6da3a41283cfe7;hp=5b59e78a814fddd7054d3dbe2de8eb8b05e9ee58;hpb=329cb909b0afd4662848b1ed8ce961840c3de69e;p=cura.git diff --git a/Cura/gui/sceneView.py b/Cura/gui/sceneView.py index 5b59e78a..337a1883 100644 --- a/Cura/gui/sceneView.py +++ b/Cura/gui/sceneView.py @@ -8,13 +8,16 @@ import os import traceback import threading import math +import platform +import cStringIO as StringIO import OpenGL -OpenGL.ERROR_CHECKING = False +#OpenGL.ERROR_CHECKING = False from OpenGL.GLU import * from OpenGL.GL import * from Cura.gui import printWindow +from Cura.gui import printWindow2 from Cura.util import profile from Cura.util import meshLoader from Cura.util import objectScene @@ -22,11 +25,14 @@ from Cura.util import resources from Cura.util import sliceEngine from Cura.util import machineCom from Cura.util import removableStorage -from Cura.util import gcodeInterpreter +from Cura.util import explorer +from Cura.util.printerConnection import printerConnectionManager from Cura.gui.util import previewTools from Cura.gui.util import opengl from Cura.gui.util import openglGui +from Cura.gui.util import engineResultView from Cura.gui.tools import youmagineGui +from Cura.gui.tools import imageToMesh class SceneView(openglGui.glGuiPanel): def __init__(self, parent): @@ -36,10 +42,6 @@ class SceneView(openglGui.glGuiPanel): self._pitch = 60 self._zoom = 300 self._scene = objectScene.Scene() - self._gcode = None - self._gcodeVBOs = [] - self._gcodeFilename = None - self._gcodeLoadThread = None self._objectShader = None self._objectLoadShader = None self._focusObj = None @@ -51,34 +53,35 @@ class SceneView(openglGui.glGuiPanel): self._viewTarget = numpy.array([0,0,0], numpy.float32) self._animView = None self._animZoom = None - self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0] - self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32) + self._platformMesh = {} + self._platformTexture = None self._isSimpleMode = True self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh()) + self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager() self._viewport = None self._modelMatrix = None self._projMatrix = None self.tempMatrix = None - self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel) - self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.OnPrintButton) + self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel) + self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton) self.printButton.setDisabled(True) group = [] - self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect) - self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect) - self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect) + self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect) + self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect) + self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect) - self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset) - self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat) + self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset) + self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat) - self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset) - self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax) + self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset) + self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax) - self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0)) - self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1)) - self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2)) + self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0)) + self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1)) + self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2)) self.rotateToolButton.setExpandArrow(True) self.scaleToolButton.setExpandArrow(True) @@ -86,31 +89,32 @@ class SceneView(openglGui.glGuiPanel): self.scaleForm = openglGui.glFrame(self, (2, -2)) openglGui.glGuiLayoutGrid(self.scaleForm) - openglGui.glLabel(self.scaleForm, 'Scale X', (0,0)) + openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0)) self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0)) - openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1)) + openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1)) self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1)) - openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2)) + openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2)) self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2)) - openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4)) + openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4)) self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0)) - openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5)) + openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5)) self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1)) - openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6)) + openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6)) self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2)) - openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8)) + openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8)) self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None) - self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange) - self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh()) + self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange) - self.youMagineButton = openglGui.glButton(self, 26, 'YouMagine upload', (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self)) + self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene)) + self.youMagineButton.setDisabled(True) self.notification = openglGui.glNotification(self, (0, 0)) - self._slicer = sliceEngine.Slicer(self._updateSliceProgress) + self._engine = sliceEngine.Engine(self._updateEngineProgress) + self._engineResultView = engineResultView.engineResultView(self) self._sceneUpdateTimer = wx.Timer(self) - self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer) + self.Bind(wx.EVT_TIMER, self._onRunEngine, self._sceneUpdateTimer) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) @@ -119,10 +123,76 @@ class SceneView(openglGui.glGuiPanel): self.updateToolButtons() self.updateProfileToControls() + def loadGCodeFile(self, filename): + self.OnDeleteAll(None) + #TODO: Load straight GCodeFile + self.printButton.setBottomText('') + self.viewSelection.setValue(4) + self.printButton.setDisabled(False) + self.youMagineButton.setDisabled(True) + self.OnViewChange() + + def loadSceneFiles(self, filenames): + self.youMagineButton.setDisabled(False) + #if self.viewSelection.getValue() == 4: + # self.viewSelection.setValue(0) + # self.OnViewChange() + self.loadScene(filenames) + + def loadFiles(self, filenames): + mainWindow = self.GetParent().GetParent().GetParent() + # only one GCODE file can be active + # so if single gcode file, process this + # otherwise ignore all gcode files + gcodeFilename = None + if len(filenames) == 1: + filename = filenames[0] + ext = os.path.splitext(filename)[1].lower() + if ext == '.g' or ext == '.gcode': + gcodeFilename = filename + mainWindow.addToModelMRU(filename) + if gcodeFilename is not None: + self.loadGCodeFile(gcodeFilename) + else: + # process directories and special file types + # and keep scene files for later processing + scene_filenames = [] + ignored_types = dict() + # use file list as queue + # pop first entry for processing and append new files at end + while filenames: + filename = filenames.pop(0) + if os.path.isdir(filename): + # directory: queue all included files and directories + filenames.extend(os.path.join(filename, f) for f in os.listdir(filename)) + else: + ext = os.path.splitext(filename)[1].lower() + if ext == '.ini': + profile.loadProfile(filename) + mainWindow.addToProfileMRU(filename) + elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions(): + scene_filenames.append(filename) + mainWindow.addToModelMRU(filename) + else: + ignored_types[ext] = 1 + if ignored_types: + ignored_types = ignored_types.keys() + ignored_types.sort() + self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types)) + mainWindow.updateProfileToAllControls() + # now process all the scene files + if scene_filenames: + self.loadSceneFiles(scene_filenames) + self._selectObject(None) + self.sceneUpdated() + newZoom = numpy.max(self._machineSize) + self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5) + self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5) + def showLoadModel(self, button = 1): if button == 1: - 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) - dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE") + 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) + dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE") if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return @@ -131,35 +201,12 @@ class SceneView(openglGui.glGuiPanel): if len(filenames) < 1: return False profile.putPreference('lastFile', filenames[0]) - gcodeFilename = None - for filename in filenames: - self.GetParent().GetParent().GetParent().addToModelMRU(filename) - ext = filename[filename.rfind('.')+1:].upper() - if ext == 'G' or ext == 'GCODE': - gcodeFilename = filename - if gcodeFilename is not None: - if self._gcode is not None: - self._gcode = None - for layerVBOlist in self._gcodeVBOs: - for vbo in layerVBOlist: - self.glReleaseList.append(vbo) - self._gcodeVBOs = [] - self._gcode = gcodeInterpreter.gcode() - self._gcodeFilename = gcodeFilename - self.printButton.setBottomText('') - self.viewSelection.setValue(4) - self.printButton.setDisabled(False) - self.OnViewChange() - else: - if self.viewSelection.getValue() == 4: - self.viewSelection.setValue(0) - self.OnViewChange() - self.loadScene(filenames) + self.loadFiles(filenames) def showSaveModel(self): if len(self._scene.objects()) < 1: return - dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) + dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) dlg.SetWildcard(meshLoader.saveWildcardFilter()) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() @@ -170,9 +217,10 @@ class SceneView(openglGui.glGuiPanel): def OnPrintButton(self, button): if button == 1: + connectionGroup = self._printerConnectionManager.getAvailableGroup() if machineCom.machineIsConnected(): self.showPrintWindow() - elif len(removableStorage.getPossibleSDcardDrives()) > 0: + elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0): drives = removableStorage.getPossibleSDcardDrives() if len(drives) > 1: 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)) @@ -184,29 +232,65 @@ class SceneView(openglGui.glGuiPanel): else: drive = drives[0] filename = self._scene._objectList[0].getName() + '.gcode' - threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start() + threading.Thread(target=self._saveGCode,args=(drive[1] + filename, drive[1])).start() + elif connectionGroup is not None: + connections = connectionGroup.getAvailableConnections() + if len(connections) < 2: + connection = connections[0] + else: + dlg = wx.SingleChoiceDialog(self, "Select the %s connection to use" % (connectionGroup.getName()), "Multiple %s connections found" % (connectionGroup.getName()), map(lambda n: n.getName(), connections)) + if dlg.ShowModal() != wx.ID_OK: + dlg.Destroy() + return + connection = connections[dlg.GetSelection()] + dlg.Destroy() + self._openPrintWindowForConnection(connection) else: self.showSaveGCode() if button == 3: menu = wx.Menu() - self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, 'Print with USB')) - self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...')) - self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...')) + self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB"))) + connections = self._printerConnectionManager.getAvailableConnections() + menu.connectionMap = {} + for connection in connections: + i = menu.Append(-1, _("Print with %s") % (connection.getName())) + menu.connectionMap[i.GetId()] = connection + self.Bind(wx.EVT_MENU, lambda e: self._openPrintWindowForConnection(e.GetEventObject().connectionMap[e.GetId()]), i) + self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode..."))) + self.Bind(wx.EVT_MENU, lambda e: self._showEngineLog(), menu.Append(-1, _("Slice engine log..."))) self.PopupMenu(menu) menu.Destroy() + def _openPrintWindowForConnection(self, connection): + print '_openPrintWindowForConnection', connection.getName() + if connection.window is None or not connection.window: + connection.window = printWindow2.printWindow(connection) + connection.window.Show() + connection.window.Raise() + #TODO: Fix for _engine.getResult + if not connection.loadFile(self._gcodeFilename): + if connection.isPrinting(): + self.notification.message("Cannot start print, because other print still running.") + else: + self.notification.message("Failed to start print...") + def showPrintWindow(self): if self._gcodeFilename is None: return - self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID()) - if self._gcodeFilename == self._slicer.getGCodeFilename(): - self._slicer.submitSliceInfoOnline() + if profile.getMachineSetting('gcode_flavor') == 'UltiGCode': + wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING) + return + #TODO: Fix for _engine.getResult + self._usbPrintMonitor.loadFile(self._gcodeFilename, self._engine.getID()) + if self._gcodeFilename is None: + self._engine.submitInfoOnline() def showSaveGCode(self): - defPath = profile.getPreference('lastFile') - defPath = defPath[0:defPath.rfind('.')] + '.gcode' - dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE) - dlg.SetFilename(self._scene._objectList[0].getName()) + if len(self._scene._objectList) < 1: + return + dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE) + filename = self._scene._objectList[0].getName() + '.gcode' + dlg.SetFilename(filename) dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g') if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() @@ -214,35 +298,43 @@ class SceneView(openglGui.glGuiPanel): filename = dlg.GetPath() dlg.Destroy() - threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start() + threading.Thread(target=self._saveGCode,args=(filename,)).start() - def _copyFile(self, fileA, fileB, allowEject = False): + def _saveGCode(self, targetFilename, ejectDrive = False): + data = self._engine.getResult().getGCode() try: - size = float(os.stat(fileA).st_size) - with open(fileA, 'rb') as fsrc: - with open(fileB, 'wb') as fdst: - while 1: - buf = fsrc.read(16*1024) - if not buf: - break - fdst.write(buf) - self.printButton.setProgressBar(float(fsrc.tell()) / size) - self._queueRefresh() + size = float(len(data)) + fsrc = StringIO.StringIO(data) + with open(targetFilename, 'wb') as fdst: + while 1: + buf = fsrc.read(16*1024) + if not buf: + break + fdst.write(buf) + self.printButton.setProgressBar(float(fsrc.tell()) / size) + self._queueRefresh() except: - import sys - print sys.exc_info() + import sys, traceback + traceback.print_exc() self.notification.message("Failed to save") else: - if allowEject: - self.notification.message("Saved as %s" % (fileB), lambda : self.notification.message('You can now eject the card.') if removableStorage.ejectDrive(allowEject) else self.notification.message('Safe remove failed...')) + if ejectDrive: + self.notification.message("Saved as %s" % (targetFilename), lambda : self._doEjectSD(ejectDrive), 31, 'Eject') + elif explorer.hasExplorer(): + self.notification.message("Saved as %s" % (targetFilename), lambda : explorer.openExplorer(targetFilename), 4, 'Open folder') else: - self.notification.message("Saved as %s" % (fileB)) + self.notification.message("Saved as %s" % (targetFilename)) self.printButton.setProgressBar(None) - if fileA == self._slicer.getGCodeFilename(): - self._slicer.submitSliceInfoOnline() + self._engine.getResult().submitInfoOnline() + + def _doEjectSD(self, drive): + if removableStorage.ejectDrive(drive): + self.notification.message('You can now eject the card.') + else: + self.notification.message('Safe remove failed...') - def _showSliceLog(self): - dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE) + def _showEngineLog(self): + 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) dlg.ShowModal() dlg.Destroy() @@ -281,9 +373,6 @@ class SceneView(openglGui.glGuiPanel): def OnViewChange(self): if self.viewSelection.getValue() == 4: self.viewMode = 'gcode' - if self._gcode is not None and self._gcode.layerList is not None: - self.layerSelect.setRange(1, len(self._gcode.layerList) - 1) - self._selectObject(None) elif self.viewSelection.getValue() == 1: self.viewMode = 'overhang' elif self.viewSelection.getValue() == 2: @@ -292,14 +381,14 @@ class SceneView(openglGui.glGuiPanel): self.viewMode = 'xray' else: self.viewMode = 'normal' - self.layerSelect.setHidden(self.viewMode != 'gcode') + self._engineResultView.setEnabled(self.viewMode == 'gcode') self.QueueRefresh() def OnRotateReset(self, button): if self._selectedObj is None: return self._selectedObj.resetRotation() - self._scene.pushFree() + self._scene.pushFree(self._selectedObj) self._selectObject(self._selectedObj) self.sceneUpdated() @@ -307,7 +396,7 @@ class SceneView(openglGui.glGuiPanel): if self._selectedObj is None: return self._selectedObj.layFlat() - self._scene.pushFree() + self._scene.pushFree(self._selectedObj) self._selectObject(self._selectedObj) self.sceneUpdated() @@ -322,8 +411,21 @@ class SceneView(openglGui.glGuiPanel): def OnScaleMax(self, button): if self._selectedObj is None: return - self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32)) - self._scene.pushFree() + machine = profile.getMachineSetting('machine_type') + self._selectedObj.setPosition(numpy.array([0.0, 0.0])) + self._scene.pushFree(self._selectedObj) + #self.sceneUpdated() + if machine == "ultimaker2": + #This is bad and Jaime should feel bad! + self._selectedObj.setPosition(numpy.array([0.0,-10.0])) + self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32)) + self._selectedObj.setPosition(numpy.array([0.0,0.0])) + self._scene.pushFree(self._selectedObj) + else: + self._selectedObj.setPosition(numpy.array([0.0, 0.0])) + self._scene.pushFree(self._selectedObj) + self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32)) + self._scene.pushFree(self._selectedObj) self._selectObject(self._selectedObj) self.updateProfileToControls() self.sceneUpdated() @@ -343,7 +445,7 @@ class SceneView(openglGui.glGuiPanel): return self._selectedObj.setScale(value, axis, self.scaleUniform.getValue()) self.updateProfileToControls() - self._scene.pushFree() + self._scene.pushFree(self._selectedObj) self._selectObject(self._selectedObj) self.sceneUpdated() @@ -356,7 +458,7 @@ class SceneView(openglGui.glGuiPanel): return self._selectedObj.setSize(value, axis, self.scaleUniform.getValue()) self.updateProfileToControls() - self._scene.pushFree() + self._scene.pushFree(self._selectedObj) self._selectObject(self._selectedObj) self.sceneUpdated() @@ -369,7 +471,7 @@ class SceneView(openglGui.glGuiPanel): if self._focusObj is None: return obj = self._focusObj - dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100) + dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return @@ -402,76 +504,77 @@ class SceneView(openglGui.glGuiPanel): self._selectObject(None) self.sceneUpdated() + def OnCenter(self, e): + if self._focusObj is None: + return + self._focusObj.setPosition(numpy.array([0.0, 0.0])) + self._scene.pushFree(self._selectedObj) + newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2]) + self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5) + self.sceneUpdated() + def _splitCallback(self, progress): print progress def OnMergeObjects(self, e): if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj: + if len(self._scene.objects()) == 2: + self._scene.merge(self._scene.objects()[0], self._scene.objects()[1]) + self.sceneUpdated() return self._scene.merge(self._selectedObj, self._focusObj) self.sceneUpdated() def sceneUpdated(self): self._sceneUpdateTimer.Start(500, True) - self._slicer.abortSlicer() - self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32)) + self._engine.abortEngine() + self._scene.updateSizeOffsets() self.QueueRefresh() - def _onRunSlicer(self, e): + def _onRunEngine(self, e): if self._isSimpleMode: self.GetTopLevelParent().simpleSettingsPanel.setupSlice() - self._slicer.runSlicer(self._scene) + self._engine.runEngine(self._scene) if self._isSimpleMode: profile.resetTempOverride() - def _updateSliceProgress(self, progressValue, ready): - if not ready: + def _updateEngineProgress(self, progressValue): + result = self._engine.getResult() + finished = result is not None and result.isFinished() + if not finished: if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01: return - self.printButton.setDisabled(not ready) + self.printButton.setDisabled(not finished) if progressValue >= 0.0: self.printButton.setProgressBar(progressValue) else: self.printButton.setProgressBar(None) - if self._gcode is not None: - self._gcode = None - for layerVBOlist in self._gcodeVBOs: - for vbo in layerVBOlist: - self.glReleaseList.append(vbo) - self._gcodeVBOs = [] - if ready: + self._engineResultView.setResult(result) + if finished: self.printButton.setProgressBar(None) - cost = self._slicer.getFilamentCost() - if cost is not None: - self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost)) - else: - self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount())) - self._gcode = gcodeInterpreter.gcode() - self._gcodeFilename = self._slicer.getGCodeFilename() + text = '%s' % (result.getPrintTime()) + for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))): + amount = result.getFilamentAmount(e) + if amount is None: + continue + text += '\n%s' % (amount) + cost = result.getFilamentCost(e) + if cost is not None: + text += '\n%s' % (cost) + self.printButton.setBottomText(text) else: self.printButton.setBottomText('') self.QueueRefresh() - def _loadGCode(self): - self._gcode.progressCallback = self._gcodeLoadCallback - self._gcode.load(self._gcodeFilename) - - def _gcodeLoadCallback(self, progress): - if self._gcode is None: - return True - if len(self._gcode.layerList) % 15 == 0: - time.sleep(0.1) - if self._gcode is None: - return True - self.layerSelect.setRange(1, len(self._gcode.layerList) - 1) - if self.viewMode == 'gcode': - self._queueRefresh() - return False - def loadScene(self, fileList): for filename in fileList: try: - objList = meshLoader.loadMeshes(filename) + ext = os.path.splitext(filename)[1].lower() + if ext in imageToMesh.supportedExtensions(): + imageToMesh.convertImageDialog(self, filename).Show() + objList = [] + else: + objList = meshLoader.loadMeshes(filename) except: traceback.print_exc() else: @@ -481,7 +584,8 @@ class SceneView(openglGui.glGuiPanel): else: obj._loadAnim = None self._scene.add(obj) - self._scene.centerAll() + if not self._scene.checkPlatform(obj): + self._scene.centerAll() self._selectObject(obj) if obj.getScale()[0] < 1.0: self.notification.message("Warning: Object scaled down.") @@ -503,10 +607,10 @@ class SceneView(openglGui.glGuiPanel): def _selectObject(self, obj, zoom = True): if obj != self._selectedObj: self._selectedObj = obj - self.updateProfileToControls() + self.updateModelSettingsToControls() self.updateToolButtons() if zoom and obj is not None: - newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2]) + newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2]) self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5) newZoom = obj.getBoundaryCircle() * 6 if newZoom > numpy.max(self._machineSize) * 3: @@ -519,15 +623,16 @@ class SceneView(openglGui.glGuiPanel): if self._isSimpleMode != oldSimpleMode: self._scene.arrangeAll() self.sceneUpdated() - self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]) + self._scene.updateSizeOffsets(True) + self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]) self._objColors[0] = profile.getPreferenceColour('model_colour') self._objColors[1] = profile.getPreferenceColour('model_colour2') self._objColors[2] = profile.getPreferenceColour('model_colour3') self._objColors[3] = profile.getPreferenceColour('model_colour4') - self._scene.setMachineSize(self._machineSize) - self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32)) - self._scene.setHeadSize(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_min_y'), profile.getPreferenceFloat('extruder_head_size_max_y'), profile.getPreferenceFloat('extruder_head_size_height')) + self._scene.updateMachineDimensions() + self.updateModelSettingsToControls() + def updateModelSettingsToControls(self): if self._selectedObj is not None: scale = self._selectedObj.getScale() size = self._selectedObj.getSize() @@ -539,21 +644,59 @@ class SceneView(openglGui.glGuiPanel): self.scaleZmmctrl.setValue(round(size[2], 2)) def OnKeyChar(self, keyCode): - if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE: + if self._engineResultView.OnKeyChar(keyCode): + return + if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"): if self._selectedObj is not None: self._deleteObject(self._selectedObj) self.QueueRefresh() if keyCode == wx.WXK_UP: - self.layerSelect.setValue(self.layerSelect.getValue() + 1) + if wx.GetKeyState(wx.WXK_SHIFT): + self._zoom /= 1.2 + if self._zoom < 1: + self._zoom = 1 + else: + self._pitch -= 15 self.QueueRefresh() elif keyCode == wx.WXK_DOWN: - self.layerSelect.setValue(self.layerSelect.getValue() - 1) + if wx.GetKeyState(wx.WXK_SHIFT): + self._zoom *= 1.2 + if self._zoom > numpy.max(self._machineSize) * 3: + self._zoom = numpy.max(self._machineSize) * 3 + else: + self._pitch += 15 + self.QueueRefresh() + elif keyCode == wx.WXK_LEFT: + self._yaw -= 15 + self.QueueRefresh() + elif keyCode == wx.WXK_RIGHT: + self._yaw += 15 + self.QueueRefresh() + elif keyCode == wx.WXK_NUMPAD_ADD or keyCode == wx.WXK_ADD or keyCode == ord('+') or keyCode == ord('='): + self._zoom /= 1.2 + if self._zoom < 1: + self._zoom = 1 + self.QueueRefresh() + elif keyCode == wx.WXK_NUMPAD_SUBTRACT or keyCode == wx.WXK_SUBTRACT or keyCode == ord('-'): + self._zoom *= 1.2 + if self._zoom > numpy.max(self._machineSize) * 3: + self._zoom = numpy.max(self._machineSize) * 3 + self.QueueRefresh() + elif keyCode == wx.WXK_HOME: + self._yaw = 30 + self._pitch = 60 self.QueueRefresh() elif keyCode == wx.WXK_PAGEUP: - self.layerSelect.setValue(self.layerSelect.getValue() + 10) + self._yaw = 0 + self._pitch = 0 self.QueueRefresh() elif keyCode == wx.WXK_PAGEDOWN: - self.layerSelect.setValue(self.layerSelect.getValue() - 10) + self._yaw = 0 + self._pitch = 90 + self.QueueRefresh() + elif keyCode == wx.WXK_END: + self._yaw = 90 + self._pitch = 90 self.QueueRefresh() if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT): @@ -612,23 +755,24 @@ class SceneView(openglGui.glGuiPanel): if e.GetButton() == 3: menu = wx.Menu() if self._focusObj is not None: - self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete')) - self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply')) - self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split')) - if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1: - self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge')) + self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object"))) + self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform"))) + self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object"))) + self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts"))) + 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: + self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge"))) if len(self._scene.objects()) > 0: - self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all')) + self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects"))) if menu.MenuItemCount > 0: self.PopupMenu(menu) menu.Destroy() elif self._mouseState == 'dragObject' and self._selectedObj is not None: - self._scene.pushFree() + self._scene.pushFree(self._selectedObj) self.sceneUpdated() elif self._mouseState == 'tool': if self.tempMatrix is not None and self._selectedObj is not None: self._selectedObj.applyMatrix(self.tempMatrix) - self._scene.pushFree() + self._scene.pushFree(self._selectedObj) self._selectObject(self._selectedObj) self.tempMatrix = None self.tool.OnDragEnd() @@ -738,15 +882,19 @@ class SceneView(openglGui.glGuiPanel): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) def OnPaint(self,e): + connectionGroup = self._printerConnectionManager.getAvailableGroup() if machineCom.machineIsConnected(): self.printButton._imageID = 6 - self.printButton._tooltip = 'Print' - elif len(removableStorage.getPossibleSDcardDrives()) > 0: + self.printButton._tooltip = _("Print") + elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0): self.printButton._imageID = 2 - self.printButton._tooltip = 'Toolpath to SD' + self.printButton._tooltip = _("Toolpath to SD") + elif connectionGroup is not None: + self.printButton._imageID = connectionGroup.getIconID() + self.printButton._tooltip = _("Print with %s") % (connectionGroup.getName()) else: self.printButton._imageID = 3 - self.printButton._tooltip = 'Save toolpath' + self.printButton._tooltip = _("Save toolpath") if self._animView is not None: self._viewTarget = self._animView.getPosition() @@ -756,11 +904,6 @@ class SceneView(openglGui.glGuiPanel): self._zoom = self._animZoom.getPosition() if self._animZoom.isDone(): self._animZoom = None - if self.viewMode == 'gcode' and self._gcode is not None: - try: - self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2] - except: - pass if self._objectShader is None: if opengl.hasShaderSupport(): self._objectShader = opengl.GLShader(""" @@ -837,7 +980,7 @@ void main(void) gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity); } """) - if self._objectShader == None or not self._objectShader.isValid(): + if self._objectShader is None or not self._objectShader.isValid(): self._objectShader = opengl.GLFakeShader() self._objectOverhangShader = opengl.GLFakeShader() self._objectLoadShader = None @@ -878,72 +1021,15 @@ void main(void) glRotate(self._yaw, 0,0,1) glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2]) - if self.viewMode == 'gcode': - if self._gcode is not None and self._gcode.layerList is None: - self._gcodeLoadThread = threading.Thread(target=self._loadGCode) - self._gcodeLoadThread.daemon = True - self._gcodeLoadThread.start() - if self._gcode is not None and self._gcode.layerList is not None: - glPushMatrix() - if profile.getPreference('machine_center_is_zero') != 'True': - glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0) - t = time.time() - drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1) - for n in xrange(0, drawUpTill): - c = 1.0 - float(drawUpTill - n) / 15 - c = max(0.3, c) - if len(self._gcodeVBOs) < n + 1: - self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n])) - if time.time() - t > 0.5: - self.QueueRefresh() - break - #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT'] - if n == drawUpTill - 1: - if len(self._gcodeVBOs[n]) < 9: - self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n]) - glColor3f(c, 0, 0) - self._gcodeVBOs[n][8].render(GL_QUADS) - glColor3f(c/2, 0, c) - self._gcodeVBOs[n][9].render(GL_QUADS) - glColor3f(0, c, c/2) - self._gcodeVBOs[n][10].render(GL_QUADS) - glColor3f(c, 0, 0) - self._gcodeVBOs[n][11].render(GL_QUADS) - - glColor3f(0, c, 0) - self._gcodeVBOs[n][12].render(GL_QUADS) - glColor3f(c/2, c/2, 0.0) - self._gcodeVBOs[n][13].render(GL_QUADS) - glColor3f(0, c, c) - self._gcodeVBOs[n][14].render(GL_QUADS) - self._gcodeVBOs[n][15].render(GL_QUADS) - glColor3f(0, 0, c) - self._gcodeVBOs[n][16].render(GL_LINES) - else: - glColor3f(c, 0, 0) - self._gcodeVBOs[n][0].render(GL_LINES) - glColor3f(c/2, 0, c) - self._gcodeVBOs[n][1].render(GL_LINES) - glColor3f(0, c, c/2) - self._gcodeVBOs[n][2].render(GL_LINES) - glColor3f(c, 0, 0) - self._gcodeVBOs[n][3].render(GL_LINES) - - glColor3f(0, c, 0) - self._gcodeVBOs[n][4].render(GL_LINES) - glColor3f(c/2, c/2, 0.0) - self._gcodeVBOs[n][5].render(GL_LINES) - glColor3f(0, c, c) - self._gcodeVBOs[n][6].render(GL_LINES) - self._gcodeVBOs[n][7].render(GL_LINES) - glPopMatrix() - else: + self._objectShader.unbind() + self._engineResultView.OnDraw() + if self.viewMode != 'gcode': glStencilFunc(GL_ALWAYS, 1, 1) glStencilOp(GL_INCR, GL_INCR, GL_INCR) if self.viewMode == 'overhang': self._objectOverhangShader.bind() - self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60))) + self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - profile.getProfileSettingFloat('support_angle')))) else: self._objectShader.bind() for obj in self._scene.objects(): @@ -1034,44 +1120,60 @@ void main(void) self._drawMachine() - if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID(): - glEnable(GL_BLEND) + if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._engine.getID(): z = self._usbPrintMonitor.getZ() - size = self._machineSize - glColor4ub(255,255,0,128) - glBegin(GL_QUADS) - glVertex3f(-size[0]/2,-size[1]/2, z) - glVertex3f( size[0]/2,-size[1]/2, z) - glVertex3f( size[0]/2, size[1]/2, z) - glVertex3f(-size[0]/2, size[1]/2, z) - glEnd() + if self.viewMode == 'gcode': + layer_height = profile.getProfileSettingFloat('layer_height') + layer1_height = profile.getProfileSettingFloat('bottom_thickness') + if layer_height > 0: + if layer1_height > 0: + layer = int((z - layer1_height) / layer_height) + 1 + else: + layer = int(z / layer_height) + else: + layer = 1 + self.layerSelect.setValue(layer) + else: + size = self._machineSize + glEnable(GL_BLEND) + glColor4ub(255,255,0,128) + glBegin(GL_QUADS) + glVertex3f(-size[0]/2,-size[1]/2, z) + glVertex3f( size[0]/2,-size[1]/2, z) + glVertex3f( size[0]/2, size[1]/2, z) + glVertex3f(-size[0]/2, size[1]/2, z) + glEnd() - if self.viewMode == 'gcode': - if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive(): - glDisable(GL_DEPTH_TEST) - glPushMatrix() - glLoadIdentity() - glTranslate(0,-4,-10) - glColor4ub(60,60,60,255) - opengl.glDrawStringCenter('Loading toolpath for visualization...') - glPopMatrix() - else: + if self.viewMode != 'gcode': #Draw the object box-shadow, so you can see where it will collide with other objects. - if self._selectedObj is not None and len(self._scene.objects()) > 1: - size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend() - glPushMatrix() - glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0) + if self._selectedObj is not None: glEnable(GL_BLEND) glEnable(GL_CULL_FACE) - glColor4f(0,0,0,0.12) - glBegin(GL_QUADS) - glVertex3f(-size[0], size[1], 0.1) - glVertex3f(-size[0], -size[1], 0.1) - glVertex3f( size[0], -size[1], 0.1) - glVertex3f( size[0], size[1], 0.1) - glEnd() + glColor4f(0,0,0,0.16) + glDepthMask(False) + for obj in self._scene.objects(): + glPushMatrix() + glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0) + glBegin(GL_TRIANGLE_FAN) + for p in obj._boundaryHull[::-1]: + glVertex3f(p[0], p[1], 0) + glEnd() + glPopMatrix() + if self._scene.isOneAtATime(): + glPushMatrix() + glColor4f(0,0,0,0.06) + glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0) + glBegin(GL_TRIANGLE_FAN) + for p in self._selectedObj._printAreaHull[::-1]: + glVertex3f(p[0], p[1], 0) + glEnd() + glBegin(GL_TRIANGLE_FAN) + for p in self._selectedObj._headAreaMinHull[::-1]: + glVertex3f(p[0], p[1], 0) + glEnd() + glPopMatrix() + glDepthMask(True) glDisable(GL_CULL_FACE) - glPopMatrix() #Draw the outline of the selected object, on top of everything else except the GUI. if self._selectedObj is not None and self._selectedObj._loadAnim is None: @@ -1104,7 +1206,7 @@ void main(void) glLoadIdentity() glTranslate(0,-4,-10) glColor4ub(60,60,60,255) - opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.') + opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support.")) glPopMatrix() def _renderObject(self, obj, brightness = False, addSink = True): @@ -1127,7 +1229,7 @@ void main(void) n = 0 for m in obj._meshList: if m.vbo is None: - m.vbo = opengl.GLVBO(m.vertexes, m.normal) + m.vbo = opengl.GLVBO(GL_TRIANGLES, m.vertexes, m.normal) if brightness: glColor4fv(map(lambda n: n * brightness, self._objColors[n])) n += 1 @@ -1138,13 +1240,62 @@ void main(void) glEnable(GL_CULL_FACE) glEnable(GL_BLEND) - size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')] + size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')] - if profile.getPreference('machine_type') == 'ultimaker': + machine = profile.getMachineSetting('machine_type') + if machine.startswith('ultimaker'): + if machine not in self._platformMesh: + meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl')) + if len(meshes) > 0: + self._platformMesh[machine] = meshes[0] + else: + self._platformMesh[machine] = None + if machine == 'ultimaker2': + self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32) + else: + self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32) glColor4f(1,1,1,0.5) self._objectShader.bind() - self._renderObject(self._platformMesh, False, False) + self._renderObject(self._platformMesh[machine], False, False) self._objectShader.unbind() + + #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text. + if machine == 'ultimaker2': + if not hasattr(self._platformMesh[machine], 'texture'): + self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png') + glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture) + glEnable(GL_TEXTURE_2D) + glPushMatrix() + glColor4f(1,1,1,1) + + glTranslate(0,150,-5) + h = 50 + d = 8 + w = 100 + glEnable(GL_BLEND) + glBlendFunc(GL_DST_COLOR, GL_ZERO) + glBegin(GL_QUADS) + glTexCoord2f(1, 0) + glVertex3f( w, 0, h) + glTexCoord2f(0, 0) + glVertex3f(-w, 0, h) + glTexCoord2f(0, 1) + glVertex3f(-w, 0, 0) + glTexCoord2f(1, 1) + glVertex3f( w, 0, 0) + + glTexCoord2f(1, 0) + glVertex3f(-w, d, h) + glTexCoord2f(0, 0) + glVertex3f( w, d, h) + glTexCoord2f(0, 1) + glVertex3f( w, d, 0) + glTexCoord2f(1, 1) + glVertex3f(-w, d, 0) + glEnd() + glDisable(GL_TEXTURE_2D) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glPopMatrix() else: glColor4f(0,0,0,1) glLineWidth(3) @@ -1157,153 +1308,59 @@ void main(void) glVertex3f(-size[0] / 2, -size[1] / 2+10, 0) glEnd() - v0 = [ size[0] / 2, size[1] / 2, size[2]] - v1 = [ size[0] / 2,-size[1] / 2, size[2]] - v2 = [-size[0] / 2, size[1] / 2, size[2]] - v3 = [-size[0] / 2,-size[1] / 2, size[2]] - v4 = [ size[0] / 2, size[1] / 2, 0] - v5 = [ size[0] / 2,-size[1] / 2, 0] - v6 = [-size[0] / 2, size[1] / 2, 0] - v7 = [-size[0] / 2,-size[1] / 2, 0] - - vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7] - glEnableClientState(GL_VERTEX_ARRAY) - glVertexPointer(3, GL_FLOAT, 3*4, vList) - - glColor4ub(5, 171, 231, 64) - glDrawArrays(GL_QUADS, 0, 4) - glColor4ub(5, 171, 231, 96) - glDrawArrays(GL_QUADS, 4, 8) - glColor4ub(5, 171, 231, 128) - glDrawArrays(GL_QUADS, 12, 8) - glDisableClientState(GL_VERTEX_ARRAY) - - sx = self._machineSize[0] - sy = self._machineSize[1] - for x in xrange(-int(sx/20)-1, int(sx / 20) + 1): - for y in xrange(-int(sx/20)-1, int(sy / 20) + 1): - x1 = x * 10 - x2 = x1 + 10 - y1 = y * 10 - y2 = y1 + 10 - x1 = max(min(x1, sx/2), -sx/2) - y1 = max(min(y1, sy/2), -sy/2) - x2 = max(min(x2, sx/2), -sx/2) - y2 = max(min(y2, sy/2), -sy/2) - if (x & 1) == (y & 1): - glColor4ub(5, 171, 231, 127) + glDepthMask(False) + + polys = profile.getMachineSizePolygons() + height = profile.getMachineSettingFloat('machine_height') + circular = profile.getMachineSetting('machine_shape') == 'Circular' + glBegin(GL_QUADS) + for n in xrange(0, len(polys[0])): + if not circular: + if n % 2 == 0: + glColor4ub(5, 171, 231, 96) else: - glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128) - glBegin(GL_QUADS) - glVertex3f(x1, y1, -0.02) - glVertex3f(x2, y1, -0.02) - glVertex3f(x2, y2, -0.02) - glVertex3f(x1, y2, -0.02) - glEnd() + glColor4ub(5, 171, 231, 64) + else: + glColor4ub(5, 171, 231, 96) + + glVertex3f(polys[0][n][0], polys[0][n][1], height) + glVertex3f(polys[0][n][0], polys[0][n][1], 0) + glVertex3f(polys[0][n-1][0], polys[0][n-1][1], 0) + glVertex3f(polys[0][n-1][0], polys[0][n-1][1], height) + glEnd() + glColor4ub(5, 171, 231, 128) + glBegin(GL_TRIANGLE_FAN) + for p in polys[0][::-1]: + glVertex3f(p[0], p[1], height) + glEnd() + + #Draw checkerboard + if self._platformTexture is None: + self._platformTexture = opengl.loadGLTexture('checkerboard.png') + glBindTexture(GL_TEXTURE_2D, self._platformTexture) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) + glColor4f(1,1,1,0.5) + glBindTexture(GL_TEXTURE_2D, self._platformTexture) + glEnable(GL_TEXTURE_2D) + glBegin(GL_TRIANGLE_FAN) + for p in polys[0]: + glTexCoord2f(p[0]/20, p[1]/20) + glVertex3f(p[0], p[1], 0) + glEnd() + glDisable(GL_TEXTURE_2D) + glColor4ub(127, 127, 127, 200) + for poly in polys[1:]: + glBegin(GL_TRIANGLE_FAN) + for p in poly: + glTexCoord2f(p[0]/20, p[1]/20) + glVertex3f(p[0], p[1], 0) + glEnd() + glDepthMask(True) glDisable(GL_BLEND) glDisable(GL_CULL_FACE) - def _generateGCodeVBOs(self, layer): - ret = [] - for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']: - if ':' in extrudeType: - extruder = int(extrudeType[extrudeType.find(':')+1:]) - extrudeType = extrudeType[0:extrudeType.find(':')] - else: - extruder = None - pointList = numpy.zeros((0,3), numpy.float32) - for path in layer: - if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder): - a = path['points'] - a = numpy.concatenate((a[:-1], a[1:]), 1) - a = a.reshape((len(a) * 2, 3)) - pointList = numpy.concatenate((pointList, a)) - ret.append(opengl.GLVBO(pointList)) - return ret - - def _generateGCodeVBOs2(self, layer): - filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 - filamentArea = math.pi * filamentRadius * filamentRadius - useFilamentArea = profile.getPreference('gcode_flavor') == 'UltiGCode' - - ret = [] - for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']: - if ':' in extrudeType: - extruder = int(extrudeType[extrudeType.find(':')+1:]) - extrudeType = extrudeType[0:extrudeType.find(':')] - else: - extruder = None - pointList = numpy.zeros((0,3), numpy.float32) - for path in layer: - if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder): - a = path['points'] - if extrudeType == 'FILL': - a[:,2] += 0.01 - - normal = a[1:] - a[:-1] - lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2) - normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens - normal[:,2] /= lens - - ePerDist = path['extrusion'][1:] / lens - if useFilamentArea: - lineWidth = ePerDist / path['layerThickness'] / 2.0 - else: - lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2) - - normal[:,0] *= lineWidth - normal[:,1] *= lineWidth - - b = numpy.zeros((len(a)-1, 0), numpy.float32) - b = numpy.concatenate((b, a[1:] + normal), 1) - b = numpy.concatenate((b, a[1:] - normal), 1) - b = numpy.concatenate((b, a[:-1] - normal), 1) - b = numpy.concatenate((b, a[:-1] + normal), 1) - b = b.reshape((len(b) * 4, 3)) - - if len(a) > 2: - normal2 = normal[:-1] + normal[1:] - lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2) - normal2[:,0] /= lens2 - normal2[:,1] /= lens2 - normal2[:,0] *= lineWidth[:-1] - normal2[:,1] *= lineWidth[:-1] - - c = numpy.zeros((len(a)-2, 0), numpy.float32) - c = numpy.concatenate((c, a[1:-1]), 1) - c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1) - c = numpy.concatenate((c, a[1:-1]+normal2), 1) - c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1) - - c = numpy.concatenate((c, a[1:-1]), 1) - c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1) - c = numpy.concatenate((c, a[1:-1]-normal2), 1) - c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1) - - c = c.reshape((len(c) * 8, 3)) - - pointList = numpy.concatenate((pointList, b, c)) - else: - pointList = numpy.concatenate((pointList, b)) - ret.append(opengl.GLVBO(pointList)) - - pointList = numpy.zeros((0,3), numpy.float32) - for path in layer: - if path['type'] == 'move': - a = path['points'] + numpy.array([0,0,0.01], numpy.float32) - a = numpy.concatenate((a[:-1], a[1:]), 1) - a = a.reshape((len(a) * 2, 3)) - pointList = numpy.concatenate((pointList, a)) - if path['type'] == 'retract': - a = path['points'] + numpy.array([0,0,0.01], numpy.float32) - a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1) - a = a.reshape((len(a) * 2, 3)) - pointList = numpy.concatenate((pointList, a)) - ret.append(opengl.GLVBO(pointList)) - - return ret - def getObjectCenterPos(self): if self._selectedObj is None: return [0.0, 0.0, 0.0]