diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py index 4157d3fbc..bc022ab3b 100644 --- a/lib/elements/fill_stitch.py +++ b/lib/elements/fill_stitch.py @@ -118,6 +118,11 @@ class FillStitch(EmbroideryElement): def interlaced(self): return self.get_boolean_param('interlaced', True) + @property + @param('avoid_self_crossing', _('Avoid self-crossing'), type='boolean', default=False, select_items=[('fill_method', 1)], sort_index=2) + def avoid_self_crossing(self): + return self.get_boolean_param('avoid_self_crossing', False) + @property @param('angle', _('Angle of lines of stitches'), @@ -569,12 +574,14 @@ class FillStitch(EmbroideryElement): connectedLine, _ = tangential_fill_stitch_line_creator.offset_poly( poly, -self.row_spacing, - self.join_style+1, + self.join_style + 1, self.max_stitch_length, min(self.min_stitch_length, self.max_stitch_length), self.interlaced, self.tangential_strategy, - shgeo.Point(starting_point)) + shgeo.Point(starting_point), + self.avoid_self_crossing + ) path = [InkstitchPoint(*p) for p in connectedLine] stitch_group = StitchGroup( color=self.color, diff --git a/lib/stitches/tangential_fill_stitch_line_creator.py b/lib/stitches/tangential_fill_stitch_line_creator.py index 7f8b3bea4..61598b58a 100644 --- a/lib/stitches/tangential_fill_stitch_line_creator.py +++ b/lib/stitches/tangential_fill_stitch_line_creator.py @@ -115,7 +115,8 @@ def check_and_prepare_tree_for_valid_spiral(tree): return process_node('root') -def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, offset_by_half, strategy, starting_point): # noqa: C901 +def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, offset_by_half, strategy, starting_point, # noqa: C901 + avoid_self_crossing): """ Takes a polygon (which can have holes) as input and creates offsetted versions until the polygon is filled with these smaller offsets. @@ -139,6 +140,7 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, In contrast to the other two options, "SPIRAL" does not end at the starting point but at the innermost point -starting_point: Defines the starting point for the stitching + -avoid_self_crossing: don't let the path cross itself when using the Inner to Outer strategy Output: -List of point coordinate tuples -Tag (origin) of each point to analyze why a point was placed @@ -277,7 +279,7 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, if strategy == StitchingStrategy.INNER_TO_OUTER: connected_line = tangential_fill_stitch_pattern_creator.connect_raster_tree_from_inner_to_outer( - tree, 'root', abs(offset), stitch_distance, min_stitch_distance, starting_point, offset_by_half) + tree, 'root', abs(offset), stitch_distance, min_stitch_distance, starting_point, offset_by_half, avoid_self_crossing) path = [Stitch(*point) for point in connected_line.coords] return running_stitch(path, stitch_distance), "whatever" elif strategy == StitchingStrategy.SPIRAL: diff --git a/lib/stitches/tangential_fill_stitch_pattern_creator.py b/lib/stitches/tangential_fill_stitch_pattern_creator.py index 20f7a6510..553241f85 100644 --- a/lib/stitches/tangential_fill_stitch_pattern_creator.py +++ b/lib/stitches/tangential_fill_stitch_pattern_creator.py @@ -110,7 +110,7 @@ def create_nearest_points_list( @debug.time def connect_raster_tree_from_inner_to_outer(tree, node, offset, stitch_distance, min_stitch_distance, starting_point, - offset_by_half): # noqa: C901 + offset_by_half, avoid_self_crossing, forward=True): """ Takes the offset curves organized as a tree, connects and samples them. Strategy: A connection from parent to child is made as fast as possible to @@ -137,6 +137,9 @@ def connect_raster_tree_from_inner_to_outer(tree, node, offset, stitch_distance, current_node = tree.nodes[node] current_ring = current_node.val + if not forward and avoid_self_crossing: + current_ring = reverse_line_string(current_ring) + # reorder the coordinates of this ring so that it starts with # a point nearest the starting_point start_distance = current_ring.project(starting_point) @@ -157,7 +160,8 @@ def connect_raster_tree_from_inner_to_outer(tree, node, offset, stitch_distance, if not nearest_points_list: # We have no children, so we're at the center of a spiral. Reversing # the ring gives a nicer visual appearance. - current_ring = reverse_line_string(current_ring) + # current_ring = reverse_line_string(current_ring) + pass else: # This is a recursive algorithm. We'll stitch along this ring, pausing # to jump to each child ring in turn and sew it before continuing on @@ -184,6 +188,8 @@ def connect_raster_tree_from_inner_to_outer(tree, node, offset, stitch_distance, min_stitch_distance, child_connection.nearest_point_child, offset_by_half, + avoid_self_crossing, + not forward ) result_coords.extend(child_path.coords) diff --git a/lib/svg/tags.py b/lib/svg/tags.py index 37eb57520..3f412c2e9 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -43,60 +43,61 @@ SVG_OBJECT_TAGS = (SVG_ELLIPSE_TAG, SVG_CIRCLE_TAG, SVG_RECT_TAG) INKSTITCH_ATTRIBS = {} inkstitch_attribs = [ - 'ties', - 'force_lock_stitches', - # clone - 'clone', - # polyline - 'polyline', - # fill - 'angle', - 'auto_fill', - 'fill_method', - 'tangential_strategy', - 'join_style', - 'interlaced', - 'expand_mm', - 'fill_underlay', - 'fill_underlay_angle', - 'fill_underlay_inset_mm', - 'fill_underlay_max_stitch_length_mm', - 'fill_underlay_row_spacing_mm', - 'fill_underlay_skip_last', - 'max_stitch_length_mm', - 'min_stitch_length_mm', - 'row_spacing_mm', - 'end_row_spacing_mm', - 'skip_last', - 'staggers', - 'underlay_underpath', - 'underpath', - 'flip', - 'expand_mm', - # stroke - 'manual_stitch', - 'bean_stitch_repeats', - 'repeats', - 'running_stitch_length_mm', - # satin column - 'satin_column', - 'running_stitch_length_mm', - 'center_walk_underlay', - 'center_walk_underlay_stitch_length_mm', - 'contour_underlay', - 'contour_underlay_stitch_length_mm', - 'contour_underlay_inset_mm', - 'zigzag_underlay', - 'zigzag_spacing_mm', - 'zigzag_underlay_inset_mm', - 'zigzag_underlay_spacing_mm', - 'zigzag_underlay_max_stitch_length_mm', - 'e_stitch', - 'pull_compensation_mm', - 'stroke_first', - # Legacy - 'trim_after', - 'stop_after' - ] + 'ties', + 'force_lock_stitches', + # clone + 'clone', + # polyline + 'polyline', + # fill + 'angle', + 'auto_fill', + 'fill_method', + 'tangential_strategy', + 'join_style', + 'interlaced', + 'avoid_self_crossing', + 'expand_mm', + 'fill_underlay', + 'fill_underlay_angle', + 'fill_underlay_inset_mm', + 'fill_underlay_max_stitch_length_mm', + 'fill_underlay_row_spacing_mm', + 'fill_underlay_skip_last', + 'max_stitch_length_mm', + 'min_stitch_length_mm', + 'row_spacing_mm', + 'end_row_spacing_mm', + 'skip_last', + 'staggers', + 'underlay_underpath', + 'underpath', + 'flip', + 'expand_mm', + # stroke + 'manual_stitch', + 'bean_stitch_repeats', + 'repeats', + 'running_stitch_length_mm', + # satin column + 'satin_column', + 'running_stitch_length_mm', + 'center_walk_underlay', + 'center_walk_underlay_stitch_length_mm', + 'contour_underlay', + 'contour_underlay_stitch_length_mm', + 'contour_underlay_inset_mm', + 'zigzag_underlay', + 'zigzag_spacing_mm', + 'zigzag_underlay_inset_mm', + 'zigzag_underlay_spacing_mm', + 'zigzag_underlay_max_stitch_length_mm', + 'e_stitch', + 'pull_compensation_mm', + 'stroke_first', + # Legacy + 'trim_after', + 'stop_after' +] for attrib in inkstitch_attribs: INKSTITCH_ATTRIBS[attrib] = inkex.addNS(attrib, 'inkstitch')