Westpodest, Osttreppe, Gartenschrank, Start in der Uebersicht
Deploy App / deploy (push) Successful in 27s

- Weg um das Haus: der westliche Arm fehlte. Vor dem Gartentor liegt
  jetzt ein Podest auf -0.14 (Plan: dreimal -0.14 bis zur Suedwestecke),
  das an der Ecke in den Weg entlang der Suedgrenze uebergeht. Gegen die
  tiefer liegende Feuerwehrzufahrt schliesst ein Betonabschluss ab.
- Westtreppe gedreht: die Trittlinien liegen im Plan parallel zur Rinne,
  die Treppe laeuft also laengs der Grenze nach Norden, vom Podest auf die
  Feuerwehrzufahrt (-0.14 auf -1.08). Die Zufahrt ist dort ausgespart.
- Osttreppe war unter Rasen verschwunden. Unter Treppen wird das Gelaende
  jetzt unter die unterste Trittkante gelegt; die Treppe reicht vom
  Gelaender bis an die Winkelelemente. Der Belagstreifen, der sie
  zugedeckt hatte, ist weg.
- Belaege und feste Ebenen legen das Gelaende auch ausserhalb der Zonen
  fest. Vorher stand am Fuss der Osttreppe ein Grashuegel ueber dem
  Podest. Podest und Stellplatte liegen gemeinsam auf -1.57.
- Gartenschrank um 90 Grad gedreht: Rueckwand zum Beerenbeet, Doppeltueren
  zum Hauszugang. Dunkler gebeizt, damit er nicht mit der Fassade
  verschmilzt.
