From ff073a4bd6172200fe79e0c080751a23afea4ec1 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sat, 22 Feb 2025 14:19:47 +0100 Subject: [PATCH] Add selection to knockdown fill extension (#3526) --- icons/inx/fill_knockdown.svg | 77 ++++++++++++++++++++++ lib/extensions/__init__.py | 2 + lib/extensions/knockdown_fill.py | 106 +++++++++++++++++++++++++++++++ templates/knockdown_fill.xml | 40 ++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 icons/inx/fill_knockdown.svg create mode 100644 lib/extensions/knockdown_fill.py create mode 100644 templates/knockdown_fill.xml diff --git a/icons/inx/fill_knockdown.svg b/icons/inx/fill_knockdown.svg new file mode 100644 index 000000000..2a46ce944 --- /dev/null +++ b/icons/inx/fill_knockdown.svg @@ -0,0 +1,77 @@ + + diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 91c205479..9b25ff918 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -30,6 +30,7 @@ from .install import Install from .install_custom_palette import InstallCustomPalette from .jump_to_stroke import JumpToStroke from .jump_to_trim import JumpToTrim +from .knockdown_fill import KnockdownFill from .layer_commands import LayerCommands from .lettering import Lettering from .lettering_along_path import LetteringAlongPath @@ -103,6 +104,7 @@ __all__ = extensions = [About, InstallCustomPalette, JumpToStroke, JumpToTrim, + KnockdownFill, LayerCommands, Lettering, LetteringAlongPath, diff --git a/lib/extensions/knockdown_fill.py b/lib/extensions/knockdown_fill.py new file mode 100644 index 000000000..35459016d --- /dev/null +++ b/lib/extensions/knockdown_fill.py @@ -0,0 +1,106 @@ +# Authors: see git history +# +# Copyright (c) 2025 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +from inkex import Boolean, Path, PathElement +from shapely import union_all +from shapely.geometry import LineString, Polygon + +from ..stitches.ripple_stitch import ripple_stitch +from ..svg import PIXELS_PER_MM, get_correction_transform +from ..utils.geometry import ensure_multi_polygon +from .base import InkstitchExtension + + +class KnockdownFill(InkstitchExtension): + ''' + This extension generates a shape around all selected shapes and inserts it into the document + ''' + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("--notebook") + self.arg_parser.add_argument("-k", "--keep-holes", type=Boolean, default=True, dest="keep_holes") + self.arg_parser.add_argument("-o", "--offset", type=float, default=0, dest="offset") + self.arg_parser.add_argument("-j", "--join-style", type=str, default="1", dest="join_style") + self.arg_parser.add_argument("-m", "--mitre-limit", type=float, default=5.0, dest="mitre_limit") + # TODO: Layer options: underlay, row spacing, angle + + def effect(self): + if not self.get_elements(): + return + + polygons = [] + for element in self.elements: + if element.name == "FillStitch": + # take expand value into account + shape = element.shrink_or_grow_shape(element.shape, element.expand) + # MultiPolygon + for polygon in shape.geoms: + polygons.append(polygon) + elif element.name == "SatinColumn": + # plot points on rails, so we get the actual satin size (including pull compensation) + rail_pairs = zip(*element.plot_points_on_rails( + 0.3, + element.pull_compensation_px, + element.pull_compensation_percent / 100) + ) + rails = [] + for rail in rail_pairs: + rails.append(LineString(rail)) + polygon = Polygon(list(rails[0].coords) + list(rails[1].reverse().coords)).buffer(0) + polygons.append(polygon) + elif element.name == "Stroke": + if element.stroke_method == 'ripple_stitch': + # for ripples this is going to be a bit complicated, so let's follow the stitch plan + stitches = ripple_stitch(element) + linestring = LineString(stitches) + polygons.append(linestring.buffer(0.15 * PIXELS_PER_MM, cap_style='flat')) + elif element.stroke_method == 'zigzag_stitch': + # zigzag stitch depends on the width of the stroke and pull compensation settings + polygons.append(element.as_multi_line_string().buffer((element.stroke_width + element.pull_compensation) / 2, cap_style='flat')) + else: + polygons.append(element.as_multi_line_string().buffer(0.15 * PIXELS_PER_MM, cap_style='flat')) + combined_shape = union_all(polygons) + combined_shape = combined_shape.buffer( + self.options.offset * PIXELS_PER_MM, + cap_style=int(self.options.join_style), + join_style=int(self.options.join_style), + mitre_limit=int(self.options.mitre_limit) + ) + combined_shape = combined_shape.simplify(0.3) + combined_shape = ensure_multi_polygon(combined_shape) + + self.insert_knockdown_elements(combined_shape) + + def insert_knockdown_elements(self, combined_shape): + first = self.svg.selection.rendering_order()[0] + try: + parent = first.getparent() + index = parent.index(first) + except AttributeError: + parent = self.svg + index = 0 + transform = get_correction_transform(first) + + for polygon in combined_shape.geoms: + d = str(Path(polygon.exterior.coords)) + if self.options.keep_holes: + for interior in polygon.interiors: + d += str(Path(interior.coords)) + + path = PathElement() + path.set('d', d) + path.label = self.svg.get_unique_id('Knockdown ') + path.set('transform', transform) + + path.set('inkstitch:row_spacing_mm', '2.6') + path.set('inkstitch:fill_underlay_angle', '60 -60') + path.set('inkstitch:fill_underlay_max_stitch_length_mm', '3') + path.set('inkstitch:fill_underlay_row_spacing_mm', '2.6') + path.set('inkstitch:underlay_underpath', 'False') + path.set('inkstitch:underpath', 'False') + path.set('inkstitch:staggers', '2') + path.set('style', 'fill:black;') + + parent.insert(index, path) diff --git a/templates/knockdown_fill.xml b/templates/knockdown_fill.xml new file mode 100644 index 000000000..a58f8c0ad --- /dev/null +++ b/templates/knockdown_fill.xml @@ -0,0 +1,40 @@ + + + Selection to Knockdown Fill + org.{{ id_inkstitch }}.knockdown_fill + knockdown_fill + + all + {{ icon_path }}inx/fill_knockdown.svg + Generates a knockdown stitch with a specified offset around selection + + + + + + + + + true + 1 + + + + + + 5.0 + + + + + + + + + + + + +