diff options
| author | Lex Neva <github.com@lexneva.name> | 2018-06-23 21:41:00 -0400 |
|---|---|---|
| committer | Lex Neva <github.com@lexneva.name> | 2018-06-23 21:41:00 -0400 |
| commit | e0a2b31ede8b412c83747fef168c3dda0d07087f (patch) | |
| tree | eaa1bd35ebe66b869f7c12d83bd3e49193bebe14 | |
| parent | b7cb98d277bcad6a9b418fc11ee716bbc754e69d (diff) | |
fix collapse_sequential_outline_edges
| -rw-r--r-- | lib/stitches/auto_fill.py | 77 |
1 files changed, 52 insertions, 25 deletions
diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index c558fa1b..42fd1ef5 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -2,7 +2,7 @@ import sys import shapely import networkx import math -from itertools import groupby +from itertools import groupby, izip from collections import deque from .fill import intersect_region_with_grating, row_num, stitch_row @@ -14,6 +14,29 @@ from ..utils.geometry import Point as InkstitchPoint class MaxQueueLengthExceeded(Exception): pass +class PathEdge(object): + OUTLINE_KEYS = ("outline", "extra", "initial") + SEGMENT_KEY = "segment" + + def __init__(self, nodes, key): + self.nodes = nodes + self._sorted_nodes = tuple(sorted(self.nodes)) + self.key = key + + def __getitem__(self, item): + return self.nodes[item] + + def __hash__(self): + return hash((self._sorted_nodes, self.key)) + + def __eq__(self, other): + return self._sorted_nodes == other._sorted_nodes and self.key == other.key + + def is_outline(self): + return self.key in self.OUTLINE_KEYS + + def is_segment(self): + return self.key == self.SEGMENT_KEY def auto_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, running_stitch_length, staggers, starting_point, ending_point=None): stitches = [] @@ -157,14 +180,20 @@ def node_list_to_edge_list(node_list): def bfs_for_loop(graph, starting_node, max_queue_length=2000): to_search = deque() - to_search.appendleft(([starting_node], set(), 0)) + to_search.append((None, set())) while to_search: if len(to_search) > max_queue_length: raise MaxQueueLengthExceeded() - path, visited_edges, visited_segments = to_search.pop() - ending_node = path[-1] + path, visited_edges = to_search.pop() + + if path is None: + # This is the very first time through the loop, so initialize. + path = [] + ending_node = starting_node + else: + ending_node = path[-1][-1] # get a list of neighbors paired with the key of the edge I can follow to get there neighbors = [ @@ -178,26 +207,21 @@ def bfs_for_loop(graph, starting_node, max_queue_length=2000): for next_node, key in neighbors: # skip if I've already followed this edge - edge = (tuple(sorted((ending_node, next_node))), key) + edge = PathEdge((ending_node, next_node), key) if edge in visited_edges: continue - new_path = path + [next_node] - - if key == "segment": - new_visited_segments = visited_segments + 1 - else: - new_visited_segments = visited_segments + new_path = path + [edge] if next_node == starting_node: # ignore trivial loops (down and back a doubled edge) if len(new_path) > 3: - return node_list_to_edge_list(new_path), new_visited_segments + return new_path new_visited_edges = visited_edges.copy() new_visited_edges.add(edge) - to_search.appendleft((new_path, new_visited_edges, new_visited_segments)) + to_search.appendleft((new_path, new_visited_edges)) def find_loop(graph, starting_nodes): @@ -314,7 +338,7 @@ def find_initial_path(graph, starting_point, ending_point=None): # along the outline -- doesn't matter which. This effectively means # we end where we started. neighbors = [n for n, keys in graph.adj[starting_node].iteritems() if 'outline' in keys] - return [(starting_node, neighbors[0])] + return [PathEdge((starting_node, neighbors[0]), "initial")] else: ending_node = nearest_node_on_outline(graph, ending_point) outline_nodes = get_outline_nodes(graph) @@ -324,11 +348,15 @@ def find_initial_path(graph, starting_point, ending_point=None): outline_nodes *= 2 start_index = outline_nodes.index(starting_node) end_index = outline_nodes.index(ending_node, start_index) - path = outline_nodes[start_index:end_index + 1] + nodes = outline_nodes[start_index:end_index + 1] # we have a series of sequential points, but we need to # turn it into an edge list - return node_list_to_edge_list(path) + path = [] + for start, end in izip(nodes[:-1], nodes[1:]): + path.append(PathEdge((start, end), "initial")) + + return path def find_stitch_path(graph, segments, starting_point=None, ending_point=None): """find a path that visits every grating segment exactly once @@ -368,23 +396,22 @@ def find_stitch_path(graph, segments, starting_point=None, ending_point=None): # Path that starts and ends at those two nodes. nodes_visited.append(path[0][0]) + #print >> sys.stderr, "nodes_visited", nodes_visited #print >> sys.stderr, "starting path:", path #return path while segments_visited < num_segments: - result = find_loop(graph, nodes_visited) + loop = find_loop(graph, nodes_visited) - if not result: + if not loop: print >> sys.stderr, _("Unexpected error while generating fill stitches. Please send your SVG file to lexelby@github.") break - loop, segments = result - #print >> sys.stderr, "found loop:", loop #dbg.flush() - segments_visited += segments + segments_visited += sum(1 for edge in loop if edge.is_segment()) nodes_visited.extend(edge[0] for edge in loop) graph.remove_edges_from(loop) @@ -417,10 +444,10 @@ def collapse_sequential_outline_edges(graph, path): new_path = [] for edge in path: - if graph.has_edge(*edge, key="segment"): + if edge.is_segment(): if start_of_run: # close off the last run - new_path.append((start_of_run, edge[0])) + new_path.append(PathEdge((start_of_run, edge[0]), "collapsed")) start_of_run = None new_path.append(edge) @@ -430,7 +457,7 @@ def collapse_sequential_outline_edges(graph, path): if start_of_run: # if we were still in a run, close it off - new_path.append((start_of_run, edge[1])) + new_path.append(PathEdge((start_of_run, edge[1]), "collapsed")) return new_path @@ -496,7 +523,7 @@ def path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, stitches = [] for edge in path: - if graph.has_edge(*edge, key="segment"): + if edge.is_segment(): stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers) else: stitches.extend(connect_points(shape, edge[0], edge[1], running_stitch_length)) |
