Commit Graph
4 Commits
Author SHA1 Message Date
knsoandClaude Sonnet 5 8bc8ca19fc feat(extract): Flamingo Park hängt seine Straße an den Graphen
World.centerlines wird zu World.graph: apply_street() und apply_park() schneiden und
verlängern jetzt den Straßengraphen statt einer losen Liste von Mittellinien, mit den neuen
roads.cut_edges()/attach() als Gegenstück zum bestehenden Schneiden der Straßenflächen.
apply_park() schneidet dabei einmal am Parkviereck (nicht je Sperrfläche - das schneidet
tatsächlich nichts, was der Parkumriss nicht schon geschnitten hätte). check_corners() bleibt
unverändert gegen world.roads (den Korridor) messen, mit einem erklärenden Docstring-Absatz und
einer Regressionswache dafür in den Tests.

MIN_CENTERLINE (15 m) entfällt zugunsten von MIN_EDGE (5 m): Der alte Grund - Laternen wählten
ihre Straßenseite über den Listenindex - fällt weg, sobald eine spätere Aufgabe die Seite aus
der Geometrie bestimmt. test_park_street.py dokumentiert das jetzt: die beiden 11-m-Enden der
geschnittenen Gasse bleiben deshalb stehen, statt wie vorher zu verschwinden.

Beim Nachrechnen der Prüfkörper aus dem Auftrag fielen zwei echte Fehler auf: Der Reste-unter-
Mindestlänge-Test schnitt ursprünglich bis übers Streckenende hinaus, sodass der Rest schon
geometrisch verschwand und der MIN_EDGE-Filter nie geprüft wurde - jetzt schneidet er innerhalb
der Strecke, sodass tatsächlich ein zu kurzes Stück verworfen wird. Und die neue
Korridor-Regressionswache verglich (12-7)/2 > 2.5, was mit den echten Maßen immer falsch ist
(2.5 > 2.5); korrigiert auf >=, denn die Halbdifferenz trifft die Toleranz exakt.

Ausserdem musste eine Test-Fixture in test_park_apply.py von einem 5-m-Reststück (exakt an der
neuen MIN_EDGE-Grenze, hätte also entgegen der ursprünglichen Testabsicht überlebt) auf 3 m
verkürzt werden, damit der Test weiterhin prüft, dass echte Kurzreste wegfallen.

run.py bleibt unangetastet: Es ist bereits durch eine frühere Aufgabe gebrochen und wird von der
nächsten Aufgabe repariert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 10:41:08 +02:00
knsoandClaude Sonnet 5 2d8f4e730c fix(extract): Kontraktion zieht jetzt alle anliegenden Kanten mit
_contract_short() ließ Kanten, die schon vorher am überlebenden Knoten
hingen, auf ihrer alten Koordinate stehen - nur die vom eingezogenen
Knoten umgehängten folgten dem neuen Mittelpunkt. Da Kontraktion beidseitig
Grad >= 3 voraussetzt, hat der überlebende Knoten danach immer mindestens
Grad 4, und _merge_chains() fasst nur Grad-2-Knoten an: Der Fehler konnte
sich nie von selbst heilen. Am heutigen Mosaik wirkungslos (die Funktion
greift dort kein einziges Mal), aber real für jeden Ausschnitt mit zwei
nahen Kreuzungen - genau der Fall, den die Funktion behandeln soll.

Dazu zwei Kleinigkeiten: _median_width() deckte den Kurzkanten-Fallback
(unter 2 * WIDTH_MARGIN) bisher nicht ab, und ein Kommentar in
_merge_chains() war ein Zeichen zu weit eingerückt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 01:52:19 +02:00
knsoandClaude Sonnet 5 f809eedf71 feat(extract): Graph säubern, Breite messen, Klasse bestimmen
Sackgassen unter 10 m sind Skelettfransen und fallen weg, Knoten mit nur
noch zwei Kanten lösen sich auf, Kreuzungen näher als 5 m werden eine. Die
Breite kommt aus der Distanztransformation ohne die ersten und letzten 8 m:
dort misst sie die Kreuzung, nicht die Straße.

Der Gegencheck am echten Mosaik deckte zwei Fälle auf, die kein
synthetischer Test zeigt: Ein Rohast ohne Kreuzungsanschluss liefert eine
Kante mit nur einem Stützpunkt (LineString verlangt 0 oder mehr als einen
Punkt), und ein Knoten mit Schleife plus Verbindungskante hat kein zweites
Ende zum Verketten. Beides fängt clean() jetzt ab, mit je einem
Regressionstest.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 01:16:13 +02:00
knsoandClaude Sonnet 5 3fe3cd5c45 feat(extract): Straßennetz als Graph aus dem Skelett
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>
2026-09-17 00:36:40 +02:00