diff --git a/docs/superpowers/specs/2026-09-13-feinkalibrierung-design.md b/docs/superpowers/specs/2026-09-13-feinkalibrierung-design.md new file mode 100644 index 0000000..930853c --- /dev/null +++ b/docs/superpowers/specs/2026-09-13-feinkalibrierung-design.md @@ -0,0 +1,137 @@ +# Feinkalibrierung — Entwurf + +**Datum:** 2026-09-13 +**Zusammenhang:** Der Nutzer will die extrahierte Welt Chunk für Chunk korrigieren, indem er im Chat eine Stelle nennt und sagt, was dort anders sein soll. Die Stelle liest er auf der Minimap ab (`docs/superpowers/specs/2026-09-13-minimap-design.md`), die dafür Chunk-Namen A1–L12 und Weltkoordinaten anzeigt. +**Reihenfolge, vom Nutzer entschieden:** Die Umsetzung beginnt, sobald die Beschriftungs-Aufgabe task_999fed4a gelandet ist, die derzeit in einer anderen Sitzung `tools/extract/run.py` ändert. Danach folgt der Straßenbau (Bordstein, Gehweg, Rissrichtung). + +## Ziel + +Eine dauerhafte Korrekturschicht in der Extraktionspipeline. Jede Korrektur überlebt jeden Pipeline-Lauf, wirkt zugleich auf 3D-Welt, Kollision, Minimap und Landemaske, und lässt sich im Gespräch über Chunk-Name und Weltkoordinate beschreiben. + +## Entscheidungen des Nutzers + +| Frage | Entscheidung | +|---|---| +| Was korrigierbar ist | Gebäude, Straßen, Flächen und Küste, Requisiten | +| Wie eine Stelle benannt wird | Chunk-Name und Weltkoordinate, wie auf der Minimap angezeigt | +| Korrektur trifft nach einem Lauf nichts mehr | Warnen und überspringen, Lauf geht durch | +| Wo Korrekturen wirken | In der Pipeline, zwischen Extraktion und Chunk-Ausgabe | +| Laternen | Ziehen aus dem Browser in die Pipeline, damit Korrekturen sie erreichen | +| Aufbau der Korrekturdatei, Anwendung, Ablauf | Abschnitte 1 bis 3 bestätigt | +| Reihenfolge | Kalibrierung vor dem Straßenbau | + +## Korrekturdatei + +`tools/extract/data/corrections.toml`, gelesen mit `tomllib` aus der Standardbibliothek — keine neue Abhängigkeit. TOML statt JSON, weil Kommentare erlaubt sind und jede Korrektur festhalten soll, warum es sie gibt. + +Eine Korrektur je `[[correction]]`-Tabelle. Koordinaten sind immer Welt-x und Welt-y in Metern, genau wie die Minimap sie anzeigt — nie three.js-z. + +```toml +[[correction]] +chunk = "C10" +action = "remove_building" +at = [412, 1830] +reason = "Beschriftung STARFISH ISLAND, kein Gebäude" + +[[correction]] +chunk = "B9" +action = "set_building_height" +at = [-490, 270] +height = 180 +reason = "Southeast Financial Center, laut Skyline 248 m, Grundriss verfehlt" +``` + +### Felder aller Korrekturen + +| Feld | Pflicht | Bedeutung | +|---|---|---| +| `chunk` | ja | Chunk-Name A1–L12. Plausibilitätsprüfung, siehe unten. | +| `action` | ja | Eine der Aktionen aus der folgenden Tabelle. | +| `reason` | nein | Freitext, warum die Korrektur existiert. | + +### Aktionen + +| Bereich | `action` | Weitere Felder | Wirkung | +|---|---|---|---| +| 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. | +| | `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. | +| 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. | + +„Liegt in" heißt Punkt-in-Polygon unter Beachtung von Löchern: ein Punkt im Innenhof eines Gebäudes liegt nicht im Gebäude. + +## Prüfregeln + +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. + +**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. + +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. + +## Anwendung in der Pipeline + +**Zeitpunkt:** nachdem Land, Beläge, Straßen, Mittellinien, Gebäude mit Höhen und Palmen als Weltgeometrie berechnet sind, und bevor die Chunk-Nutzlasten gebaut werden. Alles danach — Chunk-Dateien, Kollision, Minimap, Landemaske — sieht die Korrekturen automatisch. + +**Reihenfolge nach Ebene:** Küste (`set_area`) → Beläge (`set_surface`) → Straßen → Gebäude → Laternen werden gesetzt → Requisiten (`remove_props`, `add_palm`, `add_lamp`). Innerhalb einer Ebene gilt die Dateireihenfolge. Grund: das Ergebnis soll nicht davon abhängen, in welcher Reihenfolge Einträge in die Datei geschrieben wurden. Die Reihenfolge verhindert **nicht**, dass eine Hinzufüge-Aktion auf Wasser trifft — das leistet die Prüfregel „Nicht auf Land". + +**Laternen ziehen in die Pipeline.** Heute setzt `src/world/props.js` sie im Browser über `vergeLampPoints` entlang der Mittellinien. Künftig berechnet die Pipeline sie, mit derselben Randsuche wie die Palmen, und schreibt sie in die Nutzlast: + +``` +"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. + +**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). + +## Ablauf im Chat + +1. Der Nutzer öffnet die Minimap, liest etwa `C10 · x 412 / y 1830` ab und sagt, was dort anders sein soll. +2. Claude trägt die Korrektur mit `reason` in `corrections.toml` ein. +3. Claude rendert einen vergrößerten Ausschnitt genau dieses Chunks aus den aktuellen Chunk-Dateien — **vorher**. +4. Claude lässt `run.py` und `overview.py` laufen. +5. Claude rendert denselben Ausschnitt erneut — **nachher** — und schickt beide Bilder, zusammen mit jeder Warnung aus dem Lauf. +6. Der Nutzer lädt das Spiel neu. + +Für Schritt 3 und 5 bekommt `overview.py` eine Option `--chunk C10`, die nur diesen Chunk mit 4 px/m nach `debug/chunk-C10.png` rendert (1.000 × 1.000 px), mit denselben Farben wie die Gesamtansicht. + +## Prüfung + +**Je Aktion, an künstlicher Geometrie (pytest):** +- Entfernen und Ändern treffen genau das Objekt, das den Punkt enthält, und nicht das Nachbarobjekt. +- Ein Punkt im Innenhof eines Gebäudes trifft das Gebäude nicht. +- `add_building` erzeugt ein Rechteck der richtigen Fläche, auch gedreht. +- `remove_road` entfernt die Mittellinien im Straßenstück; `add_road` fügt eine hinzu. +- `set_area` zu Wasser entfernt ein Gebäude mit 60 % Fläche im Vieleck, behält eines mit 40 %, entfernt Palmen darin, schneidet die Mittellinien am Vieleck ab — und die anschließende Laternensetzung setzt dort keine Laterne. +- `remove_props` beachtet Radius und `kinds`. + +**Prüfregeln:** +- Jeder Formfehler bricht mit Nummer und Feld in der Meldung ab. +- Verwaist, falscher Chunk und nicht auf Land werden übersprungen und gemeldet, der Lauf liefert trotzdem ein Ergebnis. +- Die Ebenen-Reihenfolge: dieselben Einträge in umgekehrter Dateireihenfolge ergeben dasselbe Ergebnis. + +**Laternen in der Pipeline** — dieselben Zusicherungen wie bisher im Browser, jetzt in Python: nie auf der Fahrbahn, kein Stapel an zwei gegenüberliegenden Ästen einer Kreuzung, erste Laterne bei halber Teilung. Neu: eine Laterne an einer Chunk-Grenze prüft auch die Straßen des Nachbarchunks. + +**Browser:** `props.js` liest `chunk.lamps`; die entfallenen Funktionen und ihre Tests sind entfernt; eine Sichtprüfung an einer Chunk-Grenze zeigt keine Laterne auf der Fahrbahn. + +**Durchlauf mit echter Datei:** eine `corrections.toml` mit je einer Korrektur pro Bereich an bekannten Stellen; vorher und nachher als Chunk-Ausschnitt verglichen. + +## Nicht Teil dieses Entwurfs + +- Ein Markierwerkzeug auf der Karte, das Korrekturen ohne Chat anlegt. +- Anzeige vorhandener Korrekturen auf der Minimap. +- Nicht-rechteckige hinzugefügte Gebäude. +- Gekrümmte hinzugefügte Straßen. +- Bordstein, Gehweg und Rissrichtung — der anschließende Straßenbau. Er kann einzelne Straßenkorrekturen verwaisen lassen; die Prüfregel meldet sie dann.