chiark / gitweb /
Add more raft settings and enable the surface layers for the raft.
[cura.git] / Cura / util / sliceEngine.py
1 """
2 Slice engine communication.
3 This module handles all communication with the slicing engine.
4 """
5 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
6 import subprocess
7 import time
8 import math
9 import numpy
10 import os
11 import warnings
12 import threading
13 import traceback
14 import platform
15 import sys
16 import urllib
17 import urllib2
18 import hashlib
19 import socket
20 import struct
21 import errno
22 import cStringIO as StringIO
23
24 from Cura.util import profile
25 from Cura.util import pluginInfo
26 from Cura.util import version
27 from Cura.util import gcodeInterpreter
28
29 def getEngineFilename():
30         """
31                 Finds and returns the path to the current engine executable. This is OS depended.
32         :return: The full path to the engine executable.
33         """
34         if platform.system() == 'Windows':
35                 if version.isDevVersion() and os.path.exists('C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'):
36                         return 'C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'
37                 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine.exe'))
38         if hasattr(sys, 'frozen'):
39                 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../..', 'CuraEngine'))
40         if os.path.isfile('/usr/bin/CuraEngine'):
41                 return '/usr/bin/CuraEngine'
42         if os.path.isfile('/usr/local/bin/CuraEngine'):
43                 return '/usr/local/bin/CuraEngine'
44         tempPath = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine'))
45         if os.path.isdir(tempPath):
46                 tempPath = os.path.join(tempPath,'CuraEngine')
47         return tempPath
48
49 class EngineResult(object):
50         """
51         Result from running the CuraEngine.
52         Contains the engine log, polygons retrieved from the engine, the GCode and some meta-data.
53         """
54         def __init__(self):
55                 self._engineLog = []
56                 self._gcodeData = StringIO.StringIO()
57                 self._polygons = []
58                 self._replaceInfo = {}
59                 self._success = False
60                 self._printTimeSeconds = None
61                 self._filamentMM = [0.0] * 4
62                 self._modelHash = None
63                 self._profileString = profile.getProfileString()
64                 self._preferencesString = profile.getPreferencesString()
65                 self._gcodeInterpreter = gcodeInterpreter.gcode()
66                 self._gcodeLoadThread = None
67                 self._finished = False
68
69         def getFilamentWeight(self, e=0):
70                 #Calculates the weight of the filament in kg
71                 radius = float(profile.getProfileSetting('filament_diameter')) / 2
72                 volumeM3 = (self._filamentMM[e] * (math.pi * radius * radius)) / (1000*1000*1000)
73                 return volumeM3 * profile.getPreferenceFloat('filament_physical_density')
74
75         def getFilamentCost(self, e=0):
76                 cost_kg = profile.getPreferenceFloat('filament_cost_kg')
77                 cost_meter = profile.getPreferenceFloat('filament_cost_meter')
78                 if cost_kg > 0.0 and cost_meter > 0.0:
79                         return "%.2f / %.2f" % (self.getFilamentWeight(e) * cost_kg, self._filamentMM[e] / 1000.0 * cost_meter)
80                 elif cost_kg > 0.0:
81                         return "%.2f" % (self.getFilamentWeight(e) * cost_kg)
82                 elif cost_meter > 0.0:
83                         return "%.2f" % (self._filamentMM[e] / 1000.0 * cost_meter)
84                 return None
85
86         def getPrintTime(self):
87                 if self._printTimeSeconds is None:
88                         return ''
89                 if int(self._printTimeSeconds / 60 / 60) < 1:
90                         return '%d minutes' % (int(self._printTimeSeconds / 60) % 60)
91                 if int(self._printTimeSeconds / 60 / 60) == 1:
92                         return '%d hour %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
93                 return '%d hours %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
94
95         def getFilamentAmount(self, e=0):
96                 if self._filamentMM[e] == 0.0:
97                         return None
98                 return '%0.2f meter %0.0f gram' % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0)
99
100         def getLog(self):
101                 return self._engineLog
102
103         def getGCode(self):
104                 data = self._gcodeData.getvalue()
105                 if len(self._replaceInfo) > 0:
106                         block0 = data[0:2048]
107                         for k, v in self._replaceInfo.items():
108                                 v = (v + ' ' * len(k))[:len(k)]
109                                 block0 = block0.replace(k, v)
110                         return block0 + data[2048:]
111                 return data
112
113         def setGCode(self, gcode):
114                 self._gcodeData = StringIO.StringIO(gcode)
115                 self._replaceInfo = {}
116
117         def addLog(self, line):
118                 self._engineLog.append(line)
119
120         def setHash(self, hash):
121                 self._modelHash = hash
122
123         def setFinished(self, result):
124                 self._finished = result
125
126         def isFinished(self):
127                 return self._finished
128
129         def getGCodeLayers(self, loadCallback):
130                 if not self._finished:
131                         return None
132                 if self._gcodeInterpreter.layerList is None and self._gcodeLoadThread is None:
133                         self._gcodeInterpreter.progressCallback = self._gcodeInterpreterCallback
134                         self._gcodeLoadThread = threading.Thread(target=lambda : self._gcodeInterpreter.load(self._gcodeData))
135                         self._gcodeLoadCallback = loadCallback
136                         self._gcodeLoadThread.daemon = True
137                         self._gcodeLoadThread.start()
138                 return self._gcodeInterpreter.layerList
139
140         def _gcodeInterpreterCallback(self, progress):
141                 if len(self._gcodeInterpreter.layerList) % 5 == 0:
142                         time.sleep(0.1)
143                 return self._gcodeLoadCallback(self, progress)
144
145         def submitInfoOnline(self):
146                 if profile.getPreference('submit_slice_information') != 'True':
147                         return
148                 if version.isDevVersion():
149                         return
150                 data = {
151                         'processor': platform.processor(),
152                         'machine': platform.machine(),
153                         'platform': platform.platform(),
154                         'profile': self._profileString,
155                         'preferences': self._preferencesString,
156                         'modelhash': self._modelHash,
157                         'version': version.getVersion(),
158                 }
159                 try:
160                         f = urllib2.urlopen("http://www.youmagine.com/curastats/", data = urllib.urlencode(data), timeout = 1)
161                         f.read()
162                         f.close()
163                 except:
164                         pass
165
166 class Engine(object):
167         """
168         Class used to communicate with the CuraEngine.
169         The CuraEngine is ran as a 2nd process and reports back information trough stderr.
170         GCode trough stdout and has a socket connection for polygon information and loading the 3D model into the engine.
171         """
172         GUI_CMD_REQUEST_MESH = 0x01
173         GUI_CMD_SEND_POLYGONS = 0x02
174         GUI_CMD_FINISH_OBJECT = 0x03
175
176         def __init__(self, progressCallback):
177                 self._process = None
178                 self._thread = None
179                 self._callback = progressCallback
180                 self._progressSteps = ['inset', 'skin', 'export']
181                 self._objCount = 0
182                 self._result = None
183
184                 self._serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
185                 self._serverPortNr = 0xC20A
186                 while True:
187                         try:
188                                 self._serversocket.bind(('127.0.0.1', self._serverPortNr))
189                         except:
190                                 print "Failed to listen on port: %d" % (self._serverPortNr)
191                                 self._serverPortNr += 1
192                                 if self._serverPortNr > 0xFFFF:
193                                         print "Failed to listen on any port..."
194                                         break
195                         else:
196                                 break
197                 thread = threading.Thread(target=self._socketListenThread)
198                 thread.daemon = True
199                 thread.start()
200
201         def _socketListenThread(self):
202                 self._serversocket.listen(1)
203                 print 'Listening for engine communications on %d' % (self._serverPortNr)
204                 while True:
205                         try:
206                                 sock, _ = self._serversocket.accept()
207                                 thread = threading.Thread(target=self._socketConnectionThread, args=(sock,))
208                                 thread.daemon = True
209                                 thread.start()
210                         except socket.error, e:
211                                 if e.errno != errno.EINTR:
212                                         raise
213
214         def _socketConnectionThread(self, sock):
215                 layerNrOffset = 0
216                 while True:
217                         try:
218                                 data = sock.recv(4)
219                         except:
220                                 data = ''
221                         if len(data) == 0:
222                                 sock.close()
223                                 return
224                         cmd = struct.unpack('@i', data)[0]
225                         if cmd == self.GUI_CMD_REQUEST_MESH:
226                                 meshInfo = self._modelData[0]
227                                 self._modelData = self._modelData[1:]
228                                 sock.sendall(struct.pack('@i', meshInfo[0]))
229                                 sock.sendall(meshInfo[1].tostring())
230                         elif cmd == self.GUI_CMD_SEND_POLYGONS:
231                                 cnt = struct.unpack('@i', sock.recv(4))[0]
232                                 layerNr = struct.unpack('@i', sock.recv(4))[0]
233                                 layerNr += layerNrOffset
234                                 z = struct.unpack('@i', sock.recv(4))[0]
235                                 z = float(z) / 1000.0
236                                 typeNameLen = struct.unpack('@i', sock.recv(4))[0]
237                                 typeName = sock.recv(typeNameLen)
238                                 while len(self._result._polygons) < layerNr + 1:
239                                         self._result._polygons.append({})
240                                 polygons = self._result._polygons[layerNr]
241                                 if typeName not in polygons:
242                                         polygons[typeName] = []
243                                 for n in xrange(0, cnt):
244                                         length = struct.unpack('@i', sock.recv(4))[0]
245                                         data = ''
246                                         while len(data) < length * 8 * 2:
247                                                 recvData = sock.recv(length * 8 * 2 - len(data))
248                                                 if len(recvData) < 1:
249                                                         return
250                                                 data += recvData
251                                         polygon2d = numpy.array(numpy.fromstring(data, numpy.int64), numpy.float32) / 1000.0
252                                         polygon2d = polygon2d.reshape((len(polygon2d) / 2, 2))
253                                         polygon = numpy.empty((len(polygon2d), 3), numpy.float32)
254                                         polygon[:,:-1] = polygon2d
255                                         polygon[:,2] = z
256                                         polygons[typeName].append(polygon)
257                         elif cmd == self.GUI_CMD_FINISH_OBJECT:
258                                 layerNrOffset = len(self._result._polygons)
259                         else:
260                                 print "Unknown command on socket: %x" % (cmd)
261
262         def cleanup(self):
263                 self.abortEngine()
264                 self._serversocket.close()
265
266         def abortEngine(self):
267                 if self._process is not None:
268                         try:
269                                 self._process.terminate()
270                         except:
271                                 pass
272                 if self._thread is not None:
273                         self._thread.join()
274                 self._thread = None
275
276         def wait(self):
277                 if self._thread is not None:
278                         self._thread.join()
279
280         def getResult(self):
281                 return self._result
282
283         def runEngine(self, scene):
284                 if len(scene.objects()) < 1:
285                         return
286                 extruderCount = 1
287                 for obj in scene.objects():
288                         if scene.checkPlatform(obj):
289                                 extruderCount = max(extruderCount, len(obj._meshList))
290
291                 extruderCount = max(extruderCount, profile.minimalExtruderCount())
292
293                 commandList = [getEngineFilename(), '-v', '-p']
294                 for k, v in self._engineSettings(extruderCount).iteritems():
295                         commandList += ['-s', '%s=%s' % (k, str(v))]
296                 commandList += ['-g', '%d' % (self._serverPortNr)]
297                 self._objCount = 0
298                 engineModelData = []
299                 hash = hashlib.sha512()
300                 order = scene.printOrder()
301                 if order is None:
302                         pos = numpy.array(profile.getMachineCenterCoords()) * 1000
303                         objMin = None
304                         objMax = None
305                         for obj in scene.objects():
306                                 if scene.checkPlatform(obj):
307                                         oMin = obj.getMinimum()[0:2] + obj.getPosition()
308                                         oMax = obj.getMaximum()[0:2] + obj.getPosition()
309                                         if objMin is None:
310                                                 objMin = oMin
311                                                 objMax = oMax
312                                         else:
313                                                 objMin[0] = min(oMin[0], objMin[0])
314                                                 objMin[1] = min(oMin[1], objMin[1])
315                                                 objMax[0] = max(oMax[0], objMax[0])
316                                                 objMax[1] = max(oMax[1], objMax[1])
317                         if objMin is None:
318                                 return
319                         pos += (objMin + objMax) / 2.0 * 1000
320                         commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
321
322                         vertexTotal = [0] * 4
323                         meshMax = 1
324                         for obj in scene.objects():
325                                 if scene.checkPlatform(obj):
326                                         meshMax = max(meshMax, len(obj._meshList))
327                                         for n in xrange(0, len(obj._meshList)):
328                                                 vertexTotal[n] += obj._meshList[n].vertexCount
329
330                         for n in xrange(0, meshMax):
331                                 verts = numpy.zeros((0, 3), numpy.float32)
332                                 for obj in scene.objects():
333                                         if scene.checkPlatform(obj):
334                                                 if n < len(obj._meshList):
335                                                         vertexes = (numpy.matrix(obj._meshList[n].vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
336                                                         vertexes -= obj._drawOffset
337                                                         vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
338                                                         verts = numpy.concatenate((verts, vertexes))
339                                                         hash.update(obj._meshList[n].vertexes.tostring())
340                                 engineModelData.append((vertexTotal[n], verts))
341
342                         commandList += ['$' * meshMax]
343                         self._objCount = 1
344                 else:
345                         for n in order:
346                                 obj = scene.objects()[n]
347                                 for mesh in obj._meshList:
348                                         engineModelData.append((mesh.vertexCount, mesh.vertexes))
349                                         hash.update(mesh.vertexes.tostring())
350                                 pos = obj.getPosition() * 1000
351                                 pos += numpy.array(profile.getMachineCenterCoords()) * 1000
352                                 commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
353                                 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
354                                 commandList += ['$' * len(obj._meshList)]
355                                 self._objCount += 1
356                 modelHash = hash.hexdigest()
357                 if self._objCount > 0:
358                         self._thread = threading.Thread(target=self._watchProcess, args=(commandList, self._thread, engineModelData, modelHash))
359                         self._thread.daemon = True
360                         self._thread.start()
361
362         def _watchProcess(self, commandList, oldThread, engineModelData, modelHash):
363                 if oldThread is not None:
364                         if self._process is not None:
365                                 self._process.terminate()
366                         oldThread.join()
367                 self._callback(-1.0)
368                 self._modelData = engineModelData
369                 try:
370                         self._process = self._runEngineProcess(commandList)
371                 except OSError:
372                         traceback.print_exc()
373                         return
374                 if self._thread != threading.currentThread():
375                         self._process.terminate()
376
377                 self._result = EngineResult()
378                 self._result.addLog('Running: %s' % (' '.join(commandList)))
379                 self._result.setHash(modelHash)
380                 self._callback(0.0)
381
382                 logThread = threading.Thread(target=self._watchStderr, args=(self._process.stderr,))
383                 logThread.daemon = True
384                 logThread.start()
385
386                 data = self._process.stdout.read(4096)
387                 while len(data) > 0:
388                         self._result._gcodeData.write(data)
389                         data = self._process.stdout.read(4096)
390
391                 returnCode = self._process.wait()
392                 logThread.join()
393                 if returnCode == 0:
394                         pluginError = pluginInfo.runPostProcessingPlugins(self._result)
395                         if pluginError is not None:
396                                 print pluginError
397                                 self._result.addLog(pluginError)
398                         self._result.setFinished(True)
399                         self._callback(1.0)
400                 else:
401                         for line in self._result.getLog():
402                                 print line
403                         self._callback(-1.0)
404                 self._process = None
405
406         def _watchStderr(self, stderr):
407                 objectNr = 0
408                 line = stderr.readline()
409                 while len(line) > 0:
410                         line = line.strip()
411                         if line.startswith('Progress:'):
412                                 line = line.split(':')
413                                 if line[1] == 'process':
414                                         objectNr += 1
415                                 elif line[1] in self._progressSteps:
416                                         progressValue = float(line[2]) / float(line[3])
417                                         progressValue /= len(self._progressSteps)
418                                         progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
419
420                                         progressValue /= self._objCount
421                                         progressValue += 1.0 / self._objCount * objectNr
422                                         try:
423                                                 self._callback(progressValue)
424                                         except:
425                                                 pass
426                         elif line.startswith('Print time:'):
427                                 self._result._printTimeSeconds = int(line.split(':')[1].strip())
428                         elif line.startswith('Filament:'):
429                                 self._result._filamentMM[0] = int(line.split(':')[1].strip())
430                                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
431                                         radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
432                                         self._result._filamentMM[0] /= (math.pi * radius * radius)
433                         elif line.startswith('Filament2:'):
434                                 self._result._filamentMM[1] = int(line.split(':')[1].strip())
435                                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
436                                         radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
437                                         self._result._filamentMM[1] /= (math.pi * radius * radius)
438                         elif line.startswith('Replace:'):
439                                 self._result._replaceInfo[line.split(':')[1].strip()] = line.split(':')[2].strip()
440                         else:
441                                 self._result.addLog(line)
442                         line = stderr.readline()
443
444         def _engineSettings(self, extruderCount):
445                 settings = {
446                         'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
447                         'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000) if profile.getProfileSettingFloat('bottom_thickness') > 0.0 else int(profile.getProfileSettingFloat('layer_height') * 1000),
448                         'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
449                         'filamentFlow': int(profile.getProfileSettingFloat('filament_flow')),
450                         'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
451                         'layer0extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
452                         'insetCount': int(profile.calculateLineCount()),
453                         'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
454                         'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
455                         'infillOverlap': int(profile.getProfileSettingFloat('fill_overlap')),
456                         'initialSpeedupLayers': int(4),
457                         'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
458                         'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
459                         'infillSpeed': int(profile.getProfileSettingFloat('infill_speed')) if int(profile.getProfileSettingFloat('infill_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
460                         'inset0Speed': int(profile.getProfileSettingFloat('inset0_speed')) if int(profile.getProfileSettingFloat('inset0_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
461                         'insetXSpeed': int(profile.getProfileSettingFloat('insetx_speed')) if int(profile.getProfileSettingFloat('insetx_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
462                         'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
463                         'fanSpeedMin': int(profile.getProfileSettingFloat('fan_speed')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
464                         'fanSpeedMax': int(profile.getProfileSettingFloat('fan_speed_max')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
465                         'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(profile.getProfileSettingFloat('support_angle')),
466                         'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
467                         'supportLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('support_fill_rate')) if profile.getProfileSettingFloat('support_fill_rate') > 0 else -1,
468                         'supportXYDistance': int(1000 * profile.getProfileSettingFloat('support_xy_distance')),
469                         'supportZDistance': int(1000 * profile.getProfileSettingFloat('support_z_distance')),
470                         '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),
471                         'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000) if profile.getProfileSetting('retraction_enable') == 'True' else 0,
472                         'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
473                         'retractionMinimalDistance': int(profile.getProfileSettingFloat('retraction_min_travel') * 1000),
474                         'retractionAmountExtruderSwitch': int(profile.getProfileSettingFloat('retraction_dual_amount') * 1000),
475                         'retractionZHop': int(profile.getProfileSettingFloat('retraction_hop') * 1000),
476                         'minimalExtrusionBeforeRetraction': int(profile.getProfileSettingFloat('retraction_minimal_extrusion') * 1000),
477                         'enableCombing': 1 if profile.getProfileSetting('retraction_combing') == 'True' else 0,
478                         'multiVolumeOverlap': int(profile.getProfileSettingFloat('overlap_dual') * 1000),
479                         'objectSink': max(0, int(profile.getProfileSettingFloat('object_sink') * 1000)),
480                         'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
481                         'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
482                         'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
483                         'startCode': profile.getAlterationFileContents('start.gcode', extruderCount),
484                         'endCode': profile.getAlterationFileContents('end.gcode', extruderCount),
485
486                         'extruderOffset[1].X': int(profile.getMachineSettingFloat('extruder_offset_x1') * 1000),
487                         'extruderOffset[1].Y': int(profile.getMachineSettingFloat('extruder_offset_y1') * 1000),
488                         'extruderOffset[2].X': int(profile.getMachineSettingFloat('extruder_offset_x2') * 1000),
489                         'extruderOffset[2].Y': int(profile.getMachineSettingFloat('extruder_offset_y2') * 1000),
490                         'extruderOffset[3].X': int(profile.getMachineSettingFloat('extruder_offset_x3') * 1000),
491                         'extruderOffset[3].Y': int(profile.getMachineSettingFloat('extruder_offset_y3') * 1000),
492                         'fixHorrible': 0,
493                 }
494                 fanFullHeight = int(profile.getProfileSettingFloat('fan_full_height') * 1000)
495                 settings['fanFullOnLayerNr'] = (fanFullHeight - settings['initialLayerThickness'] - 1) / settings['layerThickness'] + 1
496                 if settings['fanFullOnLayerNr'] < 0:
497                         settings['fanFullOnLayerNr'] = 0
498                 if profile.getProfileSetting('support_type') == 'Lines':
499                         settings['supportType'] = 1
500
501                 if profile.getProfileSettingFloat('fill_density') == 0:
502                         settings['sparseInfillLineDistance'] = -1
503                 elif profile.getProfileSettingFloat('fill_density') == 100:
504                         settings['sparseInfillLineDistance'] = settings['extrusionWidth']
505                         #Set the up/down skins height to 10000 if we want a 100% filled object.
506                         # This gives better results then normal 100% infill as the sparse and up/down skin have some overlap.
507                         settings['downSkinCount'] = 10000
508                         settings['upSkinCount'] = 10000
509                 else:
510                         settings['sparseInfillLineDistance'] = int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density'))
511                 if profile.getProfileSetting('platform_adhesion') == 'Brim':
512                         settings['skirtDistance'] = 0
513                         settings['skirtLineCount'] = int(profile.getProfileSettingFloat('brim_line_count'))
514                 elif profile.getProfileSetting('platform_adhesion') == 'Raft':
515                         settings['skirtDistance'] = 0
516                         settings['skirtLineCount'] = 0
517                         settings['raftMargin'] = int(profile.getProfileSettingFloat('raft_margin') * 1000)
518                         settings['raftLineSpacing'] = int(profile.getProfileSettingFloat('raft_line_spacing') * 1000)
519                         settings['raftBaseThickness'] = int(profile.getProfileSettingFloat('raft_base_thickness') * 1000)
520                         settings['raftBaseLinewidth'] = int(profile.getProfileSettingFloat('raft_base_linewidth') * 1000)
521                         settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000)
522                         settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000)
523                         settings['raftAirGap'] = int(profile.getProfileSettingFloat('raft_airgap') * 1000)
524                         settings['raftBaseSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
525                         settings['raftFanSpeed'] = 0
526                         settings['raftSurfaceThickness'] = 200
527                         settings['raftSurfaceLinewidth'] = int(profile.calculateEdgeWidth() * 1000)
528                         settings['raftSurfaceLineSpacing'] = int(profile.calculateEdgeWidth() * 1000)
529                         settings['raftSurfaceLayers'] = int(profile.getProfileSettingFloat('raft_surface_layers'))
530                         settings['raftSurfaceSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
531                 else:
532                         settings['skirtDistance'] = int(profile.getProfileSettingFloat('skirt_gap') * 1000)
533                         settings['skirtLineCount'] = int(profile.getProfileSettingFloat('skirt_line_count'))
534                         settings['skirtMinLength'] = int(profile.getProfileSettingFloat('skirt_minimal_length') * 1000)
535
536                 if profile.getProfileSetting('fix_horrible_union_all_type_a') == 'True':
537                         settings['fixHorrible'] |= 0x01
538                 if profile.getProfileSetting('fix_horrible_union_all_type_b') == 'True':
539                         settings['fixHorrible'] |= 0x02
540                 if profile.getProfileSetting('fix_horrible_use_open_bits') == 'True':
541                         settings['fixHorrible'] |= 0x10
542                 if profile.getProfileSetting('fix_horrible_extensive_stitching') == 'True':
543                         settings['fixHorrible'] |= 0x04
544
545                 if settings['layerThickness'] <= 0:
546                         settings['layerThickness'] = 1000
547                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
548                         settings['gcodeFlavor'] = 1
549                 elif profile.getMachineSetting('gcode_flavor') == 'MakerBot':
550                         settings['gcodeFlavor'] = 2
551                 elif profile.getMachineSetting('gcode_flavor') == 'BFB':
552                         settings['gcodeFlavor'] = 3
553                 elif profile.getMachineSetting('gcode_flavor') == 'Mach3':
554                         settings['gcodeFlavor'] = 4
555                 elif profile.getMachineSetting('gcode_flavor') == 'RepRap (Volumetric)':
556                         settings['gcodeFlavor'] = 5
557                 if profile.getProfileSetting('spiralize') == 'True':
558                         settings['spiralizeMode'] = 1
559                 if profile.getProfileSetting('wipe_tower') == 'True' and extruderCount > 1:
560                         settings['wipeTowerSize'] = int(math.sqrt(profile.getProfileSettingFloat('wipe_tower_volume') * 1000 * 1000 * 1000 / settings['layerThickness']))
561                 if profile.getProfileSetting('ooze_shield') == 'True':
562                         settings['enableOozeShield'] = 1
563                 return settings
564
565         def _runEngineProcess(self, cmdList):
566                 kwargs = {}
567                 if subprocess.mswindows:
568                         su = subprocess.STARTUPINFO()
569                         su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
570                         su.wShowWindow = subprocess.SW_HIDE
571                         kwargs['startupinfo'] = su
572                         kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
573                 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)