From 6208201bafdeaf82883181471c6da3a41283cfe7 Mon Sep 17 00:00:00 2001 From: daid Date: Fri, 24 Jan 2014 10:19:24 +0100 Subject: [PATCH] Change how the engine is interfaced from the python code. Put the GCode viewer in a seperate file. --- Cura/gui/app.py | 13 +- Cura/gui/configWizard.py | 4 +- Cura/gui/mainWindow.py | 2 +- Cura/gui/printWindow.py | 2 +- Cura/gui/sceneView.py | 415 +++++++---------------------- Cura/gui/util/engineResultView.py | 429 ++++++++++++++++++++++++++++++ Cura/gui/util/opengl.py | 142 ++-------- Cura/gui/util/openglGui.py | 5 +- Cura/gui/util/previewTools.py | 2 +- Cura/util/gcodeInterpreter.py | 53 +--- Cura/util/profile.py | 8 +- Cura/util/sliceEngine.py | 287 ++++++++++++-------- 12 files changed, 769 insertions(+), 593 deletions(-) create mode 100644 Cura/gui/util/engineResultView.py diff --git a/Cura/gui/app.py b/Cura/gui/app.py index dffa855e..47932c60 100644 --- a/Cura/gui/app.py +++ b/Cura/gui/app.py @@ -63,11 +63,14 @@ class CuraApp(wx.App): def Win32SocketListener(self, port): import socket - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.bind(("127.0.0.1", port)) - while True: - data, addr = sock.recvfrom(2048) - self.mainWindow.OnDropFiles(data.split('\0')) + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind(("127.0.0.1", port)) + while True: + data, addr = sock.recvfrom(2048) + self.mainWindow.OnDropFiles(data.split('\0')) + except: + pass def afterSplashCallback(self): #These imports take most of the time and thus should be done after showing the splashscreen diff --git a/Cura/gui/configWizard.py b/Cura/gui/configWizard.py index 4e45d5ac..d0273bc4 100644 --- a/Cura/gui/configWizard.py +++ b/Cura/gui/configWizard.py @@ -282,7 +282,7 @@ class CustomRepRapInfoPage(InfoPage): self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap") self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80") self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80") - self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "60") + self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "55") self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5") self.heatedBed = self.AddCheckbox(_("Heated bed")) self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)")) @@ -368,7 +368,7 @@ class MachineSelectPage(InfoPage): profile.putMachineSetting('extruder_head_size_min_y', '18.0') profile.putMachineSetting('extruder_head_size_max_x', '18.0') profile.putMachineSetting('extruder_head_size_max_y', '35.0') - profile.putMachineSetting('extruder_head_size_height', '60.0') + profile.putMachineSetting('extruder_head_size_height', '55.0') else: profile.putMachineSetting('machine_width', '80') profile.putMachineSetting('machine_depth', '80') diff --git a/Cura/gui/mainWindow.py b/Cura/gui/mainWindow.py index 95903813..590252a5 100644 --- a/Cura/gui/mainWindow.py +++ b/Cura/gui/mainWindow.py @@ -575,7 +575,7 @@ class mainWindow(wx.Frame): #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting. print "Closing down" self.scene.OnPaint = lambda e : e - self.scene._slicer.cleanup() + self.scene._engine.cleanup() self.Destroy() def OnQuit(self, e): diff --git a/Cura/gui/printWindow.py b/Cura/gui/printWindow.py index 943ada16..1a0e2cbe 100644 --- a/Cura/gui/printWindow.py +++ b/Cura/gui/printWindow.py @@ -602,7 +602,7 @@ class printWindow(wx.Frame): gcodeList.append(line) prevLineType = lineType gcode = gcodeInterpreter.gcode() - gcode.loadList(gcodeList) + gcode.load(gcodeList) #print "Loaded: %s (%d)" % (filename, len(gcodeList)) self.filename = filename self.gcode = gcode diff --git a/Cura/gui/sceneView.py b/Cura/gui/sceneView.py index ebf28566..337a1883 100644 --- a/Cura/gui/sceneView.py +++ b/Cura/gui/sceneView.py @@ -9,9 +9,10 @@ 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 * @@ -24,12 +25,12 @@ 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 @@ -41,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 @@ -108,16 +105,16 @@ class SceneView(openglGui.glGuiPanel): 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.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) @@ -128,14 +125,7 @@ class SceneView(openglGui.glGuiPanel): def loadGCodeFile(self, filename): self.OnDeleteAll(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 = filename + #TODO: Load straight GCodeFile self.printButton.setBottomText('') self.viewSelection.setValue(4) self.printButton.setDisabled(False) @@ -242,7 +232,7 @@ 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: @@ -267,7 +257,7 @@ class SceneView(openglGui.glGuiPanel): 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._showSliceLog(), menu.Append(-1, _("Slice engine log..."))) + self.Bind(wx.EVT_MENU, lambda e: self._showEngineLog(), menu.Append(-1, _("Slice engine log..."))) self.PopupMenu(menu) menu.Destroy() @@ -277,6 +267,7 @@ class SceneView(openglGui.glGuiPanel): 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.") @@ -289,9 +280,10 @@ class SceneView(openglGui.glGuiPanel): 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 - self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID()) - if self._gcodeFilename == self._slicer.getGCodeFilename(): - self._slicer.submitSliceInfoOnline() + #TODO: Fix for _engine.getResult + self._usbPrintMonitor.loadFile(self._gcodeFilename, self._engine.getID()) + if self._gcodeFilename is None: + self._engine.submitInfoOnline() def showSaveGCode(self): if len(self._scene._objectList) < 1: @@ -306,34 +298,34 @@ 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._doEjectSD(allowEject), 31, 'Eject') + if ejectDrive: + self.notification.message("Saved as %s" % (targetFilename), lambda : self._doEjectSD(ejectDrive), 31, 'Eject') elif explorer.hasExplorer(): - self.notification.message("Saved as %s" % (fileB), lambda : explorer.openExplorer(fileB), 4, 'Open folder') + 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): @@ -341,8 +333,8 @@ class SceneView(openglGui.glGuiPanel): 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() @@ -381,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: @@ -392,7 +381,7 @@ 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): @@ -538,66 +527,45 @@ class SceneView(openglGui.glGuiPanel): def sceneUpdated(self): self._sceneUpdateTimer.Start(500, True) - self._slicer.abortSlicer() + 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) - text = '%s' % (self._slicer.getPrintTime()) + text = '%s' % (result.getPrintTime()) for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))): - amount = self._slicer.getFilamentAmount(e) + amount = result.getFilamentAmount(e) if amount is None: continue text += '\n%s' % (amount) - cost = self._slicer.getFilamentCost(e) + cost = result.getFilamentCost(e) if cost is not None: text += '\n%s' % (cost) self.printButton.setBottomText(text) - self._gcode = gcodeInterpreter.gcode() - self._gcodeFilename = self._slicer.getGCodeFilename() else: self.printButton.setBottomText('') self.QueueRefresh() - def _loadGCode(self): - self._gcode.progressCallback = self._gcodeLoadCallback - self._gcode.load(self._gcodeFilename) - - def _gcodeLoadCallback(self, progress): - if not self or 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: @@ -676,72 +644,60 @@ class SceneView(openglGui.glGuiPanel): self.scaleZmmctrl.setValue(round(size[2], 2)) def OnKeyChar(self, keyCode): + 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 self.viewMode == 'gcode' and (wx.GetKeyState(wx.WXK_SHIFT) or wx.GetKeyState(wx.WXK_CONTROL)): - if keyCode == wx.WXK_UP: - self.layerSelect.setValue(self.layerSelect.getValue() + 1) - self.QueueRefresh() - elif keyCode == wx.WXK_DOWN: - self.layerSelect.setValue(self.layerSelect.getValue() - 1) - self.QueueRefresh() - elif keyCode == wx.WXK_PAGEUP: - self.layerSelect.setValue(self.layerSelect.getValue() + 10) - self.QueueRefresh() - elif keyCode == wx.WXK_PAGEDOWN: - self.layerSelect.setValue(self.layerSelect.getValue() - 10) - self.QueueRefresh() - else: - if keyCode == wx.WXK_UP: - 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: - 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('='): + if keyCode == wx.WXK_UP: + if wx.GetKeyState(wx.WXK_SHIFT): 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('-'): + else: + self._pitch -= 15 + self.QueueRefresh() + elif keyCode == wx.WXK_DOWN: + 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 - self.QueueRefresh() - elif keyCode == wx.WXK_HOME: - self._yaw = 30 - self._pitch = 60 - self.QueueRefresh() - elif keyCode == wx.WXK_PAGEUP: - self._yaw = 0 - self._pitch = 0 - self.QueueRefresh() - elif keyCode == wx.WXK_PAGEDOWN: - self._yaw = 0 - self._pitch = 90 - self.QueueRefresh() - elif keyCode == wx.WXK_END: - self._yaw = 90 - self._pitch = 90 - self.QueueRefresh() + 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._yaw = 0 + self._pitch = 0 + self.QueueRefresh() + elif keyCode == wx.WXK_PAGEDOWN: + 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): shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader()) @@ -948,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(""" @@ -1070,66 +1021,9 @@ 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.getMachineSetting('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) @@ -1226,7 +1120,7 @@ void main(void) self._drawMachine() - if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID(): + if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._engine.getID(): z = self._usbPrintMonitor.getZ() if self.viewMode == 'gcode': layer_height = profile.getProfileSettingFloat('layer_height') @@ -1250,16 +1144,7 @@ void main(void) 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: glEnable(GL_BLEND) @@ -1344,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 @@ -1476,106 +1361,6 @@ void main(void) 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.getMachineSetting('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] diff --git a/Cura/gui/util/engineResultView.py b/Cura/gui/util/engineResultView.py new file mode 100644 index 00000000..a079927f --- /dev/null +++ b/Cura/gui/util/engineResultView.py @@ -0,0 +1,429 @@ +__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License" + +import wx +import numpy +import math + +import OpenGL +#OpenGL.ERROR_CHECKING = False +from OpenGL.GLU import * +from OpenGL.GL import * + +from Cura.util import profile +from Cura.gui.util import opengl +from Cura.gui.util import openglGui + +class engineResultView(object): + def __init__(self, parent): + self._parent = parent + self._result = None + self._enabled = False + self._layerVBOs = [] + self._layer20VBOs = [] + + self.layerSelect = openglGui.glSlider(self._parent, 10000, 0, 1, (-1,-2), lambda : self._parent.QueueRefresh()) + + def setResult(self, result): + if self._result == result: + return + + self._result = result + + #Clean the saved VBO's + for layer in self._layerVBOs: + for typeName in layer.keys(): + self._parent.glReleaseList.append(layer[typeName]) + for layer in self._layer20VBOs: + for typeName in layer.keys(): + self._parent.glReleaseList.append(layer[typeName]) + self._layerVBOs = [] + self._layer20VBOs = [] + + def setEnabled(self, enabled): + self._enabled = enabled + self.layerSelect.setHidden(not enabled) + + def OnDraw(self): + if not self._enabled: + return + + if self._result is not None and self._result._polygons is not None: + self.layerSelect.setRange(1, len(self._result._polygons)) + + glPushMatrix() + glEnable(GL_BLEND) + if profile.getMachineSetting('machine_center_is_zero') != 'True': + glTranslate(-profile.getMachineSettingFloat('machine_width') / 2, -profile.getMachineSettingFloat('machine_depth') / 2, 0) + glLineWidth(2) + + layerNr = self.layerSelect.getValue() + if layerNr == self.layerSelect.getMaxValue(): + layerNr = max(layerNr, len(self._result._polygons)) + viewZ = (layerNr - 1) * profile.getProfileSettingFloat('layer_height') + profile.getProfileSettingFloat('bottom_thickness') + self._parent._viewTarget[2] = viewZ + msize = max(profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth')) + lineTypeList = [('inset0',[1,0,0,1]), ('insetx',[0,1,0,1]), ('openoutline',[1,0,0,1]), ('skin',[1,1,0,1]), ('infill',[1,1,0,1]), ('support',[0,1,1,1]), ('outline',[0,0,0,1])] + n = 0 + layers = None #self._result.getGCodeLayers() + generatedVBO = False + while n < layerNr: + if layerNr - n > 30: + idx = n / 20 + while len(self._layer20VBOs) < idx + 1: + self._layer20VBOs.append({}) + if self._result is not None and self._result._polygons is not None and n + 20 < len(self._result._polygons): + layerVBOs = self._layer20VBOs[idx] + for typeName, color in lineTypeList: + if typeName in self._result._polygons[n + 19]: + if typeName not in self._layer20VBOs[idx]: + if generatedVBO: + continue + polygons = [] + for i in xrange(0, 20): + if typeName in self._result._polygons[n + i]: + polygons += self._result._polygons[n + i][typeName] + layerVBOs[typeName] = self._polygonsToVBO_lines(polygons) + generatedVBO = True + glColor4f(color[0]*0.5,color[1]*0.5,color[2]*0.5,color[3]) + layerVBOs[typeName].render() + n += 20 + else: + while len(self._layerVBOs) < n + 1: + self._layerVBOs.append({}) + c = 1.0 - ((layerNr - n) - 1) * 0.05 + c = max(0.5, c) + layerVBOs = self._layerVBOs[n] + if layers is not None and n < len(layers): + if 'GCODE-FILL' not in layerVBOs: + layerVBOs['GCODE-FILL'] = self._gcodeToVBO_quads(layers[n:n+1], 'FILL') + glColor4f(c,c,0,1) + layerVBOs['GCODE-FILL'].render() + elif self._result is not None and self._result._polygons is not None and n < len(self._result._polygons): + polygons = self._result._polygons[n] + for typeName, color in lineTypeList: + if typeName in polygons: + if typeName not in layerVBOs: + layerVBOs[typeName] = self._polygonsToVBO_lines(polygons[typeName]) + glColor4f(color[0]*c,color[1]*c,color[2]*c,color[3]) + layerVBOs[typeName].render() + n += 1 + glPopMatrix() + if generatedVBO: + self._parent._queueRefresh() + + def _polygonsToVBO_lines(self, polygons): + verts = numpy.zeros((0, 3), numpy.float32) + indices = numpy.zeros((0), numpy.uint32) + for poly in polygons: + i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32) + i[-1] = len(verts) + i = numpy.dstack((i[0:-1],i[1:])).flatten() + indices = numpy.concatenate((indices, i), 0) + verts = numpy.concatenate((verts, poly), 0) + return opengl.GLVBO(GL_LINES, verts, indicesArray=indices) + + def _polygonsToVBO_quads(self, polygons): + verts = numpy.zeros((0, 3), numpy.float32) + indices = numpy.zeros((0), numpy.uint32) + for poly in polygons: + i = numpy.arange(len(verts), len(verts) + len(poly) + 1, 1, numpy.uint32) + i2 = numpy.arange(len(verts) + len(poly), len(verts) + len(poly) + len(poly) + 1, 1, numpy.uint32) + i[-1] = len(verts) + i2[-1] = len(verts) + len(poly) + i = numpy.dstack((i[0:-1],i2[0:-1],i2[1:],i[1:])).flatten() + indices = numpy.concatenate((indices, i), 0) + verts = numpy.concatenate((verts, poly), 0) + verts = numpy.concatenate((verts, poly * numpy.array([1,0,1],numpy.float32) + numpy.array([0,-100,0],numpy.float32)), 0) + return opengl.GLVBO(GL_QUADS, verts, indicesArray=indices) + + def _gcodeToVBO_lines(self, gcodeLayers, extrudeType): + if ':' in extrudeType: + extruder = int(extrudeType[extrudeType.find(':')+1:]) + extrudeType = extrudeType[0:extrudeType.find(':')] + else: + extruder = None + verts = numpy.zeros((0, 3), numpy.float32) + indices = numpy.zeros((0), numpy.uint32) + for layer in gcodeLayers: + for path in layer: + if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder): + i = numpy.arange(len(verts), len(verts) + len(path['points']), 1, numpy.uint32) + i = numpy.dstack((i[0:-1],i[1:])).flatten() + indices = numpy.concatenate((indices, i), 0) + verts = numpy.concatenate((verts, path['points'])) + return opengl.GLVBO(GL_LINES, verts, indicesArray=indices) + + def _gcodeToVBO_quads(self, gcodeLayers, extrudeType): + useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode' + filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 + filamentArea = math.pi * filamentRadius * filamentRadius + + if ':' in extrudeType: + extruder = int(extrudeType[extrudeType.find(':')+1:]) + extrudeType = extrudeType[0:extrudeType.find(':')] + else: + extruder = None + + verts = numpy.zeros((0, 3), numpy.float32) + indices = numpy.zeros((0), numpy.uint32) + for layer in gcodeLayers: + 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 + + #Construct the normals of each line 90deg rotated on the X/Y plane + normals = a[1:] - a[:-1] + lengths = numpy.sqrt(normals[:,0]**2 + normals[:,1]**2) + normals[:,0], normals[:,1] = -normals[:,1] / lengths, normals[:,0] / lengths + normals[:,2] /= lengths + + ePerDist = path['extrusion'][1:] / lengths + if useFilamentArea: + lineWidth = ePerDist / path['layerThickness'] / 2.0 + else: + lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2) + + normals[:,0] *= lineWidth + normals[:,1] *= lineWidth + + b = numpy.zeros((len(a)-1, 0), numpy.float32) + b = numpy.concatenate((b, a[1:] + normals), 1) + b = numpy.concatenate((b, a[1:] - normals), 1) + b = numpy.concatenate((b, a[:-1] - normals), 1) + b = numpy.concatenate((b, a[:-1] + normals), 1) + b = b.reshape((len(b) * 4, 3)) + + i = numpy.arange(len(verts), len(verts) + len(b), 1, numpy.uint32) + + verts = numpy.concatenate((verts, b)) + indices = numpy.concatenate((indices, i)) + return opengl.GLVBO(GL_QUADS, verts, indicesArray=indices) + + def OnKeyChar(self, keyCode): + if not self._enabled: + return + + if wx.GetKeyState(wx.WXK_SHIFT) or wx.GetKeyState(wx.WXK_CONTROL): + if keyCode == wx.WXK_UP: + self.layerSelect.setValue(self.layerSelect.getValue() + 1) + self._parent.QueueRefresh() + return True + elif keyCode == wx.WXK_DOWN: + self.layerSelect.setValue(self.layerSelect.getValue() - 1) + self._parent.QueueRefresh() + return True + elif keyCode == wx.WXK_PAGEUP: + self.layerSelect.setValue(self.layerSelect.getValue() + 10) + self._parent.QueueRefresh() + return True + elif keyCode == wx.WXK_PAGEDOWN: + self.layerSelect.setValue(self.layerSelect.getValue() - 10) + self._parent.QueueRefresh() + return True + return False + + # 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 + + # def _loadGCode(self): + # self._gcode.progressCallback = self._gcodeLoadCallback + # if self._gcodeFilename is not None: + # self._gcode.load(self._gcodeFilename) + # else: + # self._gcode.load(self._gcodeData) + # + # def _gcodeLoadCallback(self, progress): + # if not self or 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 + + # 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() + + +# 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 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.getMachineSetting('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() + # + # 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.getMachineSetting('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 diff --git a/Cura/gui/util/opengl.py b/Cura/gui/util/opengl.py index 0bde41be..7cf19b3d 100644 --- a/Cura/gui/util/opengl.py +++ b/Cura/gui/util/opengl.py @@ -6,14 +6,11 @@ import numpy import wx import time -from Cura.util import meshLoader -from Cura.util import util3d -from Cura.util import profile -from Cura.util.resources import getPathForMesh, getPathForImage +from Cura.util.resources import getPathForImage import OpenGL -OpenGL.ERROR_CHECKING = False +#OpenGL.ERROR_CHECKING = False from OpenGL.GLUT import * from OpenGL.GLU import * from OpenGL.GL import * @@ -131,26 +128,35 @@ class GLFakeShader(GLReferenceCounter): return '' class GLVBO(GLReferenceCounter): - def __init__(self, vertexArray, normalArray = None): + def __init__(self, renderType, vertexArray, normalArray = None, indicesArray = None): super(GLVBO, self).__init__() + self._renderType = renderType if not bool(glGenBuffers): self._vertexArray = vertexArray self._normalArray = normalArray + self._indicesArray = indicesArray self._size = len(vertexArray) self._buffer = None self._hasNormals = self._normalArray is not None + self._hasIndices = self._indicesArray is not None else: self._buffer = glGenBuffers(1) self._size = len(vertexArray) self._hasNormals = normalArray is not None + self._hasIndices = indicesArray is not None glBindBuffer(GL_ARRAY_BUFFER, self._buffer) if self._hasNormals: glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW) else: glBufferData(GL_ARRAY_BUFFER, vertexArray, GL_STATIC_DRAW) glBindBuffer(GL_ARRAY_BUFFER, 0) + if self._hasIndices: + self._size = len(indicesArray) + self._bufferIndices = glGenBuffers(1) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, numpy.array(indicesArray, numpy.uint32), GL_STATIC_DRAW) - def render(self, render_type = GL_TRIANGLES): + def render(self): glEnableClientState(GL_VERTEX_ARRAY) if self._buffer is None: glVertexPointer(3, GL_FLOAT, 0, self._vertexArray) @@ -165,16 +171,23 @@ class GLVBO(GLReferenceCounter): glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4)) else: glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0)) + if self._hasIndices: + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices) - batchSize = 996 #Warning, batchSize needs to be dividable by 4, 3 and 2 - extraStartPos = int(self._size / batchSize) * batchSize - extraCount = self._size - extraStartPos + if self._hasIndices: + glDrawElements(self._renderType, self._size, GL_UNSIGNED_INT, c_void_p(0)) + else: + batchSize = 996 #Warning, batchSize needs to be dividable by 4, 3 and 2 + extraStartPos = int(self._size / batchSize) * batchSize + extraCount = self._size - extraStartPos + for i in xrange(0, int(self._size / batchSize)): + glDrawArrays(self._renderType, i * batchSize, batchSize) + glDrawArrays(self._renderType, extraStartPos, extraCount) - for i in xrange(0, int(self._size / batchSize)): - glDrawArrays(render_type, i * batchSize, batchSize) - glDrawArrays(render_type, extraStartPos, extraCount) if self._buffer is not None: glBindBuffer(GL_ARRAY_BUFFER, 0) + if self._hasIndices: + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) glDisableClientState(GL_VERTEX_ARRAY) if self._hasNormals: @@ -545,106 +558,3 @@ def DrawMeshSteep(mesh, matrix, angle): glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2]) glEnd() glDepthFunc(GL_LESS) - -def DrawGCodeLayer(layer, drawQuick = True): - filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 - filamentArea = math.pi * filamentRadius * filamentRadius - lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10 - - fillCycle = 0 - fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]] - moveColor = [0, 0, 1, 0.5] - retractColor = [1, 0, 0.5, 0.5] - supportColor = [0, 1, 1, 1] - extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]] - innerWallColor = [0, 1, 0, 1] - skirtColor = [0, 0.5, 0.5, 1] - prevPathWasRetract = False - - glDisable(GL_CULL_FACE) - for path in layer: - if path.type == 'move': - if prevPathWasRetract: - c = retractColor - else: - c = moveColor - if drawQuick: - continue - zOffset = 0.01 - if path.type == 'extrude': - if path.pathType == 'FILL': - c = fillColorCycle[fillCycle] - fillCycle = (fillCycle + 1) % len(fillColorCycle) - elif path.pathType == 'WALL-INNER': - c = innerWallColor - zOffset = 0.02 - elif path.pathType == 'SUPPORT': - c = supportColor - elif path.pathType == 'SKIRT': - c = skirtColor - else: - c = extrudeColor[path.extruder] - if path.type == 'retract': - c = retractColor - if path.type == 'extrude' and not drawQuick: - drawLength = 0.0 - prevNormal = None - for i in xrange(0, len(path.points) - 1): - v0 = path.points[i] - v1 = path.points[i + 1] - - # Calculate line width from ePerDistance (needs layer thickness and filament diameter) - dist = (v0 - v1).vsize() - if dist > 0 and path.layerThickness > 0: - extrusionMMperDist = (v1.e - v0.e) / dist - lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply - - drawLength += (v0 - v1).vsize() - normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1)) - normal.normalize() - - vv2 = v0 + normal * lineWidth - vv3 = v1 + normal * lineWidth - vv0 = v0 - normal * lineWidth - vv1 = v1 - normal * lineWidth - - glBegin(GL_QUADS) - glColor4fv(c) - glVertex3f(vv0.x, vv0.y, vv0.z - zOffset) - glVertex3f(vv1.x, vv1.y, vv1.z - zOffset) - glVertex3f(vv3.x, vv3.y, vv3.z - zOffset) - glVertex3f(vv2.x, vv2.y, vv2.z - zOffset) - glEnd() - if prevNormal is not None: - n = (normal + prevNormal) - n.normalize() - vv4 = v0 + n * lineWidth - vv5 = v0 - n * lineWidth - glBegin(GL_QUADS) - glColor4fv(c) - glVertex3f(vv2.x, vv2.y, vv2.z - zOffset) - glVertex3f(vv4.x, vv4.y, vv4.z - zOffset) - glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset) - glVertex3f(v0.x, v0.y, v0.z - zOffset) - - glVertex3f(vv0.x, vv0.y, vv0.z - zOffset) - glVertex3f(vv5.x, vv5.y, vv5.z - zOffset) - glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset) - glVertex3f(v0.x, v0.y, v0.z - zOffset) - glEnd() - - prevNormal = normal - prevVv1 = vv1 - prevVv3 = vv3 - else: - glColor4fv(c) - glBegin(GL_TRIANGLES) - for v in path.points: - glVertex3f(v[0], v[1], v[2]) - glEnd() - - if not path.type == 'move': - prevPathWasRetract = False - #if path.type == 'retract' and path.points[0].almostEqual(path.points[-1]): - # prevPathWasRetract = True - glEnable(GL_CULL_FACE) diff --git a/Cura/gui/util/openglGui.py b/Cura/gui/util/openglGui.py index 48e216de..c9b0eeeb 100644 --- a/Cura/gui/util/openglGui.py +++ b/Cura/gui/util/openglGui.py @@ -10,7 +10,7 @@ import time from wx import glcanvas import OpenGL -OpenGL.ERROR_CHECKING = False +#OpenGL.ERROR_CHECKING = False from OpenGL.GL import * from Cura.util import version @@ -245,6 +245,7 @@ class glGuiPanel(glcanvas.GLCanvas): locationInfo = tb[n] errStr += "\n @ %s:%s:%d" % (os.path.basename(locationInfo[0]), locationInfo[2], locationInfo[1]) if not self._shownError: + traceback.print_exc() wx.CallAfter(wx.MessageBox, errStr, _("3D window error"), wx.OK | wx.ICON_EXCLAMATION) self._shownError = True @@ -287,7 +288,7 @@ class glGuiPanel(glcanvas.GLCanvas): # glVertex2f(0, 0) # glEnd() # glDisable(GL_TEXTURE_2D) - glPopMatrix() + # glPopMatrix() def _OnEraseBackground(self,event): #Workaround for windows background redraw flicker. diff --git a/Cura/gui/util/previewTools.py b/Cura/gui/util/previewTools.py index dad34622..372d9cf2 100644 --- a/Cura/gui/util/previewTools.py +++ b/Cura/gui/util/previewTools.py @@ -6,7 +6,7 @@ import wx import numpy import OpenGL -OpenGL.ERROR_CHECKING = False +#OpenGL.ERROR_CHECKING = False from OpenGL.GLU import * from OpenGL.GL import * diff --git a/Cura/util/gcodeInterpreter.py b/Cura/util/gcodeInterpreter.py index 2d674641..1a9ee094 100644 --- a/Cura/util/gcodeInterpreter.py +++ b/Cura/util/gcodeInterpreter.py @@ -6,16 +6,11 @@ import math import os import time import numpy +import types +import cStringIO as StringIO from Cura.util import profile -#class gcodePath(object): -# def __init__(self, newType, pathType, layerThickness, startPoint): -# self.type = newType -# self.pathType = pathType -# self.layerThickness = layerThickness -# self.points = [startPoint] -# self.extrusion = [0.0] def gcodePath(newType, pathType, layerThickness, startPoint): return {'type': newType, 'pathType': pathType, @@ -28,22 +23,23 @@ class gcode(object): self.regMatch = {} self.layerList = None self.extrusionAmount = 0 - self.totalMoveTimeMinute = 0 self.filename = None self.progressCallback = None - def load(self, filename): - if os.path.isfile(filename): - self.filename = filename - self._fileSize = os.stat(filename).st_size - gcodeFile = open(filename, 'r') + def load(self, data): + self.filename = None + if type(data) in types.StringTypes and os.path.isfile(data): + self.filename = data + self._fileSize = os.stat(data).st_size + gcodeFile = open(data, 'r') self._load(gcodeFile) gcodeFile.close() - - def loadList(self, l): - self.filename = None - self._load(l) - + elif type(data) is list: + self._load(data) + else: + self._fileSize = len(data.getvalue()) + self._load(StringIO.StringIO(data.getvalue())) + def calculateWeight(self): #Calculates the weight of the filament in kg radius = float(profile.getProfileSetting('filament_diameter')) / 2 @@ -66,11 +62,8 @@ class gcode(object): pos = [0.0,0.0,0.0] posOffset = [0.0, 0.0, 0.0] currentE = 0.0 - totalExtrusion = 0.0 - maxExtrusion = 0.0 currentExtruder = 0 extrudeAmountMultiply = 1.0 - totalMoveTimeMinute = 0.0 absoluteE = True scale = 1.0 posAbs = True @@ -148,12 +141,6 @@ class gcode(object): pos[1] += y * scale if z is not None: pos[2] += z * scale - #if f is not None: - # feedRate = f - #if x is not None or y is not None or z is not None: - # diffX = oldPos[0] - pos[0] - # diffY = oldPos[1] - pos[1] - # totalMoveTimeMinute += math.sqrt(diffX * diffX + diffY * diffY) / feedRate moveType = 'move' if e is not None: if absoluteE: @@ -162,10 +149,7 @@ class gcode(object): moveType = 'extrude' if e < 0.0: moveType = 'retract' - totalExtrusion += e currentE += e - if totalExtrusion > maxExtrusion: - maxExtrusion = totalExtrusion else: e = 0.0 if moveType == 'move' and oldPos[2] != pos[2]: @@ -181,11 +165,7 @@ class gcode(object): currentPath['extrusion'].append(e * extrudeAmountMultiply) elif G == 4: #Delay S = getCodeFloat(line, 'S') - if S is not None: - totalMoveTimeMinute += S / 60.0 P = getCodeFloat(line, 'P') - if P is not None: - totalMoveTimeMinute += P / 60.0 / 1000.0 elif G == 10: #Retract currentPath = gcodePath('retract', pathType, layerThickness, currentPath['points'][-1]) currentPath['extruder'] = currentExtruder @@ -290,10 +270,6 @@ class gcode(object): self.layerList.append(currentLayer) if self.progressCallback is not None and self._fileSize > 0: self.progressCallback(float(gcodeFile.tell()) / float(self._fileSize)) - self.extrusionAmount = maxExtrusion - self.totalMoveTimeMinute = totalMoveTimeMinute - #print "Extruded a total of: %d mm of filament" % (self.extrusionAmount) - #print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute) def getCodeInt(line, code): n = line.find(code) + 1 @@ -324,6 +300,5 @@ if __name__ == '__main__': for filename in sys.argv[1:]: g = gcode() g.load(filename) - print g.totalMoveTimeMinute print time.time() - t diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 41469975..451d79a0 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -344,10 +344,10 @@ setting('filament_physical_density', '1240', float, 'preference', 'hidden').setR setting('language', 'English', str, 'preference', 'hidden').setLabel(_('Language'), _('Change the language in which Cura runs. Switching language requires a restart of Cura')) setting('active_machine', '0', int, 'preference', 'hidden') -setting('model_colour', '#FFC924', str, 'preference', 'hidden').setLabel(_('Model colour')) -setting('model_colour2', '#CB3030', str, 'preference', 'hidden').setLabel(_('Model colour (2)')) -setting('model_colour3', '#DDD93C', str, 'preference', 'hidden').setLabel(_('Model colour (3)')) -setting('model_colour4', '#4550D3', str, 'preference', 'hidden').setLabel(_('Model colour (4)')) +setting('model_colour', '#FFC924', str, 'preference', 'hidden').setLabel(_('Model colour'), _('Display color for first extruder')) +setting('model_colour2', '#CB3030', str, 'preference', 'hidden').setLabel(_('Model colour (2)'), _('Display color for second extruder')) +setting('model_colour3', '#DDD93C', str, 'preference', 'hidden').setLabel(_('Model colour (3)'), _('Display color for third extruder')) +setting('model_colour4', '#4550D3', str, 'preference', 'hidden').setLabel(_('Model colour (4)'), _('Display color for forth extruder')) setting('window_maximized', 'True', bool, 'preference', 'hidden') setting('window_pos_x', '-1', float, 'preference', 'hidden') diff --git a/Cura/util/sliceEngine.py b/Cura/util/sliceEngine.py index f72d0962..ce082477 100644 --- a/Cura/util/sliceEngine.py +++ b/Cura/util/sliceEngine.py @@ -12,9 +12,11 @@ import sys import urllib import urllib2 import hashlib +import cStringIO as StringIO from Cura.util import profile from Cura.util import version +from Cura.util import gcodeInterpreter def getEngineFilename(): if platform.system() == 'Windows': @@ -35,53 +37,20 @@ def getTempFilename(): warnings.simplefilter('default') return ret -class Slicer(object): - def __init__(self, progressCallback): - self._process = None - self._thread = None - self._callback = progressCallback - self._binaryStorageFilename = getTempFilename() - self._exportFilename = getTempFilename() - self._progressSteps = ['inset', 'skin', 'export'] - self._objCount = 0 - self._sliceLog = [] +class EngineResult(object): + def __init__(self): + self._engineLog = [] + self._gcodeData = StringIO.StringIO() + self._polygons = [] + self._success = False self._printTimeSeconds = None - self._filamentMM = [0.0, 0.0] + self._filamentMM = [0.0] * 4 self._modelHash = None - self._id = 0 - - def cleanup(self): - self.abortSlicer() - try: - os.remove(self._binaryStorageFilename) - except: - pass - try: - os.remove(self._exportFilename) - except: - pass - - def abortSlicer(self): - if self._process is not None: - try: - self._process.terminate() - except: - pass - self._thread.join() - self._thread = None - - def wait(self): - if self._thread is not None: - self._thread.join() - - def getGCodeFilename(self): - return self._exportFilename - - def getSliceLog(self): - return self._sliceLog - - def getID(self): - return self._id + self._profileString = profile.getProfileString() + self._preferencesString = profile.getPreferencesString() + self._gcodeInterpreter = gcodeInterpreter.gcode() + self._gcodeLoadThread = None + self._finished = False def getFilamentWeight(self, e=0): #Calculates the weight of the filament in kg @@ -112,7 +81,95 @@ class Slicer(object): return None return '%0.2f meter %0.0f gram' % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0) - def runSlicer(self, scene): + def getLog(self): + return self._engineLog + + def getGCode(self): + return self._gcodeData.getvalue() + + def addLog(self, line): + self._engineLog.append(line) + + def setHash(self, hash): + self._modelHash = hash + + def setFinished(self, result): + self._finished = result + + def isFinished(self): + return self._finished + + def getGCodeLayers(self): + if not self._finished: + return None + if self._gcodeInterpreter.layerList is None and self._gcodeLoadThread is None: + self._gcodeInterpreter.progressCallback = self._gcodeInterpreterCallback + self._gcodeLoadThread = threading.Thread(target=lambda : self._gcodeInterpreter.load(self._gcodeData)) + self._gcodeLoadThread.daemon = True + self._gcodeLoadThread.start() + return self._gcodeInterpreter.layerList + + def _gcodeInterpreterCallback(self, progress): + if len(self._gcodeInterpreter.layerList) % 15 == 0: + time.sleep(0.1) + return False + + def submitInfoOnline(self): + if profile.getPreference('submit_slice_information') != 'True': + return + if version.isDevVersion(): + return + data = { + 'processor': platform.processor(), + 'machine': platform.machine(), + 'platform': platform.platform(), + 'profile': self._profileString, + 'preferences': self._preferencesString, + 'modelhash': self._modelHash, + 'version': version.getVersion(), + } + try: + f = urllib2.urlopen("http://www.youmagine.com/curastats/", data = urllib.urlencode(data), timeout = 1) + f.read() + f.close() + except: + pass + +class Engine(object): + def __init__(self, progressCallback): + self._process = None + self._thread = None + self._callback = progressCallback + self._binaryStorageFilename = getTempFilename() + self._progressSteps = ['inset', 'skin', 'export'] + self._objCount = 0 + self._result = None + + def cleanup(self): + self.abortEngine() + try: + os.remove(self._binaryStorageFilename) + except: + pass + + def abortEngine(self): + if self._process is not None: + try: + self._process.terminate() + except: + pass + if self._thread is not None: + self._thread.join() + self._thread = None + + def wait(self): + if self._thread is not None: + self._thread.join() + + def getResult(self): + return self._result + + def runEngine(self, scene): if len(scene.objects()) < 1: return extruderCount = 1 @@ -122,10 +179,9 @@ class Slicer(object): extruderCount = max(extruderCount, profile.minimalExtruderCount()) - commandList = [getEngineFilename(), '-vv'] + commandList = [getEngineFilename(), '-vvv'] for k, v in self._engineSettings(extruderCount).iteritems(): commandList += ['-s', '%s=%s' % (k, str(v))] - commandList += ['-o', self._exportFilename] commandList += ['-b', self._binaryStorageFilename] self._objCount = 0 with open(self._binaryStorageFilename, "wb") as f: @@ -147,6 +203,8 @@ class Slicer(object): objMin[1] = min(oMin[1], objMin[1]) objMax[0] = max(oMax[0], objMax[0]) objMax[1] = max(oMax[1], objMax[1]) + if objMin is None: + return pos += (objMin + objMax) / 2.0 * 1000 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])] @@ -185,34 +243,66 @@ class Slicer(object): commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])] commandList += ['#' * len(obj._meshList)] self._objCount += 1 - self._modelHash = hash.hexdigest() + modelHash = hash.hexdigest() if self._objCount > 0: - self._thread = threading.Thread(target=self._watchProcess, args=(commandList, self._thread)) + self._thread = threading.Thread(target=self._watchProcess, args=(commandList, self._thread, modelHash)) self._thread.daemon = True self._thread.start() - def _watchProcess(self, commandList, oldThread): + def _watchProcess(self, commandList, oldThread, modelHash): if oldThread is not None: if self._process is not None: self._process.terminate() oldThread.join() - self._id += 1 - self._callback(-1.0, False) + self._callback(-1.0) try: - self._process = self._runSliceProcess(commandList) + self._process = self._runEngineProcess(commandList) except OSError: traceback.print_exc() return if self._thread != threading.currentThread(): self._process.terminate() - self._callback(0.0, False) - self._sliceLog = [] - self._printTimeSeconds = None - self._filamentMM = [0.0, 0.0] - line = self._process.stdout.readline() + self._result = EngineResult() + self._result.setHash(modelHash) + self._callback(0.0) + + logThread = threading.Thread(target=self._watchStderr, args=(self._process.stderr,)) + logThread.daemon = True + logThread.start() + + data = self._process.stdout.read(4096) + while len(data) > 0: + self._result._gcodeData.write(data) + data = self._process.stdout.read(4096) + + returnCode = self._process.wait() + logThread.join() + if returnCode == 0: + pluginError = None #profile.runPostProcessingPlugins(self._exportFilename) + if pluginError is not None: + print pluginError + self._result.addLog(pluginError) + self._result.setFinished(True) + self._callback(1.0) + else: + for line in self._result.getLog(): + print line + self._callback(-1.0) + self._process = None + + def _watchStderr(self, stderr): objectNr = 0 - while len(line): + + # data = stderr.read(4096) + # tmp = StringIO.StringIO() + # while len(data): + # tmp.write(data) + # data = stderr.read(4096) + # stderr = StringIO.StringIO(tmp.getvalue()) + + line = stderr.readline() + while len(line) > 0: line = line.strip() if line.startswith('Progress:'): line = line.split(':') @@ -226,41 +316,45 @@ class Slicer(object): progressValue /= self._objCount progressValue += 1.0 / self._objCount * objectNr try: - self._callback(progressValue, False) + self._callback(progressValue) except: pass + elif line.startswith('Polygons:'): + line = line.split(':') + typeName = line[1] + layerNr = int(line[2]) + size = int(line[3]) + z = float(line[4]) + while len(self._result._polygons) < layerNr + 1: + self._result._polygons.append({}) + polygons = self._result._polygons[layerNr] + for n in xrange(0, size): + polygon = stderr.readline().strip() + if not polygon: + continue + polygon2d = numpy.fromstring(polygon, dtype=numpy.float32, sep=' ') + polygon2d = polygon2d.reshape((len(polygon2d) / 2, 2)) + polygon = numpy.empty((len(polygon2d), 3), numpy.float32) + polygon[:,:-1] = polygon2d + polygon[:,2] = z + if typeName not in polygons: + polygons[typeName] = [] + polygons[typeName].append(polygon) elif line.startswith('Print time:'): - self._printTimeSeconds = int(line.split(':')[1].strip()) + self._result._printTimeSeconds = int(line.split(':')[1].strip()) elif line.startswith('Filament:'): - self._filamentMM[0] = int(line.split(':')[1].strip()) + self._result._filamentMM[0] = int(line.split(':')[1].strip()) if profile.getMachineSetting('gcode_flavor') == 'UltiGCode': radius = profile.getProfileSettingFloat('filament_diameter') / 2.0 - self._filamentMM[0] /= (math.pi * radius * radius) + self._result._filamentMM[0] /= (math.pi * radius * radius) elif line.startswith('Filament2:'): - self._filamentMM[1] = int(line.split(':')[1].strip()) + self._result._filamentMM[1] = int(line.split(':')[1].strip()) if profile.getMachineSetting('gcode_flavor') == 'UltiGCode': radius = profile.getProfileSettingFloat('filament_diameter') / 2.0 - self._filamentMM[1] /= (math.pi * radius * radius) + self._result._filamentMM[1] /= (math.pi * radius * radius) else: - self._sliceLog.append(line.strip()) - line = self._process.stdout.readline() - for line in self._process.stderr: - self._sliceLog.append(line.strip()) - returnCode = self._process.wait() - try: - if returnCode == 0: - pluginError = profile.runPostProcessingPlugins(self._exportFilename) - if pluginError is not None: - print pluginError - self._sliceLog.append(pluginError) - self._callback(1.0, True) - else: - for line in self._sliceLog: - print line - self._callback(-1.0, False) - except: - pass - self._process = None + self._result.addLog(line) + line = stderr.readline() def _engineSettings(self, extruderCount): settings = { @@ -361,7 +455,7 @@ class Slicer(object): settings['enableOozeShield'] = 1 return settings - def _runSliceProcess(self, cmdList): + def _runEngineProcess(self, cmdList): kwargs = {} if subprocess.mswindows: su = subprocess.STARTUPINFO() @@ -370,24 +464,3 @@ class Slicer(object): kwargs['startupinfo'] = su kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) - - def submitSliceInfoOnline(self): - if profile.getPreference('submit_slice_information') != 'True': - return - if version.isDevVersion(): - return - data = { - 'processor': platform.processor(), - 'machine': platform.machine(), - 'platform': platform.platform(), - 'profile': profile.getProfileString(), - 'preferences': profile.getPreferencesString(), - 'modelhash': self._modelHash, - 'version': version.getVersion(), - } - try: - f = urllib2.urlopen("http://www.youmagine.com/curastats/", data = urllib.urlencode(data), timeout = 1) - f.read() - f.close() - except: - pass -- 2.30.2