From 4c986117bfe1f2caa8280d4b0ddbeec69f41b18d Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 26 May 2018 21:26:40 -0400 Subject: first attempt at realistic rendering --- lib/svg/svg.py | 157 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 137 insertions(+), 20 deletions(-) (limited to 'lib/svg') diff --git a/lib/svg/svg.py b/lib/svg/svg.py index 852215f2..147fdc84 100644 --- a/lib/svg/svg.py +++ b/lib/svg/svg.py @@ -1,9 +1,124 @@ -import simpletransform, simplestyle, inkex +import math +import simpletransform, simplestyle, simplepath, inkex -from .units import get_viewbox_transform -from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG +from .units import get_viewbox_transform, PIXELS_PER_MM +from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG, SVG_DEFS_TAG from ..i18n import _ -from ..utils import cache +from ..utils import cache, Point + + +# The stitch vector path looks like this: +# _______ +# (_______) +# +# It's 0.4mm high, which is the approximate thickness of common machine embroidery threads. + +# 1.52 pixels = 0.4mm +stitch_height = 1.52 + +# This vector path starts at the origin and contains a placeholder (%s) for the stitch length. +stitch_path = "M0,0c0.386,0,0.417,0.378,0.428,0.759c0.012,0.382,-0.048,0.754,-0.428,0.759h-%sc-0.357,-0.003,-0.399,-0.376,-0.413,-0.759c-0.014,-0.382,0.067,-0.759,0.413,-0.759z" + +# This filter makes the above stitch path look like a real stitch with lighting. +realistic_filter = """ + + + + + + + + + + + + + + + + + + +""" + +def realistic_stitch(start, end): + """Generate a stitch vector path given a start and end point.""" + + end = Point(*end) + start = Point(*start) + + stitch_length = (end - start).length() + stitch_center = (end + start) / 2.0 + stitch_direction = (end - start) + stitch_angle = math.atan2(stitch_direction.y, stitch_direction.x) + + stitch_length = max(0, stitch_length - 0.2 * PIXELS_PER_MM) + + # create the path by filling in the length in the template + path = simplepath.parsePath(stitch_path % stitch_length) + + simplepath.scalePath(path, 1, 0.8) + + # rotate the path to match the stitch + rotation_center_x = -stitch_length / 2.0 + rotation_center_y = stitch_height / 2.0 + simplepath.rotatePath(path, stitch_angle, cx=rotation_center_x, cy=rotation_center_y) + + # move the path to the location of the stitch + simplepath.translatePath(path, stitch_center.x, stitch_center.y) + + return simplepath.formatPath(path) def color_block_to_point_lists(color_block): @@ -37,22 +152,21 @@ def color_block_to_paths(color_block, svg): # We could emit just a single path with one subpath per point list, but # emitting multiple paths makes it easier for the user to manipulate them. for point_list in color_block_to_point_lists(color_block): - color = color_block.color.visible_on_white.to_hex_str() - paths.append(inkex.etree.Element( - SVG_PATH_TAG, - {'style': simplestyle.formatStyle( - {'stroke': color, - 'stroke-width': "0.4", - 'fill': 'none'}), - 'd': "M" + " ".join(" ".join(str(coord) for coord in point) for point in point_list), - 'transform': get_correction_transform(svg), - 'embroider_manual_stitch': 'true', - 'embroider_trim_after': 'true', - })) - - # no need to trim at the end of a thread color - if paths: - paths[-1].attrib.pop('embroider_trim_after') + color = color_block.color.visible_on_white.darker.to_hex_str() + start = point_list[0] + for point in point_list[1:]: + paths.append(inkex.etree.Element( + SVG_PATH_TAG, + {'style': simplestyle.formatStyle( + { + 'fill': color, + 'stroke': 'none', + 'filter': 'url(#realistic-stitch-filter)' + }), + 'd': realistic_stitch(start, point), + 'transform': get_correction_transform(svg) + })) + start = point return paths @@ -78,4 +192,7 @@ def render_stitch_plan(svg, stitch_plan): INKSCAPE_LABEL: "color block %d" % (i + 1)}) group.extend(color_block_to_paths(color_block, svg)) + defs = svg.find(SVG_DEFS_TAG) + defs.append(inkex.etree.fromstring(realistic_filter)) + svg.append(layer) -- cgit v1.3.1 From d7d2328b59e4a0f0f93830d636b74c2d92ab9d56 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sun, 27 May 2018 15:36:44 -0400 Subject: tinker with rendering --- lib/svg/svg.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'lib/svg') diff --git a/lib/svg/svg.py b/lib/svg/svg.py index 147fdc84..8921ccec 100644 --- a/lib/svg/svg.py +++ b/lib/svg/svg.py @@ -11,23 +11,23 @@ from ..utils import cache, Point # _______ # (_______) # -# It's 0.4mm high, which is the approximate thickness of common machine embroidery threads. +# It's 0.32mm high, which is the approximate thickness of common machine embroidery threads. -# 1.52 pixels = 0.4mm -stitch_height = 1.52 +# 1.216 pixels = 0.32mm +stitch_height = 1.216 # This vector path starts at the origin and contains a placeholder (%s) for the stitch length. -stitch_path = "M0,0c0.386,0,0.417,0.378,0.428,0.759c0.012,0.382,-0.048,0.754,-0.428,0.759h-%sc-0.357,-0.003,-0.399,-0.376,-0.413,-0.759c-0.014,-0.382,0.067,-0.759,0.413,-0.759z" +stitch_path = "M0,0c0.386,0,0.417,0.302,0.428,0.607c0.012,0.306,-0.048,0.603,-0.428,0.607h-%sc-0.357,-0.002,-0.399,-0.3,-0.413,-0.607c-0.014,-0.305,0.067,-0.607,0.413,-0.607z" # This filter makes the above stitch path look like a real stitch with lighting. realistic_filter = """ + x="-0.1" + width="1.2" + y="-0.1" + height="1.2"> + surfaceScale="30"> @@ -108,15 +108,13 @@ def realistic_stitch(start, end): # create the path by filling in the length in the template path = simplepath.parsePath(stitch_path % stitch_length) - simplepath.scalePath(path, 1, 0.8) - # rotate the path to match the stitch rotation_center_x = -stitch_length / 2.0 rotation_center_y = stitch_height / 2.0 simplepath.rotatePath(path, stitch_angle, cx=rotation_center_x, cy=rotation_center_y) # move the path to the location of the stitch - simplepath.translatePath(path, stitch_center.x, stitch_center.y) + simplepath.translatePath(path, stitch_center.x - rotation_center_x, stitch_center.y - rotation_center_y) return simplepath.formatPath(path) -- cgit v1.3.1 From a9b834878b618a3aea6c343e71a2df3046bde3a7 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 29 May 2018 20:55:46 -0400 Subject: optimize path --- lib/svg/svg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/svg') diff --git a/lib/svg/svg.py b/lib/svg/svg.py index 8921ccec..655ddc58 100644 --- a/lib/svg/svg.py +++ b/lib/svg/svg.py @@ -17,7 +17,7 @@ from ..utils import cache, Point stitch_height = 1.216 # This vector path starts at the origin and contains a placeholder (%s) for the stitch length. -stitch_path = "M0,0c0.386,0,0.417,0.302,0.428,0.607c0.012,0.306,-0.048,0.603,-0.428,0.607h-%sc-0.357,-0.002,-0.399,-0.3,-0.413,-0.607c-0.014,-0.305,0.067,-0.607,0.413,-0.607z" +stitch_path = "M0,0c0.4,0,0.4,0.3,0.4,0.6c0,0.3,-0.1,0.6,-0.4,0.6v0.2,-0.2h-%sc-0.4,0,-0.4,-0.3,-0.4,-0.6c0,-0.3,0.1,-0.6,0.4,-0.6v-0.2,0.2z" # This filter makes the above stitch path look like a real stitch with lighting. realistic_filter = """ -- cgit v1.3.1 From 0348b03b91f64c43c5d0adba2f41d0ef21a82c9a Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 30 May 2018 20:10:11 -0400 Subject: add realistic boolean param to render_stitch_plan() --- lib/svg/realistic_rendering.py | 129 ++++++++++++++++++++++++++++++++ lib/svg/svg.py | 166 +++++++++++------------------------------ 2 files changed, 171 insertions(+), 124 deletions(-) create mode 100644 lib/svg/realistic_rendering.py (limited to 'lib/svg') diff --git a/lib/svg/realistic_rendering.py b/lib/svg/realistic_rendering.py new file mode 100644 index 00000000..e31534da --- /dev/null +++ b/lib/svg/realistic_rendering.py @@ -0,0 +1,129 @@ +import simplepath +import math + +from .units import PIXELS_PER_MM +from ..utils import cache, Point + +# The stitch vector path looks like this: +# _______ +# (_______) +# +# It's 0.32mm high, which is the approximate thickness of common machine +# embroidery threads. + +# 1.216 pixels = 0.32mm +stitch_height = 1.216 + +# This vector path starts at the upper right corner of the stitch shape and +# proceeds counter-clockwise.and contains a placeholder (%s) for the stitch +# length. +# +# It contains two invisible "whiskers" of zero width that go above and below +# to ensure that the SVG renderer allocates a large enough canvas area when +# computing the gaussian blur steps. Otherwise, we'd have to expand the +# width and height attributes of the tag to add more buffer space. +# The width and height are specified in multiples of the bounding box +# size, It's the bounding box aligned with the global SVG canvas's axes, not +# the axes of the stitch itself. That means that having a big enough value +# to add enough padding on the long sides of the stitch would waste a ton +# of space on the short sides and significantly slow down rendering. +stitch_path = "M0,0c0.4,0,0.4,0.3,0.4,0.6c0,0.3,-0.1,0.6,-0.4,0.6v0.2,-0.2h-%sc-0.4,0,-0.4,-0.3,-0.4,-0.6c0,-0.3,0.1,-0.6,0.4,-0.6v-0.2,0.2z" + +# This filter makes the above stitch path look like a real stitch with lighting. +realistic_filter = """ + + + + + + + + + + + + + + + + + + +""" + +def realistic_stitch(start, end): + """Generate a stitch vector path given a start and end point.""" + + end = Point(*end) + start = Point(*start) + + stitch_length = (end - start).length() + stitch_center = (end + start) / 2.0 + stitch_direction = (end - start) + stitch_angle = math.atan2(stitch_direction.y, stitch_direction.x) + + stitch_length = max(0, stitch_length - 0.2 * PIXELS_PER_MM) + + # create the path by filling in the length in the template + path = simplepath.parsePath(stitch_path % stitch_length) + + # rotate the path to match the stitch + rotation_center_x = -stitch_length / 2.0 + rotation_center_y = stitch_height / 2.0 + simplepath.rotatePath(path, stitch_angle, cx=rotation_center_x, cy=rotation_center_y) + + # move the path to the location of the stitch + simplepath.translatePath(path, stitch_center.x - rotation_center_x, stitch_center.y - rotation_center_y) + + return simplepath.formatPath(path) diff --git a/lib/svg/svg.py b/lib/svg/svg.py index 655ddc58..5552abd8 100644 --- a/lib/svg/svg.py +++ b/lib/svg/svg.py @@ -1,122 +1,10 @@ -import math -import simpletransform, simplestyle, simplepath, inkex +import simpletransform, simplestyle, inkex -from .units import get_viewbox_transform, PIXELS_PER_MM +from .units import get_viewbox_transform from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG, SVG_DEFS_TAG +from .realistic_rendering import realistic_stitch, realistic_filter from ..i18n import _ -from ..utils import cache, Point - - -# The stitch vector path looks like this: -# _______ -# (_______) -# -# It's 0.32mm high, which is the approximate thickness of common machine embroidery threads. - -# 1.216 pixels = 0.32mm -stitch_height = 1.216 - -# This vector path starts at the origin and contains a placeholder (%s) for the stitch length. -stitch_path = "M0,0c0.4,0,0.4,0.3,0.4,0.6c0,0.3,-0.1,0.6,-0.4,0.6v0.2,-0.2h-%sc-0.4,0,-0.4,-0.3,-0.4,-0.6c0,-0.3,0.1,-0.6,0.4,-0.6v-0.2,0.2z" - -# This filter makes the above stitch path look like a real stitch with lighting. -realistic_filter = """ - - - - - - - - - - - - - - - - - - -""" - -def realistic_stitch(start, end): - """Generate a stitch vector path given a start and end point.""" - - end = Point(*end) - start = Point(*start) - - stitch_length = (end - start).length() - stitch_center = (end + start) / 2.0 - stitch_direction = (end - start) - stitch_angle = math.atan2(stitch_direction.y, stitch_direction.x) - - stitch_length = max(0, stitch_length - 0.2 * PIXELS_PER_MM) - - # create the path by filling in the length in the template - path = simplepath.parsePath(stitch_path % stitch_length) - - # rotate the path to match the stitch - rotation_center_x = -stitch_length / 2.0 - rotation_center_y = stitch_height / 2.0 - simplepath.rotatePath(path, stitch_angle, cx=rotation_center_x, cy=rotation_center_y) - - # move the path to the location of the stitch - simplepath.translatePath(path, stitch_center.x - rotation_center_x, stitch_center.y - rotation_center_y) - - return simplepath.formatPath(path) +from ..utils import cache def color_block_to_point_lists(color_block): @@ -145,10 +33,9 @@ def get_correction_transform(svg): return transform -def color_block_to_paths(color_block, svg): +def color_block_to_realistic_stitches(color_block, svg): paths = [] - # We could emit just a single path with one subpath per point list, but - # emitting multiple paths makes it easier for the user to manipulate them. + for point_list in color_block_to_point_lists(color_block): color = color_block.color.visible_on_white.darker.to_hex_str() start = point_list[0] @@ -168,8 +55,31 @@ def color_block_to_paths(color_block, svg): return paths +def color_block_to_paths(color_block, svg): + paths = [] + # We could emit just a single path with one subpath per point list, but + # emitting multiple paths makes it easier for the user to manipulate them. + for point_list in color_block_to_point_lists(color_block): + color = color_block.color.visible_on_white.to_hex_str() + paths.append(inkex.etree.Element( + SVG_PATH_TAG, + {'style': simplestyle.formatStyle( + {'stroke': color, + 'stroke-width': "0.4", + 'fill': 'none'}), + 'd': "M" + " ".join(" ".join(str(coord) for coord in point) for point in point_list), + 'transform': get_correction_transform(svg), + 'embroider_manual_stitch': 'true', + 'embroider_trim_after': 'true', + })) + + # no need to trim at the end of a thread color + if paths: + paths[-1].attrib.pop('embroider_trim_after') + + return paths -def render_stitch_plan(svg, stitch_plan): +def render_stitch_plan(svg, stitch_plan, realistic=False): layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']") if layer is None: layer = inkex.etree.Element(SVG_GROUP_TAG, @@ -188,9 +98,17 @@ def render_stitch_plan(svg, stitch_plan): SVG_GROUP_TAG, {'id': '__color_block_%d__' % i, INKSCAPE_LABEL: "color block %d" % (i + 1)}) - group.extend(color_block_to_paths(color_block, svg)) - - defs = svg.find(SVG_DEFS_TAG) - defs.append(inkex.etree.fromstring(realistic_filter)) + if realistic: + group.extend(color_block_to_realistic_stitches(color_block, svg)) + else: + group.extend(color_block_to_paths(color_block, svg)) svg.append(layer) + + if realistic: + defs = svg.find(SVG_DEFS_TAG) + + if defs is None: + defs = inkex.etree.SubElement(svg, SVG_DEFS_TAG) + + defs.append(inkex.etree.fromstring(realistic_filter)) -- cgit v1.3.1 From 406032c0f76a6caf1657d13d2b0956551fb7c726 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sun, 10 Jun 2018 15:31:10 -0400 Subject: handle SVG with no width/height --- lib/svg/units.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'lib/svg') diff --git a/lib/svg/units.py b/lib/svg/units.py index 015da60e..126027bc 100644 --- a/lib/svg/units.py +++ b/lib/svg/units.py @@ -75,11 +75,24 @@ def convert_length(length): raise ValueError(_("Unknown unit: %s") % units) +@cache +def get_viewbox(svg): + return svg.get('viewBox').strip().replace(',', ' ').split() + @cache def get_doc_size(svg): - doc_width = convert_length(svg.get('width')) - doc_height = convert_length(svg.get('height')) + width = svg.get('width') + height = svg.get('height') + + if width is None or height is None: + # fall back to the dimensions from the viewBox + viewbox = get_viewbox(svg) + width = viewbox[2] + height = viewbox[3] + + doc_width = convert_length(width) + doc_height = convert_length(height) return doc_width, doc_height @@ -88,7 +101,7 @@ def get_viewbox_transform(node): # somewhat cribbed from inkscape-silhouette doc_width, doc_height = get_doc_size(node) - viewbox = node.get('viewBox').strip().replace(',', ' ').split() + viewbox = get_viewbox(node) dx = -float(viewbox[0]) dy = -float(viewbox[1]) -- cgit v1.3.1 From e29096ee138bd674e96a369a853d75eb7c919823 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 21 Jun 2018 15:41:06 -0400 Subject: add commands framework --- lib/commands.py | 89 +++++++++++++++++++++++++++++++++ lib/elements/element.py | 29 ++++------- lib/elements/stroke.py | 3 +- lib/extensions/base.py | 4 +- lib/extensions/params.py | 3 +- lib/svg/__init__.py | 1 + lib/svg/path.py | 20 ++++++++ lib/svg/tags.py | 5 ++ symbols/inkstitch.svg | 127 +++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 259 insertions(+), 22 deletions(-) create mode 100644 lib/commands.py create mode 100644 lib/svg/path.py create mode 100644 symbols/inkstitch.svg (limited to 'lib/svg') diff --git a/lib/commands.py b/lib/commands.py new file mode 100644 index 00000000..ec62d716 --- /dev/null +++ b/lib/commands.py @@ -0,0 +1,89 @@ +import inkex +import cubicsuperpath + +from .svg import apply_transforms +from .svg.tags import SVG_USE_TAG, SVG_SYMBOL_TAG, CONNECTION_START, CONNECTION_END, XLINK_HREF + + +class Command(object): + def __init__(self, connector): + self.connector = connector + self.svg = self.connector.getroottree().getroot() + + self.parse_command() + + def get_node_by_url(self, url): + # url will be #path12345. Find the object at the other end. + + if url is None: + raise ValueError("url is None") + + if not url.startswith('#'): + raise ValueError("invalid connection url: %s" % url) + + id = url[1:] + + try: + return self.svg.xpath(".//*[@id='%s']" % id)[0] + except (IndexError, AttributeError): + raise ValueError("could not find node by url %s" % id) + + def parse_connector_path(self): + path = cubicsuperpath.parsePath(self.connector.get('d')) + return apply_transforms(path, self.connector) + + def parse_command(self): + path = self.parse_connector_path() + + neighbors = [ + (self.get_node_by_url(self.connector.get(CONNECTION_START)), path[0][0][1]), + (self.get_node_by_url(self.connector.get(CONNECTION_END)), path[0][-1][1]) + ] + + if neighbors[0][0].tag != SVG_USE_TAG: + neighbors.reverse() + + if neighbors[0][0].tag != SVG_USE_TAG: + raise ValueError("connector does not point to a use tag") + + self.symbol = self.get_node_by_url(neighbors[0][0].get(XLINK_HREF)) + + if self.symbol.tag != SVG_SYMBOL_TAG: + raise ValueError("use points to non-symbol") + + self.command = self.symbol.get('id') + + if self.command.startswith('inkstitch_'): + self.command = self.command[10:] + else: + raise ValueError("symbol is not an Ink/Stitch command") + + self.target = neighbors[1][0] + self.target_point = neighbors[1][1] + + def __repr__(self): + return "Command('%s', %s)" % (self.command, self.target_point) + +def find_commands(node): + """Find the symbols this node is connected to and return them as Commands""" + + # find all paths that have this object as a connection + xpath = ".//*[@inkscape:connection-start='#%(id)s' or @inkscape:connection-end='#%(id)s']" % dict(id=node.get('id')) + connectors = node.getroottree().getroot().xpath(xpath, namespaces=inkex.NSS) + + # try to turn them into commands + commands = [] + for connector in connectors: + try: + commands.append(Command(connector)) + except ValueError: + import sys + import traceback + print >> sys.stderr, "not a Command:", connector.get('id'), traceback.format_exc() + # Parsing the connector failed, meaning it's not actually an Ink/Stitch command. + pass + + return commands + +def is_command(node): + return CONNECTION_START in node.attrib or CONNECTION_END in node.attrib diff --git a/lib/elements/element.py b/lib/elements/element.py index 39437c9f..465813d4 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -4,7 +4,8 @@ from shapely import geometry as shgeo from ..i18n import _ from ..utils import cache -from ..svg import PIXELS_PER_MM, get_viewbox_transform, convert_length, get_doc_size +from ..svg import PIXELS_PER_MM, convert_length, get_doc_size, apply_transforms +from ..commands import find_commands # inkscape-provided utilities import simpletransform @@ -171,10 +172,6 @@ class EmbroideryElement(object): @property def path(self): - return cubicsuperpath.parsePath(self.node.get("d")) - - @cache - def parse_path(self): # A CSP is a "cubic superpath". # # A "path" is a sequence of strung-together bezier curves. @@ -202,22 +199,16 @@ class EmbroideryElement(object): # In a path, each element in the 3-tuple is itself a tuple of (x, y). # Tuples all the way down. Hasn't anyone heard of using classes? - path = self.path - - # start with the identity transform - transform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] - - # 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) + return cubicsuperpath.parsePath(self.node.get("d")) - # apply the combined transform to this node's path - simpletransform.applyTransformToPath(transform, path) + @cache + def parse_path(self): + return apply_transforms(self.path, self.node) - return path + @property + @cache + def commands(self): + return find_commands(self.node) def strip_control_points(self, subpath): return [point for control_before, point, control_after in subpath] diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index 5239f978..eca9e0ba 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -4,6 +4,7 @@ from .element import param, EmbroideryElement, Patch from ..i18n import _ from ..utils import cache, Point from ..stitches import running_stitch +from ..svg import parse_length_with_units warned_about_legacy_running_stitch = False @@ -57,7 +58,7 @@ class Stroke(EmbroideryElement): def is_running_stitch(self): # using stroke width <= 0.5 pixels to indicate running stitch is deprecated in favor of dashed lines - stroke_width = float(self.get_style("stroke-width", 1)) + stroke_width, units = parse_length_with_units(self.get_style("stroke-width", "1")) if self.dashed: return True diff --git a/lib/extensions/base.py b/lib/extensions/base.py index 831b6dc6..78f75cf1 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -7,6 +7,7 @@ from collections import MutableMapping from ..svg.tags import * from ..elements import AutoFill, Fill, Stroke, SatinColumn, Polyline, EmbroideryElement from ..utils import cache +from ..commands import is_command SVG_METADATA_TAG = inkex.addNS("metadata", "svg") @@ -165,7 +166,8 @@ class InkstitchExtension(inkex.Effect): classes.append(Fill) if element.get_style("stroke"): - classes.append(Stroke) + if not is_command(element.node): + classes.append(Stroke) if element.get_boolean_param("stroke_first", False): classes.reverse() diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 9d8de41b..58fedd6b 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -19,6 +19,7 @@ from ..stitch_plan import patches_to_stitch_plan from ..elements import EmbroideryElement, Fill, AutoFill, Stroke, SatinColumn from ..utils import save_stderr, restore_stderr from ..simulator import EmbroiderySimulator +from ..commands import is_command def presets_path(): @@ -655,7 +656,7 @@ class Params(InkstitchExtension): classes.append(AutoFill) classes.append(Fill) - if element.get_style("stroke"): + if element.get_style("stroke") and not is_command(node): classes.append(Stroke) if element.get_style("stroke-dasharray") is None: diff --git a/lib/svg/__init__.py b/lib/svg/__init__.py index 1895bba4..50543b1b 100644 --- a/lib/svg/__init__.py +++ b/lib/svg/__init__.py @@ -1,2 +1,3 @@ from .svg import color_block_to_point_lists, render_stitch_plan from .units import * +from .path import apply_transforms diff --git a/lib/svg/path.py b/lib/svg/path.py new file mode 100644 index 00000000..a8012774 --- /dev/null +++ b/lib/svg/path.py @@ -0,0 +1,20 @@ +import simpletransform +import cubicsuperpath + +from .units import get_viewbox_transform + +def apply_transforms(path, node): + # start with the identity transform + transform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] + + # combine this node's transform with all parent groups' transforms + transform = simpletransform.composeParents(node, transform) + + # add in the transform implied by the viewBox + viewbox_transform = get_viewbox_transform(node.getroottree().getroot()) + transform = simpletransform.composeTransform(viewbox_transform, transform) + + # apply the combined transform to this node's path + simpletransform.applyTransformToPath(transform, path) + + return path diff --git a/lib/svg/tags.py b/lib/svg/tags.py index fee59957..5488608c 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -5,8 +5,13 @@ SVG_PATH_TAG = inkex.addNS('path', 'svg') SVG_POLYLINE_TAG = inkex.addNS('polyline', 'svg') SVG_DEFS_TAG = inkex.addNS('defs', 'svg') SVG_GROUP_TAG = inkex.addNS('g', 'svg') +SVG_SYMBOL_TAG = inkex.addNS('symbol', 'svg') +SVG_USE_TAG = inkex.addNS('use', 'svg') INKSCAPE_LABEL = inkex.addNS('label', 'inkscape') INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape') +CONNECTION_START = inkex.addNS('connection-start', 'inkscape') +CONNECTION_END = inkex.addNS('connection-end', 'inkscape') +XLINK_HREF = inkex.addNS('href', 'xlink') EMBROIDERABLE_TAGS = (SVG_PATH_TAG, SVG_POLYLINE_TAG) diff --git a/symbols/inkstitch.svg b/symbols/inkstitch.svg new file mode 100644 index 00000000..3b8a78b2 --- /dev/null +++ b/symbols/inkstitch.svg @@ -0,0 +1,127 @@ + + + + + + + + Ink/Stitch Commands + + + Fill stitch starting point + + + + + Fill stitch ending point + + + + + + + + image/svg+xml + + + + + + + + + + -- cgit v1.3.1 From 22102ee0e839b22668873c45756a3666d4d9cbff Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:42:53 -0400 Subject: add CONNECTION_TYPE --- lib/svg/tags.py | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/svg') diff --git a/lib/svg/tags.py b/lib/svg/tags.py index 5488608c..7eb87540 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -12,6 +12,7 @@ INKSCAPE_LABEL = inkex.addNS('label', 'inkscape') INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape') CONNECTION_START = inkex.addNS('connection-start', 'inkscape') CONNECTION_END = inkex.addNS('connection-end', 'inkscape') +CONNECTOR_TYPE = inkex.addNS('connector-type', 'inkscape') XLINK_HREF = inkex.addNS('href', 'xlink') EMBROIDERABLE_TAGS = (SVG_PATH_TAG, SVG_POLYLINE_TAG) -- cgit v1.3.1 From d9033be6fc4dcca4ee33f80a55f1ae4a7921be89 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:44:08 -0400 Subject: refactor out logic to gather node's transforms --- lib/svg/__init__.py | 2 +- lib/svg/path.py | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'lib/svg') diff --git a/lib/svg/__init__.py b/lib/svg/__init__.py index 50543b1b..8e846555 100644 --- a/lib/svg/__init__.py +++ b/lib/svg/__init__.py @@ -1,3 +1,3 @@ from .svg import color_block_to_point_lists, render_stitch_plan from .units import * -from .path import apply_transforms +from .path import apply_transforms, get_node_transform diff --git a/lib/svg/path.py b/lib/svg/path.py index a8012774..2d9c0ff3 100644 --- a/lib/svg/path.py +++ b/lib/svg/path.py @@ -4,6 +4,14 @@ import cubicsuperpath from .units import get_viewbox_transform def apply_transforms(path, node): + transform = get_node_transform(node) + + # apply the combined transform to this node's path + simpletransform.applyTransformToPath(transform, path) + + return path + +def get_node_transform(node): # start with the identity transform transform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] @@ -14,7 +22,4 @@ def apply_transforms(path, node): viewbox_transform = get_viewbox_transform(node.getroottree().getroot()) transform = simpletransform.composeTransform(viewbox_transform, transform) - # apply the combined transform to this node's path - simpletransform.applyTransformToPath(transform, path) - - return path + return transform -- cgit v1.3.1 From e0cecd6fa444e3b7ea2391ce4f2953ae9fd5f3fa Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 23 Jul 2018 20:15:49 -0400 Subject: fix a couple crashes --- lib/stitch_plan/stitch_plan.py | 4 ++++ lib/svg/svg.py | 3 +++ 2 files changed, 7 insertions(+) (limited to 'lib/svg') diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py index 0fa87d71..a7cd60e8 100644 --- a/lib/stitch_plan/stitch_plan.py +++ b/lib/stitch_plan/stitch_plan.py @@ -45,6 +45,10 @@ def patches_to_stitch_plan(patches, collapse_len=3.0 * PIXELS_PER_MM): color_block.add_stitch(stop=True) color_block = stitch_plan.new_color_block(color_block.color) + if len(color_block) == 0: + # last block ended in a stop, so now we have an empty block + del stitch_plan.color_blocks[-1] + stitch_plan.filter_duplicate_stitches() stitch_plan.add_ties() diff --git a/lib/svg/svg.py b/lib/svg/svg.py index 5552abd8..48b1343a 100644 --- a/lib/svg/svg.py +++ b/lib/svg/svg.py @@ -37,6 +37,9 @@ def color_block_to_realistic_stitches(color_block, svg): paths = [] for point_list in color_block_to_point_lists(color_block): + if not point_list: + continue + color = color_block.color.visible_on_white.darker.to_hex_str() start = point_list[0] for point in point_list[1:]: -- cgit v1.3.1