22from math import atan2 , ceil
33
44import numpy as np
5+ from shapely import prepare
56from shapely .affinity import rotate , scale , translate
6- from shapely .geometry import LineString , Point
7+ from shapely .geometry import LineString , Point , Polygon
78from shapely .ops import substring
89
910from ..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):
204206def _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
282297def _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
410428def 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+
550578def _transform_outline (translation , rotation , scaling , outline , origin , scale_axis ):
551579 # transform
552580 transformed_outline = translate (outline , translation .x , translation .y )
0 commit comments