chiark / gitweb /
Add slice log window.
[cura.git] / Cura / util / sliceEngine.py
1 import subprocess
2 import time
3 import numpy
4 import os
5 import warnings
6 import threading
7 import traceback
8 import platform
9 import sys
10
11 from Cura.util import profile
12
13 def getEngineFilename():
14         if platform.system() == 'Windows':
15                 if os.path.exists('C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'):
16                         return 'C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'
17                 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'SteamEngine.exe'))
18         if hasattr(sys, 'frozen'):
19                 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../..', 'SteamEngine'))
20         return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'SteamEngine'))
21
22 def getTempFilename():
23         warnings.simplefilter('ignore')
24         ret = os.tempnam(None, "Cura_Tmp")
25         warnings.simplefilter('default')
26         return ret
27
28 class Slicer(object):
29         def __init__(self, progressCallback):
30                 self._process = None
31                 self._thread = None
32                 self._callback = progressCallback
33                 self._binaryStorageFilename = getTempFilename()
34                 self._exportFilename = getTempFilename()
35                 self._progressSteps = ['inset', 'skin', 'export']
36                 self._objCount = 0
37                 self._sliceLog = []
38
39         def cleanup(self):
40                 self.abortSlicer()
41                 try:
42                         os.remove(self._binaryStorageFilename)
43                 except:
44                         pass
45                 try:
46                         os.remove(self._exportFilename)
47                 except:
48                         pass
49
50         def abortSlicer(self):
51                 if self._process is not None:
52                         try:
53                                 self._process.terminate()
54                         except:
55                                 pass
56                         self._thread.join()
57
58         def getGCodeFilename(self):
59                 return self._exportFilename
60
61         def getSliceLog(self):
62                 return self._sliceLog
63
64         def runSlicer(self, scene):
65                 self.abortSlicer()
66                 self._callback(0.0, False)
67
68                 commandList = [getEngineFilename(), '-vv']
69                 for k, v in self._engineSettings().iteritems():
70                         commandList += ['-s', '%s=%s' % (k, str(v))]
71                 commandList += ['-o', self._exportFilename]
72                 commandList += ['-b', self._binaryStorageFilename]
73                 self._objCount = 0
74                 with open(self._binaryStorageFilename, "wb") as f:
75                         order = scene.printOrder()
76                         if order is None:
77                                 pos = numpy.array([profile.getPreferenceFloat('machine_width') * 1000 / 2, profile.getPreferenceFloat('machine_depth') * 1000 / 2])
78                                 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
79
80                                 vertexTotal = 0
81                                 for obj in scene.objects():
82                                         if scene.checkPlatform(obj):
83                                                 for mesh in obj._meshList:
84                                                         vertexTotal += mesh.vertexCount
85
86                                 f.write(numpy.array([vertexTotal], numpy.int32).tostring())
87                                 for obj in scene.objects():
88                                         if scene.checkPlatform(obj):
89                                                 for mesh in obj._meshList:
90                                                         vertexes = (numpy.matrix(mesh.vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
91                                                         vertexes -= obj._drawOffset
92                                                         vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
93                                                         f.write(vertexes.tostring())
94
95                                 commandList += ['#']
96                                 self._objCount = 1
97                         else:
98                                 for n in order:
99                                         obj = scene.objects()[n]
100                                         for mesh in obj._meshList:
101                                                 f.write(numpy.array([mesh.vertexCount], numpy.int32).tostring())
102                                                 f.write(mesh.vertexes.tostring())
103                                         pos = obj.getPosition() * 1000
104                                         pos += numpy.array([profile.getPreferenceFloat('machine_width') * 1000 / 2, profile.getPreferenceFloat('machine_depth') * 1000 / 2])
105                                         commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
106                                         commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
107                                         commandList += ['#' * len(obj._meshList)]
108                                         self._objCount += 1
109                 if self._objCount > 0:
110                         try:
111                                 self._process = self._runSliceProcess(commandList)
112                                 self._thread = threading.Thread(target=self._watchProcess)
113                                 self._thread.daemon = True
114                                 self._thread.start()
115                         except OSError:
116                                 traceback.print_exc()
117
118         def _watchProcess(self):
119                 self._callback(0.0, False)
120                 self._sliceLog = []
121                 line = self._process.stdout.readline()
122                 objectNr = 0
123                 while len(line):
124                         line = line.strip()
125                         if line.startswith('Progress:'):
126                                 line = line.split(':')
127                                 if line[1] == 'process':
128                                         objectNr += 1
129                                 elif line[1] in self._progressSteps:
130                                         progressValue = float(line[2]) / float(line[3])
131                                         progressValue /= len(self._progressSteps)
132                                         progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
133
134                                         progressValue /= self._objCount
135                                         progressValue += 1.0 / self._objCount * objectNr
136                                         try:
137                                                 self._callback(progressValue, False)
138                                         except:
139                                                 pass
140                         else:
141                                 self._sliceLog.append(line.strip())
142                         line = self._process.stdout.readline()
143                 for line in self._process.stderr:
144                         self._sliceLog.append(line.strip())
145                 returnCode = self._process.wait()
146                 try:
147                         if returnCode == 0:
148                                 self._callback(1.0, True)
149                         else:
150                                 self._callback(0.0, False)
151                 except:
152                         pass
153                 self._process = None
154
155         def _engineSettings(self):
156                 return {
157                         'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
158                         'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000),
159                         'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
160                         'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
161                         'insetCount': int(profile.calculateLineCount()),
162                         'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
163                         'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
164                         'sparseInfillLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density')) if profile.getProfileSettingFloat('fill_density') > 0 else 9999999999,
165                         'skirtDistance': int(profile.getProfileSettingFloat('skirt_gap') * 1000),
166                         'skirtLineCount': int(profile.getProfileSettingFloat('skirt_line_count')),
167                         'initialSpeedupLayers': int(4),
168                         'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
169                         'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
170                         'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
171                         'fanOnLayerNr': int(profile.getProfileSettingFloat('fan_layer')),
172                         'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(60),
173                         'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
174                         'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000),
175                         'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
176                         'objectSink': int(profile.getProfileSettingFloat('object_sink') * 1000),
177                         'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
178                         'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
179                         'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
180                         'startCode': profile.getAlterationFileContents('start.gcode'),
181                         'endCode': profile.getAlterationFileContents('end.gcode'),
182                 }
183
184         def _runSliceProcess(self, cmdList):
185                 kwargs = {}
186                 if subprocess.mswindows:
187                         su = subprocess.STARTUPINFO()
188                         su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
189                         su.wShowWindow = subprocess.SW_HIDE
190                         kwargs['startupinfo'] = su
191                         kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
192                 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)