summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/elements/element.py4
-rw-r--r--lib/elements/satin_column.py4
-rw-r--r--lib/elements/utils/stroke_to_satin.py47
-rw-r--r--lib/extensions/stroke_to_satin.py9
4 files changed, 53 insertions, 11 deletions
diff --git a/lib/elements/element.py b/lib/elements/element.py
index d830012f..fdc0233f 100644
--- a/lib/elements/element.py
+++ b/lib/elements/element.py
@@ -467,6 +467,10 @@ class EmbroideryElement(object):
return inkex.Path(d).to_superpath()
+ @property
+ def is_closed_path(self):
+ return isinstance(self.node.get_path()[-1], inkex.paths.ZoneClose)
+
@cache
def parse_path(self):
return apply_transforms(self.path, self.node)
diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py
index 4951f8bf..26b4d534 100644
--- a/lib/elements/satin_column.py
+++ b/lib/elements/satin_column.py
@@ -26,7 +26,7 @@ from ..utils import Point, cache, cut, cut_multiple, offset_points, prng
from ..utils.param import ParamOption
from ..utils.threading import check_stop_flag
from .element import PIXELS_PER_MM, EmbroideryElement, param
-from .utils.stroke_to_satin import convert_path_to_satin
+from .utils.stroke_to_satin import convert_path_to_satin, set_first_node
from .validation import ValidationError, ValidationWarning
@@ -625,6 +625,8 @@ class SatinColumn(EmbroideryElement):
paths = [path for path in self.paths if len(path) > 1]
if len(paths) == 1:
style_args = get_join_style_args(self)
+ if self.is_closed_path:
+ set_first_node(paths, self.stroke_width)
new_satin = convert_path_to_satin(paths[0], self.stroke_width, style_args, rungs_at_nodes=True)
if new_satin:
rails, rungs = new_satin
diff --git a/lib/elements/utils/stroke_to_satin.py b/lib/elements/utils/stroke_to_satin.py
index e75ca5d3..0b8ec819 100644
--- a/lib/elements/utils/stroke_to_satin.py
+++ b/lib/elements/utils/stroke_to_satin.py
@@ -3,18 +3,19 @@
# Copyright (c) 2025 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from numpy import zeros, convolve, int32, diff, setdiff1d, sign
-from math import degrees, acos
-from ...svg import PIXELS_PER_MM
+import sys
+from math import acos, degrees
-from ...utils import Point
-from shapely import geometry as shgeo
from inkex import errormsg
-from ...utils.geometry import remove_duplicate_points
+from numpy import convolve, diff, int32, setdiff1d, sign, zeros
+from shapely import geometry as shgeo
+from shapely.affinity import rotate, scale
from shapely.ops import substring
-from shapely.affinity import scale
+
from ...i18n import _
-import sys
+from ...svg import PIXELS_PER_MM
+from ...utils import Point, roll_linear_ring
+from ...utils.geometry import remove_duplicate_points
class SelfIntersectionError(Exception):
@@ -305,3 +306,33 @@ def _merge(section, other_section):
rungs.extend(other_rungs)
return (rails, rungs)
+
+
+def set_first_node(paths, stroke_width):
+ """
+ Rolls the first path in paths to a starting node which has no intersections and is not within a sharp corner
+
+ paths is expected to be a list with only one closed path.
+ """
+ path = paths[0]
+
+ ring = shgeo.LinearRing(path)
+ buffered_ring = ring.buffer(stroke_width / 2).boundary
+
+ for point1, point2 in zip(path[:-1], path[1:]):
+ line = shgeo.LineString([point1, point2])
+ if line.length == 0:
+ continue
+
+ # create a rung at the center of the line
+ # we know that the line (and therefore it's center) is always straight
+ scale_factor = (stroke_width + 0.001) / line.length
+ rung = rotate(line, 90)
+ rung = scale(rung, xfact=scale_factor, yfact=scale_factor)
+
+ # when the rung intersects twice with the buffered ring, we assume a good starting point
+ intersection = rung.intersection(buffered_ring)
+ if isinstance(intersection, shgeo.MultiPoint) and len(intersection.geoms) == 2:
+ distance = ring.project(line.centroid)
+ paths[0] = list(roll_linear_ring(ring, distance).coords)
+ break
diff --git a/lib/extensions/stroke_to_satin.py b/lib/extensions/stroke_to_satin.py
index fa360548..878fa869 100644
--- a/lib/extensions/stroke_to_satin.py
+++ b/lib/extensions/stroke_to_satin.py
@@ -9,7 +9,8 @@ import inkex
from shapely import geometry as shgeo
from ..elements import SatinColumn, Stroke
-from ..elements.utils.stroke_to_satin import convert_path_to_satin
+from ..elements.utils.stroke_to_satin import (convert_path_to_satin,
+ set_first_node)
from ..i18n import _
from ..svg import get_correction_transform
from ..svg.styles import get_join_style_args
@@ -38,7 +39,11 @@ class StrokeToSatin(InkstitchExtension):
style_args = get_join_style_args(element)
path_style = self.path_style(element)
- for path in element.paths:
+ paths = element.paths
+ if element.is_closed_path:
+ set_first_node(paths, element.stroke_width)
+
+ for path in paths:
satin_paths = convert_path_to_satin(path, element.stroke_width, style_args)
if satin_paths is not None: