chiark / gitweb /
Change how the engine is interfaced from the python code. Put the GCode viewer in...
authordaid <daid303@gmail.com>
Fri, 24 Jan 2014 09:19:24 +0000 (10:19 +0100)
committerdaid <daid303@gmail.com>
Fri, 24 Jan 2014 09:19:24 +0000 (10:19 +0100)
12 files changed:
Cura/gui/app.py
Cura/gui/configWizard.py
Cura/gui/mainWindow.py
Cura/gui/printWindow.py
Cura/gui/sceneView.py
Cura/gui/util/engineResultView.py [new file with mode: 0644]
Cura/gui/util/opengl.py
Cura/gui/util/openglGui.py
Cura/gui/util/previewTools.py
Cura/util/gcodeInterpreter.py
Cura/util/profile.py
Cura/util/sliceEngine.py

index dffa855ebbdef70fa711efba10f7b0ef5232511e..47932c60c5e163b1415a7f1536cd9a15ba8af560 100644 (file)
@@ -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
index 4e45d5ac63afc9169d8041f7f97b6eb35b7c3ea1..d0273bc474637844f65c5310dc29405dea286129 100644 (file)
@@ -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')
index 95903813f297069dc4b4f3d349cc420e5bb8ecd4..590252a53d3bc4adfbe1d1751e5894bcd36ce187 100644 (file)
@@ -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):
index 943ada16e7a6a9b30516bcc7222836458ea540ac..1a0e2cbe116c63a3b67bb6a8ac49c121a8a23609 100644 (file)
@@ -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
index ebf28566e3d8c15d0a354e6b544b475effdfc9cd..337a18835306c54f4c2ba590ac2cbcabc99519b5 100644 (file)
@@ -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 (file)
index 0000000..a079927
--- /dev/null
@@ -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
index 0bde41bebea8e733599c00cc4600cf4345467b74..7cf19b3d76fdab682e193566b586027d115c643c 100644 (file)
@@ -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)
index 48e216de38b26e193bf574597d11690212761ede..c9b0eeebe684408fc0d1573e2f3cf0e14a7879dd 100644 (file)
@@ -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.
index dad34622a94792da63eb12b7428ec85253896f64..372d9cf2ff039bc3a3f6d16d4812ba043257665a 100644 (file)
@@ -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 *
 
index 2d67464153f7a856e00ea65357888bf1a797d0fe..1a9ee094916fa39adc62674f2a6dd331025b0816 100644 (file)
@@ -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
 
index 414699758b1dc441762ddae6e0c59c561a8fe481..451d79a011167f98c62ce8a0a451a8161b230e72 100644 (file)
@@ -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')
index f72d09626ed5b2f7e1113053a794b8e9efe1ec38..ce082477bae494c4c7bd6b12f2889c20751c0b82 100644 (file)
@@ -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