kopia lustrzana https://github.com/inkstitch/inkstitch
Merge pull request #2332 from inkstitch/lexelby/fix-skip-last
fix "skip last stitch in each row"pull/2348/head
commit
9d17fddacd
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from itertools import chain, groupby
|
from itertools import chain, groupby
|
||||||
|
import warnings
|
||||||
|
|
||||||
import networkx
|
import networkx
|
||||||
from shapely import geometry as shgeo
|
from shapely import geometry as shgeo
|
||||||
|
@ -37,6 +38,9 @@ class PathEdge(object):
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash((self._sorted_nodes, self.key))
|
return hash((self._sorted_nodes, self.key))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"PathEdge({self.key}, {self.nodes})"
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self._sorted_nodes == other._sorted_nodes and self.key == other.key
|
return self._sorted_nodes == other._sorted_nodes and self.key == other.key
|
||||||
|
|
||||||
|
@ -356,7 +360,10 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges):
|
||||||
# allows for building a set of shapes and then efficiently testing
|
# allows for building a set of shapes and then efficiently testing
|
||||||
# the set for intersection. This allows us to do blazing-fast
|
# the set for intersection. This allows us to do blazing-fast
|
||||||
# queries of which line segments overlap each underpath edge.
|
# queries of which line segments overlap each underpath edge.
|
||||||
strtree = STRtree(segments)
|
with warnings.catch_warnings():
|
||||||
|
# We know about this upcoming change and we don't want to bother users.
|
||||||
|
warnings.filterwarnings('ignore', 'STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.')
|
||||||
|
strtree = STRtree(segments)
|
||||||
|
|
||||||
# This makes the distance calculations below a bit faster. We're
|
# This makes the distance calculations below a bit faster. We're
|
||||||
# not looking for high precision anyway.
|
# not looking for high precision anyway.
|
||||||
|
@ -572,12 +579,12 @@ def pick_edge(edges):
|
||||||
return list(edges)[0]
|
return list(edges)[0]
|
||||||
|
|
||||||
|
|
||||||
def collapse_sequential_outline_edges(path):
|
def collapse_sequential_outline_edges(path, graph):
|
||||||
"""collapse sequential edges that fall on the same outline
|
"""collapse sequential edges that fall on the same outline
|
||||||
|
|
||||||
When the path follows multiple edges along the outline of the region,
|
When the path follows multiple edges along the outline of the region,
|
||||||
replace those edges with the starting and ending points. We'll use
|
replace those edges with the starting and ending points. We'll use
|
||||||
these to stitch along the outline later on.
|
these to underpath later on.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
start_of_run = None
|
start_of_run = None
|
||||||
|
@ -586,8 +593,24 @@ def collapse_sequential_outline_edges(path):
|
||||||
for edge in path:
|
for edge in path:
|
||||||
if edge.is_segment():
|
if edge.is_segment():
|
||||||
if start_of_run:
|
if start_of_run:
|
||||||
# close off the last run
|
# Collapse the run of edges along the outline. If we're
|
||||||
new_path.append(PathEdge((start_of_run, edge[0]), "collapsed"))
|
# traveling from one segment to its neighbor, we can just go
|
||||||
|
# there directly. Otherwise, we're going to have to underpath.
|
||||||
|
# The tricky thing is that even if we're going to a neighboring
|
||||||
|
# segment, the algorithm may have taken a weird route to get
|
||||||
|
# there, doubling back on itself.
|
||||||
|
#
|
||||||
|
# We can test whether the segments are neighboring by just
|
||||||
|
# seeing if there's an edge in the graph directly from one
|
||||||
|
# segment to the next.
|
||||||
|
#
|
||||||
|
# This test is important for the "skip last stitch in each row"
|
||||||
|
# feature.
|
||||||
|
if graph.has_edge(start_of_run, edge[0]):
|
||||||
|
new_path.append(PathEdge((start_of_run, edge[0]), "outline"))
|
||||||
|
else:
|
||||||
|
new_path.append(PathEdge((start_of_run, edge[0]), "collapsed"))
|
||||||
|
|
||||||
start_of_run = None
|
start_of_run = None
|
||||||
|
|
||||||
new_path.append(edge)
|
new_path.append(edge)
|
||||||
|
@ -602,9 +625,10 @@ def collapse_sequential_outline_edges(path):
|
||||||
return new_path
|
return new_path
|
||||||
|
|
||||||
|
|
||||||
def travel(travel_graph, start, end, running_stitch_length, running_stitch_tolerance, skip_last):
|
def travel(travel_graph, edge, running_stitch_length, running_stitch_tolerance, skip_last):
|
||||||
"""Create stitches to get from one point on an outline of the shape to another."""
|
"""Create stitches to get from one point on an outline of the shape to another."""
|
||||||
|
|
||||||
|
start, end = edge
|
||||||
path = networkx.shortest_path(travel_graph, start, end, weight='weight')
|
path = networkx.shortest_path(travel_graph, start, end, weight='weight')
|
||||||
path = [Stitch(*p) for p in path]
|
path = [Stitch(*p) for p in path]
|
||||||
stitches = running_stitch(path, running_stitch_length, running_stitch_tolerance)
|
stitches = running_stitch(path, running_stitch_length, running_stitch_tolerance)
|
||||||
|
@ -614,7 +638,7 @@ def travel(travel_graph, start, end, running_stitch_length, running_stitch_toler
|
||||||
|
|
||||||
# The path's first stitch will start at the end of a row of stitches. We
|
# The path's first stitch will start at the end of a row of stitches. We
|
||||||
# don't want to double that last stitch, so we'd like to skip it.
|
# don't want to double that last stitch, so we'd like to skip it.
|
||||||
if skip_last and len(path) > 2:
|
if skip_last and not edge.is_outline():
|
||||||
# However, we don't want to skip it if we've had to do any actual
|
# However, we don't want to skip it if we've had to do any actual
|
||||||
# travel in the interior of the shape. The reason is that we can
|
# travel in the interior of the shape. The reason is that we can
|
||||||
# potentially cut a corner and stitch outside the shape.
|
# potentially cut a corner and stitch outside the shape.
|
||||||
|
@ -631,7 +655,7 @@ def travel(travel_graph, start, end, running_stitch_length, running_stitch_toler
|
||||||
@debug.time
|
@debug.time
|
||||||
def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, running_stitch_length, running_stitch_tolerance,
|
def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, running_stitch_length, running_stitch_tolerance,
|
||||||
staggers, skip_last):
|
staggers, skip_last):
|
||||||
path = collapse_sequential_outline_edges(path)
|
path = collapse_sequential_outline_edges(path, fill_stitch_graph)
|
||||||
|
|
||||||
stitches = []
|
stitches = []
|
||||||
|
|
||||||
|
@ -644,7 +668,7 @@ 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)
|
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', []))
|
travel_graph.remove_edges_from(fill_stitch_graph[edge[0]][edge[1]]['segment'].get('underpath_edges', []))
|
||||||
else:
|
else:
|
||||||
stitches.extend(travel(travel_graph, edge[0], edge[1], running_stitch_length, running_stitch_tolerance, skip_last))
|
stitches.extend(travel(travel_graph, edge, running_stitch_length, running_stitch_tolerance, skip_last))
|
||||||
|
|
||||||
check_stop_flag()
|
check_stop_flag()
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue