diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py index 77b4ac7cf..fb8838ccf 100644 --- a/lib/elements/fill_stitch.py +++ b/lib/elements/fill_stitch.py @@ -137,6 +137,20 @@ class FillStitch(EmbroideryElement): def avoid_self_crossing(self): return self.get_boolean_param('avoid_self_crossing', False) + @property + @param('smoothness_mm', _('Smoothness'), + tooltip=_( + 'Smooth the stitch path. Smoothness limits how far the smoothed stitch path ' + + 'is allowed to deviate from the original path. Hint: a lower stitchc tolerance may be needed too.' + ), + type='integer', + unit='mm', + default=0, + select_items=[('fill_method', 1)], + sort_index=5) + def smoothness(self): + return self.get_float_param('smoothness_mm', 0) + @property @param('clockwise', _('Clockwise'), type='boolean', default=True, select_items=[('fill_method', 1)], sort_index=5) def clockwise(self): @@ -651,9 +665,11 @@ class FillStitch(EmbroideryElement): if self.contour_strategy == 0: stitches = contour_fill.inner_to_outer( tree, + polygon, self.row_spacing, self.max_stitch_length, self.running_stitch_tolerance, + self.smoothness, starting_point, self.avoid_self_crossing ) diff --git a/lib/stitches/contour_fill.py b/lib/stitches/contour_fill.py index 885a7e6c7..8e47518fe 100644 --- a/lib/stitches/contour_fill.py +++ b/lib/stitches/contour_fill.py @@ -406,11 +406,16 @@ def _find_path_inner_to_outer(tree, node, offset, starting_point, avoid_self_cro return LineString(result_coords) -def inner_to_outer(tree, offset, stitch_length, tolerance, starting_point, avoid_self_crossing): +def inner_to_outer(tree, polygon, offset, stitch_length, tolerance, smoothness, starting_point, avoid_self_crossing): """Fill a shape with spirals, from innermost to outermost.""" stitch_path = _find_path_inner_to_outer(tree, 'root', offset, starting_point, avoid_self_crossing) points = [Stitch(*point) for point in stitch_path.coords] + + if smoothness > 0: + smoothed = smooth_path(points, smoothness) + points = clamp_path_to_polygon(smoothed, polygon) + stitches = running_stitch(points, stitch_length, tolerance) return stitches diff --git a/lib/svg/tags.py b/lib/svg/tags.py index 64f6c2f30..8c1dd5589 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -66,6 +66,7 @@ inkstitch_attribs = [ 'guided_fill_strategy', 'join_style', 'avoid_self_crossing', + 'smoothness_mm', 'clockwise', 'reverse', 'expand_mm', diff --git a/lib/utils/geometry.py b/lib/utils/geometry.py index c07172c70..2903bc565 100644 --- a/lib/utils/geometry.py +++ b/lib/utils/geometry.py @@ -182,13 +182,17 @@ def smooth_path(path, smoothness=100.0): coords = _remove_duplicate_coordinates(np.array(path)) num_points = len(coords) - # splprep's s parameter seems to depend on the number of points you pass - # as per the docs, so let's normalize it. - s = round(smoothness / 100 * num_points) + # s is explained in this issue: https://github.com/scipy/scipy/issues/11916 + # the smoothness parameter limits how much the smoothed path can deviate + # from the original path. The standard deviation of the distance between + # the smoothed path and the original path is equal to the smoothness. + # In practical terms, if smoothness is 1mm, then the smoothed path can be + # up to 1mm away from the original path. + s = num_points * smoothness ** 2 # .T transposes the array (for some reason splprep expects # [[x1, x2, ...], [y1, y2, ...]] - tck, fp, ier, msg = splprep(coords.T, s=s, nest=-1, full_output=1) + tck, fp, ier, msg = splprep(coords.T, s=s, k=3, nest=-1, full_output=1) if ier > 0: from ..debug import debug debug.log(f"error {ier} smoothing path: {msg}")