Commit Graph
221 Commits
Author SHA1 Message Date
knsoandClaude Opus 5 67c655fb67 docs(strassen): Bordstein an jeder Straße und die gemessene Sichtweite
Hält die Nutzerentscheidung fest, dass der Bordstein an jede Straße gehört,
und warum Brücken ausgenommen bleiben. Dazu die Messungen: Abdeckung des
Fahrbahnrands je Klasse vorher und nachher, und der Befund zur gemeldeten
Sichtweite von etwa 40 m — die ließ sich nicht als Zeichenproblem
nachstellen, der Bordstein hörte schlicht an jeder Gasse auf.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 13:43:47 +02:00
knsoandClaude Opus 5 4e201d2b79 fix(world): Bordstein bekommt einen eigenen Tiefenversatz
Er war die einzige Straßengeometrie neben den Belägen ohne Versatz und
konnte beim flachen Blick gegen den Gehwegbelag verlieren. Jetzt trägt er
denselben kleinen Faktor wie die Beläge und steht eine Einheitenstufe über
dem obersten von ihnen.

Gemessen im Browser (Schicht aus- und wieder eingeblendet, Bild Pixel für
Pixel verglichen): Der Bordstein zeichnet bis 504 m, dieselbe Reichweite
wie Fahrbahn, Gehweg und Markierung. Das erreichte er auch ohne den
Versatz — er ist Absicherung gegen den Tiefenstreit, keine Reichweite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 13:43:33 +02:00
knsoandClaude Opus 5 b69239fd00 chore(data): Welt mit Bordstein an jeder Straße neu erzeugt
Bordstein 184,33 km statt 120,28 km (+53 %) in 1.341 Stücken je Chunk statt
1.415 — mehr Länge in weniger Stücken, weil der Rand jetzt durchläuft,
statt an jeder Gasse unterbrochen zu werden.

Abdeckung des Fahrbahnrands, an den fertigen Chunks nachgemessen: Gasse
7,0 % → 83,1 %, Wohnstraße 87,9 % → 88,0 %, Hauptstraße 87,2 % → 87,3 %,
Boulevard 86,3 % → 86,3 %. Fahrbahn (0,9076 km²), Gehweg (0,2987 km²),
Restflächen (0,0967 km²) und Markierungen (23.272 Stücke) bleiben, wie sie
waren.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 13:43:20 +02:00
knsoandClaude Opus 5 322693c892 feat(extract): Bordstein an jeder Straße, auch an der Gasse
Der Bordstein ist keine Ausstattung wie ein Gehweg, sondern die Kante, an
der der Asphalt endet — und die hat auch eine Hinterhofgasse. _kerb_lines()
schneidet darum nur noch die Brücken aus dem Fahrbahnrand: Dort steckt die
Fahrbahn im Deck, eine Kante stünde in der Luft. Gehweg und Markierung
bleiben der Gasse weiterhin fern.

Das Feld kerb im Querschnitt entfällt. Es steuerte zuletzt nur noch Gehweg
und Markierung und war aus walk > 0 ableitbar; kerb_only heißt deshalb
jetzt walk_only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 13:43:09 +02:00
knsoandClaude Opus 5 897eb8a1f6 chore(data): Namensnennung im Manifest ohne das nicht mehr benutzte Kit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 12:42:10 +02:00
knsoandClaude Opus 5 f226bc2c95 feat(world): Bordstein bekommt gebürsteten Beton von Poly Haven
Auf Wunsch des Nutzers: brushed_concrete_04 statt der Textur aus
modular_sidewalk_curb_kit. Die Wand ist 18 cm hoch, da trägt eine Textur mit
waagerechter Struktur mehr als das Modulmuster des Kits.

Damit ist das Kit nirgends mehr im Spiel: Die Geometrie des Bordsteins war von
Anfang an selbst gebaut, der Gehweg nimmt seit der Platzhalter-Korrektur
concrete_pavement, und der Bordstein jetzt brushed_concrete_04 - beides
Poly Haven (CC0). Der Kit-Import in tools/assets/textures.mjs entfällt samt
sharp-Abhängigkeit, die drei kopierten Kit-Texturen sind entfernt, und die
Namensnennung im Manifest nennt Just8 nicht mehr, weil nichts davon mehr
benutzt wird. Der Kit-Ordner des Nutzers bleibt unangetastet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 12:42:10 +02:00
knsoandClaude Opus 5 50eee9281a fix(world): Bordstein verschwindet beim flachen Blick nicht mehr
Gemeldet: Der Bordstein ist zu sehen, wenn man davor steht und nach unten
schaut, verschwindet aber beim Blick nach oben und aus der Entfernung.

Ursache ist der Tiefenversatz der Beläge. glPolygonOffset rechnet
offset = factor * Tiefensteigung + units * r, und die Tiefensteigung geht
beim streifenden Blick über eine Bodenfläche gegen unendlich. Die Rangfolge
der Beläge steckte im Faktor (Gehweg -9), also zog der Gehwegbelag beim
flachen Blick um ein Vielfaches seines nominellen Versatzes nach vorn und
deckte die 18 cm hohe Bordsteinwand daneben (Versatz 0) vollständig zu. Im
Browser nachgemessen: aus 18 m war die Wand weg, aus zwei Metern und von
oben war sie da; mit Faktor 0 oder -1 kam sie zurück.

