12 from Cura.util import profile
14 def getEngineFilename():
15 if platform.system() == 'Windows':
16 if os.path.exists('C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'):
17 return 'C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'
18 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine.exe'))
19 if hasattr(sys, 'frozen'):
20 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../..', 'CuraEngine'))
21 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine'))
23 def getTempFilename():
24 warnings.simplefilter('ignore')
25 ret = os.tempnam(None, "Cura_Tmp")
26 warnings.simplefilter('default')
30 def __init__(self, progressCallback):
33 self._callback = progressCallback
34 self._binaryStorageFilename = getTempFilename()
35 self._exportFilename = getTempFilename()
36 self._progressSteps = ['inset', 'skin', 'export']
39 self._printTimeSeconds = None
40 self._filamentMM = None
45 os.remove(self._binaryStorageFilename)
49 os.remove(self._exportFilename)
53 def abortSlicer(self):
54 if self._process is not None:
56 self._process.terminate()
61 def getGCodeFilename(self):
62 return self._exportFilename
64 def getSliceLog(self):
67 def getFilamentWeight(self):
68 #Calculates the weight of the filament in kg
69 radius = float(profile.getProfileSetting('filament_diameter')) / 2
70 volumeM3 = (self._filamentMM * (math.pi * radius * radius)) / (1000*1000*1000)
71 return volumeM3 * profile.getPreferenceFloat('filament_physical_density')
73 def getFilamentCost(self):
74 cost_kg = profile.getPreferenceFloat('filament_cost_kg')
75 cost_meter = profile.getPreferenceFloat('filament_cost_meter')
76 if cost_kg > 0.0 and cost_meter > 0.0:
77 return "%.2f / %.2f" % (self.getFilamentWeight() * cost_kg, self._filamentMM / 1000.0 * cost_meter)
79 return "%.2f" % (self.getFilamentWeight() * cost_kg)
80 elif cost_meter > 0.0:
81 return "%.2f" % (self._filamentMM / 1000.0 * cost_meter)
84 def getPrintTime(self):
85 if int(self._printTimeSeconds / 60 / 60) < 1:
86 return '%d minutes' % (int(self._printTimeSeconds / 60) % 60)
87 if int(self._printTimeSeconds / 60 / 60) == 1:
88 return '%d hour %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
89 return '%d hours %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
91 def getFilamentAmount(self):
92 return '%0.2f meter %0.0f gram' % (float(self._filamentMM) / 1000.0, self.getFilamentWeight() * 1000.0)
94 def runSlicer(self, scene):
96 self._callback(-1.0, False)
98 commandList = [getEngineFilename(), '-vv']
99 for k, v in self._engineSettings().iteritems():
100 commandList += ['-s', '%s=%s' % (k, str(v))]
101 commandList += ['-o', self._exportFilename]
102 commandList += ['-b', self._binaryStorageFilename]
104 with open(self._binaryStorageFilename, "wb") as f:
105 order = scene.printOrder()
107 pos = numpy.array([profile.getPreferenceFloat('machine_width') * 1000 / 2, profile.getPreferenceFloat('machine_depth') * 1000 / 2])
108 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
111 for obj in scene.objects():
112 if scene.checkPlatform(obj):
113 for mesh in obj._meshList:
114 vertexTotal += mesh.vertexCount
116 f.write(numpy.array([vertexTotal], numpy.int32).tostring())
117 for obj in scene.objects():
118 if scene.checkPlatform(obj):
119 for mesh in obj._meshList:
120 vertexes = (numpy.matrix(mesh.vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
121 vertexes -= obj._drawOffset
122 vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
123 f.write(vertexes.tostring())
129 obj = scene.objects()[n]
130 for mesh in obj._meshList:
131 f.write(numpy.array([mesh.vertexCount], numpy.int32).tostring())
132 f.write(mesh.vertexes.tostring())
133 pos = obj.getPosition() * 1000
134 pos += numpy.array([profile.getPreferenceFloat('machine_width') * 1000 / 2, profile.getPreferenceFloat('machine_depth') * 1000 / 2])
135 commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
136 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
137 commandList += ['#' * len(obj._meshList)]
139 if self._objCount > 0:
141 self._process = self._runSliceProcess(commandList)
142 self._thread = threading.Thread(target=self._watchProcess)
143 self._thread.daemon = True
146 traceback.print_exc()
148 def _watchProcess(self):
149 self._callback(0.0, False)
151 self._printTimeSeconds = None
152 self._filamentMM = None
154 line = self._process.stdout.readline()
158 if line.startswith('Progress:'):
159 line = line.split(':')
160 if line[1] == 'process':
162 elif line[1] in self._progressSteps:
163 progressValue = float(line[2]) / float(line[3])
164 progressValue /= len(self._progressSteps)
165 progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
167 progressValue /= self._objCount
168 progressValue += 1.0 / self._objCount * objectNr
170 self._callback(progressValue, False)
173 elif line.startswith('Print time:'):
174 self._printTimeSeconds = int(line.split(':')[1].strip())
175 elif line.startswith('Filament:'):
176 self._filamentMM = int(line.split(':')[1].strip())
178 self._sliceLog.append(line.strip())
179 line = self._process.stdout.readline()
180 for line in self._process.stderr:
181 self._sliceLog.append(line.strip())
182 returnCode = self._process.wait()
185 profile.runPostProcessingPlugins(self._exportFilename)
186 self._callback(1.0, True)
188 self._callback(-1.0, False)
193 def _engineSettings(self):
195 'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
196 'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000) if profile.getProfileSettingFloat('bottom_thickness') > 0.0 else int(profile.getProfileSettingFloat('layer_height') * 1000),
197 'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
198 'filamentFlow': int(profile.getProfileSettingFloat('filament_flow')),
199 'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
200 'insetCount': int(profile.calculateLineCount()),
201 'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
202 'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
203 'sparseInfillLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density')) if profile.getProfileSettingFloat('fill_density') > 0 else -1,
204 'infillOverlap': int(profile.getProfileSettingFloat('fill_overlap')),
205 'initialSpeedupLayers': int(4),
206 'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
207 'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
208 'infillSpeed': int(profile.getProfileSettingFloat('infill_speed')) if int(profile.getProfileSettingFloat('infill_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
209 'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
210 'fanOnLayerNr': int(profile.getProfileSettingFloat('fan_layer')),
211 'fanSpeedMin': int(profile.getProfileSettingFloat('fan_speed')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
212 'fanSpeedMax': int(profile.getProfileSettingFloat('fan_speed_max')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
213 'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(60),
214 'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
215 'supportLineWidth': int(profile.getProfileSettingFloat('support_rate') * profile.calculateEdgeWidth() * 1000 / 100),
216 'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000),
217 'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
218 'objectSink': int(profile.getProfileSettingFloat('object_sink') * 1000),
219 'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
220 'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
221 'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
222 'startCode': profile.getAlterationFileContents('start.gcode'),
223 'endCode': profile.getAlterationFileContents('end.gcode'),
225 'extruderOffset[1].X': int(profile.getPreferenceFloat('extruder_offset_x1') * 1000),
226 'extruderOffset[1].Y': int(profile.getPreferenceFloat('extruder_offset_y1') * 1000),
227 'extruderOffset[2].X': int(profile.getPreferenceFloat('extruder_offset_x2') * 1000),
228 'extruderOffset[2].Y': int(profile.getPreferenceFloat('extruder_offset_y2') * 1000),
229 'extruderOffset[3].X': int(profile.getPreferenceFloat('extruder_offset_x3') * 1000),
230 'extruderOffset[3].Y': int(profile.getPreferenceFloat('extruder_offset_y3') * 1000),
232 if profile.getProfileSetting('platform_adhesion') == 'Brim':
233 settings['skirtDistance'] = 0
234 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('brim_line_count'))
235 elif profile.getProfileSetting('platform_adhesion') == 'Raft':
236 settings['skirtDistance'] = 0
237 settings['skirtLineCount'] = 0
238 settings['raftMargin'] = int(profile.getProfileSettingFloat('raft_margin') * 1000);
239 settings['raftBaseThickness'] = int(profile.getProfileSettingFloat('raft_base_thickness') * 1000);
240 settings['raftBaseLinewidth'] = int(profile.getProfileSettingFloat('raft_base_linewidth') * 1000);
241 settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000);
242 settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000);
244 settings['skirtDistance'] = int(profile.getProfileSettingFloat('skirt_gap') * 1000)
245 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('skirt_line_count'))
246 if settings['layerThickness'] <= 0:
247 settings['layerThickness'] = 1000
250 def _runSliceProcess(self, cmdList):
252 if subprocess.mswindows:
253 su = subprocess.STARTUPINFO()
254 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
255 su.wShowWindow = subprocess.SW_HIDE
256 kwargs['startupinfo'] = su
257 kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
258 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)