From 502f20d6f821a28a2965a7f28be3af91d059a5e5 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 31 Jan 2020 16:43:19 -0500 Subject: [PATCH 1/4] avoid jumps when not underpathing autofill --- lib/stitches/auto_fill.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 5833b779f..3952a6ff3 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -1,18 +1,18 @@ # -*- coding: UTF-8 -*- -from itertools import groupby, chain import math +from itertools import chain, groupby import networkx from shapely import geometry as shgeo from shapely.ops import snap from shapely.strtree import STRtree +from .fill import intersect_region_with_grating, stitch_row +from .running_stitch import running_stitch from ..debug import debug from ..svg import PIXELS_PER_MM from ..utils.geometry import Point as InkstitchPoint, line_string_to_point_list -from .fill import intersect_region_with_grating, stitch_row -from .running_stitch import running_stitch class PathEdge(object): @@ -52,7 +52,6 @@ def auto_fill(shape, starting_point, ending_point=None, underpath=True): - fill_stitch_graph = build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing) if not graph_is_valid(fill_stitch_graph, shape, max_stitch_length): @@ -159,6 +158,27 @@ def tag_nodes_with_outline_and_projection(graph, shape, nodes): graph.add_node(node, outline=outline_index, projection=outline_projection) +def add_boundary_travel_nodes(graph, shape): + for outline_index, outline in enumerate(shape.boundary): + prev = None + for point in outline.coords: + point = shgeo.Point(point) + if prev is not None: + # Subdivide long straight line segments. Otherwise we may not + # have a node near the user's chosen starting or ending point + length = point.distance(prev) + segment = shgeo.LineString((prev, point)) + if length > 1: + # Just plot a point every pixel, that should be plenty of + # resolution. A pixel is around a quarter of a millimeter. + for i in xrange(1, int(length)): + subpoint = segment.interpolate(i) + 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) + prev = point + + def add_edges_between_outline_nodes(graph, duplicate_every_other=False): """Add edges around the outlines of the graph, connecting sequential nodes. @@ -240,6 +260,8 @@ def build_travel_graph(fill_stitch_graph, shape, fill_stitch_angle, underpath): # 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. tag_nodes_with_outline_and_projection(graph, shape, boundary_points) + else: + add_boundary_travel_nodes(graph, shape) add_edges_between_outline_nodes(graph) From 9bb4686df702be6e930aa1216cc9e254ae9e5b01 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 31 Jan 2020 19:47:43 -0500 Subject: [PATCH 2/4] fix NetworkXNoPath error --- lib/stitches/auto_fill.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 3952a6ff3..771be6dd8 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -52,7 +52,7 @@ def auto_fill(shape, starting_point, ending_point=None, underpath=True): - fill_stitch_graph = build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing) + fill_stitch_graph = build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting_point, ending_point) if not graph_is_valid(fill_stitch_graph, shape, max_stitch_length): return fallback(shape, running_stitch_length) @@ -94,7 +94,7 @@ def project(shape, coords, outline_index): @debug.time -def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing): +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 @@ -145,11 +145,35 @@ def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing): tag_nodes_with_outline_and_projection(graph, shape, graph.nodes()) add_edges_between_outline_nodes(graph, duplicate_every_other=True) + if starting_point: + insert_node(graph, shape, starting_point) + + if ending_point: + insert_node(graph, shape, ending_point) + debug.log_graph(graph, "graph") return graph +def insert_node(graph, shape, node): + """Add node to graph, splitting one of the outline edges""" + node = tuple(node) + point = shgeo.Point(node) + + edges = [] + for start, end, key, data in graph.edges(keys=True, data=True): + if key == "outline": + edges.append(((start, end), data)) + + edge, data = min(edges, key=lambda (edge, data): shgeo.LineString(edge).distance(point)) + + graph.remove_edge(*edge, key="outline") + graph.add_edge(edge[0], node, key="outline", **data) + graph.add_edge(node, edge[1], key="outline", **data) + tag_nodes_with_outline_and_projection(graph, shape, nodes=[node]) + + def tag_nodes_with_outline_and_projection(graph, shape, nodes): for node in nodes: outline_index = which_outline(shape, node) From c81c11eb53c9e326812e6baa3390f5019be2abcd Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 15 Feb 2020 16:45:53 -0500 Subject: [PATCH 3/4] ensure starting point is on the border --- lib/stitches/auto_fill.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 771be6dd8..b5f20afb5 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -158,7 +158,8 @@ def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting def insert_node(graph, shape, node): """Add node to graph, splitting one of the outline edges""" - node = tuple(node) + + node = nearest_node(graph, tuple(node)) point = shgeo.Point(node) edges = [] From 46dbe4668ea4a2d2ad2ad370f5e96ee89a4eadd2 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 15 Feb 2020 21:13:20 -0500 Subject: [PATCH 4/4] actually split outline edge --- lib/stitches/auto_fill.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index b5f20afb5..dbbef1365 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -156,18 +156,21 @@ def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting return graph -def insert_node(graph, shape, node): +def insert_node(graph, shape, point): """Add node to graph, splitting one of the outline edges""" - node = nearest_node(graph, tuple(node)) - point = shgeo.Point(node) + point = tuple(point) + outline = which_outline(shape, point) + projection = project(shape, point, outline) + projected_point = list(shape.boundary)[outline].interpolate(projection) + node = (projected_point.x, projected_point.y) edges = [] for start, end, key, data in graph.edges(keys=True, data=True): if key == "outline": edges.append(((start, end), data)) - edge, data = min(edges, key=lambda (edge, data): shgeo.LineString(edge).distance(point)) + edge, data = min(edges, key=lambda (edge, data): shgeo.LineString(edge).distance(projected_point)) graph.remove_edge(*edge, key="outline") graph.add_edge(edge[0], node, key="outline", **data)