Skip to content

Commit 5a9ae03

Browse files
authored
Running stitch length sequence (#4034)
* allow running stitch length sequences * contour fill: fix error message for long stitch length * satin: fix center underlay stitch length
1 parent 383f164 commit 5a9ae03

File tree

16 files changed

+121
-56
lines changed

16 files changed

+121
-56
lines changed

lib/elements/element.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ def get_multiple_int_param(self, param, default="0"):
161161
try:
162162
params = [int(param) for param in params if param]
163163
except (TypeError, ValueError):
164-
return [int(default)]
164+
params = [int(default)]
165+
166+
if param.endswith('_mm'):
167+
params = [value * PIXELS_PER_MM for value in params]
168+
165169
return params
166170

167171
# returns an array of multiple space separated float values
@@ -171,7 +175,11 @@ def get_multiple_float_param(self, param, default="0"):
171175
try:
172176
params = [float(param) for param in params if param]
173177
except (TypeError, ValueError):
174-
return [float(default)]
178+
params = [float(default)]
179+
180+
if param.endswith('_mm'):
181+
params = [value * PIXELS_PER_MM for value in params]
182+
175183
return params
176184

177185
def get_json_param(self, param, default=None):

lib/elements/satin_column.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,14 +1117,16 @@ def offset_center_line(self):
11171117
linestring = shgeo.LineString(stitches)
11181118
return linestring
11191119

1120-
def _get_center_line_stitches(self, position):
1120+
def _get_center_line_stitches(self, position, stitch_length=None):
11211121
inset_prop = -np.array([position, 100-position]) / 100
1122+
if stitch_length is None:
1123+
stitch_length = self.running_stitch_length
11221124

11231125
# Do it like contour underlay, but inset all the way to the center.
11241126
pairs = self.plot_points_on_rails(self.running_stitch_tolerance, (0, 0), inset_prop)
11251127

11261128
points = [points[0] for points in pairs]
1127-
stitches = running_stitch.even_running_stitch(points, self.running_stitch_length, self.running_stitch_tolerance)
1129+
stitches = running_stitch.even_running_stitch(points, [stitch_length], self.running_stitch_tolerance)
11281130

11291131
if len(stitches) == 1:
11301132
stitches.append(stitches[0])
@@ -1338,12 +1340,12 @@ def do_contour_underlay(self, end_point):
13381340

13391341
first_side = running_stitch.even_running_stitch(
13401342
[points[0] for points in pairs],
1341-
self.contour_underlay_stitch_length,
1343+
[self.contour_underlay_stitch_length],
13421344
self.contour_underlay_stitch_tolerance
13431345
)
13441346
second_side = running_stitch.even_running_stitch(
13451347
[points[1] for points in pairs],
1346-
self.contour_underlay_stitch_length,
1348+
[self.contour_underlay_stitch_length],
13471349
self.contour_underlay_stitch_tolerance
13481350
)
13491351

@@ -1381,7 +1383,7 @@ def do_center_walk(self, end_point):
13811383
repeats = self.center_walk_underlay_repeats
13821384

13831385
stitch_groups = []
1384-
stitches = self._get_center_line_stitches(self.center_walk_underlay_position)
1386+
stitches = self._get_center_line_stitches(self.center_walk_underlay_position, self.center_walk_underlay_stitch_length)
13851387
if end_point:
13861388
tags = ("satin_column", "satin_column_underlay", "satin_center_walk")
13871389
stitches = shgeo.LineString(stitches)

lib/elements/stroke.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,15 @@ def manual_pattern_placement(self):
118118
@property
119119
@param('running_stitch_length_mm',
120120
_('Running stitch length'),
121-
tooltip=_('Length of stitches. Stitches can be shorter according to the stitch tolerance setting.'),
121+
tooltip=_('Length of stitches. Stitches can be shorter according to the stitch tolerance setting.\n'
122+
'It is possible to create stitch length patterns by adding multiple values separated by a space.'),
122123
unit='mm',
123-
type='float',
124+
type='string',
124125
select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch')],
125-
default=2.5,
126+
default="2.5",
126127
sort_index=4)
127128
def running_stitch_length(self):
128-
return max(self.get_float_param("running_stitch_length_mm", 2.5), 0.01)
129+
return [max(value, 0.01) for value in self.get_multiple_float_param("running_stitch_length_mm", "2.5")]
129130

130131
@property
131132
@param('running_stitch_tolerance_mm',
@@ -185,13 +186,13 @@ def max_stitch_length(self):
185186
_('Zig-zag spacing (peak-to-peak)'),
186187
tooltip=_('Length of stitches in zig-zag mode.'),
187188
unit='mm',
188-
type='float',
189-
default=0.4,
189+
type='string',
190+
default="0.4",
190191
select_items=[('stroke_method', 'zigzag_stitch')],
191192
sort_index=6)
192193
@cache
193194
def zigzag_spacing(self):
194-
return max(self.get_float_param("zigzag_spacing_mm", 0.4), 0.01)
195+
return [max(value, 0.01) for value in self.get_multiple_float_param("zigzag_spacing_mm", "0.4")]
195196

196197
@property
197198
@param('stroke_pull_compensation_mm',
@@ -551,7 +552,8 @@ def simple_satin(self, path, zigzag_spacing, stroke_width, pull_compensation):
551552
# `self.zigzag_spacing` is the length for a zig and a zag
552553
# together (a V shape). Start with running stitch at half
553554
# that length:
554-
stitch_group = self.running_stitch(path, zigzag_spacing / 2.0, self.running_stitch_tolerance, False, 0, "")
555+
spacing = [value / 2 for value in zigzag_spacing]
556+
stitch_group = self.running_stitch(path, spacing, self.running_stitch_tolerance, False, 0, "")
555557
stitch_group.stitches = zigzag_stitch(stitch_group.stitches, zigzag_spacing, stroke_width, pull_compensation)
556558

557559
return stitch_group

lib/extensions/fill_to_stroke.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,11 @@ def _get_lines(self, multipolygon):
110110

111111
def _get_high_res_polygon(self, polygon):
112112
# use running stitch method
113-
runs = [even_running_stitch(line_string_to_point_list(polygon.exterior), 1, 0.1)]
113+
runs = [even_running_stitch(line_string_to_point_list(polygon.exterior), [1], 0.1)]
114114
if len(runs[0]) < 3:
115115
return
116116
for interior in polygon.interiors:
117-
shape = even_running_stitch(line_string_to_point_list(interior), 1, 0.1)
117+
shape = even_running_stitch(line_string_to_point_list(interior), [1], 0.1)
118118
if len(shape) >= 3:
119119
runs.append(shape)
120120
return MultiPolygon([(runs[0], runs[1:])])

lib/stitches/auto_fill.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ def fallback(shape, running_stitch_length, running_stitch_tolerance):
391391
boundary = ensure_multi_line_string(shape.boundary)
392392
outline = boundary.geoms[0]
393393

394-
return even_running_stitch(line_string_to_point_list(outline), running_stitch_length, running_stitch_tolerance)
394+
return even_running_stitch(line_string_to_point_list(outline), [running_stitch_length], running_stitch_tolerance)
395395

396396

397397
@debug.time
@@ -941,7 +941,7 @@ def path_to_stitches(shape, path, travel_graph, fill_stitch_graph, angle, row_sp
941941
if fill_stitch_graph.has_edge(edge[0], edge[1], key='segment'):
942942
travel_graph.remove_edges_from(fill_stitch_graph[edge[0]][edge[1]]['segment'].get('underpath_edges', []))
943943
else:
944-
stitches.extend(travel(shape, travel_graph, edge, running_stitch_length, running_stitch_tolerance, skip_last, underpath))
944+
stitches.extend(travel(shape, travel_graph, edge, [running_stitch_length], running_stitch_tolerance, skip_last, underpath))
945945

946946
check_stop_flag()
947947

lib/stitches/circular_fill.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def circular_fill(shape,
7979
if isinstance(line, shgeo.LineString):
8080
# use running stitch here to adjust the stitch length
8181
coords = running_stitch([Point(*point) for point in line.coords],
82-
running_stitch_length,
82+
[running_stitch_length],
8383
running_stitch_tolerance,
8484
enable_random_stitch_length,
8585
running_stitch_length_jitter,
@@ -162,6 +162,6 @@ def path_to_stitches(shape, path, travel_graph, fill_stitch_graph, running_stitc
162162

163163
travel_graph.remove_edges_from(fill_stitch_graph[edge[0]][edge[1]]['segment'].get('underpath_edges', []))
164164
else:
165-
stitches.extend(travel(shape, travel_graph, edge, running_stitch_length, running_stitch_tolerance, skip_last, underpath))
165+
stitches.extend(travel(shape, travel_graph, edge, [running_stitch_length], running_stitch_tolerance, skip_last, underpath))
166166

167167
return stitches

lib/stitches/contour_fill.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ def inner_to_outer(tree, polygon, offset,
422422
smoothed = smooth_path(points, smoothness)
423423
points = clamp_path_to_polygon(smoothed, polygon)
424424

425-
stitches = running_stitch(points, stitch_length, tolerance, enable_random_stitch_length, random_sigma, random_seed)
425+
stitches = running_stitch(points, [stitch_length], tolerance, enable_random_stitch_length, random_sigma, random_seed)
426426

427427
return stitches
428428

@@ -460,6 +460,9 @@ def _interpolate_linear_rings(ring1, ring2, max_stitch_length, start=None):
460460
# orders of magnitude faster because we're not building and querying a KDTree.
461461

462462
num_points = int(20 * ring1.length / max_stitch_length)
463+
if num_points <= 1:
464+
return LineString()
465+
463466
ring1_resampled = trimesh.path.traversal.resample_path(np.array(ring1.coords), count=num_points)
464467
ring2_resampled = trimesh.path.traversal.resample_path(np.array(ring2.coords), count=num_points)
465468

@@ -535,7 +538,7 @@ def _spiral_fill(tree, stitch_length, tolerance, close_point, enable_random_stit
535538
path = spiral_maker(rings, stitch_length, starting_point)
536539
path = [Stitch(*stitch) for stitch in path]
537540

538-
return running_stitch(path, stitch_length, tolerance, enable_random_stitch_length, random_sigma, random_seed)
541+
return running_stitch(path, [stitch_length], tolerance, enable_random_stitch_length, random_sigma, random_seed)
539542

540543

541544
def _get_spiral_rings(tree):

lib/stitches/fill.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, stagge
6363
stitches.append(beg)
6464

6565
if enable_random_stitch_length:
66-
stitches += split_segment_random_phase(beg, end, max_stitch_length, random_sigma, random_seed)
66+
stitched_line = split_segment_random_phase(beg, end, max_stitch_length, random_sigma, random_seed)
67+
stitches.extend([Stitch(stitch, tags=('fill_row',)) for stitch in stitched_line])
6768
else:
6869
# We want our stitches to look like this:
6970
#

lib/stitches/guided_fill.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def path_to_stitches(shape, path, travel_graph, fill_stitch_graph,
102102

103103
travel_graph.remove_edges_from(fill_stitch_graph[edge[0]][edge[1]]['segment'].get('underpath_edges', []))
104104
else:
105-
stitches.extend(travel(shape, travel_graph, edge, running_stitch_length, running_stitch_tolerance, skip_last, underpath))
105+
stitches.extend(travel(shape, travel_graph, edge, [running_stitch_length], running_stitch_tolerance, skip_last, underpath))
106106

107107
return stitches
108108

@@ -166,6 +166,7 @@ def take_only_line_strings(thing):
166166
def apply_stitches(line, max_stitch_length, num_staggers, row_spacing, row_num, threshold=None) -> shgeo.LineString:
167167
if num_staggers == 0:
168168
num_staggers = 1 # sanity check to avoid division by zero.
169+
max_stitch_length = max_stitch_length[0]
169170
start = ((row_num / num_staggers) % 1) * max_stitch_length
170171
projections = np.arange(start, line.length, max_stitch_length)
171172
points = np.array([line.interpolate(projection).coords[0] for projection in projections])
@@ -275,9 +276,9 @@ def intersect_region_with_grating_guideline(shape, line, row_spacing, num_stagge
275276
if enable_random_stitch_length:
276277
points = [InkstitchPoint(*x) for x in offset_line.coords]
277278
stitched_line = shgeo.LineString(random_running_stitch(
278-
points, max_stitch_length, tolerance, random_sigma, prng.join_args(random_seed, row)))
279+
points, [max_stitch_length], tolerance, random_sigma, prng.join_args(random_seed, row)))
279280
else:
280-
stitched_line = apply_stitches(offset_line, max_stitch_length, num_staggers, row_spacing, row)
281+
stitched_line = apply_stitches(offset_line, [max_stitch_length], num_staggers, row_spacing, row)
281282
intersection = shape.intersection(stitched_line)
282283

283284
if not intersection.is_empty and shape_envelope.intersects(stitched_line):

lib/stitches/linear_gradient_fill.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,11 @@ def _get_lines(fill, shape, bounding_box, angle):
127127
if fill.enable_random_stitch_length:
128128
points = [InkstitchPoint(*x) for x in line]
129129
staggered_line = LineString(random_running_stitch(
130-
points, fill.max_stitch_length, fill.running_stitch_tolerance, fill.random_stitch_length_jitter, prng.join_args(fill.random_seed, i)))
130+
points,
131+
[fill.max_stitch_length], fill.running_stitch_tolerance, fill.random_stitch_length_jitter, prng.join_args(fill.random_seed, i))
132+
)
131133
else:
132-
staggered_line = apply_stitches(LineString(line), fill.max_stitch_length, fill.staggers, fill.row_spacing, i)
134+
staggered_line = apply_stitches(LineString(line), [fill.max_stitch_length], fill.staggers, fill.row_spacing, i)
133135
staggered_lines.append(staggered_line)
134136
return staggered_lines, bottom_line
135137

0 commit comments

Comments
 (0)