chiark / gitweb /
Change how the engine is interfaced from the python code. Put the GCode viewer in...
[cura.git] / Cura / util / sliceEngine.py
index d6bdec7b2b84bf6749ab8722356ef3f3c65c23d3..ce082477bae494c4c7bd6b12f2889c20751c0b82 100644 (file)
@@ -1,3 +1,4 @@
+__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 import subprocess
 import time
 import math
@@ -8,17 +9,27 @@ import threading
 import traceback
 import platform
 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':
-               if os.path.exists('C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'):
+               if version.isDevVersion() and os.path.exists('C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'):
                        return 'C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'
-               return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'SteamEngine.exe'))
+               return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine.exe'))
        if hasattr(sys, 'frozen'):
-               return os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../..', 'SteamEngine'))
-       return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'SteamEngine'))
+               return os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../..', 'CuraEngine'))
+       if os.path.isfile('/usr/bin/CuraEngine'):
+               return '/usr/bin/CuraEngine'
+       if os.path.isfile('/usr/local/bin/CuraEngine'):
+               return '/usr/local/bin/CuraEngine'
+       return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine'))
 
 def getTempFilename():
        warnings.simplefilter('ignore')
@@ -26,130 +37,272 @@ def getTempFilename():
        warnings.simplefilter('default')
        return ret
 
-class Slicer(object):
+class EngineResult(object):
+       def __init__(self):
+               self._engineLog = []
+               self._gcodeData = StringIO.StringIO()
+               self._polygons = []
+               self._success = False
+               self._printTimeSeconds = None
+               self._filamentMM = [0.0] * 4
+               self._modelHash = None
+               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
+               radius = float(profile.getProfileSetting('filament_diameter')) / 2
+               volumeM3 = (self._filamentMM[e] * (math.pi * radius * radius)) / (1000*1000*1000)
+               return volumeM3 * profile.getPreferenceFloat('filament_physical_density')
+
+       def getFilamentCost(self, e=0):
+               cost_kg = profile.getPreferenceFloat('filament_cost_kg')
+               cost_meter = profile.getPreferenceFloat('filament_cost_meter')
+               if cost_kg > 0.0 and cost_meter > 0.0:
+                       return "%.2f / %.2f" % (self.getFilamentWeight(e) * cost_kg, self._filamentMM[e] / 1000.0 * cost_meter)
+               elif cost_kg > 0.0:
+                       return "%.2f" % (self.getFilamentWeight(e) * cost_kg)
+               elif cost_meter > 0.0:
+                       return "%.2f" % (self._filamentMM[e] / 1000.0 * cost_meter)
+               return None
+
+       def getPrintTime(self):
+               if int(self._printTimeSeconds / 60 / 60) < 1:
+                       return '%d minutes' % (int(self._printTimeSeconds / 60) % 60)
+               if int(self._printTimeSeconds / 60 / 60) == 1:
+                       return '%d hour %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
+               return '%d hours %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
+
+       def getFilamentAmount(self, e=0):
+               if self._filamentMM[e] == 0.0:
+                       return None
+               return '%0.2f meter %0.0f gram' % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0)
+
+       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._exportFilename = getTempFilename()
                self._progressSteps = ['inset', 'skin', 'export']
                self._objCount = 0
-               self._sliceLog = []
-               self._printTimeSeconds = None
-               self._filamentMM = None
+               self._result = None
 
        def cleanup(self):
-               self.abortSlicer()
+               self.abortEngine()
                try:
                        os.remove(self._binaryStorageFilename)
                except:
                        pass
-               try:
-                       os.remove(self._exportFilename)
-               except:
-                       pass
 
-       def abortSlicer(self):
+       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 getGCodeFilename(self):
-               return self._exportFilename
-
-       def getSliceLog(self):
-               return self._sliceLog
-
-       def getFilamentWeight(self):
-               #Calculates the weight of the filament in kg
-               radius = float(profile.getProfileSetting('filament_diameter')) / 2
-               volumeM3 = (self._filamentMM * (math.pi * radius * radius)) / (1000*1000*1000)
-               return volumeM3 * profile.getPreferenceFloat('filament_physical_density')
-
-       def getFilamentCost(self):
-               cost_kg = profile.getPreferenceFloat('filament_cost_kg')
-               cost_meter = profile.getPreferenceFloat('filament_cost_meter')
-               if cost_kg > 0.0 and cost_meter > 0.0:
-                       return "%.2f / %.2f" % (self.getFilamentWeight() * cost_kg, self._filamentMM / 1000.0 * cost_meter)
-               elif cost_kg > 0.0:
-                       return "%.2f" % (self.getFilamentWeight() * cost_kg)
-               elif cost_meter > 0.0:
-                       return "%.2f" % (self._filamentMM / 1000.0 * cost_meter)
-               return None
+       def wait(self):
+               if self._thread is not None:
+                       self._thread.join()
 
