kaalleen/image-to-stroke
Lex Neva 2023-04-02 00:14:57 -04:00
rodzic acdb911145
commit f57d61b6e6
4 zmienionych plików z 43 dodań i 18 usunięć

Wyświetl plik

@ -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)
if new_edges:
edges_to_consider.extend(new_edges)
break 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):

Wyświetl plik

@ -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]]

Wyświetl plik

@ -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:

Wyświetl plik

@ -70,6 +70,7 @@ 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, ...]]
with debug.time_this("splprep"):
tck, fp, ier, msg = splprep(coords.T, s=s, k=3, nest=-1, full_output=1) 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}")
@ -78,6 +79,7 @@ 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.
with debug.time_this("splev"):
smoothed_x_values, smoothed_y_values = splev(np.linspace(0, 1, int(num_points * 2)), tck[0]) 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 coords = np.array([smoothed_x_values, smoothed_y_values]).T