Genau davor warnt der Kommentar über POLYGON_OFFSET_ORDER seit Teil 1 ("wächst
die Liste weiter, ist die Stufenhöhe zu entkoppeln"). Das passiert jetzt: Die
Rangfolge tragen allein die Units - sie zählen in der kleinsten auflösbaren
Tiefendifferenz, hängen nicht vom Blickwinkel ab und trennen koplanare Beläge
auf jede Entfernung. Der Faktor ist für alle Beläge derselbe kleine Wert und
hält sie über der Bodenplatte, ohne Geometrie daneben zu überdecken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 12:41:54 +02:00
knsoandClaude Opus 5 4a91e84bfc Merge branch 'main' into leonida/strassen
Bringt den Stand von main (Animationen, Showroom) in den Straßenzweig,
damit die zusammengeführte Fassung geprüft werden kann, bevor main auf
diesen Stand vorgezogen wird. main ist in einem zweiten Arbeitsverzeichnis
ausgecheckt und kann hier nicht ausgecheckt werden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 12:09:42 +02:00
knsoandClaude Sonnet 5 35d69c3e6c test(extract): toothless Gassen-Gehweg-Probe entfernt, Flaechenvaliditaet abgesichert
Nachpruefung der Fixwelle, drei Punkte:

test_gehweg_waechst_auch_bei_engem_winkel_nicht_in_die_gasse
(test_street.py) besteht identisch (0,0000 m²) unter dem aktuellen Code,
unter der verworfenen Fassung aus Fixrunde 4 und sogar unter einem
komplett ungefilterten walk_source = carriageway_area(graph) - nachgerechnet
mit einer Wegwerf-Kopie aller drei Varianten. Ursache: SECTION["gasse"].corridor
== SECTION["gasse"].carriageway, weshalb .difference(carriage) am Ende von
_sidewalk() den geprueften Streifen (die eigene Gassen-Fahrbahn) immer
loescht, unabhaengig davon, was _walk_source() liefert. Die Eigenschaft
selbst bleibt durch die drei benachbarten Tests
(test_gasse_bekommt_weder_gehweg_noch_bordstein,
test_gehweg_waechst_nicht_ueber_den_gassenstummel_an_geteilter_kreuzung,
test_gehwegrest_an_kreuzung_mit_mehreren_gassen_bleibt_aus) abgesichert,
die gegen dieselbe verworfene Fassung mit 53,11 bzw. 53,30 m² hart
durchfallen. Ein Test, der nicht fehlschlagen kann, taeuscht Deckung vor,
die es nicht gibt - entfernt statt umdimensioniert, weil eine Geometrie
mit nur einem Gassenarm die eigentliche Fixrunde-4-Nische (zwischen ZWEI
benachbarten Gassenstummeln) strukturell nie nachstellen kann.

test_schleife_die_auf_sich_selbst_zurueckfuehrt_bekommt_keine_markierung_daneben
(test_marking_junction.py) galt in der Nachpruefung ebenfalls als
unterscheidungslos (100% Fahrbahnanteil auch mit altem Code). Mit den
byte-genauen historischen Fassungen von junction_marks() (git show
4bf670e / ca01141) direkt in die bestehende Testfunktion eingesetzt
(Monkeypatch) zeigt sich das Gegenteil: Beide Vorstufen schlagen rot an
(schlechtester Anteil 64,9% bzw. 85,3%, an Haltelinie und aeusserstem
Zebrabalken), nur der aktuelle Code besteht. Der Test bleibt unveraendert,
mit einem Kommentar zur Gegenpruefung.

Neu: test_alle_flaechenschichten_sind_gueltige_polygone
(test_committed_roads.py) - Waechter aus Befund 5 (die alte
Koordinatenrundung erzeugte acht Selbstschnitte, ohne dass ein Test es
merkte). Prueft ueber alle 144 Chunks und alle sieben Flaechenschichten
(carriageway, land, sidewalk, surfaces, lots, bridges, paths), dass jeder
Ring ein gueltiges Polygon ergibt. Heute gruen (4.190 Flaechen, 0
ungueltige); rot nachgewiesen gegen den Datenstand vor Commit a397892
(9 ungueltige Polygone in 6 Chunks, per git show ohne Codeaenderung
nachgestellt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 09:07:27 +02:00
knsoandClaude Sonnet 5 5dcd188423 chore(data): Welt mit der Gehwegtextur-Korrektur neu erzeugt
cd tools/extract && PYTHONUTF8=1 uv run python run.py, nur wegen der
geaenderten Namensnennung im Manifest (Bordstein statt Bordstein und
Gehweg). Wie erwartet aendern sich nur manifest.json (die Attribution-
Zeile) und map.json (ihr abhaengiger manifest_sha256) - alle 144
Chunk-Dateien und die beiden Bilder bleiben byte-gleich, das Straßennetz
unveraendert (1.537 Knoten, 1.790 Kanten; 830 Bordsteinstuecke, 169
Restflaechen, 22.393 Markierungen, 56 Palmen entfernt - identisch zum
Fixwelle-Stand).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 09:06:56 +02:00
knsoandClaude Sonnet 5 2e7b30fd15 fix(world): Gehwegtextur aus Poly Haven statt Kit-Platzhalter
Die Fixwelle hat loadSet() so geaendert, dass eine Textur auch ohne
Normal- und ARM-Karte laedt (Befund 2) - richtig fuer sich, aber dadurch
laedt public/textures/sidewalk_Diffuse.jpg jetzt tatsaechlich. Die Datei
ist keine Betontextur, sondern das Platzhalterbild aus
modular_sidewalk_curb_kit: dunkler Grund, roter Schriftzug "Tru-planar
Sidewalk texture", im Browser geprueft. Vorher fiel das nie auf, weil
loadSet() an der fehlenden Normal-/ARM-Karte scheiterte und auf den
einfarbigen Ersatz zurueckfiel.

TEXTURES.sidewalk zeigt jetzt auf concrete_pavement (Poly Haven, CC0),
denselben Belag wie die Parkwege (curb_Diffuse.jpg ist dagegen eine
echte Betontextur mit Fugen und bleibt beim Kit). uvScale wechselt
von 4 auf 1,8, wie paths schon vormacht - Poly Haven misst die Kachel
mit 1,8 x 1,8 m. Der Kopierschritt fuer sidewalk_baseColor.jpeg entfaellt
in tools/assets/textures.mjs, die Namensnennung in run.py und im Entwurf
nennt fuer den Kit nur noch den Bordstein, und die committete
Platzhalter-Kachel ist aus dem Repository entfernt (der Kit-Ordner des
Nutzers bleibt unangetastet). tests/materials.test.js prueft jetzt die
neue Quelle, nicht mehr den Kit-Slug.

Sichtgeprueft im Browser: Gehwege zeigen helle Betonplatten statt
rotem Schriftzug, keine "Textur ... fehlt"-Warnung in der Konsole.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 09:06:40 +02:00
knso 349212fedf test(extract): festgehaltene Zahlen auf den Fixwelle-Stand gebracht, Markierung-auf-Fahrbahn abgesichert
Befund 9: Nach dem Pipeline-Lauf mit den Befunden 3-8 geänderte Zahlen in
test_committed_roads.py eingetragen (Restflächen 169 statt 171,
Markierungsstücke haltelinie 1.555/zebra 14.846 statt 1.553/14.832,
Flächen Gehweg 0,2987/Restflächen 0,0967 statt 0,2982/0,0972 km² -
Bordstein, Fahrbahn und Kantenstücke je Klasse bleiben unverändert, weil
keiner der Befunde am Graphen oder an carriageway/curb rührt) sowie den
Messwerte-Absatz „Gemessen nach der Fixwelle" im Entwurf ergänzt. Beide
Abnahmen erneut bestanden: zweiter Lauf ändert keine Datei, beide Suiten
grün.

Befund 10: Neuer Test test_markierung_liegt_nicht_wesentlich_ausserhalb_
der_fahrbahn haelt fest, wie viel Markierungsflaeche ausserhalb der
Fahrbahn liegt. Rechnerisch gegen die Daten vor der Fixwelle nachgewiesen,
dass er vor Befund 3 mit 452,65 m² weit über jeder sinnvollen Schranke rot
gewesen waere. Die Schranke (130 m²) ist kein reiner Rundungswert: Befund
3 senkt den echten Wert auf 110,06 m² (0,25 % von 43.906 m²), behebt das
Problem aber nicht vollständig - ein Bogen kann zwischen reach und
reach + STOP_SETBACK + STOP_DEPTH noch einmal abknicken, was die
umgesetzte Korrektur nicht auffängt. Das ist im Test-Docstring und im
Fixwelle-Bericht offengelegt, nicht stillschweigend toleriert.
2026-09-18 07:55:55 +02:00
knso 37f9381934 chore(data): Welt mit den Fixwelle-Korrekturen neu erzeugt
Ein Lauf (cd tools/extract && PYTHONUTF8=1 uv run python run.py) nach allen
datenwirksamen Fixwelle-Korrekturen (Befunde 3-8): Markierungsrichtung an
geknickten und an sich selbst zurücklaufenden Zufahrten, robuste
Markierungsgeometrie bei engem Zickzack, gültige Ringe durch
set_precision(), carriageway ohne das irreführende klasse-Feld,
geschlossene Gehweglücke an gewöhnlichen Kreuzungsecken, mittig verteilte
Zebrabalken und gerundete Kantenbreite.

Straßennetz und Kantenzahl unverändert (1.537 Knoten, 1.790 Kanten - keiner
der Fixes rührt am Graphen). Bordstein (830 Stücke, 120,28 km) und Fahrbahn
(0,9076 km²) ebenfalls unverändert. Gehweg wächst leicht (0,2987 statt
0,2982 km², Befund 7 schließt die Eckenlücke), Restflächen entsprechend
kleiner (169 statt 171 Stücke). Markierungsstückzahlen je Chunk verschieben
sich geringfügig durch die neue Position (Befunde 3 und 8), die
Gesamtsumme vor dem Chunk-Zuschnitt bleibt bis auf eine entfernte
Nullflächen-Randlinie gleich (22.393 statt 22.394 - Befund 4 fängt genau
diesen einen Fall ab).

Einzelheiten und alle Messwerte im Fixwelle-Bericht.
2026-09-18 07:48:23 +02:00
knso 015d15d82a fix(extract): Kreuzungsmarkierung am tatsächlichen Punkt verankern, nicht am Knoten
Nacharbeit zu Befund 3, beim Nachmessen am echten Netz entdeckt: Die erste
Korrektur (Richtung aus origin -> interpolate(reach) statt aus points[1])
reichte an zwei Stellen nicht.

Erstens kann eine Schleife geradewegs von ihrem Knoten weg- und auf
demselben Weg wieder zurücklaufen - am echten Netz eine 21,63 m lange
Boulevard-Schleife, deren Punktliste den Hinweg exakt rückwärts
wiederholt. Landet reach nahe dieser Kehre, zeigt die Sehne vom Ursprung
weiter nach außen, obwohl die Kante dort längst umkehrt: Die Markierung
lag an dieser einen Schleife weiterhin vollständig (100 %) neben der
Fahrbahn. Die Richtung kommt jetzt aus dem letzten Teilstück von
substring(line, 0, reach) - der tatsächlichen Anfahrtsrichtung an der
Schnittstelle, gleich wohin die Kante von dort aus weiterläuft.

Zweitens blieb "origin" (der Kreuzungsknoten) der Anker für Balken und
Haltelinie. origin und der Punkt bei Streckenlänge reach fallen nur auf
einer geraden Kante zusammen - mit der neuen Anfahrtsrichtung, aber dem
Knoten als Anker, läge selbst eine gerade Fortsetzung ab reach
systematisch daneben, sobald die Kante zwischen Knoten und reach von der
reinen Ursprungsrichtung abweicht. Anker ist jetzt der tatsächliche Punkt
auf der Kante bei Streckenlänge min(reach, länge).

Nachgemessen am echten Netz: von 452,65 m² (vor Befund 3) über 342,66 m²
(nur die erste Korrektur) auf 110,06 m² (0,25 % der Gesamtfläche) - beide
Korrekturen zusammen eine Verbesserung um 75,7 %. Der Rest ist eine engere
Spielart desselben Problems: Ein Bogen kann auch noch zwischen reach und
reach + STOP_SETBACK + STOP_DEPTH abknicken, was ein einzelner
Richtungsstrahl nicht mehr auffangen kann - das verlangte die vom Befund
selbst genannte, größere Alternative ("die Markierung entlang der
tatsächlichen Kante aufbauen"), die diese Fixwelle nicht umsetzt. Siehe
Fixwelle-Bericht für die Einzelheiten und die verbleibenden Einzelfälle.

Neuer Test mit den echten Schleifenmaßen (9 m hin, 6 m quer, reach 11 m):
vor dieser Korrektur lag die gesamte Markierung an der Schleife daneben,
danach vollständig darauf.
2026-09-18 07:47:24 +02:00
knso ca01141c5b fix(extract): Zebrabalken mittig verteilt, Kantenbreite gerundet
Befund 8, zwei kleine, aber datenwirksame Funde:

- Zebrastreifen: Der Startversatz (-halb + BAR/2, Takt 2*BAR) liess die
  Balkenreihe an einem Fahrbahnrand bündig sitzen und am gegenüber-
  liegenden bis zu eine Taktbreite frei (0,6 m bei Hauptstraße/Boulevard,
  0,4 m bei Wohnstraße), statt der laut Entwurf verlangten Aufteilung
  "Balken 60 cm, Lücke 60 cm" über die ganze Breite. n = floor((2*halb +
  BAR) / (2*BAR)) Balken, symmetrisch bei -(n-1)*BAR gestartet, lassen an
  beiden Rändern denselben Rest frei (nachgemessen: 0,3 m statt 0/0,6 m
  bei Hauptstraße, 0,2 m statt 0/0,4 m bei Wohnstraße), ohne die Balkenzahl
  je Arm zu ändern.
- edges[].breite (Median aus der Distanztransformation) kam ungerundet an
  (Werte wie 31.622776601683793), während jede andere Zahl in der Ausgabe
  auf 2 Stellen gerundet ist. fahrbahn bleibt unverändert, weil es schon
  eine der vier festen SECTION-Konstanten ist.

Zwei Tests, beide ohne die jeweilige Korrektur rot: die Zebrabalken einer
Kreuzungszufahrt lassen ungleiche Ränder (0,0/0,6 m statt 0,3/0,3 m), und
eine krumme Kantenbreite blieb ungerundet in der Ausgabe.
2026-09-18 07:09:15 +02:00
knso 2605f95951 fix(extract): Gehweglücke an gewöhnlichen Kreuzungsecken geschlossen
Befund 7 (seit Task 7 geparkt): carriageway_area() rundet die einspringenden
Ecken einer Kreuzung mit FILLET=6,0 (Schließen: aufblasen, dann wieder
schrumpfen), die Gehwegquelle in _walk_source() blieb seit Fixrunde 5
dagegen ungerundet. Eine reine Dilatation der Quelle um WALK_MAX=3,0
zeichnet eine Reflexecke nicht nach - zwischen der gerundeten Fahrbahn und
dem ungerundeten Gehweg blieb an jeder gewöhnlichen Kreuzung (mehrere Arme
mit Bordstein, keine Gasse beteiligt) eine kleine Lücke, zu klein für
LOT_MIN und damit ein unsichtbares Loch im Boden statt einer Restfläche.
Gemessen 0,26-2,2 m² je Ecke bei 690 Kreuzungen.

junction_area() (section.py) bekommt einen kerb_only-Schalter und rundet
damit wahlweise nur den Kreuzungsanteil der bordsteintragenden Arme;
_walk_source() ruft sie jetzt statt des rohen junction_arms(kerb_only=True)
auf. Der Unterschied zu Fixrunde 4 (die dieselbe Rundung auf ALLE Arme
anwandte und damit Gassen mit rundete): Eine Gasse liefert gar keinen Arm
in die zu schließende Fläche, kann also strukturell keine Nische mit einem
Nachbararm bilden - unabhängig vom Winkel.

Nachgemessen an einer T-Kreuzung aus drei Wohnstraßen (derselbe großzügige
Korridor wie in den bestehenden Gassen-Tests, carriageway_area()+3 m): vor
der Korrektur 0,256 m² Lücke je der beiden Ecken (0,5114 m² zusammen), exakt
im berichteten Bereich; danach 0,0 m². Eine zweite, an sich schon bei
Fixrunde 5 bestehende Kontrolle: Gasse in einem engen Winkel (10-30°)
zwischen zwei Wohnstraßen-Armen - der Gehweg bleibt für jeden geprüften
Winkel bei 0,0 m² im Gassenkorridor. Alle bestehenden Gassen-Sicherungen in
test_street.py (fünf Fixrunden Vorarbeit) bleiben grün.
2026-09-18 07:04:30 +02:00
knso 77d53fb170 fix(extract): carriageway[].klasse als irreführendes Feld entfernt
Befund 6, Controller-Entscheidung: carriageway_area() vereinigt das ganze
Straßennetz zu wenigen Zusammenhangskomponenten, und _classified() gab
jeder Komponente die Klasse ihrer breitesten enthaltenen Straße - am
echten Netz trugen dadurch 91,2 % der Fahrbahnfläche "boulevard", auch wo
tatsächlich eine Wohnstraße lag. Kein Verbraucher las das Feld (Browser
und Minimap färben die Fahrbahn einheitlich); die Klasse je Straße steht
weiterhin in edges.

_classified() samt Aufruf aus run.py entfernt, chunk_payload() nimmt
carriageway jetzt als reine Liste von Polygonen statt (poly, klasse)-
Tupeln entgegen, Sortierschlüssel entsprechend auf poly allein verkürzt.
Fixtures in test_emit.py, test_overview.py, tests/geometry.test.js und
tests/streaming.test.js sowie das Chunk-Format im Entwurf nachgezogen;
tests/props.test.js bleibt unverändert, weil dort edges betroffen sind,
nicht carriageway.
2026-09-18 06:54:01 +02:00
knso a397892847 fix(extract): set_precision statt Koordinatenrundung gegen selbstschneidende Ringe
_rings() rundete bislang jede Koordinate unabhängig auf 2 Stellen, nach
normalize(). Das kann zwei Ringpunkte so verschieben, dass sich der Ring
selbst schneidet, obwohl das Ausgangspolygon gültig war - Ursache der acht
neuen ungültigen Polygone in den ausgelieferten Chunks (carriageway 3,
land 2, sidewalk 2, surfaces 1). shapely.set_precision(poly, 0.01) läuft
jetzt vor normalize() und vor der Rundung: Es ist eine echte
Geometrieoperation (GEOS hält das Ergebnis mit mode="valid_output" aktiv
gültig) statt eines blinden round() je Zahl.

Gegen alle 29.037 Polygone der committeten (bereits einmal falsch
gerundeten) Chunks probeweise gerechnet: kein Absturz, 8 der 9 ungültigen
Polygone (die neunte ist die bekannte, von diesem Zweig unabhängige
Vorbelastung in "paths", chunk_10_6, schon vor diesem Zweig ungültig)
werden gültig, Flächenänderung insgesamt -24,35 m² von 11.711.354 m²
(-0,00021 %). Zwei Sonderfälle brauchten eine eigene Behandlung, die die
Zeilen im Befund nicht erwähnen, ohne die aber der Vier-Minuten-Lauf hätte
abbrechen oder eine falsche Fläche ausliefern können:

- An einer Engstelle unter 1 cm kann set_precision ein Polygon in mehrere
  Teile spalten (3 von 29.037 Fällen, größter abgeschnittener Teil
  25,9 m²) - poly.exterior auf einer MultiPolygon wäre ein AttributeError
  gewesen. Es bleibt jetzt das größere Teilstück.
- Ganz vereinzelt lehnt GEOS das Schnappen mit einer TopologyException ab
  (in der Stichprobe nur an einem bereits vorher ungültigen Polygon); dann
  bleibt das Polygon ungerundet, und die alte Rundung je Koordinate greift
  wie vor diesem Befund, statt den Lauf abzubrechen.

Zwei Tests, beide ohne die Korrektur rot: ein valides Polygon, dessen
unabhängig gerundete Ecken sich kreuzen (Beispiel selbst nachgerechnet,
nicht nur behauptet), und eine Hantelform mit einem Steg unter 1 cm, die
am unkorrigierten _rings() unter der Rundung selbst ungültig wird statt
sich sauber zu teilen.
2026-09-18 06:47:14 +02:00
knso 83140f89e1 fix(extract): Markierungen folgen der Kante und stürzen nicht am Zickzack ab
Befund 3: In junction_marks() kam die Richtung einer Zufahrt aus
origin -> points[1], also nur aus dem ersten Segment der Kante. Haltelinie
und Zebrastreifen wurden aber bis reach + STOP_SETBACK + STOP_DEPTH (bis
16 m) auf dieser Geraden aufgespannt - knickte die Kante innerhalb dieser
Strecke ab, landete die Markierung daneben statt darauf. Am echten Netz
(gemessen über alle 144 committeten Chunks): 452,65 m² von 43.906 m²
Markierungsfläche (1,03 %) liegen außerhalb der Fahrbahn, davon 291,44 m²
Zebra und 147,68 m² Haltelinie; die schlimmsten Einzelfälle sind
vollständige Haltelinien mit 5,41 m². Die Richtung kommt jetzt aus
line.interpolate(min(reach, länge)) - dem tatsächlichen Punkt auf der
Kante bei Streckenlänge reach - statt aus dem zweiten Stützpunkt.

Befund 4: offset_curve() liefert bei einem engen Zickzack eine
MultiLineString statt einer LineString; substring() in _dashed()
akzeptiert nur LineString und wirft dann GeometryTypeError. Am heutigen
Mosaik tritt das nicht auf, aber eine geänderte Korrektur oder eine
eingehängte Kante mit engem Knick hätte den Vier-Minuten-Lauf abgebrochen.
Zweiter, stiller Ast: Eine sehr spitze Kehre lässt offset_curve() leer
zurückkommen; _solid() puffert das ungeprüft zu einem Nullflächen-Polygon.
_offset() normalisiert jetzt auf eine Liste von LineStrings (leer -> [],
MultiLineString -> ihre geoms), _dashed() und _solid() iterieren darüber;
longitudinal() flacht die jetzt listenwertigen _solid()-Aufrufe ab.

Beide Befunde mit Tests, die ohne die jeweilige Korrektur rot sind: der
Zickzack-Fall bricht am unkorrigierten Code mit GeometryTypeError ab, der
Kehre-Fall lieferte zwei Nullflächen-Polygone unter den Rand-/
Doppellinien.
2026-09-18 06:35:36 +02:00
knso ceb23bf912 chore: Kit-Ordner ignorieren, veralteten Docstring berichtigen
.gitignore führte die beiden fremden Kit-Ordner modular_city_road_pack__
game_ready/ und modular_sidewalk_curb_kit/ noch nicht, obwohl derselbe
Fall für public/models/props/raw/ schon geregelt ist (31 MB unversionierte
Quelle für tools/assets/textures.mjs, nur die daraus gebauten Kacheln
unter public/textures/ werden committet).

_kerbless_zone() in street.py behauptete noch, _walk_source() riefe die
Zone für den Kreuzungsanteil der Gehwegquelle auf; seit Fixrunde 5 nimmt
_walk_source() stattdessen direkt junction_arms(kerb_only=True) und
braucht die Zone nicht mehr. Der Docstring beschreibt jetzt den
tatsächlichen, einzigen Aufrufer (_kerb_lines()).
2026-09-18 06:16:26 +02:00
knso 3f4fa7f1ed fix(world): Gehweg lädt seine Diffuse-Textur auch ohne Normal- und ARM-Karte
loadSet() lud Diffuse-, Normal- und ARM-Karte in einem gemeinsamen
Promise.all und fiel schon beim Fehlen einer einzigen Datei auf den
einfarbigen Ersatz zurück. modular_sidewalk_curb_kit liefert für den
Gehweg nur sidewalk_Diffuse.jpg (keine Normal-Map, und die
metallicRoughness-Karte ist laut Entwurf eine 605-Byte-Einfarbfläche,
die bewusst nicht übernommen wird) — Folge: 0,298 km² rendern einfarbig
#d8d2c4, bei jedem Laden "Textur sidewalk fehlt", die committete Kachel
blieb ungenutzt.

Nur die Diffuse-Karte ist jetzt Pflicht; Normal- und ARM-Karte werden
einzeln geladen und fallen bei einem Fehler je für sich auf null zurück,
ohne die anderen beiden mitzureißen. Ohne ARM-Karte bleibt roughness auf
0.9 (derselbe Wert wie der bisherige einfarbige Ersatz).

loadSet() ist jetzt exportiert, damit tests/materials.test.js sie mit
einem Attrappenloader ohne echte Dateien prüfen kann: Diffuse allein,
Diffuse+Normal ohne ARM, der volle Satz, und weiterhin der einfarbige
Ersatz, wenn die Diffuse-Karte selbst fehlt.
2026-09-18 06:16:12 +02:00
knso 736dcbc1ad fix(skills): placing-editor-models nach dem Formatwechsel repariert
vergeLampPoints() und der Schlüssel roads existieren seit Teil 2 nicht mehr:
site-data.mjs importierte einen fehlenden Export (ESM-Link-Fehler, das
Skript starb vor der ersten Zeile) und las c.roads/c.centerlines;
render-site.py griff auf c["roads"] zu (KeyError). Beide lesen jetzt
chunkLampPoints(c) sowie die drei neuen Schichten carriageway, sidewalk
und lots (zusammen die frühere Korridorfläche); render-site.py bekommt
dafür eine eigene Vierfarb-Darstellung inklusive bridges.

Verifiziert mit einem echten Ausschnitt (1414..1564 | 1042.5..1092.5,
12 Chunks I5-L7): site-data.mjs liefert carriageway/sidewalk/lots/bridges
mit echten Werten, 4 Laternen und 3 Palmen im Fenster; render-site.py
zeichnet daraus ein plausibles Vergleichsbild ohne Absturz.
2026-09-18 06:15:55 +02:00
knsoandClaude Opus 5 61316a4470 Merge branch 'leonida/animationen'
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:09:37 +02:00
knsoandClaude Opus 5 a0cd95cceb fix(showroom): Funde der Schlussdurchsicht auf der Testseite behoben
Die Spielfigur wirft jetzt einen Schatten (player.init durchlief applyCharacter
nie); drei stille Fehlerpfade meckern jetzt statt roh abzubrechen (fehlender
Clip fuer die Fingerhaltung, Waffe ohne Griff, mit neuem Test); die
Waffenauswahl im Panel zeigt Beschriftungen statt IDs; der Waffen-Umbau prueft
jetzt die Meshzahl je Datei; stageTransform ist aus main.js nach lineup.js
gezogen und mit dem im Browser gemessenen Fall getestet; slotPosition nimmt
DUMMIES als Argument statt es aus dem Modul zu lesen; dazu Kommentar- und
Entwurfskorrekturen (Docstring, Beckenspur-Test, Frustum-Culling, Port/Kamera/
VIEW_DISTANCE im Entwurf).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 03:19:42 +02:00
knsoandClaude Opus 5 c8f1832eae docs: Messwerte der Browserprüfung im Entwurf festhalten
Dateigrößen, Maßstab und Dummy-Größe, Griffwerte, Schleifennaht je Dummy,
Bildzeit mit Messverfahren, Draw Calls und Dreiecke, Befund zu Maske und
Normalmap sowie die geprüften Durchdringungen an Kopf und Gewehr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 02:40:17 +02:00
knsoandClaude Opus 5 01cb63e7f1 feat(showroom): Griffe der fünf Waffen im Browser eingepasst
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 02:18:38 +02:00
knsoandClaude Opus 5 da4d078cab feat(showroom): Testseite mit Dummies, Schildern und Panel
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:58:20 +02:00
knsoandClaude Opus 5 12a77051d6 feat(showroom): Dummy aus Mannequin, Clip, Fingerhaltung und Waffe
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:33:35 +02:00
knsoandClaude Sonnet 5 0ad6eee71f docs(strassen): Messwerte-Absatz auf den Endstand des Branches bringen
„Gemessen im Spiel" behauptete noch einen offenen Befund zu tests/parkData.test.js, der seit
Commit 05e2914 (Fix-Runde 1) erledigt ist: der Test prüft inzwischen gegen carriageway und ist
grün. Satz entsprechend gekürzt, damit der Entwurf nicht mehr sich selbst widerspricht (direkt
neben „Beide Abnahmen bestanden").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 22:33:00 +02:00
knsoandClaude Opus 5 149ec9c5d9 feat(showroom): Aufstellung der 13 Dummies mit Clips und Waffen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:28:59 +02:00
knsoandClaude Opus 5 4422432998 feat(assets): elf Mocap-Clips als weapons-mocap.glb für das Mannequin
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:15:28 +02:00
knsoandClaude Sonnet 5 05e2914ff3 test(world): letzte Verweise auf alte Chunk-Schlüssel entfernen
parkData.test.js prüfte die Fahrzeug-Startpunkte noch gegen c.roads, den es im neuen
Chunk-Format nicht mehr gibt; jetzt gegen c.carriageway, die schärfere Aussage (das Auto
steht auf der Fahrbahn, nicht nur irgendwo im alten, breiteren Korridor). materials.test.js
und props.test.js trugen noch die toten Schlüssel roads/centerlines bzw. einen Kommentar auf
centerlines() — beides Relikte vor dem Straßen-Umbau, ersatzlos entfernt bzw. auf edges umformuliert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 22:15:26 +02:00
knsoandClaude Sonnet 5 8bc688561c test(extract): ausgelieferte Straßen festhalten, Messwerte in den Entwurf
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 22:06:35 +02:00
knsoandClaude Sonnet 5 e640f551a4 test(extract): Minimap-Abgleich mit den neuen Daten wieder scharf
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 22:02:38 +02:00
knsoandClaude Sonnet 5 be102448c7 chore(data): Welt mit Bordstein, Gehweg und Markierung neu erzeugt
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 21:55:59 +02:00
knsoandClaude Opus 5 aa6cde79d3 feat(assets): Blickrichtung, Drift und Schleifennaht der Mocap-Clips
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:46:51 +02:00
knsoandClaude Sonnet 5 62c8e267c4 test(world): Laterne außer Reichweite und markings-Versatz end-to-end abdecken
Ersetzt den zu schwachen Leerlisten-Test für sidewalkLampPoints() durch einen Fall mit
echter, aber für LAMP_VERGE zu weit entfernter Gehwegfläche, und belegt in materials.test.js,
dass applyPolygonOffsets() den eigenen markings-Versatz tatsächlich in ein Materialobjekt
durchreicht.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 21:44:42 +02:00
knsoandClaude Opus 5 070bb79593 feat(assets): Mocap-Knochen auf das Mannequin übertragen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:34:38 +02:00
knsoandClaude Sonnet 5 25248b3929 feat(world): Materialien, Kollision und Laternen für die neuen Straßen
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 21:26:18 +02:00
knsoandClaude Opus 5 2a60f92539 feat(assets): fünf Waffen aus dem Pack als GLB mit WebP und meshopt
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:19:40 +02:00
knsoandClaude Opus 5 e97ef14f78 feat(assets): TGA-Maske, UV-Spiegelung und Längenprüfung für die Waffen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:14:04 +02:00
knsoandClaude Sonnet 5 ca4f7161af feat(world): Fahrbahnmulde, Bordsteinwand und Markierungen bauen
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 21:02:14 +02:00
knsoandClaude Opus 5 456fbe89b9 docs: Umsetzungsplan für die Animations-Testseite
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:53:02 +02:00
knsoandClaude Opus 5 3d39a9bd66 docs: Task 13, 14 und 16 trocken erprobt und berichtigt
Die drei Browser- und Abnahmeaufgaben in einem Wegwerf-Arbeitsverzeichnis
durchgespielt, mit Gegenproben aus absichtlich falschem Code. Gefunden:

- Task 13: Höhen wurden exakt verglichen, die Positionen liegen aber als
  Float32 vor - die Tests wären auch mit richtigem Code rot gewesen.
- Task 13: Die Oberkante des Bordsteins lag auf y = 0 und verschwand damit
  unter dem Gehwegbelag; auf welcher Seite sie liegt, war offen. Sie liegt
  jetzt 1 cm über den Belägen, und die Seite kommt aus zwei Proben an der
  Fahrbahn. Die Wand steht 1 cm in der Fahrbahn, sonst fällt sie
  deckungsgleich auf die Lochwand der Bodenplatte.
- Task 13: Die Brückenmarkierung hatte weder Code noch Test; die Regel ist
  jetzt "alle Eckpunkte auf der Brücke" statt "Schwerpunkt", sonst schwebt
  der Rest einer halb aufliegenden Markierung über der Mulde.
- Task 14: Ein Laternentest wäre mit richtigem Code rot gewesen, ein
  zweiter konnte nicht fehlschlagen, und die geforderte Laterne aus einem
  Chunk im neuen Format hatte keinen Test.
- Task 14: Die Tönung ließ die grüne Parkfarbe aus Teil 1 fallen, die
  Ersatzfarben fehlten, DECALS ist nicht exportiert, und die Markierung
  sollte die oberste Versatzstufe bekommen, obwohl der Kommentar dort vor
  genau diesem Wachstum warnt.
- Task 14: tests/placesSite.test.js stand nicht in der Dateiliste und
  wäre gebrochen.
- Task 16: Die Erwartungszahlen stammten von vor dem Park, der vom Entwurf
  verlangte Integrationstest fehlte, und das Entfernen des Skips hatte
  keinen eigenen Schritt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:52:41 +02:00
knsoandClaude Opus 5 70730d9362 docs: Entwurf Animations-Testseite mit Waffen-Dummies
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 19:54:37 +02:00
knsoandClaude Sonnet 5 2201be37bd feat(extract): Pipeline baut Straßen aus dem Graphen
run.py baut jetzt aus dem Straßengraphen (Tasks 3-11) statt aus den nachgezeichneten
Straßenrändern: Fahrbahn, Bordstein, Gehweg und Restflächen aus build_street(),
amerikanische Markierungen aus markings(). fit_to_carriageway() bringt Bodenplatte und
Beläge auf dasselbe Loch wie die Fahrbahn und hält Palmen dabei von ihr fern - nach
derselben Regel, mit der apply_street() sie schon von der Jefferson Avenue fernhält.

split_by_chunk_all() bekommt dafür eine parametrisierbare Splittergrenze: Ein Strich der
gestrichelten Linien ist 3 m x 10 cm = 0,3 m² und wäre an der bisherigen 0,5-m²-Grenze für
Flächen verschwunden. Markierungen laufen deshalb über den neuen Helfer chunk_markings()
mit eigener, kleinerer Grenze; alle anderen Aufrufer bleiben bei 0,5 m².

Am echten Netz gemessen: 1.537 Knoten, 1.790 Kanten, 830 Bordsteinstücke, 171
Restflächen, 22.394 Markierungen, 56 Palmen an der Fahrbahn entfernt. Fahrbahn-, Gehweg-
und Restflächen sowie die Bordsteinlänge treffen die vorab am echten Netz gemessenen
Erwartungswerte auf 0,03-0,3 % genau.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 19:43:16 +02:00
knsoandClaude Opus 5 892558fc50 docs: Task 12 gegen das echte Netz berichtigen
Vor dem Auftrag die Kette am echten Netz nachgemessen. Vier Fehler im Plan:

- Der Zuschnitt auf Chunks verwirft Stücke bis 0,5 m², ein Strich ist
  0,3 m² - keiner der 4.363 Striche hätte überlebt. Markierungen bekommen
  eine eigene Splittergrenze.
- _classified() stand in der Chunk-Schleife: 144 x 5,8 s, rund 14 Minuten.
- Der Entwurf setzt Palmen nach dem Straßenbau neu, der Plan nicht:
  24 Palmen stünden auf der Fahrbahn.
- Der erste Test verglich einen Aufruf mit sich selbst.

Dazu Messskript und Erwartungen auf den Stand nach dem Flamingo Park.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 19:22:48 +02:00
knsoandClaude Sonnet 5 6c73b7f816 fix(extract): Fahrbahn, Restflächen und Brücken sind begehbar, nicht nur der Gehweg
Korrektur eines Fehlers im Plantext, nicht in der vorigen Umsetzung: "begehbar" ist in
src/ui/mapMath.js (nearestWalkable()/landingFor()) als "Kollisionsboden ohne Gebäude"
definiert, nicht als Fußgängerzone. Fahrbahn ist die kollidierende Mulde, Restflächen
liegen auf der Bodenplatte, Brücken haben ihr eigenes Deck - alle drei tragen wie die
Landplatte und der Gehweg. Nur Gebäude sperren; Wasser bleibt gesperrt, weil nichts
darüber gemalt wird. Ein Klick auf eine Straße oder einen Damm über Wasser landet damit
wieder auf ihr, statt ins Nichts zu greifen.

chunk["bridges"] wird dafür wieder gelesen und zusätzlich im Kartenbild in BRIDGE
gezeichnet, nach der Fahrbahn, damit eine Brücke als Brücke zu sehen ist und nicht als
Straße im Wasser.

test_walkable_mask_counts_only_ground_with_collision und
test_walkable_mask_blocks_buildings_and_water zurückgestellt; ein neuer Test sichert die
Brückenfarbe im Kartenbild. test_gehweg_ist_begehbar_die_fahrbahn_nicht sagte nach der
Korrektur nicht mehr die Wahrheit über sich selbst - Fahrbahn ist jetzt auch begehbar -
und ist umbenannt in test_gehweg_und_fahrbahn_sind_beide_begehbar. Der Skip-Grund von
test_committed_map_files_match_the_committed_chunks nennt jetzt die Bedingung fürs
Entfernen statt einer Aufgabennummer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 18:52:56 +02:00
knsoandClaude Opus 5 4e21367e73 docs: Brücken in Task 13 und Skip-Pflicht in Task 16 nachtragen
Die Fahrbahn liegt überall 15 cm unter dem Boden, über Brücken also im
Inneren des Decks. Markierungen auf Brücken kommen deshalb auf Deckhöhe.
Dazu die Pflicht, den bis zur Abnahme übersprungenen Minimap-Test mit den
neu erzeugten Daten wieder scharf zu schalten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 18:45:33 +02:00
knsoandClaude Sonnet 5 f1c8371365 feat(extract): Minimap zeichnet Fahrbahn, Gehweg und Restflächen
overview.py las chunk["roads"] und item["bridge"] ohne Rückfall und riss mit Chunks im
Format aus Task 10 (carriageway/sidewalk/lots/bridges) einen KeyError auf. _paint_layers()
liest jetzt carriageway, sidewalk und lots getrennt; chunk["bridges"] braucht keine eigene
Regel mehr, weil seine Fahrbahn schon in carriageway steckt und sein begehbarer Streifen im
sidewalk. Markierungen werden nicht gezeichnet: bei 1 px je 3 m wäre eine 10-cm-Linie ein
Drittel Pixel und nur Rauschen.

In der Begehbarkeitsmaske sperren Fahrbahn und Restflächen jetzt aktiv, auch über der
Landplatte - dort fährt Verkehr, kein Fußgänger soll dort landen. Der Gehweg trägt dafür
auch ohne Landplatte, wie zuvor nur die Brücke; die alte Sonderregel für Brücken entfällt
darum ersatzlos.

Alle Fixtures in test_overview.py, die von Hand im alten Format standen, sind mitgezogen;
eine davon musste dabei die Zusicherung wechseln (Fahrbahn auf Land ist jetzt gesperrt statt
begehbar), zwei weitere kamen dazu (Restfläche und Brücke ohne Land). Der Gehweg-Streifen im
neuen Prüfkörper war im Planentwurf 3 statt 12 m breit: schmaler als eine 4-m-Maskenzelle,
die deshalb nie ganz hineinpasste, unabhängig von der Rundung - jetzt auf 12 m korrigiert wie
die Fahrbahn.

test_committed_map_files_match_the_committed_chunks ist bis Task 16 übersprungen: die
ausgelieferten Chunks unter public/data/vice-a bleiben laut Plan bis dahin im alten Format,
render_map() kennt das nicht mehr.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 18:41:19 +02:00