kopia lustrzana https://github.com/inkstitch/inkstitch
add running stitch tolerance param (#1701)
rodzic
b6bde000fe
commit
e884fb78db
|
@ -366,6 +366,19 @@ class FillStitch(EmbroideryElement):
|
|||
def running_stitch_length(self):
|
||||
return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
|
||||
|
||||
@property
|
||||
@param('running_stitch_tolerance_mm',
|
||||
_('Running stitch tolerance'),
|
||||
tooltip=_('All stitches must be within this distance of the path. ' +
|
||||
'A lower tolerance means stitches will be closer together. ' +
|
||||
'A higher tolerance means sharp corners may be rounded.'),
|
||||
unit='mm',
|
||||
type='float',
|
||||
default=0.2,
|
||||
sort_index=6)
|
||||
def running_stitch_tolerance(self):
|
||||
return max(self.get_float_param("running_stitch_tolerance_mm", 0.2), 0.01)
|
||||
|
||||
@property
|
||||
@param('fill_underlay', _('Underlay'), type='toggle', group=_('Fill Underlay'), default=True)
|
||||
def fill_underlay(self):
|
||||
|
@ -560,6 +573,7 @@ class FillStitch(EmbroideryElement):
|
|||
self.fill_underlay_row_spacing,
|
||||
self.fill_underlay_max_stitch_length,
|
||||
self.running_stitch_length,
|
||||
self.running_stitch_tolerance,
|
||||
self.staggers,
|
||||
self.fill_underlay_skip_last,
|
||||
starting_point,
|
||||
|
@ -580,6 +594,7 @@ class FillStitch(EmbroideryElement):
|
|||
self.end_row_spacing,
|
||||
self.max_stitch_length,
|
||||
self.running_stitch_length,
|
||||
self.running_stitch_tolerance,
|
||||
self.staggers,
|
||||
self.skip_last,
|
||||
starting_point,
|
||||
|
@ -601,6 +616,7 @@ class FillStitch(EmbroideryElement):
|
|||
tree,
|
||||
self.row_spacing,
|
||||
self.max_stitch_length,
|
||||
self.running_stitch_tolerance,
|
||||
starting_point,
|
||||
self.avoid_self_crossing
|
||||
)
|
||||
|
@ -608,12 +624,14 @@ class FillStitch(EmbroideryElement):
|
|||
stitches = contour_fill.single_spiral(
|
||||
tree,
|
||||
self.max_stitch_length,
|
||||
self.running_stitch_tolerance,
|
||||
starting_point
|
||||
)
|
||||
elif self.contour_strategy == 2:
|
||||
stitches = contour_fill.double_spiral(
|
||||
tree,
|
||||
self.max_stitch_length,
|
||||
self.running_stitch_tolerance,
|
||||
starting_point
|
||||
)
|
||||
|
||||
|
|
|
@ -111,6 +111,19 @@ class Stroke(EmbroideryElement):
|
|||
def running_stitch_length(self):
|
||||
return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
|
||||
|
||||
@property
|
||||
@param('running_stitch_tolerance_mm',
|
||||
_('Running stitch tolerance'),
|
||||
tooltip=_('All stitches must be within this distance from the path. ' +
|
||||
'A lower tolerance means stitches will be closer together. ' +
|
||||
'A higher tolerance means sharp corners may be rounded.'),
|
||||
unit='mm',
|
||||
type='float',
|
||||
default=0.2,
|
||||
sort_index=4)
|
||||
def running_stitch_tolerance(self):
|
||||
return max(self.get_float_param("running_stitch_tolerance_mm", 0.2), 0.01)
|
||||
|
||||
@property
|
||||
@param('zigzag_spacing_mm',
|
||||
_('Zig-zag spacing (peak-to-peak)'),
|
||||
|
@ -360,7 +373,7 @@ class Stroke(EmbroideryElement):
|
|||
# `self.zigzag_spacing` is the length for a zig and a zag
|
||||
# together (a V shape). Start with running stitch at half
|
||||
# that length:
|
||||
patch = self.running_stitch(path, zigzag_spacing / 2.0)
|
||||
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,
|
||||
|
@ -385,7 +398,7 @@ class Stroke(EmbroideryElement):
|
|||
|
||||
return patch
|
||||
|
||||
def running_stitch(self, path, stitch_length):
|
||||
def running_stitch(self, path, stitch_length, tolerance):
|
||||
repeated_path = []
|
||||
|
||||
# go back and forth along the path as specified by self.repeats
|
||||
|
@ -398,7 +411,7 @@ class Stroke(EmbroideryElement):
|
|||
|
||||
repeated_path.extend(this_path)
|
||||
|
||||
stitches = running_stitch(repeated_path, stitch_length)
|
||||
stitches = running_stitch(repeated_path, stitch_length, tolerance)
|
||||
|
||||
return StitchGroup(self.color, stitches)
|
||||
|
||||
|
@ -429,7 +442,7 @@ class Stroke(EmbroideryElement):
|
|||
patch = StitchGroup(color=self.color, stitches=path, stitch_as_is=True)
|
||||
# running stitch
|
||||
elif self.is_running_stitch():
|
||||
patch = self.running_stitch(path, self.running_stitch_length)
|
||||
patch = self.running_stitch(path, self.running_stitch_length, self.running_stitch_tolerance)
|
||||
if self.bean_stitch_repeats > 0:
|
||||
patch.stitches = self.do_bean_repeats(patch.stitches)
|
||||
# simple satin
|
||||
|
|
|
@ -53,6 +53,7 @@ def auto_fill(shape,
|
|||
end_row_spacing,
|
||||
max_stitch_length,
|
||||
running_stitch_length,
|
||||
running_stitch_tolerance,
|
||||
staggers,
|
||||
skip_last,
|
||||
starting_point,
|
||||
|
@ -64,15 +65,16 @@ def auto_fill(shape,
|
|||
fill_stitch_graph = build_fill_stitch_graph(shape, segments, starting_point, ending_point)
|
||||
except ValueError:
|
||||
# Small shapes will cause the graph to fail - min() arg is an empty sequence through insert node
|
||||
return fallback(shape, running_stitch_length)
|
||||
return fallback(shape, running_stitch_length, running_stitch_tolerance)
|
||||
|
||||
if not graph_is_valid(fill_stitch_graph, shape, max_stitch_length):
|
||||
return fallback(shape, running_stitch_length)
|
||||
return fallback(shape, running_stitch_length, running_stitch_tolerance)
|
||||
|
||||
travel_graph = build_travel_graph(fill_stitch_graph, shape, angle, underpath)
|
||||
path = find_stitch_path(fill_stitch_graph, travel_graph, starting_point, ending_point)
|
||||
result = path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing,
|
||||
max_stitch_length, running_stitch_length, staggers, skip_last)
|
||||
max_stitch_length, running_stitch_length, running_stitch_tolerance,
|
||||
staggers, skip_last)
|
||||
|
||||
return result
|
||||
|
||||
|
@ -251,7 +253,7 @@ def graph_is_valid(graph, shape, max_stitch_length):
|
|||
return not networkx.is_empty(graph) and networkx.is_eulerian(graph)
|
||||
|
||||
|
||||
def fallback(shape, running_stitch_length):
|
||||
def fallback(shape, running_stitch_length, running_stitch_tolerance):
|
||||
"""Generate stitches when the auto-fill algorithm fails.
|
||||
|
||||
If graph_is_valid() returns False, we're not going to be able to run the
|
||||
|
@ -263,7 +265,7 @@ def fallback(shape, running_stitch_length):
|
|||
boundary = ensure_multi_line_string(shape.boundary)
|
||||
outline = boundary.geoms[0]
|
||||
|
||||
return running_stitch(line_string_to_point_list(outline), running_stitch_length)
|
||||
return running_stitch(line_string_to_point_list(outline), running_stitch_length, running_stitch_tolerance)
|
||||
|
||||
|
||||
@debug.time
|
||||
|
@ -583,12 +585,12 @@ def collapse_sequential_outline_edges(path):
|
|||
return new_path
|
||||
|
||||
|
||||
def travel(travel_graph, start, end, running_stitch_length, skip_last):
|
||||
def travel(travel_graph, start, end, running_stitch_length, running_stitch_tolerance, skip_last):
|
||||
"""Create stitches to get from one point on an outline of the shape to another."""
|
||||
|
||||
path = networkx.shortest_path(travel_graph, start, end, weight='weight')
|
||||
path = [Stitch(*p) for p in path]
|
||||
stitches = running_stitch(path, running_stitch_length)
|
||||
stitches = running_stitch(path, running_stitch_length, running_stitch_tolerance)
|
||||
|
||||
for stitch in stitches:
|
||||
stitch.add_tag('auto_fill_travel')
|
||||
|
@ -610,7 +612,8 @@ def travel(travel_graph, start, end, running_stitch_length, skip_last):
|
|||
|
||||
|
||||
@debug.time
|
||||
def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last):
|
||||
def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, running_stitch_length, running_stitch_tolerance,
|
||||
staggers, skip_last):
|
||||
path = collapse_sequential_outline_edges(path)
|
||||
|
||||
stitches = []
|
||||
|
@ -624,6 +627,6 @@ def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing,
|
|||
stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers, skip_last)
|
||||
travel_graph.remove_edges_from(fill_stitch_graph[edge[0]][edge[1]]['segment'].get('underpath_edges', []))
|
||||
else:
|
||||
stitches.extend(travel(travel_graph, edge[0], edge[1], running_stitch_length, skip_last))
|
||||
stitches.extend(travel(travel_graph, edge[0], edge[1], running_stitch_length, running_stitch_tolerance, skip_last))
|
||||
|
||||
return stitches
|
||||
|
|
|
@ -391,12 +391,12 @@ 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, starting_point, avoid_self_crossing):
|
||||
def inner_to_outer(tree, offset, stitch_length, tolerance, 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]
|
||||
stitches = running_stitch(points, stitch_length)
|
||||
stitches = running_stitch(points, stitch_length, tolerance)
|
||||
|
||||
return stitches
|
||||
|
||||
|
@ -490,24 +490,24 @@ def _check_and_prepare_tree_for_valid_spiral(tree):
|
|||
return process_node('root')
|
||||
|
||||
|
||||
def single_spiral(tree, stitch_length, starting_point):
|
||||
def single_spiral(tree, stitch_length, tolerance, starting_point):
|
||||
"""Fill a shape with a single spiral going from outside to center."""
|
||||
return _spiral_fill(tree, stitch_length, starting_point, _make_spiral)
|
||||
return _spiral_fill(tree, stitch_length, tolerance, starting_point, _make_spiral)
|
||||
|
||||
|
||||
def double_spiral(tree, stitch_length, starting_point):
|
||||
def double_spiral(tree, stitch_length, tolerance, starting_point):
|
||||
"""Fill a shape with a double spiral going from outside to center and back to outside. """
|
||||
return _spiral_fill(tree, stitch_length, starting_point, _make_fermat_spiral)
|
||||
return _spiral_fill(tree, stitch_length, tolerance, starting_point, _make_fermat_spiral)
|
||||
|
||||
|
||||
def _spiral_fill(tree, stitch_length, close_point, spiral_maker):
|
||||
def _spiral_fill(tree, stitch_length, tolerance, close_point, spiral_maker):
|
||||
starting_point = close_point.coords[0]
|
||||
|
||||
rings = _get_spiral_rings(tree)
|
||||
path = spiral_maker(rings, stitch_length, starting_point)
|
||||
path = [Stitch(*stitch) for stitch in path]
|
||||
|
||||
return running_stitch(path, stitch_length)
|
||||
return running_stitch(path, stitch_length, tolerance)
|
||||
|
||||
|
||||
def _get_spiral_rings(tree):
|
||||
|
|
|
@ -30,7 +30,7 @@ def ripple_stitch(stroke):
|
|||
if stroke.grid_size != 0:
|
||||
ripple_points.extend(_do_grid(stroke, helper_lines))
|
||||
|
||||
stitches = running_stitch(ripple_points, stroke.running_stitch_length)
|
||||
stitches = running_stitch(ripple_points, stroke.running_stitch_length, stroke.running_stitch_tolerance)
|
||||
|
||||
return _repeat_coords(stitches, stroke.repeats)
|
||||
|
||||
|
@ -57,7 +57,9 @@ def _get_helper_lines(stroke):
|
|||
if len(lines) > 1:
|
||||
return True, _get_satin_ripple_helper_lines(stroke)
|
||||
else:
|
||||
outline = LineString(running_stitch(line_string_to_point_list(lines[0]), stroke.grid_size or stroke.running_stitch_length))
|
||||
outline = LineString(running_stitch(line_string_to_point_list(lines[0]),
|
||||
stroke.grid_size or stroke.running_stitch_length,
|
||||
stroke.running_stitch_tolerance))
|
||||
|
||||
if stroke.is_closed:
|
||||
return False, _get_circular_ripple_helper_lines(stroke, outline)
|
||||
|
@ -146,7 +148,7 @@ def _get_guided_helper_lines(stroke, outline, max_distance):
|
|||
def _generate_guided_helper_lines(stroke, outline, max_distance, guide_line):
|
||||
# helper lines are generated by making copies of the outline alog the guide line
|
||||
line_point_dict = defaultdict(list)
|
||||
outline = LineString(running_stitch(line_string_to_point_list(outline), max_distance))
|
||||
outline = LineString(running_stitch(line_string_to_point_list(outline), max_distance, stroke.running_stitch_tolerance))
|
||||
|
||||
center = outline.centroid
|
||||
center = InkstitchPoint(center.x, center.y)
|
||||
|
|
|
@ -3,16 +3,15 @@
|
|||
# Copyright (c) 2010 Authors
|
||||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||
|
||||
from ..debug import debug
|
||||
import math
|
||||
from copy import copy
|
||||
|
||||
from shapely.geometry import LineString
|
||||
|
||||
""" Utility functions to produce running stitches. """
|
||||
|
||||
|
||||
@debug.time
|
||||
def running_stitch(points, stitch_length):
|
||||
def running_stitch(points, stitch_length, tolerance):
|
||||
"""Generate running stitch along a path.
|
||||
|
||||
Given a path and a stitch length, walk along the path in increments of the
|
||||
|
@ -28,9 +27,9 @@ def running_stitch(points, stitch_length):
|
|||
return []
|
||||
|
||||
# simplify will remove as many points as possible while ensuring that the
|
||||
# resulting path stays within 0.75 pixels (0.2mm) of the original path.
|
||||
# resulting path stays within the specified tolerance of the original path.
|
||||
path = LineString(points)
|
||||
simplified = path.simplify(0.75, preserve_topology=False)
|
||||
simplified = path.simplify(tolerance, preserve_topology=False)
|
||||
|
||||
# save the points that simplify picked and make sure we stitch them
|
||||
important_points = set(simplified.coords)
|
||||
|
@ -50,7 +49,7 @@ def running_stitch(points, stitch_length):
|
|||
section_length = section_ls.length
|
||||
if section_length > stitch_length:
|
||||
# a fractional stitch needs to be rounded up, which will make all
|
||||
# of the stitches shorter
|
||||
# the stitches shorter
|
||||
num_stitches = math.ceil(section_length / stitch_length)
|
||||
actual_stitch_length = section_length / num_stitches
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ inkstitch_attribs = [
|
|||
'bean_stitch_repeats',
|
||||
'repeats',
|
||||
'running_stitch_length_mm',
|
||||
'running_stitch_tolerance_mm',
|
||||
# satin column
|
||||
'satin_column',
|
||||
'running_stitch_length_mm',
|
||||
|
|
Ładowanie…
Reference in New Issue