From e9278c55c34b72bb0beccf0d9b8bfe300aacac70 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 27 Nov 2022 08:37:59 +0100 Subject: This and that (#1727) * dont fail on satin with fill * fill stitch error message * convert to satin mac issue * auto_satin: add rung for two node old style satins * avoid divide by zero in intersect_region_with_grating * fix for incorrect stagger in guided fill * better rail sectioning algorithm * fix #1780 * fix #1816 Co-authored-by: Lex Neva --- lib/extensions/convert_to_satin.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'lib/extensions') diff --git a/lib/extensions/convert_to_satin.py b/lib/extensions/convert_to_satin.py index 93850789..5512a095 100644 --- a/lib/extensions/convert_to_satin.py +++ b/lib/extensions/convert_to_satin.py @@ -129,11 +129,16 @@ class ConvertToSatin(InkstitchExtension): if Point(*path[0]).distance(Point(*path[-1])) < 1: raise SelfIntersectionError() + # Shapely is supposed to return right sided offsets in reversed direction, which it does, except for macOS. + # To avoid direction checking, we are going to rely on left side offsets only. + # Therefore we need to reverse the original path. + reversed_path = shgeo.LineString(reversed(path)) path = shgeo.LineString(path) + distance = stroke_width / 2.0 try: - left_rail = path.parallel_offset(stroke_width / 2.0, 'left', **style_args) - right_rail = path.parallel_offset(stroke_width / 2.0, 'right', **style_args) + left_rail = path.parallel_offset(distance, 'left', **style_args) + right_rail = reversed_path.parallel_offset(distance, 'left', **style_args) except ValueError: # TODO: fix this error automatically # Error reference: https://github.com/inkstitch/inkstitch/issues/964 @@ -149,7 +154,6 @@ class ConvertToSatin(InkstitchExtension): # https://shapely.readthedocs.io/en/latest/manual.html#object.parallel_offset raise SelfIntersectionError() - # for whatever reason, shapely returns a right-side offset's coordinates in reverse left_rail = list(left_rail.coords) right_rail = list(reversed(right_rail.coords)) -- cgit v1.2.3 From 3f346ea5ff0703b0d6b7bc723fec91a92fce0eed Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 27 Nov 2022 08:54:10 +0100 Subject: fix untranslatable trim selection in lettering (#1923) --- lib/extensions/lettering.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/extensions') diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py index 40fd48af..0d18449a 100644 --- a/lib/extensions/lettering.py +++ b/lib/extensions/lettering.py @@ -71,8 +71,8 @@ class LetteringFrame(wx.Frame): self.back_and_forth_checkbox = wx.CheckBox(self, label=_("Stitch lines of text back and forth")) self.back_and_forth_checkbox.Bind(wx.EVT_CHECKBOX, lambda event: self.on_change("back_and_forth", event)) - self.trim_option_choice = wx.Choice(self, choices=["Never", "after each line", "after each word", "after each letter"], - name=_("Add trim after")) + self.trim_option_choice = wx.Choice(self, choices=[_("Never"), _("after each line"), _("after each word"), _("after each letter")], + name=_("Add trim command")) self.trim_option_choice.Bind(wx.EVT_CHOICE, lambda event: self.on_trim_option_change(event)) # text editor -- cgit v1.2.3 From 815b3c60351b8e6572fcacba5d37d4bd6f1c6092 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sat, 10 Dec 2022 10:50:14 +0100 Subject: Add extension: Lettering along path (#1937) --- lib/extensions/__init__.py | 3 + lib/extensions/lettering_along_path.py | 144 +++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 lib/extensions/lettering_along_path.py (limited to 'lib/extensions') diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 5c702ce8..a2261a80 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -48,6 +48,8 @@ from .stitch_plan_preview import StitchPlanPreview from .stitch_plan_preview_undo import StitchPlanPreviewUndo from .zip import Zip +from.lettering_along_path import LetteringAlongPath + __all__ = extensions = [StitchPlanPreview, StitchPlanPreviewUndo, DensityMap, @@ -75,6 +77,7 @@ __all__ = extensions = [StitchPlanPreview, LetteringRemoveKerning, LetteringCustomFontDir, LetteringForceLockStitches, + LetteringAlongPath, LettersToFont, Troubleshoot, RemoveEmbroiderySettings, diff --git a/lib/extensions/lettering_along_path.py b/lib/extensions/lettering_along_path.py new file mode 100644 index 00000000..63ffc817 --- /dev/null +++ b/lib/extensions/lettering_along_path.py @@ -0,0 +1,144 @@ +# 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 json +import sys +from math import atan2, degrees + +from inkex import Boolean, Transform, errormsg +from shapely.ops import substring + +from ..elements import Stroke +from ..i18n import _ +from ..svg import get_correction_transform +from ..svg.tags import EMBROIDERABLE_TAGS, INKSTITCH_LETTERING, SVG_GROUP_TAG +from ..utils import DotDict +from ..utils import Point as InkstitchPoint +from .base import InkstitchExtension + + +class LetteringAlongPath(InkstitchExtension): + ''' + This extension aligns an Ink/Stitch Lettering group along a path + ''' + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("-o", "--options", type=str, default=None, dest="page_1") + self.arg_parser.add_argument("-i", "--info", type=str, default=None, dest="page_2") + self.arg_parser.add_argument("-s", "--stretch-spaces", type=Boolean, default=False, dest="stretch_spaces") + + def effect(self): + # we ignore everything but the first path/text + text, path = self.get_selection() + self.load_settings(text) + + glyphs = [glyph for glyph in text.iterdescendants(SVG_GROUP_TAG) if len(glyph.label) == 1] + if not glyphs: + errormsg(_("The text doesn't contain any glyphs.")) + sys.exit(1) + + path = Stroke(path).as_multi_line_string().geoms[0] + path_length = path.length + + # overall bounding box - get from direct glyph parent + text_bbox = glyphs[0].getparent().bounding_box() + text_y = text_bbox.bottom + + if self.options.stretch_spaces: + text_content = self.settings["text"] + space_indices = [i for i, t in enumerate(text_content) if t == " "] + text_width = text_bbox.width + + if len(text_content) - 1 != 0: + stretch_space = (path_length - text_width) / (len(text_content) - 1) + else: + stretch_space = 0 + else: + stretch_space = 0 + space_indices = [] + + self.transform_glyphs(glyphs, path, stretch_space, space_indices, text_y) + + def transform_glyphs(self, glyphs, path, stretch_space, space_indices, text_y): + text_scale = Transform(f'scale({self.settings["scale"] / 100})') + distance = 0 + old_bbox = None + i = 0 + + for glyph in glyphs: + # dimensions + bbox = glyph.bounding_box() + left = bbox.left + width = bbox.width + + # adjust position + if old_bbox: + right = old_bbox.right + distance += left - right + stretch_space + + if self.options.stretch_spaces and i in space_indices: + distance += stretch_space + i += 1 + + new_distance = distance + width + + # calculate and apply transform + first = substring(path, distance, distance) + last = substring(path, new_distance, new_distance) + + angle = degrees(atan2(last.y - first.y, last.x - first.x)) % 360 + translate = InkstitchPoint(first.x, first.y) - InkstitchPoint(left, text_y) + + transform = Transform(f"rotate({angle}, {first.x}, {first.y}) translate({translate.x} {translate.y})") + correction_transform = Transform(get_correction_transform(glyph)) + glyph.transform = correction_transform @ transform @ glyph.transform @ text_scale + + # set values for next iteration + distance = new_distance + old_bbox = bbox + i += 1 + + def load_settings(self, text): + """Load the settings saved into the text element""" + + self.settings = DotDict({ + "text": "", + "back_and_forth": False, + "font": None, + "scale": 100, + "trim_option": 0 + }) + + if INKSTITCH_LETTERING in text.attrib: + try: + self.settings.update(json.loads(text.get(INKSTITCH_LETTERING))) + except (TypeError, ValueError): + pass + + def get_selection(self): + groups = list() + paths = list() + + for node in self.svg.selection: + lettering = False + if node.tag == SVG_GROUP_TAG and INKSTITCH_LETTERING in node.attrib: + groups.append(node) + lettering = True + continue + + for group in node.iterancestors(SVG_GROUP_TAG): + if INKSTITCH_LETTERING in group.attrib: + groups.append(group) + lettering = True + break + + if not lettering and node.tag in EMBROIDERABLE_TAGS: + paths.append(node) + + if not groups or not paths: + errormsg(_("Please select one path and one Ink/Stitch lettering group.")) + sys.exit(1) + + return [groups[0], paths[0]] -- cgit v1.2.3