diff options
Diffstat (limited to 'embroider.py')
| -rw-r--r-- | embroider.py | 113 |
1 files changed, 106 insertions, 7 deletions
diff --git a/embroider.py b/embroider.py index 392182e4..7f22ccb8 100644 --- a/embroider.py +++ b/embroider.py @@ -47,6 +47,8 @@ SVG_GROUP_TAG = inkex.addNS('g', 'svg') EMBROIDERABLE_TAGS = (SVG_PATH_TAG, SVG_POLYLINE_TAG) +# modern versions of Inkscape use 96 pixels per inch as per the CSS standard +PIXELS_PER_MM = 96 / 25.4 class Param(object): def __init__(self, name, description, unit=None, values=[], type=None, group=None, inverse=False, default=None): @@ -73,6 +75,91 @@ def param(*args, **kwargs): return decorator +# cribbed from inkscape-silhouette +def parse_length_with_units( str ): + + ''' + Parse an SVG value which may or may not have units attached + This version is greatly simplified in that it only allows: no units, + units of px, mm, and %. Everything else, it returns None for. + There is a more general routine to consider in scour.py if more + generality is ever needed. + ''' + + u = 'px' + s = str.strip() + if s[-2:] == 'px': + s = s[:-2] + elif s[-2:] == 'mm': + u = 'mm' + s = s[:-2] + elif s[-2:] == 'pt': + u = 'pt' + s = s[:-2] + elif s[-2:] == 'pc': + u = 'pc' + s = s[:-2] + elif s[-2:] == 'cm': + u = 'cm' + s = s[:-2] + elif s[-2:] == 'in': + u = 'in' + s = s[:-2] + elif s[-1:] == '%': + u = '%' + s = s[:-1] + try: + v = float( s ) + except: + raise ValueError("parseLengthWithUnits: unknown unit %s" % s) + + return v, u + + +def convert_length(length): + value, units = parse_length_with_units(length) + + if not units or units == "px": + return value + + if units == 'cm': + value *= 10 + units == 'mm' + + if units == 'mm': + value = value / 25.4 + units = 'in' + + if units == 'in': + # modern versions of Inkscape use CSS's 96 pixels per inch. When you + # open an old document, inkscape will add a viewbox for you. + return value * 96 + + raise ValueError("Unknown unit: %s" % units) + +@cache +def get_viewbox_transform(node): + # somewhat cribbed from inkscape-silhouette + + doc_width = convert_length(node.get('width')) + doc_height = convert_length(node.get('height')) + + viewbox = node.get('viewBox').strip().replace(',', ' ').split() + + dx = -float(viewbox[0]) + dy = -float(viewbox[1]) + transform = simpletransform.parseTransform("translate(%f, %f)" % (dx, dy)) + + try: + sx = doc_width / float(viewbox[2]) + sy = doc_height / float(viewbox[3]) + scale_transform = simpletransform.parseTransform("scale(%f, %f)" % (sx, sy)) + transform = simpletransform.composeTransform(transform, scale_transform) + except ZeroDivisionError: + pass + + return transform + class EmbroideryElement(object): def __init__(self, node, options=None): self.node = node @@ -121,7 +208,7 @@ class EmbroideryElement(object): if param.endswith('_mm'): # print >> dbg, "get_float_param", param, value, "*", self.options.pixels_per_mm - value = value * getattr(self.options, "pixels_per_mm", 10) + value = value * PIXELS_PER_MM return value @@ -133,7 +220,7 @@ class EmbroideryElement(object): return default if param.endswith('_mm'): - value = int(value * getattr(self.options, "pixels_per_mm", 10)) + value = int(value * PIXELS_PER_MM) return value @@ -197,9 +284,14 @@ class EmbroideryElement(object): # combine this node's transform with all parent groups' transforms transform = simpletransform.composeParents(self.node, transform) + # add in the transform implied by the viewBox + viewbox_transform = get_viewbox_transform(self.node.getroottree().getroot()) + transform = simpletransform.composeTransform(viewbox_transform, transform) + # apply the combined transform to this node's path simpletransform.applyTransformToPath(transform, path) + return path def flatten(self, path): @@ -500,7 +592,7 @@ class Fill(EmbroideryElement): # only stitch the first point if it's a reasonable distance away from the # last stitch - if not patch.stitches or (beg - patch.stitches[-1]).length() > 0.5 * getattr(self.options, "pixels_per_mm", 10): + if not patch.stitches or (beg - patch.stitches[-1]).length() > 0.5 * PIXELS_PER_MM: patch.add_stitch(beg) first_stitch = self.adjust_stagger(beg, angle, row_spacing, max_stitch_length) @@ -515,7 +607,7 @@ class Fill(EmbroideryElement): patch.add_stitch(beg + offset * row_direction) offset += max_stitch_length - if (end - patch.stitches[-1]).length() > 0.1 * getattr(self.options, "pixels_per_mm", 10): + if (end - patch.stitches[-1]).length() > 0.1 * PIXELS_PER_MM: patch.add_stitch(end) @@ -1000,7 +1092,7 @@ class AutoFill(Fill): patch.add_stitch(PyEmb.Point(*outline.interpolate(pos).coords[0])) end = PyEmb.Point(*end) - if (end - patch.stitches[-1]).length() > 0.1 * getattr(self.options, "pixels_per_mm", 10): + if (end - patch.stitches[-1]).length() > 0.1 * PIXELS_PER_MM: patch.add_stitch(end) print >> dbg, "end connect_points" @@ -1761,6 +1853,12 @@ def stitches_to_polylines(stitches): return polylines def emit_inkscape(parent, stitches): + transform = get_viewbox_transform(parent.getroottree().getroot()) + + # we need to correct for the viewbox + transform = simpletransform.invertTransform(transform) + transform = simpletransform.formatTransform(transform) + for color, polyline in stitches_to_polylines(stitches): # dbg.write('polyline: %s %s\n' % (color, repr(polyline))) inkex.etree.SubElement(parent, @@ -1770,6 +1868,7 @@ def emit_inkscape(parent, stitches): 'stroke-width': "0.4", 'fill': 'none'}), 'points': " ".join(",".join(str(coord) for coord in point) for point in polyline), + 'transform': transform }) @@ -1916,8 +2015,8 @@ class Embroider(inkex.Effect): patches.extend(element.to_patches(last_patch)) - stitches = patches_to_stitches(patches, self.options.collapse_length_mm * self.options.pixels_per_mm) - emb = PyEmb.Embroidery(stitches, self.options.pixels_per_mm) + stitches = patches_to_stitches(patches, self.options.collapse_length_mm * PIXELS_PER_MM) + emb = PyEmb.Embroidery(stitches, PIXELS_PER_MM) emb.export(self.get_output_path(), self.options.output_format) new_layer = inkex.etree.SubElement(self.document.getroot(), SVG_GROUP_TAG, {}) |
