Deploy App / deploy (push) Successful in 28s
Haus - Oeffnungen aus den Fassadenplaenen von EHLE abgegriffen. Zum Garten steht im Erdgeschoss eine einzige, mittige Fensterfront (x -8.82 bis -4.67), daneben das Lamellenband vor dem gedeckten Sitzplatz; im Obergeschoss zwei Fenster mit zwei schmalen Lamellen dazwischen. - Der gedeckte Sitzplatz ist nur noch gegen Westen offen, so wie es die Suedwestfassade als "Aussensitzplatz" zeigt. Gegen den Garten steht dort eine geschlossene Wand - bisher schaute man durch die Fassade hinein. - Hauszugang an der Nordostfassade statt an der Nordwestfassade: Tuerfluegel und feststehende Verglasung, zusammen 3.15 m, darueber das Vordach. Das Vordach kennt jetzt seine Fassadenseite. - Die Fassadennamen im Datenmodell waren vertauscht; jetzt mit Tabelle im README festgehalten. Garten - Sichtschutz: die gruenen Punkte im Kiesbett sind Ziergraeser, keine Polster. Neue Halmgeometrie - schmales, sich verjuengendes Band, das nach oben ueberhaengt, rund 1.10 m. Die Bodenleuchten sind jetzt Strahler und leuchten Stelen und Graeser von unten an. - Baenkli: die Fuesse standen quer zur Sitzplatte, weil der Versatz in Welt-Z statt entlang der gedrehten Achse gerechnet wurde. - Obere Ebene bis an das Kraeuterbeet eben auf -0.02. Zwischen Grillfeld und Kraeuterbeet stieg der Rasen um gut 30 cm an. - Unter dem Spielhaus steht der Betonklotz aus dem Plan (graues Rechteck 1.01 x 2.70 m); die offene Plattform daneben bleibt auf Stuetzen. - Aus dem Gartenhaus wird ein Gartenschrank: 1.80 x 0.50 m, 2.00 m hoch, mit dem Ruecken in der Ecke und den Doppeltueren zum Garten. - Neben dem Gelaender der Osttreppe lag Rasen; dort liegt jetzt derselbe Belag wie auf dem Weg, bis an den Treppenkopf. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
332 lines
14 KiB
Markdown
332 lines
14 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.
|
||
- **Treppen mit Rampe, Geländer und Rinnen** – die linearen Bauteile aus
|
||
`LINEAR` in `tools/authoring.mjs`, gerendert in `src/scene/structures.ts`:
|
||
Blockstufen aus Travertin, Stahlrohrgeländer mit Hand- und Knieholm entlang
|
||
des Gefälles, Entwässerungsrinnen mit bündigem Rost.
|
||
- **Gartentor** – Stabgitterflügel von 100 cm mit zwei Pfosten und senkrechten
|
||
Stäben, nicht als geschlossenes Blatt.
|
||
- **Spielhaus** – ein Stelzenhaus. Der Plan zeichnet zwei Rechtecke: das Podest
|
||
1.81 × 2.70 m mit der Kote 1.47 (rund 0.9 m über dem Gelände) und darauf an
|
||
der Ostseite die Hütte 1.01 × 2.70 m. Der westliche Teil bleibt offene
|
||
Plattform mit Brüstung und Leiter.
|
||
|
||
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.
|
||
|
||
Die **Rasenrandplatten** sind im Plan ein durchgehendes Band von 6 cm Breite
|
||
zwischen Rasen und Kiesbett des Sichtschutzes. Sie werden nicht als Belag,
|
||
sondern als hochkant stehende Randplatte mit sichtbarer Kante aufgebaut
|
||
(Belagstyp `randplatte`).
|
||
|
||
Am **Gartentor** liegen ebenfalls zwei schwebende Travertinstufen: der Plan
|
||
zeichnet dort ein Feld von 0.80 × 1.60 m mit den Koten −0.13 (Podest am Tor)
|
||
und −0.32 (Anschluss an den Hauptsitzplatz).
|
||
|
||
Der Plan zeigt **zwei** orange markierte Felder von 1.50 × 0.46 m in der
|
||
Sichtschutzlinie; die Beschriftung „Holzscheiterwand 150 cm x 200 cm" steht
|
||
zwischen ihnen und gilt für beide.
|
||
|
||
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.
|
||
|
||
### Ebene Ebenen, geneigte Wege
|
||
|
||
`LEVELS` in `tools/authoring.mjs` gibt einzelnen Bereichen ihre Höhe fest vor,
|
||
statt sie aus streuenden Koten zu schätzen. Zwei Formen:
|
||
|
||
- **`level` + `flatten`** – waagrecht und zwingend. Die obere Ebene mit Rasen
|
||
und Kiesfeld liegt eben auf −0.02. Ohne diese Vorgabe zieht die Interpolation
|
||
den Rasen hügelig hoch, weil die Koten 0.09 bis 0.26 schon im angehobenen
|
||
Kiesbett des Sichtschutzes liegen und nicht mehr im Rasen.
|
||
- **`plane`** – eine geneigte Ebene, für die Wege. Sie sind Rampen, keine
|
||
waagrechten Felder: Ostweg rund 12 %, Weg an der Südgrenze rund 3 %,
|
||
Feuerwehrzufahrt rund 13.6 % (Plankoten −2.08 und −1.08). Der
|
||
Architekturplan (`Erdgeschoss mit Umgebung 1:100`) beschriftet dieselben
|
||
Gefälle mit „Steigung 9.9 %“ und „Steigung 13.0 %“.
|
||
|
||
Ausserhalb aller Zonen wird die Interpolation der nächstgelegenen Zone
|
||
fortgesetzt, nicht ihr konstanter Ersatzwert – sonst steht der Belag als
|
||
schwebende Platte über einer Geländekante.
|
||
|
||
### Das Haus nach den Fassadenplänen
|
||
|
||
Die Öffnungen sind aus den Fassadenplänen von EHLE Architekten (1:100)
|
||
abgegriffen. Die Pläne benennen die Fassaden nach Himmelsrichtung, das
|
||
Datenmodell nach Achsrichtung:
|
||
|
||
| Plan | Site | Lage |
|
||
| --- | --- | --- |
|
||
| Südostfassade | `south`, z +0.96 | lang, zum Garten |
|
||
| Nordostfassade | `east`, x +0.28 | kurz, Hauszugang |
|
||
| Südwestfassade | `west`, x −13.82 | kurz, zur Terrasse |
|
||
| Nordwestfassade | `north`, z −6.87 | lang, zum Nachbarn – im Plansatz nicht gezeichnet, bleibt geschlossen |
|
||
|
||
Zum Garten steht im Erdgeschoss **eine** mittige Fensterfront, daneben das
|
||
Lamellenband vor dem gedeckten Sitzplatz; im Obergeschoss zwei Fenster mit zwei
|
||
schmalen Lamellen dazwischen. Der gedeckte Sitzplatz ist **nur gegen Westen
|
||
offen** („Aussensitzplatz“ in der Südwestfassade) – gegen den Garten steht dort
|
||
eine geschlossene Wand. Ohne sie schaut man von aussen durch die Fassade in den
|
||
Sitzplatz hinein.
|
||
|
||
### Treppen begehen und springen
|
||
|
||
Stufen und Treppen stecken nicht im Höhenraster, sondern sind eigene Körper.
|
||
Damit man sie begehen kann, rechnet `main.ts` ihre Trittflächen in die
|
||
Höhenabfrage des Rundgangs ein (`treppeTritte` in `src/scene/structures.ts`);
|
||
die schwebenden Stufen sind entsprechend **kein** Hindernis mehr. Dazu kommt
|
||
der Sprung mit der Leertaste – rund 55 cm, genug für jede Mauerkrone.
|
||
|
||
### 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.
|