chiark / gitweb /
Make the expert settings dialog modal so we can slice again after it closes.
[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
38         def cleanup(self):
39                 self.abortSlicer()
40                 try:
41                         os.remove(self._binaryStorageFilename)
42                 except:
43                         pass
44                 try:
45                         os.remove(self._exportFilename)
46                 except:
47                         pass
48
49         def abortSlicer(self):
50                 if self._process is not None:
51                         try:
52                                 self._process.terminate()
53                         except:
54                                 pass
55                         self._thread.join()
56
57         def getGCodeFilename(self):
58                 return self._exportFilename
59
60         def runSlicer(self, scene):
61                 self.abortSlicer()
62                 self._callback(0.0, False)
63
64                 commandList = [getEngineFilename(), '-vv']
65                 for k, v in self._engineSettings().iteritems():
66                         commandList += ['-s', '%s=%s' % (k, str(v))]
67                 commandList += ['-o', self._exportFilename]
68                 commandList += ['-b', self._binaryStorageFilename]
69                 self._objCount = 0
70                 with open(self._binaryStorageFilename, "wb") as f:
71                         order = scene.printOrder()
72                         if order is None:
73                                 pos = numpy.array([profile.getPreferenceFloat('machine_width') * 1000 / 2, profile.getPreferenceFloat('machine_depth') * 1000 / 2])
74                                 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
75
76                                 vertexTotal = 0
77                                 for obj in scene.objects():
78                                         if scene.checkPlatform(obj):
79                                                 for mesh in obj._meshList:
80                                                         vertexTotal += mesh.vertexCount
81
82                                 f.write(numpy.array([vertexTotal], numpy.int32).tostring())
83                                 for obj in scene.objects():
84                                         if scene.checkPlatform(obj):
85                                                 for mesh in obj._meshList:
86                                                         vertexes = (numpy.matrix(mesh.vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
87                                                         vertexes -= obj._drawOffset
88                                                         vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
89                                                         f.write(vertexes.tostring())
90
91                                 commandList += ['#']
92                                 self._objCount = 1
93                         else:
94                                 for n in order:
95                                         obj = scene.objects()[n]
96                                         for mesh in obj._meshList:
97                                                 f.write(numpy.array([mesh.vertexCount], numpy.int32).tostring())
98                                                 f.write(mesh.vertexes.tostring())
99                                         pos = obj.getPosition() * 1000
100                                         pos += numpy.array([profile.getPreferenceFloat('machine_width') * 1000 / 2, profile.getPreferenceFloat('machine_depth') * 1000 / 2])
101                                         commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
102                                         commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
103                                         commandList += ['#' * len(obj._meshList)]
104                                         self._objCount += 1
105                 if self._objCount > 0:
106                         print ' '.join(commandList)
107                         try:
108                                 self._process = self._runSliceProcess(commandList)
109                                 self._thread = threading.Thread(target=self._watchProcess)
110                                 self._thread.daemon = True
111                                 self._thread.start()
112                         except OSError:
113                                 traceback.print_exc()
114
115         def _watchProcess(self):
116                 self._callback(0.0, False)
117                 line = self._process.stdout.readline()
118                 objectNr = 0
119                 while len(line):
120                         line = line.strip()
121                         if line.startswith('Progress:'):
122                                 line = line.split(':')
123                                 if line[1] == 'process':
124                                         objectNr += 1
125                                 elif line[1] in self._progressSteps:
126                                         progressValue = float(line[2]) / float(line[3])
127                                         progressValue /= len(self._progressSteps)
128                                         progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
129
130                                         progressValue /= self._objCount
131                                         progressValue += 1.0 / self._objCount * objectNr
132                                         try:
133                                                 self._callback(progressValue, False)
134                                         except:
135                                                 pass
136                         else:
137                                 print '#', line.strip()
138                                 pass
139                         line = self._process.stdout.readline()
140                 for line in self._process.stderr:
141                         print line.strip()
142                 returnCode = self._process.wait()
143                 print returnCode
144                 try:
145                         if returnCode == 0:
146                                 self._callback(1.0, True)
147                         else:
148                                 self._callback(0.0, False)
149                 except:
150                         pass
151                 self._process = None
152
153         def _engineSettings(self):
154                 return {
155                         'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
156                         'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000),
157                         'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
158                         'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
159                         'insetCount': int(profile.calculateLineCount()),
160                         'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
161                         'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
162                         'sparseInfillLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density')) if profile.getProfileSettingFloat('fill_density') > 0 else 9999999999,
163                         'skirtDistance': int(profile.getProfileSettingFloat('skirt_gap') * 1000),
164                         'skirtLineCount': int(profile.getProfileSettingFloat('skirt_line_count')),
165                         'initialSpeedupLayers': int(4),
166                         'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
167                         'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
168                         'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
169                         'fanOnLayerNr': int(profile.getProfileSettingFloat('fan_layer')),
170                         'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(60),
171                         'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
172                         'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000),
173                         'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
174                         'objectSink': int(profile.getProfileSettingFloat('object_sink') * 1000),
175                         'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
176                         'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
177                         'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0
178                 }
179
180         def _runSliceProcess(self, cmdList):
181                 kwargs = {}
182                 if subprocess.mswindows:
183                         su = subprocess.STARTUPINFO()
184                         su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
185                         su.wShowWindow = subprocess.SW_HIDE
186                         kwargs['startupinfo'] = su
187                         kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
188                 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)