svg2gcode/svg2gcode.py

Sat, 07 Nov 2015 16:57:15 +0100

author
mbayer
date
Sat, 07 Nov 2015 16:57:15 +0100
changeset 6
ff679c15cb0e
parent 5
b41cdab37aab
child 7
421aae29d9d9
permissions
-rwxr-xr-x

infill only on Polygon or Path which have fill style attribute

4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
1 #!/usr/bin/env python
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
2 # -*- coding: utf-8 -*-
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
3
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
4 import svg, sys
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
5 from gcode import Gcode
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
6 from optparse import OptionParser
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
7 from tinycss import CSS21Parser
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
8
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
9 from shapely.geometry import box, MultiLineString, Point, Polygon
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
10 from shapely.affinity import rotate
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
11 from shapely import speedups
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
12 from math import sqrt
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
13
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
14 # enable Shapely speedups, if possible
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
15 if speedups.available:
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
16 speedups.enable()
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
17
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
18 def hatchbox(rect, angle, spacing):
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
19 """
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
20 returns a Shapely geometry (MULTILINESTRING, or more rarely,
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
21 GEOMETRYCOLLECTION) for a simple hatched rectangle.
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
22
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
23 args:
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
24 rect - a Shapely geometry for the outer boundary of the hatch
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
25 Likely most useful if it really is a rectangle
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
26
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
27 angle - angle of hatch lines, conventional anticlockwise -ve
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
28
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
29 spacing - spacing between hatch lines
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
30
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
31 GEOMETRYCOLLECTION case occurs when a hatch line intersects with
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
32 the corner of the clipping rectangle, which produces a point
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
33 along with the usual lines.
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
34 """
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
35
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
36 (llx, lly, urx, ury) = rect.bounds
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
37 centre_x = (urx + llx) / 2
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
38 centre_y = (ury + lly) / 2
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
39 diagonal_length = sqrt((urx - llx) ** 2 + (ury - lly) ** 2)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
40 number_of_lines = 2 + int(diagonal_length / spacing)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
41 hatch_length = spacing * (number_of_lines - 1)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
42
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
43 # build a square (of side hatch_length) horizontal lines
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
44 # centred on centroid of the bounding box, 'spacing' units apart
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
45 coords = []
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
46 for i in range(number_of_lines):
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
47 # alternate lines l2r and r2l to keep HP-7470A plotter happy ☺
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
48 if i % 2:
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
49 coords.extend([((centre_x - hatch_length / 2, centre_y
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
50 - hatch_length / 2 + i * spacing), (centre_x
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
51 + hatch_length / 2, centre_y - hatch_length
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
52 / 2 + i * spacing))])
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
53 else:
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
54 coords.extend([((centre_x + hatch_length / 2, centre_y
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
55 - hatch_length / 2 + i * spacing), (centre_x
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
56 - hatch_length / 2, centre_y - hatch_length
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
57 / 2 + i * spacing))])
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
58 # turn array into Shapely object
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
59 lines = MultiLineString(coords)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
60 # Rotate by angle around box centre
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
61 lines = rotate(lines, angle, origin='centroid', use_radians=False)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
62 # return clipped array
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
63 return rect.intersection(lines)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
64
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
65 def get_infill(polygon):
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
66 # create shape from polygon:
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
67 segments = []
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
68 for pnt in polygon:
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
69 x, y = pnt.coord()
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
70 segments.append((x, y))
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
71
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
72 shape = Polygon(segments)
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
73 if shape.is_valid:
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
74 return shape.intersection(INFILL)
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
75 else:
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
76 return []
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
77
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
78 parser = OptionParser()
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
79 parser.add_option("-f", "--file", dest="filename", default=None,
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
80 help="Load SVG file", metavar="FILE")
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
81 parser.add_option("-s", "--scale",
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
82 dest="scale", type="float", default=1.0,
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
83 help="set scale factor (default 1.0)")
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
84 parser.add_option("-e", "",
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
85 dest="engrave_speed", type="float", default=20,
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
86 help="engrave speed mm/sec (default 20)")
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
87 parser.add_option("-t", "",
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
88 dest="travel_speed", type="float", default=130,
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
89 help="travel speed mm/sec (default 130)")
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
90 parser.add_option("-o", "--outline", action="store_true",
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
91 dest="outline", default=False,
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
92 help="no infill, only outlines")
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
93
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
94
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
95 (options, args) = parser.parse_args()
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
96
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
97
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
98 if not options.filename:
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
99 print "no filename given!"
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
100 sys.exit(1)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
101
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
102 gcode = Gcode(scale=options.scale, travel_speed=options.travel_speed, engrave_speed=options.engrave_speed)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
103
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
104 im = svg.parse(options.filename)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
105 b1, b2 = im.bbox()
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
106 width, height = b2.coord()
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
107 print "Original dimension: %.2f x %.2f" % (width, height)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
108 width *= gcode.mm_pixel * options.scale
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
109 height *= gcode.mm_pixel * options.scale
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
110 print "Print dimension: %.2fmm x %.2fmm" % (width, height)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
111
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
112 x1, y1 = b1.coord()
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
113 x2, y2 = b2.coord()
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
114 page = box(x1, y1, x2, y2)
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
115 INFILL = hatchbox(page, 0, 2)
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
116
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
117 def parse_style(stylestr):
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
118 if stylestr.strip() == '':
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
119 return None
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
120 parser = CSS21Parser()
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
121 style = parser.parse_style_attr(stylestr)
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
122 kv = {}
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
123 for obj in style[0]:
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
124 kv[obj.name] = obj.value[0].value
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
125 return kv
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
126
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
127 def normalize(coord):
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
128 x = coord[0]
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
129 y = coord[1]
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
130 # flip y
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
131 y = (b2.coord()[1] - y)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
132 return (x, y)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
133
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
134 data = im.flatten()
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
135 for d in data:
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
136 if hasattr(d, "segments"):
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
137 for l in d.segments(1):
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
138 # THE OUTLINE
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
139 x, y = normalize(l[0].coord())
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
140 gcode.move(x, y)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
141 for pt in l[1:]:
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
142 x, y = normalize(pt.coord())
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
143 gcode.engrave(x, y)
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
144
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
145 if options.outline:
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
146 continue
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
147
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
148 if (isinstance(d, svg.Polygon) or isinstance(d, svg.Path)):
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
149 #check if we shoild infill?
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
150 style = parse_style(d.style)
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
151 if not style:
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
152 continue
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
153 if not 'fill' in style.keys():
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
154 continue
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
155 if style['fill'] == 'none':
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
156 continue
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
157
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
158 # THE INFILL
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
159 for line in get_infill(l):
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
160 start = normalize((line.coords[0][0], line.coords[0][1]))
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
161 end = normalize((line.coords[1][0], line.coords[1][1]))
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
162 gcode.move(start[0], start[1])
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
163 gcode.engrave(end[0], end[1])
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
164
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
165 # write gcode file
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
166 gcode.write(options.filename + ".g")

mercurial