added generic infill support, alpha stage

Sat, 07 Nov 2015 15:51:29 +0100

author
mbayer
date
Sat, 07 Nov 2015 15:51:29 +0100
changeset 5
b41cdab37aab
parent 4
234ad2069fdd
child 6
ff679c15cb0e

added generic infill support, alpha stage

svg2gcode/svg2gcode.py file | annotate | diff | comparison | revisions
--- a/svg2gcode/svg2gcode.py	Sat Nov 07 13:37:23 2015 +0100
+++ b/svg2gcode/svg2gcode.py	Sat Nov 07 15:51:29 2015 +0100
@@ -1,8 +1,81 @@
 #!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 import svg, sys
 from gcode import Gcode
 from optparse import OptionParser
 
+from shapely.geometry import box, MultiLineString, Point, Polygon
+from shapely.affinity import rotate
+from shapely import speedups
+from math import sqrt
+
+# enable Shapely speedups, if possible
+if speedups.available:
+    speedups.enable()
+
+def hatchbox(rect, angle, spacing):
+    """
+    returns a Shapely geometry (MULTILINESTRING, or more rarely,
+    GEOMETRYCOLLECTION) for a simple hatched rectangle.
+
+    args:
+    rect - a Shapely geometry for the outer boundary of the hatch
+           Likely most useful if it really is a rectangle
+
+    angle - angle of hatch lines, conventional anticlockwise -ve
+
+    spacing - spacing between hatch lines
+
+    GEOMETRYCOLLECTION case occurs when a hatch line intersects with
+    the corner of the clipping rectangle, which produces a point
+    along with the usual lines.
+    """
+
+    (llx, lly, urx, ury) = rect.bounds
+    centre_x = (urx + llx) / 2
+    centre_y = (ury + lly) / 2
+    diagonal_length = sqrt((urx - llx) ** 2 + (ury - lly) ** 2)
+    number_of_lines = 2 + int(diagonal_length / spacing)
+    hatch_length = spacing * (number_of_lines - 1)
+
+    # build a square (of side hatch_length) horizontal lines
+    # centred on centroid of the bounding box, 'spacing' units apart
+    coords = []
+    for i in range(number_of_lines):
+        # alternate lines l2r and r2l to keep HP-7470A plotter happy ☺
+        if i % 2:
+            coords.extend([((centre_x - hatch_length / 2, centre_y
+                          - hatch_length / 2 + i * spacing), (centre_x
+                          + hatch_length / 2, centre_y - hatch_length
+                          / 2 + i * spacing))])
+        else:
+            coords.extend([((centre_x + hatch_length / 2, centre_y
+                          - hatch_length / 2 + i * spacing), (centre_x
+                          - hatch_length / 2, centre_y - hatch_length
+                          / 2 + i * spacing))])
+    # turn array into Shapely object
+    lines = MultiLineString(coords)
+    # Rotate by angle around box centre
+    lines = rotate(lines, angle, origin='centroid', use_radians=False)
+    # return clipped array
+    return rect.intersection(lines)
+
+def infill(bbox, polygon, angle=0, spacing=10):
+    b1, b2 = bbox
+    x1, y1 = b1.coord()
+    x2, y2 = b2.coord()
+    page = box(x1, y1, x2, y2)
+    hatching = hatchbox(page, angle, spacing)
+    # create shape from polygon:
+    segments = []
+    for pnt in polygon:
+        x, y = pnt.coord()
+        segments.append((x, y))
+
+    shape = Polygon(segments)
+    return shape.intersection(hatching)
+
 parser = OptionParser()
 parser.add_option("-f", "--file", dest="filename", default=None,
                   help="Load SVG file", metavar="FILE")
@@ -15,6 +88,9 @@
 parser.add_option("-t", "",
                   dest="travel_speed", type="float", default=130,
                   help="travel speed mm/sec (default 130)")
+parser.add_option("-o", "--outline", action="store_true",
+                  dest="outline", default=False,
+                  help="no infill, only outlines")
 
 
 (options, args) = parser.parse_args()
@@ -45,11 +121,20 @@
 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)
 
+            if not options.outline:
+                # THE INFILL
+                for line in infill(im.bbox(), l, spacing=2):
+                    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])
+
 # write gcode file
 gcode.write(options.filename + ".g")

mercurial