Skip to content

Commit b82cc27

Browse files
authored
ripples: improve varying width distances (#4079)
1 parent ccf8dc2 commit b82cc27

File tree

2 files changed

+64
-36
lines changed

2 files changed

+64
-36
lines changed

lib/elements/stroke.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ def min_line_dist(self):
233233
min_dist = self.get_float_param("min_line_dist_mm")
234234
if min_dist is None:
235235
return
236-
return max(min_dist, 0.01)
236+
return min_dist
237237

238238
_satin_guided_pattern_options = [
239239
ParamOption('default', _('Line count / Minimum line distance')),

lib/stitches/ripple_stitch.py

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
from math import atan2, ceil
33

44
import numpy as np
5+
from shapely import prepare
56
from shapely.affinity import rotate, scale, translate
6-
from shapely.geometry import LineString, Point
7+
from shapely.geometry import LineString, Point, Polygon
78
from shapely.ops import substring
89

910
from ..elements import SatinColumn
@@ -28,7 +29,7 @@ def ripple_stitch(stroke):
2829
if stroke.as_multi_line_string().length < 0.1:
2930
return []
3031

31-
is_linear, helper_lines = _get_helper_lines(stroke)
32+
is_linear, helper_lines, grid_lines = _get_helper_lines(stroke)
3233
if not helper_lines:
3334
return []
3435

@@ -43,7 +44,7 @@ def ripple_stitch(stroke):
4344
stitches.reverse()
4445

4546
if stitches and stroke.grid_size != 0:
46-
stitches.extend(_do_grid(stroke, helper_lines, skip_start, skip_end, is_linear, stitches[-1]))
47+
stitches.extend(_do_grid(stroke, grid_lines, skip_start, skip_end, is_linear, stitches[-1]))
4748
if stroke.grid_first:
4849
stitches = stitches[::-1]
4950

@@ -60,7 +61,8 @@ def _get_stitches(stroke, is_linear, lines, skip_start):
6061
# even though this is manual stitch placement, we do not want the
6162
# connector lines to exceed the running stitch length value
6263
# so let's add some points
63-
points = LineString([stitches[-1], line[0]]).segmentize(stroke.running_stitch_length).coords
64+
segments = LineString([stitches[-1], line[0]]).segmentize(stroke.running_stitch_length)
65+
points = list(segments)[0].coords
6466
if len(points) > 2:
6567
stitches.extend([InkstitchPoint(coord[0], coord[1]) for coord in points[1:-1]])
6668
stitches.extend(line)
@@ -204,31 +206,44 @@ def _line_count_adjust(stroke, num_lines):
204206
def _get_helper_lines(stroke):
205207
lines = stroke.as_multi_line_string().geoms
206208
if len(lines) > 1:
207-
return True, _get_satin_ripple_helper_lines(stroke)
209+
helper_lines = _get_satin_ripple_helper_lines(stroke)
210+
if stroke.grid_size > 0:
211+
grid_lines = _get_satin_ripple_helper_lines(stroke, True)
212+
else:
213+
grid_lines = helper_lines
214+
return True, helper_lines, grid_lines
208215
else:
209216
if stroke.manual_pattern_placement:
210217
path = stroke.parse_path()
211218
path = [stroke.strip_control_points(subpath) for subpath in path][0]
212219
outline = LineString(path)
213220
else:
221+
if stroke.grid_size > 0:
222+
stitch_len_list = [stroke.grid_size]
223+
else:
224+
stitch_len_list = stroke.running_stitch_length
214225
outline = LineString(even_running_stitch(
215226
line_string_to_point_list(lines[0]),
216-
stroke.grid_size or stroke.running_stitch_length,
227+
stitch_len_list,
217228
stroke.running_stitch_tolerance)
218229
)
219230

220231
if stroke.is_closed:
221-
return False, _get_circular_ripple_helper_lines(stroke, outline)
232+
helper_lines = _get_circular_ripple_helper_lines(stroke, outline)
233+
return False, helper_lines, helper_lines
222234
elif stroke.join_style == 1:
223-
return True, _get_point_style_linear_helper_lines(stroke, outline)
235+
helper_lines = _get_point_style_linear_helper_lines(stroke, outline)
236+
return True, helper_lines, helper_lines
224237
else:
225-
return True, _get_linear_ripple_helper_lines(stroke, outline)
238+
helper_lines = _get_linear_ripple_helper_lines(stroke, outline)
239+
return True, helper_lines, helper_lines
226240

227241

228-
def _get_satin_ripple_helper_lines(stroke):
229-
# if grid_size has a number use this, otherwise use running_stitch_length
230-
length = stroke.grid_size or min(stroke.running_stitch_length)
231-
242+
def _get_satin_ripple_helper_lines(stroke, grid=False):
243+
if grid and stroke.grid_size > 0:
244+
length = stroke.grid_size
245+
else:
246+
length = stroke.running_stitch_tolerance
232247
# use satin column points for satin like build ripple stitches
233248
rail_pairs = SatinColumn(stroke.node).plot_points_on_rails(length)
234249
count = _get_satin_line_count(stroke, rail_pairs)
@@ -281,7 +296,10 @@ def _get_point_style_linear_helper_lines(stroke, outline):
281296

282297
def _get_linear_ripple_helper_lines(stroke, outline):
283298
guide_line = stroke.get_guide_line()
284-
max_dist = stroke.grid_size or stroke.running_stitch_length
299+
if stroke.grid_size > 0:
300+
max_dist = [stroke.grid_size]
301+
else:
302+
max_dist = stroke.running_stitch_length
285303

286304
if guide_line:
287305
return _get_guided_helper_lines(stroke, outline, max_dist)
@@ -386,7 +404,7 @@ def _generate_guided_helper_lines(stroke, outline, max_distance, guide_line):
386404
translation = guide_point - start_point
387405
scaling = scale_steps[i]
388406
if stroke.rotate_ripples and previous_guide_point:
389-
rotation = atan2(guide_point.y - previous_guide_point.y, guide_point.x - previous_guide_point.x)
407+
rotation = get_rotation(previous_guide_point, guide_point)
390408
rotation = rotation - start_rotation
391409
else:
392410
rotation = 0
@@ -404,7 +422,7 @@ def _get_start_rotation(line):
404422
point0 = line.interpolate(0)
405423
point1 = line.interpolate(0.1)
406424

407-
return atan2(point1.y - point0.y, point1.x - point0.x)
425+
return get_rotation(point0, point1)
408426

409427

410428
def generate_satin_guide_helper_lines(stroke, outline, guide_line):
@@ -422,7 +440,7 @@ def generate_satin_guide_helper_lines(stroke, outline, guide_line):
422440
return _generate_simple_satin_guide_helper_lines(stroke, outline, guide_line)
423441

424442
outline_width = (outline1 - outline0).length()
425-
outline_rotation = atan2(outline1.y - outline0.y, outline1.x - outline0.x)
443+
outline_rotation = get_rotation(outline0, outline1)
426444

427445
if stroke.satin_guide_pattern_position == "adaptive":
428446
return _generate_satin_guide_helper_lines_with_varying_pattern_distance(
@@ -450,7 +468,7 @@ def _generate_simple_satin_guide_helper_lines(stroke, outline, guide_line):
450468
point0 = pairs[0][0]
451469
point1 = pairs[0][1]
452470

453-
start_rotation = atan2(point1.y - point0.y, point1.x - point0.x)
471+
start_rotation = get_rotation(point0, point1)
454472
start_scale = (point1 - point0).length()
455473
outline_center = InkstitchPoint.from_shapely_point(outline.centroid)
456474

@@ -463,7 +481,7 @@ def _generate_simple_satin_guide_helper_lines(stroke, outline, guide_line):
463481
guide_center = (point0 + point1) / 2
464482
translation = guide_center - outline_center
465483
if stroke.rotate_ripples:
466-
rotation = atan2(point1.y - point0.y, point1.x - point0.x)
484+
rotation = get_rotation(point0, point1)
467485
rotation = rotation - start_rotation
468486
else:
469487
rotation = 0
@@ -499,7 +517,7 @@ def _generate_satin_guide_helper_lines_with_constant_pattern_distance(stroke, gu
499517

500518
# move to point0, rotate and scale so the other point hits point1
501519
scaling = (point1 - point0).length() / outline_width
502-
rotation = atan2(point1.y - point0.y, point1.x - point0.x)
520+
rotation = get_rotation(point0, point1)
503521
rotation = rotation - outline_rotation
504522
translation = point0 - outline0
505523
transformed_outline = _transform_outline(translation, rotation, scaling, outline, Point(point0), 0)
@@ -511,33 +529,39 @@ def _generate_satin_guide_helper_lines_with_constant_pattern_distance(stroke, gu
511529
return _point_dict_to_helper_lines(len(outline.coords), line_point_dict)
512530

513531

514-
def _generate_satin_guide_helper_lines_with_varying_pattern_distance(stroke, guide_line, outline, outline0, outline_width, outline_rotation):
515-
# rotate pattern and get the pattern width
516-
transformed_outline = _transform_outline(Point([0, 0]), outline_rotation, 1, outline, Point(outline0), 0)
517-
minx, miny, maxx, maxy = transformed_outline.bounds
518-
pattern_height = maxy - miny
532+
def _generate_satin_guide_helper_lines_with_varying_pattern_distance(stroke, guide_line, outline, outline0, outline_height, outline_rotation):
533+
outline = _transform_outline(Point([0, 0]), -outline_rotation, 1, outline, Point(outline0), 0)
534+
minx, miny, maxx, maxy = outline.bounds
535+
bbox = Polygon([(minx, miny), (minx, maxy), (maxx, maxy), (maxx, miny)])
536+
537+
min_distance = stroke.min_line_dist or 0.1
538+
miny -= min_distance
539+
maxy - min_distance
540+
min_dist_bbox = Polygon([(minx, miny), (minx, maxy), (maxx, maxy), (maxx, miny)])
519541

520-
distance = 0
521-
min_distance = stroke.min_line_dist or 0
522542
line_point_dict = defaultdict(list)
523-
while True:
524-
if distance > guide_line.center_line.length:
525-
break
543+
544+
pairs = guide_line.plot_points_on_rails(0.1)
545+
last_bbox = None
546+
for point0, point1 in pairs:
526547
check_stop_flag()
527-
point0, point1 = get_cut_points(guide_line, distance)
528548

529549
# move to point0, rotate and scale so the other point hits point1
530-
scaling = (point1 - point0).length() / outline_width
531-
rotation = atan2(point1.y - point0.y, point1.x - point0.x)
532-
rotation = rotation - outline_rotation
550+
scaling = (point1 - point0).length() / outline_height
551+
rotation = get_rotation(point0, point1)
533552
translation = point0 - outline0
534553
transformed_outline = _transform_outline(translation, rotation, scaling, outline, Point(point0), 0)
535554

536-
distance += max(0.01, (pattern_height * scaling) + min_distance)
555+
this_bbox = _transform_outline(translation, rotation, scaling, bbox, Point(point0), 0)
556+
if last_bbox is not None:
557+
if not last_bbox.disjoint(this_bbox):
558+
continue
537559

538560
# outline to helper line points
539561
for j, point in enumerate(transformed_outline.coords):
540562
line_point_dict[j].append(InkstitchPoint(point[0], point[1]))
563+
last_bbox = _transform_outline(translation, rotation, scaling, min_dist_bbox, Point(point0), 0)
564+
prepare(last_bbox)
541565

542566
return _point_dict_to_helper_lines(len(outline.coords), line_point_dict)
543567

@@ -547,6 +571,10 @@ def get_cut_points(guide_line, distance):
547571
return guide_line.find_cut_points(*cut_point.coords)
548572

549573

574+
def get_rotation(point0, point1):
575+
return atan2(point1.y - point0.y, point1.x - point0.x)
576+
577+
550578
def _transform_outline(translation, rotation, scaling, outline, origin, scale_axis):
551579
# transform
552580
transformed_outline = translate(outline, translation.x, translation.y)

0 commit comments

Comments
 (0)