diff --git a/lib/commands.py b/lib/commands.py
index f2ab8c3ee..ea6d35091 100644
--- a/lib/commands.py
+++ b/lib/commands.py
@@ -46,10 +46,12 @@ COMMANDS = {
# L10N command attached to an object
"satin_cut_point": N_("Satin cut point (use with Cut Satin Column)"),
-
# L10N command that affects a layer
"ignore_layer": N_("Ignore layer (do not stitch any objects in this layer)"),
+ # L10N command that affects a group
+ "pattern_group": N_("Strokes in this group will be interpretet as a pattern"),
+
# L10N command that affects entire document
"origin": N_("Origin for exported embroidery files"),
@@ -58,6 +60,7 @@ COMMANDS = {
}
OBJECT_COMMANDS = ["fill_start", "fill_end", "satin_start", "satin_end", "stop", "trim", "ignore_object", "satin_cut_point"]
+GROUP_COMMANDS = ["pattern_group"]
LAYER_COMMANDS = ["ignore_layer"]
GLOBAL_COMMANDS = ["origin", "stop_position"]
@@ -184,6 +187,12 @@ def find_commands(node):
return commands
+def group_commands(node, command):
+ xpath = "./ancestor::svg:g/svg:use[@xlink:href='#inkstitch_%(command)s']" % dict(id=node.get('id'), command=command)
+ group_command = node.xpath(xpath, namespaces=inkex.NSS)
+ return group_command
+
+
def layer_commands(layer, command):
"""Find standalone (unconnected) command symbols in this layer."""
diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py
index 3f5f05e55..43948e425 100644
--- a/lib/elements/satin_column.py
+++ b/lib/elements/satin_column.py
@@ -6,7 +6,7 @@
from copy import deepcopy
from itertools import chain
-from inkex import paths
+from inkex import paths, NSS
from shapely import affinity as shaffinity
from shapely import geometry as shgeo
from shapely.ops import nearest_points
@@ -14,6 +14,7 @@ from shapely.ops import nearest_points
from ..i18n import _
from ..svg import (PIXELS_PER_MM, apply_transforms, line_strings_to_csp,
point_lists_to_csp)
+from ..svg.tags import EMBROIDERABLE_TAGS
from ..utils import Point, cache, collapse_duplicate_point, cut
from .element import EmbroideryElement, Patch, param
from .validation import ValidationError, ValidationWarning
@@ -577,10 +578,13 @@ class SatinColumn(EmbroideryElement):
return SatinColumn(node)
def get_patterns(self):
- xpath = ".//*[@inkstitch:pattern='%(id)s']" % dict(id=self.node.get('id'))
- patterns = self.node.getroottree().getroot().xpath(xpath)
+ xpath = "./ancestor::svg:g[svg:use[@xlink:href='#inkstitch_pattern_group']]//*[not(@inkstitch:satin_column='true')]"
+ patterns = self.node.xpath(xpath, namespaces=NSS)
line_strings = []
for pattern in patterns:
+ # TODO: exclude fills in case we will want to use them with the pattern too
+ if pattern.tag not in EMBROIDERABLE_TAGS:
+ continue
d = pattern.get_path()
path = paths.Path(d).to_superpath()
path = apply_transforms(path, pattern)
diff --git a/lib/elements/utils.py b/lib/elements/utils.py
index 03ea48d4d..cbac3d40c 100644
--- a/lib/elements/utils.py
+++ b/lib/elements/utils.py
@@ -17,6 +17,7 @@ from .polyline import Polyline
from .satin_column import SatinColumn
from .stroke import Stroke
from .text import TextObject
+from ..commands import group_commands
def node_to_elements(node): # noqa: C901
@@ -29,7 +30,8 @@ def node_to_elements(node): # noqa: C901
elif node.tag == SVG_PATH_TAG and not node.get('d', ''):
return [EmptyDObject(node)]
- elif node.get(INKSTITCH_ATTRIBS['pattern']):
+ # TODO: exclude fills
+ elif group_commands(node, 'pattern_group') and not node.get(INKSTITCH_ATTRIBS['satin_column']):
return [PatternObject(node)]
elif node.tag in EMBROIDERABLE_TAGS:
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py
index 70df7c373..25f835c33 100644
--- a/lib/extensions/__init__.py
+++ b/lib/extensions/__init__.py
@@ -5,7 +5,6 @@
from lib.extensions.troubleshoot import Troubleshoot
-from .apply_satin_pattern import ApplySatinPattern
from .auto_satin import AutoSatin
from .break_apart import BreakApart
from .cleanup import Cleanup
@@ -46,7 +45,6 @@ __all__ = extensions = [StitchPlanPreview,
GlobalCommands,
ConvertToSatin,
CutSatin,
- ApplySatinPattern,
AutoSatin,
Lettering,
LetteringGenerateJson,
diff --git a/lib/extensions/apply_satin_pattern.py b/lib/extensions/apply_satin_pattern.py
deleted file mode 100644
index 47eb4d836..000000000
--- a/lib/extensions/apply_satin_pattern.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Authors: see git history
-#
-# Copyright (c) 2021 Authors
-# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-
-import inkex
-from lxml import etree
-
-from ..elements import SatinColumn
-from ..i18n import _
-from ..svg.tags import INKSTITCH_ATTRIBS, SVG_DEFS_TAG
-from .base import InkstitchExtension
-
-
-class ApplySatinPattern(InkstitchExtension):
- # Add inkstitch:pattern attribute to selected patterns. The patterns will be projected on a satin column, which must be in the selection too
-
- def effect(self):
- if not self.get_elements():
- return
-
- if not self.svg.selected or not any(isinstance(item, SatinColumn) for item in self.elements) or len(self.svg.selected) < 2:
- inkex.errormsg(_("Please select at least one satin column and a pattern."))
- return
-
- if sum(isinstance(item, SatinColumn) for item in self.elements) > 1:
- inkex.errormsg(_("Please select only one satin column."))
- return
-
- satin_id = self.get_satin_column().node.get('id', None)
- patterns = self.get_patterns()
-
- for pattern in patterns:
- pattern.node.set(INKSTITCH_ATTRIBS['pattern'], satin_id)
- self.set_marker(pattern.node)
-
- def get_satin_column(self):
- return list(filter(lambda satin: isinstance(satin, SatinColumn), self.elements))[0]
-
- def get_patterns(self):
- return list(filter(lambda satin: not isinstance(satin, SatinColumn), self.elements))
-
- def set_marker(self, node):
- document = node.getroottree().getroot()
- xpath = ".//marker[@id='inkstitch-pattern-marker']"
- pattern_marker = document.xpath(xpath)
- if not pattern_marker:
- # get or create def element
- defs = document.find(SVG_DEFS_TAG)
- if defs is None:
- defs = etree.SubElement(document, SVG_DEFS_TAG)
-
- # insert marker
- marker = """
-
-
-
-
- """ # noqa: E501
- defs.append(etree.fromstring(marker))
-
- # attach marker to node
- style = node.get('style', '').split(";")
- import sys
- print(style, file=sys.stderr)
- style = [i for i in style if not i.startswith('marker-start')]
- style.append('marker-start:url(#inkstitch-pattern-marker)')
- node.set('style', ";".join(style))
diff --git a/lib/extensions/base.py b/lib/extensions/base.py
index 00d4a00df..ce5f8b1d8 100644
--- a/lib/extensions/base.py
+++ b/lib/extensions/base.py
@@ -12,7 +12,7 @@ import inkex
from lxml import etree
from stringcase import snakecase
-from ..commands import is_command, layer_commands
+from ..commands import is_command, layer_commands, group_commands
from ..elements import EmbroideryElement, nodes_to_elements
from ..elements.clone import is_clone
from ..i18n import _
@@ -171,9 +171,12 @@ class InkstitchExtension(inkex.Effect):
if selected:
if node.tag == SVG_GROUP_TAG:
pass
- elif (node.tag in EMBROIDERABLE_TAGS or is_clone(node)) and not node.get(INKSTITCH_ATTRIBS['pattern']):
+ elif ((node.tag in EMBROIDERABLE_TAGS or is_clone(node)) and not
+ (len(list(group_commands(node, 'pattern_group'))) and not node.get(INKSTITCH_ATTRIBS['satin_column']))):
nodes.append(node)
- elif troubleshoot and (node.tag in NOT_EMBROIDERABLE_TAGS or node.get(INKSTITCH_ATTRIBS['pattern'])):
+ # add images, text and patterns for the troubleshoot extension
+ elif (troubleshoot and (node.tag in NOT_EMBROIDERABLE_TAGS or
+ (len(list(group_commands(node, 'pattern_group'))) and not node.get(INKSTITCH_ATTRIBS['satin_column'])))):
nodes.append(node)
return nodes
diff --git a/symbols/inkstitch.svg b/symbols/inkstitch.svg
index 4a67ae1cd..f380d1064 100644
--- a/symbols/inkstitch.svg
+++ b/symbols/inkstitch.svg
@@ -58,6 +58,21 @@
id="title9425">Ink/Stitch Commands
+
+ Pattern group
+
+
+
+