summaryrefslogtreecommitdiff
path: root/lib/stitches
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stitches')
-rw-r--r--lib/stitches/guided_fill.py3
-rw-r--r--lib/stitches/ripple_stitch.py127
2 files changed, 85 insertions, 45 deletions
diff --git a/lib/stitches/guided_fill.py b/lib/stitches/guided_fill.py
index 79eff6b5..6ad80da0 100644
--- a/lib/stitches/guided_fill.py
+++ b/lib/stitches/guided_fill.py
@@ -171,7 +171,8 @@ def apply_stitches(line, max_stitch_length, num_staggers, row_spacing, row_num,
points = np.array([line.interpolate(projection).coords[0] for projection in projections])
if len(points) < 2:
- return line
+ coords = line.coords
+ points = [coords[0], coords[-1]]
stitched_line = shgeo.LineString(points)
diff --git a/lib/stitches/ripple_stitch.py b/lib/stitches/ripple_stitch.py
index e150945e..5ebd531b 100644
--- a/lib/stitches/ripple_stitch.py
+++ b/lib/stitches/ripple_stitch.py
@@ -33,20 +33,30 @@ def ripple_stitch(stroke):
skip_start = _adjust_skip(stroke, num_lines, stroke.skip_start)
skip_end = _adjust_skip(stroke, num_lines, stroke.skip_end)
- lines = _get_ripple_lines(stroke, helper_lines, is_linear, skip_start, skip_end)
+ lines = _get_ripple_lines(helper_lines, is_linear, skip_start, skip_end)
stitches = _get_stitches(stroke, is_linear, lines, skip_start)
if stroke.reverse:
stitches.reverse()
- if stroke.grid_size != 0:
- stitches.extend(_do_grid(stroke, helper_lines, skip_start, skip_end, is_linear))
+ if stitches and stroke.grid_size != 0:
+ stitches.extend(_do_grid(stroke, helper_lines, skip_start, skip_end, is_linear, stitches[-1]))
return _repeat_coords(stitches, stroke.repeats)
def _get_stitches(stroke, is_linear, lines, skip_start):
- if is_linear:
+ if stroke.manual_pattern_placement:
+ if stroke.flip_copies:
+ stitches = []
+ for i, line in enumerate(lines):
+ if i % 2 == 0:
+ stitches.extend(line[::-1])
+ else:
+ stitches.extend(line)
+ return stitches
+ return [point for line in lines for point in line]
+ if is_linear and stroke.flip_copies:
return _get_staggered_stitches(stroke, lines, skip_start)
else:
points = [point for line in lines for point in line]
@@ -69,7 +79,7 @@ def _get_staggered_stitches(stroke, lines, skip_start):
for i, line in enumerate(lines):
connector = []
if i != 0 and stroke.join_style == 0:
- if i % 2 == 0:
+ if i % 2 == 0 or not stroke.flip_copies:
first_point = line[0]
else:
first_point = line[-1]
@@ -81,7 +91,7 @@ def _get_staggered_stitches(stroke, lines, skip_start):
should_reverse = (i + skip_start) % 2 == 1
if enable_random_stitch_length or stroke.staggers == 0:
- if should_reverse:
+ if should_reverse and stroke.flip_copies:
line.reverse()
points = running_stitch(line, stitch_length, tolerance, enable_random_stitch_length, length_sigma, prng.join_args(random_seed, i))
stitched_line = connector + points
@@ -89,7 +99,7 @@ def _get_staggered_stitches(stroke, lines, skip_start):
# uses the guided fill alforithm to stagger rows of stitches
points = list(apply_stitches(LineString(line), stitch_length, stroke.staggers, 0.5, i, tolerance).coords)
stitched_line = [InkstitchPoint(*point) for point in points]
- if should_reverse:
+ if should_reverse and stroke.flip_copies:
stitched_line.reverse()
stitched_line = connector + stitched_line
@@ -104,7 +114,7 @@ def _adjust_skip(stroke, num_lines, skip):
return skip
-def _get_ripple_lines(stroke, helper_lines, is_linear, skip_start, skip_end):
+def _get_ripple_lines(helper_lines, is_linear, skip_start, skip_end):
lines = []
for point_num in range(skip_start, len(helper_lines[0]) - skip_end):
row = []
@@ -158,9 +168,16 @@ def _get_helper_lines(stroke):
if len(lines) > 1:
return True, _get_satin_ripple_helper_lines(stroke)
else:
- outline = LineString(even_running_stitch(line_string_to_point_list(lines[0]),
- stroke.grid_size or stroke.running_stitch_length,
- stroke.running_stitch_tolerance))
+ if stroke.manual_pattern_placement:
+ path = stroke.parse_path()
+ path = [stroke.strip_control_points(subpath) for subpath in path][0]
+ outline = LineString(path)
+ else:
+ outline = LineString(even_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)
@@ -189,13 +206,13 @@ def _get_satin_ripple_helper_lines(stroke):
for step in steps:
helper_lines[-1].append(InkstitchPoint.from_shapely_point(helper_line.interpolate(step, normalized=True)))
- if stroke.join_style == 1:
- helper_lines = _converge_helper_line_points(helper_lines, True)
+ if stroke.join_style == 1 or not stroke.flip_copies:
+ helper_lines = _converge_helper_line_points(helper_lines, True, stroke.flip_copies)
return helper_lines
-def _converge_helper_line_points(helper_lines, point_edge=False):
+def _converge_helper_line_points(helper_lines, point_edge=False, flip_copies=True):
num_lines = len(helper_lines)
steps = _get_steps(num_lines)
for i, line in enumerate(helper_lines):
@@ -203,7 +220,7 @@ def _converge_helper_line_points(helper_lines, point_edge=False):
points = []
for j in range(len(line) - 1):
- if point_edge and j % 2 == 1:
+ if point_edge and j % 2 == 1 and flip_copies:
k = num_lines - 1 - i
points.append(line[j] * (1 - steps[k]) + line[j + 1] * steps[k])
else:
@@ -250,37 +267,36 @@ def _target_point_helper_lines(stroke, outline):
return helper_lines
-def _adjust_helper_lines_for_grid(stroke, helper_lines, skip_start, skip_end, is_linear):
- num_lines = len(helper_lines[0])
- count = num_lines - skip_start - skip_end
-
- if stroke.join_style == 0 and (stroke.reverse and count % 2 != 0):
- count += 1
- elif (stroke.join_style == 1 and ((stroke.reverse and (count + skip_start) % 2 != 0) or
- (not stroke.reverse and skip_start % 2 != 0))):
- count += 1
-
- if not is_linear:
- count = 1
- if stroke.reverse:
- count = 0
-
- if count % 2 != 0:
- helper_lines.reverse()
- return helper_lines
+def _adjust_helper_lines_for_grid(grid, last_stitch):
+ # check which end of the grid is nearest to the last stitch
+ # and adjust grid accordingly
+ last_stitch = Point(last_stitch)
+ distances = [
+ Point(grid[0][0]).distance(last_stitch),
+ Point(grid[0][-1]).distance(last_stitch),
+ Point(grid[-1][0]).distance(last_stitch),
+ Point(grid[-1][-1]).distance(last_stitch)
+ ]
+ nearest = distances.index(min(distances))
+ if nearest in [1, 3]:
+ adjusted_grid = []
+ for line in grid:
+ adjusted_grid.append(line[::-1])
+ grid = adjusted_grid
+ if nearest in [2, 3]:
+ grid.reverse()
+ return grid
-def _do_grid(stroke, helper_lines, skip_start, skip_end, is_linear):
- helper_lines = _adjust_helper_lines_for_grid(stroke, helper_lines, skip_start, skip_end, is_linear)
+def _do_grid(stroke, helper_lines, skip_start, skip_end, is_linear, last_stitch):
grid = []
for i, helper in enumerate(helper_lines):
end = len(helper) - skip_end
points = helper[skip_start:end]
- if stroke.reverse:
- points.reverse()
- if len(helper_lines) - skip_start - skip_end % 2 != 0:
+ if i % 2 != 0 and is_linear and not stroke.flip_copies and stroke.join_style == 0:
points.reverse()
grid.append(points)
+ grid = _adjust_helper_lines_for_grid(grid, last_stitch)
grid = _get_staggered_stitches(stroke, grid, 0)
return grid
@@ -299,14 +315,22 @@ 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 along the guide line
line_point_dict = defaultdict(list)
- outline = LineString(even_running_stitch(line_string_to_point_list(outline), max_distance, stroke.running_stitch_tolerance))
+ if not stroke.manual_pattern_placement:
+ outline = LineString(even_running_stitch(
+ line_string_to_point_list(outline),
+ max_distance,
+ stroke.running_stitch_tolerance
+ ))
center = outline.centroid
center = InkstitchPoint(center.x, center.y)
- count = _get_guided_line_count(stroke, guide_line)
+ if stroke.render_at_rungs:
+ count = len(guide_line.coords)
+ else:
+ count = _get_guided_line_count(stroke, guide_line)
+ outline_steps = _get_steps(count, exponent=stroke.exponent, flip=stroke.flip_exponent)
- outline_steps = _get_steps(count, exponent=stroke.exponent, flip=stroke.flip_exponent)
scale_steps = _get_steps(count, start=stroke.scale_start / 100.0, end=stroke.scale_end / 100.0)
start_point = InkstitchPoint(*(guide_line.coords[0]))
@@ -316,7 +340,11 @@ def _generate_guided_helper_lines(stroke, outline, max_distance, guide_line):
for i in range(count):
check_stop_flag()
- guide_point = InkstitchPoint.from_shapely_point(guide_line.interpolate(outline_steps[i], normalized=True))
+ if stroke.render_at_rungs:
+ # Requires the guide line to be defined as manual stitch
+ guide_point = InkstitchPoint(*guide_line.coords[i])
+ else:
+ guide_point = InkstitchPoint.from_shapely_point(guide_line.interpolate(outline_steps[i], normalized=True))
translation = guide_point - start_point
scaling = scale_steps[i]
if stroke.rotate_ripples and previous_guide_point:
@@ -343,11 +371,20 @@ def _get_start_rotation(line):
def _generate_satin_guide_helper_lines(stroke, outline, guide_line):
count = _get_guided_line_count(stroke, guide_line.center_line)
- spacing = guide_line.center_line.length / (count - 1)
- pairs = guide_line.plot_points_on_rails(spacing)
+
+ spacing = guide_line.center_line.length / max(1, count - 1)
+ if stroke.render_at_rungs:
+ sections = guide_line.flattened_sections
+ pairs = []
+ for (rail0, rail1) in sections:
+ pairs.append((rail0[-1], rail1[-1]))
+ pairs = pairs[:-1]
+ else:
+ pairs = guide_line.plot_points_on_rails(spacing)
point0 = pairs[0][0]
point1 = pairs[0][1]
+
start_rotation = atan2(point1.y - point0.y, point1.x - point0.x)
start_scale = (point1 - point0).length()
outline_center = InkstitchPoint.from_shapely_point(outline.centroid)
@@ -411,6 +448,8 @@ def _get_steps(num_steps, start=0.0, end=1.0, exponent=1, flip=False):
def _repeat_coords(coords, repeats):
+ if not coords:
+ return coords
final_coords = []
for i in range(repeats):
if i % 2 == 1: