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