From 36f7610cc0844a034ba068b549332cab3ef7b033 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Thu, 9 Dec 2021 15:05:21 +0100 Subject: [PATCH] Force lock stitches option/extension and some typos (#1471) --- Makefile | 2 +- lib/elements/element.py | 13 +++ lib/extensions/__init__.py | 2 + .../lettering_force_lock_stitches.py | 86 +++++++++++++++++++ lib/extensions/reorder.py | 2 +- lib/extensions/stitch_plan_preview.py | 2 +- lib/lettering/glyph.py | 2 +- lib/stitch_plan/stitch.py | 26 +++--- lib/stitch_plan/stitch_group.py | 4 +- lib/stitch_plan/stitch_plan.py | 7 +- lib/stitch_plan/ties.py | 2 +- lib/svg/tags.py | 1 + templates/lettering_force_lock_stitches.xml | 30 +++++++ templates/stitch_plan_preview.xml | 2 +- 14 files changed, 161 insertions(+), 20 deletions(-) create mode 100644 lib/extensions/lettering_force_lock_stitches.py create mode 100644 templates/lettering_force_lock_stitches.xml diff --git a/Makefile b/Makefile index 2ed80a17b..23ff6f4f6 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ inx: version locales messages.po: inx rm -f messages.po xgettext inx/*.inx --its=its/inx.its -o messages-inx.po - # There seems to be no propper way to set the charset to utf-8 + # There seems to be no proper way to set the charset to utf-8 sed -i 's/charset=CHARSET/charset=UTF-8/g' messages-inx.po bin/pyembroidery-gettext > pyembroidery-format-descriptions.py bin/inkstitch-fonts-gettext > inkstitch-fonts-metadata.py diff --git a/lib/elements/element.py b/lib/elements/element.py index f06982b20..05bfd353e 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -207,6 +207,18 @@ class EmbroideryElement(object): def ties(self): return self.get_int_param("ties", 0) + @property + @param('force_lock_stitches', + _('Force lock stitches'), + tooltip=_('Sew lock stitches after sewing this element, ' + 'even if the distance to the next object is shorter than defined by the collapse length value in the Ink/Stitch preferences.'), + type='boolean', + default=False, + sort_index=5) + @cache + def force_lock_stitches(self): + return self.get_boolean_param('force_lock_stitches', False) + @property def path(self): # A CSP is a "cubic superpath". @@ -312,6 +324,7 @@ class EmbroideryElement(object): for patch in patches: patch.tie_modus = self.ties + patch.force_lock_stitches = self.force_lock_stitches if patches: patches[-1].trim_after = self.has_command("trim") or self.trim_after diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index ec21b592f..2d3445785 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -24,6 +24,7 @@ from .lettering import Lettering from .lettering_custom_font_dir import LetteringCustomFontDir from .lettering_generate_json import LetteringGenerateJson from .lettering_remove_kerning import LetteringRemoveKerning +from .lettering_force_lock_stitches import LetteringForceLockStitches from .letters_to_font import LettersToFont from .object_commands import ObjectCommands from .output import Output @@ -56,6 +57,7 @@ __all__ = extensions = [StitchPlanPreview, LetteringGenerateJson, LetteringRemoveKerning, LetteringCustomFontDir, + LetteringForceLockStitches, LettersToFont, Troubleshoot, RemoveEmbroiderySettings, diff --git a/lib/extensions/lettering_force_lock_stitches.py b/lib/extensions/lettering_force_lock_stitches.py new file mode 100644 index 000000000..62d7ae145 --- /dev/null +++ b/lib/extensions/lettering_force_lock_stitches.py @@ -0,0 +1,86 @@ +# 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 shapely.geometry import Point + +from ..i18n import _ +from ..svg import PIXELS_PER_MM +from ..svg.tags import INKSTITCH_ATTRIBS +from .base import InkstitchExtension + + +class LetteringForceLockStitches(InkstitchExtension): + ''' + This extension helps font creators to add the force lock stitches attribute to the last objects of each glyph + Font creators to add forced lock stitches on glyphs with accents / spaces. + ''' + + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("-a", "--max_distance", type=float, default=3, dest="max_distance") + self.arg_parser.add_argument("-i", "--min_distance", type=float, default=1, dest="min_distance") + self.arg_parser.add_argument("-l", "--last_element", type=inkex.Boolean, dest="last_element") + + def effect(self): + if self.options.max_distance < self.options.min_distance: + inkex.errormssg(_("The maximum value is smaller than the minimum value.")) + + # Set glyph layers to be visible. We don't want them to be ignored by self.elements + self._update_layer_visibility('inline') + + # mark last elements of a glyph + xpath = ".//svg:g[@inkscape:groupmode='layer']//svg:path[last()]" + last_elements = self.document.xpath(xpath, namespaces=inkex.NSS) + for last_element in last_elements: + last_element.set('lastglyphelement', str(True)) + + # find last point of an element + if not self.get_elements(): + return + + previous_element = None + last_stitch = None + for element in self.elements: + stitch_group = element.to_stitch_groups(None) + # if the distance of the last stitch of the previous object to the first stitch of this objects + # lies within the user defined distance range, set the force_lock_stitches-attribute. + if last_stitch: + first_stitch = stitch_group[0].stitches[0] + first_stitch = Point(first_stitch.x, first_stitch.y) + self._set_force_attribute(first_stitch, last_stitch, previous_element) + + # if this is the last element of a glyph, we don't want to compare it to the next element + if element.node.get('lastglyphelement', False): + previous_element = None + last_stitch = None + else: + previous_element = element + last_stitch = stitch_group[-1].stitches[-1] + last_stitch = Point(last_stitch.x, last_stitch.y) + + # remove last element attributes again + # set force lock stitches attribute if needed + for last_element in last_elements: + last_element.attrib.pop('lastglyphelement') + if self.options.last_element: + last_element.set(INKSTITCH_ATTRIBS['force_lock_stitches'], True) + + # hide glyph layers again + self._update_layer_visibility('none') + + def _set_force_attribute(self, first_stitch, last_stitch, previous_element): + distance_mm = first_stitch.distance(last_stitch) / PIXELS_PER_MM + + if distance_mm < self.options.max_distance and distance_mm > self.options.min_distance: + previous_element.node.set(INKSTITCH_ATTRIBS['force_lock_stitches'], True) + + def _update_layer_visibility(self, display): + xpath = ".//svg:g[@inkscape:groupmode='layer']" + layers = self.document.xpath(xpath, namespaces=inkex.NSS) + for layer in layers: + display_style = 'display:%s' % display + style = inkex.Style(layer.get('style', '')) + inkex.Style(display_style) + layer.set('style', style) diff --git a/lib/extensions/reorder.py b/lib/extensions/reorder.py index 933c1d70d..be478e39b 100644 --- a/lib/extensions/reorder.py +++ b/lib/extensions/reorder.py @@ -7,7 +7,7 @@ from .base import InkstitchExtension class Reorder(InkstitchExtension): - # Remove selected objects from the document and readd them in the order they + # Remove selected objects from the document and re-add them in the order they # were selected. def effect(self): diff --git a/lib/extensions/stitch_plan_preview.py b/lib/extensions/stitch_plan_preview.py index 041686656..e5e570fb3 100644 --- a/lib/extensions/stitch_plan_preview.py +++ b/lib/extensions/stitch_plan_preview.py @@ -41,7 +41,7 @@ class StitchPlanPreview(InkstitchExtension): # apply options layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']") - # update layer visibilty 0 = unchanged, 1 = hidden, 2 = lower opacity + # update layer visibility 0 = unchanged, 1 = hidden, 2 = lower opacity if self.options.layer_visibility == 1: self.hide_all_layers() layer.set('style', None) diff --git a/lib/lettering/glyph.py b/lib/lettering/glyph.py index f50d3bb40..fd97885bd 100644 --- a/lib/lettering/glyph.py +++ b/lib/lettering/glyph.py @@ -88,7 +88,7 @@ class Glyph(object): self.min_x = left def _process_commands(self): - # Save object ids with commmands in a dictionary: {object_id: [connector_id, symbol_id]} + # Save object ids with commands in a dictionary: {object_id: [connector_id, symbol_id]} self.commands = {} for node in self.node.iter(SVG_USE_TAG): diff --git a/lib/stitch_plan/stitch.py b/lib/stitch_plan/stitch.py index f163d09c4..a4c50b60d 100644 --- a/lib/stitch_plan/stitch.py +++ b/lib/stitch_plan/stitch.py @@ -10,7 +10,8 @@ from copy import deepcopy class Stitch(Point): """A stitch is a Point with extra information telling how to sew it.""" - def __init__(self, x, y=None, color=None, jump=False, stop=False, trim=False, color_change=False, tie_modus=0, no_ties=False, tags=None): + def __init__(self, x, y=None, color=None, jump=False, stop=False, trim=False, color_change=False, + tie_modus=0, force_lock_stitches=False, no_ties=False, tags=None): if isinstance(x, Stitch): # Allow creating a Stitch from another Stitch. Attributes passed as # arguments will override any existing attributes. @@ -28,6 +29,7 @@ class Stitch(Point): self.trim = trim self.stop = stop self.color_change = color_change + self.force_lock_stitches = force_lock_stitches self.tie_modus = tie_modus self.no_ties = no_ties self.tags = set() @@ -35,15 +37,16 @@ class Stitch(Point): self.add_tags(tags or []) def __repr__(self): - return "Stitch(%s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.x, - self.y, - self.color, - "JUMP" if self.jump else " ", - "TRIM" if self.trim else " ", - "STOP" if self.stop else " ", - "TIE MODUS" if self.tie_modus else " ", - "NO TIES" if self.no_ties else " ", - "COLOR CHANGE" if self.color_change else " ") + return "Stitch(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.x, + self.y, + self.color, + "JUMP" if self.jump else " ", + "TRIM" if self.trim else " ", + "STOP" if self.stop else " ", + "TIE MODUS" if self.tie_modus else " ", + "FORCE LOCK STITCHES" if self.force_lock_stitches else " ", + "NO TIES" if self.no_ties else " ", + "COLOR CHANGE" if self.color_change else " ") def add_tags(self, tags): for tag in tags: @@ -68,7 +71,8 @@ class Stitch(Point): return tag in self.tags def copy(self): - return Stitch(self.x, self.y, self.color, self.jump, self.stop, self.trim, self.color_change, self.tie_modus, self.no_ties, self.tags) + return Stitch(self.x, self.y, self.color, self.jump, self.stop, self.trim, self.color_change, + self.tie_modus, self.force_lock_stitches, self.no_ties, self.tags) def __json__(self): attributes = dict(vars(self)) diff --git a/lib/stitch_plan/stitch_group.py b/lib/stitch_plan/stitch_group.py index 98d9799ef..21beebe16 100644 --- a/lib/stitch_plan/stitch_group.py +++ b/lib/stitch_plan/stitch_group.py @@ -17,11 +17,13 @@ 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, tags=None): + def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, + tie_modus=0, force_lock_stitches=False, stitch_as_is=False, tags=None): self.color = color self.trim_after = trim_after self.stop_after = stop_after self.tie_modus = tie_modus + self.force_lock_stitches = force_lock_stitches self.stitch_as_is = stitch_as_is self.stitches = [] diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py index 7e7621c17..f68951976 100644 --- a/lib/stitch_plan/stitch_plan.py +++ b/lib/stitch_plan/stitch_plan.py @@ -42,10 +42,13 @@ def stitch_groups_to_stitch_plan(stitch_groups, collapse_len=None, disable_ties= # always start a color with a JUMP to the first stitch position color_block.add_stitch(stitch_group.stitches[0], jump=True) else: - if len(color_block) and (stitch_group.stitches[0] - color_block.stitches[-1]).length() > collapse_len: + if (len(color_block) and + ((stitch_group.stitches[0] - color_block.stitches[-1]).length() > collapse_len or + color_block.stitches[-1].force_lock_stitches)): color_block.add_stitch(stitch_group.stitches[0], jump=True) - color_block.add_stitches(stitches=stitch_group.stitches, tie_modus=stitch_group.tie_modus, no_ties=stitch_group.stitch_as_is) + color_block.add_stitches(stitches=stitch_group.stitches, tie_modus=stitch_group.tie_modus, + force_lock_stitches=stitch_group.force_lock_stitches, no_ties=stitch_group.stitch_as_is) if stitch_group.trim_after: color_block.add_stitch(trim=True) diff --git a/lib/stitch_plan/ties.py b/lib/stitch_plan/ties.py index c649ee441..a95f9805b 100644 --- a/lib/stitch_plan/ties.py +++ b/lib/stitch_plan/ties.py @@ -37,7 +37,7 @@ def add_tie(stitches, tie_path): def add_tie_off(stitches): # tie_modus: 0 = both | 1 = before | 2 = after | 3 = neither - if stitches[-1].tie_modus not in [1, 3]: + if stitches[-1].tie_modus not in [1, 3] or stitches[-1].force_lock_stitches: add_tie(stitches, stitches[-1:-3:-1]) diff --git a/lib/svg/tags.py b/lib/svg/tags.py index 66a707e9f..8b6f02a49 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -49,6 +49,7 @@ SVG_OBJECT_TAGS = (SVG_ELLIPSE_TAG, SVG_CIRCLE_TAG, SVG_RECT_TAG) INKSTITCH_ATTRIBS = {} inkstitch_attribs = [ 'ties', + 'force_lock_stitches', # clone 'clone', # polyline diff --git a/templates/lettering_force_lock_stitches.xml b/templates/lettering_force_lock_stitches.xml new file mode 100644 index 000000000..ee7049ade --- /dev/null +++ b/templates/lettering_force_lock_stitches.xml @@ -0,0 +1,30 @@ + + + Force lock stitches + org.inkstitch.force_lock_stitches + lettering_force_lock_stitches + + all + + + + + + + + Small fonts will sometimes unravel if threads are cut after the embroidery machine has finished the work. + Therefore it is important that also diacritics with a smaller distance to the font body than defined by the collapse length value (default: 3mm) have lock stitches. + This can be achieved by adding a forced lock stitch attribute to them. + + + + This extension has been build to help font authors to define "force lock stitches"-attributes automatically if they are placed in a predefined distance to the next object. + + 1 + 3 + + false + + diff --git a/templates/stitch_plan_preview.xml b/templates/stitch_plan_preview.xml index a40f79e02..ba602aaf4 100644 --- a/templates/stitch_plan_preview.xml +++ b/templates/stitch_plan_preview.xml @@ -12,7 +12,7 @@ true - +