From 1adfa87a68be6bcc92d9521b97ab59dc022ab3be Mon Sep 17 00:00:00 2001 From: Kaalleen Date: Tue, 22 Jun 2021 20:04:39 +0200 Subject: satin pattern and split stitch --- lib/elements/satin_column.py | 98 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) (limited to 'lib/elements') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 778fc88a..3f5f05e5 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -12,7 +12,8 @@ from shapely import geometry as shgeo from shapely.ops import nearest_points from ..i18n import _ -from ..svg import line_strings_to_csp, point_lists_to_csp +from ..svg import (PIXELS_PER_MM, apply_transforms, line_strings_to_csp, + point_lists_to_csp) from ..utils import Point, cache, collapse_duplicate_point, cut from .element import EmbroideryElement, Patch, param from .validation import ValidationError, ValidationWarning @@ -74,12 +75,31 @@ class SatinColumn(EmbroideryElement): def satin_column(self): return self.get_boolean_param("satin_column") + # I18N: Split stitch divides a satin column into equal with parts if the maximum stitch length is exceeded + @property + @param('split_stitch', + _('Split stitch'), + tooltip=_('Sets additional stitches if the satin column exceeds the maximum stitch length.'), + type='boolean', + default='false') + def split_stitch(self): + return self.get_boolean_param("split_stitch") + # I18N: "E" stitch is so named because it looks like the letter E. @property @param('e_stitch', _('"E" stitch'), type='boolean', default='false') def e_stitch(self): return self.get_boolean_param("e_stitch") + @property + @param('max_stitch_length_mm', + _('Maximum stitch length'), + tooltip=_('Maximum stitch length for split stitches.'), + type='float', unit="mm", + default=12.1) + def max_stitch_length(self): + return max(self.get_float_param("max_stitch_length_mm", 12.4), 0.1 * PIXELS_PER_MM) + @property def color(self): return self.get_style("stroke") @@ -556,6 +576,20 @@ 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) + line_strings = [] + for pattern in patterns: + d = pattern.get_path() + path = paths.Path(d).to_superpath() + path = apply_transforms(path, pattern) + path = self.flatten(path) + lines = [shgeo.LineString(p) for p in path] + for line in lines: + line_strings.append(line) + return shgeo.MultiLineString(line_strings) + @property @cache def center_line(self): @@ -787,6 +821,63 @@ class SatinColumn(EmbroideryElement): return patch + def do_pattern_satin(self, patterns): + # elements with the attribute 'inkstitch:pattern' set to this elements id will cause extra stitches to be added + patch = Patch(color=self.color) + sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) + for i, (left, right) in enumerate(zip(*sides)): + patch.add_stitch(left) + for point in self._get_pattern_points(left, right, patterns): + patch.add_stitch(point) + patch.add_stitch(right) + if not i+1 >= len(sides[0]): + for point in self._get_pattern_points(right, sides[0][i+1], patterns): + patch.add_stitch(point) + return patch + + def do_split_stitch(self): + # stitches exceeding the maximum stitch length will be divided into equal parts through additional stitches + patch = Patch(color=self.color) + sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) + for i, (left, right) in enumerate(zip(*sides)): + patch.add_stitch(left) + points, count = self._get_split_points(left, right) + for point in points: + patch.add_stitch(point) + patch.add_stitch(right) + # it is possible that the way back has a different length from the first + # but it looks ugly if the points differ too much + # so let's make sure they have at least the same amount of divisions + if not i+1 >= len(sides[0]): + points, count = self._get_split_points(right, sides[0][i+1], count) + for point in points: + patch.add_stitch(point) + + return patch + + def _get_pattern_points(self, left, right, patterns): + points = [] + for pattern in patterns: + intersection = shgeo.LineString([left, right]).intersection(pattern) + if isinstance(intersection, shgeo.Point): + points.append(Point(intersection.x, intersection.y)) + if isinstance(intersection, shgeo.MultiPoint): + for point in intersection: + points.append(Point(point.x, point.y)) + # sort points after their distance to left + points.sort(key=lambda point: point.distance(left)) + return points + + def _get_split_points(self, left, right, count=None): + points = [] + distance = left.distance(right) + split_count = count or int(distance / self.max_stitch_length) + for i in range(split_count): + line = shgeo.LineString((left, right)) + split_point = line.interpolate((i+1)/split_count, normalized=True) + points.append(Point(split_point.x, split_point.y)) + return [points, split_count] + def to_patches(self, last_patch): # Stitch a variable-width satin column, zig-zagging between two paths. @@ -807,8 +898,13 @@ class SatinColumn(EmbroideryElement): # zigzags sit on the contour walk underlay like rail ties on rails. patch += self.do_zigzag_underlay() + patterns = self.get_patterns() if self.e_stitch: patch += self.do_e_stitch() + elif self.split_stitch: + patch += self.do_split_stitch() + elif self.get_patterns(): + patch += self.do_pattern_satin(patterns) else: patch += self.do_satin() -- cgit v1.3.1 From d6df8084f4a0fe8c8e174ea230d158512bd8f094 Mon Sep 17 00:00:00 2001 From: Kaalleen Date: Thu, 24 Jun 2021 22:25:13 +0200 Subject: add start markers, add troubleshoot pattern warning and fix wxpython language issue --- lib/elements/pattern.py | 32 +++++++++++++++++++++++++ lib/elements/utils.py | 8 +++++-- lib/extensions/apply_satin_pattern.py | 44 +++++++++++++++++++++++++++++++++-- lib/extensions/base.py | 2 +- lib/extensions/lettering.py | 4 +++- lib/extensions/params.py | 4 +++- 6 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 lib/elements/pattern.py (limited to 'lib/elements') diff --git a/lib/elements/pattern.py b/lib/elements/pattern.py new file mode 100644 index 00000000..98f29456 --- /dev/null +++ b/lib/elements/pattern.py @@ -0,0 +1,32 @@ +# Authors: see git history +# +# Copyright (c) 2010 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +import inkex + +from ..i18n import _ +from .element import EmbroideryElement +from .validation import ObjectTypeWarning + + +class PatternWarning(ObjectTypeWarning): + name = _("Pattern Element") + description = _("This element will only be stitched out as a pattern within the specified object.") + steps_to_solve = [ + _("If you want to remove the pattern configuration for a pattern object follow these steps:"), + _("* Select pattern element(s)"), + _('* Run Extensions > Ink/Stitch > Troubleshoot > Remove embroidery settings...'), + _('* Make sure "Remove params" is enables'), + _('* Click "Apply"') + ] + + +class PatternObject(EmbroideryElement): + + def validation_warnings(self): + repr_point = next(inkex.Path(self.parse_path()).end_points) + yield PatternWarning(repr_point) + + def to_patches(self, last_patch): + return [] diff --git a/lib/elements/utils.py b/lib/elements/utils.py index aceab485..03ea48d4 100644 --- a/lib/elements/utils.py +++ b/lib/elements/utils.py @@ -4,14 +4,15 @@ # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. from ..commands import is_command -from ..svg.tags import (EMBROIDERABLE_TAGS, SVG_IMAGE_TAG, SVG_PATH_TAG, - SVG_POLYLINE_TAG, SVG_TEXT_TAG) +from ..svg.tags import (EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS, SVG_IMAGE_TAG, + SVG_PATH_TAG, SVG_POLYLINE_TAG, SVG_TEXT_TAG) from .auto_fill import AutoFill from .clone import Clone, is_clone from .element import EmbroideryElement from .empty_d_object import EmptyDObject from .fill import Fill from .image import ImageObject +from .pattern import PatternObject from .polyline import Polyline from .satin_column import SatinColumn from .stroke import Stroke @@ -28,6 +29,9 @@ 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']): + return [PatternObject(node)] + elif node.tag in EMBROIDERABLE_TAGS: element = EmbroideryElement(node) diff --git a/lib/extensions/apply_satin_pattern.py b/lib/extensions/apply_satin_pattern.py index 9da81075..47eb4d83 100644 --- a/lib/extensions/apply_satin_pattern.py +++ b/lib/extensions/apply_satin_pattern.py @@ -4,11 +4,12 @@ # 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 +from ..svg.tags import INKSTITCH_ATTRIBS, SVG_DEFS_TAG from .base import InkstitchExtension -from ..elements import SatinColumn class ApplySatinPattern(InkstitchExtension): @@ -31,9 +32,48 @@ class ApplySatinPattern(InkstitchExtension): 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 f23ec5e2..00d4a00d 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -173,7 +173,7 @@ class InkstitchExtension(inkex.Effect): pass elif (node.tag in EMBROIDERABLE_TAGS or is_clone(node)) and not node.get(INKSTITCH_ATTRIBS['pattern']): nodes.append(node) - elif troubleshoot and node.tag in NOT_EMBROIDERABLE_TAGS: + elif troubleshoot and (node.tag in NOT_EMBROIDERABLE_TAGS or node.get(INKSTITCH_ATTRIBS['pattern'])): nodes.append(node) return nodes diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py index e55365c6..cf627fe5 100644 --- a/lib/extensions/lettering.py +++ b/lib/extensions/lettering.py @@ -31,9 +31,11 @@ class LetteringFrame(wx.Frame): def __init__(self, *args, **kwargs): # This is necessary because of https://github.com/inkstitch/inkstitch/issues/1186 - if sys.platform.startswith('win'): + if sys.platform.startswith('win32'): import locale locale.setlocale(locale.LC_ALL, "C") + lc = wx.Locale() + lc.Init(wx.LANGUAGE_DEFAULT) # begin wxGlade: MyFrame.__init__ self.group = kwargs.pop('group') diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 40494ec7..7775aed1 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -331,9 +331,11 @@ class ParamsTab(ScrolledPanel): class SettingsFrame(wx.Frame): def __init__(self, *args, **kwargs): # This is necessary because of https://github.com/inkstitch/inkstitch/issues/1186 - if sys.platform.startswith('win'): + if sys.platform.startswith('win32'): import locale locale.setlocale(locale.LC_ALL, "C") + lc = wx.Locale() + lc.Init(wx.LANGUAGE_DEFAULT) # begin wxGlade: MyFrame.__init__ self.tabs_factory = kwargs.pop('tabs_factory', []) -- cgit v1.3.1 From c602c4c517cab40dfc2dc7dbc5c29c037cccafae Mon Sep 17 00:00:00 2001 From: Kaalleen Date: Sun, 27 Jun 2021 22:29:57 +0200 Subject: group patterns --- lib/commands.py | 11 ++++- lib/elements/satin_column.py | 10 +++-- lib/elements/utils.py | 4 +- lib/extensions/__init__.py | 2 - lib/extensions/apply_satin_pattern.py | 79 ----------------------------------- lib/extensions/base.py | 9 ++-- symbols/inkstitch.svg | 23 ++++++++++ templates/apply_satin_pattern.xml | 17 -------- 8 files changed, 49 insertions(+), 106 deletions(-) delete mode 100644 lib/extensions/apply_satin_pattern.py delete mode 100644 templates/apply_satin_pattern.xml (limited to 'lib/elements') diff --git a/lib/commands.py b/lib/commands.py index f2ab8c3e..ea6d3509 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 3f5f05e5..43948e42 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 03ea48d4..cbac3d40 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 70df7c37..25f835c3 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 47eb4d83..00000000 --- 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 00d4a00d..ce5f8b1d 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 4a67ae1c..f380d106 100644 --- a/symbols/inkstitch.svg +++ b/symbols/inkstitch.svg @@ -58,6 +58,21 @@ id="title9425">Ink/Stitch Commands + + Pattern group + + + + <use + xlink:href="#inkstitch_pattern_group" + id="use9462" + x="0" + y="0" + width="100%" + height="100%" + transform="translate(189.0002,37.680421)" /> <use xlink:href="#inkstitch_trim" id="use9461" diff --git a/templates/apply_satin_pattern.xml b/templates/apply_satin_pattern.xml deleted file mode 100644 index e52fb1a4..00000000 --- a/templates/apply_satin_pattern.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> - <name>{% trans %}Apply Satin Pattern{% endtrans %}</name> - <id>org.inkstitch.apply_satin_pattern.{{ locale }}</id> - <param name="extension" type="string" gui-hidden="true">apply_satin_pattern</param> - <effect> - <object-type>all</object-type> - <effects-menu> - <submenu name="Ink/Stitch"> - <submenu name="{% trans %}Satin Tools{% endtrans %}" /> - </submenu> - </effects-menu> - </effect> - <script> - {{ command_tag | safe }} - </script> -</inkscape-extension> -- cgit v1.3.1 From 2f54ff2a436f2774bfdc730b6e95c43f18ed81ac Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Sun, 27 Jun 2021 22:47:43 +0200 Subject: group command extension --- lib/elements/satin_column.py | 2 +- lib/elements/utils.py | 3 +-- lib/extensions/__init__.py | 2 ++ lib/extensions/base.py | 2 +- lib/extensions/group_commands.py | 41 ++++++++++++++++++++++++++++++++++++++++ lib/inx/extensions.py | 9 +++++++-- templates/group_commands.xml | 21 ++++++++++++++++++++ 7 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 lib/extensions/group_commands.py create mode 100644 templates/group_commands.xml (limited to 'lib/elements') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 43948e42..65e523d4 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, NSS +from inkex import NSS, paths from shapely import affinity as shaffinity from shapely import geometry as shgeo from shapely.ops import nearest_points diff --git a/lib/elements/utils.py b/lib/elements/utils.py index cbac3d40..78dace6a 100644 --- a/lib/elements/utils.py +++ b/lib/elements/utils.py @@ -3,7 +3,7 @@ # Copyright (c) 2010 Authors # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. -from ..commands import is_command +from ..commands import group_commands, is_command from ..svg.tags import (EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS, SVG_IMAGE_TAG, SVG_PATH_TAG, SVG_POLYLINE_TAG, SVG_TEXT_TAG) from .auto_fill import AutoFill @@ -17,7 +17,6 @@ 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 diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 25f835c3..a6d25d2c 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -14,6 +14,7 @@ from .duplicate_params import DuplicateParams from .embroider_settings import EmbroiderSettings from .flip import Flip from .global_commands import GlobalCommands +from .group_commands import GroupCommands from .import_threadlist import ImportThreadlist from .input import Input from .install import Install @@ -41,6 +42,7 @@ __all__ = extensions = [StitchPlanPreview, Zip, Flip, ObjectCommands, + GroupCommands, LayerCommands, GlobalCommands, ConvertToSatin, diff --git a/lib/extensions/base.py b/lib/extensions/base.py index ce5f8b1d..353e433c 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, group_commands +from ..commands import group_commands, is_command, layer_commands from ..elements import EmbroideryElement, nodes_to_elements from ..elements.clone import is_clone from ..i18n import _ diff --git a/lib/extensions/group_commands.py b/lib/extensions/group_commands.py new file mode 100644 index 00000000..af1f9fb1 --- /dev/null +++ b/lib/extensions/group_commands.py @@ -0,0 +1,41 @@ +# Authors: see git history +# +# Copyright (c) 2010 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +import inkex +from lxml import etree + +from ..commands import GROUP_COMMANDS, ensure_symbol, get_command_description +from ..i18n import _ +from ..svg import get_correction_transform +from ..svg.tags import INKSCAPE_LABEL, SVG_USE_TAG, XLINK_HREF +from .commands import CommandsExtension + + +class GroupCommands(CommandsExtension): + COMMANDS = GROUP_COMMANDS + + def effect(self): + commands = [command for command in self.COMMANDS if getattr(self.options, command)] + + if not commands: + inkex.errormsg(_("Please choose one or more commands to add.")) + return + + correction_transform = get_correction_transform(self.svg.get_current_layer(), child=True) + + for i, command in enumerate(commands): + ensure_symbol(self.document, command) + + etree.SubElement(self.svg.get_current_layer(), SVG_USE_TAG, + { + "id": self.uniqueId("use"), + INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), + XLINK_HREF: "#inkstitch_%s" % command, + "height": "100%", + "width": "100%", + "x": str(i * 20), + "y": "-10", + "transform": correction_transform + }) diff --git a/lib/inx/extensions.py b/lib/inx/extensions.py index 9a197c5d..8ca0addc 100755 --- a/lib/inx/extensions.py +++ b/lib/inx/extensions.py @@ -5,8 +5,8 @@ import pyembroidery -from ..commands import (COMMANDS, GLOBAL_COMMANDS, LAYER_COMMANDS, - OBJECT_COMMANDS) +from ..commands import (COMMANDS, GLOBAL_COMMANDS, GROUP_COMMANDS, + LAYER_COMMANDS, OBJECT_COMMANDS) from ..extensions import Input, Output, extensions from ..threads import ThreadCatalog from .outputs import pyembroidery_output_formats @@ -24,6 +24,10 @@ def global_commands(): return [(command, COMMANDS[command]) for command in GLOBAL_COMMANDS] +def group_commands(): + return [(command, COMMANDS[command]) for command in GROUP_COMMANDS] + + def object_commands(): return [(command, COMMANDS[command]) for command in OBJECT_COMMANDS] @@ -51,6 +55,7 @@ def generate_extension_inx_files(): write_inx_file(name, template.render(formats=pyembroidery_output_formats(), debug_formats=pyembroidery_debug_formats(), threadcatalog=threadcatalog(), + group_commands=group_commands(), layer_commands=layer_commands(), object_commands=object_commands(), global_commands=global_commands())) diff --git a/templates/group_commands.xml b/templates/group_commands.xml new file mode 100644 index 00000000..2a67c5b7 --- /dev/null +++ b/templates/group_commands.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <name>{% trans %}Add Group Commands{% endtrans %}</name> + <id>org.inkstitch.group_commands.{{ locale }}</id> + <param name="description" type="description">{% trans %}Commands will be added to the currently-selected group.{% endtrans %}</param> + {% for command, description in group_commands %} + <param name="{{ command }}" type="boolean" _gui-text="{{ _(description) }}">false</param> + {% endfor %} + <param name="extension" type="string" gui-hidden="true">group_commands</param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu name="Ink/Stitch"> + <submenu name="{% trans %}Commands{% endtrans %}" /> + </submenu> + </effects-menu> + </effect> + <script> + {{ command_tag | safe }} + </script> +</inkscape-extension> -- cgit v1.3.1 From ecacb9829e9c2b7050486707211f9d176aafdf75 Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Mon, 28 Jun 2021 20:05:50 +0200 Subject: pattern markers --- lib/commands.py | 7 ----- lib/elements/pattern.py | 4 +++ lib/elements/satin_column.py | 7 +++-- lib/elements/utils.py | 11 ++++--- lib/extensions/__init__.py | 4 +-- lib/extensions/apply_pattern.py | 62 ++++++++++++++++++++++++++++++++++++++++ lib/extensions/base.py | 12 ++++---- lib/extensions/group_commands.py | 41 -------------------------- lib/inx/extensions.py | 9 ++---- templates/apply_pattern.xml | 15 ++++++++++ templates/group_commands.xml | 21 -------------- 11 files changed, 100 insertions(+), 93 deletions(-) create mode 100644 lib/extensions/apply_pattern.py delete mode 100644 lib/extensions/group_commands.py create mode 100644 templates/apply_pattern.xml delete mode 100644 templates/group_commands.xml (limited to 'lib/elements') diff --git a/lib/commands.py b/lib/commands.py index ea6d3509..cb2a74d5 100644 --- a/lib/commands.py +++ b/lib/commands.py @@ -60,7 +60,6 @@ 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"] @@ -187,12 +186,6 @@ 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/pattern.py b/lib/elements/pattern.py index 98f29456..c66ffbdc 100644 --- a/lib/elements/pattern.py +++ b/lib/elements/pattern.py @@ -30,3 +30,7 @@ class PatternObject(EmbroideryElement): def to_patches(self, last_patch): return [] + + +def is_pattern(node): + return "marker-start:url(#inkstitch-pattern-marker)" in node.get('style', '') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 65e523d4..77cb7d22 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -578,11 +578,14 @@ class SatinColumn(EmbroideryElement): return SatinColumn(node) def get_patterns(self): - xpath = "./ancestor::svg:g[svg:use[@xlink:href='#inkstitch_pattern_group']]//*[not(@inkstitch:satin_column='true')]" + # TODO: which one is better?!? + # All child groups of pattern + # xpath = "./ancestor::svg:g//*[contains(@style, 'marker-start:url(#inkstitch-pattern-marker)')]" + # Only direct siblings of pattern + xpath = "./parent::svg:g/*[contains(@style, 'marker-start:url(#inkstitch-pattern-marker)')]" 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() diff --git a/lib/elements/utils.py b/lib/elements/utils.py index 78dace6a..cd87cec8 100644 --- a/lib/elements/utils.py +++ b/lib/elements/utils.py @@ -3,16 +3,16 @@ # Copyright (c) 2010 Authors # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. -from ..commands import group_commands, is_command -from ..svg.tags import (EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS, SVG_IMAGE_TAG, - SVG_PATH_TAG, SVG_POLYLINE_TAG, SVG_TEXT_TAG) +from ..commands import is_command +from ..svg.tags import (EMBROIDERABLE_TAGS, SVG_IMAGE_TAG, SVG_PATH_TAG, + SVG_POLYLINE_TAG, SVG_TEXT_TAG) from .auto_fill import AutoFill from .clone import Clone, is_clone from .element import EmbroideryElement from .empty_d_object import EmptyDObject from .fill import Fill from .image import ImageObject -from .pattern import PatternObject +from .pattern import PatternObject, is_pattern from .polyline import Polyline from .satin_column import SatinColumn from .stroke import Stroke @@ -29,8 +29,7 @@ def node_to_elements(node): # noqa: C901 elif node.tag == SVG_PATH_TAG and not node.get('d', ''): return [EmptyDObject(node)] - # TODO: exclude fills - elif group_commands(node, 'pattern_group') and not node.get(INKSTITCH_ATTRIBS['satin_column']): + elif is_pattern(node): return [PatternObject(node)] elif node.tag in EMBROIDERABLE_TAGS: diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index a6d25d2c..3bd2fef6 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -5,6 +5,7 @@ from lib.extensions.troubleshoot import Troubleshoot +from .apply_pattern import ApplyPattern from .auto_satin import AutoSatin from .break_apart import BreakApart from .cleanup import Cleanup @@ -14,7 +15,6 @@ from .duplicate_params import DuplicateParams from .embroider_settings import EmbroiderSettings from .flip import Flip from .global_commands import GlobalCommands -from .group_commands import GroupCommands from .import_threadlist import ImportThreadlist from .input import Input from .install import Install @@ -41,8 +41,8 @@ __all__ = extensions = [StitchPlanPreview, Output, Zip, Flip, + ApplyPattern, ObjectCommands, - GroupCommands, LayerCommands, GlobalCommands, ConvertToSatin, diff --git a/lib/extensions/apply_pattern.py b/lib/extensions/apply_pattern.py new file mode 100644 index 00000000..ad881604 --- /dev/null +++ b/lib/extensions/apply_pattern.py @@ -0,0 +1,62 @@ +# 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 ..i18n import _ +from ..svg.tags import SVG_DEFS_TAG +from .base import InkstitchExtension + + +class ApplyPattern(InkstitchExtension): + # This extension will mark selected + + def effect(self): + if not self.get_elements(): + return + + if not self.svg.selected: + inkex.errormsg(_("Please select at least one object to be marked as a pattern.")) + return + + for pattern in self.svg.selected.values(): + self.set_marker(pattern) + + def set_marker(self, node): + xpath = ".//marker[@id='inkstitch-pattern-marker']" + pattern_marker = self.document.xpath(xpath) + + if not pattern_marker: + # get or create def element + defs = self.document.find(SVG_DEFS_TAG) + if defs is None: + defs = etree.SubElement(self.document, SVG_DEFS_TAG) + + # insert marker + marker = """<marker + refX="10" + refY="5" + orient="auto" + id="inkstitch-pattern-marker"> + <g + id="inkstitch-pattern-group"> + <path + style="fill:#fafafa;stroke:#ff5500;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0;stroke-opacity:1;fill-opacity:0.8;" + d="M 10.12911,5.2916678 A 4.8374424,4.8374426 0 0 1 5.2916656,10.12911 4.8374424,4.8374426 0 0 1 0.45422399,5.2916678 4.8374424,4.8374426 0 0 1 5.2916656,0.45422399 4.8374424,4.8374426 0 0 1 10.12911,5.2916678 Z" + id="inkstitch-pattern-marker-circle" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-miterlimit:4;" + id="inkstitch-pattern-marker-spiral" + d="M 4.9673651,5.7245662 C 4.7549848,5.7646159 4.6247356,5.522384 4.6430021,5.3419847 4.6765851,5.0103151 5.036231,4.835347 5.3381858,4.8987426 5.7863901,4.9928495 6.0126802,5.4853625 5.9002872,5.9065088 5.7495249,6.4714237 5.1195537,6.7504036 4.5799191,6.5874894 3.898118,6.3816539 3.5659013,5.6122905 3.7800789,4.9545192 4.0402258,4.1556558 4.9498996,3.7699484 5.7256318,4.035839 6.6416744,4.3498087 7.0810483,5.4003986 6.7631909,6.2939744 6.395633,7.3272552 5.2038143,7.8204128 4.1924535,7.4503931 3.0418762,7.0294421 2.4948761,5.6961604 2.9171752,4.567073 3.3914021,3.2991406 4.8663228,2.6982592 6.1130974,3.1729158 7.4983851,3.7003207 8.1531869,5.3169977 7.6260947,6.6814205 7.0456093,8.1841025 5.2870784,8.8928844 3.8050073,8.3132966 2.1849115,7.6797506 1.4221671,5.7793073 2.0542715,4.1796074 2.7408201,2.4420977 4.7832541,1.6253548 6.5005435,2.310012 8.3554869,3.0495434 9.2262638,5.2339874 8.4890181,7.0688861 8.4256397,7.2266036 8.3515789,7.379984 8.2675333,7.5277183" /> + </g> + </marker>""" # noqa: E501 + defs.append(etree.fromstring(marker)) + + # attach marker to node + style = node.get('style', '').split(";") + 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 353e433c..1c10cd4a 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -12,14 +12,14 @@ import inkex from lxml import etree from stringcase import snakecase -from ..commands import group_commands, is_command, layer_commands +from ..commands import is_command, layer_commands from ..elements import EmbroideryElement, nodes_to_elements from ..elements.clone import is_clone +from ..elements.pattern import is_pattern from ..i18n import _ from ..svg import generate_unique_id from ..svg.tags import (CONNECTOR_TYPE, EMBROIDERABLE_TAGS, INKSCAPE_GROUPMODE, - INKSTITCH_ATTRIBS, NOT_EMBROIDERABLE_TAGS, - SVG_DEFS_TAG, SVG_GROUP_TAG) + NOT_EMBROIDERABLE_TAGS, SVG_DEFS_TAG, SVG_GROUP_TAG) SVG_METADATA_TAG = inkex.addNS("metadata", "svg") @@ -171,12 +171,10 @@ 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 - (len(list(group_commands(node, 'pattern_group'))) and not node.get(INKSTITCH_ATTRIBS['satin_column']))): + elif (node.tag in EMBROIDERABLE_TAGS or is_clone(node)) and not is_pattern(node): nodes.append(node) # 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'])))): + elif troubleshoot and (node.tag in NOT_EMBROIDERABLE_TAGS or is_pattern(node)): nodes.append(node) return nodes diff --git a/lib/extensions/group_commands.py b/lib/extensions/group_commands.py deleted file mode 100644 index af1f9fb1..00000000 --- a/lib/extensions/group_commands.py +++ /dev/null @@ -1,41 +0,0 @@ -# Authors: see git history -# -# Copyright (c) 2010 Authors -# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. - -import inkex -from lxml import etree - -from ..commands import GROUP_COMMANDS, ensure_symbol, get_command_description -from ..i18n import _ -from ..svg import get_correction_transform -from ..svg.tags import INKSCAPE_LABEL, SVG_USE_TAG, XLINK_HREF -from .commands import CommandsExtension - - -class GroupCommands(CommandsExtension): - COMMANDS = GROUP_COMMANDS - - def effect(self): - commands = [command for command in self.COMMANDS if getattr(self.options, command)] - - if not commands: - inkex.errormsg(_("Please choose one or more commands to add.")) - return - - correction_transform = get_correction_transform(self.svg.get_current_layer(), child=True) - - for i, command in enumerate(commands): - ensure_symbol(self.document, command) - - etree.SubElement(self.svg.get_current_layer(), SVG_USE_TAG, - { - "id": self.uniqueId("use"), - INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), - XLINK_HREF: "#inkstitch_%s" % command, - "height": "100%", - "width": "100%", - "x": str(i * 20), - "y": "-10", - "transform": correction_transform - }) diff --git a/lib/inx/extensions.py b/lib/inx/extensions.py index 8ca0addc..9a197c5d 100755 --- a/lib/inx/extensions.py +++ b/lib/inx/extensions.py @@ -5,8 +5,8 @@ import pyembroidery -from ..commands import (COMMANDS, GLOBAL_COMMANDS, GROUP_COMMANDS, - LAYER_COMMANDS, OBJECT_COMMANDS) +from ..commands import (COMMANDS, GLOBAL_COMMANDS, LAYER_COMMANDS, + OBJECT_COMMANDS) from ..extensions import Input, Output, extensions from ..threads import ThreadCatalog from .outputs import pyembroidery_output_formats @@ -24,10 +24,6 @@ def global_commands(): return [(command, COMMANDS[command]) for command in GLOBAL_COMMANDS] -def group_commands(): - return [(command, COMMANDS[command]) for command in GROUP_COMMANDS] - - def object_commands(): return [(command, COMMANDS[command]) for command in OBJECT_COMMANDS] @@ -55,7 +51,6 @@ def generate_extension_inx_files(): write_inx_file(name, template.render(formats=pyembroidery_output_formats(), debug_formats=pyembroidery_debug_formats(), threadcatalog=threadcatalog(), - group_commands=group_commands(), layer_commands=layer_commands(), object_commands=object_commands(), global_commands=global_commands())) diff --git a/templates/apply_pattern.xml b/templates/apply_pattern.xml new file mode 100644 index 00000000..cbd83dbc --- /dev/null +++ b/templates/apply_pattern.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <name>{% trans %}Apply Pattern{% endtrans %}</name> + <id>org.inkstitch.apply_pattern.{{ locale }}</id> + <param name="extension" type="string" gui-hidden="true">apply_pattern</param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu name="Ink/Stitch" /> + </effects-menu> + </effect> + <script> + {{ command_tag | safe }} + </script> +</inkscape-extension> diff --git a/templates/group_commands.xml b/templates/group_commands.xml deleted file mode 100644 index 2a67c5b7..00000000 --- a/templates/group_commands.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> - <name>{% trans %}Add Group Commands{% endtrans %}</name> - <id>org.inkstitch.group_commands.{{ locale }}</id> - <param name="description" type="description">{% trans %}Commands will be added to the currently-selected group.{% endtrans %}</param> - {% for command, description in group_commands %} - <param name="{{ command }}" type="boolean" _gui-text="{{ _(description) }}">false</param> - {% endfor %} - <param name="extension" type="string" gui-hidden="true">group_commands</param> - <effect> - <object-type>all</object-type> - <effects-menu> - <submenu name="Ink/Stitch"> - <submenu name="{% trans %}Commands{% endtrans %}" /> - </submenu> - </effects-menu> - </effect> - <script> - {{ command_tag | safe }} - </script> -</inkscape-extension> -- cgit v1.3.1 From aa24b3a3a215cc536bdac94d8ace667f692ffc45 Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Tue, 29 Jun 2021 19:17:12 +0200 Subject: everywhere patterns --- lib/elements/element.py | 48 ++++++++++++++++++++++++++++++++++++++++++++ lib/elements/satin_column.py | 35 -------------------------------- 2 files changed, 48 insertions(+), 35 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/element.py b/lib/elements/element.py index 9b894d89..aa0c4795 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -9,6 +9,7 @@ from copy import deepcopy import inkex import tinycss2 from inkex import bezier +from shapely import geometry as shgeo from ..commands import find_commands from ..i18n import _ @@ -330,6 +331,52 @@ class EmbroideryElement(object): else: return None + @cache + def get_patterns(self): + xpath = "./parent::svg:g/*[contains(@style, 'marker-start:url(#inkstitch-pattern-marker)')]" + patterns = self.node.xpath(xpath, namespaces=inkex.NSS) + line_strings = [] + for pattern in patterns: + if pattern.tag not in EMBROIDERABLE_TAGS: + continue + d = pattern.get_path() + path = inkex.paths.Path(d).to_superpath() + path = apply_transforms(path, pattern) + path = self.flatten(path) + lines = [shgeo.LineString(p) for p in path] + for line in lines: + line_strings.append(line) + return shgeo.MultiLineString(line_strings) + + def _apply_patterns(self, patches): + patterns = self.get_patterns() + if not patterns: + return patches + + patch_points = [] + for patch in patches: + for i, stitch in enumerate(patch.stitches): + patch_points.append(stitch) + if i == len(patch.stitches) - 1: + continue + intersection_points = self._get_pattern_points(stitch, patch.stitches[i+1], patterns) + for point in intersection_points: + patch_points.append(point) + patch.stitches = patch_points + + def _get_pattern_points(self, first, second, patterns): + points = [] + for pattern in patterns: + intersection = shgeo.LineString([first, second]).intersection(pattern) + if isinstance(intersection, shgeo.Point): + points.append(Point(intersection.x, intersection.y)) + if isinstance(intersection, shgeo.MultiPoint): + for point in intersection: + points.append(Point(point.x, point.y)) + # sort points after their distance to left + points.sort(key=lambda point: point.distance(first)) + return points + def strip_control_points(self, subpath): return [point for control_before, point, control_after in subpath] @@ -362,6 +409,7 @@ class EmbroideryElement(object): self.validate() patches = self.to_patches(last_patch) + self._apply_patterns(patches) for patch in patches: patch.tie_modus = self.ties diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 77cb7d22..00a8500a 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -578,10 +578,6 @@ class SatinColumn(EmbroideryElement): return SatinColumn(node) def get_patterns(self): - # TODO: which one is better?!? - # All child groups of pattern - # xpath = "./ancestor::svg:g//*[contains(@style, 'marker-start:url(#inkstitch-pattern-marker)')]" - # Only direct siblings of pattern xpath = "./parent::svg:g/*[contains(@style, 'marker-start:url(#inkstitch-pattern-marker)')]" patterns = self.node.xpath(xpath, namespaces=NSS) line_strings = [] @@ -828,20 +824,6 @@ class SatinColumn(EmbroideryElement): return patch - def do_pattern_satin(self, patterns): - # elements with the attribute 'inkstitch:pattern' set to this elements id will cause extra stitches to be added - patch = Patch(color=self.color) - sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) - for i, (left, right) in enumerate(zip(*sides)): - patch.add_stitch(left) - for point in self._get_pattern_points(left, right, patterns): - patch.add_stitch(point) - patch.add_stitch(right) - if not i+1 >= len(sides[0]): - for point in self._get_pattern_points(right, sides[0][i+1], patterns): - patch.add_stitch(point) - return patch - def do_split_stitch(self): # stitches exceeding the maximum stitch length will be divided into equal parts through additional stitches patch = Patch(color=self.color) @@ -859,22 +841,8 @@ class SatinColumn(EmbroideryElement): points, count = self._get_split_points(right, sides[0][i+1], count) for point in points: patch.add_stitch(point) - return patch - def _get_pattern_points(self, left, right, patterns): - points = [] - for pattern in patterns: - intersection = shgeo.LineString([left, right]).intersection(pattern) - if isinstance(intersection, shgeo.Point): - points.append(Point(intersection.x, intersection.y)) - if isinstance(intersection, shgeo.MultiPoint): - for point in intersection: - points.append(Point(point.x, point.y)) - # sort points after their distance to left - points.sort(key=lambda point: point.distance(left)) - return points - def _get_split_points(self, left, right, count=None): points = [] distance = left.distance(right) @@ -905,13 +873,10 @@ class SatinColumn(EmbroideryElement): # zigzags sit on the contour walk underlay like rail ties on rails. patch += self.do_zigzag_underlay() - patterns = self.get_patterns() if self.e_stitch: patch += self.do_e_stitch() elif self.split_stitch: patch += self.do_split_stitch() - elif self.get_patterns(): - patch += self.do_pattern_satin(patterns) else: patch += self.do_satin() -- cgit v1.3.1 From 20176008f761d0bdcc1a2ab4f863229ed0c32e52 Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Tue, 29 Jun 2021 20:42:16 +0200 Subject: update split pattern --- lib/elements/satin_column.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 00a8500a..dd7df7d1 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -76,16 +76,6 @@ class SatinColumn(EmbroideryElement): def satin_column(self): return self.get_boolean_param("satin_column") - # I18N: Split stitch divides a satin column into equal with parts if the maximum stitch length is exceeded - @property - @param('split_stitch', - _('Split stitch'), - tooltip=_('Sets additional stitches if the satin column exceeds the maximum stitch length.'), - type='boolean', - default='false') - def split_stitch(self): - return self.get_boolean_param("split_stitch") - # I18N: "E" stitch is so named because it looks like the letter E. @property @param('e_stitch', _('"E" stitch'), type='boolean', default='false') @@ -96,10 +86,12 @@ class SatinColumn(EmbroideryElement): @param('max_stitch_length_mm', _('Maximum stitch length'), tooltip=_('Maximum stitch length for split stitches.'), - type='float', unit="mm", - default=12.1) + type='float', unit="mm") def max_stitch_length(self): - return max(self.get_float_param("max_stitch_length_mm", 12.4), 0.1 * PIXELS_PER_MM) + max_stitch_length = self.get_float_param("max_stitch_length_mm") or None + if max_stitch_length: + max_stitch_length *= PIXELS_PER_MM + return self.get_float_param("max_stitch_length_mm") or None @property def color(self): @@ -793,6 +785,9 @@ class SatinColumn(EmbroideryElement): # print >> dbg, "satin", self.zigzag_spacing, self.pull_compensation + if self.max_stitch_length: + return self.do_split_stitch() + patch = Patch(color=self.color) sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) @@ -846,7 +841,7 @@ class SatinColumn(EmbroideryElement): def _get_split_points(self, left, right, count=None): points = [] distance = left.distance(right) - split_count = count or int(distance / self.max_stitch_length) + split_count = count or int(-(-distance // self.max_stitch_length)) for i in range(split_count): line = shgeo.LineString((left, right)) split_point = line.interpolate((i+1)/split_count, normalized=True) @@ -875,8 +870,6 @@ class SatinColumn(EmbroideryElement): if self.e_stitch: patch += self.do_e_stitch() - elif self.split_stitch: - patch += self.do_split_stitch() else: patch += self.do_satin() -- cgit v1.3.1 From 9604e411d4e50b011e4ae41bdcfdf95c45640684 Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Tue, 29 Jun 2021 20:52:46 +0200 Subject: remove get_pattern from satin_column --- lib/elements/satin_column.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index dd7df7d1..a3cc527f 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -6,15 +6,13 @@ from copy import deepcopy from itertools import chain -from inkex import NSS, paths +from inkex import paths from shapely import affinity as shaffinity from shapely import geometry as shgeo 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 ..svg import PIXELS_PER_MM, line_strings_to_csp, point_lists_to_csp from ..utils import Point, cache, collapse_duplicate_point, cut from .element import EmbroideryElement, Patch, param from .validation import ValidationError, ValidationWarning @@ -569,22 +567,6 @@ class SatinColumn(EmbroideryElement): return SatinColumn(node) - def get_patterns(self): - xpath = "./parent::svg:g/*[contains(@style, 'marker-start:url(#inkstitch-pattern-marker)')]" - patterns = self.node.xpath(xpath, namespaces=NSS) - line_strings = [] - for pattern in patterns: - if pattern.tag not in EMBROIDERABLE_TAGS: - continue - d = pattern.get_path() - path = paths.Path(d).to_superpath() - path = apply_transforms(path, pattern) - path = self.flatten(path) - lines = [shgeo.LineString(p) for p in path] - for line in lines: - line_strings.append(line) - return shgeo.MultiLineString(line_strings) - @property @cache def center_line(self): -- cgit v1.3.1 From a152e1edea19ae16f8226032f9cd2ded004b168c Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Tue, 29 Jun 2021 21:00:45 +0200 Subject: only check embroiderable tags for pattern marker --- lib/elements/pattern.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/elements') diff --git a/lib/elements/pattern.py b/lib/elements/pattern.py index c66ffbdc..fa54b7ba 100644 --- a/lib/elements/pattern.py +++ b/lib/elements/pattern.py @@ -6,6 +6,7 @@ import inkex from ..i18n import _ +from ..svg.tags import EMBROIDERABLE_TAGS from .element import EmbroideryElement from .validation import ObjectTypeWarning @@ -33,4 +34,6 @@ class PatternObject(EmbroideryElement): def is_pattern(node): + if node.tag not in EMBROIDERABLE_TAGS: + return False return "marker-start:url(#inkstitch-pattern-marker)" in node.get('style', '') -- cgit v1.3.1 From 52d9ee6a6d97b2ea752f5fdd3080a160a3574f82 Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Wed, 30 Jun 2021 14:05:13 +0200 Subject: structuring --- lib/elements/element.py | 50 ++------------------------ lib/elements/pattern.py | 7 ---- lib/elements/utils.py | 3 +- lib/extensions/__init__.py | 4 +-- lib/extensions/apply_pattern.py | 62 -------------------------------- lib/extensions/base.py | 2 +- lib/extensions/selection_to_pattern.py | 61 +++++++++++++++++++++++++++++++ lib/patterns.py | 66 ++++++++++++++++++++++++++++++++++ templates/apply_pattern.xml | 15 -------- templates/selection_to_pattern.xml | 17 +++++++++ 10 files changed, 151 insertions(+), 136 deletions(-) delete mode 100644 lib/extensions/apply_pattern.py create mode 100644 lib/extensions/selection_to_pattern.py create mode 100644 lib/patterns.py delete mode 100644 templates/apply_pattern.xml create mode 100644 templates/selection_to_pattern.xml (limited to 'lib/elements') diff --git a/lib/elements/element.py b/lib/elements/element.py index aa0c4795..dc466fbf 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -9,10 +9,10 @@ from copy import deepcopy import inkex import tinycss2 from inkex import bezier -from shapely import geometry as shgeo from ..commands import find_commands from ..i18n import _ +from ..patterns import apply_patterns from ..svg import (PIXELS_PER_MM, apply_transforms, convert_length, get_node_transform) from ..svg.tags import (EMBROIDERABLE_TAGS, INKSCAPE_LABEL, INKSTITCH_ATTRIBS, @@ -331,52 +331,6 @@ class EmbroideryElement(object): else: return None - @cache - def get_patterns(self): - xpath = "./parent::svg:g/*[contains(@style, 'marker-start:url(#inkstitch-pattern-marker)')]" - patterns = self.node.xpath(xpath, namespaces=inkex.NSS) - line_strings = [] - for pattern in patterns: - if pattern.tag not in EMBROIDERABLE_TAGS: - continue - d = pattern.get_path() - path = inkex.paths.Path(d).to_superpath() - path = apply_transforms(path, pattern) - path = self.flatten(path) - lines = [shgeo.LineString(p) for p in path] - for line in lines: - line_strings.append(line) - return shgeo.MultiLineString(line_strings) - - def _apply_patterns(self, patches): - patterns = self.get_patterns() - if not patterns: - return patches - - patch_points = [] - for patch in patches: - for i, stitch in enumerate(patch.stitches): - patch_points.append(stitch) - if i == len(patch.stitches) - 1: - continue - intersection_points = self._get_pattern_points(stitch, patch.stitches[i+1], patterns) - for point in intersection_points: - patch_points.append(point) - patch.stitches = patch_points - - def _get_pattern_points(self, first, second, patterns): - points = [] - for pattern in patterns: - intersection = shgeo.LineString([first, second]).intersection(pattern) - if isinstance(intersection, shgeo.Point): - points.append(Point(intersection.x, intersection.y)) - if isinstance(intersection, shgeo.MultiPoint): - for point in intersection: - points.append(Point(point.x, point.y)) - # sort points after their distance to left - points.sort(key=lambda point: point.distance(first)) - return points - def strip_control_points(self, subpath): return [point for control_before, point, control_after in subpath] @@ -409,7 +363,7 @@ class EmbroideryElement(object): self.validate() patches = self.to_patches(last_patch) - self._apply_patterns(patches) + apply_patterns(patches, self.node) for patch in patches: patch.tie_modus = self.ties diff --git a/lib/elements/pattern.py b/lib/elements/pattern.py index fa54b7ba..98f29456 100644 --- a/lib/elements/pattern.py +++ b/lib/elements/pattern.py @@ -6,7 +6,6 @@ import inkex from ..i18n import _ -from ..svg.tags import EMBROIDERABLE_TAGS from .element import EmbroideryElement from .validation import ObjectTypeWarning @@ -31,9 +30,3 @@ class PatternObject(EmbroideryElement): def to_patches(self, last_patch): return [] - - -def is_pattern(node): - if node.tag not in EMBROIDERABLE_TAGS: - return False - return "marker-start:url(#inkstitch-pattern-marker)" in node.get('style', '') diff --git a/lib/elements/utils.py b/lib/elements/utils.py index cd87cec8..99df7002 100644 --- a/lib/elements/utils.py +++ b/lib/elements/utils.py @@ -4,6 +4,7 @@ # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. from ..commands import is_command +from ..patterns import is_pattern from ..svg.tags import (EMBROIDERABLE_TAGS, SVG_IMAGE_TAG, SVG_PATH_TAG, SVG_POLYLINE_TAG, SVG_TEXT_TAG) from .auto_fill import AutoFill @@ -12,7 +13,7 @@ from .element import EmbroideryElement from .empty_d_object import EmptyDObject from .fill import Fill from .image import ImageObject -from .pattern import PatternObject, is_pattern +from .pattern import PatternObject from .polyline import Polyline from .satin_column import SatinColumn from .stroke import Stroke diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 3bd2fef6..7996770d 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -5,7 +5,6 @@ from lib.extensions.troubleshoot import Troubleshoot -from .apply_pattern import ApplyPattern from .auto_satin import AutoSatin from .break_apart import BreakApart from .cleanup import Cleanup @@ -29,6 +28,7 @@ from .params import Params from .print_pdf import Print from .remove_embroidery_settings import RemoveEmbroiderySettings from .reorder import Reorder +from .selection_to_pattern import SelectionToPattern from .simulator import Simulator from .stitch_plan_preview import StitchPlanPreview from .zip import Zip @@ -41,7 +41,7 @@ __all__ = extensions = [StitchPlanPreview, Output, Zip, Flip, - ApplyPattern, + SelectionToPattern, ObjectCommands, LayerCommands, GlobalCommands, diff --git a/lib/extensions/apply_pattern.py b/lib/extensions/apply_pattern.py deleted file mode 100644 index ad881604..00000000 --- a/lib/extensions/apply_pattern.py +++ /dev/null @@ -1,62 +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 ..i18n import _ -from ..svg.tags import SVG_DEFS_TAG -from .base import InkstitchExtension - - -class ApplyPattern(InkstitchExtension): - # This extension will mark selected - - def effect(self): - if not self.get_elements(): - return - - if not self.svg.selected: - inkex.errormsg(_("Please select at least one object to be marked as a pattern.")) - return - - for pattern in self.svg.selected.values(): - self.set_marker(pattern) - - def set_marker(self, node): - xpath = ".//marker[@id='inkstitch-pattern-marker']" - pattern_marker = self.document.xpath(xpath) - - if not pattern_marker: - # get or create def element - defs = self.document.find(SVG_DEFS_TAG) - if defs is None: - defs = etree.SubElement(self.document, SVG_DEFS_TAG) - - # insert marker - marker = """<marker - refX="10" - refY="5" - orient="auto" - id="inkstitch-pattern-marker"> - <g - id="inkstitch-pattern-group"> - <path - style="fill:#fafafa;stroke:#ff5500;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0;stroke-opacity:1;fill-opacity:0.8;" - d="M 10.12911,5.2916678 A 4.8374424,4.8374426 0 0 1 5.2916656,10.12911 4.8374424,4.8374426 0 0 1 0.45422399,5.2916678 4.8374424,4.8374426 0 0 1 5.2916656,0.45422399 4.8374424,4.8374426 0 0 1 10.12911,5.2916678 Z" - id="inkstitch-pattern-marker-circle" /> - <path - style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-miterlimit:4;" - id="inkstitch-pattern-marker-spiral" - d="M 4.9673651,5.7245662 C 4.7549848,5.7646159 4.6247356,5.522384 4.6430021,5.3419847 4.6765851,5.0103151 5.036231,4.835347 5.3381858,4.8987426 5.7863901,4.9928495 6.0126802,5.4853625 5.9002872,5.9065088 5.7495249,6.4714237 5.1195537,6.7504036 4.5799191,6.5874894 3.898118,6.3816539 3.5659013,5.6122905 3.7800789,4.9545192 4.0402258,4.1556558 4.9498996,3.7699484 5.7256318,4.035839 6.6416744,4.3498087 7.0810483,5.4003986 6.7631909,6.2939744 6.395633,7.3272552 5.2038143,7.8204128 4.1924535,7.4503931 3.0418762,7.0294421 2.4948761,5.6961604 2.9171752,4.567073 3.3914021,3.2991406 4.8663228,2.6982592 6.1130974,3.1729158 7.4983851,3.7003207 8.1531869,5.3169977 7.6260947,6.6814205 7.0456093,8.1841025 5.2870784,8.8928844 3.8050073,8.3132966 2.1849115,7.6797506 1.4221671,5.7793073 2.0542715,4.1796074 2.7408201,2.4420977 4.7832541,1.6253548 6.5005435,2.310012 8.3554869,3.0495434 9.2262638,5.2339874 8.4890181,7.0688861 8.4256397,7.2266036 8.3515789,7.379984 8.2675333,7.5277183" /> - </g> - </marker>""" # noqa: E501 - defs.append(etree.fromstring(marker)) - - # attach marker to node - style = node.get('style', '').split(";") - 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 1c10cd4a..862d031e 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -15,8 +15,8 @@ from stringcase import snakecase from ..commands import is_command, layer_commands from ..elements import EmbroideryElement, nodes_to_elements from ..elements.clone import is_clone -from ..elements.pattern import is_pattern from ..i18n import _ +from ..patterns import is_pattern from ..svg import generate_unique_id from ..svg.tags import (CONNECTOR_TYPE, EMBROIDERABLE_TAGS, INKSCAPE_GROUPMODE, NOT_EMBROIDERABLE_TAGS, SVG_DEFS_TAG, SVG_GROUP_TAG) diff --git a/lib/extensions/selection_to_pattern.py b/lib/extensions/selection_to_pattern.py new file mode 100644 index 00000000..3527cc5e --- /dev/null +++ b/lib/extensions/selection_to_pattern.py @@ -0,0 +1,61 @@ +# 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 ..i18n import _ +from ..svg.tags import SVG_DEFS_TAG +from .base import InkstitchExtension + + +class SelectionToPattern(InkstitchExtension): + + def effect(self): + if not self.get_elements(): + return + + if not self.svg.selected: + inkex.errormsg(_("Please select at least one object to be marked as a pattern.")) + return + + for pattern in self.svg.selected.values(): + self.set_marker(pattern) + + def set_marker(self, node): + xpath = ".//marker[@id='inkstitch-pattern-marker']" + pattern_marker = self.document.xpath(xpath) + + if not pattern_marker: + # get or create def element + defs = self.document.find(SVG_DEFS_TAG) + if defs is None: + defs = etree.SubElement(self.document, SVG_DEFS_TAG) + + # insert marker + marker = """<marker + refX="10" + refY="5" + orient="auto" + id="inkstitch-pattern-marker"> + <g + id="inkstitch-pattern-group"> + <path + style="fill:#fafafa;stroke:#ff5500;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0;stroke-opacity:1;fill-opacity:0.8;" + d="M 10.12911,5.2916678 A 4.8374424,4.8374426 0 0 1 5.2916656,10.12911 4.8374424,4.8374426 0 0 1 0.45422399,5.2916678 4.8374424,4.8374426 0 0 1 5.2916656,0.45422399 4.8374424,4.8374426 0 0 1 10.12911,5.2916678 Z" + id="inkstitch-pattern-marker-circle" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-miterlimit:4;" + id="inkstitch-pattern-marker-spiral" + d="M 4.9673651,5.7245662 C 4.7549848,5.7646159 4.6247356,5.522384 4.6430021,5.3419847 4.6765851,5.0103151 5.036231,4.835347 5.3381858,4.8987426 5.7863901,4.9928495 6.0126802,5.4853625 5.9002872,5.9065088 5.7495249,6.4714237 5.1195537,6.7504036 4.5799191,6.5874894 3.898118,6.3816539 3.5659013,5.6122905 3.7800789,4.9545192 4.0402258,4.1556558 4.9498996,3.7699484 5.7256318,4.035839 6.6416744,4.3498087 7.0810483,5.4003986 6.7631909,6.2939744 6.395633,7.3272552 5.2038143,7.8204128 4.1924535,7.4503931 3.0418762,7.0294421 2.4948761,5.6961604 2.9171752,4.567073 3.3914021,3.2991406 4.8663228,2.6982592 6.1130974,3.1729158 7.4983851,3.7003207 8.1531869,5.3169977 7.6260947,6.6814205 7.0456093,8.1841025 5.2870784,8.8928844 3.8050073,8.3132966 2.1849115,7.6797506 1.4221671,5.7793073 2.0542715,4.1796074 2.7408201,2.4420977 4.7832541,1.6253548 6.5005435,2.310012 8.3554869,3.0495434 9.2262638,5.2339874 8.4890181,7.0688861 8.4256397,7.2266036 8.3515789,7.379984 8.2675333,7.5277183" /> + </g> + </marker>""" # noqa: E501 + defs.append(etree.fromstring(marker)) + + # attach marker to node + style = node.get('style', '').split(";") + 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/patterns.py b/lib/patterns.py new file mode 100644 index 00000000..8872a8ce --- /dev/null +++ b/lib/patterns.py @@ -0,0 +1,66 @@ +# Authors: see git history +# +# Copyright (c) 2010 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +import inkex +from shapely import geometry as shgeo + +from .svg import apply_transforms +from .svg.tags import EMBROIDERABLE_TAGS +from .utils import Point + + +def is_pattern(node): + if node.tag not in EMBROIDERABLE_TAGS: + return False + return "marker-start:url(#inkstitch-pattern-marker)" in node.get('style', '') + + +def apply_patterns(patches, node): + patterns = _get_patterns(node) + if not patterns: + return patches + + patch_points = [] + for patch in patches: + for i, stitch in enumerate(patch.stitches): + patch_points.append(stitch) + if i == len(patch.stitches) - 1: + continue + intersection_points = _get_pattern_points(stitch, patch.stitches[i+1], patterns) + for point in intersection_points: + patch_points.append(point) + patch.stitches = patch_points + + +def _get_patterns(node): + xpath = "./parent::svg:g/*[contains(@style, 'marker-start:url(#inkstitch-pattern-marker)')]" + patterns = node.xpath(xpath, namespaces=inkex.NSS) + line_strings = [] + for pattern in patterns: + if pattern.tag not in EMBROIDERABLE_TAGS: + continue + d = pattern.get_path() + path = inkex.paths.Path(d).to_superpath() + path = apply_transforms(path, pattern) + inkex.bezier.cspsubdiv(path, 0.1) + path = [[point for control_before, point, control_after in subpath] for subpath in path] + lines = [shgeo.LineString(p) for p in path] + for line in lines: + line_strings.append(line) + return shgeo.MultiLineString(line_strings) + + +def _get_pattern_points(first, second, patterns): + points = [] + for pattern in patterns: + intersection = shgeo.LineString([first, second]).intersection(pattern) + if isinstance(intersection, shgeo.Point): + points.append(Point(intersection.x, intersection.y)) + if isinstance(intersection, shgeo.MultiPoint): + for point in intersection: + points.append(Point(point.x, point.y)) + # sort points after their distance to left + points.sort(key=lambda point: point.distance(first)) + return points diff --git a/templates/apply_pattern.xml b/templates/apply_pattern.xml deleted file mode 100644 index cbd83dbc..00000000 --- a/templates/apply_pattern.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> - <name>{% trans %}Apply Pattern{% endtrans %}</name> - <id>org.inkstitch.apply_pattern.{{ locale }}</id> - <param name="extension" type="string" gui-hidden="true">apply_pattern</param> - <effect> - <object-type>all</object-type> - <effects-menu> - <submenu name="Ink/Stitch" /> - </effects-menu> - </effect> - <script> - {{ command_tag | safe }} - </script> -</inkscape-extension> diff --git a/templates/selection_to_pattern.xml b/templates/selection_to_pattern.xml new file mode 100644 index 00000000..859a51ed --- /dev/null +++ b/templates/selection_to_pattern.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> + <name>{% trans %}Selection to pattern{% endtrans %}</name> + <id>org.inkstitch.selection_to_pattern.{{ locale }}</id> + <param name="extension" type="string" gui-hidden="true">selection_to_pattern</param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu name="Ink/Stitch"> + <submenu name="{% trans %}Edit{% endtrans %}" /> + </submenu> + </effects-menu> + </effect> + <script> + {{ command_tag | safe }} + </script> +</inkscape-extension> -- cgit v1.3.1 From 8e229770a5b5d28f68e065f61af3452f7e25c6b5 Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Wed, 30 Jun 2021 14:12:27 +0200 Subject: corrections --- lib/elements/satin_column.py | 5 +---- lib/svg/tags.py | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index a3cc527f..02d7a36c 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -12,7 +12,7 @@ from shapely import geometry as shgeo from shapely.ops import nearest_points from ..i18n import _ -from ..svg import PIXELS_PER_MM, line_strings_to_csp, point_lists_to_csp +from ..svg import line_strings_to_csp, point_lists_to_csp from ..utils import Point, cache, collapse_duplicate_point, cut from .element import EmbroideryElement, Patch, param from .validation import ValidationError, ValidationWarning @@ -86,9 +86,6 @@ class SatinColumn(EmbroideryElement): tooltip=_('Maximum stitch length for split stitches.'), type='float', unit="mm") def max_stitch_length(self): - max_stitch_length = self.get_float_param("max_stitch_length_mm") or None - if max_stitch_length: - max_stitch_length *= PIXELS_PER_MM return self.get_float_param("max_stitch_length_mm") or None @property diff --git a/lib/svg/tags.py b/lib/svg/tags.py index cacf8b78..5c1d892a 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -86,7 +86,6 @@ inkstitch_attribs = [ 'zigzag_underlay_inset_mm', 'zigzag_underlay_spacing_mm', 'e_stitch', - 'split_stitch', 'pull_compensation_mm', 'stroke_first', # Legacy -- cgit v1.3.1 From 8eba84a239ce655f32f131d6a82e3142693edd47 Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Wed, 30 Jun 2021 21:47:33 +0200 Subject: update pattern troubleshoot description --- lib/elements/pattern.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/pattern.py b/lib/elements/pattern.py index 98f29456..a32e72c0 100644 --- a/lib/elements/pattern.py +++ b/lib/elements/pattern.py @@ -12,13 +12,14 @@ from .validation import ObjectTypeWarning class PatternWarning(ObjectTypeWarning): name = _("Pattern Element") - description = _("This element will only be stitched out as a pattern within the specified object.") + description = _("This element will not be embroidered. " + "It will appear as a pattern in objects of it's group in the object panel. " + "Sub-group objects will be ignored.") steps_to_solve = [ - _("If you want to remove the pattern configuration for a pattern object follow these steps:"), - _("* Select pattern element(s)"), - _('* Run Extensions > Ink/Stitch > Troubleshoot > Remove embroidery settings...'), - _('* Make sure "Remove params" is enables'), - _('* Click "Apply"') + _("Turn the pattern marker off:"), + _('* Open the Fill and Stroke panel (Objects > Fill and Stroke)'), + _('* Go to the Stroke style tab'), + _('* Under "Markers" choose the first (empty) option in the first dropdown list.') ] -- cgit v1.3.1 From 736e10240a5c8357fac449371f8ac7451b7696b0 Mon Sep 17 00:00:00 2001 From: Lex Neva <lexelby@users.noreply.github.com> Date: Wed, 30 Jun 2021 19:48:51 -0400 Subject: slight wording adjustment --- lib/elements/pattern.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/pattern.py b/lib/elements/pattern.py index a32e72c0..95ce81a1 100644 --- a/lib/elements/pattern.py +++ b/lib/elements/pattern.py @@ -13,10 +13,10 @@ from .validation import ObjectTypeWarning class PatternWarning(ObjectTypeWarning): name = _("Pattern Element") description = _("This element will not be embroidered. " - "It will appear as a pattern in objects of it's group in the object panel. " - "Sub-group objects will be ignored.") + "It will appear as a pattern applied to objects in the same group as it. " + "Objects in sub-groups will be ignored.") steps_to_solve = [ - _("Turn the pattern marker off:"), + _("To disable pattern mode, remove the pattern marker:"), _('* Open the Fill and Stroke panel (Objects > Fill and Stroke)'), _('* Go to the Stroke style tab'), _('* Under "Markers" choose the first (empty) option in the first dropdown list.') -- cgit v1.3.1 From c1e6558f7852def419adfbeb087b2194e6030a2c Mon Sep 17 00:00:00 2001 From: Lex Neva <github.com@lexneva.name> Date: Sat, 7 Aug 2021 10:57:53 -0400 Subject: rename Patch to StitchGroup --- lib/elements/auto_fill.py | 4 ++-- lib/elements/element.py | 10 +++++----- lib/elements/fill.py | 4 ++-- lib/elements/polyline.py | 4 ++-- lib/elements/satin_column.py | 16 ++++++++-------- lib/elements/stroke.py | 6 +++--- lib/gui/simulator.py | 2 +- lib/stitch_plan/stitch_plan.py | 4 ++-- 8 files changed, 25 insertions(+), 25 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index 69533f62..29ca3545 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -12,7 +12,7 @@ from shapely import geometry as shgeo from ..i18n import _ from ..stitches import auto_fill from ..utils import cache, version -from .element import Patch, param +from .element import StitchGroup, param from .fill import Fill from .validation import ValidationWarning @@ -260,7 +260,7 @@ class AutoFill(Fill): self.fatal(message) - return [Patch(stitches=stitches, color=self.color)] + return [StitchGroup(stitches=stitches, color=self.color)] def validation_warnings(self): if self.shape.area < 20: diff --git a/lib/elements/element.py b/lib/elements/element.py index dc466fbf..bcb59bf4 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -20,7 +20,7 @@ from ..svg.tags import (EMBROIDERABLE_TAGS, INKSCAPE_LABEL, INKSTITCH_ATTRIBS, from ..utils import Point, cache -class Patch: +class StitchGroup: """A raw collection of stitches with attached instructions.""" def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, tie_modus=0, stitch_as_is=False): @@ -32,10 +32,10 @@ class Patch: self.stitch_as_is = stitch_as_is def __add__(self, other): - if isinstance(other, Patch): - return Patch(self.color, self.stitches + other.stitches) + if isinstance(other, StitchGroup): + return StitchGroup(self.color, self.stitches + other.stitches) else: - raise TypeError("Patch can only be added to another Patch") + raise TypeError("StitchGroup can only be added to another StitchGroup") def __len__(self): # This method allows `len(patch)` and `if patch: @@ -45,7 +45,7 @@ class Patch: self.stitches.append(stitch) def reverse(self): - return Patch(self.color, self.stitches[::-1]) + return StitchGroup(self.color, self.stitches[::-1]) class Param(object): diff --git a/lib/elements/fill.py b/lib/elements/fill.py index b6799165..75a86ffd 100644 --- a/lib/elements/fill.py +++ b/lib/elements/fill.py @@ -14,7 +14,7 @@ from ..i18n import _ from ..stitches import legacy_fill from ..svg import PIXELS_PER_MM from ..utils import cache -from .element import EmbroideryElement, Patch, param +from .element import EmbroideryElement, StitchGroup, param from .validation import ValidationError @@ -198,4 +198,4 @@ class Fill(EmbroideryElement): self.flip, self.staggers, self.skip_last) - return [Patch(stitches=stitch_list, color=self.color) for stitch_list in stitch_lists] + return [StitchGroup(stitches=stitch_list, color=self.color) for stitch_list in stitch_lists] diff --git a/lib/elements/polyline.py b/lib/elements/polyline.py index 5ea00508..f63dfc3b 100644 --- a/lib/elements/polyline.py +++ b/lib/elements/polyline.py @@ -9,7 +9,7 @@ from shapely import geometry as shgeo from ..i18n import _ from ..utils import cache from ..utils.geometry import Point -from .element import EmbroideryElement, Patch, param +from .element import EmbroideryElement, StitchGroup, param from .validation import ValidationWarning @@ -101,7 +101,7 @@ class Polyline(EmbroideryElement): yield PolylineWarning(self.points[0]) def to_patches(self, last_patch): - patch = Patch(color=self.color) + patch = StitchGroup(color=self.color) for stitch in self.stitches: patch.add_stitch(Point(*stitch)) diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 02d7a36c..3d0e7ff5 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -14,7 +14,7 @@ from shapely.ops import nearest_points from ..i18n import _ from ..svg import line_strings_to_csp, point_lists_to_csp from ..utils import Point, cache, collapse_duplicate_point, cut -from .element import EmbroideryElement, Patch, param +from .element import EmbroideryElement, StitchGroup, param from .validation import ValidationError, ValidationWarning @@ -716,7 +716,7 @@ class SatinColumn(EmbroideryElement): # other. forward, back = self.plot_points_on_rails(self.contour_underlay_stitch_length, -self.contour_underlay_inset) - return Patch(color=self.color, stitches=(forward + list(reversed(back)))) + return StitchGroup(color=self.color, stitches=(forward + list(reversed(back)))) def do_center_walk(self): # Center walk underlay is just a running stitch down and back on the @@ -725,7 +725,7 @@ class SatinColumn(EmbroideryElement): # Do it like contour underlay, but inset all the way to the center. forward, back = self.plot_points_on_rails(self.center_walk_underlay_stitch_length, -100000) - return Patch(color=self.color, stitches=(forward + list(reversed(back)))) + return StitchGroup(color=self.color, stitches=(forward + list(reversed(back)))) def do_zigzag_underlay(self): # zigzag underlay, usually done at a much lower density than the @@ -738,7 +738,7 @@ class SatinColumn(EmbroideryElement): # "German underlay" described here: # http://www.mrxstitch.com/underlay-what-lies-beneath-machine-embroidery/ - patch = Patch(color=self.color) + patch = StitchGroup(color=self.color) sides = self.plot_points_on_rails(self.zigzag_underlay_spacing / 2.0, -self.zigzag_underlay_inset) @@ -767,7 +767,7 @@ class SatinColumn(EmbroideryElement): if self.max_stitch_length: return self.do_split_stitch() - patch = Patch(color=self.color) + patch = StitchGroup(color=self.color) sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) @@ -785,7 +785,7 @@ class SatinColumn(EmbroideryElement): # print >> dbg, "satin", self.zigzag_spacing, self.pull_compensation - patch = Patch(color=self.color) + patch = StitchGroup(color=self.color) sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) @@ -800,7 +800,7 @@ class SatinColumn(EmbroideryElement): def do_split_stitch(self): # stitches exceeding the maximum stitch length will be divided into equal parts through additional stitches - patch = Patch(color=self.color) + patch = StitchGroup(color=self.color) sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) for i, (left, right) in enumerate(zip(*sides)): patch.add_stitch(left) @@ -834,7 +834,7 @@ class SatinColumn(EmbroideryElement): # beziers. The boundary points between beziers serve as "checkpoints", # allowing the user to control how the zigzags flow around corners. - patch = Patch(color=self.color) + patch = StitchGroup(color=self.color) if self.center_walk_underlay: patch += self.do_center_walk() diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index cf034d21..edd5525a 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -11,7 +11,7 @@ from ..i18n import _ from ..stitches import bean_stitch, running_stitch from ..svg import parse_length_with_units from ..utils import Point, cache -from .element import EmbroideryElement, Patch, param +from .element import EmbroideryElement, StitchGroup, param warned_about_legacy_running_stitch = False @@ -190,7 +190,7 @@ class Stroke(EmbroideryElement): stitches = running_stitch(repeated_path, stitch_length) - return Patch(self.color, stitches) + return StitchGroup(self.color, stitches) def to_patches(self, last_patch): patches = [] @@ -198,7 +198,7 @@ class Stroke(EmbroideryElement): for path in self.paths: path = [Point(x, y) for x, y in path] if self.manual_stitch_mode: - patch = Patch(color=self.color, stitches=path, stitch_as_is=True) + patch = StitchGroup(color=self.color, stitches=path, stitch_as_is=True) elif self.is_running_stitch(): patch = self.running_stitch(path, self.running_stitch_length) diff --git a/lib/gui/simulator.py b/lib/gui/simulator.py index 3e1f68c5..94be66db 100644 --- a/lib/gui/simulator.py +++ b/lib/gui/simulator.py @@ -733,7 +733,7 @@ class SimulatorPreview(Thread): The parent is expected to be a wx.Window and also implement the following methods: def generate_patches(self, abort_event): - Produce an list of Patch instances. This method will be + Produce an list of StitchGroup instances. This method will be invoked in a background thread and it is expected that it may take awhile. diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py index fc0d3760..d1af5365 100644 --- a/lib/stitch_plan/stitch_plan.py +++ b/lib/stitch_plan/stitch_plan.py @@ -12,9 +12,9 @@ from .ties import add_ties def patches_to_stitch_plan(patches, collapse_len=None, disable_ties=False): # noqa: C901 - """Convert a collection of inkstitch.element.Patch objects to a StitchPlan. + """Convert a collection of inkstitch.element.StitchGroup objects to a StitchPlan. - * applies instructions embedded in the Patch such as trim_after and stop_after + * applies instructions embedded in the StitchGroup such as trim_after and stop_after * adds tie-ins and tie-offs * adds jump-stitches between patches if necessary """ -- cgit v1.3.1 From 84cb4e2c333d331eb863714797a55589f41e51b2 Mon Sep 17 00:00:00 2001 From: Lex Neva <github.com@lexneva.name> Date: Sat, 7 Aug 2021 11:21:13 -0400 Subject: move StitchGroup into lib.stitch_plan --- lib/elements/auto_fill.py | 7 +- lib/elements/element.py | 28 -------- lib/elements/fill.py | 5 +- lib/elements/polyline.py | 5 +- lib/elements/satin_column.py | 5 +- lib/elements/stroke.py | 3 +- lib/stitch_plan/__init__.py | 4 +- lib/stitch_plan/color_block.py | 142 +++++++++++++++++++++++++++++++++++++++ lib/stitch_plan/stitch.py | 3 +- lib/stitch_plan/stitch_group.py | 34 ++++++++++ lib/stitch_plan/stitch_plan.py | 144 +--------------------------------------- 11 files changed, 197 insertions(+), 183 deletions(-) create mode 100644 lib/stitch_plan/color_block.py create mode 100644 lib/stitch_plan/stitch_group.py (limited to 'lib/elements') diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index 3c13a081..e72af1ef 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -9,13 +9,14 @@ import traceback from shapely import geometry as shgeo +from .element import param +from .fill import Fill +from .validation import ValidationWarning from ..i18n import _ +from ..stitch_plan import StitchGroup from ..stitches import auto_fill from ..svg.tags import INKSCAPE_LABEL from ..utils import cache, version -from .element import StitchGroup, param -from .fill import Fill -from .validation import ValidationWarning class SmallShapeWarning(ValidationWarning): diff --git a/lib/elements/element.py b/lib/elements/element.py index 17ed9167..a3577a5c 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -18,34 +18,6 @@ from ..svg.tags import INKSCAPE_LABEL, INKSTITCH_ATTRIBS from ..utils import Point, cache -class StitchGroup: - """A raw collection of stitches with attached instructions.""" - - def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, tie_modus=0, stitch_as_is=False): - self.color = color - self.stitches = stitches or [] - self.trim_after = trim_after - self.stop_after = stop_after - self.tie_modus = tie_modus - self.stitch_as_is = stitch_as_is - - def __add__(self, other): - if isinstance(other, StitchGroup): - return StitchGroup(self.color, self.stitches + other.stitches) - else: - raise TypeError("StitchGroup can only be added to another StitchGroup") - - def __len__(self): - # This method allows `len(patch)` and `if patch: - return len(self.stitches) - - def add_stitch(self, stitch): - self.stitches.append(stitch) - - def reverse(self): - return StitchGroup(self.color, self.stitches[::-1]) - - class Param(object): def __init__(self, name, description, unit=None, values=[], type=None, group=None, inverse=False, options=[], default=None, tooltip=None, sort_index=0): diff --git a/lib/elements/fill.py b/lib/elements/fill.py index 75a86ffd..2d2ae2ed 100644 --- a/lib/elements/fill.py +++ b/lib/elements/fill.py @@ -10,12 +10,13 @@ import re from shapely import geometry as shgeo from shapely.validation import explain_validity +from .element import EmbroideryElement, param +from .validation import ValidationError from ..i18n import _ +from ..stitch_plan import StitchGroup from ..stitches import legacy_fill from ..svg import PIXELS_PER_MM from ..utils import cache -from .element import EmbroideryElement, StitchGroup, param -from .validation import ValidationError class UnconnectedError(ValidationError): diff --git a/lib/elements/polyline.py b/lib/elements/polyline.py index f63dfc3b..aeae17d9 100644 --- a/lib/elements/polyline.py +++ b/lib/elements/polyline.py @@ -6,11 +6,12 @@ from inkex import Path from shapely import geometry as shgeo +from .element import EmbroideryElement, param +from .validation import ValidationWarning from ..i18n import _ +from ..stitch_plan import StitchGroup from ..utils import cache from ..utils.geometry import Point -from .element import EmbroideryElement, StitchGroup, param -from .validation import ValidationWarning class PolylineWarning(ValidationWarning): diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 1f28cb45..e066f3fb 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -11,11 +11,12 @@ from shapely import affinity as shaffinity from shapely import geometry as shgeo from shapely.ops import nearest_points +from .element import EmbroideryElement, param +from .validation import ValidationError, ValidationWarning from ..i18n import _ +from ..stitch_plan import StitchGroup from ..svg import line_strings_to_csp, point_lists_to_csp from ..utils import Point, cache, collapse_duplicate_point, cut -from .element import EmbroideryElement, StitchGroup, param -from .validation import ValidationError, ValidationWarning class SatinHasFillError(ValidationError): diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index 76e80688..9fcb4e21 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -7,11 +7,12 @@ import sys import shapely.geometry +from .element import EmbroideryElement, param from ..i18n import _ +from ..stitch_plan import StitchGroup from ..stitches import bean_stitch, running_stitch from ..svg import parse_length_with_units from ..utils import Point, cache -from .element import EmbroideryElement, StitchGroup, param warned_about_legacy_running_stitch = False diff --git a/lib/stitch_plan/__init__.py b/lib/stitch_plan/__init__.py index 68301e94..ba4729bb 100644 --- a/lib/stitch_plan/__init__.py +++ b/lib/stitch_plan/__init__.py @@ -3,6 +3,8 @@ # Copyright (c) 2010 Authors # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. -from .stitch_plan import patches_to_stitch_plan, StitchPlan, ColorBlock +from .stitch_plan import patches_to_stitch_plan, StitchPlan +from .color_block import ColorBlock +from .stitch_group import StitchGroup from .stitch import Stitch from .read_file import stitch_plan_from_file diff --git a/lib/stitch_plan/color_block.py b/lib/stitch_plan/color_block.py new file mode 100644 index 00000000..1cff8aa4 --- /dev/null +++ b/lib/stitch_plan/color_block.py @@ -0,0 +1,142 @@ +from .stitch import Stitch +from ..threads import ThreadColor +from ..utils.geometry import Point +from ..svg import PIXELS_PER_MM + + +class ColorBlock(object): + """Holds a set of stitches, all with the same thread color.""" + + def __init__(self, color=None, stitches=None): + self.color = color + self.stitches = stitches or [] + + def __iter__(self): + return iter(self.stitches) + + def __len__(self): + return len(self.stitches) + + def __repr__(self): + return "ColorBlock(%s, %s)" % (self.color, self.stitches) + + def __getitem__(self, item): + return self.stitches[item] + + def __delitem__(self, item): + del self.stitches[item] + + def __json__(self): + return dict(color=self.color, stitches=self.stitches) + + def has_color(self): + return self._color is not None + + @property + def color(self): + return self._color + + @color.setter + def color(self, value): + if isinstance(value, ThreadColor): + self._color = value + elif value is None: + self._color = None + else: + self._color = ThreadColor(value) + + @property + def last_stitch(self): + if self.stitches: + return self.stitches[-1] + else: + return None + + @property + def num_stitches(self): + """Number of stitches in this color block.""" + return len(self.stitches) + + @property + def num_trims(self): + """Number of trims in this color block.""" + + return sum(1 for stitch in self if stitch.trim) + + @property + def stop_after(self): + if self.last_stitch is not None: + return self.last_stitch.stop + else: + return False + + @property + def trim_after(self): + # If there's a STOP, it will be at the end. We still want to return + # True. + for stitch in reversed(self.stitches): + if stitch.stop or stitch.jump: + continue + elif stitch.trim: + return True + else: + break + + return False + + def filter_duplicate_stitches(self): + if not self.stitches: + return + + stitches = [self.stitches[0]] + + for stitch in self.stitches[1:]: + if stitches[-1].jump or stitch.stop or stitch.trim or stitch.color_change: + # Don't consider jumps, stops, color changes, or trims as candidates for filtering + pass + else: + length = (stitch - stitches[-1]).length() + if length <= 0.1 * PIXELS_PER_MM: + # duplicate stitch, skip this one + continue + + stitches.append(stitch) + + self.stitches = stitches + + def add_stitch(self, *args, **kwargs): + if not args: + # They're adding a command, e.g. `color_block.add_stitch(stop=True)``. + # Use the position from the last stitch. + if self.last_stitch: + args = (self.last_stitch.x, self.last_stitch.y) + else: + raise ValueError("internal error: can't add a command to an empty stitch block") + + if isinstance(args[0], Stitch): + self.stitches.append(args[0]) + elif isinstance(args[0], Point): + self.stitches.append(Stitch(args[0].x, args[0].y, *args[1:], **kwargs)) + else: + if not args and self.last_stitch: + args = (self.last_stitch.x, self.last_stitch.y) + self.stitches.append(Stitch(*args, **kwargs)) + + def add_stitches(self, stitches, *args, **kwargs): + for stitch in stitches: + if isinstance(stitch, (Stitch, Point)): + self.add_stitch(stitch, *args, **kwargs) + else: + self.add_stitch(*stitch, *args, **kwargs) + + def replace_stitches(self, stitches): + self.stitches = stitches + + @property + def bounding_box(self): + minx = min(stitch.x for stitch in self) + miny = min(stitch.y for stitch in self) + maxx = max(stitch.x for stitch in self) + maxy = max(stitch.y for stitch in self) + + return minx, miny, maxx, maxy diff --git a/lib/stitch_plan/stitch.py b/lib/stitch_plan/stitch.py index ae6fa480..85d71935 100644 --- a/lib/stitch_plan/stitch.py +++ b/lib/stitch_plan/stitch.py @@ -8,8 +8,7 @@ from ..utils.geometry import Point class Stitch(Point): def __init__(self, x, y=None, color=None, jump=False, stop=False, trim=False, color_change=False, tie_modus=0, no_ties=False): - self.x = x - self.y = y + Point.__init__(self, x, y) self.color = color self.jump = jump self.trim = trim diff --git a/lib/stitch_plan/stitch_group.py b/lib/stitch_plan/stitch_group.py new file mode 100644 index 00000000..d1e6bae7 --- /dev/null +++ b/lib/stitch_plan/stitch_group.py @@ -0,0 +1,34 @@ +class StitchGroup: + """A collection of Stitch objects with attached instructions. + + StitchGroups will later be combined to make ColorBlocks, which in turn are + combined to make a StitchPlan. Jump stitches are allowed between + StitchGroups, but not between stitches inside a StitchGroup. This means + that EmbroideryElement classes should produce multiple StitchGroups only if + they want to allow for the possibility of jump stitches to be added in + between them by the stitch plan generation code. + """ + + def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, tie_modus=0, stitch_as_is=False): + self.color = color + self.stitches = stitches or [] + self.trim_after = trim_after + self.stop_after = stop_after + self.tie_modus = tie_modus + self.stitch_as_is = stitch_as_is + + def __add__(self, other): + if isinstance(other, StitchGroup): + return StitchGroup(self.color, self.stitches + other.stitches) + else: + raise TypeError("StitchGroup can only be added to another StitchGroup") + + def __len__(self): + # This method allows `len(patch)` and `if patch: + return len(self.stitches) + + def add_stitch(self, stitch): + self.stitches.append(stitch) + + def reverse(self): + return StitchGroup(self.color, self.stitches[::-1]) diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py index d1af5365..f5981fb9 100644 --- a/lib/stitch_plan/stitch_plan.py +++ b/lib/stitch_plan/stitch_plan.py @@ -3,11 +3,9 @@ # Copyright (c) 2010 Authors # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. -from ..svg import PIXELS_PER_MM -from ..threads import ThreadColor -from ..utils.geometry import Point -from .stitch import Stitch from .ties import add_ties +from .color_block import ColorBlock +from ..svg import PIXELS_PER_MM def patches_to_stitch_plan(patches, collapse_len=None, disable_ties=False): # noqa: C901 @@ -168,141 +166,3 @@ class StitchPlan(object): return self.color_blocks[-1] else: return None - - -class ColorBlock(object): - """Holds a set of stitches, all with the same thread color.""" - - def __init__(self, color=None, stitches=None): - self.color = color - self.stitches = stitches or [] - - def __iter__(self): - return iter(self.stitches) - - def __len__(self): - return len(self.stitches) - - def __repr__(self): - return "ColorBlock(%s, %s)" % (self.color, self.stitches) - - def __getitem__(self, item): - return self.stitches[item] - - def __delitem__(self, item): - del self.stitches[item] - - def __json__(self): - return dict(color=self.color, stitches=self.stitches) - - def has_color(self): - return self._color is not None - - @property - def color(self): - return self._color - - @color.setter - def color(self, value): - if isinstance(value, ThreadColor): - self._color = value - elif value is None: - self._color = None - else: - self._color = ThreadColor(value) - - @property - def last_stitch(self): - if self.stitches: - return self.stitches[-1] - else: - return None - - @property - def num_stitches(self): - """Number of stitches in this color block.""" - return len(self.stitches) - - @property - def num_trims(self): - """Number of trims in this color block.""" - - return sum(1 for stitch in self if stitch.trim) - - @property - def stop_after(self): - if self.last_stitch is not None: - return self.last_stitch.stop - else: - return False - - @property - def trim_after(self): - # If there's a STOP, it will be at the end. We still want to return - # True. - for stitch in reversed(self.stitches): - if stitch.stop or stitch.jump: - continue - elif stitch.trim: - return True - else: - break - - return False - - def filter_duplicate_stitches(self): - if not self.stitches: - return - - stitches = [self.stitches[0]] - - for stitch in self.stitches[1:]: - if stitches[-1].jump or stitch.stop or stitch.trim or stitch.color_change: - # Don't consider jumps, stops, color changes, or trims as candidates for filtering - pass - else: - length = (stitch - stitches[-1]).length() - if length <= 0.1 * PIXELS_PER_MM: - # duplicate stitch, skip this one - continue - - stitches.append(stitch) - - self.stitches = stitches - - def add_stitch(self, *args, **kwargs): - if not args: - # They're adding a command, e.g. `color_block.add_stitch(stop=True)``. - # Use the position from the last stitch. - if self.last_stitch: - args = (self.last_stitch.x, self.last_stitch.y) - else: - raise ValueError("internal error: can't add a command to an empty stitch block") - - if isinstance(args[0], Stitch): - self.stitches.append(args[0]) - elif isinstance(args[0], Point): - self.stitches.append(Stitch(args[0].x, args[0].y, *args[1:], **kwargs)) - else: - if not args and self.last_stitch: - args = (self.last_stitch.x, self.last_stitch.y) - self.stitches.append(Stitch(*args, **kwargs)) - - def add_stitches(self, stitches, *args, **kwargs): - for stitch in stitches: - if isinstance(stitch, (Stitch, Point)): - self.add_stitch(stitch, *args, **kwargs) - else: - self.add_stitch(*(list(stitch) + args), **kwargs) - - def replace_stitches(self, stitches): - self.stitches = stitches - - @property - def bounding_box(self): - minx = min(stitch.x for stitch in self) - miny = min(stitch.y for stitch in self) - maxx = max(stitch.x for stitch in self) - maxy = max(stitch.y for stitch in self) - - return minx, miny, maxx, maxy -- cgit v1.3.1 From 923ff3cb97c764f9999ac908c9b3aa321fd02301 Mon Sep 17 00:00:00 2001 From: Lex Neva <github.com@lexneva.name> Date: Sat, 7 Aug 2021 12:37:17 -0400 Subject: fix more patch references --- lib/api/stitch_plan.py | 2 +- lib/elements/auto_fill.py | 2 +- lib/elements/clone.py | 4 ++-- lib/elements/element.py | 6 +++--- lib/elements/empty_d_object.py | 2 +- lib/elements/fill.py | 2 +- lib/elements/image.py | 2 +- lib/elements/pattern.py | 2 +- lib/elements/polyline.py | 2 +- lib/elements/satin_column.py | 2 +- lib/elements/stroke.py | 2 +- lib/elements/text.py | 2 +- lib/extensions/base.py | 2 +- lib/extensions/output.py | 2 +- lib/extensions/print_pdf.py | 2 +- lib/extensions/stitch_plan_preview.py | 2 +- lib/extensions/zip.py | 2 +- 17 files changed, 20 insertions(+), 20 deletions(-) (limited to 'lib/elements') diff --git a/lib/api/stitch_plan.py b/lib/api/stitch_plan.py index 9c9bb4ac..6d64d781 100644 --- a/lib/api/stitch_plan.py +++ b/lib/api/stitch_plan.py @@ -18,7 +18,7 @@ def get_stitch_plan(): metadata = g.extension.get_inkstitch_metadata() collapse_len = metadata['collapse_len_mm'] - patches = g.extension.elements_to_patches(g.extension.elements) + patches = g.extension.elements_to_stitch_groups(g.extension.elements) stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len) return jsonify(stitch_plan) diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index e72af1ef..f5558cbb 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -213,7 +213,7 @@ class AutoFill(Fill): else: return None - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): stitches = [] starting_point = self.get_starting_point(last_patch) diff --git a/lib/elements/clone.py b/lib/elements/clone.py index 6dafa63d..a9e10d94 100644 --- a/lib/elements/clone.py +++ b/lib/elements/clone.py @@ -93,7 +93,7 @@ class Clone(EmbroideryElement): return elements - def to_patches(self, last_patch=None): + def to_stitch_groups(self, last_patch=None): patches = [] source_node = get_clone_source(self.node) @@ -123,7 +123,7 @@ class Clone(EmbroideryElement): elements = self.clone_to_element(self.node) for element in elements: - patches.extend(element.to_patches(last_patch)) + patches.extend(element.to_stitch_groups(last_patch)) return patches diff --git a/lib/elements/element.py b/lib/elements/element.py index a3577a5c..f06982b2 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -301,13 +301,13 @@ class EmbroideryElement(object): def stop_after(self): return self.get_boolean_param('stop_after', False) - def to_patches(self, last_patch): - raise NotImplementedError("%s must implement to_patches()" % self.__class__.__name__) + def to_stitch_groups(self, last_patch): + raise NotImplementedError("%s must implement to_stitch_groups()" % self.__class__.__name__) def embroider(self, last_patch): self.validate() - patches = self.to_patches(last_patch) + patches = self.to_stitch_groups(last_patch) apply_patterns(patches, self.node) for patch in patches: diff --git a/lib/elements/empty_d_object.py b/lib/elements/empty_d_object.py index 19fb58a4..3c24f333 100644 --- a/lib/elements/empty_d_object.py +++ b/lib/elements/empty_d_object.py @@ -23,5 +23,5 @@ class EmptyDObject(EmbroideryElement): label = self.node.get(INKSCAPE_LABEL) or self.node.get("id") yield EmptyD((0, 0), label) - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): return [] diff --git a/lib/elements/fill.py b/lib/elements/fill.py index 2d2ae2ed..442922b6 100644 --- a/lib/elements/fill.py +++ b/lib/elements/fill.py @@ -190,7 +190,7 @@ class Fill(EmbroideryElement): else: yield InvalidShapeError((x, y)) - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): stitch_lists = legacy_fill(self.shape, self.angle, self.row_spacing, diff --git a/lib/elements/image.py b/lib/elements/image.py index 0828b5ef..73a46871 100644 --- a/lib/elements/image.py +++ b/lib/elements/image.py @@ -29,5 +29,5 @@ class ImageObject(EmbroideryElement): def validation_warnings(self): yield ImageTypeWarning(self.center()) - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): return [] diff --git a/lib/elements/pattern.py b/lib/elements/pattern.py index 95ce81a1..4b92d366 100644 --- a/lib/elements/pattern.py +++ b/lib/elements/pattern.py @@ -29,5 +29,5 @@ class PatternObject(EmbroideryElement): repr_point = next(inkex.Path(self.parse_path()).end_points) yield PatternWarning(repr_point) - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): return [] diff --git a/lib/elements/polyline.py b/lib/elements/polyline.py index aeae17d9..c7a9ea48 100644 --- a/lib/elements/polyline.py +++ b/lib/elements/polyline.py @@ -101,7 +101,7 @@ class Polyline(EmbroideryElement): def validation_warnings(self): yield PolylineWarning(self.points[0]) - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): patch = StitchGroup(color=self.color) for stitch in self.stitches: diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index e066f3fb..b11bb2c3 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -828,7 +828,7 @@ class SatinColumn(EmbroideryElement): points.append(Point(split_point.x, split_point.y)) return [points, split_count] - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): # Stitch a variable-width satin column, zig-zagging between two paths. # The algorithm will draw zigzags between each consecutive pair of diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index 9fcb4e21..763167ad 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -193,7 +193,7 @@ class Stroke(EmbroideryElement): return StitchGroup(self.color, stitches) - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): patches = [] for path in self.paths: diff --git a/lib/elements/text.py b/lib/elements/text.py index dbf76c85..8a3846c0 100644 --- a/lib/elements/text.py +++ b/lib/elements/text.py @@ -29,5 +29,5 @@ class TextObject(EmbroideryElement): def validation_warnings(self): yield TextTypeWarning(self.pointer()) - def to_patches(self, last_patch): + def to_stitch_groups(self, last_patch): return [] diff --git a/lib/extensions/base.py b/lib/extensions/base.py index 476e4969..828e3685 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -188,7 +188,7 @@ class InkstitchExtension(inkex.Effect): selected.append(node) return selected - def elements_to_patches(self, elements): + def elements_to_stitch_groups(self, elements): patches = [] for element in elements: if patches: diff --git a/lib/extensions/output.py b/lib/extensions/output.py index f993f79f..7cc12ee0 100644 --- a/lib/extensions/output.py +++ b/lib/extensions/output.py @@ -52,7 +52,7 @@ class Output(InkstitchExtension): self.metadata = self.get_inkstitch_metadata() collapse_len = self.metadata['collapse_len_mm'] - patches = self.elements_to_patches(self.elements) + patches = self.elements_to_stitch_groups(self.elements) stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len, disable_ties=self.settings.get('laser_mode', False)) temp_file = tempfile.NamedTemporaryFile(suffix=".%s" % self.file_extension, delete=False) diff --git a/lib/extensions/print_pdf.py b/lib/extensions/print_pdf.py index ed796bc7..e5cb25d8 100644 --- a/lib/extensions/print_pdf.py +++ b/lib/extensions/print_pdf.py @@ -302,7 +302,7 @@ class Print(InkstitchExtension): self.metadata = self.get_inkstitch_metadata() collapse_len = self.metadata['collapse_len_mm'] - patches = self.elements_to_patches(self.elements) + patches = self.elements_to_stitch_groups(self.elements) stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len) palette = ThreadCatalog().match_and_apply_palette(stitch_plan, self.get_inkstitch_metadata()['thread-palette']) diff --git a/lib/extensions/stitch_plan_preview.py b/lib/extensions/stitch_plan_preview.py index ac1a4a67..c50fa738 100644 --- a/lib/extensions/stitch_plan_preview.py +++ b/lib/extensions/stitch_plan_preview.py @@ -23,7 +23,7 @@ class StitchPlanPreview(InkstitchExtension): realistic = False self.metadata = self.get_inkstitch_metadata() collapse_len = self.metadata['collapse_len_mm'] - patches = self.elements_to_patches(self.elements) + patches = self.elements_to_stitch_groups(self.elements) stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len) render_stitch_plan(svg, stitch_plan, realistic) diff --git a/lib/extensions/zip.py b/lib/extensions/zip.py index 3f81af71..22654560 100644 --- a/lib/extensions/zip.py +++ b/lib/extensions/zip.py @@ -43,7 +43,7 @@ class Zip(InkstitchExtension): self.metadata = self.get_inkstitch_metadata() collapse_len = self.metadata['collapse_len_mm'] - patches = self.elements_to_patches(self.elements) + patches = self.elements_to_stitch_groups(self.elements) stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len) base_file_name = self.get_base_file_name() -- cgit v1.3.1 From dd865008356d1e04b29a5eb59a8480900f255628 Mon Sep 17 00:00:00 2001 From: Lex Neva <github.com@lexneva.name> Date: Sun, 15 Aug 2021 17:24:59 -0400 Subject: keep underlay, underpath, and border travel --- lib/elements/auto_fill.py | 82 +++++++++++++++++++++++------------------ lib/patterns.py | 6 +++ lib/stitch_plan/stitch_group.py | 13 ++++++- lib/stitches/auto_fill.py | 8 +++- 4 files changed, 71 insertions(+), 38 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index f5558cbb..fbbd86d3 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -214,7 +214,7 @@ class AutoFill(Fill): return None def to_stitch_groups(self, last_patch): - stitches = [] + stitch_groups = [] starting_point = self.get_starting_point(last_patch) ending_point = self.get_ending_point() @@ -222,29 +222,40 @@ class AutoFill(Fill): try: if self.fill_underlay: for i in range(len(self.fill_underlay_angle)): - stitches.extend(auto_fill(self.underlay_shape, - self.fill_underlay_angle[i], - self.fill_underlay_row_spacing, - self.fill_underlay_row_spacing, - self.fill_underlay_max_stitch_length, - self.running_stitch_length, - self.staggers, - self.fill_underlay_skip_last, - starting_point, - underpath=self.underlay_underpath)) - starting_point = stitches[-1] - - stitches.extend(auto_fill(self.fill_shape, - self.angle, - self.row_spacing, - self.end_row_spacing, - self.max_stitch_length, - self.running_stitch_length, - self.staggers, - self.skip_last, - starting_point, - ending_point, - self.underpath)) + underlay = StitchGroup( + color=self.color, + tags=("auto_fill", "auto_fill_underlay"), + stitches=auto_fill( + self.underlay_shape, + self.fill_underlay_angle[i], + self.fill_underlay_row_spacing, + self.fill_underlay_row_spacing, + self.fill_underlay_max_stitch_length, + self.running_stitch_length, + self.staggers, + self.fill_underlay_skip_last, + starting_point, + underpath=self.underlay_underpath)) + stitch_groups.append(underlay) + + starting_point = underlay.stitches[-1] + + stitch_group = StitchGroup( + color=self.color, + tags=("auto_fill", "auto_fill_top"), + stitches=auto_fill( + self.fill_shape, + self.angle, + self.row_spacing, + self.end_row_spacing, + self.max_stitch_length, + self.running_stitch_length, + self.staggers, + self.skip_last, + starting_point, + ending_point, + self.underpath)) + stitch_groups.append(stitch_group) except Exception: if hasattr(sys, 'gettrace') and sys.gettrace(): # if we're debugging, let the exception bubble up @@ -262,18 +273,19 @@ class AutoFill(Fill): self.fatal(message) - return [StitchGroup(stitches=stitches, color=self.color)] + return stitch_groups - def validation_warnings(self): - if self.shape.area < 20: - label = self.node.get(INKSCAPE_LABEL) or self.node.get("id") - yield SmallShapeWarning(self.shape.centroid, label) - if self.shrink_or_grow_shape(self.expand, True).is_empty: - yield ExpandWarning(self.shape.centroid) +def validation_warnings(self): + if self.shape.area < 20: + label = self.node.get(INKSCAPE_LABEL) or self.node.get("id") + yield SmallShapeWarning(self.shape.centroid, label) - if self.shrink_or_grow_shape(-self.fill_underlay_inset, True).is_empty: - yield UnderlayInsetWarning(self.shape.centroid) + if self.shrink_or_grow_shape(self.expand, True).is_empty: + yield ExpandWarning(self.shape.centroid) - for warning in super(AutoFill, self).validation_warnings(): - yield warning + if self.shrink_or_grow_shape(-self.fill_underlay_inset, True).is_empty: + yield UnderlayInsetWarning(self.shape.centroid) + + for warning in super(AutoFill, self).validation_warnings(): + yield warning diff --git a/lib/patterns.py b/lib/patterns.py index d282dc9c..70700f18 100644 --- a/lib/patterns.py +++ b/lib/patterns.py @@ -52,6 +52,12 @@ def _apply_fill_patterns(patterns, patches): elif stitch.has_tag('fill_row_start') or stitch.has_tag('fill_row_end'): # keep points if they are the start or end of a fill stitch row patch_points.append(stitch) + elif stitch.has_tag('auto_fill') and not stitch.has_tag('auto_fill_top'): + # keep auto-fill underlay + patch_points.append(stitch) + elif stitch.has_tag('auto_fill_travel'): + # keep travel stitches (underpath or travel around the border) + patch_points.append(stitch) patch.stitches = patch_points diff --git a/lib/stitch_plan/stitch_group.py b/lib/stitch_plan/stitch_group.py index ee077f26..98d9799e 100644 --- a/lib/stitch_plan/stitch_group.py +++ b/lib/stitch_plan/stitch_group.py @@ -17,7 +17,7 @@ class StitchGroup: between them by the stitch plan generation code. """ - def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, tie_modus=0, stitch_as_is=False): + def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, tie_modus=0, stitch_as_is=False, tags=None): self.color = color self.trim_after = trim_after self.stop_after = stop_after @@ -28,6 +28,9 @@ class StitchGroup: if stitches: self.add_stitches(stitches) + if tags: + self.add_tags(tags) + def __add__(self, other): if isinstance(other, StitchGroup): return StitchGroup(self.color, self.stitches + other.stitches) @@ -51,3 +54,11 @@ class StitchGroup: def reverse(self): return StitchGroup(self.color, self.stitches[::-1]) + + def add_tags(self, tags): + for stitch in self.stitches: + stitch.add_tags(tags) + + def add_tag(self, tag): + for stitch in self.stitches: + stitch.add_tag(tag) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 07361f13..160d927e 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -14,6 +14,7 @@ from shapely.ops import snap from shapely.strtree import STRtree from ..debug import debug +from ..stitch_plan import Stitch from ..svg import PIXELS_PER_MM from ..utils.geometry import Point as InkstitchPoint from ..utils.geometry import line_string_to_point_list @@ -592,9 +593,12 @@ def travel(travel_graph, start, end, running_stitch_length, skip_last): """Create stitches to get from one point on an outline of the shape to another.""" path = networkx.shortest_path(travel_graph, start, end, weight='weight') - path = [InkstitchPoint(*p) for p in path] + path = [Stitch(*p) for p in path] stitches = running_stitch(path, running_stitch_length) + for stitch in stitches: + stitch.add_tag('auto_fill_travel') + # The path's first stitch will start at the end of a row of stitches. We # don't want to double that last stitch, so we'd like to skip it. if skip_last and len(path) > 2: @@ -619,7 +623,7 @@ def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, # If the very first stitch is travel, we'll omit it in travel(), so add it here. if not path[0].is_segment(): - stitches.append(InkstitchPoint(*path[0].nodes[0])) + stitches.append(Stitch(*path[0].nodes[0])) for edge in path: if edge.is_segment(): -- cgit v1.3.1 From b49f7d28314f30727f9f963bddb795b88a95f2bd Mon Sep 17 00:00:00 2001 From: Kaalleen <reni@allenka.de> Date: Mon, 16 Aug 2021 16:30:22 +0200 Subject: keep satin column edges --- lib/elements/satin_column.py | 17 +++++++++++++++-- lib/patterns.py | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'lib/elements') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index b11bb2c3..cf31c2af 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -717,7 +717,10 @@ class SatinColumn(EmbroideryElement): # other. forward, back = self.plot_points_on_rails(self.contour_underlay_stitch_length, -self.contour_underlay_inset) - return StitchGroup(color=self.color, stitches=(forward + list(reversed(back)))) + return StitchGroup( + color=self.color, + tags=("satin_column", "satin_column_underlay", "satin_contour_underlay"), + stitches=(forward + list(reversed(back)))) def do_center_walk(self): # Center walk underlay is just a running stitch down and back on the @@ -726,7 +729,10 @@ class SatinColumn(EmbroideryElement): # Do it like contour underlay, but inset all the way to the center. forward, back = self.plot_points_on_rails(self.center_walk_underlay_stitch_length, -100000) - return StitchGroup(color=self.color, stitches=(forward + list(reversed(back)))) + return StitchGroup( + color=self.color, + tags=("satin_column", "satin_column_underlay", "satin_center_walk"), + stitches=(forward + list(reversed(back)))) def do_zigzag_underlay(self): # zigzag underlay, usually done at a much lower density than the @@ -754,6 +760,7 @@ class SatinColumn(EmbroideryElement): for point in chain.from_iterable(zip(*sides)): patch.add_stitch(point) + patch.add_tags(("satin_column", "satin_column_underlay", "satin_zigzag_underlay")) return patch def do_satin(self): @@ -776,6 +783,7 @@ class SatinColumn(EmbroideryElement): for point in chain.from_iterable(zip(*sides)): patch.add_stitch(point) + patch.add_tags(("satin_column", "satin_column_edge")) return patch def do_e_stitch(self): @@ -797,6 +805,7 @@ class SatinColumn(EmbroideryElement): patch.add_stitch(right) patch.add_stitch(left) + patch.add_tags(("satin_column", "e_stitch")) return patch def do_split_stitch(self): @@ -805,10 +814,13 @@ class SatinColumn(EmbroideryElement): sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) for i, (left, right) in enumerate(zip(*sides)): patch.add_stitch(left) + patch.stitches[-1].add_tags(("satin_column", "satin_column_edge")) points, count = self._get_split_points(left, right) for point in points: patch.add_stitch(point) + patch.stitches[-1].add_tags(("satin_column", "satin_split_stitch")) patch.add_stitch(right) + patch.stitches[-1].add_tags(("satin_column", "satin_column_edge")) # it is possible that the way back has a different length from the first # but it looks ugly if the points differ too much # so let's make sure they have at least the same amount of divisions @@ -816,6 +828,7 @@ class SatinColumn(EmbroideryElement): points, count = self._get_split_points(right, sides[0][i+1], count) for point in points: patch.add_stitch(point) + patch.stitches[-1].add_tags(("satin_column", "satin_split_stitch")) return patch def _get_split_points(self, left, right, count=None): diff --git a/lib/patterns.py b/lib/patterns.py index 70700f18..8a0c8449 100644 --- a/lib/patterns.py +++ b/lib/patterns.py @@ -58,6 +58,9 @@ def _apply_fill_patterns(patterns, patches): elif stitch.has_tag('auto_fill_travel'): # keep travel stitches (underpath or travel around the border) patch_points.append(stitch) + elif stitch.has_tag('satin_column') and not stitch.has_tag('satin_split_stitch'): + # keep satin column stitches unless they are split stitches + patch_points.append(stitch) patch.stitches = patch_points -- cgit v1.3.1