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