This repository has been archived by the owner on Dec 4, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
svg2gcode.py
executable file
·226 lines (165 loc) · 5.24 KB
/
svg2gcode.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
#!/usr/bin/env python3
# pylint: disable=logging-too-many-args,too-many-return-statements
import os
import sys
import math
import shutil
import logging
import argparse
from collections import defaultdict
from tempfile import NamedTemporaryFile
from common import color_log
from geo import svg2paths
DEFAULT_Z_SAFETY = 2.5
LOG = logging.getLogger("svg2gcode")
def paths_to_gcode(out, paths, offset):
"""
Write paths in G-code format.
out: Stream object to write to.
"""
out.write("""
M92 X160 Y160 Z800 E300 ; Set steps-per-unit
M201 X500 Y500 Z50 E10000 ; Set max print acceleration
M203 X300 Y300 Z5 E25 ; Set max feedrate
M204 P3500 R3500 T3500 ; Set default acceleration (Print, Retract, Travel)
M205 X15 Y15 ; Set jerk
M140 S0 ; Set bed temperature, don't wait
M104 S0 ; Set extruder temperature and wait
M107 ; disable fan
G21 ; set units to millimeters
G90 ; use absolute coordinates
M82 ; use absolute distances for extrusion
G28 ; Home all
M420 S1 Z5 V ; Activate bed levelling with 5mm fade and print table
""")
z_plot = offset["z"]
z_move = z_plot + offset["z-safety"]
out.write("G1 Z%0.3f F4000\n" % z_move)
out.write("G1 X%0.3f Y%0.3f F4000\n" % (offset["x"], offset["y"]))
for path in paths:
out.write("G1 X%0.3f Y%0.3f F4000\n" % (
offset["x"] + path[0][0], offset["y"] + path[0][1]))
out.write("G1 Z%0.3f F4000\n" % z_plot)
for vert in path[1:]:
out.write("G0 X%0.3f Y%0.3f F4000\n" % (
offset["x"] + vert[0], offset["y"] + vert[1]))
out.write("G1 Z%0.3f F4000\n" % z_move)
def dist2d(a, b):
return math.sqrt(pow(b[0] - a[0], 2) + pow(b[1] - a[1], 2))
def order_paths(path_list, allow_reverse=False):
index = defaultdict(list)
for i, path in enumerate(path_list):
if not path:
continue
start = tuple(path[0])
index[start].append({
"i": i,
"reverse": False
})
if allow_reverse:
end = tuple(path[-1])
index[end].append({
"i": i,
"reverse": True
})
out_list = []
cursor = (0, 0)
def add_item(start):
nonlocal index
nonlocal cursor
nonlocal out_list
item = index[start].pop(0)
if not index[start]:
del index[start]
i = item["i"]
path = path_list[i]
if allow_reverse:
if item["reverse"]:
path = path[::-1]
end = tuple(path[-1])
index[end] = [v for v in index[end] if v["i"] != i]
if not index[end]:
del index[end]
out_list.append(path)
cursor = path[-1]
while index:
lo_point = None
lo_dist = None
for point in index.keys():
dist = dist2d(cursor, point)
if lo_dist is None or dist < lo_dist:
lo_dist = dist
lo_point = point
add_item(lo_point)
return out_list
def svg2gcode(out, svg_file, offset):
"""
Use millimeters for output unit.
"""
paths = svg2paths(svg_file)
paths = order_paths(paths)
paths_to_gcode(out, paths, offset)
def main():
log_geo = logging.getLogger("geo")
for log in LOG, log_geo:
log.addHandler(logging.StreamHandler())
color_log(log)
parser = argparse.ArgumentParser(
description="Convert paths in an SVG file to "
"G-code format for plotting.")
parser.add_argument(
"--verbose", "-v",
action="count", default=0,
help="Print verbose information for debugging.")
parser.add_argument(
"--quiet", "-q",
action="count", default=0,
help="Suppress warnings.")
parser.add_argument(
"--x-offset", "-x",
action="store", default=0, type=float,
help="X offset.")
parser.add_argument(
"--y-offset", "-y",
action="store", default=0, type=float,
help="X offset.")
parser.add_argument(
"--z-offset", "-z",
action="store", default=0, type=float,
help="X offset.")
parser.add_argument(
"--z-safety", "-Z",
action="store", default=DEFAULT_Z_SAFETY, type=float,
help="X offset.")
parser.add_argument(
"svg",
metavar="SVG",
help="Path to SVG file.")
parser.add_argument(
"gcode",
metavar="GCODE",
nargs="?",
help="Path to G-code file.")
args = parser.parse_args()
level = (logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG)[
max(0, min(3, 1 + args.verbose - args.quiet))]
for log in LOG, log_geo:
log.setLevel(level)
offset = {
"x": args.x_offset,
"y": args.y_offset,
"z": args.z_offset,
"z-safety": args.z_safety,
}
if args.gcode:
out = NamedTemporaryFile("w", encoding="utf=8", delete=False)
os.fchmod(out.fileno(), os.stat(args.svg).st_mode)
else:
out = sys.stdout
with open(args.svg, "r", encoding="utf-8") as svg:
svg2gcode(out, svg, offset)
if args.gcode:
out.close()
shutil.move(out.name, args.gcode)
if __name__ == "__main__":
main()