code refactoring

Sat, 07 Nov 2015 19:30:27 +0100

author
mbayer
date
Sat, 07 Nov 2015 19:30:27 +0100
changeset 8
86f90bddac0f
parent 7
421aae29d9d9
child 9
89d724cfd8c3

code refactoring

svg2gcode/gcode.py file | annotate | diff | comparison | revisions
svg2gcode/svg2gcode.py file | annotate | diff | comparison | revisions
--- a/svg2gcode/gcode.py	Sat Nov 07 18:45:17 2015 +0100
+++ b/svg2gcode/gcode.py	Sat Nov 07 19:30:27 2015 +0100
@@ -1,17 +1,13 @@
 import math
 
 INCH = 25.4 # mm
+STEPS_PER_MM = 80 # hardware: steps/mm on XY
 
 class Gcode(object):
-    def __init__(self, bbox=None, scale=1.0, travel_speed=20, engrave_speed=20):
+    def __init__(self, scale=1.0, travel_speed=20, engrave_speed=20):
         self.dpi = 300
         self.e_factor = 0.1
-
         self.mm_pixel = round(INCH / self.dpi, 4)
-        self.steps_pixel = self.mm_pixel * 80 # mine is 80 steps/mm on XY
-        print "Resolution is %f mm per pixel" % self.mm_pixel
-        if self.steps_pixel <= 5:
-            print "Warning: Steps per pixel (needs to be > 5, otherwise marlin joins lines): %f" % self.steps_pixel
 
         self.lines = []
         self.dist_start = (0, 0)
@@ -21,6 +17,14 @@
         self.travel_speed = travel_speed * 60
         self.engrave_speed = engrave_speed * 60
 
+        self._check_resolution()
+
+    def _check_resolution(self):
+        steps_pixel = self.mm_pixel * STEPS_PER_MM
+        print "Resolution is %f mm per pixel" % self.mm_pixel
+        if steps_pixel <= 5:
+            print "Warning: Steps per pixel (needs to be > 5, otherwise marlin joins lines): %f" % steps_pixel
+
     def _dimensions(self, x, y):
         x = self.mm_pixel * x
         y = self.mm_pixel * y
@@ -29,6 +33,9 @@
             y = y * self.scale
         return (x, y)
 
+    def comment(self, msg):
+        self.lines.append("; %s" % msg)
+
     def move(self, x, y):
         x, y = self._dimensions(x, y)
         if self.e_factor > 0:
@@ -56,10 +63,9 @@
         # write gcode file
         fout = open(filename, "w")
 
-        fout.write("""
-; Filename: %s
-; GCode generated by bitplotter one-night-quick-hack script (marlin code flavour)
-
+        fout.write("""; Filename: %s
+; GCode generated by one-night-and-several-days-not-so-quick-hack-script (marlin code flavour)
+; https://neo-soft.org/hg-public/lasercut-scripts
 G21 ; Metric
 ; We assume Z is in focus height and laser head is focus at bottom left of image!
 G92 X0 Y0 E0 ; set zero position - new origin
--- a/svg2gcode/svg2gcode.py	Sat Nov 07 18:45:17 2015 +0100
+++ b/svg2gcode/svg2gcode.py	Sat Nov 07 19:30:27 2015 +0100
@@ -2,12 +2,11 @@
 # -*- coding: utf-8 -*-
 
 import svg, sys
-import cairo
 from gcode import Gcode
 from optparse import OptionParser
 from tinycss import CSS21Parser
 
-from shapely.geometry import box, MultiLineString, MultiPolygon, Polygon
+from shapely.geometry import box, MultiLineString, Polygon
 from shapely.affinity import rotate
 from shapely import speedups
 from math import sqrt
@@ -47,15 +46,17 @@
     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))])
+            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))])
+            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
@@ -63,46 +64,6 @@
     # return clipped array
     return rect.intersection(lines)
 
-
-parser = OptionParser()
-parser.add_option("-f", "--file", dest="filename", default=None,
-                  help="Load SVG file", metavar="FILE")
-parser.add_option("-s", "--scale",
-                  dest="scale", type="float", default=1.0,
-                  help="set scale factor (default 1.0)")
-parser.add_option("-e", "",
-                  dest="engrave_speed", type="float", default=20,
-                  help="engrave speed mm/sec (default 20)")
-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()
-
-
-if not options.filename:
-    print "no filename given!"
-    sys.exit(1)
-
-gcode = Gcode(scale=options.scale, travel_speed=options.travel_speed, engrave_speed=options.engrave_speed)
-
-im = svg.parse(options.filename)
-b1, b2 = im.bbox()
-width, height = b2.coord()
-print "Original dimension: %.2f x %.2f" % (width, height)
-width *= gcode.mm_pixel * options.scale
-height *= gcode.mm_pixel * options.scale
-print "Print dimension: %.2fmm x %.2fmm" % (width, height)
-
-x1, y1 = b1.coord()
-x2, y2 = b2.coord()
-page = box(x1, y1, x2, y2)
-INFILL = hatchbox(page, 0, 2)
-
 def parse_style(stylestr):
     if stylestr.strip() == '':
         return None
@@ -113,62 +74,114 @@
         kv[obj.name] = obj.value[0].value
     return kv
 
-def normalize(coord):
-    x = coord[0]
-    y = coord[1]
-    # flip y
-    y = (b2.coord()[1] - y)
-    return (x, y)
+
+def svg2gcode(options, gcode):
+
+    def normalize(coord):
+        x = coord[0]
+        y = coord[1]
+        # flip y
+        y = (b2.coord()[1] - y)
+        return (x, y)
+
+    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)
 
-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)
+    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)
 
-            if options.outline:
-                continue
+    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)
+
+                if options.outline:
+                    continue
+
+            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 (isinstance(d, svg.Polygon) or isinstance(d, svg.Path)):
-            #check if we shoild infill?
-            style = parse_style(d.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
+                        else:
+                            if shape.within(poly):
+                                poly = poly.difference(shape)
+                            else:
+                                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])
 
-            poly = None
-            for l in d.segments(1):
-                # create shape from polygon:
-                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
-                    else:
-                        if shape.within(poly):
-                            poly = poly.difference(shape)
-                        else:
-                            poly = poly.union(shape)
+if __name__ == "__main__":
+    parser = OptionParser()
+    parser.add_option("-f", "--file", dest="filename", default=None,
+                      help="Load SVG file", metavar="FILE")
+    parser.add_option("-s", "--scale",
+                      dest="scale", type="float", default=1.0,
+                      help="set scale factor (default 1.0)")
+    parser.add_option("-e", "",
+                      dest="engrave_speed", type="float", default=20,
+                      help="engrave speed mm/sec (default 20)")
+    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")
 
-            lines = poly.intersection(INFILL)
-            if lines:
-                # THE INFILL
-                for line in lines:
-                    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])
+    (options, args) = parser.parse_args()
+
+    if not options.filename:
+        print "no filename given!"
+        sys.exit(1)
 
-# write gcode file
-gcode.write(options.filename + ".g")
+    # initialize gcode worker
+    gcode = Gcode(scale=options.scale, travel_speed=options.travel_speed, engrave_speed=options.engrave_speed)
+
+    # processing
+    svg2gcode(options, gcode)
+
+    # write gcode file
+    gcode.write(options.filename + ".g")

mercurial