diff --git a/docs/superpowers/specs/2026-09-13-feinkalibrierung-design.md b/docs/superpowers/specs/2026-09-13-feinkalibrierung-design.md index 930853c..9ac8cd5 100644 --- a/docs/superpowers/specs/2026-09-13-feinkalibrierung-design.md +++ b/docs/superpowers/specs/2026-09-13-feinkalibrierung-design.md @@ -55,12 +55,12 @@ reason = "Southeast Financial Center, laut Skyline 248 m, Grundriss verfehlt" |---|---|---|---| | Gebäude | `remove_building` | `at` | Entfernt jeden Grundriss, in dem `at` liegt. | | | `set_building_height` | `at`, `height` (> 0) | Setzt die Höhe jedes Grundrisses, in dem `at` liegt; `source` wird `"correction"`. | -| | `add_building` | `at` (Mittelpunkt), `size = [breite, tiefe]` (> 0), `height` (> 0), `rotation` (Grad, Vorgabe 0) | Fügt einen rechteckigen Grundriss hinzu, `speculative = false`, `source = "correction"`. | -| Straßen | `remove_road` | `at` | Entfernt jedes Straßenstück, in dem `at` liegt, **samt der Mittellinien innerhalb dieses Stücks** — sonst setzte die Pipeline Laternen an eine Straße, die es nicht mehr gibt. | +| | `add_building` | `at` (Mittelpunkt), `size = [breite, tiefe]` (> 0), `height` (> 0), `rotation` (Grad, Vorgabe 0) | Fügt einen rechteckigen Grundriss hinzu, `speculative = false`, `source = "correction"`. `breite` läuft entlang Welt-x, `tiefe` entlang Welt-y; `rotation` dreht gegen den Uhrzeigersinn um den Mittelpunkt, von oben mit Norden oben gesehen. | +| Straßen | `remove_road` | `at` | Entfernt jedes Straßenstück, in dem `at` liegt, und **schneidet die Mittellinien an diesem Stück ab** — Mittellinien laufen über mehrere Straßenstücke; bliebe ihr Abschnitt stehen, setzte die Pipeline Laternen an eine Straße, die es nicht mehr gibt. Reststücke unter 1 m entfallen. | | | `set_bridge` | `at`, `bridge` (Wahrheitswert) | Setzt `bridge` jedes Straßenstücks, in dem `at` liegt. | | | `add_road` | `from = [x, y]`, `to = [x, y]`, `width` (> 0) | Fügt ein gerades Straßenstück der Breite `width` hinzu, `bridge = false`, **samt einer Mittellinie von `from` nach `to`**. | | Flächen und Küste | `set_surface` | `at`, `kind` (`sand`, `park`, `urban`) | Verschiebt die Belagsfläche, in der `at` liegt, in die Art `kind`. | -| | `set_area` | `polygon = [[x, y], …]` (≥ 3 Punkte), `to` (`land`, `water`) | `water`: schneidet das Vieleck aus dem Land, entfernt Straßen und Gebäude, die zu mehr als der Hälfte ihrer Fläche darin liegen, schneidet die Mittellinien am Vieleck ab und entfernt Palmen darin. Laternen gibt es zu diesem Zeitpunkt noch nicht; sie werden später gesetzt und nie auf Wasser. `land`: fügt das Vieleck dem Land hinzu, sonst nichts. | +| | `set_area` | `polygon = [[x, y], …]` (≥ 3 Punkte, gültiges Vieleck), `to` (`land`, `water`) | `water`: schneidet das Vieleck aus dem Land **und aus Sand, Park und Stadtfläche**, entfernt Straßen und Gebäude, die zu mehr als der Hälfte ihrer Fläche darin liegen, schneidet die Mittellinien am Vieleck ab und entfernt Palmen darin. Laternen gibt es zu diesem Zeitpunkt noch nicht; sie werden später gesetzt und nie auf Wasser. `land`: fügt das Vieleck dem Land hinzu, sonst nichts. | | Requisiten | `remove_props` | `at`, `radius` (> 0), `kinds` (Liste aus `palm`, `lamp`; Vorgabe beide) | Entfernt die genannten Requisiten im Umkreis. | | | `add_palm` | `at` | Setzt eine Palme. | | | `add_lamp` | `at` | Setzt eine Laterne, ausgerichtet zur nächstgelegenen Mittellinie. | @@ -71,12 +71,12 @@ reason = "Southeast Financial Center, laut Skyline 248 m, Grundriss verfehlt" Es gibt zwei Arten von Problemen, und sie werden unterschiedlich behandelt: -**Formfehler — der Lauf bricht mit klarer Meldung ab.** Eine unbekannte `action`, ein fehlendes Pflichtfeld, ein falscher Typ, ein ungültiger Chunk-Name, eine Höhe oder Breite ≤ 0, ein Vieleck mit weniger als drei Punkten. Das sind Tippfehler in der Datei; sie still zu überspringen, ließe eine gewollte Korrektur verschwinden, ohne dass es jemand merkt. Die Meldung nennt die Nummer des Eintrags und das Feld. +**Formfehler — der Lauf bricht mit klarer Meldung ab.** Eine unbekannte `action`, ein fehlendes Pflichtfeld, **ein Feld, das diese Aktion nicht kennt** (etwa `hight` statt `height` — sonst fehlte die Höhe still), ein falscher Typ, ein ungültiger Chunk-Name, eine Höhe, Breite oder ein Radius ≤ 0, ein Vieleck mit weniger als drei Punkten oder mit sich selbst schneidenden Kanten. Das sind Tippfehler in der Datei; sie still zu überspringen, ließe eine gewollte Korrektur verschwinden, ohne dass es jemand merkt. Die Meldung nennt die Nummer des Eintrags und das Feld. **Inhaltliche Nichttreffer — Warnung, Eintrag wird übersprungen, Lauf geht durch.** Vom Nutzer so entschieden. - **Verwaist:** kein Objekt enthält `at`. - **Falscher Chunk:** `at` liegt nicht im genannten `chunk`; bei `set_area` gilt der Schwerpunkt des Vielecks, bei `add_road` der Mittelpunkt von `from` und `to`. Fast immer ein Zahlendreher in der Koordinate. -- **Nicht auf Land:** eine Hinzufüge-Aktion (`add_building`, `add_road`, `add_palm`, `add_lamp`), deren Punkt nach Anwendung aller Küstenkorrekturen im Wasser liegt. Dieselbe Regel, die schon verhindert, dass Palmen im Meer stehen. +- **Nicht auf Land:** eine Hinzufüge-Aktion, deren Ort nach Anwendung aller Küstenkorrekturen im Wasser liegt — bei `add_building`, `add_palm` und `add_lamp` der Punkt `at`, bei `add_road` **beide Endpunkte** `from` und `to`. Bei der Straße gelten die Endpunkte statt des Mittelpunkts, damit eine Brücke über Wasser hinzugefügt werden kann: ihr Mittelpunkt liegt im Wasser, ihre Enden an Land. Dieselbe Regel, die schon verhindert, dass Palmen im Meer stehen. Am Ende jedes Laufs listet die Pipeline alle übersprungenen Einträge mit Nummer, Chunk, Koordinate, Grund der Warnung und dem `reason` der Korrektur, sowie die Zahl der angewendeten. @@ -92,7 +92,27 @@ Am Ende jedes Laufs listet die Pipeline alle übersprungenen Einträge mit Numme "lamps": [ [x, z, heading], ... ] // chunk-lokal wie palms, heading in Radiant ``` -Der Browser liest `chunk.lamps`; `vergeLampPoints` und `pointInPolygon` in `props.js` entfallen. Nebeneffekt: Im Browser kannte die Laternensuche nur die Straßen des eigenen Chunks, weshalb an Chunk-Grenzen einzelne Laternen auf der Fahrbahn stehen konnten. In der Pipeline ist der ganze Ausschnitt bekannt — diese Lücke schließt sich. Die Regeln bleiben: erste Laterne bei halber Teilung, Seiten abwechselnd, nie auf der Fahrbahn, kein Stapel an Kreuzungen. +Der Browser liest `chunk.lamps`; `lampPoints`, `vergeLampPoints` und `pointInPolygon` in `props.js` entfallen samt ihren Tests. Nebeneffekt: Im Browser kannte die Laternensuche nur die Straßen des eigenen Chunks, weshalb an Chunk-Grenzen einzelne Laternen auf der Fahrbahn stehen konnten. In der Pipeline ist der ganze Ausschnitt bekannt — diese Lücke schließt sich. + +Regeln der Laternensetzung, unverändert übernommen wo nicht anders gesagt: + +| Regel | Wert | +|---|---| +| Abstand entlang der Mittellinie | 35 m | +| erste Laterne | bei `min(35 / 2, Linienlänge / 2)` — kein Stapel an Kreuzungen | +| Seitenversatz, von innen nach außen probiert | 6, 8, 10, 12, 14, 16 m | +| Seite | abwechselnd **je Linie** (links, rechts, links …). Der Browser wechselte über alle Punkte eines Chunks hinweg, das hing von der Aufteilung in Chunks ab. | +| Abstand zur Fahrbahn | mindestens 0,5 m | +| **neu:** nicht in einem Gebäude | Der Browser prüfte nur die Fahrbahn. Bei 16 m Versatz von einer schmalen Straße landete eine Laterne leicht in einem Haus. | +| auf Land | wie bei den Palmen | + +**Ausrichtung:** Der Browser dreht jede Laterne heute um `rotation.y = atan2(dz, dx)`, gemessen entlang der Mittellinie in three-Koordinaten. Die Pipeline rechnet in Weltkoordinaten mit y nach Norden und z = −y. Derselbe Winkel entsteht dort als + +``` +heading = atan2(-(y1 - y0), x1 - x0) +``` + +mit `(x0, y0) → (x1, y1)` als Laufrichtung der Mittellinie am Laternenpunkt. Eine andere Umrechnung dreht alle Laternen falsch herum. **Chunk-Zuordnung in Python:** `tools/extract/leonida/coords.py` bekommt `chunk_for_world(x, y) -> tuple[int, int]` als Spiegel von `chunkForWorld` in `src/world/coords.js`, mit einem Test am selben Beispiel wie in JavaScript (Welt `(−511, 254)` → `(1, 8)` → B9).