-       def getPrintTime(self):
-               return '%02d:%02d' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
+       def getResult(self):
+               return self._result
 
-       def getFilamentAmount(self):
-               return '%0.2fm %0.0fgram' % (float(self._filamentMM) / 1000.0, self.getFilamentWeight() * 1000.0)
+       def runEngine(self, scene):
+               if len(scene.objects()) < 1:
+                       return
+               extruderCount = 1
+               for obj in scene.objects():
+                       if scene.checkPlatform(obj):
+                               extruderCount = max(extruderCount, len(obj._meshList))
 
-       def runSlicer(self, scene):
-               self.abortSlicer()
-               self._callback(-1.0, False)
+               extruderCount = max(extruderCount, profile.minimalExtruderCount())
 
-               commandList = [getEngineFilename(), '-vv']
-               for k, v in self._engineSettings().iteritems():
+               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:
+                       hash = hashlib.sha512()
                        order = scene.printOrder()
                        if order is None:
-                               pos = numpy.array([profile.getPreferenceFloat('machine_width') * 1000 / 2, profile.getPreferenceFloat('machine_depth') * 1000 / 2])
-                               commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
-
-                               vertexTotal = 0
+                               pos = numpy.array(profile.getMachineCenterCoords()) * 1000
+                               objMin = None
+                               objMax = None
                                for obj in scene.objects():
                                        if scene.checkPlatform(obj):
-                                               for mesh in obj._meshList:
-                                                       vertexTotal += mesh.vertexCount
+                                               oMin = obj.getMinimum()[0:2] + obj.getPosition()
+                                               oMax = obj.getMaximum()[0:2] + obj.getPosition()
+                                               if objMin is None:
+                                                       objMin = oMin
+                                                       objMax = oMax
+                                               else:
+                                                       objMin[0] = min(oMin[0], objMin[0])
+                                                       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])]
 
-                               f.write(numpy.array([vertexTotal], numpy.int32).tostring())
+                               vertexTotal = [0] * 4
+                               meshMax = 1
                                for obj in scene.objects():
                                        if scene.checkPlatform(obj):
