Deploy App / deploy (push) Successful in 49s
Nach der Plankontrolle ueberarbeitet: - Belaege sind eben. Je Belag wird aus den Hoehenkoten eine Ebene bestimmt (Gefaelle <= 5 %, am Kotenschwerpunkt aufgehaengt, nur Koten derselben Gelaendezone). Das Raster wird darauf gezwungen und dort nicht geglaettet. Jeder Belag bekommt eine senkrechte Sichtkante zum Gelaende. - Mauern sind Bauteile: Travertin-Schichtmauerwerk mit versetzten Stossfugen und Abdeckplatte, einzelne Winkelelemente mit Fussplatte, Ortbeton in Schaltafelfeldern. - Sichtschutz: die Eibenreihen des Plans werden zu geschnittenen Hecken verkettet, ebenso die Ligusterhecke. Lamellenwand im Planrhythmus. - Der Plan zeichnet die Travertinplatten 40x60 einzeln im Dekorkies - jetzt 30 erhabene Platten an den Planpositionen statt geschlossenem Belag. - Normalmaps aus dem Fugenbild fuer Platten, Pflaster, Holzschalung und Laub. - Nachbarhaus Zollet richtig platziert (stand auf dem Nachbargarten) und wie Haus 9a ausgebildet. Behobene Fehler: boxGeometry erzeugte Seiten und Deckel mit umgekehrtem Wickelsinn - saemtliche Boxen waren innen nach aussen gekehrt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
200 lines
8.1 KiB
Markdown
200 lines
8.1 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 | −0.30 … −0.02 | 650.87 … 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.
|
||
|
||
### Ebene Beläge statt gewelltem Gelände
|
||
|
||
Gebaute Flächen sind eben. Für jeden Belag wird aus den Höhenkoten darin und
|
||
knapp daneben eine Ebene bestimmt – Gefälle auf 5 % begrenzt, am Schwerpunkt der
|
||
Koten aufgehängt, und nur Koten derselben Geländezone. 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 weiterhin 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 **Eibenhecke** entsteht nicht aus Einzelkugeln: die Pflanzstandorte des Plans
|
||
werden zu Reihen verkettet, begradigt, an scharfen Knicken getrennt und als
|
||
geschnittener Heckenkörper mit gerundetem Querschnitt aufgezogen. Ebenso die
|
||
Ligusterhecke (170 cm hoch, 75 cm breit). Das ist der eigentliche Sichtschutz
|
||
im Garten.
|
||
|
||
Die **Travertinplatten 40 × 60 × 3 cm** liegen laut Plan einzeln im Dekorkies –
|
||
30 Platten, exakt an den Planpositionen, als erhabene Platten auf dem Kiesbett.
|
||
|
||
### 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.
|