diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py index 593791e50..267dbbd51 100644 --- a/lib/elements/fill_stitch.py +++ b/lib/elements/fill_stitch.py @@ -422,6 +422,32 @@ class FillStitch(EmbroideryElement): def bean_stitch_repeats(self): return self.get_multiple_int_param("bean_stitch_repeats", "0") + @property + @param('zigzag_spacing_mm', + _('Zig-zag spacing (peak-to-peak)'), + tooltip=_('Length of stitches in zig-zag mode.'), + unit='mm', + type='float', + select_items=[('fill_method', 'meander_fill')], + default=0, + sort_index=35) + @cache + def zigzag_spacing(self): + return self.get_float_param("zigzag_spacing_mm", 0) + + @property + @param('zigzag_width_mm', + _('Zigzag width'), + tooltip=_('Width of the zigzag line.'), + unit='mm', + type='float', + select_items=[('fill_method', 'meander_fill')], + default=3, + sort_index=36) + @cache + def zigzag_width(self): + return self.get_float_param("zigzag_width_mm", 0) + @property def color(self): # SVG spec says the default fill is black diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index e6bcba5ce..a4df5118f 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -13,7 +13,8 @@ from ..i18n import _ from ..marker import get_marker_elements from ..stitch_plan import StitchGroup from ..stitches.ripple_stitch import ripple_stitch -from ..stitches.running_stitch import bean_stitch, running_stitch +from ..stitches.running_stitch import (bean_stitch, running_stitch, + zigzag_stitch) from ..svg import get_node_transform, parse_length_with_units from ..svg.clip import get_clip_path from ..threads import ThreadColor @@ -444,27 +445,7 @@ class Stroke(EmbroideryElement): # that length: patch = self.running_stitch(path, zigzag_spacing / 2.0, self.running_stitch_tolerance) - # Now move the points left and right. Consider each pair - # of points in turn, and move perpendicular to them, - # alternating left and right. - - stroke_width = stroke_width + pull_compensation - offset = stroke_width / 2.0 - - for i in range(len(patch) - 1): - start = patch.stitches[i] - end = patch.stitches[i + 1] - # sometimes the stitch results into zero length which cause a division by zero error - # ignoring this leads to a slightly bad result, but that is better than no output - if (end - start).length() == 0: - continue - segment_direction = (end - start).unit() - zigzag_direction = segment_direction.rotate_left() - - if i % 2 == 1: - zigzag_direction *= -1 - - patch.stitches[i] += zigzag_direction * offset + patch.stitches = zigzag_stitch(patch.stitches, zigzag_spacing, stroke_width, pull_compensation) return patch diff --git a/lib/stitches/meander_fill.py b/lib/stitches/meander_fill.py index c1308bf4e..16510ddee 100644 --- a/lib/stitches/meander_fill.py +++ b/lib/stitches/meander_fill.py @@ -14,7 +14,7 @@ from ..utils.list import poprandom from ..utils.prng import iter_uniform_floats from ..utils.smoothing import smooth_path from ..utils.threading import check_stop_flag -from .running_stitch import bean_stitch, running_stitch +from .running_stitch import bean_stitch, running_stitch, zigzag_stitch def meander_fill(fill, shape, original_shape, shape_index, starting_point, ending_point): @@ -178,7 +178,11 @@ def post_process(points, shape, original_shape, fill): smoothed_points = smooth_path(points, fill.smoothness) smoothed_points = [InkStitchPoint.from_tuple(point) for point in smoothed_points] - stitches = running_stitch(smoothed_points, fill.running_stitch_length, fill.running_stitch_tolerance) + if fill.zigzag_spacing > 0: + stitches = running_stitch(smoothed_points, fill.zigzag_spacing / 2, fill.running_stitch_tolerance) + stitches = zigzag_stitch(stitches, fill.zigzag_spacing, fill.zigzag_width, 0) + else: + stitches = running_stitch(smoothed_points, fill.running_stitch_length, fill.running_stitch_tolerance) if fill.clip: stitches = clamp_path_to_polygon(stitches, original_shape) diff --git a/lib/stitches/running_stitch.py b/lib/stitches/running_stitch.py index 6c6a4d1df..139a20066 100644 --- a/lib/stitches/running_stitch.py +++ b/lib/stitches/running_stitch.py @@ -4,9 +4,9 @@ # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. import math -from math import tau import typing from copy import copy +from math import tau import numpy as np from shapely import geometry as shgeo @@ -310,3 +310,29 @@ def bean_stitch(stitches, repeats, tags_to_ignore=None): new_stitches.extend(copy(new_stitches[-2:])) return new_stitches + + +def zigzag_stitch(stitches, zigzag_spacing, stroke_width, pull_compensation): + # Move the points left and right. Consider each pair + # of points in turn, and move perpendicular to them, + # alternating left and right. + + stroke_width = stroke_width + pull_compensation + offset = stroke_width / 2.0 + + for i in range(len(stitches) - 1): + start = stitches[i] + end = stitches[i + 1] + # sometimes the stitch results into zero length which cause a division by zero error + # ignoring this leads to a slightly bad result, but that is better than no output + if (end - start).length() == 0: + continue + segment_direction = (end - start).unit() + zigzag_direction = segment_direction.rotate_left() + + if i % 2 == 1: + zigzag_direction *= -1 + + stitches[i] += zigzag_direction * offset + + return stitches diff --git a/lib/svg/tags.py b/lib/svg/tags.py index e3d336dfb..36721a1a6 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -107,6 +107,7 @@ inkstitch_attribs = [ 'running_stitch_length_mm', 'running_stitch_tolerance_mm', 'cutwork_needle', + 'zigzag_width_mm', # ripples 'line_count', 'min_line_dist_mm',