Wege, Ebenen, Stufen und ein Sprung
Deploy App / deploy (push) Successful in 28s

Rueckmeldung Punkt fuer Punkt:

Weg rund ums Haus
- Der Ostweg ist jetzt durchgehend gepflastert von der Verbundstein-
  Anpassung (x 2.86) bis zur Grenze (x 4.45) - vorher 0.83 m breit mit
  Rasenstreifen daneben. Der Weg an der Suedgrenze reicht von der Grenze
  bis an die Nachbarmauer (z 4.49 bis 5.97).
- Winkelelemente Ost auf die Grenzlinie x 4.45; die Linie bei z 4.96 im
  Sueden war keine eigene Stuetzwand, das ist die Nachbarmauer selbst.

Hoehen und Gefaelle
- LEVELS koennen jetzt auch eine geneigte Ebene vorgeben. Die Wege sind
  Rampen: Ostweg rund 12%, Suedweg rund 3%, Feuerwehrzufahrt 13.6% aus
  den Plankoten -2.08 und -1.08. Der Architekturplan nennt an denselben
  Stellen "Steigung 9.9%" und "Steigung 13.0%".
- Ausserhalb der Zonen wird die Interpolation der naechsten Zone
  fortgesetzt statt ihres konstanten Ersatzwerts - die Feuerwehrzufahrt
  stand vorher als schwebende Platte ueber einer Gelaendekante.
- Gelaende oestlich der Grenze korrigiert: dort lag ein Huegel ueber dem
  Weg, obwohl der Plan -0.72 und -1.57 angibt.

Obere Ebene
- Der Rasen ist eben auf -0.02. Die Koten 0.09 bis 0.26 liegen schon im
  angehobenen Kiesbett des Sichtschutzes, nicht mehr im Rasen; ohne feste
  Vorgabe zog die Interpolation den Rasen huegelig hoch.

Stufen
- Beim gedeckten Sitzplatz stimmen jetzt beide schwebenden Platten: der
  Plan zeichnet zwei versetzte Rechtecke mit den Koten -0.02 und -0.17,
  bisher lagen sie auf -0.17 und -0.31 - die untere verschwand im Belag.
- Das Gartentor steht auf dem oberen Niveau (-0.13), von dort zwei Stufen
  hinunter auf den Hauptsitzplatz.
- Westtreppe ueber den ganzen Treppenblock (5 Stufen statt 4), die
  Gelaenderpfosten reichen bis ins Gelaende statt in der Luft zu enden.

Rundgang
- Treppen und schwebende Stufen sind begehbar: ihre Trittflaechen gehen in
  die Hoehenabfrage ein, die Stufen sind kein Hindernis mehr.
