diff options
Diffstat (limited to 'lib/stitches')
| -rw-r--r-- | lib/stitches/auto_fill.py | 2 | ||||
| -rw-r--r-- | lib/stitches/auto_run.py | 5 | ||||
| -rw-r--r-- | lib/stitches/contour_fill.py | 15 | ||||
| -rw-r--r-- | lib/stitches/fill.py | 13 | ||||
| -rw-r--r-- | lib/stitches/utils/autoroute.py | 97 |
5 files changed, 81 insertions, 51 deletions
diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 24ce6610..40b74d23 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -195,7 +195,7 @@ def insert_node(graph, shape, point): edges = [] for start, end, key, data in graph.edges(keys=True, data=True): - if key == "outline": + if key == "outline" and data['outline'] == outline: edges.append(((start, end), data)) edge, data = min(edges, key=lambda edge_data: shgeo.LineString(edge_data[0]).distance(projected_point)) diff --git a/lib/stitches/auto_run.py b/lib/stitches/auto_run.py index d833a885..b8ab84c7 100644 --- a/lib/stitches/auto_run.py +++ b/lib/stitches/auto_run.py @@ -5,23 +5,22 @@ from collections import defaultdict +import inkex import networkx as nx from shapely.geometry import LineString, MultiLineString, MultiPoint, Point from shapely.ops import nearest_points, substring, unary_union -import inkex - from ..commands import add_commands from ..elements import Stroke from ..i18n import _ from ..svg import PIXELS_PER_MM, generate_unique_id from ..svg.tags import INKSCAPE_LABEL, INKSTITCH_ATTRIBS +from ..utils.threading import check_stop_flag from .utils.autoroute import (add_elements_to_group, add_jumps, create_new_group, find_path, get_starting_and_ending_nodes, preserve_original_groups, remove_original_elements) -from ..utils.threading import check_stop_flag class LineSegments: diff --git a/lib/stitches/contour_fill.py b/lib/stitches/contour_fill.py index 993fa104..e19e1aad 100644 --- a/lib/stitches/contour_fill.py +++ b/lib/stitches/contour_fill.py @@ -4,6 +4,7 @@ from itertools import chain import networkx as nx import numpy as np import trimesh +from shapely import offset_curve from shapely.geometry import (GeometryCollection, LineString, MultiPolygon, Point, Polygon) from shapely.geometry.polygon import orient @@ -14,7 +15,7 @@ from ..stitch_plan import Stitch from ..utils import DotDict from ..utils.clamp_path import clamp_path_to_polygon from ..utils.geometry import (cut, ensure_geometry_collection, - ensure_multi_polygon, reverse_line_string, + ensure_multi_line_string, reverse_line_string, roll_linear_ring) from ..utils.smoothing import smooth_path from ..utils.threading import check_stop_flag @@ -49,10 +50,10 @@ nearest_neighbor_tuple = namedtuple( def _offset_linear_ring(ring, offset, resolution, join_style, mitre_limit): - result = Polygon(ring).buffer(-offset, resolution, cap_style=2, join_style=join_style, mitre_limit=mitre_limit, single_sided=True) - result = ensure_multi_polygon(result) - rings = GeometryCollection([poly.exterior for poly in result.geoms]) - rings = rings.simplify(0.01, False) + ring = Polygon(ring) + result = offset_curve(ring, -offset, resolution, join_style=join_style, mitre_limit=mitre_limit) + result = ensure_multi_line_string(result) + rings = result.simplify(0.01, False) return _take_only_valid_linear_rings(rings) @@ -200,11 +201,11 @@ def _match_polygons_and_holes(outer, inners): def _convert_polygon_to_nodes(tree, polygon, parent_polygon, child_holes): - polygon = orient(polygon, -1) - if polygon.area < 0.1: return None, None + polygon = orient(polygon, -1) + valid_rings = _take_only_valid_linear_rings(polygon.exterior) try: diff --git a/lib/stitches/fill.py b/lib/stitches/fill.py index 2c5cdffc..9e9ff790 100644 --- a/lib/stitches/fill.py +++ b/lib/stitches/fill.py @@ -149,14 +149,13 @@ def intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing=Non res = grating_line.intersection(shape) - if (isinstance(res, shapely.geometry.MultiLineString) or isinstance(res, shapely.geometry.GeometryCollection)): - runs = [line_string.coords for line_string in res.geoms if isinstance(line_string, shapely.geometry.LineString)] + if res.geom_type in ["MultiLineString", "GeometryCollection"]: + runs = [line_string.coords for line_string in res.geoms if line_string.geom_type == "LineString"] + elif res.geom_type in ["Point", "MultiPoint"] or res.is_empty: + # ignore if we intersected at a single point or no points + runs = [] else: - if res.is_empty or len(res.coords) == 1: - # ignore if we intersected at a single point or no points - runs = [] - else: - runs = [res.coords] + runs = [res.coords] if runs: runs.sort(key=lambda seg: (InkstitchPoint(*seg[0]) - upper_left).length()) diff --git a/lib/stitches/utils/autoroute.py b/lib/stitches/utils/autoroute.py index 3ada4299..ed07c9a4 100644 --- a/lib/stitches/utils/autoroute.py +++ b/lib/stitches/utils/autoroute.py @@ -5,12 +5,12 @@ from itertools import combinations +import inkex import networkx as nx -from shapely.geometry import Point, MultiPoint +from shapely.geometry import MultiPoint, Point from shapely.ops import nearest_points -import inkex - +from ...elements import SatinColumn from ...svg import get_correction_transform from ...svg.tags import INKSCAPE_LABEL from ...utils.threading import check_stop_flag @@ -83,40 +83,71 @@ def add_jumps(graph, elements, preserve_order): Jump stitches are added to ensure that all elements can be reached. Only the minimal number and length of jumps necessary will be added. """ - if preserve_order: - # For each sequential pair of elements, find the shortest possible jump - # stitch between them and add it. The directions of these new edges - # will enforce stitching the elements in order. - - for element1, element2 in zip(elements[:-1], elements[1:]): - check_stop_flag() - - potential_edges = [] - - nodes1 = get_nodes_on_element(graph, element1) - nodes2 = get_nodes_on_element(graph, element2) + _add_ordered_jumps(graph, elements) + else: + _add_unordered_jumps(graph, elements) + return graph - for node1 in nodes1: - for node2 in nodes2: - point1 = graph.nodes[node1]['point'] - point2 = graph.nodes[node2]['point'] - potential_edges.append((point1, point2)) - if potential_edges: - edge = min(potential_edges, key=lambda p1_p2: p1_p2[0].distance(p1_p2[1])) - graph.add_edge(str(edge[0]), str(edge[1]), jump=True) - else: - # networkx makes this super-easy! k_edge_agumentation tells us what edges - # we need to add to ensure that the graph is fully connected. We give it a - # set of possible edges that it can consider adding (avail). Each edge has - # a weight, which we'll set as the length of the jump stitch. The - # algorithm will minimize the total length of jump stitches added. - for jump in nx.k_edge_augmentation(graph, 1, avail=list(possible_jumps(graph))): - check_stop_flag() - graph.add_edge(*jump, jump=True) +def _add_ordered_jumps(graph, elements): + # For each sequential pair of elements, find the shortest possible jump + # stitch between them and add it. The directions of these new edges + # will enforce stitching the elements in order. + for element1, element2 in zip(elements[:-1], elements[1:]): + check_stop_flag() + _insert_smallest_jump(graph, element1, element2) - return graph + # add jumps between subpath too, we do not care about directions here + for element in elements: + if isinstance(element, SatinColumn): + # don't try this for satin columns + continue + check_stop_flag() + geoms = list(element.as_multi_line_string().geoms) + i = 0 + for line1 in geoms: + for line2 in geoms[i+1:]: + if line1.distance(line2) == 0: + continue + node1, node2 = nearest_points(line1, line2) + _insert_jump(graph, node1, node2) + i += 1 + + +def _insert_smallest_jump(graph, element1, element2): + potential_edges = [] + + nodes1 = get_nodes_on_element(graph, element1) + nodes2 = get_nodes_on_element(graph, element2) + + for node1 in nodes1: + for node2 in nodes2: + point1 = graph.nodes[node1]['point'] + point2 = graph.nodes[node2]['point'] + potential_edges.append((point1, point2)) + + if potential_edges: + edge = min(potential_edges, key=lambda p1_p2: p1_p2[0].distance(p1_p2[1])) + graph.add_edge(str(edge[0]), str(edge[1]), jump=True) + + +def _insert_jump(graph, node1, node2): + graph.add_node(str(node1), point=node1) + graph.add_node(str(node2), point=node2) + graph.add_edge(str(node1), str(node2), jump=True) + graph.add_edge(str(node2), str(node1), jump=True) + + +def _add_unordered_jumps(graph, elements): + # networkx makes this super-easy! k_edge_agumentation tells us what edges + # we need to add to ensure that the graph is fully connected. We give it a + # set of possible edges that it can consider adding (avail). Each edge has + # a weight, which we'll set as the length of the jump stitch. The + # algorithm will minimize the total length of jump stitches added. + for jump in nx.k_edge_augmentation(graph, 1, avail=list(possible_jumps(graph))): + check_stop_flag() + graph.add_edge(*jump, jump=True) def possible_jumps(graph): |
