summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLex Neva <github.com@lexneva.name>2018-06-21 15:41:06 -0400
committerLex Neva <github.com@lexneva.name>2018-06-21 15:41:06 -0400
commite29096ee138bd674e96a369a853d75eb7c919823 (patch)
tree963188c1d0ec3fa010fe353e2d97be64b6856066 /lib
parentc8fe52f136c77906741fdab7093942eeb363a209 (diff)
add commands framework
Diffstat (limited to 'lib')
-rw-r--r--lib/commands.py89
-rw-r--r--lib/elements/element.py29
-rw-r--r--lib/elements/stroke.py3
-rw-r--r--lib/extensions/base.py4
-rw-r--r--lib/extensions/params.py3
-rw-r--r--lib/svg/__init__.py1
-rw-r--r--lib/svg/path.py20
-rw-r--r--lib/svg/tags.py5
8 files changed, 132 insertions, 22 deletions
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)