- Springen mit der Leertaste, rund 55 cm.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 19:49:46 +02:00
co-authored by Claude Opus 5
parent ce40d6275b
commit fad23a34c3
10 changed files with 215 additions and 51 deletions
+72 -29
View File
@@ -84,6 +84,42 @@ export const TREES = [
* bleibt die Ausgleichsebene aus den Koten stehen.
*/
export const LEVELS = [
{
// 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
// legt sich der Belag als schwebende Platte ueber das Gelaende.
id: 'feuerwehrzufahrt', plane: [0, 0.1364, -1.096], types: ['tegula'],
note: 'Feuerwehrzufahrt als Rampe',
polygon: [[-25.90, -13.80], [-21.90, -13.80], [-21.90, 6.60], [-25.90, 6.60]],
},
{
// Der Ostweg steigt von der Treppenkrone (-0.37 bei z -4.18) bis zur
// Suedostecke (0.63 bei z 4.31) gleichmaessig an - im Architekturplan
// steht dort "Steigung 9.9%".
id: 'weg-ost', plane: [0, 0.1178, 0.1224], types: ['tegula'],
note: 'Ostweg als Rampe',
polygon: [[2.80, -4.30], [4.55, -4.30], [4.55, 5.05], [2.80, 5.05]],
},
{
id: 'weg-ost-unten', level: -1.57, types: ['tegula'],
note: 'Podest am Fuss der Osttreppe',
polygon: [[2.80, -7.70], [4.55, -7.70], [4.55, -6.45], [2.80, -6.45]],
},
{
// Der Weg an der Suedgrenze folgt dem Anstieg der Grenzkoten von -0.04
// im Westen auf 0.63 im Osten.
id: 'weg-sued', plane: [0.0305, 0, 0.5634], types: ['tegula'],
note: 'Weg an der Suedgrenze als Rampe',
polygon: [[-21.90, 4.45], [4.55, 4.45], [4.55, 6.05], [-21.90, 6.05]],
},
{
// Der Rasen der oberen Ebene ist eben. Die Koten 0.09 bis 0.26 liegen
// schon im angehobenen Kiesbett des Sichtschutzes, nicht mehr im Rasen -
// ohne feste Vorgabe zieht die Interpolation den Rasen huegelig hoch.
id: 'oben', level: -0.02, flatten: true,
note: 'Obere Ebene: Rasen und Kiesfeld, eben auf -0.02',
polygon: [[-18.30, 0.90], [-8.60, 0.90], [-8.60, 3.90], [-18.30, 3.90]],
},
{
id: 'unten',
level: -0.33,
@@ -142,16 +178,8 @@ export const WALLS = [
// sie stuetzen den Garten gegen den tiefer liegenden Fussweg ab.
id: 'winkelelemente-ost', type: 'beton',
note: 'Winkelelemente aus Beton an der Ostgrenze, gegen den Fussweg',
path: [[3.735, -6.90], [3.735, 4.90]],
t: 0.14, top: null, base: null, retains: 'ost',
},
{
// Der Plan beschriftet die Linie bei z 4.96 mit "Winkelelemente": sie
// fasst das hoeher liegende Nachbargrundstueck hinter dem Sichtschutz.
id: 'winkelelemente-sued', type: 'beton',
note: 'Winkelelemente aus Beton entlang der Suedgrenze',
path: [[-21.20, 4.96], [1.99, 4.96]],
t: 0.14, top: null, base: null, retains: 'sued',
path: [[4.45, -6.90], [4.45, 4.90]],
t: 0.16, top: null, base: null, retains: 'ost',
},
{
id: 'betonmauer-nachbar', type: 'beton',
@@ -209,14 +237,14 @@ export const PROPS = [
note: 'Schlaufschacht D = 30 cm' },
// Der Torfluegel liegt im Plan bei x -21.53 und laeuft ueber z 2.56 bis 3.56.
{ id: 'gartentor', type: 'tor', x: -21.53, z: 3.06, w: 1.00, d: 0.06, h: 1.20, rot: 90,
note: 'Gartentor 100 cm, Stabgitter aus Stahl' },
base: -0.13, note: 'Gartentor 100 cm, Stabgitter aus Stahl, auf dem oberen Niveau' },
// Zwei schwebende Travertinplatten fuehren vom gedeckten Sitzplatz (-0.02)
// auf den Hauptsitzplatz (-0.33). Lage und Format stehen im Plan, die
// Ausbildung als auskragende Platte zeigt das Referenzbild des Planers.
{ id: 'stufe-oben', type: 'stufe', x: -14.335, z: -1.065, w: 0.81, d: 3.25, h: 0.08, rot: 0,
top: -0.17, note: 'Schwebende Travertinstufe, Tritthoehe -0.17' },
top: -0.02, note: 'Schwebende Travertinstufe, Tritthoehe -0.02 (Kote im Plan)' },
{ id: 'stufe-unten', type: 'stufe', x: -14.880, z: -1.480, w: 0.80, d: 3.20, h: 0.08, rot: 0,
top: -0.31, note: 'Schwebende Travertinstufe, Tritthoehe -0.31' },
top: -0.17, note: 'Schwebende Travertinstufe, Tritthoehe -0.17 (Kote im Plan)' },
// Beim Gartentor liegen ebenfalls zwei schwebende Travertinstufen: der Plan
// zeichnet ein Feld 0.80 x 1.60 m mit den Koten -0.13 (Podest am Tor) und
// -0.32 (Anschluss an den Hauptsitzplatz).
@@ -245,15 +273,20 @@ export const EXTRA_SURFACES = [
// Der Plan zeigt oestlich der Grenze einen gepflasterten Weg, erst dahinter
// beginnt die Wiese. Zwei Leuchten stehen daran.
type: 'tegula',
note: 'Fussweg an der Ostseite zwischen Verbundstein-Anpassung und Winkelelementen',
polygon: [[2.86, -7.00], [3.69, -7.00], [3.69, 4.90], [2.86, 4.90]],
note: 'Weg an der Ostseite, Rampe oberhalb der Treppe (Steigung rund 12%)',
polygon: [[2.86, -4.18], [4.45, -4.18], [4.45, 4.96], [2.86, 4.96]],
},
{
type: 'tegula',
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]],
},
{
// Schmaler gepflasterter Streifen jenseits der Suedgrenze, zwischen
// Grenze (z 4.49) und den Winkelelementen des Nachbarn (z 4.96).
type: 'tegula',
note: 'Tegula-Streifen des Suednachbarn entlang der Grenze',
polygon: [[-21.74, 4.49], [1.99, 4.49], [1.99, 4.96], [-21.74, 4.96]],
note: 'Weg entlang der Suedgrenze: von der Grenze bis an die Nachbarmauer gepflastert',
polygon: [[-21.74, 4.49], [4.45, 4.49], [4.45, 5.97], [-21.74, 5.97]],
},
{
type: 'tegula',
@@ -295,12 +328,12 @@ export const LINEAR = [
// x -23.34; Koten -0.28 oben und -1.08 auf der Feuerwehrzufahrt.
id: 'treppe-west', kind: 'treppe',
note: 'Treppe und Rampe von der Feuerwehrzufahrt zum Gartentor (-0.28 auf -1.08)',
path: [[-21.84, 1.37], [-22.64, 1.37]], width: 1.70, fromY: -0.28, toY: -1.08, steps: 4,
path: [[-21.80, 1.37], [-23.34, 1.37]], width: 1.70, fromY: -0.28, toY: -1.08, steps: 5,
},
{
id: 'gelaender-west', kind: 'gelaender',
note: 'Gelaender an der Treppe zur Feuerwehrzufahrt',
path: [[-21.84, 0.52], [-22.64, 0.52]], fromY: -0.28, toY: -1.08, height: 0.95,
path: [[-21.80, 0.52], [-23.34, 0.52]], fromY: -0.28, toY: -1.08, height: 0.95,
},
{
id: 'rinne-gartentor', kind: 'rinne',
@@ -397,7 +430,7 @@ export const NEIGHBOUR_BUILDINGS = [
export const TERRAIN_ZONES = [
{
id: 'garten', label: 'Garten Kellenberger',
polygon: [[-20.90, -6.60], [3.74, -6.60], [3.74, 4.96], [-20.90, 4.96]],
polygon: [[-20.90, -6.60], [4.55, -6.60], [4.55, 5.97], [-20.90, 5.97]],
fallback: -0.05,
},
{
@@ -415,12 +448,12 @@ export const TERRAIN_ZONES = [
// faengt der Nachbar sein hoeher liegendes Terrain, dahinter steht die
// bestehende Betonmauer.
id: 'sued', label: 'Nachbargrundstück Süd',
polygon: [[-22.00, 4.96], [6.00, 4.96], [6.00, 12.00], [-22.00, 12.00]],
polygon: [[-22.00, 5.97], [6.00, 5.97], [6.00, 12.00], [-22.00, 12.00]],
fallback: 0.45,
},
{
id: 'ost', label: 'Wiese Ost',
polygon: [[3.74, -6.60], [9.00, -6.60], [9.00, 4.96], [3.74, 4.96]],
polygon: [[4.55, -6.60], [9.00, -6.60], [9.00, 5.97], [4.55, 5.97]],
fallback: 0.55,
},
];
@@ -430,10 +463,14 @@ export const TERRAIN_ZONES = [
* plausibles Gelaende aber noetig sind (Auslauf gegen die Grundstuecksraender).
*/
export const EXTRA_SPOTS = [
{ x: -24.5, z: -8.0, y: -2.60, zone: 'zufahrt' },
{ x: -24.5, z: 0.0, y: -1.60, zone: 'zufahrt' },
{ x: -24.5, z: 5.0, y: -0.60, zone: 'zufahrt' },
{ x: -22.6, z: 5.5, y: -0.30, zone: 'zufahrt' },
// Auf der Zufahrt selbst gelten die Plankoten -2.08 (z -7.21) und -1.08
// (z 0.12); quer dazu ist sie eben, sonst entsteht eine Gelaendekante.
{ x: -25.0, z: -12.0, y: -2.75, zone: 'zufahrt' },
{ x: -25.0, z: -7.2, y: -2.08, zone: 'zufahrt' },
{ x: -25.0, z: 0.1, y: -1.08, zone: 'zufahrt' },
{ x: -25.0, z: 6.0, y: -0.28, zone: 'zufahrt' },
{ x: -22.4, z: -7.2, y: -2.08, zone: 'zufahrt' },
{ x: -22.4, z: 6.0, y: -0.28, zone: 'zufahrt' },
{ x: -20.0, z: -12.5, y: -1.75, zone: 'zollet' },
{ x: -8.0, z: -12.5, y: -1.70, zone: 'zollet' },
{ x: 2.0, z: -11.5, y: -1.65, zone: 'zollet' },
@@ -446,9 +483,15 @@ export const EXTRA_SPOTS = [
{ x: -18.0, z: 9.0, y: 0.25, zone: 'sued' },
{ x: -6.0, z: 9.0, y: 0.60, zone: 'sued' },
{ x: 4.0, z: 9.0, y: 1.00, zone: 'sued' },
{ x: 7.5, z: -5.0, y: 0.10, zone: 'ost' },
{ x: 7.5, z: 0.0, y: 0.45, zone: 'ost' },
{ x: 7.5, z: 5.0, y: 0.80, zone: 'ost' },
// Oestlich der Grenze faellt das Gelaende nach Norden zum Stellplatz des
// Nachbarn ab - die Plankoten dort lauten -0.72 und -1.57. Ohne diese
// Werte legt sich ein Huegel neben den Weg und deckt ihn zu.
{ x: 7.5, z: -6.4, y: -1.25, zone: 'ost' },
{ x: 7.5, z: -3.0, y: -0.35, 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: 5.0, z: -6.4, y: -1.35, zone: 'ost' },
{ x: 3.0, z: -8.5, y: -1.55, zone: 'zollet' },
];
/** Startposition und Blickrichtung beim Betreten der Szene. yaw = Grad ab Nord. */
+19 -6
View File
@@ -293,8 +293,12 @@ for (const s of surfaces) {
let plane, ys;
if (fixed) {
plane = { a: 0, b: 0, c: fixed.level };
ys = [fixed.level];
// Eine Ebene darf auch geneigt vorgegeben sein - Wege sind Rampen.
plane = fixed.plane
? { a: fixed.plane[0], b: fixed.plane[1], c: fixed.plane[2] }
: { a: 0, b: 0, c: fixed.level };
const corners = s.polygon.map(([x, z]) => plane.a * x + plane.b * z + plane.c);
ys = [Math.min(...corners), Math.max(...corners)];
s.level = fixed.id;
} else {
const inside = terrainSpots.filter((sp) => owner.get(sp) === s);
@@ -363,16 +367,25 @@ for (let j = 0; j < nz; j++) {
const zone = zoneOf(x, z);
let y;
if (!zone) {
// Ausserhalb aller Zonen: naechstgelegene Zone weiterziehen
// Ausserhalb aller Zonen: naechstgelegene Zone weiterziehen. Dabei wird
// deren Interpolation fortgesetzt, nicht ihr konstanter Ersatzwert -
// sonst entsteht am Zonenrand eine Gelaendekante.
let best = null, bd = Infinity;
for (const t of TERRAIN_ZONES) {
const [cx, cz] = centroid(t.polygon);
const d = Math.hypot(cx - x, cz - z);
const d = distToPolygon(x, z, t.polygon);
if (d < bd) { bd = d; best = t; }
}
y = best.fallback;
y = interpolate(x, z, best.id);
} else {
y = interpolate(x, z, zone);
// Ausdruecklich flach vorgegebene Ebenen: der Rasen der oberen Ebene
// ist eben, ohne Vorgabe zieht ihn die Interpolation huegelig hoch.
for (const lv of LEVELS) {
if (!lv.flatten || !pointInPolygon(x, z, lv.polygon)) continue;
y = lv.level;
locked[j * nx + i] = 1;
break;
}
for (const f of flatSurfaces) {
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));