summaryrefslogtreecommitdiff
path: root/lib/extensions/knockdown_fill.py
diff options
context:
space:
mode:
authorKaalleen <36401965+kaalleen@users.noreply.github.com>2025-02-22 14:19:47 +0100
committerGitHub <noreply@github.com>2025-02-22 14:19:47 +0100
commitff073a4bd6172200fe79e0c080751a23afea4ec1 (patch)
tree76e9a37169d122081daa6bd046c09b7d83afb37a /lib/extensions/knockdown_fill.py
parent10fdc509f526c68acbb2ae2de226bc5f80e88208 (diff)
Add selection to knockdown fill extension (#3526)
Diffstat (limited to 'lib/extensions/knockdown_fill.py')
-rw-r--r--lib/extensions/knockdown_fill.py106
1 files changed, 106 insertions, 0 deletions
diff --git a/lib/extensions/knockdown_fill.py b/lib/extensions/knockdown_fill.py
new file mode 100644
index 00000000..35459016
--- /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)