svg2gcode/svg2gcode.py

changeset 5
b41cdab37aab
parent 4
234ad2069fdd
child 6
ff679c15cb0e
equal deleted inserted replaced
4:234ad2069fdd 5:b41cdab37aab
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
2 import svg, sys 4 import svg, sys
3 from gcode import Gcode 5 from gcode import Gcode
4 from optparse import OptionParser 6 from optparse import OptionParser
7
8 from shapely.geometry import box, MultiLineString, Point, Polygon
9 from shapely.affinity import rotate
10 from shapely import speedups
11 from math import sqrt
12
13 # enable Shapely speedups, if possible
14 if speedups.available:
15 speedups.enable()
16
17 def hatchbox(rect, angle, spacing):
18 """
19 returns a Shapely geometry (MULTILINESTRING, or more rarely,
20 GEOMETRYCOLLECTION) for a simple hatched rectangle.
21
22 args:
23 rect - a Shapely geometry for the outer boundary of the hatch
24 Likely most useful if it really is a rectangle
25
26 angle - angle of hatch lines, conventional anticlockwise -ve
27
28 spacing - spacing between hatch lines
29
30 GEOMETRYCOLLECTION case occurs when a hatch line intersects with
31 the corner of the clipping rectangle, which produces a point
32 along with the usual lines.
33 """
34
35 (llx, lly, urx, ury) = rect.bounds
36 centre_x = (urx + llx) / 2
37 centre_y = (ury + lly) / 2
38 diagonal_length = sqrt((urx - llx) ** 2 + (ury - lly) ** 2)
39 number_of_lines = 2 + int(diagonal_length / spacing)
40 hatch_length = spacing * (number_of_lines - 1)
41
42 # build a square (of side hatch_length) horizontal lines
43 # centred on centroid of the bounding box, 'spacing' units apart
44 coords = []
45 for i in range(number_of_lines):
46 # alternate lines l2r and r2l to keep HP-7470A plotter happy ☺
47 if i % 2:
48 coords.extend([((centre_x - hatch_length / 2, centre_y
49 - hatch_length / 2 + i * spacing), (centre_x
50 + hatch_length / 2, centre_y - hatch_length
51 / 2 + i * spacing))])
52 else:
53 coords.extend([((centre_x + hatch_length / 2, centre_y
54 - hatch_length / 2 + i * spacing), (centre_x
55 - hatch_length / 2, centre_y - hatch_length
56 / 2 + i * spacing))])
57 # turn array into Shapely object
58 lines = MultiLineString(coords)
59 # Rotate by angle around box centre
60 lines = rotate(lines, angle, origin='centroid', use_radians=False)
61 # return clipped array
62 return rect.intersection(lines)
63
64 def infill(bbox, polygon, angle=0, spacing=10):
65 b1, b2 = bbox
66 x1, y1 = b1.coord()
67 x2, y2 = b2.coord()
68 page = box(x1, y1, x2, y2)
69 hatching = hatchbox(page, angle, spacing)
70 # create shape from polygon:
71 segments = []
72 for pnt in polygon:
73 x, y = pnt.coord()
74 segments.append((x, y))
75
76 shape = Polygon(segments)
77 return shape.intersection(hatching)
5 78
6 parser = OptionParser() 79 parser = OptionParser()
7 parser.add_option("-f", "--file", dest="filename", default=None, 80 parser.add_option("-f", "--file", dest="filename", default=None,
8 help="Load SVG file", metavar="FILE") 81 help="Load SVG file", metavar="FILE")
9 parser.add_option("-s", "--scale", 82 parser.add_option("-s", "--scale",
13 dest="engrave_speed", type="float", default=20, 86 dest="engrave_speed", type="float", default=20,
14 help="engrave speed mm/sec (default 20)") 87 help="engrave speed mm/sec (default 20)")
15 parser.add_option("-t", "", 88 parser.add_option("-t", "",
16 dest="travel_speed", type="float", default=130, 89 dest="travel_speed", type="float", default=130,
17 help="travel speed mm/sec (default 130)") 90 help="travel speed mm/sec (default 130)")
91 parser.add_option("-o", "--outline", action="store_true",
92 dest="outline", default=False,
93 help="no infill, only outlines")
18 94
19 95
20 (options, args) = parser.parse_args() 96 (options, args) = parser.parse_args()
21 97
22 98
43 119
44 data = im.flatten() 120 data = im.flatten()
45 for d in data: 121 for d in data:
46 if hasattr(d, "segments"): 122 if hasattr(d, "segments"):
47 for l in d.segments(1): 123 for l in d.segments(1):
124 # THE OUTLINE
48 x, y = normalize(l[0].coord()) 125 x, y = normalize(l[0].coord())
49 gcode.move(x, y) 126 gcode.move(x, y)
50 for pt in l[1:]: 127 for pt in l[1:]:
51 x, y = normalize(pt.coord()) 128 x, y = normalize(pt.coord())
52 gcode.engrave(x, y) 129 gcode.engrave(x, y)
53 130
131 if not options.outline:
132 # THE INFILL
133 for line in infill(im.bbox(), l, spacing=2):
134 start = normalize((line.coords[0][0], line.coords[0][1]))
135 end = normalize((line.coords[1][0], line.coords[1][1]))
136 gcode.move(start[0], start[1])
137 gcode.engrave(end[0], end[1])
138
54 # write gcode file 139 # write gcode file
55 gcode.write(options.filename + ".g") 140 gcode.write(options.filename + ".g")

mercurial