-                                               for mesh in obj._meshList:
-                                                       vertexes = (numpy.matrix(mesh.vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
-                                                       vertexes -= obj._drawOffset
-                                                       vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
-                                                       f.write(vertexes.tostring())
+                                               meshMax = max(meshMax, len(obj._meshList))
+                                               for n in xrange(0, len(obj._meshList)):
+                                                       vertexTotal[n] += obj._meshList[n].vertexCount
 
-                               commandList += ['#']
+                               for n in xrange(0, meshMax):
+                                       f.write(numpy.array([vertexTotal[n]], numpy.int32).tostring())
+                                       for obj in scene.objects():
+                                               if scene.checkPlatform(obj):
+                                                       if n < len(obj._meshList):
+                                                               vertexes = (numpy.matrix(obj._meshList[n].vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
+                                                               vertexes -= obj._drawOffset
+                                                               vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
+                                                               f.write(vertexes.tostring())
+                                                               hash.update(obj._meshList[n].vertexes.tostring())
+
+                               commandList += ['#' * meshMax]
                                self._objCount = 1
                        else:
                                for n in order:
                                        obj = scene.objects()[n]
                                        for mesh in obj._meshList:
                                                f.write(numpy.array([mesh.vertexCount], numpy.int32).tostring())
-                                               f.write(mesh.vertexes.tostring())
+                                               s = mesh.vertexes.tostring()
+                                               f.write(s)
+                                               hash.update(s)
                                        pos = obj.getPosition() * 1000
-                                       pos += numpy.array([profile.getPreferenceFloat('machine_width') * 1000 / 2, profile.getPreferenceFloat('machine_depth') * 1000 / 2])
+                                       pos += numpy.array(profile.getMachineCenterCoords()) * 1000
                                        commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
                                        commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
                                        commandList += ['#' * len(obj._meshList)]
                                        self._objCount += 1
+                       modelHash = hash.hexdigest()
                if self._objCount > 0:
-                       try:
-                               self._process = self._runSliceProcess(commandList)
-                               self._thread = threading.Thread(target=self._watchProcess)
-                               self._thread.daemon = True
-                               self._thread.start()
-                       except OSError:
-                               traceback.print_exc()
-
-       def _watchProcess(self):
-               self._callback(0.0, False)
-               self._sliceLog = []
-               self._printTimeSeconds = None
-               self._filamentMM = None
+                       self._thread = threading.Thread(target=self._watchProcess, args=(commandList, self._thread, modelHash))
+                       self._thread.daemon = True
+                       self._thread.start()
+
+       def _watchProcess(self, commandList, oldThread, modelHash):
+               if oldThread is not None:
+                       if self._process is not None:
+                               self._process.terminate()
+                       oldThread.join()
+               self._callback(-1.0)
+               try:
+                       self._process = self._runEngineProcess(commandList)
+               except OSError:
+                       traceback.print_exc()
+                       return
+               if self._thread != threading.currentThread():
+                       self._process.terminate()
 
-               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(':')
@@ -163,58 +316,146 @@ 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 = 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._result._filamentMM[0] /= (math.pi * radius * radius)
+                       elif line.startswith('Filament2:'):
+                               self._result._filamentMM[1] = int(line.split(':')[1].strip())
+                               if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
+                                       radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
+                                       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:
-                               self._callback(1.0, True)
-                       else:
-                               self._callback(-1.0, False)
-               except:
-                       pass
-               self._process = None
+                               self._result.addLog(line)
+                       line = stderr.readline()
 
-       def _engineSettings(self):
-               return {
+       def _engineSettings(self, extruderCount):
+               settings = {
                        'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
-                       'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000),
+                       'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000) if profile.getProfileSettingFloat('bottom_thickness') > 0.0 else int(profile.getProfileSettingFloat('layer_height') * 1000),
                        'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
+                       'filamentFlow': int(profile.getProfileSettingFloat('filament_flow')),
                        'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
                        'insetCount': int(profile.calculateLineCount()),
                        'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
                        'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
-                       'sparseInfillLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density')) if profile.getProfileSettingFloat('fill_density') > 0 else 9999999999,
-                       'skirtDistance': int(profile.getProfileSettingFloat('skirt_gap') * 1000),
-                       'skirtLineCount': int(profile.getProfileSettingFloat('skirt_line_count')),
+                       'infillOverlap': int(profile.getProfileSettingFloat('fill_overlap')),
                        'initialSpeedupLayers': int(4),
                        'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
                        'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
+                       'infillSpeed': int(profile.getProfileSettingFloat('infill_speed')) if int(profile.getProfileSettingFloat('infill_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
                        'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
-                       'fanOnLayerNr': int(profile.getProfileSettingFloat('fan_layer')),
-                       'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(60),
+                       'fanSpeedMin': int(profile.getProfileSettingFloat('fan_speed')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
+                       'fanSpeedMax': int(profile.getProfileSettingFloat('fan_speed_max')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
+                       'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(profile.getProfileSettingFloat('support_angle')),
                        'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
-                       'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000),
+                       'supportLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('support_fill_rate')) if profile.getProfileSettingFloat('support_fill_rate') > 0 else -1,
+                       'supportXYDistance': int(1000 * profile.getProfileSettingFloat('support_xy_distance')),
+                       'supportZDistance': int(1000 * profile.getProfileSettingFloat('support_z_distance')),
+                       'supportExtruder': 0 if profile.getProfileSetting('support_dual_extrusion') == 'First extruder' else (1 if profile.getProfileSetting('support_dual_extrusion') == 'Second extruder' and profile.minimalExtruderCount() > 1 else -1),
+                       'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000) if profile.getProfileSetting('retraction_enable') == 'True' else 0,
                        'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
+                       'retractionMinimalDistance': int(profile.getProfileSettingFloat('retraction_min_travel') * 1000),
+                       'retractionAmountExtruderSwitch': int(profile.getProfileSettingFloat('retraction_dual_amount') * 1000),
+                       'minimalExtrusionBeforeRetraction': int(profile.getProfileSettingFloat('retraction_minimal_extrusion') * 1000),
+                       'enableCombing': 1 if profile.getProfileSetting('retraction_combing') == 'True' else 0,
+                       'multiVolumeOverlap': int(profile.getProfileSettingFloat('overlap_dual') * 1000),
                        'objectSink': int(profile.getProfileSettingFloat('object_sink') * 1000),
                        'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
                        'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
                        'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
-                       'startCode': profile.getAlterationFileContents('start.gcode'),
-                       'endCode': profile.getAlterationFileContents('end.gcode'),
+                       'startCode': profile.getAlterationFileContents('start.gcode', extruderCount),
+                       'endCode': profile.getAlterationFileContents('end.gcode', extruderCount),
+
+                       'extruderOffset[1].X': int(profile.getMachineSettingFloat('extruder_offset_x1') * 1000),
+                       'extruderOffset[1].Y': int(profile.getMachineSettingFloat('extruder_offset_y1') * 1000),
+                       'extruderOffset[2].X': int(profile.getMachineSettingFloat('extruder_offset_x2') * 1000),
+                       'extruderOffset[2].Y': int(profile.getMachineSettingFloat('extruder_offset_y2') * 1000),
+                       'extruderOffset[3].X': int(profile.getMachineSettingFloat('extruder_offset_x3') * 1000),
+                       'extruderOffset[3].Y': int(profile.getMachineSettingFloat('extruder_offset_y3') * 1000),
+                       'fixHorrible': 0,
                }
+               fanFullHeight = int(profile.getProfileSettingFloat('fan_full_height') * 1000)
+               settings['fanFullOnLayerNr'] = (fanFullHeight - settings['initialLayerThickness'] - 1) / settings['layerThickness'] + 1
+               if settings['fanFullOnLayerNr'] < 0:
+                       settings['fanFullOnLayerNr'] = 0
+
+               if profile.getProfileSettingFloat('fill_density') == 0:
+                       settings['sparseInfillLineDistance'] = -1
+               elif profile.getProfileSettingFloat('fill_density') == 100:
+                       settings['sparseInfillLineDistance'] = settings['extrusionWidth']
+                       #Set the up/down skins height to 10000 if we want a 100% filled object.
+                       # This gives better results then normal 100% infill as the sparse and up/down skin have some overlap.
+                       settings['downSkinCount'] = 10000
+                       settings['upSkinCount'] = 10000
+               else:
+                       settings['sparseInfillLineDistance'] = int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density'))
+               if profile.getProfileSetting('platform_adhesion') == 'Brim':
+                       settings['skirtDistance'] = 0
+                       settings['skirtLineCount'] = int(profile.getProfileSettingFloat('brim_line_count'))
+               elif profile.getProfileSetting('platform_adhesion') == 'Raft':
+                       settings['skirtDistance'] = 0
+                       settings['skirtLineCount'] = 0
+                       settings['raftMargin'] = int(profile.getProfileSettingFloat('raft_margin') * 1000)
+                       settings['raftLineSpacing'] = int(profile.getProfileSettingFloat('raft_line_spacing') * 1000)
+                       settings['raftBaseThickness'] = int(profile.getProfileSettingFloat('raft_base_thickness') * 1000)
+                       settings['raftBaseLinewidth'] = int(profile.getProfileSettingFloat('raft_base_linewidth') * 1000)
+                       settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000)
+                       settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000)
+               else:
+                       settings['skirtDistance'] = int(profile.getProfileSettingFloat('skirt_gap') * 1000)
+                       settings['skirtLineCount'] = int(profile.getProfileSettingFloat('skirt_line_count'))
+                       settings['skirtMinLength'] = int(profile.getProfileSettingFloat('skirt_minimal_length') * 1000)
+
+               if profile.getProfileSetting('fix_horrible_union_all_type_a') == 'True':
+                       settings['fixHorrible'] |= 0x01
+               if profile.getProfileSetting('fix_horrible_union_all_type_b') == 'True':
+                       settings['fixHorrible'] |= 0x02
+               if profile.getProfileSetting('fix_horrible_use_open_bits') == 'True':
+                       settings['fixHorrible'] |= 0x10
+               if profile.getProfileSetting('fix_horrible_extensive_stitching') == 'True':
+                       settings['fixHorrible'] |= 0x04
+
+               if settings['layerThickness'] <= 0:
+                       settings['layerThickness'] = 1000
+               if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
+                       settings['gcodeFlavor'] = 1
+               if profile.getProfileSetting('spiralize') == 'True':
+                       settings['spiralizeMode'] = 1
+               if profile.getProfileSetting('wipe_tower') == 'True':
+                       settings['wipeTowerSize'] = int(math.sqrt(profile.getProfileSettingFloat('wipe_tower_volume') * 1000 * 1000 * 1000 / settings['layerThickness']))
+               if profile.getProfileSetting('ooze_shield') == 'True':
+                       settings['enableOozeShield'] = 1
+               return settings
 
-       def _runSliceProcess(self, cmdList):
+       def _runEngineProcess(self, cmdList):
                kwargs = {}
                if subprocess.mswindows:
                        su = subprocess.STARTUPINFO()