fix(extract): Chunk-Ausgabe lauf-stabil, Eckenprüfung in beide Richtungen
emit.py normalisiert jede ausgegebene Fläche und sortiert die Listen nach der Ausgabestruktur selbst. unary_union() in apply_street() gab das kartenweite Straßenpolygon mit anderem Ringstartpunkt und an anderer Listenstelle zurück und änderte damit 88 Chunkdateien, in denen sich geometrisch nichts bewegt hatte (symmetrische Differenz 0,00 m²). Belagsarten und Mittellinien behalten ihre Reihenfolge: overview.py malt die Beläge in Listenreihenfolge übereinander, und vergeLampPoints() hängt an Reihenfolge und Laufrichtung der Mittellinien — Sortieren verschöbe dort 1.848 von 3.921 Laternen auf die andere Straßenseite. check_corners() misst gegen den Straßenrand statt gegen die Straßenfläche. Alle vier Ecken liegen in der Fahrbahn, ihr Flächenabstand ist 0,000 m; die alte Prüfung schlug deshalb nur an, wenn eine Straße sich zurückzieht. Eine nach innen verbreiterte Straße hätte apply_park() still auf das veraltete Viereck zurückgeschnitten. Die Parkwege werden am Parkviereck abgeschnitten — die runden Kappen von LineString.buffer() lagen mit 95,96 m² darüber hinaus, rund 41 m² davon sichtbar als Betonzungen auf den umgebenden Belägen. load_elements() prüft den OSM-Abzug so streng wie die Parkdatei, mit Dateiname statt nacktem KeyError. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a3504df43d
commit
3c74202b5e
10 files changed
+333
-102
No files matched your search
@@ -19,7 +19,7 @@
|
||||
- Python-Aufrufe unter Windows (Git Bash) immer mit `PYTHONUTF8=1`, aus `tools/extract`: `PYTHONUTF8=1 uv run …`. Neue Python-Dateien lesen und schreiben Text immer mit `encoding="utf-8"`.
|
||||
- Keine neuen Abhängigkeiten, weder in `pyproject.toml` noch in `package.json`.
|
||||
- Welt-x/Welt-y wie auf der Minimap; three: x = X, z = −Y; chunk-lokal u = X − origin[0], v = −Y − origin[1].
|
||||
- Werte aus der Spec, wörtlich: `scale_u` 1.0, `offset_u` 6.25, Hauptwege 6 m, übrige Wege 3 m, Parkplatzgasse und Zufahrt 6 m, Gebäudehöhe ohne OSM-Angabe 4 m, Jefferson Avenue 12 m von (1516,5 | 664) bis (1545,2 | 334,2), Anker höchstens 1,5 m, Spielecken höchstens 2 m von einer Straße, Mittellinienstücke unter 15 m entfallen, Straßenstücke unter 20 m² entfallen, Palmenabstand 6 m (`PALM_CLEARANCE`), Palmen zur neuen Straße 1,5 m (`PALM_KERB`), Wege nur bis 5 m vor dem echten West- oder Ostrand verlängern.
|
||||
- Werte aus der Spec, wörtlich: `scale_u` 1.0, `offset_u` 6.25, Hauptwege 6 m, übrige Wege 3 m, Parkplatzgasse und Zufahrt 6 m, Gebäudehöhe ohne OSM-Angabe 4 m, Jefferson Avenue 12 m von (1516,5 | 664) bis (1545,2 | 334,2), Anker höchstens 1,5 m, Spielecken höchstens 2,5 m vom Rand einer Straße (Fixrunde 3, Befund A; ursprünglich 2 m von der Straßenfläche), Mittellinienstücke unter 15 m entfallen, Straßenstücke unter 20 m² entfallen, Palmenabstand 6 m (`PALM_CLEARANCE`), Palmen zur neuen Straße 1,5 m (`PALM_KERB`), Wege nur bis 5 m vor dem echten West- oder Ostrand verlängern.
|
||||
- Hilfsdateien für die Browserprüfung (`index-debug.html` o. ä.) nie committen.
|
||||
|
||||
## Dateien
|
||||
@@ -52,7 +52,7 @@ Prüfaufgabe ohne Codeänderung. Sie klärt, ob ein Lauf auf diesem Rechner die
|
||||
- Consumes: nichts
|
||||
- Produces: eine funktionierende `tools/extract/.venv` mit Python 3.12 und die Aussage „Lauf reproduziert die Daten“ (oder ein Stopp)
|
||||
|
||||
- [ ] **Step 1: Umgebung anlegen**
|
||||
- [x] **Step 1: Umgebung anlegen**
|
||||
|
||||
Run (Git Bash, aus dem Repo-Wurzelordner):
|
||||
```bash
|
||||
@@ -60,7 +60,7 @@ cd tools/extract && uv sync --python 3.12
|
||||
```
|
||||
Expected: `uv` lädt bei Bedarf Python 3.12 und installiert die Pakete aus `uv.lock` ohne Fehler.
|
||||
|
||||
- [ ] **Step 2: Bestehende Tests als Ausgangslage**
|
||||
- [x] **Step 2: Bestehende Tests als Ausgangslage**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
@@ -69,7 +69,7 @@ cd ../.. && npm test
|
||||
```
|
||||
Expected: beide grün. Anzahl der Tests notieren. Schlägt schon hier etwas fehl, anhalten und dem Nutzer die Ausgabe zeigen.
|
||||
|
||||
- [ ] **Step 3: Pipeline unverändert laufen lassen**
|
||||
- [x] **Step 3: Pipeline unverändert laufen lassen**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
@@ -77,7 +77,7 @@ cd tools/extract && PYTHONUTF8=1 uv run python run.py
|
||||
```
|
||||
Expected: lädt 144 Kacheln nach `cache/`, gibt den Entfernen-Bericht, „Gebäude: …“ und am Ende das Ergebnis von `render_map` aus. Dauer: einige Minuten, Speicherbedarf bis etwa 2 GB.
|
||||
|
||||
- [ ] **Step 4: Ergebnis mit dem eingecheckten Stand vergleichen**
|
||||
- [x] **Step 4: Ergebnis mit dem eingecheckten Stand vergleichen**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
@@ -102,7 +102,7 @@ EOF
|
||||
```
|
||||
Expected: `identisch`.
|
||||
|
||||
- [ ] **Step 5: Entscheiden**
|
||||
- [x] **Step 5: Entscheiden**
|
||||
|
||||
- `identisch`: Run `cd ../.. && git status --short public/data/vice-a`. Zeigt Git dort Änderungen (nur Bytes der PNGs), mit `git checkout -- public/data/vice-a` zurücksetzen. Weiter mit Task 2. Kein Commit.
|
||||
- Sonst: **anhalten.** Dem Nutzer die Ausgabe von Step 4 zeigen, `git checkout -- public/data/vice-a` ausführen und auf seine Entscheidung warten (Spec, Fehlerfälle: „Windows baut die Chunks nicht gleich“).
|
||||
@@ -120,7 +120,7 @@ Expected: `identisch`.
|
||||
- Consumes: nichts
|
||||
- Produces: `tools/extract/data/osm/flamingo-park.json` mit `{"source", "license", "fetched", "elements": [{"id": int, "tags": dict, "geometry": [[lat, lon], …]}]}`; im Modul `overpass_query(bbox) -> str`, `keep(element) -> bool`, `snapshot(raw) -> dict`, `dumps(snap) -> str`
|
||||
|
||||
- [ ] **Step 1: Failing test schreiben**
|
||||
- [x] **Step 1: Failing test schreiben**
|
||||
|
||||
`tools/extract/tests/test_osm.py`:
|
||||
```python
|
||||
@@ -178,12 +178,12 @@ def test_query_asks_for_ways_with_geometry_in_the_box():
|
||||
assert "way(1,2,3,4)[amenity=parking]" in query and query.endswith("out geom;")
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
- [x] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_osm.py -q`
|
||||
Expected: FAIL mit `ModuleNotFoundError: No module named 'osm'`
|
||||
|
||||
- [ ] **Step 3: `osm.py` schreiben**
|
||||
- [x] **Step 3: `osm.py` schreiben**
|
||||
|
||||
`tools/extract/osm.py`:
|
||||
```python
|
||||
@@ -269,12 +269,12 @@ if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_osm.py -q`
|
||||
Expected: 7 passed
|
||||
|
||||
- [ ] **Step 5: Abruf ausführen (Netz)**
|
||||
- [x] **Step 5: Abruf ausführen (Netz)**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run python osm.py`
|
||||
Expected: etwa `230 Elemente, Stand 2026-…` (am 2026-09-15 gezählt: 53 footway, 18 service, 10 pitch, 3 parking, 1 dog_park, 1 playground, 2 swimming_pool, 1 sports_centre, 140 building). Dann prüfen:
|
||||
@@ -283,7 +283,7 @@ grep -c '"Flamingo Park Path' data/osm/flamingo-park.json
|
||||
```
|
||||
Expected: mindestens 5 Zeilen. Weicht die Gesamtzahl um mehr als 20 % ab, anhalten und dem Nutzer melden (OpenStreetMap hat sich geändert).
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
- [x] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/osm.py tools/extract/tests/test_osm.py tools/extract/data/osm/flamingo-park.json
|
||||
@@ -311,7 +311,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- `@dataclass(frozen=True) ParkConfig(osm_path: Path, real: dict[str, tuple] (sw, se, nw), game: dict[str, tuple] (sw, se, ne, nw), scale_u: float, offset_u: float, anchors: tuple[Anchor, ...], keepouts: tuple[Keepout, ...], main_width: float, path_width: float, service_width: float, default_height: float, street: Street)`
|
||||
- `load_park(path: Path) -> ParkConfig`, `parse_park(text: str, base: Path, name: str = "flamingo-park.toml") -> ParkConfig`
|
||||
|
||||
- [ ] **Step 1: Failing test schreiben**
|
||||
- [x] **Step 1: Failing test schreiben**
|
||||
|
||||
`tools/extract/tests/test_park_config.py`:
|
||||
```python
|
||||
@@ -411,12 +411,12 @@ def test_committed_park_file_loads():
|
||||
assert len(config.street.alley_cut) == 12
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
- [x] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_config.py -q`
|
||||
Expected: FAIL mit `ModuleNotFoundError: No module named 'leonida.park'`
|
||||
|
||||
- [ ] **Step 3: `park.py` mit dem Leseteil anlegen**
|
||||
- [x] **Step 3: `park.py` mit dem Leseteil anlegen**
|
||||
|
||||
`tools/extract/leonida/park.py`:
|
||||
```python
|
||||
@@ -615,7 +615,7 @@ def _is_point(value) -> bool:
|
||||
return isinstance(value, list) and len(value) == 2 and all(_is_number(c) for c in value)
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Parkdatei anlegen**
|
||||
- [x] **Step 4: Parkdatei anlegen**
|
||||
|
||||
`tools/extract/data/flamingo-park.toml`:
|
||||
```toml
|
||||
@@ -677,12 +677,12 @@ alley_cut = [[1470, 644], [1496, 644], [1500, 600], [1506, 540], [1514, 460], [1
|
||||
[1497, 340.5], [1486, 380], [1480, 460], [1474, 540], [1468, 600]]
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 5: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_config.py -q`
|
||||
Expected: 12 passed
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
- [x] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/leonida/park.py tools/extract/data/flamingo-park.toml tools/extract/tests/test_park_config.py
|
||||
@@ -706,7 +706,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- `local_metres(lat: float, lon: float) -> tuple[float, float]`
|
||||
- `class ParkFit(config)` mit Attributen `real_width`, `game_width`, `game_u`, `game_quad: Polygon` und Methoden `park_coords(x, y) -> (s, t)`, `game_point(s, t) -> (X, Y)`, `local_to_game(x, y) -> (X, Y)`, `to_game(lat, lon) -> (X, Y)`, `real_quad() -> Polygon`
|
||||
|
||||
- [ ] **Step 1: Failing test schreiben**
|
||||
- [x] **Step 1: Failing test schreiben**
|
||||
|
||||
`tools/extract/tests/test_park_fit.py`:
|
||||
```python
|
||||
@@ -775,12 +775,12 @@ def test_committed_fit_keeps_the_south_edge_true_to_scale_and_offset_from_the_co
|
||||
assert math.dist(nw, config.game["nw"]) == pytest.approx(config.offset_u, abs=0.01)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
- [x] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_fit.py -q`
|
||||
Expected: FAIL mit `ImportError: cannot import name 'M_PER_DEG_LAT' from 'leonida.park'`
|
||||
|
||||
- [ ] **Step 3: Einpassung implementieren**
|
||||
- [x] **Step 3: Einpassung implementieren**
|
||||
|
||||
In `tools/extract/leonida/park.py` den Importblock ergänzen, sodass er so aussieht:
|
||||
```python
|
||||
@@ -851,12 +851,12 @@ class ParkFit:
|
||||
return Polygon([(ox, oy), (ox + ux, oy + uy), (ox + ux + vx, oy + uy + vy), (ox + vx, oy + vy)])
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_fit.py tests/test_park_config.py -q`
|
||||
Expected: 18 passed. Scheitert nur der Anker-Test, `offset_u` nicht still ändern: anhalten und dem Nutzer die beiden Abstände zeigen.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
- [x] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/leonida/park.py tools/extract/tests/test_park_fit.py
|
||||
@@ -883,7 +883,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- `build_layout(elements: list[dict], config, fit: ParkFit) -> ParkLayout`
|
||||
- Hilfen `_pieces(geometry) -> list`, `_polygons(geometry, min_area=1.0) -> list[Polygon]`, `_points(geometry) -> list[tuple]`
|
||||
|
||||
- [ ] **Step 1: Failing test schreiben**
|
||||
- [x] **Step 1: Failing test schreiben**
|
||||
|
||||
`tools/extract/tests/test_park_layout.py`:
|
||||
```python
|
||||
@@ -1028,12 +1028,12 @@ def test_service_ways_become_six_metre_service_surfaces():
|
||||
assert result.paths == []
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
- [x] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_layout.py -q`
|
||||
Expected: FAIL mit `ImportError: cannot import name 'build_layout' from 'leonida.park'`
|
||||
|
||||
- [ ] **Step 3: Anlage implementieren**
|
||||
- [x] **Step 3: Anlage implementieren**
|
||||
|
||||
Importblock von `park.py` so ersetzen:
|
||||
```python
|
||||
@@ -1211,12 +1211,12 @@ def _points(geometry) -> list[tuple[float, float]]:
|
||||
return out
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_layout.py -q`
|
||||
Expected: 14 passed
|
||||
|
||||
- [ ] **Step 5: Gegen den echten OSM-Stand prüfen**
|
||||
- [x] **Step 5: Gegen den echten OSM-Stand prüfen**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
@@ -1232,7 +1232,7 @@ EOF
|
||||
```
|
||||
Expected: `field` 3 (oder mehr Stücke, falls eine Sperrfläche ein Feld teilt), `court` 7, `pool` 2, `parking` mindestens 1, `sand` 1, `Gebäude 11`, `Warnungen []`. Abweichungen dem Nutzer zeigen, nicht still hinnehmen.
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
- [x] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/leonida/park.py tools/extract/tests/test_park_layout.py
|
||||
@@ -1257,7 +1257,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- `apply_street(world: World, street: Street) -> World`
|
||||
- Hilfe `_lines(geometry, min_length=MIN_CENTERLINE) -> list[LineString]`
|
||||
|
||||
- [ ] **Step 1: Failing test schreiben**
|
||||
- [x] **Step 1: Failing test schreiben**
|
||||
|
||||
`tools/extract/tests/test_park_street.py`:
|
||||
```python
|
||||
@@ -1325,12 +1325,12 @@ def test_palms_on_the_new_street_go():
|
||||
assert apply_street(world(), STREET).palms == [(60.0, 500.0), (140.0, 500.0)]
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
- [x] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_street.py -q`
|
||||
Expected: FAIL mit `ImportError: cannot import name 'World' from 'leonida.park'`
|
||||
|
||||
- [ ] **Step 3: Jefferson Avenue implementieren**
|
||||
- [x] **Step 3: Jefferson Avenue implementieren**
|
||||
|
||||
Importblock von `park.py` so ersetzen:
|
||||
```python
|
||||
@@ -1406,12 +1406,12 @@ def apply_street(world: World, street: Street) -> World:
|
||||
surfaces=surfaces, palms=palms)
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_street.py tests/test_park_layout.py -q`
|
||||
Expected: 19 passed
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
- [x] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/leonida/park.py tools/extract/tests/test_park_street.py
|
||||
@@ -1434,7 +1434,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- `check_corners(config, roads: list[tuple[Polygon, bool]], tolerance: float = 2.0) -> None` (liest nur `config.game`)
|
||||
- `apply_park(world: World, layout: ParkLayout, seed: int) -> World`
|
||||
|
||||
- [ ] **Step 1: Failing test schreiben**
|
||||
- [x] **Step 1: Failing test schreiben**
|
||||
|
||||
`tools/extract/tests/test_park_apply.py`:
|
||||
```python
|
||||
@@ -1517,12 +1517,12 @@ def test_corners_must_lie_at_a_road():
|
||||
check_corners(far, roads)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
- [x] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_apply.py -q`
|
||||
Expected: FAIL mit `ImportError: cannot import name 'apply_park' from 'leonida.park'`
|
||||
|
||||
- [ ] **Step 3: Einsetzen implementieren**
|
||||
- [x] **Step 3: Einsetzen implementieren**
|
||||
|
||||
In `park.py` die Zeile `from .heights import PALM_KERB` ersetzen durch:
|
||||
```python
|
||||
@@ -1567,12 +1567,12 @@ def apply_park(world: World, layout: ParkLayout, seed: int) -> World:
|
||||
buildings=buildings, palms=palms, paths=world.paths + layout.paths)
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_park_apply.py -q`
|
||||
Expected: 6 passed
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
- [x] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/leonida/park.py tools/extract/tests/test_park_apply.py
|
||||
@@ -1599,7 +1599,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- `chunk_payload(i, j, *, land, surfaces, roads, centerlines, buildings, palms, paths=()) -> dict` mit neuem Schlüssel `"paths": [{"poly": rings}]`
|
||||
- `manifest.json` → `attribution` endet auf „Flamingo Park: © OpenStreetMap-Mitwirkende (ODbL).“
|
||||
|
||||
- [ ] **Step 1: Failing tests schreiben**
|
||||
- [x] **Step 1: Failing tests schreiben**
|
||||
|
||||
In `tools/extract/tests/test_emit.py` den Test `test_payload_matches_schema` ersetzen und einen Test anhängen:
|
||||
```python
|
||||
@@ -1646,12 +1646,12 @@ def test_flamingo_park_goes_into_a_world_with_roads_around_the_park():
|
||||
assert any(message.startswith("Flamingo Park:") for message in messages)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Tests laufen lassen, sie müssen scheitern**
|
||||
- [x] **Step 2: Tests laufen lassen, sie müssen scheitern**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_emit.py tests/test_park_apply.py -q`
|
||||
Expected: FAIL: `test_payload_matches_schema` (Schlüssel `paths` fehlt), `test_paths_are_chunk_local_rings_like_roads` (`TypeError: chunk_payload() got an unexpected keyword argument 'paths'`), Integrationstest mit `ImportError: cannot import name 'apply_flamingo_park'`
|
||||
|
||||
- [ ] **Step 3: `chunk_payload` erweitern**
|
||||
- [x] **Step 3: `chunk_payload` erweitern**
|
||||
|
||||
In `tools/extract/leonida/emit.py` die Funktion `chunk_payload` ersetzen:
|
||||
```python
|
||||
@@ -1673,7 +1673,7 @@ def chunk_payload(i, j, *, land, surfaces, roads, centerlines, buildings, palms,
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Gesamtablauf in `park.py`**
|
||||
- [x] **Step 4: Gesamtablauf in `park.py`**
|
||||
|
||||
Ans Dateiende von `park.py` anhängen:
|
||||
```python
|
||||
@@ -1691,7 +1691,7 @@ def apply_flamingo_park(world: World, config_path: Path, seed: int) -> tuple[Wor
|
||||
return world, messages
|
||||
```
|
||||
|
||||
- [ ] **Step 5: `run.py` umstellen**
|
||||
- [x] **Step 5: `run.py` umstellen**
|
||||
|
||||
In `tools/extract/run.py`:
|
||||
|
||||
@@ -1736,12 +1736,12 @@ PARK = Path("data/flamingo-park.toml")
|
||||
```
|
||||
Die Zeile `print(render_map(OUT, OUT), "->", OUT)` bleibt danach unverändert stehen.
|
||||
|
||||
- [ ] **Step 6: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 6: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest -q`
|
||||
Expected: alle bestehen, einschließlich `test_committed_map_files_match_the_committed_chunks` (die Chunks sind noch nicht neu erzeugt). `run.py` selbst läuft erst in Task 12.
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
- [x] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/leonida/park.py tools/extract/leonida/emit.py tools/extract/run.py tools/extract/tests/test_emit.py tools/extract/tests/test_park_apply.py
|
||||
@@ -1762,7 +1762,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- Consumes: Chunk-Format mit `paths` und neuen `surfaces`-Arten (Task 8)
|
||||
- Produces: `overview.PATH`, `overview.SURFACE["field" | "court" | "pool" | "parking"]`, `_paint_layers(…, path=None)`
|
||||
|
||||
- [ ] **Step 1: Failing test schreiben**
|
||||
- [x] **Step 1: Failing test schreiben**
|
||||
|
||||
An `tools/extract/tests/test_overview.py` anhängen:
|
||||
```python
|
||||
@@ -1796,12 +1796,12 @@ def test_map_picture_draws_paths_and_the_new_surface_kinds(tmp_path):
|
||||
assert Image.open(dst / "map-walkable.png").getpixel((25, 31)) == overview.WALKABLE # Weg auf Land
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
- [x] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_overview.py -q`
|
||||
Expected: FAIL mit `KeyError: 'field'`
|
||||
|
||||
- [ ] **Step 3: `overview.py` erweitern**
|
||||
- [x] **Step 3: `overview.py` erweitern**
|
||||
|
||||
Die Farbkonstanten so ordnen und ergänzen (ROAD vor SURFACE):
|
||||
```python
|
||||
@@ -1837,12 +1837,12 @@ In `render_map` ebenso:
|
||||
```
|
||||
`_walkable_mask` bleibt unverändert: ohne `path` zählen Wege dort nicht, wie Beläge.
|
||||
|
||||
- [ ] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_overview.py -q`
|
||||
Expected: 8 passed (auch `test_committed_map_files_match_the_committed_chunks`: die eingecheckten Chunks haben noch keine Wege, das Bild bleibt gleich)
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
- [x] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/overview.py tools/extract/tests/test_overview.py
|
||||
@@ -1867,7 +1867,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- Consumes: Chunk-Format aus Task 8 (`chunk.paths[].poly`, `surfaces[].kind` ∈ `field|court|pool|parking`)
|
||||
- Produces: `SURFACE_KINDS` mit sieben Arten, `buildChunkGeometry(chunk).paths`, `MATERIAL_KEYS` mit `field, court, pool, parking, paths`, `SURFACE_COLORS = { court, pool }`, neue `POLYGON_OFFSET_ORDER`, `DECALS` mit den neuen Schichten
|
||||
|
||||
- [ ] **Step 1: Failing tests schreiben**
|
||||
- [x] **Step 1: Failing tests schreiben**
|
||||
|
||||
In `tests/geometry.test.js` im `describe("geometry")` vor dem Test „gibt Gebäuden eine Farbe je Haus“ einfügen:
|
||||
```js
|
||||
@@ -1944,12 +1944,12 @@ In `tests/streaming.test.js` im `describe("ChunkStreamer")` als letzten Test ein
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Tests laufen lassen, sie müssen scheitern**
|
||||
- [x] **Step 2: Tests laufen lassen, sie müssen scheitern**
|
||||
|
||||
Run: `npx vitest run tests/geometry.test.js tests/materials.test.js tests/streaming.test.js`
|
||||
Expected: FAIL: `out.field` ist `undefined`, `SURFACE_COLORS` fehlt, neue Reihenfolge fehlt, Collider hat 6 statt 1 Kind
|
||||
|
||||
- [ ] **Step 3: `geometry.js` erweitern**
|
||||
- [x] **Step 3: `geometry.js` erweitern**
|
||||
|
||||
Zeile 5 und 15 ändern:
|
||||
```js
|
||||
@@ -1971,7 +1971,7 @@ In `buildChunkGeometry` das Objekt `out` ersetzen:
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 4: `materials.js` erweitern**
|
||||
- [x] **Step 4: `materials.js` erweitern**
|
||||
|
||||
`MATERIAL_KEYS`, `TEXTURES` und `FALLBACK_COLORS` ersetzen:
|
||||
```js
|
||||
@@ -2039,19 +2039,19 @@ export async function createMaterials(maxAnisotropy) {
|
||||
applyPolygonOffsets(materials);
|
||||
```
|
||||
|
||||
- [ ] **Step 5: `streaming.js` erweitern**
|
||||
- [x] **Step 5: `streaming.js` erweitern**
|
||||
|
||||
Zeile 7 ersetzen:
|
||||
```js
|
||||
const DECALS = new Set(["roads", "sand", "park", "urban", "field", "court", "pool", "parking", "paths"]);
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 6: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `npm test`
|
||||
Expected: alle bestehen
|
||||
|
||||
- [ ] **Step 7: Textur laden**
|
||||
- [x] **Step 7: Textur laden**
|
||||
|
||||
`tools/assets/textures.mjs` ersetzen:
|
||||
```js
|
||||
@@ -2083,7 +2083,7 @@ for (const slug of SLUGS) {
|
||||
Run: `node tools/assets/textures.mjs`
|
||||
Expected: drei Zeilen `concrete_pavement_….jpg x.x MB`, sonst nichts. `git status --short public/textures` zeigt nur die drei neuen Dateien.
|
||||
|
||||
- [ ] **Step 8: Commit**
|
||||
- [x] **Step 8: Commit**
|
||||
|
||||
```bash
|
||||
git add src/world/geometry.js src/world/materials.js src/world/streaming.js tools/assets/textures.mjs tests/geometry.test.js tests/materials.test.js tests/streaming.test.js public/textures/concrete_pavement_Diffuse.jpg public/textures/concrete_pavement_nor_gl.jpg public/textures/concrete_pavement_arm.jpg
|
||||
@@ -2106,7 +2106,7 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- Consumes: `PLACES` (`src/world/places.js`), `pointInPolygon` (`src/world/props.js`), `chunkForWorld` (`src/world/coords.js`), `tools/extract/data/flamingo-park.toml` (Task 3)
|
||||
- Produces: `src/world/spawn.js` mit `SPAWN = { x, y, z }` und `VEHICLE_SPAWN_POINTS = [[x, y, z], …]`
|
||||
|
||||
- [ ] **Step 1: Failing test schreiben**
|
||||
- [x] **Step 1: Failing test schreiben**
|
||||
|
||||
`tests/parkData.test.js`:
|
||||
```js
|
||||
@@ -2182,12 +2182,12 @@ In `tests/placesSite.test.js` nach dem Test „liegt ganz auf Land, auf keiner S
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
- [x] **Step 2: Test laufen lassen, er muss scheitern**
|
||||
|
||||
Run: `npx vitest run tests/parkData.test.js tests/placesSite.test.js`
|
||||
Expected: FAIL mit `Failed to resolve import "../src/world/spawn.js"`
|
||||
|
||||
- [ ] **Step 3: `spawn.js` anlegen und `main.js` umstellen**
|
||||
- [x] **Step 3: `spawn.js` anlegen und `main.js` umstellen**
|
||||
|
||||
`src/world/spawn.js`:
|
||||
```js
|
||||
@@ -2221,12 +2221,12 @@ import { SPAWN, VEHICLE_SPAWN_POINTS } from "./world/spawn.js";
|
||||
const VEHICLE_SPAWNS = VEHICLE_SPAWN_POINTS.map(([x, y, z]) => new Vector3(x, y, z));
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
- [x] **Step 4: Tests laufen lassen, sie müssen bestehen**
|
||||
|
||||
Run: `npm test`
|
||||
Expected: alle bestehen. Mit den noch alten Chunk-Daten gibt es keine Wege, der neue Test in `placesSite.test.js` besteht deshalb trivial; scharf wird er in Task 12.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
- [x] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/world/spawn.js src/main.js tests/parkData.test.js tests/placesSite.test.js
|
||||
@@ -2251,12 +2251,12 @@ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>"
|
||||
- Consumes: alles aus Task 1 bis 11
|
||||
- Produces: ausgelieferte Daten mit Flamingo Park und Jefferson Avenue
|
||||
|
||||
- [ ] **Step 1: Pipeline laufen lassen**
|
||||
- [x] **Step 1: Pipeline laufen lassen**
|
||||
|
||||
Run: `cd tools/extract && PYTHONUTF8=1 uv run python run.py`
|
||||
Expected: wie in Task 1, dazu die Zeile `Flamingo Park: … Wegflächen, … Flächen, 11 Gebäude, Jefferson Avenue 12 m breit` und genau eine Warnzeile: `Warnung: Zufahrt schneidet die Sperrfläche tribuene`. Das ist der bekannte, gewollte Zustand — die Zufahrt zum Parkplatz quert die Sperrfläche der Tribüne und wird dort ausgeschnitten. Jede weitere oder andere Warnung ist ein Befund. Bricht der Lauf mit `Spielecke … liegt weiter als 2 m` ab: anhalten und dem Nutzer melden.
|
||||
|
||||
- [ ] **Step 2: Änderungen auf Park und Jefferson Avenue begrenzt?**
|
||||
- [x] **Step 2: Änderungen auf Park und Jefferson Avenue begrenzt?**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
@@ -2300,7 +2300,7 @@ EOF
|
||||
```
|
||||
Expected: Ausgaben nur für J6–K9 (`chunk_9_5` … `chunk_10_8`) und am Ende `nur erwartete Chunks`. Sonst anhalten und dem Nutzer die Liste zeigen.
|
||||
|
||||
- [ ] **Step 3: Tribüne ohne `replaces`**
|
||||
- [x] **Step 3: Tribüne ohne `replaces`**
|
||||
|
||||
Mit den neuen Daten ist der alte Gebäudekasten der Tribüne weg, weil er zu mehr als der Hälfte im Park lag. `npx vitest run tests/placesSite.test.js` scheitert deshalb jetzt an „ersetzt mit jedem replaces-Punkt genau ein Gebäude unter sich“. In `src/world/places.js` im Eintrag `tribuene` die zwei Zeilen
|
||||
```js
|
||||
@@ -2313,7 +2313,7 @@ ersetzen durch:
|
||||
// OpenStreetMap schon die Pipeline (docs/superpowers/specs/2026-09-15-flamingo-park-design.md).
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Alle Tests**
|
||||
- [x] **Step 4: Alle Tests**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
@@ -2322,7 +2322,7 @@ cd ../.. && npm test
|
||||
```
|
||||
Expected: beide vollständig grün, auch `test_committed_map_files_match_the_committed_chunks`, `tests/placesSite.test.js` (der replaces-Test ist für beide Orte übersprungen) und `tests/parkData.test.js`.
|
||||
|
||||
- [ ] **Step 5: Kartenausschnitt ansehen**
|
||||
- [x] **Step 5: Kartenausschnitt ansehen**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
@@ -2335,7 +2335,7 @@ img.crop((x0 + 1000, 2500 - y1, x1 + 1000, 2500 - y0)).resize(((x1 - x0) * 2, (y
|
||||
```
|
||||
Das Bild `tools/extract/debug/flamingo-park.png` mit dem Read-Werkzeug ansehen und prüfen: Wege glatt, Kreis mit 13th-St-Weg, Felder oben, Tennisanlage, Pool und Parkplatz unten, Jefferson Avenue gerade nach Süden, keine Gasse mehr, Tribüne- und Hood-Yard-Fläche frei. Dem Nutzer das Bild mit `SendUserFile` schicken.
|
||||
|
||||
- [ ] **Step 6: Im Browser prüfen**
|
||||
- [x] **Step 6: Im Browser prüfen**
|
||||
|
||||
Vorgehen nach `.claude/skills/placing-editor-models/browser-checks.md`, Abschnitte 2, 3, 6, 7 (nur das Setzen der Kapsel) und 8. Der Server ist der Eintrag `leonida` aus `.claude/launch.json` (Hauptordner, kein Arbeitsbaum). Die Debug-Seite `index-debug.html` nur lokal anlegen und am Ende löschen.
|
||||
|
||||
@@ -2349,15 +2349,15 @@ Dazu festhalten:
|
||||
- Die Konsole zeigt keine Fehler; Pointer-Lock-Meldungen des verborgenen Bereichs sind ausgenommen.
|
||||
- Im Blick 1: Draw Calls (`renderer.info.render.calls` direkt nach `renderer.render`) und Bildzeit nach Abschnitt 8 als Median aus 60 Bildern.
|
||||
|
||||
- [ ] **Step 7: Farben und Textur abstimmen, falls nötig**
|
||||
- [x] **Step 7: Farben und Textur abstimmen, falls nötig**
|
||||
|
||||
Wirkt etwas falsch (Felder kaum vom Rasen zu unterscheiden, Wege zu dunkel, Kachelmuster zu grob), mit dem Nutzer entscheiden und dann nur diese Werte ändern: `FIELD_TINT` und `SURFACE_COLORS` in `src/world/materials.js`, `uvScale` der Wege in `src/world/geometry.js`. Vorher-Nachher-Bild desselben Blicks schicken. Danach `npm test`.
|
||||
|
||||
- [ ] **Step 8: Messwerte in die Spec**
|
||||
- [x] **Step 8: Messwerte in die Spec**
|
||||
|
||||
In `docs/superpowers/specs/2026-09-15-flamingo-park-design.md` im Abschnitt „Fehlerfälle und Risiken“ einen Punkt **Gemessen im Spiel** mit dem Datum des Laufs ergänzen: Zahl der Wegflächen, Flächen und Gebäude aus Step 1, geänderte Chunks aus Step 2, Draw Calls und Bildzeit aus Step 6 und jede Anpassung aus Step 7.
|
||||
|
||||
- [ ] **Step 9: Commit**
|
||||
- [x] **Step 9: Commit**
|
||||
|
||||
```bash
|
||||
git add public/data/vice-a src/world/places.js docs/superpowers/specs/2026-09-15-flamingo-park-design.md src/world/materials.js src/world/geometry.js
|
||||
|
||||
Reference in new issue
Block a user