svg2gcode/svg2gcode.py

Sun, 08 Nov 2015 05:21:58 +0100

author
mbayer
date
Sun, 08 Nov 2015 05:21:58 +0100
changeset 13
e2fd4d7b3cb6
parent 12
a90b8113be25
permissions
-rwxr-xr-x

infill controllable

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
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
4 import svg, sys, math
11
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
5 from pprint import pprint
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
6 from gcode import Gcode
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
7 from optparse import OptionParser
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
8 from tinycss import CSS21Parser
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
9
11
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
10
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
11 from shapely.geometry import box, MultiLineString, Polygon
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
12 from shapely.affinity import rotate
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
13 from shapely import speedups
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
14 from math import sqrt
11
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
15 from functools import cmp_to_key
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
16
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
17 # enable Shapely speedups, if possible
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
18 if speedups.available:
11
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
19 print "shapely speedups available and enabled!"
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
20 speedups.enable()
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
21
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
22 def hatchbox(rect, angle, spacing):
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
23 """
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
24 returns a Shapely geometry (MULTILINESTRING, or more rarely,
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
25 GEOMETRYCOLLECTION) for a simple hatched 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 args:
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
28 rect - a Shapely geometry for the outer boundary of the hatch
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
29 Likely most useful if it really is a rectangle
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 angle - angle of hatch lines, conventional anticlockwise -ve
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
32
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
33 spacing - spacing between hatch 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 GEOMETRYCOLLECTION case occurs when a hatch line intersects with
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
36 the corner of the clipping rectangle, which produces a point
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
37 along with the usual lines.
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
38 """
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
39
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
40 (llx, lly, urx, ury) = rect.bounds
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
41 centre_x = (urx + llx) / 2
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
42 centre_y = (ury + lly) / 2
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
43 diagonal_length = sqrt((urx - llx) ** 2 + (ury - lly) ** 2)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
44 number_of_lines = 2 + int(diagonal_length / spacing)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
45 hatch_length = spacing * (number_of_lines - 1)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
46
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
47 # build a square (of side hatch_length) horizontal lines
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
48 # centred on centroid of the bounding box, 'spacing' units apart
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
49 coords = []
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
50 for i in range(number_of_lines):
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
51 # alternate lines l2r and r2l to keep HP-7470A plotter happy ☺
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
52 if i % 2:
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
53 coords.extend([((
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
54 centre_x - hatch_length / 2, \
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
55 centre_y - hatch_length / 2 + i * spacing), (
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
56 centre_x + hatch_length / 2, \
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
57 centre_y - hatch_length / 2 + i * spacing))])
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
58 else:
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
59 coords.extend([(( \
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
60 centre_x + hatch_length / 2, \
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
61 centre_y - hatch_length / 2 + i * spacing), (
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
62 centre_x - hatch_length / 2, \
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
63 centre_y - hatch_length / 2 + i * spacing))])
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
64 # turn array into Shapely object
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
65 lines = MultiLineString(coords)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
66 # Rotate by angle around box centre
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
67 lines = rotate(lines, angle, origin='centroid', use_radians=False)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
68 # return clipped array
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
69 return rect.intersection(lines)
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
70
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
71 def parse_style(stylestr):
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
72 """
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
73 Parse the given string containing CSS2.1 syntax
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
74 Returns a dict with the keys/values
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
75 """
12
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
76 if not stylestr or stylestr.strip() == '':
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
77 return None
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
78 parser = CSS21Parser()
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
79 style = parser.parse_style_attr(stylestr)
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
80 data = {}
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
81 for obj in style[0]:
13
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
82 val = obj.value[0]
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
83 if hasattr(val, 'value'):
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
84 data[obj.name] = val.value
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
85 return data
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
86
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
87 class Image(object):
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
88 """
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
89 SVG Image handler class
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
90 """
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
91 def __init__(self, filename, options, gcoder):
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
92 self.gcoder = gcoder
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
93 self.options = options
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
94 self.svg = svg.parse(filename)
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
95 self.bb1, self.bb2 = self.svg.bbox()
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
96 self.width, self.height = self.bb2.coord()
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
97 self.infill = None
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
98
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
99 self._check_dimensions()
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
100 self._generate_infill()
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
101
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
102 def _check_dimensions(self):
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
103 """
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
104 Output image dimensions/scaling to console and gcode
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
105 """
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
106 msg = "Original dimension: %.2f x %.2f" % (self.width, self.height)
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
107 print msg
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
108 self.gcoder.comment(msg)
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
109 self.gcoder.comment("Scale: %.2f" % (self.options.scale))
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
110 width = self.width * self.gcoder.mm_pixel * self.options.scale
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
111 height = self.height * self.gcoder.mm_pixel * self.options.scale
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
112 msg = "Print dimension: %.2fmm x %.2fmm" % (width, height)
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
113 print msg
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
114 self.gcoder.comment(msg)
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
115
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
116 def _generate_infill(self):
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
117 """
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
118 Generates infill pattern image for later use
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
119 """
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
120 b1x, b1y = self.bb1.coord()
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
121 b2x, b2y = self.bb2.coord()
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
122 page = box(b1x, b1y, b2x, b2y)
13
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
123 # TODO: Infill spacing needs to be calculated with proper scaling and gcode MM dimensions, when set to 0 (auto)
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
124 # Make infill angle 0, 45 or 90 degrees configurable to options parser (0° = X, 90° = Y, 45° = X and Y but half the speed/accel needed!)
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
125 self.infill = hatchbox(page, self.options.infill_angle, self.options.infill_spacing)
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
126
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
127 def normalize(self, coord):
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
128 """
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
129 Normalize X / Y Axis of coordinates
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
130 At the moment only Y gets flipped to match Reprap coordinate system (0,0 is bottom left instead top left on SVG)
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
131 """
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
132 c_x = coord[0]
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
133 c_y = coord[1]
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
134 # flip y
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
135 c_y = (self.height - c_y)
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
136 return (c_x, c_y)
4
234ad2069fdd initial untested svg to gcode script
mbayer
parents:
diff changeset
137
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
138 def get_drawings(self):
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
139 """
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
140 Returns a list of all svg drawings with segments attribute
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
141 """
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
142 data = []
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
143 for dwg in self.svg.flatten():
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
144 if hasattr(dwg, "segments"):
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
145 data.append(dwg)
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
146 return data
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
147
11
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
148 def cmp_smallest_distance(line1, line2, coords1=None, coords2=None):
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
149 if not coords1:
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
150 coords1 = list(line1.coords)
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
151 if not coords2:
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
152 coords2 = list(line2.coords)
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
153
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
154 dist = [
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
155 abs(math.hypot(coords1[0][0] - coords2[0][0], coords1[0][1] - coords2[0][1])),
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
156 abs(math.hypot(coords1[0][0] - coords2[1][0], coords1[0][1] - coords2[1][1])),
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
157 abs(math.hypot(coords1[1][0] - coords2[0][0], coords1[1][1] - coords2[0][1])),
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
158 abs(math.hypot(coords1[1][0] - coords2[1][0], coords1[1][1] - coords2[1][1]))
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
159 ]
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
160
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
161 # return the smallest distance between the two lines
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
162 # check both start and endpoints to each other
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
163 return sorted(dist)[0]
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
164
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
165 def slow_sort_lines_by_distance(multilines):
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
166 lines = []
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
167 coords = []
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
168 for line in multilines:
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
169 lines.append(line)
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
170 # coords list for brutal speedup!
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
171 # without this it would be terrible_slow_sort_lines_by_distance()
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
172 coords.append(list(line.coords))
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
173
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
174 data = [lines.pop(0)]
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
175 last = coords.pop(0)
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
176
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
177 def pop_nearest(line, last):
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
178 idx = -1
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
179 dist = 99999999
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
180 for test in lines:
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
181 idx += 1
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
182 tmp = cmp_smallest_distance(line, test, last, coords[idx])
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
183 if tmp < dist:
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
184 dist = tmp
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
185 dist_idx = idx
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
186 # nearest item found
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
187 return (lines.pop(dist_idx), coords.pop(dist_idx))
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
188
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
189 print "Optimizing infill movement, please wait..."
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
190 while len(lines) > 0:
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
191 tmp = len(lines)
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
192 if not (tmp % 10):
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
193 sys.stdout.write("\r%d " % tmp)
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
194 sys.stdout.flush()
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
195 tmp = pop_nearest(data[-1], last)
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
196 data.append(tmp[0])
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
197 last = tmp[1]
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
198 print "\rdone"
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
199 return data
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
200
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
201 def svg2gcode(options, gcoder):
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
202 image = Image(options.filename, options, gcoder)
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
203
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
204 for dwg in image.get_drawings():
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
205 for l in dwg.segments(1):
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
206 # THE OUTLINE
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
207 coord = image.normalize(l[0].coord())
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
208 gcoder.move(coord[0], coord[1])
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
209 for pt in l[1:]:
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
210 coord = image.normalize(pt.coord())
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
211 gcoder.engrave(coord[0], coord[1])
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
212
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
213 if options.outline:
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
214 continue
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
215
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
216 if isinstance(dwg, svg.Polygon) or isinstance(dwg, svg.Path):
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
217 #check if we should infill?
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
218 style = parse_style(dwg.style)
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
219 if not style:
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
220 continue
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
221 if not 'fill' in style.keys():
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
222 continue
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
223 if style['fill'] == 'none':
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
224 continue
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
225
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
226 # try to generate the infill poly complex
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
227 poly = None
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
228 for l in dwg.segments(1):
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
229 segments = []
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
230 for pnt in l:
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
231 segments.append(pnt.coord())
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
232 shape = Polygon(segments)
12
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
233 #if shape.is_valid:
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
234 if not poly:
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
235 poly = shape
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
236 else:
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
237 try:
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
238 if shape.within(poly):
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
239 poly = poly.difference(shape)
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
240 else:
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
241 poly = poly.union(shape)
12
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
242 except Exception:
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
243 pass
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
244
12
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
245 if not poly:
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
246 continue
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
247
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
248 try:
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
249 lines = poly.intersection(image.infill)
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
250 except Exception:
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
251 lines = None
a90b8113be25 bugfixes
mbayer
parents: 11
diff changeset
252
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
253 if lines:
11
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
254 #pprint (dir(lines))
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
255 # THE INFILL
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
256 prev_end = None
11
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
257 # sort lines by nearest
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
258 lines_ordered = slow_sort_lines_by_distance(lines)
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
259
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
260
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
261 #lines_distances = []
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
262 #prev_line = lines[0]
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
263 #for line in lines:
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
264 # lines_distances.append(cmp_smallest_distance(line, prev_line))
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
265 # prev_line = line
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
266 ##lines_ordered = sorted(lines, key=cmp_to_key(cmp_smallest_distance))
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
267 ## decorate, sort, undecorate:
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
268 #lines_distances, lines = zip(*sorted(zip(lines_distances, lines)))
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
269
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
270
e7feabe18234 infill improved movement
mbayer
parents: 10
diff changeset
271 for line in lines_ordered:
10
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
272 coords = [
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
273 image.normalize((line.coords[0][0], line.coords[0][1])),
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
274 image.normalize((line.coords[1][0], line.coords[1][1]))
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
275 ]
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
276 if prev_end:
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
277 # calculate distances to previous end, swap if current end is nearest
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
278 dist = [
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
279 abs(math.hypot(coords[0][0] - prev_end[0], coords[0][1] - prev_end[1])),
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
280 abs(math.hypot(coords[1][0] - prev_end[0], coords[1][1] - prev_end[1]))
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
281 ]
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
282 if dist[0] > dist[1]:
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
283 coords = list(reversed(coords))
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
284 prev_end = coords[1]
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
285 gcoder.move(coords[0][0], coords[0][1])
92835c3f171a finished refactoring
mbayer
parents: 9
diff changeset
286 gcoder.engrave(coords[1][0], coords[1][1])
6
ff679c15cb0e infill only on Polygon or Path which have fill style attribute
mbayer
parents: 5
diff changeset
287
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
288 def init_options():
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
289 parser = OptionParser()
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
290 parser.add_option("-f", "--file", dest="filename", default=None,
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
291 help="Load SVG file", metavar="FILE")
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
292 parser.add_option("-s", "--scale",
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
293 dest="scale", type="float", default=1.0,
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
294 help="set scale factor (default 1.0)")
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
295 parser.add_option("-e", "",
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
296 dest="engrave_speed", type="float", default=20,
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
297 help="engrave speed mm/sec (default 20)")
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
298 parser.add_option("-t", "",
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
299 dest="travel_speed", type="float", default=130,
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
300 help="travel speed mm/sec (default 130)")
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
301 parser.add_option("-o", "--outline", action="store_true",
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
302 dest="outline", default=False,
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
303 help="no infill, only outlines")
13
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
304 parser.add_option("", "--infill-angle",
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
305 dest="infill_angle", type="int", default=45,
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
306 help="infill angle: 0 = X, 90 = Y (default 45)")
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
307 parser.add_option("", "--infill-spacing",
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
308 dest="infill_spacing", type="float", default=1.5,
e2fd4d7b3cb6 infill controllable
mbayer
parents: 12
diff changeset
309 help="infill spacing in SVG units (default 1.5)")
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
310 return parser.parse_args()
7
421aae29d9d9 finished poly infill
mbayer
parents: 6
diff changeset
311
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
312 if __name__ == "__main__":
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
313 (OPTIONS, ARGS) = init_options()
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
314 if not OPTIONS.filename:
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
315 print "no filename given!"
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
316 sys.exit(1)
5
b41cdab37aab added generic infill support, alpha stage
mbayer
parents: 4
diff changeset
317
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
318 # initialize gcode worker
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
319 GCODER = Gcode(scale=OPTIONS.scale, travel_speed=OPTIONS.travel_speed, engrave_speed=OPTIONS.engrave_speed)
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
320
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
321 # processing
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
322 svg2gcode(OPTIONS, GCODER)
8
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
323
86f90bddac0f code refactoring
mbayer
parents: 7
diff changeset
324 # write gcode file
9
89d724cfd8c3 code refactoring - more cleanup
mbayer
parents: 8
diff changeset
325 GCODER.write(OPTIONS.filename + ".g")

mercurial