Deploy App / deploy (push) Successful in 28s
Nach erneutem Detailstudium des Gartenplans und der Referenzbilder darin: - Sichtschutz ist nicht durchgehend: 28 aufrechte Travertinstelen (50x6 cm, Dreiergruppen, Positionen direkt aus dem Plan) wechseln sich mit Eibengruppen ab, alle in einem Dekorkiesbett. Hoehe 1.80 m gegen den Suednachbarn, 1.50 m gegen Zollet auf der unteren Ebene. - Zwei schwebende Travertinstufen fuehren vom gedeckten Sitzplatz (-0.02) auf den Hauptsitzplatz hinunter, als auskragende Platten mit Schattenfuge. - Hoehenprofil auf zwei klare Ebenen gebracht: untere Ebene -0.33, Travertinsitzmauer mit 31 cm Ansicht, obere Ebene -0.02. Die Niveaus stehen fest in LEVELS statt aus streuenden Koten geschaetzt zu werden. - Jede Hoehenkote gehoert neu zu genau einer Flaeche, der kleinsten, die sie enthaelt - sonst kippen benachbarte Ebenen ineinander. - Der schmale Streifen am Sichtschutz ist Kiesbett, nicht Plattenbelag. - Travertin nach den Referenzbildern deutlich heller, eigene Steintextur fuer die Stelen; Flammengrill als Stahlkubus mit Holzfach und Feueroeffnung. - Stirnflaechen der Hecken hatten entartete UVs und zeigten Strahlenmuster. - tools/plan-bilder.mjs zieht die Referenzfotos aus dem Plan-PDF. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
262 lines
11 KiB
Markdown
262 lines
11 KiB
Markdown
# Gartenvisualisierung Egglihaldenstrasse 9a
|
||
|
||
Begehbare 3D-Visualisierung des geplanten Gartens von Jasmin und Damian
|
||
Kellenberger, Egglihaldenstrasse 9a, 9030 Abtwil SG.
|
||
|
||
Live: **https://garten.aicds.ch**
|
||
|
||
Die gesamte Geometrie ist **massstäblich aus dem Gartenplan** abgeleitet – nicht
|
||
nachmodelliert. Ein Node-Werkzeug liest die Vektordaten direkt aus dem Plan-PDF,
|
||
rechnet sie in Meter um und erzeugt daraus das Szenenmodell.
|
||
|
||
---
|
||
|
||
## Grundlagen
|
||
|
||
| Quelle | Inhalt |
|
||
|---|---|
|
||
| `Info Dateien/Gartenplan 1 …pdf` | Garten Design AG, 14.08.2026, Massstab 1:100, A1 |
|
||
| `Info Dateien/20210427 - Projektpläne Haus 9a.pdf` | EHLE Architekten AG, Grundrisse, Fassaden, Umgebung |
|
||
| `Info Dateien/WhatsApp Image …jpeg` | Fotos des Ist-Zustands (Fassade, Beläge, Nachbarschaft) |
|
||
|
||
Die DWG wird nicht benötigt – das PDF enthält dieselbe Vektorgeometrie.
|
||
|
||
### Koordinatensystem
|
||
|
||
Alles rechnet im **Site-Frame**, in Metern:
|
||
|
||
- **X** – Hausachse ENE, Peilung 61.1615° gegen Nord
|
||
- **Z** – senkrecht dazu, SSE, Peilung 151.1615°
|
||
- **Y** – Höhe über dem Fixpunkt `0.00` = **651.17 m ü. M.** (OK Fertigboden EG)
|
||
|
||
Der Nordpfeil des Plans zeigt exakt zur Blattoberkante, das Zeichnungsraster
|
||
liegt um −28.8385° gedreht dazu. Der Massstab ist über das Plattenraster
|
||
verifiziert: die Keramikplatten messen sich auf 0.600 m genau.
|
||
|
||
### Höhen
|
||
|
||
| Bezug | relativ | absolut |
|
||
|---|---|---|
|
||
| Feuerwehrzufahrt, Nordwestecke | −2.08 | 648.09 m |
|
||
| Nachbargarten Zollet (Haus 9, OK FB EG) | −1.70 | 649.47 m |
|
||
| Hauptsitzplatz Travertin (untere Ebene) | −0.33 | 650.84 m |
|
||
| Rasen und Kiesfeld (obere Ebene) | −0.02 | 651.15 m |
|
||
| Fixpunkt Hauseingang | 0.00 | 651.17 m |
|
||
| Ostseite beim Apfelbaum | +0.63 | 651.80 m |
|
||
| OK Fertigboden Obergeschoss | +2.90 | 654.07 m |
|
||
| OK Attika Haus 9a | +6.15 | 657.32 m |
|
||
|
||
---
|
||
|
||
## Datenpipeline
|
||
|
||
```bash
|
||
npm run data # extract-plan + build-site
|
||
```
|
||
|
||
1. **`tools/extract-plan.mjs`** – öffnet das Plan-PDF mit MuPDF (WASM) und
|
||
protokolliert alle Zeichenoperationen mit. Ergebnis: rund 5400 Polygone,
|
||
234 Pflanzenkreise, 229 Beschriftungen und 46 Höhenkoten, transformiert in
|
||
den Site-Frame. → `tools/out/plan-raw.json`
|
||
|
||
2. **`tools/authoring.mjs`** – handgepflegte Ergänzungen: Zuordnung der
|
||
Planfarben zu Belagsarten, Mauerachsen und -höhen, Gebäudekubatur,
|
||
Geländezonen, Standorte. Alles kommentiert und auf Plan oder
|
||
Architekturpläne zurückgeführt.
|
||
|
||
3. **`tools/build-site.mjs`** – klassiert die Flächen, leitet Pflanzen und
|
||
Leuchten ab, interpoliert das Geländeraster (0.25 m) aus den Höhenkoten je
|
||
Zone und schreibt `public/data/site.json` (rund 110 kB).
|
||
|
||
Zur Kontrolle: `node tools/debug-svg.mjs` zeichnet die extrahierte Geometrie als
|
||
SVG-Draufsicht, `node tools/inspect-colors.mjs` listet die Planfarben mit den
|
||
nächstgelegenen Beschriftungen.
|
||
|
||
### Die beiden Ebenen
|
||
|
||
Der Garten liegt auf zwei Niveaus, und der Sprung dazwischen ist ein Bauteil:
|
||
|
||
| | |
|
||
|---|---|
|
||
| Untere Ebene – Hauptsitzplatz Travertin | **−0.33 m** |
|
||
| Travertinsitzmauer, Ansichtshöhe | **0.31 m** |
|
||
| Obere Ebene – Rasen, Kiesfeld, Flammengrill | **−0.02 m** |
|
||
| Gedeckter Sitzplatz | −0.02 m |
|
||
| Zwei schwebende Travertinstufen dazwischen | −0.17 / −0.31 m |
|
||
|
||
Gegen Osten steigt der Garten laut Plan weiter an (0.26 bis 0.63 m); dort bleibt
|
||
die aus den Koten ausgeglichene Ebene stehen. Die beiden Hauptniveaus stehen als
|
||
`LEVELS` in `tools/authoring.mjs` und werden nicht geschätzt – Flächen, deren
|
||
Schwerpunkt darin liegt, bekommen exakt dieses Niveau.
|
||
|
||
### Der Sichtschutz
|
||
|
||
Er ist bewusst **nicht durchgehend**. Der Plan zeichnet ihn als Wechsel von
|
||
aufrechten **Travertinstelen** (50 × 6 cm, in Dreiergruppen) und
|
||
**Eibengruppen**, alle zusammen in einem **Dekorkiesbett**. Die 28 Stelen kommen
|
||
mit Position und Ausrichtung direkt aus dem Plan; die Eiben werden nur innerhalb
|
||
einer Gruppe zu einem Heckenkörper verkettet, damit die Lücken für die Stelen
|
||
offen bleiben.
|
||
|
||
Höhen nach Abschirmrichtung:
|
||
|
||
- gegen den Südnachbarn, entlang der langen Fläche: **1.80 m**
|
||
- gegen Zollet und die Feuerwehrzufahrt, untere Ebene: **1.50 m**
|
||
|
||
### Ebene Beläge statt gewelltem Gelände
|
||
|
||
Gebaute Flächen sind eben. Wo kein festes Niveau vorgegeben ist, wird aus den
|
||
Höhenkoten eine Ausgleichsebene bestimmt – Gefälle auf 4 % begrenzt, am
|
||
Schwerpunkt der Koten aufgehängt. Jede Kote gehört dabei zu genau **einer**
|
||
Fläche, nämlich der kleinsten, die sie enthält; sonst zieht etwa die Kote einer
|
||
Stufe die darunterliegende Terrasse mit hoch und aus zwei sauberen Ebenen wird
|
||
eine schiefe.
|
||
|
||
Das Geländeraster wird in diesen Feldern auf die Ebene gezwungen und dort
|
||
**nicht** geglättet. Im Rendering bekommt jeder Belag eine senkrechte Sichtkante
|
||
zum angrenzenden Gelände – erst dadurch wirkt eine Terrasse gebaut und nicht
|
||
aufgemalt. Wachsende Flächen (Rasen, Schotterwiese, Beet) folgen dem Gelände.
|
||
|
||
### Bauteile statt hochgezogener Flächen
|
||
|
||
Der Plan nennt vier verschiedene Konstruktionen, und sie sehen auch verschieden
|
||
aus (`src/scene/walls.ts`):
|
||
|
||
- **Travertinmauer / Sitzmauern** – Schichtmauerwerk aus einzelnen Blöcken mit
|
||
versetzten Stossfugen und überstehender Abdeckplatte.
|
||
- **Winkelelemente aus Beton** – Fertigteile von 1 m Breite, einzeln versetzt,
|
||
mit Stossfugen, nach unten dicker werdendem Stiel und Fussplatte.
|
||
- **Betonmauer Nachbar** – Ortbeton in Schaltafelfeldern mit abgefaster Krone.
|
||
- **Lamellenwand** – senkrechte Bretter im Rhythmus des Plans (10 cm breit,
|
||
35 cm tief, 90 cm Achsabstand) vor dem gedeckten Sitzplatz.
|
||
- **Holzscheiterwand** – Stahlrahmen, gefüllt mit gestapelten Scheitern.
|
||
|
||
Die **Eibengruppen** entstehen nicht aus Einzelkugeln: die Pflanzstandorte des
|
||
Plans werden gruppenweise verkettet, begradigt, an scharfen Knicken getrennt und
|
||
als geschnittener Heckenkörper mit gerundetem Querschnitt aufgezogen – siehe
|
||
Abschnitt „Der Sichtschutz". Ebenso die Ligusterhecke (170 cm, 75 cm breit).
|
||
|
||
Die **Travertinplatten 40 × 60 × 3 cm** liegen laut Plan einzeln im Dekorkies –
|
||
30 Platten, exakt an den Planpositionen, als erhabene Platten auf dem Kiesbett.
|
||
|
||
Materialien und Ausbildung folgen den **Referenzbildern im Plan-PDF**: heller,
|
||
geschichteter Travertin für Mauern, Stelen und Platten, die schwebenden Stufen
|
||
als auskragende Platten mit Schattenfuge, der Flammengrill als anthrazitfarbener
|
||
Stahlkubus mit Holzfach und runder Feueröffnung. Die Bilder lassen sich mit
|
||
`node tools/plan-bilder.mjs` aus dem PDF ziehen.
|
||
|
||
### Warum Zonen statt eines einzigen Geländes
|
||
|
||
Das Grundstück fällt über rund 3.5 m ab, die Sprünge liegen aber nicht im
|
||
Gelände, sondern in den Stützbauten: Travertinmauer zum Nachbargrundstück,
|
||
Winkelelemente aus Beton entlang der Feuerwehrzufahrt, Betonmauer im Süden.
|
||
Deshalb wird je Zone getrennt interpoliert – über die Zonengrenze hinweg nie.
|
||
Die Mauern erhalten ihr Kronen- und Fussprofil aus dem Gelände beider Seiten.
|
||
|
||
---
|
||
|
||
## Anwendung
|
||
|
||
```bash
|
||
npm install
|
||
npm run dev # http://localhost:5173
|
||
npm run build # dist/, rund 640 kB
|
||
```
|
||
|
||
Stack: Vite + TypeScript + three.js. **Keine externen Assets** – sämtliche
|
||
Texturen (Travertin, Keramik, Tegula, Rasen, Kies, Beton, Holzschalung,
|
||
Scheiterwand) werden zur Laufzeit prozedural auf ein Canvas gezeichnet. Die UVs
|
||
sind weltbezogen, damit die Plattenraster über Flächengrenzen hinweg durchlaufen
|
||
– so wie auf dem Plan.
|
||
|
||
### Bedienung
|
||
|
||
| | |
|
||
|---|---|
|
||
| Rundgang | Klicken, dann `W` `A` `S` `D`, `Shift` zum Rennen, `Esc` gibt die Maus frei |
|
||
| Handy | links Joystick, rechts wischen zum Umsehen |
|
||
| Übersicht | ziehen zum Drehen, Mausrad zum Zoomen |
|
||
| Tageszeit | Regler oben rechts, Sonnenstand astronomisch für Abtwil SG gerechnet |
|
||
|
||
Der Rundgang folgt dem Gelände, steigt Stufen bis 45 cm und wird von Mauern,
|
||
Gebäuden, Hecken und Objekten aufgehalten. Ab Dämmerung schalten die 17 Leuchten
|
||
des Plans automatisch ein.
|
||
|
||
### Aufbau
|
||
|
||
```
|
||
src/
|
||
site.ts Datenmodell und Höhen-Sampler
|
||
scene/textures.ts prozedurale Beläge und Materialien
|
||
scene/geometry.ts Polygone auf das Gelände legen, Mauern extrudieren
|
||
scene/terrain.ts Geländeraster und Umland
|
||
scene/surfaces.ts Beläge
|
||
scene/walls.ts Mauern als Bauteile: Schichtmauerwerk, Winkelelemente, Lamellen
|
||
scene/structures.ts Stelen, Objekte, Leuchten
|
||
scene/vegetation.ts Gehölze, Hecken, Gräser, Stauden
|
||
scene/building.ts Wohnhaus 9a und Nachbargebäude
|
||
scene/environment.ts Himmel, Sonnenstand, Licht
|
||
controls/walker.ts Begehen mit Kollision und Geländeführung
|
||
ui/hud.ts Bedienoberfläche
|
||
```
|
||
|
||
---
|
||
|
||
## Deployment
|
||
|
||
Push auf `main` löst den Gitea-Workflow aus; die App landet unter
|
||
`/opt/aicds/apps/Gartenvisualisierung/` und wird per `docker compose` gebaut.
|
||
Traefik-Router `garten`, Host `garten.aicds.ch`, Netzwerk `agent-net`.
|
||
|
||
```bash
|
||
docker compose config # Prüfung vor dem Push
|
||
docker compose up -d --build
|
||
```
|
||
|
||
Das Image enthält nur `dist/` hinter nginx – die Planunterlagen und Fotos aus
|
||
`Info Dateien/` werden über `.dockerignore` ausgeschlossen und gelangen nie in
|
||
den Webcontainer.
|
||
|
||
---
|
||
|
||
## Was bewusst interpretiert ist
|
||
|
||
Der Plan ist ein Situationsplan; einiges steht nur als Beschriftung darin. Diese
|
||
Annahmen stecken in `tools/authoring.mjs` und sind dort einzeln kommentiert:
|
||
|
||
- **Mauerhöhen** aus den Höhenkoten beidseits abgeleitet; die gelbe Kote `25`
|
||
bei der Sitzmauer als 25 cm Aufkantung gelesen, `35` bei den Heidelbeeren
|
||
analog.
|
||
- **Lamellenwand zum Sitzen** als senkrechte Holzlamellen im Planrhythmus
|
||
(10 cm breit, 35 cm tief, rund 90 cm Achsabstand), passend zum Foto des
|
||
Ist-Zustands.
|
||
- **Wohnhaus** in Kubatur und Öffnungen nach Gartenplan und Fassadenplänen,
|
||
Materialisierung nach den Fotos: vertikale Holzschalung graubraun, heller
|
||
Betonsockel, Flachdach mit extensiver Begrünung.
|
||
- **Nachbarhaus Zollet** als einfaches Kontextvolumen, 1.70 m tiefer liegend.
|
||
- **Solitärgehölze** mit den Kronenradien des Plans; Wuchshöhen als
|
||
Erfahrungswerte.
|
||
- **Gartenhaus, Spielhaus, Flammengrill, Bänkli** in Lage und Grundfläche nach
|
||
Plan, in der Ausformulierung frei.
|
||
|
||
---
|
||
|
||
## Stolperstein beim Deployment
|
||
|
||
Der erste Rollout lieferte trotz laufendem Container und korrekten Traefik-Labels
|
||
ein 404 vom Proxy. Ursache war der HEALTHCHECK im Dockerfile: busybox-`wget`
|
||
löst `localhost` zuerst nach `::1` auf, nginx lauschte aber nur auf IPv4 – der
|
||
Check schlug dauerhaft fehl und der Container blieb auf `health: starting`.
|
||
|
||
**Traefik nimmt Container, die nicht `healthy` sind, gar nicht in die
|
||
Routing-Tabelle auf.** Deshalb entstand kein Router, kein Zertifikat und damit
|
||
ein 404 mit dem Traefik-Default-Zertifikat – ein Fehlerbild, das leicht nach
|
||
einem Proxy- oder DNS-Problem aussieht.
|
||
|
||
Behoben durch `http://127.0.0.1/` im Healthcheck und ein zusätzliches
|
||
`listen [::]:80;` in der nginx-Konfiguration. Der Deploy-Workflow wartet
|
||
seither auf `healthy` und prüft den Router, bevor er den Lauf abschliesst.
|
||
|
||
Merksatz für weitere AICDS-Apps: ein Container mit fehlschlagendem Healthcheck
|
||
wird von Traefik stillschweigend ignoriert.
|