summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaalleen <36401965+kaalleen@users.noreply.github.com>2025-05-20 15:56:44 +0200
committerGitHub <noreply@github.com>2025-05-20 15:56:44 +0200
commit0bbf0f3195037496b5c324de5abbc317b7d79832 (patch)
tree2820e1c8d8a1132674814b6c47825af2cacfd582
parent5af9dc0aee4358cca13d9c845043fbdfa47e8dc4 (diff)
knockdown: add embossing options (#3738)
-rw-r--r--lib/extensions/knockdown_fill.py104
-rw-r--r--templates/knockdown_fill.xml18
2 files changed, 83 insertions, 39 deletions
diff --git a/lib/extensions/knockdown_fill.py b/lib/extensions/knockdown_fill.py
index 8815efa5..fc0b51cc 100644
--- a/lib/extensions/knockdown_fill.py
+++ b/lib/extensions/knockdown_fill.py
@@ -4,7 +4,7 @@
# 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 import minimum_bounding_circle, union_all
from shapely.geometry import LineString, Polygon
from ..stitches.ripple_stitch import ripple_stitch
@@ -24,6 +24,10 @@ class KnockdownFill(InkstitchExtension):
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")
+
+ self.arg_parser.add_argument("-s", "--shape", type=str, default='', dest="shape")
+ self.arg_parser.add_argument("-f", "--shape-offset", type=float, default=0, dest="shape_offset")
+ self.arg_parser.add_argument("-p", "--shape-join-style", type=str, default="1", dest="shape_join_style")
# TODO: Layer options: underlay, row spacing, angle
def effect(self):
@@ -32,46 +36,59 @@ class KnockdownFill(InkstitchExtension):
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'))
+ polygons.extend(self.element_outlines(element))
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),
+
+ offset_shape = self._apply_offset(combined_shape, self.options.offset, self.options.join_style)
+ offset_shape = offset_shape.simplify(0.3)
+ offset_shape = ensure_multi_polygon(offset_shape)
+
+ self.insert_knockdown_elements(offset_shape)
+
+ def element_outlines(self, element):
+ polygons = []
+ 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'))
+ elif element.name == "Clone":
+ with element.clone_elements() as elements:
+ for clone_child in elements:
+ polygons.extend(self.element_outlines(clone_child))
+ return polygons
+
+ def _apply_offset(self, shape, offset_mm, join_style):
+ return shape.buffer(
+ offset_mm * PIXELS_PER_MM,
+ cap_style=int(join_style),
+ join_style=int(join_style),
mitre_limit=float(max(self.options.mitre_limit, 0.1))
)
- 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]
@@ -89,6 +106,17 @@ class KnockdownFill(InkstitchExtension):
for interior in polygon.interiors:
d += str(Path(interior.coords))
+ if self.options.shape == 'square':
+ square = polygon.envelope
+ offset_square = self._apply_offset(square, self.options.shape_offset, self.options.shape_join_style)
+ offset_square = offset_square.reverse()
+ d = str(Path(offset_square.exterior.coords)) + d
+ elif self.options.shape == 'circle':
+ circle = minimum_bounding_circle(polygon)
+ offset_circle = self._apply_offset(circle, self.options.shape_offset, self.options.shape_join_style)
+ offset_circle = offset_circle.reverse()
+ d = str(Path(offset_circle.exterior.coords)) + d
+
path = PathElement()
path.set('d', d)
path.label = self.svg.get_unique_id('Knockdown ')
diff --git a/templates/knockdown_fill.xml b/templates/knockdown_fill.xml
index f1d9cef4..c2420b21 100644
--- a/templates/knockdown_fill.xml
+++ b/templates/knockdown_fill.xml
@@ -17,12 +17,28 @@
<page name="options" gui-text="Options">
<param name="keep-holes" type="bool" gui-text="Keep holes">true</param>
<param name="offset" type="float" gui-text="Offset" min="-50" max="50">1</param>
- <param name="join-style" type="optiongroup" gui-text="Method">
+ <param name="join-style" type="optiongroup" appearance="combo" gui-text="Join style">
<option value="1">Round</option>
<option value="2">Mitre</option>
<option value="3">Bevel</option>
</param>
<param name="mitre-limit" type="float" gui-text="Mitre limit" min="0.1" max="50">5.0</param>
+ <spacer />
+ <separator />
+ <spacer />
+ </page>
+ <page name="embossing" gui-text="Embossing">
+ <param name="shape" type="optiongroup" appearance="combo" gui-text="Shape">
+ <option value="">None</option>
+ <option value="square">Square</option>
+ <option value="circle">Circle</option>
+ </param>
+ <param name="shape-offset" type="float" gui-text="Offset" min="0" max="50">1</param>
+ <param name="shape-join-style" type="optiongroup" appearance="combo" gui-text="Join style">
+ <option value="1">Round</option>
+ <option value="2">Mitre</option>
+ <option value="3">Bevel</option>
+ </param>
</page>
<page name="info" gui-text="Help">
<label appearance="header">This extension generates a knockdown fill area with a specified offset around selected elements.</label>