--- a/svg2gcode/svg2gcode.py Sat Nov 07 19:30:27 2015 +0100 +++ b/svg2gcode/svg2gcode.py Sat Nov 07 20:31:30 2015 +0100 @@ -74,87 +74,105 @@ kv[obj.name] = obj.value[0].value return kv - -def svg2gcode(options, gcode): +class Image(object): + def __init__(self, filename, options, gcoder): + self.gcoder = gcoder + self.options = options + self.svg = svg.parse(filename) + self.bb1, self.bb2 = self.svg.bbox() + self.width, self.height = self.bb2.coord() + self.infill = None - def normalize(coord): - x = coord[0] - y = coord[1] - # flip y - y = (b2.coord()[1] - y) - return (x, y) + self._check_dimensions() + self._generate_infill() + + def _check_dimensions(self): + msg = "Original dimension: %.2f x %.2f" % (self.width, self.height) + print msg + self.gcoder.comment(msg) + width = self.width * self.gcoder.mm_pixel * self.options.scale + height = self.height * self.gcoder.mm_pixel * self.options.scale + msg = "Print dimension: %.2fmm x %.2fmm" % (width, height) + print msg + self.gcoder.comment(msg) - im = svg.parse(options.filename) - b1, b2 = im.bbox() - width, height = b2.coord() - msg = "Original dimension: %.2f x %.2f" % (width, height) - print msg - gcode.comment(msg) - width *= gcode.mm_pixel * options.scale - height *= gcode.mm_pixel * options.scale - msg = "Print dimension: %.2fmm x %.2fmm" % (width, height) - print msg - gcode.comment(msg) + def _generate_infill(self): + b1x, b1y = self.bb1.coord() + b2x, b2y = self.bb2.coord() + page = box(b1x, b1y, b2x, b2y) + # TODO: Infill spacing needs to be calculated with proper scaling and gcode MM dimensions + # TODO: 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!) + self.infill = hatchbox(page, 0, 2) - x1, y1 = b1.coord() - x2, y2 = b2.coord() - page = box(x1, y1, x2, y2) - # TODO: Infill spacing needs to be calculated with proper scaling and gcode MM dimensions - # TODO: 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!) - INFILL = hatchbox(page, 0, 2) + def normalize(self, coord): + c_x = coord[0] + c_y = coord[1] + # flip y + c_y = (self.height - c_y) + return (c_x, c_y) - data = im.flatten() - for d in data: - if hasattr(d, "segments"): - for l in d.segments(1): - # THE OUTLINE - x, y = normalize(l[0].coord()) - gcode.move(x, y) - for pt in l[1:]: - x, y = normalize(pt.coord()) - gcode.engrave(x, y) + def get_drawings(self): + """ + Returns a list of all svg drawings with segments attribute + """ + data = [] + for dwg in self.svg.flatten(): + if hasattr(dwg, "segments"): + data.append(dwg) + return data + +def svg2gcode(options, gcoder): + image = Image(options.filename, options, gcoder) - if options.outline: - continue + for dwg in image.get_drawings(): + for l in dwg.segments(1): + # THE OUTLINE + coord = image.normalize(l[0].coord()) + gcoder.move(coord[0], coord[1]) + for pt in l[1:]: + coord = image.normalize(pt.coord()) + gcoder.engrave(coord[0], coord[1]) - if isinstance(d, svg.Polygon) or isinstance(d, svg.Path): - #check if we should infill? - style = parse_style(d.style) - if not style: - continue - if not 'fill' in style.keys(): - continue - if style['fill'] == 'none': - continue + if options.outline: + continue + + if isinstance(dwg, svg.Polygon) or isinstance(dwg, svg.Path): + #check if we should infill? + style = parse_style(dwg.style) + if not style: + continue + if not 'fill' in style.keys(): + continue + if style['fill'] == 'none': + continue - # try to generate the infill poly complex - poly = None - for l in d.segments(1): - segments = [] - for pnt in l: - x, y = pnt.coord() - segments.append((x, y)) - shape = Polygon(segments) - if shape.is_valid: - if not poly: - poly = shape + # try to generate the infill poly complex + poly = None + for l in dwg.segments(1): + segments = [] + for pnt in l: + segments.append(pnt.coord()) + shape = Polygon(segments) + if shape.is_valid: + if not poly: + poly = shape + else: + if shape.within(poly): + poly = poly.difference(shape) else: - if shape.within(poly): - poly = poly.difference(shape) - else: - poly = poly.union(shape) + poly = poly.union(shape) - lines = poly.intersection(INFILL) - if lines: - # THE INFILL - for line in lines: - # TODO: swap start/end to nearest move! - start = normalize((line.coords[0][0], line.coords[0][1])) - end = normalize((line.coords[1][0], line.coords[1][1])) - gcode.move(start[0], start[1]) - gcode.engrave(end[0], end[1]) + lines = poly.intersection(image.infill) + if lines: + # THE INFILL + for line in lines: + # TODO: swap start/end to nearest move! + start = image.normalize((line.coords[0][0], line.coords[0][1])) + end = image.normalize((line.coords[1][0], line.coords[1][1])) + gcoder.move(start[0], start[1]) + gcoder.engrave(end[0], end[1]) -if __name__ == "__main__": +def init_options(): parser = OptionParser() parser.add_option("-f", "--file", dest="filename", default=None, help="Load SVG file", metavar="FILE") @@ -170,18 +188,19 @@ parser.add_option("-o", "--outline", action="store_true", dest="outline", default=False, help="no infill, only outlines") + return parser.parse_args() - (options, args) = parser.parse_args() - - if not options.filename: +if __name__ == "__main__": + (OPTIONS, ARGS) = init_options() + if not OPTIONS.filename: print "no filename given!" sys.exit(1) # initialize gcode worker - gcode = Gcode(scale=options.scale, travel_speed=options.travel_speed, engrave_speed=options.engrave_speed) + GCODER = Gcode(scale=OPTIONS.scale, travel_speed=OPTIONS.travel_speed, engrave_speed=OPTIONS.engrave_speed) # processing - svg2gcode(options, gcode) + svg2gcode(OPTIONS, GCODER) # write gcode file - gcode.write(options.filename + ".g") + GCODER.write(OPTIONS.filename + ".g")