- Schwebender Travertinblock an der Nordwestecke entfernt.
- Die App oeffnet im Uebersichtsmodus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 09:21:46 +02:00
co-authored by Claude Opus 5
parent fefa8e8c9a
commit 866bedf64d
5 changed files with 84 additions and 37 deletions
File diff suppressed because one or more lines are too long
+2
View File
@@ -234,6 +234,8 @@ async function main() {
onJoystick: (x, y, active) => walker.setJoystick(x, y, active), onJoystick: (x, y, active) => walker.setJoystick(x, y, active),
}); });
walker.onPointerLockChange = (locked) => hud.setPointerLocked(locked); walker.onPointerLockChange = (locked) => hud.setPointerLocked(locked);
// Die App oeffnet in der Uebersicht; der Rundgang ist einen Klick entfernt.
hud.setMode('overview');
// -- Renderschleife ------------------------------------------------------ // -- Renderschleife ------------------------------------------------------
function resize() { function resize() {
+2 -1
View File
@@ -88,7 +88,8 @@ function schwebendeStufe(p: Prop, y: number): Group {
*/ */
function gartenhaus(p: Prop, y: number): Group { function gartenhaus(p: Prop, y: number): Group {
const g = new Group(); const g = new Group();
const holz = material(tex.fassadeHolz(), { roughness: 0.8 }); // Dunkler gebeizt als die Fassade, damit der Schrank nicht mit dem Haus verschmilzt.
const holz = material(tex.fassadeHolz(), { color: 0x857868, roughness: 0.8 });
const dunkel = new MeshStandardMaterial({ color: 0x3b3934, roughness: 0.62 }); const dunkel = new MeshStandardMaterial({ color: 0x3b3934, roughness: 0.62 });
const metall = new MeshStandardMaterial({ color: 0x55565a, roughness: 0.45, metalness: 0.6 }); const metall = new MeshStandardMaterial({ color: 0x55565a, roughness: 0.45, metalness: 0.6 });
const a = (p.rot * Math.PI) / 180; const a = (p.rot * Math.PI) / 180;
+38 -21
View File
@@ -84,6 +84,15 @@ export const TREES = [
* bleibt die Ausgleichsebene aus den Koten stehen. * bleibt die Ausgleichsebene aus den Koten stehen.
*/ */
export const LEVELS = [ export const LEVELS = [
{
// Vor dem Gartentor liegt ein Podest auf -0.14 (Plan: dreimal -0.14 bis
// zur Suedwestecke). Es ist der westliche Arm des Wegs um das Haus und
// geht an der Ecke in den Weg entlang der Suedgrenze ueber. Erst von hier
// fuehrt die Treppe hinunter auf die Feuerwehrzufahrt.
id: 'weg-west', level: -0.14, types: ['tegula'],
note: 'Podest vor dem Gartentor',
polygon: [[-23.40, 2.15], [-21.70, 2.15], [-21.70, 6.05], [-23.40, 6.05]],
},
{ {
// Die Feuerwehrzufahrt ist eine Rampe: der Plan gibt -2.08 bei z -7.21 // Die Feuerwehrzufahrt ist eine Rampe: der Plan gibt -2.08 bei z -7.21
// und -1.08 bei z 0.12 an, also rund 13.6 % Steigung. Ohne feste Vorgabe // und -1.08 bei z 0.12 an, also rund 13.6 % Steigung. Ohne feste Vorgabe
@@ -101,19 +110,19 @@ export const LEVELS = [
polygon: [[2.05, -4.30], [4.55, -4.30], [4.55, 5.05], [2.05, 5.05]], polygon: [[2.05, -4.30], [4.55, -4.30], [4.55, 5.05], [2.05, 5.05]],
}, },
{ {
id: 'weg-ost-gelaender', level: -0.45, types: ['tegula'], id: 'stellplatte-nordost', level: -1.57, types: ['tegula'],
note: 'Belagstreifen neben dem Gelaender der Osttreppe', note: 'Stellplatte des Nachbarn auf der Hoehe des Treppenfusses',
polygon: [[2.05, -6.60], [2.92, -6.60], [2.92, -4.15], [2.05, -4.15]], polygon: [[0.30, -11.30], [4.70, -11.30], [4.70, -7.25], [0.30, -7.25]],
}, },
{ {
id: 'weg-ost-unten', level: -1.57, types: ['tegula'], id: 'weg-ost-unten', level: -1.57, types: ['tegula'],
note: 'Podest am Fuss der Osttreppe', note: 'Podest am Fuss der Osttreppe',
polygon: [[2.80, -7.70], [4.55, -7.70], [4.55, -6.45], [2.80, -6.45]], polygon: [[2.10, -7.40], [4.55, -7.40], [4.55, -6.45], [2.10, -6.45]],
}, },
{ {
// Der Weg an der Suedgrenze folgt dem Anstieg der Grenzkoten von -0.04 // Der Weg an der Suedgrenze folgt dem Anstieg der Grenzkoten von -0.04
// im Westen auf 0.63 im Osten. // im Westen auf 0.63 im Osten.
id: 'weg-sued', plane: [0.0305, 0, 0.5634], types: ['tegula'], id: 'weg-sued', plane: [0.0305, 0, 0.5231], types: ['tegula'],
note: 'Weg an der Suedgrenze als Rampe', note: 'Weg an der Suedgrenze als Rampe',
polygon: [[-21.90, 4.45], [4.55, 4.45], [4.55, 6.05], [-21.90, 6.05]], polygon: [[-21.90, 4.45], [4.55, 4.45], [4.55, 6.05], [-21.90, 6.05]],
}, },
@@ -210,9 +219,11 @@ export const WALLS = [
path: [[-8.74, 4.18], [-7.24, 4.18]], t: 0.46, top: 2.28, base: 0.18, path: [[-8.74, 4.18], [-7.24, 4.18]], t: 0.46, top: 2.28, base: 0.18,
}, },
{ {
id: 'stufe-nordwest', type: 'travertin', // Das Podest liegt bis 0.65 m ueber der Feuerwehrzufahrt. Ohne Abschluss
note: 'Stufe an der Nordwestecke, vom Sitzplatz auf die Schotterwiese', // stuende dort eine Grasboeschung von einer Rasterzelle Breite.
path: [[-20.90, -7.29], [-20.10, -7.29]], t: 0.42, top: -0.33, base: -0.95, id: 'abschluss-podest-west', type: 'beton',
note: 'Betonabschluss des Podests gegen die Feuerwehrzufahrt',
path: [[-23.42, 2.20], [-23.42, 6.00]], t: 0.16, top: -0.10, base: -1.00,
}, },
{ {
id: 'lamellenwand', type: 'lamellen', id: 'lamellenwand', type: 'lamellen',
@@ -225,8 +236,8 @@ export const WALLS = [
export const PROPS = [ export const PROPS = [
// Der Plan reserviert 1.8 x 1.8 m fuer das Gartenhaus. Gebaut wird dort ein // Der Plan reserviert 1.8 x 1.8 m fuer das Gartenhaus. Gebaut wird dort ein
// schmaler Gartenschrank: 1.80 m breit, nur 0.50 m tief, rund 2.00 m hoch, // schmaler Gartenschrank: 1.80 m breit, nur 0.50 m tief, rund 2.00 m hoch,
// mit dem Ruecken in der Ecke und den Tueren zum Garten. // mit dem Ruecken zum Beerenbeet und den Tueren zum Hauszugang hin.
{ id: 'gartenschrank', type: 'gartenhaus', x: 1.87, z: -3.58, w: 0.50, d: 1.80, h: 2.00, rot: 0, { id: 'gartenschrank', type: 'gartenhaus', x: 1.215, z: -4.23, w: 0.50, d: 1.80, h: 2.00, rot: -90,
note: 'Gartenschrank 1.80 x 0.50 m, 2.00 m hoch' }, note: 'Gartenschrank 1.80 x 0.50 m, 2.00 m hoch' },
// Der Plan zeichnet zwei Rechtecke: das Podest 1.81 x 2.70 m (x -0.95 bis // Der Plan zeichnet zwei Rechtecke: das Podest 1.81 x 2.70 m (x -0.95 bis
// 0.86) und darauf die Huette 1.01 x 2.70 m an der Ostseite (x -0.15 bis // 0.86) und darauf die Huette 1.01 x 2.70 m an der Ostseite (x -0.15 bis
@@ -275,7 +286,12 @@ export const EXTRA_SURFACES = [
{ {
type: 'tegula', type: 'tegula',
note: 'Feuerwehrzufahrt und Zugangsweg westlich der Grenze, Tegula-Pflastersteine grau', note: 'Feuerwehrzufahrt und Zugangsweg westlich der Grenze, Tegula-Pflastersteine grau',
polygon: [[-25.60, -13.50], [-22.10, -13.50], [-22.10, 6.40], [-25.60, 6.40]], polygon: [[-25.60, -13.50], [-22.10, -13.50], [-22.10, 0.35], [-23.40, 0.35], [-23.40, 6.40], [-25.60, 6.40]],
},
{
type: 'tegula',
note: 'Podest vor dem Gartentor, westlicher Arm des Wegs um das Haus',
polygon: [[-23.34, 2.20], [-21.74, 2.20], [-21.74, 5.97], [-23.34, 5.97]],
}, },
{ {
// Der Plan zeigt oestlich der Grenze einen gepflasterten Weg, erst dahinter // Der Plan zeigt oestlich der Grenze einen gepflasterten Weg, erst dahinter
@@ -284,15 +300,10 @@ export const EXTRA_SURFACES = [
note: 'Weg an der Ostseite, Rampe oberhalb der Treppe (Steigung rund 12%)', note: 'Weg an der Ostseite, Rampe oberhalb der Treppe (Steigung rund 12%)',
polygon: [[2.12, -4.18], [4.45, -4.18], [4.45, 4.96], [2.12, 4.96]], polygon: [[2.12, -4.18], [4.45, -4.18], [4.45, 4.96], [2.12, 4.96]],
}, },
{
type: 'tegula',
note: 'Belagstreifen neben dem Gelaender, bis an den Kopf der Osttreppe',
polygon: [[2.12, -6.55], [2.86, -6.55], [2.86, -4.18], [2.12, -4.18]],
},
{ {
type: 'tegula', type: 'tegula',
note: 'Weg an der Ostseite, Podest unterhalb der Treppe auf -1.57', note: 'Weg an der Ostseite, Podest unterhalb der Treppe auf -1.57',
polygon: [[2.86, -7.60], [4.45, -7.60], [4.45, -6.52], [2.86, -6.52]], polygon: [[2.17, -7.35], [4.45, -7.35], [4.45, -6.52], [2.17, -6.52]],
}, },
{ {
// Schmaler gepflasterter Streifen jenseits der Suedgrenze, zwischen // Schmaler gepflasterter Streifen jenseits der Suedgrenze, zwischen
@@ -329,7 +340,7 @@ export const LINEAR = [
// Breite x 2.17 bis 3.78; die Koten daneben lauten -0.37 und -1.57. // Breite x 2.17 bis 3.78; die Koten daneben lauten -0.37 und -1.57.
id: 'treppe-ost', kind: 'treppe', id: 'treppe-ost', kind: 'treppe',
note: 'Treppe und Rampe hinunter zum Stellplatz des Nachbarn (-0.37 auf -1.57)', note: 'Treppe und Rampe hinunter zum Stellplatz des Nachbarn (-0.37 auf -1.57)',
path: [[2.975, -4.18], [2.975, -6.52]], width: 1.61, fromY: -0.37, toY: -1.57, steps: 7, path: [[3.31, -4.18], [3.31, -6.52]], width: 2.28, fromY: -0.37, toY: -1.57, steps: 7,
}, },
{ {
id: 'gelaender-ost', kind: 'gelaender', id: 'gelaender-ost', kind: 'gelaender',
@@ -340,13 +351,15 @@ export const LINEAR = [
// Trittlinien bei x -21.94 bis -22.54 im Abstand von 20 cm, Block bis // Trittlinien bei x -21.94 bis -22.54 im Abstand von 20 cm, Block bis
// x -23.34; Koten -0.28 oben und -1.08 auf der Feuerwehrzufahrt. // x -23.34; Koten -0.28 oben und -1.08 auf der Feuerwehrzufahrt.
id: 'treppe-west', kind: 'treppe', id: 'treppe-west', kind: 'treppe',
note: 'Treppe und Rampe von der Feuerwehrzufahrt zum Gartentor (-0.28 auf -1.08)', // Die Trittlinien liegen im Plan parallel zur Rinne, die Treppe laeuft
path: [[-21.80, 1.37], [-23.34, 1.37]], width: 1.70, fromY: -0.28, toY: -1.08, steps: 5, // also laengs der Grenze nach Norden - nicht quer von ihr weg.
note: 'Treppe und Rampe vom Podest vor dem Gartentor hinunter zur Feuerwehrzufahrt (-0.14 auf -1.08)',
path: [[-22.54, 2.20], [-22.54, 0.35]], width: 1.60, fromY: -0.14, toY: -1.08, steps: 6,
}, },
{ {
id: 'gelaender-west', kind: 'gelaender', id: 'gelaender-west', kind: 'gelaender',
note: 'Gelaender an der Treppe zur Feuerwehrzufahrt', note: 'Gelaender an der Treppe zur Feuerwehrzufahrt',
path: [[-21.80, 0.52], [-23.34, 0.52]], fromY: -0.28, toY: -1.08, height: 0.95, path: [[-23.38, 2.20], [-23.38, 0.35]], fromY: -0.14, toY: -1.08, height: 0.95,
}, },
{ {
id: 'rinne-gartentor', kind: 'rinne', id: 'rinne-gartentor', kind: 'rinne',
@@ -525,6 +538,10 @@ export const EXTRA_SPOTS = [
{ x: 7.5, z: 0.0, y: 0.20, zone: 'ost' }, { x: 7.5, z: 0.0, y: 0.20, zone: 'ost' },
{ x: 7.5, z: 5.0, y: 0.75, zone: 'ost' }, { x: 7.5, z: 5.0, y: 0.75, zone: 'ost' },
{ x: 5.0, z: -6.4, y: -1.35, zone: 'ost' }, { x: 5.0, z: -6.4, y: -1.35, zone: 'ost' },
// Fuss der Osttreppe: hier liegt das Gelaende auf Hoehe des Podests, sonst
// steht neben dem Treppenfuss ein Grashuegel.
{ x: 4.70, z: -6.55, y: -1.52, zone: 'ost' },
{ x: 4.70, z: -5.40, y: -0.95, zone: 'ost' },
{ x: 3.0, z: -8.5, y: -1.55, zone: 'zollet' }, { x: 3.0, z: -8.5, y: -1.55, zone: 'zollet' },
]; ];
+41 -14
View File
@@ -389,25 +389,52 @@ for (let j = 0; j < nz; j++) {
y = interpolate(x, z, best.id); y = interpolate(x, z, best.id);
} else { } else {
y = interpolate(x, z, zone); y = interpolate(x, z, zone);
// Ausdruecklich flach vorgegebene Ebenen: der Rasen der oberen Ebene }
// ist eben, ohne Vorgabe zieht ihn die Interpolation huegelig hoch. // Feste Ebenen und Belaege gelten auch ausserhalb der Zonen. Vorher
for (const lv of LEVELS) { // griffen sie nur innerhalb - das Podest am Fuss der Osttreppe liegt
if (!lv.flatten || !pointInPolygon(x, z, lv.polygon)) continue; // aber zwischen den Zonen, und dort stand ein Grashuegel darueber.
y = lv.level; for (const lv of LEVELS) {
locked[j * nx + i] = 1; if (!lv.flatten || !pointInPolygon(x, z, lv.polygon)) continue;
break; y = lv.level;
} locked[j * nx + i] = 1;
for (const f of flatSurfaces) { break;
if (!pointInPolygon(x, z, f.polygon)) continue; }
y = Math.max(f.min, Math.min(f.max, f.plane.a * x + f.plane.b * z + f.plane.c)); for (const f of flatSurfaces) {
locked[j * nx + i] = 1; if (!pointInPolygon(x, z, f.polygon)) continue;
break; y = Math.max(f.min, Math.min(f.max, f.plane.a * x + f.plane.b * z + f.plane.c));
} locked[j * nx + i] = 1;
break;
} }
height[j * nx + i] = y; height[j * nx + i] = y;
} }
} }
// Unter den Treppen muss das Gelaende unter der untersten Trittkante liegen.
// Sonst waechst die Boeschung durch die Blockstufen hindurch und die Treppe
// verschwindet unter Rasen - genau das war an der Osttreppe passiert.
for (const f of LINEAR) {
if (f.kind !== 'treppe') continue;
const [ax, az] = f.path[0], [bx, bz] = f.path[f.path.length - 1];
const len = Math.hypot(bx - ax, bz - az) || 1;
const ux = (bx - ax) / len, uz = (bz - az) / len;
const riser = (f.fromY - f.toY) / f.steps;
const half = f.width / 2 + 0.08;
for (let j = 0; j < nz; j++) {
const z = GRID.z0 + j * CELL;
for (let i = 0; i < nx; i++) {
const x = GRID.x0 + i * CELL;
const s = (x - ax) * ux + (z - az) * uz;
const q = -(x - ax) * uz + (z - az) * ux;
if (s < -0.1 || s > len + 0.1 || Math.abs(q) > half) continue;
const t = Math.max(0, Math.min(1, s / len));
const unter = f.fromY + (f.toY - f.fromY) * t - riser - 0.03;
const k = j * nx + i;
height[k] = Math.min(height[k], unter);
locked[k] = 1;
}
}
}
// Glaetten - aber nur die freien Felder. Die Belaege bleiben exakt eben. // Glaetten - aber nur die freien Felder. Die Belaege bleiben exakt eben.
let smoothed = Float32Array.from(height); let smoothed = Float32Array.from(height);
for (let pass = 0; pass < 3; pass++) { for (let pass = 0; pass < 3; pass++) {