From 0fcf8bb97ced8df552cd0283b4ea009b6ca42623 Mon Sep 17 00:00:00 2001 From: Andreas Date: Thu, 21 Oct 2021 16:24:40 +0200 Subject: added tangential and guided fill --- lib/stitches/auto_fill.py | 98 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 19 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 160d927e..71cfd80f 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -12,14 +12,17 @@ import networkx from shapely import geometry as shgeo from shapely.ops import snap from shapely.strtree import STRtree - +from depq import DEPQ from ..debug import debug from ..stitch_plan import Stitch from ..svg import PIXELS_PER_MM +from ..utils import geometry from ..utils.geometry import Point as InkstitchPoint from ..utils.geometry import line_string_to_point_list -from .fill import intersect_region_with_grating, stitch_row +from .fill import intersect_region_with_grating, intersect_region_with_grating_line, stitch_row from .running_stitch import running_stitch +from .PointTransfer import transfer_points_to_surrounding_graph +from .LineStringSampling import raster_line_string_with_priority_points_graph class PathEdge(object): @@ -49,6 +52,7 @@ class PathEdge(object): @debug.time def auto_fill(shape, + line, angle, row_spacing, end_row_spacing, @@ -58,10 +62,13 @@ def auto_fill(shape, skip_last, starting_point, ending_point=None, - underpath=True): + underpath=True, + offset_by_half=True): + #offset_by_half only relevant for line != None; staggers only relevant for line == None! + fill_stitch_graph = [] try: - fill_stitch_graph = build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting_point, ending_point) + fill_stitch_graph = build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, 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) @@ -72,7 +79,7 @@ def auto_fill(shape, 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, staggers, skip_last,line!=None,offset_by_half) return result @@ -106,7 +113,7 @@ def project(shape, coords, outline_index): @debug.time -def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting_point=None, ending_point=None): +def build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, starting_point=None, ending_point=None): """build a graph representation of the grating segments This function builds a specialized graph (as in graph theory) that will @@ -141,18 +148,34 @@ def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting debug.add_layer("auto-fill fill stitch") - # Convert the shape into a set of parallel line segments. - rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) - segments = [segment for row in rows_of_segments for segment in row] + if line == None: + # Convert the shape into a set of parallel line segments. + rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) + else: + rows_of_segments = intersect_region_with_grating_line(shape, line, row_spacing, end_row_spacing) + + #segments = [segment for row in rows_of_segments for segment in row] graph = networkx.MultiGraph() - # First, add the grating segments as edges. We'll use the coordinates - # of the endpoints as nodes, which networkx will add automatically. - for segment in segments: - # networkx allows us to label nodes with arbitrary data. We'll - # mark this one as a grating segment. - graph.add_edge(*segment, key="segment", underpath_edges=[]) + + for i in range(len(rows_of_segments)): + for segment in rows_of_segments[i]: + # First, add the grating segments as edges. We'll use the coordinates + # of the endpoints as nodes, which networkx will add automatically. + + # networkx allows us to label nodes with arbitrary data. We'll + # mark this one as a grating segment. + #graph.add_edge(*segment, key="segment", underpath_edges=[]) + previous_neighbors_ = [(seg[0],seg[-1]) for seg in rows_of_segments[i-1] if i > 0] + next_neighbors_ = [(seg[0],seg[-1]) for seg in rows_of_segments[(i+1)% len(rows_of_segments)] if i < len(rows_of_segments)-1] + + graph.add_edge(segment[0],segment[-1], key="segment", underpath_edges=[], + geometry=shgeo.LineString(segment), previous_neighbors = previous_neighbors_, next_neighbors = next_neighbors_, + projected_points=DEPQ(iterable=None, maxlen=None), already_rastered=False) + + +#fill_stitch_graph[start][end]['segment']['underpath_edges'].append(edge) tag_nodes_with_outline_and_projection(graph, shape, graph.nodes()) add_edges_between_outline_nodes(graph, duplicate_every_other=True) @@ -325,7 +348,8 @@ def get_segments(graph): segments = [] for start, end, key, data in graph.edges(keys=True, data=True): if key == 'segment': - segments.append(shgeo.LineString((start, end))) + segments.append(data["geometry"]) + #segments.append(shgeo.LineString((start, end))) return segments @@ -363,7 +387,8 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): # segments that _might_ intersect ls. Refining the result is # necessary but the STRTree still saves us a ton of time. if segment.crosses(ls): - start, end = segment.coords + start = segment.coords[0] + end = segment.coords[-1] fill_stitch_graph[start][end]['segment']['underpath_edges'].append(edge) # The weight of a travel edge is the length of the line segment. @@ -614,9 +639,28 @@ def travel(travel_graph, start, end, running_stitch_length, skip_last): # stitch. return stitches[1:] +def stitch_line(stitches, stitching_direction, geometry,projected_points, max_stitch_length,row_spacing,skip_last,offset_by_half): + #print(start_point) + #print(geometry[0]) + #if stitching_direction == -1: + # geometry.coords = geometry.coords[::-1] + stitched_line, stitched_line_origin = raster_line_string_with_priority_points_graph(geometry,max_stitch_length,stitching_direction,projected_points,abs(row_spacing),offset_by_half) + + + stitches.append(Stitch(*stitched_line[0], tags=('fill_row_start',))) + for i in range(1,len(stitched_line)): + stitches.append(Stitch(*stitched_line[i], tags=('fill_row'))) + + if not skip_last: + if stitching_direction==1: + stitches.append(Stitch(*geometry.coords[-1], tags=('fill_row_end',))) + else: + stitches.append(Stitch(*geometry.coords[0], tags=('fill_row_end',))) + @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, staggers, skip_last, offsetted_line, offset_by_half): path = collapse_sequential_outline_edges(path) stitches = [] @@ -627,7 +671,23 @@ def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, for edge in path: if edge.is_segment(): - stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers, skip_last) + if offsetted_line: + new_stitches = [] + current_edge = fill_stitch_graph[edge[0]][edge[-1]]['segment'] + path_geometry = current_edge['geometry'] + projected_points = current_edge['projected_points'] + stitching_direction = 1 + if (abs(edge[0][0]-path_geometry.coords[0][0])+abs(edge[0][1]-path_geometry.coords[0][1]) > + abs(edge[0][0]-path_geometry.coords[-1][0])+abs(edge[0][1]-path_geometry.coords[-1][1])): + stitching_direction = -1 + stitch_line(new_stitches, stitching_direction, path_geometry,projected_points, max_stitch_length,row_spacing,skip_last,offset_by_half) + current_edge['already_rastered'] = True + transfer_points_to_surrounding_graph(fill_stitch_graph,current_edge,row_spacing,False,new_stitches,overnext_neighbor=True) + transfer_points_to_surrounding_graph(fill_stitch_graph,current_edge,row_spacing,offset_by_half,new_stitches,overnext_neighbor=False,transfer_forbidden_points=offset_by_half) + + stitches.extend(new_stitches) + else: + 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)) -- cgit v1.2.3 From 125db3f83b3b330df757f7cc0faf6489b3cb348d Mon Sep 17 00:00:00 2001 From: Andreas Date: Fri, 29 Oct 2021 16:18:22 +0200 Subject: Applied style guide --- lib/stitches/auto_fill.py | 140 ++++++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 55 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 71cfd80f..1331ecb2 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -16,7 +16,6 @@ from depq import DEPQ from ..debug import debug from ..stitch_plan import Stitch from ..svg import PIXELS_PER_MM -from ..utils import geometry from ..utils.geometry import Point as InkstitchPoint from ..utils.geometry import line_string_to_point_list from .fill import intersect_region_with_grating, intersect_region_with_grating_line, stitch_row @@ -64,11 +63,12 @@ def auto_fill(shape, ending_point=None, underpath=True, offset_by_half=True): - #offset_by_half only relevant for line != None; staggers only relevant for line == None! + # offset_by_half only relevant for line != None; staggers only relevant for line == None! fill_stitch_graph = [] try: - fill_stitch_graph = build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, starting_point, ending_point) + fill_stitch_graph = build_fill_stitch_graph( + shape, line, angle, row_spacing, end_row_spacing, 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) @@ -76,10 +76,12 @@ def auto_fill(shape, if not graph_is_valid(fill_stitch_graph, shape, max_stitch_length): return fallback(shape, running_stitch_length) - travel_graph = build_travel_graph(fill_stitch_graph, shape, angle, underpath) - path = find_stitch_path(fill_stitch_graph, travel_graph, starting_point, ending_point) + 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,line!=None,offset_by_half) + max_stitch_length, running_stitch_length, staggers, skip_last, line is not None, offset_by_half) return result @@ -97,7 +99,8 @@ def which_outline(shape, coords): point = shgeo.Point(*coords) outlines = list(shape.boundary) outline_indices = list(range(len(outlines))) - closest = min(outline_indices, key=lambda index: outlines[index].distance(point)) + closest = min(outline_indices, + key=lambda index: outlines[index].distance(point)) return closest @@ -148,17 +151,18 @@ def build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, st debug.add_layer("auto-fill fill stitch") - if line == None: + if line is None: # Convert the shape into a set of parallel line segments. - rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) + rows_of_segments = intersect_region_with_grating( + shape, angle, row_spacing, end_row_spacing) else: - rows_of_segments = intersect_region_with_grating_line(shape, line, row_spacing, end_row_spacing) + rows_of_segments = intersect_region_with_grating_line( + shape, line, row_spacing, end_row_spacing) - #segments = [segment for row in rows_of_segments for segment in row] + # segments = [segment for row in rows_of_segments for segment in row] graph = networkx.MultiGraph() - for i in range(len(rows_of_segments)): for segment in rows_of_segments[i]: # First, add the grating segments as edges. We'll use the coordinates @@ -166,16 +170,18 @@ def build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, st # networkx allows us to label nodes with arbitrary data. We'll # mark this one as a grating segment. - #graph.add_edge(*segment, key="segment", underpath_edges=[]) - previous_neighbors_ = [(seg[0],seg[-1]) for seg in rows_of_segments[i-1] if i > 0] - next_neighbors_ = [(seg[0],seg[-1]) for seg in rows_of_segments[(i+1)% len(rows_of_segments)] if i < len(rows_of_segments)-1] + # graph.add_edge(*segment, key="segment", underpath_edges=[]) + previous_neighbors_ = [(seg[0], seg[-1]) + for seg in rows_of_segments[i-1] if i > 0] + next_neighbors_ = [(seg[0], seg[-1]) for seg in rows_of_segments[(i+1) % + len(rows_of_segments)] if i < len(rows_of_segments)-1] - graph.add_edge(segment[0],segment[-1], key="segment", underpath_edges=[], - geometry=shgeo.LineString(segment), previous_neighbors = previous_neighbors_, next_neighbors = next_neighbors_, - projected_points=DEPQ(iterable=None, maxlen=None), already_rastered=False) + graph.add_edge(segment[0], segment[-1], key="segment", underpath_edges=[], + geometry=shgeo.LineString(segment), previous_neighbors=previous_neighbors_, next_neighbors=next_neighbors_, + projected_points=DEPQ(iterable=None, maxlen=None), already_rastered=False) -#fill_stitch_graph[start][end]['segment']['underpath_edges'].append(edge) +# fill_stitch_graph[start][end]['segment']['underpath_edges'].append(edge) tag_nodes_with_outline_and_projection(graph, shape, graph.nodes()) add_edges_between_outline_nodes(graph, duplicate_every_other=True) @@ -205,7 +211,8 @@ def insert_node(graph, shape, point): if key == "outline": edges.append(((start, end), data)) - edge, data = min(edges, key=lambda edge_data: shgeo.LineString(edge_data[0]).distance(projected_point)) + edge, data = min(edges, key=lambda edge_data: shgeo.LineString( + edge_data[0]).distance(projected_point)) graph.remove_edge(*edge, key="outline") graph.add_edge(edge[0], node, key="outline", **data) @@ -218,7 +225,8 @@ def tag_nodes_with_outline_and_projection(graph, shape, nodes): outline_index = which_outline(shape, node) outline_projection = project(shape, node, outline_index) - graph.add_node(node, outline=outline_index, projection=outline_projection) + graph.add_node(node, outline=outline_index, + projection=outline_projection) def add_boundary_travel_nodes(graph, shape): @@ -236,9 +244,11 @@ def add_boundary_travel_nodes(graph, shape): # resolution. A pixel is around a quarter of a millimeter. for i in range(1, int(length)): subpoint = segment.interpolate(i) - graph.add_node((subpoint.x, subpoint.y), projection=outline.project(subpoint), outline=outline_index) + graph.add_node((subpoint.x, subpoint.y), projection=outline.project( + subpoint), outline=outline_index) - graph.add_node((point.x, point.y), projection=outline.project(point), outline=outline_index) + graph.add_node((point.x, point.y), projection=outline.project( + point), outline=outline_index) prev = point @@ -253,7 +263,8 @@ def add_edges_between_outline_nodes(graph, duplicate_every_other=False): outline. """ - nodes = list(graph.nodes(data=True)) # returns a list of tuples: [(node, {data}), (node, {data}) ...] + # returns a list of tuples: [(node, {data}), (node, {data}) ...] + nodes = list(graph.nodes(data=True)) nodes.sort(key=lambda node: (node[1]['outline'], node[1]['projection'])) for outline_index, nodes in groupby(nodes, key=lambda node: node[1]['outline']): @@ -318,7 +329,8 @@ def build_travel_graph(fill_stitch_graph, shape, fill_stitch_angle, underpath): graph.add_nodes_from(fill_stitch_graph.nodes(data=True)) if underpath: - boundary_points, travel_edges = build_travel_edges(shape, fill_stitch_angle) + boundary_points, travel_edges = build_travel_edges( + shape, fill_stitch_angle) # This will ensure that a path traveling inside the shape can reach its # target on the outline, which will be one of the points added above. @@ -349,7 +361,7 @@ def get_segments(graph): for start, end, key, data in graph.edges(keys=True, data=True): if key == 'segment': segments.append(data["geometry"]) - #segments.append(shgeo.LineString((start, end))) + # segments.append(shgeo.LineString((start, end))) return segments @@ -371,7 +383,8 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): # This makes the distance calculations below a bit faster. We're # not looking for high precision anyway. - outline = shape.boundary.simplify(0.5 * PIXELS_PER_MM, preserve_topology=False) + outline = shape.boundary.simplify( + 0.5 * PIXELS_PER_MM, preserve_topology=False) for ls in travel_edges: # In most cases, ls will be a simple line segment. If we're @@ -389,7 +402,8 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): if segment.crosses(ls): start = segment.coords[0] end = segment.coords[-1] - fill_stitch_graph[start][end]['segment']['underpath_edges'].append(edge) + fill_stitch_graph[start][end]['segment']['underpath_edges'].append( + edge) # The weight of a travel edge is the length of the line segment. weight = p1.distance(p2) @@ -458,9 +472,12 @@ def build_travel_edges(shape, fill_angle): else: scale = 1.0 - grating1 = travel_grating(shape, fill_angle + math.pi / 4, scale * 2 * PIXELS_PER_MM) - grating2 = travel_grating(shape, fill_angle - math.pi / 4, scale * 2 * PIXELS_PER_MM) - grating3 = travel_grating(shape, fill_angle - math.pi / 2, scale * math.sqrt(2) * PIXELS_PER_MM) + grating1 = travel_grating( + shape, fill_angle + math.pi / 4, scale * 2 * PIXELS_PER_MM) + grating2 = travel_grating( + shape, fill_angle - math.pi / 4, scale * 2 * PIXELS_PER_MM) + grating3 = travel_grating( + shape, fill_angle - math.pi / 2, scale * math.sqrt(2) * PIXELS_PER_MM) debug.add_layer("auto-fill travel") debug.log_line_strings(grating1, "grating1") @@ -471,10 +488,12 @@ def build_travel_edges(shape, fill_angle): for ls in mls for coord in ls.coords] - diagonal_edges = ensure_multi_line_string(grating1.symmetric_difference(grating2)) + diagonal_edges = ensure_multi_line_string( + grating1.symmetric_difference(grating2)) # without this, floating point inaccuracies prevent the intersection points from lining up perfectly. - vertical_edges = ensure_multi_line_string(snap(grating3.difference(grating1), diagonal_edges, 0.005)) + vertical_edges = ensure_multi_line_string( + snap(grating3.difference(grating1), diagonal_edges, 0.005)) return endpoints, chain(diagonal_edges, vertical_edges) @@ -536,7 +555,8 @@ def find_stitch_path(graph, travel_graph, starting_point=None, ending_point=None last_vertex, last_key = current_vertex, current_key vertex_stack.pop() else: - ignore, next_vertex, next_key = pick_edge(graph.edges(current_vertex, keys=True)) + ignore, next_vertex, next_key = pick_edge( + graph.edges(current_vertex, keys=True)) vertex_stack.append((next_vertex, next_key)) graph.remove_edge(current_vertex, next_vertex, next_key) @@ -565,7 +585,8 @@ def find_stitch_path(graph, travel_graph, starting_point=None, ending_point=None # relevant in the case that the user specifies an underlay with an inset # value, because the starting point (and possibly ending point) can be # inside the shape. - outline_nodes = [node for node, outline in travel_graph.nodes(data="outline") if outline is not None] + outline_nodes = [node for node, outline in travel_graph.nodes( + data="outline") if outline is not None] real_end = nearest_node(outline_nodes, ending_point) path.append(PathEdge((ending_node, real_end), key="outline")) @@ -639,28 +660,31 @@ def travel(travel_graph, start, end, running_stitch_length, skip_last): # stitch. return stitches[1:] -def stitch_line(stitches, stitching_direction, geometry,projected_points, max_stitch_length,row_spacing,skip_last,offset_by_half): - #print(start_point) - #print(geometry[0]) - #if stitching_direction == -1: - # geometry.coords = geometry.coords[::-1] - stitched_line, stitched_line_origin = raster_line_string_with_priority_points_graph(geometry,max_stitch_length,stitching_direction,projected_points,abs(row_spacing),offset_by_half) +def stitch_line(stitches, stitching_direction, geometry, projected_points, max_stitch_length, row_spacing, skip_last, offset_by_half): + # print(start_point) + # print(geometry[0]) + # if stitching_direction == -1: + # geometry.coords = geometry.coords[::-1] + stitched_line, stitched_line_origin = raster_line_string_with_priority_points_graph( + geometry, max_stitch_length, stitching_direction, projected_points, abs(row_spacing), offset_by_half) stitches.append(Stitch(*stitched_line[0], tags=('fill_row_start',))) - for i in range(1,len(stitched_line)): + for i in range(1, len(stitched_line)): stitches.append(Stitch(*stitched_line[i], tags=('fill_row'))) - + if not skip_last: - if stitching_direction==1: - stitches.append(Stitch(*geometry.coords[-1], tags=('fill_row_end',))) + if stitching_direction == 1: + stitches.append( + Stitch(*geometry.coords[-1], tags=('fill_row_end',))) else: - stitches.append(Stitch(*geometry.coords[0], tags=('fill_row_end',))) + stitches.append( + Stitch(*geometry.coords[0], tags=('fill_row_end',))) @debug.time -def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, - running_stitch_length, staggers, skip_last, offsetted_line, offset_by_half): +def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, + running_stitch_length, staggers, skip_last, offsetted_line, offset_by_half): path = collapse_sequential_outline_edges(path) stitches = [] @@ -678,18 +702,24 @@ def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, projected_points = current_edge['projected_points'] stitching_direction = 1 if (abs(edge[0][0]-path_geometry.coords[0][0])+abs(edge[0][1]-path_geometry.coords[0][1]) > - abs(edge[0][0]-path_geometry.coords[-1][0])+abs(edge[0][1]-path_geometry.coords[-1][1])): + abs(edge[0][0]-path_geometry.coords[-1][0])+abs(edge[0][1]-path_geometry.coords[-1][1])): stitching_direction = -1 - stitch_line(new_stitches, stitching_direction, path_geometry,projected_points, max_stitch_length,row_spacing,skip_last,offset_by_half) + stitch_line(new_stitches, stitching_direction, path_geometry, projected_points, + max_stitch_length, row_spacing, skip_last, offset_by_half) current_edge['already_rastered'] = True - transfer_points_to_surrounding_graph(fill_stitch_graph,current_edge,row_spacing,False,new_stitches,overnext_neighbor=True) - transfer_points_to_surrounding_graph(fill_stitch_graph,current_edge,row_spacing,offset_by_half,new_stitches,overnext_neighbor=False,transfer_forbidden_points=offset_by_half) + transfer_points_to_surrounding_graph( + fill_stitch_graph, current_edge, row_spacing, False, new_stitches, overnext_neighbor=True) + transfer_points_to_surrounding_graph(fill_stitch_graph, current_edge, row_spacing, offset_by_half, + new_stitches, overnext_neighbor=False, transfer_forbidden_points=offset_by_half) stitches.extend(new_stitches) else: - 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', [])) + 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, skip_last)) return stitches -- cgit v1.2.3 From 1a1939b5daf421116791b5ae45434cb1aba2ea38 Mon Sep 17 00:00:00 2001 From: Andreas Date: Sat, 30 Oct 2021 21:28:40 +0200 Subject: Bug fixing + refactoring --- lib/stitches/auto_fill.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 1331ecb2..a0d9637a 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -21,7 +21,7 @@ from ..utils.geometry import line_string_to_point_list from .fill import intersect_region_with_grating, intersect_region_with_grating_line, stitch_row from .running_stitch import running_stitch from .PointTransfer import transfer_points_to_surrounding_graph -from .LineStringSampling import raster_line_string_with_priority_points_graph +from .LineStringSampling import raster_line_string_with_priority_points class PathEdge(object): @@ -666,8 +666,12 @@ def stitch_line(stitches, stitching_direction, geometry, projected_points, max_s # print(geometry[0]) # if stitching_direction == -1: # geometry.coords = geometry.coords[::-1] - stitched_line, stitched_line_origin = raster_line_string_with_priority_points_graph( - geometry, max_stitch_length, stitching_direction, projected_points, abs(row_spacing), offset_by_half) + if stitching_direction == 1: + stitched_line, _ = raster_line_string_with_priority_points( + geometry, 0.0, geometry.length, max_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) + else: + stitched_line, _ = raster_line_string_with_priority_points( + geometry, geometry.length, 0.0, max_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) stitches.append(Stitch(*stitched_line[0], tags=('fill_row_start',))) for i in range(1, len(stitched_line)): -- cgit v1.2.3 From e15bce1401af8b80a72aa528714e5667edbd9429 Mon Sep 17 00:00:00 2001 From: Andreas Date: Fri, 19 Nov 2021 19:32:29 +0100 Subject: minor changes --- lib/stitches/auto_fill.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index a0d9637a..95cc9103 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -165,6 +165,10 @@ def build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, st for i in range(len(rows_of_segments)): for segment in rows_of_segments[i]: + if abs(segment[0][0]-396.5081896849414) < 0.01: + print("HIER") + if segment[0][0] == segment[-1][0] and segment[0][1] == segment[-1][1]: + print("FEHLER HIER!") # First, add the grating segments as edges. We'll use the coordinates # of the endpoints as nodes, which networkx will add automatically. @@ -674,7 +678,7 @@ def stitch_line(stitches, stitching_direction, geometry, projected_points, max_s geometry, geometry.length, 0.0, max_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) stitches.append(Stitch(*stitched_line[0], tags=('fill_row_start',))) - for i in range(1, len(stitched_line)): + for i in range(1, len(stitched_line)-1): stitches.append(Stitch(*stitched_line[i], tags=('fill_row'))) if not skip_last: @@ -684,6 +688,8 @@ def stitch_line(stitches, stitching_direction, geometry, projected_points, max_s else: stitches.append( Stitch(*geometry.coords[0], tags=('fill_row_end',))) + if stitches[-1].x == stitches[-2].x and stitches[-1].y == stitches[-2].y: + print("FEHLER") @debug.time -- cgit v1.2.3 From 8966fa1919df5c2070cebebd080b54a56c7001b1 Mon Sep 17 00:00:00 2001 From: Andreas Date: Fri, 19 Nov 2021 19:42:28 +0100 Subject: minor changes --- lib/stitches/auto_fill.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 95cc9103..b63f4be1 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -165,10 +165,10 @@ def build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, st for i in range(len(rows_of_segments)): for segment in rows_of_segments[i]: - if abs(segment[0][0]-396.5081896849414) < 0.01: - print("HIER") - if segment[0][0] == segment[-1][0] and segment[0][1] == segment[-1][1]: - print("FEHLER HIER!") + # if abs(segment[0][0]-396.5081896849414) < 0.01: + # print("HIER") + # if segment[0][0] == segment[-1][0] and segment[0][1] == segment[-1][1]: + # print("FEHLER HIER!") # First, add the grating segments as edges. We'll use the coordinates # of the endpoints as nodes, which networkx will add automatically. @@ -688,8 +688,8 @@ def stitch_line(stitches, stitching_direction, geometry, projected_points, max_s else: stitches.append( Stitch(*geometry.coords[0], tags=('fill_row_end',))) - if stitches[-1].x == stitches[-2].x and stitches[-1].y == stitches[-2].y: - print("FEHLER") + # if stitches[-1].x == stitches[-2].x and stitches[-1].y == stitches[-2].y: + # print("FEHLER") @debug.time -- cgit v1.2.3 From d514eac81937bb64815239dd3aa96e38d6556a32 Mon Sep 17 00:00:00 2001 From: Andreas Date: Wed, 2 Feb 2022 21:19:31 +0100 Subject: adjusting namings --- lib/stitches/auto_fill.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index b63f4be1..7af99560 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -20,8 +20,8 @@ from ..utils.geometry import Point as InkstitchPoint from ..utils.geometry import line_string_to_point_list from .fill import intersect_region_with_grating, intersect_region_with_grating_line, stitch_row from .running_stitch import running_stitch -from .PointTransfer import transfer_points_to_surrounding_graph -from .LineStringSampling import raster_line_string_with_priority_points +from .point_transfer import transfer_points_to_surrounding_graph +from .sample_linestring import raster_line_string_with_priority_points class PathEdge(object): @@ -165,10 +165,6 @@ def build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, st for i in range(len(rows_of_segments)): for segment in rows_of_segments[i]: - # if abs(segment[0][0]-396.5081896849414) < 0.01: - # print("HIER") - # if segment[0][0] == segment[-1][0] and segment[0][1] == segment[-1][1]: - # print("FEHLER HIER!") # First, add the grating segments as edges. We'll use the coordinates # of the endpoints as nodes, which networkx will add automatically. @@ -666,10 +662,6 @@ def travel(travel_graph, start, end, running_stitch_length, skip_last): def stitch_line(stitches, stitching_direction, geometry, projected_points, max_stitch_length, row_spacing, skip_last, offset_by_half): - # print(start_point) - # print(geometry[0]) - # if stitching_direction == -1: - # geometry.coords = geometry.coords[::-1] if stitching_direction == 1: stitched_line, _ = raster_line_string_with_priority_points( geometry, 0.0, geometry.length, max_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) @@ -688,8 +680,6 @@ def stitch_line(stitches, stitching_direction, geometry, projected_points, max_s else: stitches.append( Stitch(*geometry.coords[0], tags=('fill_row_end',))) - # if stitches[-1].x == stitches[-2].x and stitches[-1].y == stitches[-2].y: - # print("FEHLER") @debug.time -- cgit v1.2.3 From 515ed3ea2fc8357482527d6e4a170db154baa205 Mon Sep 17 00:00:00 2001 From: Kaalleen Date: Fri, 18 Feb 2022 15:36:01 +0100 Subject: separate guided fill methods --- lib/stitches/auto_fill.py | 114 +++++++++------------------------------------- 1 file changed, 21 insertions(+), 93 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 7af99560..52dc6a81 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -12,16 +12,14 @@ import networkx from shapely import geometry as shgeo from shapely.ops import snap from shapely.strtree import STRtree -from depq import DEPQ + from ..debug import debug from ..stitch_plan import Stitch from ..svg import PIXELS_PER_MM from ..utils.geometry import Point as InkstitchPoint from ..utils.geometry import line_string_to_point_list -from .fill import intersect_region_with_grating, intersect_region_with_grating_line, stitch_row +from .fill import intersect_region_with_grating, stitch_row from .running_stitch import running_stitch -from .point_transfer import transfer_points_to_surrounding_graph -from .sample_linestring import raster_line_string_with_priority_points class PathEdge(object): @@ -51,7 +49,6 @@ class PathEdge(object): @debug.time def auto_fill(shape, - line, angle, row_spacing, end_row_spacing, @@ -61,14 +58,10 @@ def auto_fill(shape, skip_last, starting_point, ending_point=None, - underpath=True, - offset_by_half=True): - # offset_by_half only relevant for line != None; staggers only relevant for line == None! - + underpath=True): fill_stitch_graph = [] try: - fill_stitch_graph = build_fill_stitch_graph( - shape, line, angle, row_spacing, end_row_spacing, starting_point, ending_point) + fill_stitch_graph = build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, 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) @@ -81,7 +74,7 @@ def auto_fill(shape, 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, line is not None, offset_by_half) + max_stitch_length, running_stitch_length, staggers, skip_last) return result @@ -116,7 +109,7 @@ def project(shape, coords, outline_index): @debug.time -def build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, starting_point=None, ending_point=None): +def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting_point=None, ending_point=None): """build a graph representation of the grating segments This function builds a specialized graph (as in graph theory) that will @@ -151,37 +144,18 @@ def build_fill_stitch_graph(shape, line, angle, row_spacing, end_row_spacing, st debug.add_layer("auto-fill fill stitch") - if line is None: - # Convert the shape into a set of parallel line segments. - rows_of_segments = intersect_region_with_grating( - shape, angle, row_spacing, end_row_spacing) - else: - rows_of_segments = intersect_region_with_grating_line( - shape, line, row_spacing, end_row_spacing) - - # segments = [segment for row in rows_of_segments for segment in row] + # Convert the shape into a set of parallel line segments. + rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) + segments = [segment for row in rows_of_segments for segment in row] graph = networkx.MultiGraph() - for i in range(len(rows_of_segments)): - for segment in rows_of_segments[i]: - # First, add the grating segments as edges. We'll use the coordinates - # of the endpoints as nodes, which networkx will add automatically. - - # networkx allows us to label nodes with arbitrary data. We'll - # mark this one as a grating segment. - # graph.add_edge(*segment, key="segment", underpath_edges=[]) - previous_neighbors_ = [(seg[0], seg[-1]) - for seg in rows_of_segments[i-1] if i > 0] - next_neighbors_ = [(seg[0], seg[-1]) for seg in rows_of_segments[(i+1) % - len(rows_of_segments)] if i < len(rows_of_segments)-1] - - graph.add_edge(segment[0], segment[-1], key="segment", underpath_edges=[], - geometry=shgeo.LineString(segment), previous_neighbors=previous_neighbors_, next_neighbors=next_neighbors_, - projected_points=DEPQ(iterable=None, maxlen=None), already_rastered=False) - - -# fill_stitch_graph[start][end]['segment']['underpath_edges'].append(edge) + # First, add the grating segments as edges. We'll use the coordinates + # of the endpoints as nodes, which networkx will add automatically. + for segment in segments: + # networkx allows us to label nodes with arbitrary data. We'll + # mark this one as a grating segment. + graph.add_edge(*segment, key="segment", underpath_edges=[]) tag_nodes_with_outline_and_projection(graph, shape, graph.nodes()) add_edges_between_outline_nodes(graph, duplicate_every_other=True) @@ -360,8 +334,7 @@ def get_segments(graph): segments = [] for start, end, key, data in graph.edges(keys=True, data=True): if key == 'segment': - segments.append(data["geometry"]) - # segments.append(shgeo.LineString((start, end))) + segments.append(shgeo.LineString((start, end))) return segments @@ -400,10 +373,8 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): # segments that _might_ intersect ls. Refining the result is # necessary but the STRTree still saves us a ton of time. if segment.crosses(ls): - start = segment.coords[0] - end = segment.coords[-1] - fill_stitch_graph[start][end]['segment']['underpath_edges'].append( - edge) + start, end = segment.coords + fill_stitch_graph[start][end]['segment']['underpath_edges'].append(edge) # The weight of a travel edge is the length of the line segment. weight = p1.distance(p2) @@ -661,30 +632,8 @@ def travel(travel_graph, start, end, running_stitch_length, skip_last): return stitches[1:] -def stitch_line(stitches, stitching_direction, geometry, projected_points, max_stitch_length, row_spacing, skip_last, offset_by_half): - if stitching_direction == 1: - stitched_line, _ = raster_line_string_with_priority_points( - geometry, 0.0, geometry.length, max_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) - else: - stitched_line, _ = raster_line_string_with_priority_points( - geometry, geometry.length, 0.0, max_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) - - stitches.append(Stitch(*stitched_line[0], tags=('fill_row_start',))) - for i in range(1, len(stitched_line)-1): - stitches.append(Stitch(*stitched_line[i], tags=('fill_row'))) - - if not skip_last: - if stitching_direction == 1: - stitches.append( - Stitch(*geometry.coords[-1], tags=('fill_row_end',))) - else: - stitches.append( - Stitch(*geometry.coords[0], tags=('fill_row_end',))) - - @debug.time -def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, - running_stitch_length, staggers, skip_last, offsetted_line, offset_by_half): +def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last): path = collapse_sequential_outline_edges(path) stitches = [] @@ -695,29 +644,8 @@ def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, for edge in path: if edge.is_segment(): - if offsetted_line: - new_stitches = [] - current_edge = fill_stitch_graph[edge[0]][edge[-1]]['segment'] - path_geometry = current_edge['geometry'] - projected_points = current_edge['projected_points'] - stitching_direction = 1 - if (abs(edge[0][0]-path_geometry.coords[0][0])+abs(edge[0][1]-path_geometry.coords[0][1]) > - abs(edge[0][0]-path_geometry.coords[-1][0])+abs(edge[0][1]-path_geometry.coords[-1][1])): - stitching_direction = -1 - stitch_line(new_stitches, stitching_direction, path_geometry, projected_points, - max_stitch_length, row_spacing, skip_last, offset_by_half) - current_edge['already_rastered'] = True - transfer_points_to_surrounding_graph( - fill_stitch_graph, current_edge, row_spacing, False, new_stitches, overnext_neighbor=True) - transfer_points_to_surrounding_graph(fill_stitch_graph, current_edge, row_spacing, offset_by_half, - new_stitches, overnext_neighbor=False, transfer_forbidden_points=offset_by_half) - - stitches.extend(new_stitches) - else: - 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', [])) + 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)) -- cgit v1.2.3 From f4c47a8e226af11852e2e530c9b4a6a6403bff07 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 6 Apr 2022 07:56:58 -0400 Subject: generalize get_segments and process_travel_edges --- lib/stitches/auto_fill.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 52dc6a81..a8118039 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -155,7 +155,7 @@ def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting for segment in segments: # networkx allows us to label nodes with arbitrary data. We'll # mark this one as a grating segment. - graph.add_edge(*segment, key="segment", underpath_edges=[]) + graph.add_edge(*segment, key="segment", underpath_edges=[], geometry=shgeo.LineString(segment)) tag_nodes_with_outline_and_projection(graph, shape, graph.nodes()) add_edges_between_outline_nodes(graph, duplicate_every_other=True) @@ -334,7 +334,7 @@ def get_segments(graph): segments = [] for start, end, key, data in graph.edges(keys=True, data=True): if key == 'segment': - segments.append(shgeo.LineString((start, end))) + segments.append(data["geometry"]) return segments @@ -373,7 +373,8 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): # segments that _might_ intersect ls. Refining the result is # necessary but the STRTree still saves us a ton of time. if segment.crosses(ls): - start, end = segment.coords + start = segment.coords[0] + end = segment.coords[-1] fill_stitch_graph[start][end]['segment']['underpath_edges'].append(edge) # The weight of a travel edge is the length of the line segment. -- cgit v1.2.3 From e6fcf11035d3d953c2b07e6d153a1225f79cb781 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sun, 1 May 2022 16:31:51 -0400 Subject: fix some shapelydeprecations --- lib/stitches/auto_fill.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index a8118039..27328bab 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -90,7 +90,7 @@ def which_outline(shape, coords): # fail sometimes. point = shgeo.Point(*coords) - outlines = list(shape.boundary) + outlines = list(shape.boundary.geoms) outline_indices = list(range(len(outlines))) closest = min(outline_indices, key=lambda index: outlines[index].distance(point)) @@ -104,7 +104,7 @@ def project(shape, coords, outline_index): This returns the distance along the outline at which the point resides. """ - outline = list(shape.boundary)[outline_index] + outline = list(shape.boundary.geoms)[outline_index] return outline.project(shgeo.Point(*coords)) @@ -204,7 +204,7 @@ def tag_nodes_with_outline_and_projection(graph, shape, nodes): def add_boundary_travel_nodes(graph, shape): - for outline_index, outline in enumerate(shape.boundary): + for outline_index, outline in enumerate(shape.boundary.geoms): prev = None for point in outline.coords: point = shgeo.Point(point) -- cgit v1.2.3 From bd8cb0d1ff2ce1f17ed8d3a5eaca19f8e8652ad0 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 2 May 2022 14:38:33 -0400 Subject: use running_stitch instead for guided fill --- lib/stitches/auto_fill.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 27328bab..35412e93 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -59,9 +59,9 @@ def auto_fill(shape, starting_point, ending_point=None, underpath=True): - fill_stitch_graph = [] try: - fill_stitch_graph = build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting_point, ending_point) + segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) + 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) @@ -109,7 +109,7 @@ def project(shape, coords, outline_index): @debug.time -def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting_point=None, ending_point=None): +def build_fill_stitch_graph(shape, segments, starting_point=None, ending_point=None): """build a graph representation of the grating segments This function builds a specialized graph (as in graph theory) that will @@ -144,10 +144,6 @@ def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting debug.add_layer("auto-fill fill stitch") - # Convert the shape into a set of parallel line segments. - rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) - segments = [segment for row in rows_of_segments for segment in row] - graph = networkx.MultiGraph() # First, add the grating segments as edges. We'll use the coordinates @@ -155,7 +151,7 @@ def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting for segment in segments: # networkx allows us to label nodes with arbitrary data. We'll # mark this one as a grating segment. - graph.add_edge(*segment, key="segment", underpath_edges=[], geometry=shgeo.LineString(segment)) + graph.add_edge(segment[0], segment[-1], key="segment", underpath_edges=[], geometry=shgeo.LineString(segment)) tag_nodes_with_outline_and_projection(graph, shape, graph.nodes()) add_edges_between_outline_nodes(graph, duplicate_every_other=True) @@ -177,7 +173,7 @@ def insert_node(graph, shape, point): point = tuple(point) outline = which_outline(shape, point) projection = project(shape, point, outline) - projected_point = list(shape.boundary)[outline].interpolate(projection) + projected_point = list(shape.boundary.geoms)[outline].interpolate(projection) node = (projected_point.x, projected_point.y) edges = [] @@ -395,10 +391,9 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): def travel_grating(shape, angle, row_spacing): - rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing) - segments = list(chain(*rows_of_segments)) + segments = intersect_region_with_grating(shape, angle, row_spacing) - return shgeo.MultiLineString(segments) + return shgeo.MultiLineString(list(segments)) def ensure_multi_line_string(thing): @@ -457,7 +452,7 @@ def build_travel_edges(shape, fill_angle): debug.log_line_strings(grating3, "grating3") endpoints = [coord for mls in (grating1, grating2, grating3) - for ls in mls + for ls in mls.geoms for coord in ls.coords] diagonal_edges = ensure_multi_line_string( @@ -467,7 +462,7 @@ def build_travel_edges(shape, fill_angle): vertical_edges = ensure_multi_line_string( snap(grating3.difference(grating1), diagonal_edges, 0.005)) - return endpoints, chain(diagonal_edges, vertical_edges) + return endpoints, chain(diagonal_edges.geoms, vertical_edges.geoms) def nearest_node(nodes, point, attr=None): -- cgit v1.2.3 From aeeaf72338e2d7645309725be641d552a3c56190 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 3 May 2022 16:58:55 -0400 Subject: wip --- lib/stitches/auto_fill.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 35412e93..630178c4 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -16,8 +16,7 @@ from shapely.strtree import STRtree from ..debug import debug from ..stitch_plan import Stitch from ..svg import PIXELS_PER_MM -from ..utils.geometry import Point as InkstitchPoint -from ..utils.geometry import line_string_to_point_list +from ..utils.geometry import Point as InkstitchPoint, line_string_to_point_list, ensure_multi_line_string from .fill import intersect_region_with_grating, stitch_row from .running_stitch import running_stitch @@ -396,15 +395,6 @@ def travel_grating(shape, angle, row_spacing): return shgeo.MultiLineString(list(segments)) -def ensure_multi_line_string(thing): - """Given either a MultiLineString or a single LineString, return a MultiLineString""" - - if isinstance(thing, shgeo.LineString): - return shgeo.MultiLineString([thing]) - else: - return thing - - def build_travel_edges(shape, fill_angle): r"""Given a graph, compute the interior travel edges. -- cgit v1.2.3 From a275d49a24dc91b734c6dbee1e29157bfd0d59d5 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 5 May 2022 22:53:31 -0400 Subject: tangential->contour, fix legacy, remove unused params --- lib/stitches/auto_fill.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 630178c4..b3b9434f 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -59,7 +59,8 @@ def auto_fill(shape, ending_point=None, underpath=True): try: - segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) + rows = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) + segments = [segment for row in rows for segment in row] 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 @@ -390,7 +391,8 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): def travel_grating(shape, angle, row_spacing): - segments = intersect_region_with_grating(shape, angle, row_spacing) + rows = intersect_region_with_grating(shape, angle, row_spacing) + segments = [segment for row in rows for segment in row] return shgeo.MultiLineString(list(segments)) -- cgit v1.2.3 From 672bded1259589d609d1a6656df5537c5da20569 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 6 May 2022 21:03:56 -0400 Subject: shapely geoms fixes --- lib/stitches/auto_fill.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index b3b9434f..1d72e710 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -90,7 +90,7 @@ def which_outline(shape, coords): # fail sometimes. point = shgeo.Point(*coords) - outlines = list(shape.boundary.geoms) + outlines = ensure_multi_line_string(shape.boundary).geoms outline_indices = list(range(len(outlines))) closest = min(outline_indices, key=lambda index: outlines[index].distance(point)) @@ -104,7 +104,7 @@ def project(shape, coords, outline_index): This returns the distance along the outline at which the point resides. """ - outline = list(shape.boundary.geoms)[outline_index] + outline = ensure_multi_line_string(shape.boundary).geoms[outline_index] return outline.project(shgeo.Point(*coords)) @@ -173,7 +173,7 @@ def insert_node(graph, shape, point): point = tuple(point) outline = which_outline(shape, point) projection = project(shape, point, outline) - projected_point = list(shape.boundary.geoms)[outline].interpolate(projection) + projected_point = ensure_multi_line_string(shape.boundary).geoms[outline].interpolate(projection) node = (projected_point.x, projected_point.y) edges = [] @@ -200,7 +200,8 @@ def tag_nodes_with_outline_and_projection(graph, shape, nodes): def add_boundary_travel_nodes(graph, shape): - for outline_index, outline in enumerate(shape.boundary.geoms): + outlines = ensure_multi_line_string(shape.boundary).geoms + for outline_index, outline in enumerate(outlines): prev = None for point in outline.coords: point = shgeo.Point(point) @@ -265,7 +266,10 @@ def fallback(shape, running_stitch_length): matter. """ - return running_stitch(line_string_to_point_list(shape.boundary[0]), 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) @debug.time -- cgit v1.2.3 From b30fce85dbdb4097bb9e01c3d68a77e0c50dd80a Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 7 May 2022 16:20:15 -0400 Subject: undo aggressive line wrapping --- lib/stitches/auto_fill.py | 48 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) (limited to 'lib/stitches/auto_fill.py') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 1d72e710..65b1e06d 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -69,10 +69,8 @@ def auto_fill(shape, if not graph_is_valid(fill_stitch_graph, shape, max_stitch_length): return fallback(shape, running_stitch_length) - travel_graph = build_travel_graph( - fill_stitch_graph, shape, angle, underpath) - path = find_stitch_path( - fill_stitch_graph, travel_graph, starting_point, ending_point) + 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) @@ -181,8 +179,7 @@ def insert_node(graph, shape, point): if key == "outline": edges.append(((start, end), data)) - edge, data = min(edges, key=lambda edge_data: shgeo.LineString( - edge_data[0]).distance(projected_point)) + edge, data = min(edges, key=lambda edge_data: shgeo.LineString(edge_data[0]).distance(projected_point)) graph.remove_edge(*edge, key="outline") graph.add_edge(edge[0], node, key="outline", **data) @@ -195,8 +192,7 @@ def tag_nodes_with_outline_and_projection(graph, shape, nodes): outline_index = which_outline(shape, node) outline_projection = project(shape, node, outline_index) - graph.add_node(node, outline=outline_index, - projection=outline_projection) + graph.add_node(node, outline=outline_index, projection=outline_projection) def add_boundary_travel_nodes(graph, shape): @@ -215,11 +211,9 @@ def add_boundary_travel_nodes(graph, shape): # resolution. A pixel is around a quarter of a millimeter. for i in range(1, int(length)): subpoint = segment.interpolate(i) - graph.add_node((subpoint.x, subpoint.y), projection=outline.project( - subpoint), outline=outline_index) + graph.add_node((subpoint.x, subpoint.y), projection=outline.project(subpoint), outline=outline_index) - graph.add_node((point.x, point.y), projection=outline.project( - point), outline=outline_index) + graph.add_node((point.x, point.y), projection=outline.project(point), outline=outline_index) prev = point @@ -303,8 +297,7 @@ def build_travel_graph(fill_stitch_graph, shape, fill_stitch_angle, underpath): graph.add_nodes_from(fill_stitch_graph.nodes(data=True)) if underpath: - boundary_points, travel_edges = build_travel_edges( - shape, fill_stitch_angle) + boundary_points, travel_edges = build_travel_edges(shape, fill_stitch_angle) # This will ensure that a path traveling inside the shape can reach its # target on the outline, which will be one of the points added above. @@ -356,8 +349,7 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): # This makes the distance calculations below a bit faster. We're # not looking for high precision anyway. - outline = shape.boundary.simplify( - 0.5 * PIXELS_PER_MM, preserve_topology=False) + outline = shape.boundary.simplify(0.5 * PIXELS_PER_MM, preserve_topology=False) for ls in travel_edges: # In most cases, ls will be a simple line segment. If we're @@ -435,12 +427,9 @@ def build_travel_edges(shape, fill_angle): else: scale = 1.0 - grating1 = travel_grating( - shape, fill_angle + math.pi / 4, scale * 2 * PIXELS_PER_MM) - grating2 = travel_grating( - shape, fill_angle - math.pi / 4, scale * 2 * PIXELS_PER_MM) - grating3 = travel_grating( - shape, fill_angle - math.pi / 2, scale * math.sqrt(2) * PIXELS_PER_MM) + grating1 = travel_grating(shape, fill_angle + math.pi / 4, scale * 2 * PIXELS_PER_MM) + grating2 = travel_grating(shape, fill_angle - math.pi / 4, scale * 2 * PIXELS_PER_MM) + grating3 = travel_grating(shape, fill_angle - math.pi / 2, scale * math.sqrt(2) * PIXELS_PER_MM) debug.add_layer("auto-fill travel") debug.log_line_strings(grating1, "grating1") @@ -451,12 +440,10 @@ def build_travel_edges(shape, fill_angle): for ls in mls.geoms for coord in ls.coords] - diagonal_edges = ensure_multi_line_string( - grating1.symmetric_difference(grating2)) + diagonal_edges = ensure_multi_line_string(grating1.symmetric_difference(grating2)) # without this, floating point inaccuracies prevent the intersection points from lining up perfectly. - vertical_edges = ensure_multi_line_string( - snap(grating3.difference(grating1), diagonal_edges, 0.005)) + vertical_edges = ensure_multi_line_string(snap(grating3.difference(grating1), diagonal_edges, 0.005)) return endpoints, chain(diagonal_edges.geoms, vertical_edges.geoms) @@ -518,8 +505,7 @@ def find_stitch_path(graph, travel_graph, starting_point=None, ending_point=None last_vertex, last_key = current_vertex, current_key vertex_stack.pop() else: - ignore, next_vertex, next_key = pick_edge( - graph.edges(current_vertex, keys=True)) + ignore, next_vertex, next_key = pick_edge(graph.edges(current_vertex, keys=True)) vertex_stack.append((next_vertex, next_key)) graph.remove_edge(current_vertex, next_vertex, next_key) @@ -548,8 +534,7 @@ def find_stitch_path(graph, travel_graph, starting_point=None, ending_point=None # relevant in the case that the user specifies an underlay with an inset # value, because the starting point (and possibly ending point) can be # inside the shape. - outline_nodes = [node for node, outline in travel_graph.nodes( - data="outline") if outline is not None] + outline_nodes = [node for node, outline in travel_graph.nodes(data="outline") if outline is not None] real_end = nearest_node(outline_nodes, ending_point) path.append(PathEdge((ending_node, real_end), key="outline")) @@ -639,7 +624,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, skip_last)) return stitches -- cgit v1.2.3