centerlines() verwirft alles unter 15 m und zerlegt das Netz damit an jeder Kreuzung: von 3.038 Linienenden treffen sich nur 81. Der neue Graph lässt das Skelett ungefiltert, macht aus den 5.970 Pixeln mit Grad >= 3 durch Verschmelzen im Umkreis von 8 m die 862 Kreuzungen und hängt die Kanten dort ein. _skeleton_graph/_to_world in vectors.py sind jetzt öffentliche Schnittstelle für roads.py (skeleton_graph/to_world); inpaint.py zieht seinen einzigen Aufruf von skeleton_graph mit, sonst bräche dessen Import. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
61 lines
2.2 KiB
Python
61 lines
2.2 KiB
Python
"""Der Graph, aus dem die Straßen entstehen. Gegenstück zum Entwurf, Abschnitt "Das Netz"."""
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
from leonida.coords import SLICE
|
|
from leonida.roads import JUNCTION_RADIUS, graph_from_skeleton
|
|
|
|
|
|
def skeleton(pixels: list[tuple[int, int]], shape=(120, 120)) -> np.ndarray:
|
|
skel = np.zeros(shape, dtype=bool)
|
|
for r, c in pixels:
|
|
skel[r, c] = True
|
|
return skel
|
|
|
|
|
|
def kreuz(arm: int = 40) -> np.ndarray:
|
|
"""Ein Pluszeichen: vier Arme von 40 px an einem Mittelpunkt."""
|
|
pixels = [(60, c) for c in range(60 - arm, 60 + arm + 1)]
|
|
pixels += [(r, 60) for r in range(60 - arm, 60 + arm + 1)]
|
|
return skeleton(pixels)
|
|
|
|
|
|
def test_ein_kreuz_gibt_einen_knoten_und_vier_kanten():
|
|
graph = graph_from_skeleton(kreuz())
|
|
junctions = [i for i in range(len(graph.nodes)) if graph.degree(i) >= 3]
|
|
assert len(junctions) == 1
|
|
assert graph.degree(junctions[0]) == 4
|
|
assert len(graph.edges) == 4
|
|
|
|
|
|
def test_kanten_beginnen_und_enden_am_knotenpunkt():
|
|
graph = graph_from_skeleton(kreuz())
|
|
mitte = graph.nodes[next(i for i in range(len(graph.nodes)) if graph.degree(i) >= 3)].point
|
|
for edge in graph.edges:
|
|
enden = {edge.points[0], edge.points[-1]}
|
|
assert mitte in enden, "jede Kante des Kreuzes hängt am Mittelknoten"
|
|
|
|
|
|
def test_zwei_kreuzungen_naeher_als_der_radius_werden_ein_knoten():
|
|
# Zwei Abzweige im Abstand von 4 m — unter JUNCTION_RADIUS, also ein Knoten.
|
|
pixels = [(60, c) for c in range(20, 100)]
|
|
pixels += [(r, 58) for r in range(40, 60)]
|
|
pixels += [(r, 62) for r in range(60, 80)]
|
|
graph = graph_from_skeleton(skeleton(pixels))
|
|
assert 4 < JUNCTION_RADIUS
|
|
assert len([i for i in range(len(graph.nodes)) if graph.degree(i) >= 3]) == 1
|
|
|
|
|
|
def test_gerade_strecke_ohne_abzweig_gibt_eine_kante_mit_zwei_enden():
|
|
graph = graph_from_skeleton(skeleton([(60, c) for c in range(20, 100)]))
|
|
assert len(graph.edges) == 1
|
|
assert [graph.degree(i) for i in range(len(graph.nodes))] == [1, 1]
|
|
|
|
|
|
def test_punkte_liegen_in_weltkoordinaten():
|
|
graph = graph_from_skeleton(skeleton([(60, c) for c in range(20, 100)]))
|
|
xs = [x for x, _ in graph.edges[0].points]
|
|
assert min(xs) == SLICE["x0"] + 20
|
|
assert max(xs) == SLICE["x0"] + 99
|