From 422e770eadd1805601861632845b3e63c66c5acc Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 30 Jun 2018 14:16:47 -0400 Subject: [PATCH 01/20] add stop and trim command symbols --- symbols/inkstitch.svg | 72 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/symbols/inkstitch.svg b/symbols/inkstitch.svg index 2af52d926..9c551964d 100644 --- a/symbols/inkstitch.svg +++ b/symbols/inkstitch.svg @@ -24,9 +24,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="2" - inkscape:cx="64.500275" - inkscape:cy="322.07765" + inkscape:zoom="4" + inkscape:cx="127.82066" + inkscape:cy="323.33371" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -90,6 +90,48 @@ inkscape:transform-center-x="-1.7419043" style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.74180555;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + + Stop the machine after sewing this object (for applique, etc) + + + + + + Trim the thread after sewing this object. + + + @@ -123,5 +165,29 @@ y="0" width="100%" height="100%" /> + + + From 61983b615b202bb95c21d7a5021af3373615e839 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 30 Jun 2018 14:16:56 -0400 Subject: [PATCH 02/20] add has_command() --- lib/elements/element.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/elements/element.py b/lib/elements/element.py index 3c31f1b01..f0b7ea6f4 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -214,6 +214,10 @@ class EmbroideryElement(object): def get_commands(self, command): return [c for c in self.commands if c.command == command] + @cache + def has_command(self, command): + return len(self.get_commands(command)) > 0 + @cache def get_command(self, command): commands = self.get_commands(command) From 3893d13b52b2755ea134ec4d3a215ee807dbbc2e Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 30 Jun 2018 14:18:45 -0400 Subject: [PATCH 03/20] add support for trim/stop commands --- lib/elements/element.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/elements/element.py b/lib/elements/element.py index f0b7ea6f4..1c67d123e 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -268,8 +268,8 @@ class EmbroideryElement(object): patches = self.to_patches(last_patch) if patches: - patches[-1].trim_after = self.trim_after - patches[-1].stop_after = self.stop_after + patches[-1].trim_after = self.has_command("trim") or self.trim_after + patches[-1].stop_after = self.has_command("stop") or self.stop_after return patches From aa86dc56ad5cb9166ab1c9cda036d9521855ad29 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 30 Jun 2018 14:19:28 -0400 Subject: [PATCH 04/20] remove 'TRIM after' and 'STOP after' from Params dialog --- lib/elements/element.py | 12 ------------ messages.po | 16 +--------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/lib/elements/element.py b/lib/elements/element.py index 1c67d123e..62e9745d0 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -242,22 +242,10 @@ class EmbroideryElement(object): return [self.strip_control_points(subpath) for subpath in path] @property - @param('trim_after', - _('TRIM after'), - tooltip=_('Trim thread after this object (for supported machines and file formats)'), - type='boolean', - default=False, - sort_index=1000) def trim_after(self): return self.get_boolean_param('trim_after', False) @property - @param('stop_after', - _('STOP after'), - tooltip=_('Add STOP instruction after this object (for supported machines and file formats)'), - type='boolean', - default=False, - sort_index=1000) def stop_after(self): return self.get_boolean_param('stop_after', False) diff --git a/messages.po b/messages.po index 4607abebc..621db2e3b 100644 --- a/messages.po +++ b/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2018-07-12 20:04-0400\n" +"POT-Creation-Date: 2018-07-12 20:13-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -51,20 +51,6 @@ msgstr "" msgid "%(id)s has more than one command of type '%(command)s' linked to it" msgstr "" -msgid "TRIM after" -msgstr "" - -msgid "Trim thread after this object (for supported machines and file formats)" -msgstr "" - -msgid "STOP after" -msgstr "" - -msgid "" -"Add STOP instruction after this object (for supported machines and file " -"formats)" -msgstr "" - msgid "Fill" msgstr "" From c0a04f3dca01fc6ccdc398e8c54fff67383f760d Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 2 Jul 2018 21:07:31 -0400 Subject: [PATCH 05/20] center symbols on the origin --- symbols/inkstitch.svg | 168 +++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 86 deletions(-) diff --git a/symbols/inkstitch.svg b/symbols/inkstitch.svg index 9c551964d..db8813b65 100644 --- a/symbols/inkstitch.svg +++ b/symbols/inkstitch.svg @@ -18,6 +18,7 @@ inkscape:version="0.92.3 (unknown)" sodipodi:docname="inkstitch.svg"> + inkscape:snap-global="true" + showguides="false" + inkscape:snap-others="true" + inkscape:object-nodes="false" + inkscape:snap-nodes="false"> + id="inkstitch_stop"> Fill stitch starting point - + id="title13328">Stop the machine after sewing this object (for applique, etc) + style="opacity:1;vector-effect:none;fill:#fafafa;fill-opacity:1;fill-rule:evenodd;stroke:#003399;stroke-width:1.06501234;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.19503705, 3.19503705;stroke-dashoffset:0;stroke-opacity:1" + d="m 9.246554,58.4095 c -1.9e-6,5.106729 -4.1398241,9.24655 -9.246553,9.24655 -5.1067293,0 -9.2465521,-4.139821 -9.246554,-9.24655 1e-7,-2.452338 0.9741879,-4.804235 2.7082531,-6.538301 1.7340653,-1.734065 4.0859624,-2.708252 6.5383009,-2.708252 5.1067301,0 9.2465528,4.139823 9.246553,9.246553 0,0 0,0 0,0" + id="circle13330" + inkscape:connector-curvature="0" /> + + Fill stitch ending point - Fill stitch starting point + + d="M 9.246554,58.4095 C 9.2465521,63.516229 5.1067293,67.65605 0,67.65605 c -5.1067289,0 -9.2465511,-4.139821 -9.246553,-9.24655 2e-7,-5.10673 4.1398229,-9.246553 9.246553,-9.246553 2.4523385,0 4.8042356,0.974187 6.5383009,2.708252 1.7340652,1.734066 2.708253,4.085963 2.7082531,6.538301 0,0 0,0 0,0" + id="circle13166-6" /> - - Stop the machine after sewing this object (for applique, etc) - - - - Trim the thread after sewing this object. - + inkscape:connector-curvature="0" /> + + + + Fill stitch ending point + style="opacity:1;vector-effect:none;fill:#fafafa;fill-opacity:1;fill-rule:evenodd;stroke:#003399;stroke-width:1.06500006;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.19500017, 3.19500017;stroke-dashoffset:0;stroke-opacity:1" + d="m 9.260977,58.431186 c -1.9e-6,5.106729 -4.1398241,9.24655 -9.246553,9.24655 -5.1067293,0 -9.2465521,-4.139821 -9.246554,-9.24655 10e-8,-2.452338 0.9741879,-4.804235 2.7082531,-6.538301 1.7340653,-1.734065 4.0859624,-2.708252 6.5383009,-2.708252 5.1067301,0 9.2465528,4.139823 9.246553,9.246553 0,0 0,0 0,0" + id="circle13166" /> + - Create symbols carefully! They must be centered about the origin before being converted to a symbol. + height="100%" + transform="translate(151.18108,-1.7165886)" /> - - + transform="translate(113.3858,-1.7165886)" /> + height="100%" + transform="translate(75.590552,-1.7165891)" /> + From a0659e2c2d7609294c1a5a70153b1aa647595e94 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:15:49 -0400 Subject: [PATCH 06/20] remove leftover debugging code (oops) --- lib/commands.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/commands.py b/lib/commands.py index ec62d7167..02c13b25d 100644 --- a/lib/commands.py +++ b/lib/commands.py @@ -77,9 +77,6 @@ def find_commands(node): 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 From d090fa003830f117918fac201ca527d513507a70 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:16:49 -0400 Subject: [PATCH 07/20] move get_bundled_dir to utils --- lib/extensions/install.py | 10 ++-------- lib/utils/__init__.py | 1 + lib/utils/paths.py | 10 ++++++++++ 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 lib/utils/paths.py diff --git a/lib/extensions/install.py b/lib/extensions/install.py index d55b96d03..42a92113c 100644 --- a/lib/extensions/install.py +++ b/lib/extensions/install.py @@ -13,7 +13,7 @@ import logging import wx import inkex -from ..utils import guess_inkscape_config_path +from ..utils import guess_inkscape_config_path, get_bundled_dir class InstallerFrame(wx.Frame): @@ -78,15 +78,9 @@ class InstallerFrame(wx.Frame): def install_addons(self, type): path = os.path.join(self.path, type) - src_dir = self.get_bundled_dir(type) + src_dir = get_bundled_dir(type) self.copy_files(glob(os.path.join(src_dir, "*")), path) - def get_bundled_dir(self, name): - if getattr(sys, 'frozen', None) is not None: - return realpath(os.path.join(sys._MEIPASS, '..', name)) - else: - return realpath(os.path.join(dirname(realpath(__file__)), '..', '..', name)) - if (sys.platform == "win32"): # If we try to just use shutil.copy it says the operation requires elevation. def copy_files(self, files, dest): diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index ff06d4a9f..78d037f18 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -2,3 +2,4 @@ from geometry import * from cache import cache from io import * from inkscape import * +from paths import * diff --git a/lib/utils/paths.py b/lib/utils/paths.py new file mode 100644 index 000000000..863e8e696 --- /dev/null +++ b/lib/utils/paths.py @@ -0,0 +1,10 @@ +import sys +import os +from os.path import dirname, realpath + + +def get_bundled_dir(name): + if getattr(sys, 'frozen', None) is not None: + return realpath(os.path.join(sys._MEIPASS, "..", name)) + else: + return realpath(os.path.join(dirname(realpath(__file__)), '..', '..', name)) From 62ef2850a2f57d64d0e65fbfc055b85e3c940031 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:17:20 -0400 Subject: [PATCH 08/20] generalize Element.shape and implement in all element types --- lib/elements/element.py | 4 ++++ lib/elements/polyline.py | 7 +++++++ lib/elements/satin_column.py | 11 +++++++++++ lib/elements/stroke.py | 7 +++++++ 4 files changed, 29 insertions(+) diff --git a/lib/elements/element.py b/lib/elements/element.py index 62e9745d0..ebca90a41 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -205,6 +205,10 @@ class EmbroideryElement(object): def parse_path(self): return apply_transforms(self.path, self.node) + @property + def shape(self): + raise NotImplementedError("INTERNAL ERROR: %s must implement shape()", self.__class__) + @property @cache def commands(self): diff --git a/lib/elements/polyline.py b/lib/elements/polyline.py index 5c474237b..b9ffdc0b7 100644 --- a/lib/elements/polyline.py +++ b/lib/elements/polyline.py @@ -1,3 +1,5 @@ +from shapely import geometry as shgeo + from .element import param, EmbroideryElement, Patch from ..i18n import _ from ..utils.geometry import Point @@ -27,6 +29,11 @@ class Polyline(EmbroideryElement): return points + @property + @cache + def shape(self): + return shgeo.LineString(self.points) + @property def path(self): # A polyline is a series of connected line segments described by their diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 1d13c5e05..2ceb38de5 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -87,6 +87,17 @@ class SatinColumn(EmbroideryElement): # the edges of the satin column. return self.get_float_param("zigzag_underlay_inset_mm") or self.contour_underlay_inset / 2.0 + @property + @cache + def shape(self): + # This isn't used for satins at all, but other parts of the code + # may need to know the general shape of a satin column. + + flattened = self.flatten(self.parse_path()) + line_strings = [shgeo.LineString(path) for path in flattened] + + return shgeo.MultiLineString(line_strings) + @property @cache def csp(self): diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index eca9e0ba6..e8eb4783a 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -1,4 +1,5 @@ import sys +import shapely.geometry from .element import param, EmbroideryElement, Patch from ..i18n import _ @@ -50,6 +51,12 @@ class Stroke(EmbroideryElement): else: return self.flatten(path) + @property + @cache + def shape(self): + line_strings = [shapely.geometry.LineString(path) for path in self.paths] + return shapely.geometry.MultiLineString(line_strings) + @property @param('manual_stitch', _('Manual stitch placement'), tooltip=_("Stitch every node in the path. Stitch length and zig-zag spacing are ignored."), type='boolean', default=False) def manual_stitch_mode(self): From 22102ee0e839b22668873c45756a3666d4d9cbff Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:42:53 -0400 Subject: [PATCH 09/20] add CONNECTION_TYPE --- lib/svg/tags.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/svg/tags.py b/lib/svg/tags.py index 5488608c1..7eb875402 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) From d9033be6fc4dcca4ee33f80a55f1ae4a7921be89 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:44:08 -0400 Subject: [PATCH 10/20] 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(-) diff --git a/lib/svg/__init__.py b/lib/svg/__init__.py index 50543b1bf..8e8465551 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 a80127745..2d9c0ff36 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 From 0d41f8685c4bae261ef11d9795de63567e335c19 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:44:56 -0400 Subject: [PATCH 11/20] fixup! center symbols on the origin --- symbols/inkstitch.svg | 156 +++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/symbols/inkstitch.svg b/symbols/inkstitch.svg index db8813b65..a9c3bf177 100644 --- a/symbols/inkstitch.svg +++ b/symbols/inkstitch.svg @@ -26,8 +26,8 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="4" - inkscape:cx="56.98931" - inkscape:cy="305.83326" + inkscape:cx="30.48931" + inkscape:cy="293.08326" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -45,37 +45,47 @@ inkscape:object-nodes="false" inkscape:snap-nodes="false"> + units="mm" + spacingx="18.897638" + spacingy="18.897638" + color="#f03fff" + opacity="0.1254902" + empspacing="2" /> Ink/Stitch Commands + id="inkstitch_fill_end"> Stop the machine after sewing this object (for applique, etc) + id="title9427">Fill stitch ending point + + + Trim the thread after sewing this object. + Fill stitch starting point + inkscape:connector-curvature="0" /> - - - Trim the thread after sewing this object. - - + id="inkstitch_stop"> Fill stitch ending point + id="title13328">Stop the machine after sewing this object (for applique, etc) + id="circle13330" + d="m 9.2465269,-2.6e-6 c -1.9e-6,5.106729 -4.1398241,9.24655 -9.246553026709,9.24655 C -5.1067554,9.2465474 -9.2465782,5.1067264 -9.2465801,-2.6e-6 c 10e-8,-2.452338 0.9741879,-4.804235 2.7082531,-6.538301 1.7340653,-1.734065 4.0859624,-2.708252 6.538300873291,-2.708252 C 5.106704,-9.2465556 9.2465267,-5.1067326 9.2465269,-2.6e-6 c 0,0 0,0 0,0" + style="opacity:1;vector-effect:none;fill:#fafafa;fill-opacity:1;fill-rule:evenodd;stroke:#003399;stroke-width:1.06501234;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.19503705, 3.19503705;stroke-dashoffset:0;stroke-opacity:1" + inkscape:connector-curvature="0" /> + id="path13332" + d="m -3.1690251,-4.6497026 c 0,0 2.51587797,0 2.51587797,0 0,0 0,9.14087 0,9.14087 0,0 -2.51587797,0 -2.51587797,0 0,0 0,-9.14087 0,-9.14087" + style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.60622311;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:4.81866985, 4.81866985;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" + inkscape:connector-curvature="0" /> + + id="layer1" + style="display:inline"> Create symbols carefully! They must be centered about the origin before being converted to a symbol. Create symbols carefully! They must be centered about the origin before being converted to a symbol. - + transform="translate(37.82169,75.511319)" /> + transform="translate(75.590552,75.590552)" /> + transform="translate(113.38583,75.590552)" /> + From 3de394e14b00ac2653084f534149db418bd6cebd Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:45:21 -0400 Subject: [PATCH 12/20] add new extension to attach commands to objects --- inx/inkstitch_commands.inx | 21 ++++++ lib/extensions/__init__.py | 1 + lib/extensions/commands.py | 128 +++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 inx/inkstitch_commands.inx create mode 100644 lib/extensions/commands.py diff --git a/inx/inkstitch_commands.inx b/inx/inkstitch_commands.inx new file mode 100644 index 000000000..7b42ca0ea --- /dev/null +++ b/inx/inkstitch_commands.inx @@ -0,0 +1,21 @@ + + + <_name>Attach Commands + org.inkstitch.commands + inkstitch.py + inkex.py + false + false + false + false + commands + + all + + + + + + diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index b11ba1a4d..dfdc7a3e3 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -7,3 +7,4 @@ from input import Input from output import Output from zip import Zip from flip import Flip +from commands import * diff --git a/lib/extensions/commands.py b/lib/extensions/commands.py new file mode 100644 index 000000000..26ced110e --- /dev/null +++ b/lib/extensions/commands.py @@ -0,0 +1,128 @@ +import os +import sys +import inkex +import simpletransform +import cubicsuperpath +from copy import deepcopy +from shapely import geometry as shgeo + +from .base import InkstitchExtension +from ..i18n import _ +from ..elements import SatinColumn +from ..utils import get_bundled_dir, cache +from ..svg.tags import SVG_DEFS_TAG, SVG_GROUP_TAG, SVG_USE_TAG, SVG_PATH_TAG, INKSCAPE_GROUPMODE, XLINK_HREF, CONNECTION_START, CONNECTION_END, CONNECTOR_TYPE +from ..svg import get_node_transform + + +class Commands(InkstitchExtension): + COMMANDS = ["fill_start", "fill_end", "stop", "trim"] + + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + for command in self.COMMANDS: + self.OptionParser.add_option("--%s" % command, type="inkbool") + + @property + def symbols_path(self): + return os.path.join(get_bundled_dir("symbols"), "inkstitch.svg") + + @property + @cache + def symbols_svg(self): + with open(self.symbols_path) as symbols_file: + return inkex.etree.parse(symbols_file) + + @property + @cache + def symbol_defs(self): + return self.symbols_svg.find(SVG_DEFS_TAG) + + @property + @cache + def defs(self): + return self.document.find(SVG_DEFS_TAG) + + def ensure_symbol(self, command): + path = "./*[@id='inkstitch_%s']" % command + if self.defs.find(path) is None: + self.defs.append(deepcopy(self.symbol_defs.find(path))) + + def get_correction_transform(self, node): + # if we want to place our new nodes in the same group as this node, + # then we'll need to factor in the effects of any transforms set on + # the parents of this node. + + # we can ignore the transform on the node itself since it won't apply + # to the objects we add + transform = get_node_transform(node.getparent()) + + # now invert it, so that we can position our objects in absolute + # coordinates + transform = simpletransform.invertTransform(transform) + + return simpletransform.formatTransform(transform) + + def add_connector(self, symbol, element): + # I'd like it if I could position the connector endpoint nicely but inkscape just + # moves it to the element's center immediately after the extension runs. + start_pos = (symbol.get('x'), symbol.get('y')) + end_pos = element.shape.centroid + + path = inkex.etree.Element(SVG_PATH_TAG, + { + "id": self.uniqueId("connector"), + "d": "M %s,%s %s,%s" % (start_pos[0], start_pos[1], end_pos.x, end_pos.y), + "style": "stroke:#000000;stroke-width:1px;", + "transform": self.get_correction_transform(symbol), + CONNECTION_START: "#%s" % symbol.get('id'), + CONNECTION_END: "#%s" % element.node.get('id'), + CONNECTOR_TYPE: "polyline", + } + ) + + symbol.getparent().insert(symbol.getparent().index(symbol), path) + + def get_command_pos(self, element, index, total): + # Put command symbols 30 pixels out from the shape, spaced evenly around it. + outline = element.shape.buffer(30).exterior + return outline.interpolate(index / float(total), normalized=True) + + def add_command(self, element, commands): + for i, command in enumerate(commands): + pos = self.get_command_pos(element, i, len(commands)) + + symbol = inkex.etree.SubElement(element.node.getparent(), SVG_USE_TAG, + { + "id": self.uniqueId("use"), + XLINK_HREF: "#inkstitch_%s" % command, + "height": "100%", + "width": "100%", + "x": str(pos.x), + "y": str(pos.y), + "transform": self.get_correction_transform(element.node) + } + ) + + self.add_connector(symbol, element) + + def effect(self): + if not self.get_elements(): + return + + if not self.selected: + inkex.errormsg(_("Please select one or more objects to which to attach commands.")) + return + + self.svg = self.document.getroot() + + commands = [command for command in self.COMMANDS if getattr(self.options, command)] + + if not commands: + inkex.errormsg(_("Please choose one or more commands to attach.")) + return + + for command in commands: + self.ensure_symbol(command) + + for element in self.elements: + self.add_command(element, commands) From 1c5e4fbf73e673a12d67d27a1f0e88c2265c762f Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 21:54:23 -0400 Subject: [PATCH 13/20] set fill to none for connectors --- lib/extensions/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/extensions/commands.py b/lib/extensions/commands.py index 26ced110e..aeda2cc2f 100644 --- a/lib/extensions/commands.py +++ b/lib/extensions/commands.py @@ -72,7 +72,7 @@ class Commands(InkstitchExtension): { "id": self.uniqueId("connector"), "d": "M %s,%s %s,%s" % (start_pos[0], start_pos[1], end_pos.x, end_pos.y), - "style": "stroke:#000000;stroke-width:1px;", + "style": "stroke:#000000;stroke-width:1px;fill:none;", "transform": self.get_correction_transform(symbol), CONNECTION_START: "#%s" % symbol.get('id'), CONNECTION_END: "#%s" % element.node.get('id'), From 7ccc6aa72ce02ea335f129577c8984698a987d0e Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 22:08:08 -0400 Subject: [PATCH 14/20] remove legacy params when attaching the equivalent command --- lib/extensions/commands.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/extensions/commands.py b/lib/extensions/commands.py index aeda2cc2f..9c080b4e5 100644 --- a/lib/extensions/commands.py +++ b/lib/extensions/commands.py @@ -87,8 +87,26 @@ class Commands(InkstitchExtension): outline = element.shape.buffer(30).exterior return outline.interpolate(index / float(total), normalized=True) + def remove_legacy_param(self, element, command): + if command == "trim" or command == "stop": + # If they had the old "TRIM after" or "STOP after" attributes set, + # automatically delete them. THe new commands will do the same + # thing. + # + # If we didn't delete these here, then things would get confusing. + # If the user were to delete a "trim" symbol added by this extension + # but the "embroider_trim_after" attribute is still set, then the + # trim would keep happening. + + attribute = "embroider_%s_after" % command + + if attribute in element.node.attrib: + del element.node.attrib[attribute] + def add_command(self, element, commands): for i, command in enumerate(commands): + self.remove_legacy_param(element, command) + pos = self.get_command_pos(element, i, len(commands)) symbol = inkex.etree.SubElement(element.node.getparent(), SVG_USE_TAG, @@ -125,4 +143,4 @@ class Commands(InkstitchExtension): self.ensure_symbol(command) for element in self.elements: - self.add_command(element, commands) + self.add_command(element, commands) From 1b63ac5bfa12dc4fc4162a4e7c8e06130e5e906c Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 4 Jul 2018 23:00:03 -0400 Subject: [PATCH 15/20] only process each node once --- lib/extensions/commands.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/extensions/commands.py b/lib/extensions/commands.py index 9c080b4e5..4c9fd1729 100644 --- a/lib/extensions/commands.py +++ b/lib/extensions/commands.py @@ -142,5 +142,11 @@ class Commands(InkstitchExtension): for command in commands: self.ensure_symbol(command) + # Each object (node) in the SVG may correspond to multiple Elements of different + # types (e.g. stroke + fill). We only want to process each one once. + seen_nodes = set() + for element in self.elements: - self.add_command(element, commands) + if element.node not in seen_nodes: + self.add_command(element, commands) + seen_nodes.add(element.node) From 942afda383ea6444ba9460054e04ad5e0cc5128e Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 5 Jul 2018 17:26:44 -0400 Subject: [PATCH 16/20] change symbol object IDs to avoid conflicts --- symbols/inkstitch.svg | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/symbols/inkstitch.svg b/symbols/inkstitch.svg index a9c3bf177..4497e679f 100644 --- a/symbols/inkstitch.svg +++ b/symbols/inkstitch.svg @@ -61,64 +61,64 @@ Fill stitch ending point + id="inkstitch_title9427">Fill stitch ending point Trim the thread after sewing this object. + id="inkstitch_title9282">Trim the thread after sewing this object. Fill stitch starting point + id="inkstitch_title9432">Fill stitch starting point Stop the machine after sewing this object (for applique, etc) + id="inkstitch_title13328">Stop the machine after sewing this object (for applique, etc) From 73565ae691b409af9190a02485e51ed590510384 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 9 Jul 2018 20:59:46 -0400 Subject: [PATCH 17/20] fix stop count --- lib/stitch_plan/stitch_plan.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py index 93bcd1952..742916f05 100644 --- a/lib/stitch_plan/stitch_plan.py +++ b/lib/stitch_plan/stitch_plan.py @@ -183,10 +183,7 @@ class ColorBlock(object): def num_stops(self): """Number of pauses in this color block.""" - # Stops are encoded using two STOP stitches each. See the comment in - # stop.py for an explanation. - - return sum(1 for stitch in self if stitch.stop) / 2 + return sum(1 for stitch in self if stitch.stop) @property def num_trims(self): From 0c6288f7693a6aeb2d3dd15ad727d868de183b6a Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 10 Jul 2018 20:03:51 -0400 Subject: [PATCH 18/20] perturb the positions of commands a bit --- lib/extensions/commands.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/extensions/commands.py b/lib/extensions/commands.py index 4c9fd1729..5767447c8 100644 --- a/lib/extensions/commands.py +++ b/lib/extensions/commands.py @@ -4,6 +4,7 @@ import inkex import simpletransform import cubicsuperpath from copy import deepcopy +from random import random from shapely import geometry as shgeo from .base import InkstitchExtension @@ -84,8 +85,16 @@ class Commands(InkstitchExtension): def get_command_pos(self, element, index, total): # Put command symbols 30 pixels out from the shape, spaced evenly around it. + + # get a line running 30 pixels out from the shape outline = element.shape.buffer(30).exterior - return outline.interpolate(index / float(total), normalized=True) + + # pick this item's spot arond the outline and perturb it a bit to avoid + # stacking up commands if they run the extension multiple times + position = index / float(total) + position += random() * 0.1 + + return outline.interpolate(position, normalized=True) def remove_legacy_param(self, element, command): if command == "trim" or command == "stop": From b90d4c152e4c319e74b984207aa369b47af05074 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 10 Jul 2018 20:07:47 -0400 Subject: [PATCH 19/20] make connector 50% transparent --- lib/extensions/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/extensions/commands.py b/lib/extensions/commands.py index 5767447c8..2f3006ff6 100644 --- a/lib/extensions/commands.py +++ b/lib/extensions/commands.py @@ -73,7 +73,7 @@ class Commands(InkstitchExtension): { "id": self.uniqueId("connector"), "d": "M %s,%s %s,%s" % (start_pos[0], start_pos[1], end_pos.x, end_pos.y), - "style": "stroke:#000000;stroke-width:1px;fill:none;", + "style": "stroke:#000000;stroke-width:1px;stroke-opacity:0.5;fill:none;", "transform": self.get_correction_transform(symbol), CONNECTION_START: "#%s" % symbol.get('id'), CONNECTION_END: "#%s" % element.node.get('id'), From 6caba7b839e9f4e90ab9f3ff1110c8759e30337d Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 10 Jul 2018 20:12:38 -0400 Subject: [PATCH 20/20] fix import --- lib/extensions/__init__.py | 2 +- messages.po | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index dfdc7a3e3..8b2431769 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -7,4 +7,4 @@ from input import Input from output import Output from zip import Zip from flip import Flip -from commands import * +from commands import Commands diff --git a/messages.po b/messages.po index 621db2e3b..34abe079d 100644 --- a/messages.po +++ b/messages.po @@ -178,6 +178,12 @@ msgstr "" msgid "Tip: use Path -> Object to Path to convert non-paths." msgstr "" +msgid "Please select one or more objects to which to attach commands." +msgstr "" + +msgid "Please choose one or more commands to attach." +msgstr "" + msgid "" "\n" "\n"