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
-
+