diff --git a/PyEmb.py b/PyEmb.py index 46d7bf77f..0423b5836 100644 --- a/PyEmb.py +++ b/PyEmb.py @@ -31,6 +31,9 @@ class Point: def rotate_left(self): return Point(-self.y, self.x) + def rotate(self, angle): + return Point(self.x * math.cos(angle) - self.y * math.sin(angle), self.y * math.cos(angle) + self.x * math.sin(angle)) + def as_int(self): return Point(int(round(self.x)), int(round(self.y))) diff --git a/embroider.py b/embroider.py index ca239d3db..4f1f835ba 100644 --- a/embroider.py +++ b/embroider.py @@ -2,8 +2,11 @@ # # documentation: see included index.html # LICENSE: -# This code is copyright 2010 by Jon Howell, -# licensed under GPLv3. +# Copyright 2010 by Jon Howell, +# Originally licensed under GPLv3. +# Copyright 2015 by Bas Wijnen . +# New parts are licensed under AGPL3 or later. +# (Note that this means this work is licensed under the common part of those two: AGPL version 3.) # # Important resources: # lxml interface for walking SVG tree: @@ -14,7 +17,6 @@ # http://gispython.org/shapely/manual.html#multipolygons # Embroidery file format documentation: # http://www.achatina.de/sewing/main/TECHNICL.HTM -# import sys sys.path.append("/usr/share/inkscape/extensions") @@ -33,6 +35,7 @@ import operator import lxml.etree as etree from lxml.builder import E import shapely.geometry as shgeo +import shapely.affinity as affinity dbg = open("/tmp/embroider-debug.txt", "w") PyEmb.dbg = dbg @@ -515,14 +518,21 @@ class Embroider(inkex.Effect): self.stacking_order_counter += 1 return SortOrder(threadcolor, self.stacking_order_counter, self.options.preserve_order=="true") - def process_one_path(self, shpath, threadcolor, sortorder): + def process_one_path(self, shpath, threadcolor, sortorder, angle): #self.add_shapely_geo_to_svg(shpath.boundary, color="#c0c000") - rows_of_segments = self.intersect_region_with_grating(shpath) + rows_of_segments = self.intersect_region_with_grating(shpath, angle) segments = self.visit_segments_one_by_one(rows_of_segments) def small_stitches(patch, beg, end): - old_dist = None + vector = (end-beg) + patch.addStitch(beg) + old_dist = vector.length() + if (old_dist < self.max_stitch_len_px): + patch.addStitch(end) + return + one_stitch = vector.mul(1.0 / old_dist * self.max_stitch_len_px * random.random()) + beg = beg + one_stitch while (True): vector = (end-beg) dist = vector.length() @@ -546,17 +556,19 @@ class Embroider(inkex.Effect): small_stitches(patch, PyEmb.Point(*beg),PyEmb.Point(*end)) return [patch] - def intersect_region_with_grating(self, shpath): + def intersect_region_with_grating(self, shpath, angle): #dbg.write("bounds = %s\n" % str(shpath.bounds)) - bbox = shpath.bounds - #dbg.write("hatching is %s\n" % hatching) + rotated_shpath = affinity.rotate(shpath, angle, use_radians = True) + bbox = rotated_shpath.bounds + delta = self.row_spacing_px * 50 # *2 should be enough but isn't. TODO: find out why, and if this always works. + bbox = affinity.rotate(shgeo.LinearRing(((bbox[0] - delta, bbox[1] - delta), (bbox[2] + delta, bbox[1] - delta), (bbox[2] + delta, bbox[3] + delta), (bbox[0] - delta, bbox[3] + delta))), -angle, use_radians = True).coords - delta = self.row_spacing_px/2.0 - bbox_sz = (bbox[2]-bbox[0],bbox[3]-bbox[1]) - p0 = PyEmb.Point(bbox[0]-delta,bbox[1]) - p1 = PyEmb.Point(bbox[0]-delta,bbox[3]) - p_inc = PyEmb.Point(self.row_spacing_px, 0) - count = (bbox[2]-bbox[0])/self.row_spacing_px + 2 + p0 = PyEmb.Point(bbox[0][0], bbox[0][1]) + p1 = PyEmb.Point(bbox[1][0], bbox[1][1]) + p2 = PyEmb.Point(bbox[3][0], bbox[3][1]) + count = (p2 - p0).length() / self.row_spacing_px + p_inc = (p2 - p0).mul(1 / count) + count += 2 rows = [] steps = 0 @@ -619,13 +631,22 @@ class Embroider(inkex.Effect): israw = False desc = node.findtext(inkex.addNS('desc', 'svg')) - if (desc!=None): - israw = desc.find("embroider_raw")>=0 + if desc is None: + desc = '' + descparts = {} + for part in desc.split(';'): + if '=' in part: + k, v = part.split('=', 1) + else: + k, v = part, '' + descparts[k] = v + israw = 'embroider_raw' in descparts if (israw): self.patchList.patches.extend(self.path_to_patch_list(node)) else: if (self.get_style(node, "fill")!=None): - self.patchList.patches.extend(self.filled_region_to_patchlist(node)) + angle = math.radians(float(descparts.get('embroider_angle', 0))) + self.patchList.patches.extend(self.filled_region_to_patchlist(node, angle)) if (self.get_style(node, "stroke")!=None): self.patchList.patches.extend(self.path_to_patch_list(node)) @@ -762,7 +783,7 @@ class Embroider(inkex.Effect): return [patch] - def filled_region_to_patchlist(self, node): + def filled_region_to_patchlist(self, node, angle): p = cubicsuperpath.parsePath(node.get("d")) cspsubdiv.cspsubdiv(p, self.options.flat) shapelyPolygon = cspToShapelyPolygon(p) @@ -771,7 +792,8 @@ class Embroider(inkex.Effect): return self.process_one_path( shapelyPolygon, threadcolor, - sortorder) + sortorder, + angle) #TODO def make_stroked_patch(self, node):