kopia lustrzana https://github.com/inkstitch/inkstitch
meander fixes
rodzic
acdb911145
commit
f57d61b6e6
|
@ -2,7 +2,7 @@ from itertools import combinations
|
||||||
|
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
from inkex import errormsg
|
from inkex import errormsg
|
||||||
from shapely.geometry import MultiPoint, Point
|
from shapely.geometry import LineString, MultiPoint, Point
|
||||||
from shapely.ops import nearest_points
|
from shapely.ops import nearest_points
|
||||||
|
|
||||||
from .. import tiles
|
from .. import tiles
|
||||||
|
@ -126,10 +126,16 @@ def generate_meander_path(graph, start, end, rng):
|
||||||
check_stop_flag()
|
check_stop_flag()
|
||||||
|
|
||||||
edge1, edge2 = poprandom(edge_pairs, rng)
|
edge1, edge2 = poprandom(edge_pairs, rng)
|
||||||
edges_to_consider.extend(replace_edge_pair(meander_path, edge1, edge2, graph, graph_nodes))
|
new_edges = replace_edge_pair(meander_path, edge1, edge2, graph, graph_nodes)
|
||||||
break
|
if new_edges:
|
||||||
|
edges_to_consider.extend(new_edges)
|
||||||
|
break
|
||||||
|
|
||||||
return path_to_points(meander_path)
|
debug.log_graph(graph, "remaining graph", "#FF0000")
|
||||||
|
points = path_to_points(meander_path)
|
||||||
|
debug.log_line_string(LineString(points), "meander path", "#00FF00")
|
||||||
|
|
||||||
|
return points
|
||||||
|
|
||||||
|
|
||||||
def replace_edge(path, edge, graph, graph_nodes):
|
def replace_edge(path, edge, graph, graph_nodes):
|
||||||
|
|
|
@ -10,6 +10,8 @@ from copy import copy
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from shapely import geometry as shgeo
|
from shapely import geometry as shgeo
|
||||||
|
|
||||||
|
from ..debug import debug
|
||||||
from ..utils import prng
|
from ..utils import prng
|
||||||
from ..utils.geometry import Point
|
from ..utils.geometry import Point
|
||||||
from ..utils.threading import check_stop_flag
|
from ..utils.threading import check_stop_flag
|
||||||
|
@ -246,6 +248,7 @@ def path_to_curves(points: typing.List[Point], min_len: float):
|
||||||
return curves
|
return curves
|
||||||
|
|
||||||
|
|
||||||
|
@debug.time
|
||||||
def running_stitch(points, stitch_length, tolerance):
|
def running_stitch(points, stitch_length, tolerance):
|
||||||
# Turn a continuous path into a running stitch.
|
# Turn a continuous path into a running stitch.
|
||||||
stitches = [points[0]]
|
stitches = [points[0]]
|
||||||
|
|
36
lib/tiles.py
36
lib/tiles.py
|
@ -5,7 +5,7 @@ import inkex
|
||||||
import json
|
import json
|
||||||
import lxml
|
import lxml
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
from shapely.geometry import LineString
|
from shapely.geometry import LineString, MultiLineString
|
||||||
from shapely.prepared import prep
|
from shapely.prepared import prep
|
||||||
|
|
||||||
from .debug import debug
|
from .debug import debug
|
||||||
|
@ -59,8 +59,9 @@ class Tile:
|
||||||
|
|
||||||
def _load_paths(self, tile_svg):
|
def _load_paths(self, tile_svg):
|
||||||
path_elements = tile_svg.findall('.//svg:path', namespaces=inkex.NSS)
|
path_elements = tile_svg.findall('.//svg:path', namespaces=inkex.NSS)
|
||||||
self.tile = self._path_elements_to_line_strings(path_elements)
|
tile = self._path_elements_to_line_strings(path_elements)
|
||||||
# self.center, ignore, ignore = self._get_center_and_dimensions(self.tile)
|
center, ignore, ignore = self._get_center_and_dimensions(MultiLineString(tile))
|
||||||
|
self.tile = [(start - center, end - center) for start, end in tile]
|
||||||
|
|
||||||
def _load_dimensions(self, tile_svg):
|
def _load_dimensions(self, tile_svg):
|
||||||
svg_element = tile_svg.getroot()
|
svg_element = tile_svg.getroot()
|
||||||
|
@ -136,22 +137,34 @@ class Tile:
|
||||||
shift0, shift1, tile = self._scale(x_scale, y_scale)
|
shift0, shift1, tile = self._scale(x_scale, y_scale)
|
||||||
|
|
||||||
shape_center, shape_width, shape_height = self._get_center_and_dimensions(shape)
|
shape_center, shape_width, shape_height = self._get_center_and_dimensions(shape)
|
||||||
shape_diagonal = Point(shape_width, shape_height).length()
|
|
||||||
prepared_shape = prep(shape)
|
prepared_shape = prep(shape)
|
||||||
|
|
||||||
return self._generate_graph(prepared_shape, shape_center, shape_diagonal, shift0, shift1, tile)
|
return self._generate_graph(prepared_shape, shape_center, shape_width, shape_height, shift0, shift1, tile)
|
||||||
|
|
||||||
def _generate_graph(self, shape, shape_center, shape_diagonal, shift0, shift1, tile):
|
@debug.time
|
||||||
|
def _generate_graph(self, shape, shape_center, shape_width, shape_height, shift0, shift1, tile):
|
||||||
graph = nx.Graph()
|
graph = nx.Graph()
|
||||||
tiles0 = ceil(shape_diagonal / shift0.length()) + 2
|
|
||||||
tiles1 = ceil(shape_diagonal / shift1.length()) + 2
|
shape_diagonal = Point(shape_width, shape_height).length()
|
||||||
for repeat0 in range(floor(-tiles0 / 2), ceil(tiles0 / 2)):
|
num_tiles = ceil(shape_diagonal / min(shift0.length(), shift1.length()))
|
||||||
for repeat1 in range(floor(-tiles1 / 2), ceil(tiles1 / 2)):
|
debug.log(f"num_tiles: {num_tiles}")
|
||||||
|
|
||||||
|
tile_diagonal = (shift0 + shift1).length()
|
||||||
|
x_cutoff = shape_width / 2 + tile_diagonal
|
||||||
|
y_cutoff = shape_height / 2 + tile_diagonal
|
||||||
|
|
||||||
|
for repeat0 in range(-num_tiles, num_tiles):
|
||||||
|
for repeat1 in range(-num_tiles, num_tiles):
|
||||||
check_stop_flag()
|
check_stop_flag()
|
||||||
|
|
||||||
offset0 = repeat0 * shift0
|
offset0 = repeat0 * shift0
|
||||||
offset1 = repeat1 * shift1
|
offset1 = repeat1 * shift1
|
||||||
this_tile = self._translate_tile(tile, offset0 + offset1 + shape_center)
|
offset = offset0 + offset1
|
||||||
|
|
||||||
|
if abs(offset.x) > x_cutoff or abs(offset.y) > y_cutoff:
|
||||||
|
continue
|
||||||
|
|
||||||
|
this_tile = self._translate_tile(tile, offset + shape_center)
|
||||||
for line in this_tile:
|
for line in this_tile:
|
||||||
line_string = LineString(line)
|
line_string = LineString(line)
|
||||||
if shape.contains(line_string):
|
if shape.contains(line_string):
|
||||||
|
@ -161,6 +174,7 @@ class Tile:
|
||||||
|
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
|
@debug.time
|
||||||
def _remove_dead_ends(self, graph):
|
def _remove_dead_ends(self, graph):
|
||||||
graph.remove_edges_from(nx.selfloop_edges(graph))
|
graph.remove_edges_from(nx.selfloop_edges(graph))
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -70,7 +70,8 @@ def smooth_path(path, smoothness=1.0):
|
||||||
|
|
||||||
# .T transposes the array (for some reason splprep expects
|
# .T transposes the array (for some reason splprep expects
|
||||||
# [[x1, x2, ...], [y1, y2, ...]]
|
# [[x1, x2, ...], [y1, y2, ...]]
|
||||||
tck, fp, ier, msg = splprep(coords.T, s=s, k=3, nest=-1, full_output=1)
|
with debug.time_this("splprep"):
|
||||||
|
tck, fp, ier, msg = splprep(coords.T, s=s, k=3, nest=-1, full_output=1)
|
||||||
if ier > 0:
|
if ier > 0:
|
||||||
debug.log(f"error {ier} smoothing path: {msg}")
|
debug.log(f"error {ier} smoothing path: {msg}")
|
||||||
return path
|
return path
|
||||||
|
@ -78,7 +79,8 @@ def smooth_path(path, smoothness=1.0):
|
||||||
# Evaluate the spline curve at many points along its length to produce the
|
# Evaluate the spline curve at many points along its length to produce the
|
||||||
# smoothed point list. 2 * num_points seems to be a good number, but it
|
# smoothed point list. 2 * num_points seems to be a good number, but it
|
||||||
# does produce a lot of points.
|
# does produce a lot of points.
|
||||||
smoothed_x_values, smoothed_y_values = splev(np.linspace(0, 1, int(num_points * 2)), tck[0])
|
with debug.time_this("splev"):
|
||||||
coords = np.array([smoothed_x_values, smoothed_y_values]).T
|
smoothed_x_values, smoothed_y_values = splev(np.linspace(0, 1, int(num_points * 2)), tck[0])
|
||||||
|
coords = np.array([smoothed_x_values, smoothed_y_values]).T
|
||||||
|
|
||||||
return [Point(x, y) for x, y in coords]
|
return [Point(x, y) for x, y in coords]
|
||||||
|
|
Ładowanie…
Reference in New Issue