forked from spegelius/filaswitch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
slicer_simplify3d.py
251 lines (218 loc) · 9.46 KB
/
slicer_simplify3d.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
import logging
import re
from extruder import Extruder
from switch_tower import PEEK
from gcode import GCode
import utils
from gcode_file import SLICER_SIMPLIFY3D, GCodeFile
#from flavor_makerbot import MakerBotFlavor
gcode = GCode()
log = logging.getLogger("S3DSlicer")
class Simplify3dGCodeFile(GCodeFile):
slicer_type = SLICER_SIMPLIFY3D
LAYER_START_RE = re.compile(b".*layer (\d+), Z = (\d+\.*\d*)")
VERSION_RE = re.compile(b".*Version (\d)\.(\d)\.(\d)")
def __init__(self, logger, hw_config):
super().__init__(logger, hw_config)
self.extruder_diameter = []
self.extruder_use_retract = []
self.extruder_retract_dist = []
self.extruder_retract_speed = []
self.extruder_zhop = []
self.relative_e = False
self.retract_while_wiping = False
self.version = None
self.default_speed = None
def process(self, gcode_file):
super().process(gcode_file)
self.fix_retract_during_wipe()
return self.save_new_file()
def get_extruders(self):
"""
Populate extruder list
:return:
"""
for i in range(len(self.extruder_diameter)):
self.extruders[i] = Extruder(i, self.extruder_diameter[i],
self.extruder_retract_dist[i],
self.extruder_retract_speed[i],
self.extruder_zhop[i])
def parse_header(self):
"""
Parse S3D header for print settings
:return: none
"""
for cmd, comment in self.layers[0].lines:
if not comment:
pass
elif b"Simplify3D(R)" in comment:
# parse version
try:
m = self.VERSION_RE.match(comment)
self.version = (int(m.groups()[0]), int(m.groups()[1]), int(m.groups()[2]))
except Exception as e:
print(e)
elif b"printMaterial" in comment:
self.material = comment.split(b",")[-1]
elif b"extruderDiameter" in comment:
for d in comment.split(b",")[1:]:
self.extruder_diameter.append(float(d))
elif b"extruderUseRetract" in comment:
for d in comment.split(b",")[1:]:
self.extruder_use_retract.append(d == b"1")
elif b"extruderRetractionDistance" in comment:
for d in comment.split(b",")[1:]:
self.extruder_retract_dist.append(float(d))
elif b"extruderRetractionZLift" in comment:
for d in comment.split(b",")[1:]:
self.extruder_zhop.append(float(d))
elif b"layerHeight" in comment:
self.layer_height = float(comment.split(b",")[1])
elif b"extruderRetractionSpeed" in comment:
for d in comment.split(b",")[1:]:
self.extruder_retract_speed.append(float(d))
elif b"relativeEdistances" in comment:
self.relative_e = comment.split(b",")[-1] == b"1"
elif b"retractWhileWiping" in comment:
self.retract_while_wiping = comment.split(b",")[-1] == b"1"
elif b"defaultSpeed" in comment:
self.default_speed = int(comment.split(b",")[-1])
elif b"rapidXYspeed" in comment:
self.travel_xy_speed = int(comment.split(b",")[-1])
elif b"rapidZspeed" in comment:
self.travel_z_speed = int(comment.split(b",")[-1])
if not self.relative_e:
raise ValueError("Relative E distances not enabled! Filaswitch won't work without relative E distances")
if not self.version:
self.log.warning("Could not detect Simplify3D version. Use at your own risk")
else:
self.log.info("Simplify3D version %d.%d.%d" % self.version)
def parse_print_settings(self):
""" S3D specific settings """
super().parse_print_settings()
for cmd, comment, line_index in self.layers[0].read_lines():
# find first tool change and remove it if it's T0. No need to
# do tool change as e already have T0 active
if cmd and gcode.is_tool_change(cmd) == 0:
self.layers[0].delete_line(line_index)
break
def check_layer_change(self, line, current_layer):
"""
Check if line is layer change
:param line: g-code line
:param current_layer: current layer data
:return: None or tuple of layer nr and layer z
"""
m = self.LAYER_START_RE.match(line)
if m:
return int(m.groups()[0]), float(m.groups()[1])
return current_layer
def find_perimeter_path(self):
""" Find perimeter path for determining feed rate"""
pass
# TODO: needed?
def filter_layers(self, last_switch_height):
"""
Filter layers so that only layers needed for purge tower processing
are returned.
Layers that are left out:
- empty (no command lines)
- non-tool
:param last_switch_height: z height of last switch layer
:return: tuple of layer, tower_needed, has_tool_change
"""
layers = []
z_positions = []
# step 1: filter out empty layers and add z_positions to array
for layer in self.layers:
if layer.z > last_switch_height:
break
if layer.is_empty_layer():
continue
if layer.z not in z_positions:
z_positions.append(layer.z)
layers.append(layer)
# step 2: go through the z position list and store layers with info if infill is needed for the layer
z_groups = []
for z in z_positions:
z_group = []
z_layers = []
tower_z_ok = False
for l in layers:
if l.z == z:
z_layers.append(l)
# first check tool change layers
for l in z_layers:
if l.has_tool_changes():
tower_z_ok= True
z_group.append((l, True, True))
# then check other layers, only after tower_z_ok is prepared
for l in z_layers:
if not l.has_tool_changes():
if not tower_z_ok:
tower_z_ok = True
z_group.append((l, True, False))
else:
z_group.append((l, False, False))
z_groups.append(z_group)
# step 3: pack groups to list
layers = []
for z_group in z_groups:
for l, needed, has_tool in z_group:
#print(l.z, needed, has_tool)
layers.append((l, needed, has_tool))
layers = sorted(layers, key=lambda x: x[0].num)
return layers
def fix_retract_during_wipe(self):
"""
Fix S3D 3.1.1 bug where option "Retract during wipe" causes over-extrusion
:return: none
"""
if not self.retract_while_wiping:
# not needed
return
if not self.version == (3,1,1):
self.log.warning("Not applying fix for 'Retract during wipe'; S3D version not 3.1.1")
return
self.log.info("Fixing S3D 3.1.1 bug with 'Retract during wipe'-feature")
for layer in self.layers:
wipe_on = False
wipe_indexes = []
wipe_speed = 0
wipe_length = 0.0
extruder = self.extruders[0]
for cmd, comment, index in layer.read_lines():
if not cmd:
continue
if gcode.is_extrusion_speed_move(cmd):
# detect retract/wipe
if gcode.last_match[2] < 0:
wipe_on = True
wipe_speed = gcode.last_match[3]
wipe_indexes.append((index, gcode.last_match[0], gcode.last_match[1], wipe_speed))
wipe_length += gcode.last_match[2]
elif gcode.is_extrusion_move(cmd) and wipe_on:
wipe_indexes.append((index, gcode.last_match[0], gcode.last_match[1], wipe_speed))
wipe_length += gcode.last_match[2]
elif gcode.is_head_move(cmd) and wipe_on:
# retract/wipe ended
wipe_on = False
elif gcode.is_extruder_move(cmd) and wipe_indexes:
# check if prime move
if gcode.last_match[0] > 0 and abs(extruder.retract - gcode.last_match[0]) < 0.001:
# replace retract/wipes with wipes and add full retract
for index, x, y, speed in wipe_indexes:
layer.replace_line(index, gcode.generate_head_move_gcode(x,y,speed), b"fixed wipe")
first_wipe = wipe_indexes[0][0]
layer.insert_line(first_wipe, *extruder.get_retract_gcode())
wipe_indexes = []
wipe_on = False
wipe_length = 0
elif gcode.is_tool_change(cmd) is not None:
# tool change, set active extruder
extruder = self.extruders[gcode.last_match]
if __name__ == "__main__":
import logger
logger = logger.Logger(".")
s = Simplify3dGCodeFile(logger, PEEK)
print(s.check_layer_change(b" layer 1, Z = 1", None))