Planabgleich Etappe A bis C: Zugaenge, Rasenrandplatten, Torstufen
Deploy App / deploy (push) Successful in 28s
Deploy App / deploy (push) Successful in 28s
Systematischer Abgleich Plan gegen Szene, Bereich fuer Bereich. Neu gebaut - Lineare Bauteile (LINEAR): Treppe und Rampe am Ost- und Westzugang, Stahlrohrgelaender laengs des Gefaelles, Entwaesserungsrinnen mit buendigem Rost. Geometrie aus den Trittlinien des Plans abgegriffen (Ost: 8 Trittlinien z -4.18 bis -6.52, Koten -0.37/-1.57; West: Trittlinien alle 20 cm ab x -21.94, Koten -0.28/-1.08). - Rasenrandplatten: das durchgehende Band von 6 cm Breite entlang der Rasenkante wird als hochkant stehende Randplatte mit Sichtkante aufgebaut, nicht mehr als Belag (neuer Typ randplatte, 1.2 m2). - Zwei schwebende Travertinstufen am Gartentor, Koten -0.13 und -0.32. - Winkelelemente entlang der Suedgrenze, Tegula-Streifen des Nachbarn. Korrigiert - Holzscheiterwand: der Plan zeigt zwei Felder 1.50 x 0.46 m, nicht eines. Farbe #ffaa00 war faelschlich als Tegula klassiert. - Gartentor: Fluegel laeuft ueber z, nicht ueber x - das Tor stand um 90 Grad verdreht. Jetzt Stabgitter mit Pfosten und senkrechten Staeben statt geschlossenem schwarzem Blatt. - Winkelelemente Ost auf x 3.735 (Plan: Doppellinie 3.69/3.78), Fussweg auf x 2.86 bis 3.69 zwischen Verbundstein-Anpassung und Winkelelementen. - Rinne beim Gartentor auf die Schraffur des Plans (z 2.295, x -23.34 bis -21.74). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -130,6 +130,12 @@ aus (`src/scene/walls.ts`):
|
|||||||
- **Lamellenwand** – senkrechte Bretter im Rhythmus des Plans (10 cm breit,
|
- **Lamellenwand** – senkrechte Bretter im Rhythmus des Plans (10 cm breit,
|
||||||
35 cm tief, 90 cm Achsabstand) vor dem gedeckten Sitzplatz.
|
35 cm tief, 90 cm Achsabstand) vor dem gedeckten Sitzplatz.
|
||||||
- **Holzscheiterwand** – Stahlrahmen, gefüllt mit gestapelten Scheitern.
|
- **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.
|
||||||
|
|
||||||
Die **Eibengruppen** entstehen nicht aus Einzelkugeln: die Pflanzstandorte des
|
Die **Eibengruppen** entstehen nicht aus Einzelkugeln: die Pflanzstandorte des
|
||||||
Plans werden gruppenweise verkettet, begradigt, an scharfen Knicken getrennt und
|
Plans werden gruppenweise verkettet, begradigt, an scharfen Knicken getrennt und
|
||||||
@@ -139,6 +145,19 @@ Abschnitt „Der Sichtschutz". Ebenso die Ligusterhecke (170 cm, 75 cm breit).
|
|||||||
Die **Travertinplatten 40 × 60 × 3 cm** liegen laut Plan einzeln im Dekorkies –
|
Die **Travertinplatten 40 × 60 × 3 cm** liegen laut Plan einzeln im Dekorkies –
|
||||||
30 Platten, exakt an den Planpositionen, als erhabene Platten auf dem Kiesbett.
|
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,
|
Materialien und Ausbildung folgen den **Referenzbildern im Plan-PDF**: heller,
|
||||||
geschichteter Travertin für Mauern, Stelen und Platten, die schwebenden Stufen
|
geschichteter Travertin für Mauern, Stelen und Platten, die schwebenden Stufen
|
||||||
als auskragende Platten mit Schattenfuge, der Flammengrill als anthrazitfarbener
|
als auskragende Platten mit Schattenfuge, der Flammengrill als anthrazitfarbener
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+9
-1
@@ -13,7 +13,7 @@ import './style.css';
|
|||||||
import { loadSite, makeHeightSampler, type SiteData } from './site';
|
import { loadSite, makeHeightSampler, type SiteData } from './site';
|
||||||
import { buildTerrain } from './scene/terrain';
|
import { buildTerrain } from './scene/terrain';
|
||||||
import { buildSurfaces } from './scene/surfaces';
|
import { buildSurfaces } from './scene/surfaces';
|
||||||
import { buildStelen, buildProps, buildLamps } from './scene/structures';
|
import { buildStelen, buildProps, buildLamps, buildLinear } from './scene/structures';
|
||||||
import { buildWalls } from './scene/walls';
|
import { buildWalls } from './scene/walls';
|
||||||
import { buildVegetation } from './scene/vegetation';
|
import { buildVegetation } from './scene/vegetation';
|
||||||
import { buildBuilding, buildNeighbourBuildings } from './scene/building';
|
import { buildBuilding, buildNeighbourBuildings } from './scene/building';
|
||||||
@@ -59,6 +59,7 @@ async function main() {
|
|||||||
scene.add(buildWalls(site.walls, height));
|
scene.add(buildWalls(site.walls, height));
|
||||||
scene.add(buildStelen(site.stelen, height));
|
scene.add(buildStelen(site.stelen, height));
|
||||||
scene.add(buildProps(site.props, height));
|
scene.add(buildProps(site.props, height));
|
||||||
|
scene.add(buildLinear(site.linear, height));
|
||||||
|
|
||||||
await progress(66, 'Haus und Nachbarschaft …');
|
await progress(66, 'Haus und Nachbarschaft …');
|
||||||
scene.add(buildBuilding(site.building));
|
scene.add(buildBuilding(site.building));
|
||||||
@@ -104,6 +105,13 @@ async function main() {
|
|||||||
obstacles.push(obstacleFromSegment(w.path[i - 1], w.path[i], w.t + 0.1));
|
obstacles.push(obstacleFromSegment(w.path[i - 1], w.path[i], w.t + 0.1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Gelaender begrenzen den Rundgang, Treppen und Rinnen bleiben begehbar.
|
||||||
|
for (const f of site.linear) {
|
||||||
|
if (f.kind !== 'gelaender') continue;
|
||||||
|
for (let i = 1; i < f.path.length; i++) {
|
||||||
|
obstacles.push(obstacleFromSegment(f.path[i - 1], f.path[i], 0.14));
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const p of site.props) {
|
for (const p of site.props) {
|
||||||
if (p.type === 'schacht' || p.type === 'wasserhahn') continue;
|
if (p.type === 'schacht' || p.type === 'wasserhahn') continue;
|
||||||
obstacles.push({ cx: p.x, cz: p.z, hw: p.w / 2, hd: p.d / 2, rot: (p.rot * Math.PI) / 180 });
|
obstacles.push({ cx: p.x, cz: p.z, hw: p.w / 2, hd: p.d / 2, rot: (p.rot * Math.PI) / 180 });
|
||||||
|
|||||||
+169
-11
@@ -7,7 +7,7 @@ import {
|
|||||||
BoxGeometry, BufferGeometry, CylinderGeometry, Group, Mesh, MeshStandardMaterial,
|
BoxGeometry, BufferGeometry, CylinderGeometry, Group, Mesh, MeshStandardMaterial,
|
||||||
PointLight, type MeshStandardMaterialParameters,
|
PointLight, type MeshStandardMaterialParameters,
|
||||||
} from 'three';
|
} from 'three';
|
||||||
import type { Lamp, Prop, Stele } from '../site';
|
import type { Lamp, Linear, Prop, Stele } from '../site';
|
||||||
import { boxGeometry, mergeGeometries } from './geometry';
|
import { boxGeometry, mergeGeometries } from './geometry';
|
||||||
import * as tex from './textures';
|
import * as tex from './textures';
|
||||||
|
|
||||||
@@ -250,19 +250,40 @@ function schachtdeckel(p: Prop, y: number): Group {
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gartentor: Stabgitterfluegel aus Stahl. Zwei Pfosten, umlaufender Rahmen aus
|
||||||
|
* Flachstahl und senkrechte Staebe im Abstand von rund 11 cm - das Tor bleibt
|
||||||
|
* durchsichtig, es ist kein geschlossenes Blatt.
|
||||||
|
*/
|
||||||
function gartentor(p: Prop, y: number): Group {
|
function gartentor(p: Prop, y: number): Group {
|
||||||
const g = new Group();
|
const g = new Group();
|
||||||
const mat = new MeshStandardMaterial({ color: 0x4a4a4c, roughness: 0.5, metalness: 0.4 });
|
const mat = new MeshStandardMaterial({ color: 0x43464a, roughness: 0.45, metalness: 0.55 });
|
||||||
const frame = new Mesh(boxGeometry(p.x, p.z, p.w, 0.05, y, y + p.h, p.rot, 1), mat);
|
const a = (p.rot * Math.PI) / 180;
|
||||||
g.add(frame);
|
// Laengsrichtung des Fluegels in der Ebene, quer dazu die Blattdicke.
|
||||||
for (let i = 1; i < 6; i++) {
|
const ux = Math.cos(a), uz = Math.sin(a);
|
||||||
const bar = new Mesh(
|
const at = (t: number): [number, number] => [p.x + ux * t, p.z + uz * t];
|
||||||
boxGeometry(p.x - p.w / 2 + (i * p.w) / 6, p.z, 0.03, 0.03, y, y + p.h, p.rot, 1),
|
const parts: BufferGeometry[] = [];
|
||||||
mat,
|
const half = p.w / 2;
|
||||||
);
|
const top = y + p.h;
|
||||||
g.add(bar);
|
|
||||||
|
// Pfosten links und rechts, etwas hoeher als der Fluegel
|
||||||
|
for (const t of [-half - 0.05, half + 0.05]) {
|
||||||
|
const [x, z] = at(t);
|
||||||
|
parts.push(boxGeometry(x, z, 0.08, 0.08, y - 0.35, top + 0.06, p.rot, 1));
|
||||||
}
|
}
|
||||||
g.children.forEach((c) => { (c as Mesh).castShadow = true; });
|
// Rahmen: unterer und oberer Riegel
|
||||||
|
for (const [y0, y1] of [[y + 0.08, y + 0.14], [top - 0.08, top]] as const) {
|
||||||
|
parts.push(boxGeometry(p.x, p.z, p.w, 0.035, y0, y1, p.rot, 1));
|
||||||
|
}
|
||||||
|
// Senkrechte Staebe
|
||||||
|
const n = Math.max(4, Math.round(p.w / 0.11));
|
||||||
|
for (let i = 0; i <= n; i++) {
|
||||||
|
const [x, z] = at(-half + (i * p.w) / n);
|
||||||
|
parts.push(boxGeometry(x, z, 0.016, 0.016, y + 0.08, top, p.rot, 1));
|
||||||
|
}
|
||||||
|
const m = new Mesh(mergeGeometries(parts), mat);
|
||||||
|
m.castShadow = true;
|
||||||
|
g.add(m);
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,3 +365,140 @@ export function buildLamps(lamps: Lamp[], height: (x: number, z: number) => numb
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Lineare Bauteile: Treppen mit Rampe, Gelaender, Entwaesserungsrinnen
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Winkel fuer boxGeometry, so dass die lokale v-Achse (Tiefe d) entlang der
|
||||||
|
* Richtung (dx, dz) liegt. Aus world = (cx + u*cos - v*sin, y, cz + u*sin + v*cos)
|
||||||
|
* folgt v-Achse = (-sin, cos), also sin = -dx und cos = dz.
|
||||||
|
*/
|
||||||
|
function axisAngle(dx: number, dz: number): number {
|
||||||
|
return (Math.atan2(-dx, dz) * 180) / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blockstufentreppe. Der Pfad laeuft von oben nach unten; jede Stufe ist ein
|
||||||
|
* Travertinblock, dessen Trittflaeche eine Steigung tiefer liegt als die
|
||||||
|
* vorige. Die Bloecke reichen bis unter das untere Niveau, damit die Treppe
|
||||||
|
* auch von der Seite geschlossen wirkt.
|
||||||
|
*/
|
||||||
|
function blockTreppe(f: Linear): Group {
|
||||||
|
const g = new Group();
|
||||||
|
const belag = tex.travertinSlab();
|
||||||
|
const mat = material(belag, { roughness: 0.6 });
|
||||||
|
const [[x0, z0], [x1, z1]] = [f.path[0], f.path[f.path.length - 1]];
|
||||||
|
const dx = x1 - x0, dz = z1 - z0;
|
||||||
|
const len = Math.hypot(dx, dz) || 1;
|
||||||
|
const rot = axisAngle(dx / len, dz / len);
|
||||||
|
const steps = f.steps ?? 4;
|
||||||
|
const width = f.width ?? 1.2;
|
||||||
|
const fromY = f.fromY ?? 0, toY = f.toY ?? -1;
|
||||||
|
const riser = (fromY - toY) / steps;
|
||||||
|
const tread = len / steps;
|
||||||
|
const parts: BufferGeometry[] = [];
|
||||||
|
for (let i = 0; i < steps; i++) {
|
||||||
|
const t = (i + 0.5) / steps;
|
||||||
|
const top = fromY - (i + 1) * riser;
|
||||||
|
parts.push(boxGeometry(
|
||||||
|
x0 + dx * t, z0 + dz * t, width, tread + 0.01,
|
||||||
|
toY - 0.45, top, rot, belag.tile,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
const m = new Mesh(mergeGeometries(parts), mat);
|
||||||
|
m.castShadow = true;
|
||||||
|
m.receiveShadow = true;
|
||||||
|
g.add(m);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gelaender aus Stahlrohr: Pfosten alle rund 1.1 m, Handlauf und Knieholm
|
||||||
|
* folgen dem Gefaelle des Laufs.
|
||||||
|
*/
|
||||||
|
function gelaender(f: Linear): Group {
|
||||||
|
const g = new Group();
|
||||||
|
const mat = new MeshStandardMaterial({ color: 0x4c4f52, roughness: 0.42, metalness: 0.65 });
|
||||||
|
const [[x0, z0], [x1, z1]] = [f.path[0], f.path[f.path.length - 1]];
|
||||||
|
const dx = x1 - x0, dz = z1 - z0;
|
||||||
|
const len = Math.hypot(dx, dz) || 1;
|
||||||
|
const rot = axisAngle(dx / len, dz / len);
|
||||||
|
const fromY = f.fromY ?? 0, toY = f.toY ?? -1;
|
||||||
|
const h = f.height ?? 0.95;
|
||||||
|
const parts: BufferGeometry[] = [];
|
||||||
|
const n = Math.max(2, Math.round(len / 1.1) + 1);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
const t = i / (n - 1);
|
||||||
|
const y = fromY + (toY - fromY) * t;
|
||||||
|
parts.push(boxGeometry(x0 + dx * t, z0 + dz * t, 0.05, 0.05, y - 0.35, y + h, rot, 1));
|
||||||
|
}
|
||||||
|
// Holme: als flache Bloecke ueber die ganze Laenge, um die Neigung gelegt.
|
||||||
|
for (const [level, thick] of [[h, 0.055], [h * 0.52, 0.04]] as const) {
|
||||||
|
const yA = fromY + level, yB = toY + level;
|
||||||
|
const rise = yA - yB;
|
||||||
|
const holmLen = Math.hypot(len, rise);
|
||||||
|
const geo = boxGeometry(0, 0, thick, holmLen, -thick / 2, thick / 2, 0, 1);
|
||||||
|
geo.rotateX(Math.atan2(rise, len));
|
||||||
|
geo.rotateY((rot * Math.PI) / 180);
|
||||||
|
geo.translate((x0 + x1) / 2, (yA + yB) / 2, (z0 + z1) / 2);
|
||||||
|
parts.push(geo);
|
||||||
|
}
|
||||||
|
const m = new Mesh(mergeGeometries(parts), mat);
|
||||||
|
m.castShadow = true;
|
||||||
|
g.add(m);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entwaesserungsrinne: schmaler Kanal im Belag mit buendigem Stahlrost.
|
||||||
|
*/
|
||||||
|
function rinne(f: Linear, height: (x: number, z: number) => number): Group {
|
||||||
|
const g = new Group();
|
||||||
|
const beton = new MeshStandardMaterial({ color: 0x8e8a84, roughness: 0.85 });
|
||||||
|
const rost = new MeshStandardMaterial({ color: 0x6a6d70, roughness: 0.4, metalness: 0.7 });
|
||||||
|
const w = f.width ?? 0.15;
|
||||||
|
for (let i = 1; i < f.path.length; i++) {
|
||||||
|
const [ax, az] = f.path[i - 1], [bx, bz] = f.path[i];
|
||||||
|
const dx = bx - ax, dz = bz - az;
|
||||||
|
const len = Math.hypot(dx, dz) || 1;
|
||||||
|
const rot = axisAngle(dx / len, dz / len);
|
||||||
|
const cx = (ax + bx) / 2, cz = (az + bz) / 2;
|
||||||
|
const y = height(cx, cz);
|
||||||
|
const kasten = new Mesh(boxGeometry(cx, cz, w + 0.09, len, y - 0.30, y - 0.015, rot, 1), beton);
|
||||||
|
kasten.receiveShadow = true;
|
||||||
|
g.add(kasten);
|
||||||
|
// Rost in Segmenten von 50 cm, damit die Stossfugen sichtbar bleiben.
|
||||||
|
const seg = Math.max(1, Math.round(len / 0.5));
|
||||||
|
for (let s = 0; s < seg; s++) {
|
||||||
|
const t = (s + 0.5) / seg;
|
||||||
|
const r = new Mesh(
|
||||||
|
boxGeometry(ax + dx * t, az + dz * t, w, len / seg - 0.012, y - 0.035, y + 0.004, rot, 1),
|
||||||
|
rost,
|
||||||
|
);
|
||||||
|
r.receiveShadow = true;
|
||||||
|
g.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Baut Treppen, Gelaender und Rinnen aus dem Plan. */
|
||||||
|
export function buildLinear(features: Linear[], height: (x: number, z: number) => number): Group {
|
||||||
|
const group = new Group();
|
||||||
|
group.name = 'Treppen, Gelaender und Rinnen';
|
||||||
|
for (const f of features) {
|
||||||
|
let obj: Group;
|
||||||
|
switch (f.kind) {
|
||||||
|
case 'treppe': obj = blockTreppe(f); break;
|
||||||
|
case 'gelaender': obj = gelaender(f); break;
|
||||||
|
case 'rinne': obj = rinne(f, height); break;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
obj.name = f.id;
|
||||||
|
obj.userData.note = f.note;
|
||||||
|
group.add(obj);
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ interface BelagDef {
|
|||||||
edging?: boolean;
|
edging?: boolean;
|
||||||
/** Material der Sichtkante, falls abweichend. */
|
/** Material der Sichtkante, falls abweichend. */
|
||||||
edge?: () => tex.Belag;
|
edge?: () => tex.Belag;
|
||||||
|
/**
|
||||||
|
* Randplatte: folgt dem Gelaende wie eine gewachsene Flaeche, steht aber
|
||||||
|
* mit einer sichtbaren Kante darueber. Fuer die Rasenrandplatten.
|
||||||
|
*/
|
||||||
|
kerb?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BELAG: Record<SurfaceType, BelagDef> = {
|
const BELAG: Record<SurfaceType, BelagDef> = {
|
||||||
@@ -45,6 +50,7 @@ const BELAG: Record<SurfaceType, BelagDef> = {
|
|||||||
rasen: { belag: tex.rasen, label: 'Rasen', lift: 0.012, built: false },
|
rasen: { belag: tex.rasen, label: 'Rasen', lift: 0.012, built: false },
|
||||||
schotterwiese: { belag: tex.schotterwiese, label: 'Schotterwiese', lift: 0.010, built: false },
|
schotterwiese: { belag: tex.schotterwiese, label: 'Schotterwiese', lift: 0.010, built: false },
|
||||||
beet: { belag: tex.beet, label: 'Pflanzbeet', lift: 0.008, built: false, edging: true },
|
beet: { belag: tex.beet, label: 'Pflanzbeet', lift: 0.008, built: false, edging: true },
|
||||||
|
randplatte: { belag: tex.travertinMauer, label: 'Rasenrandplatten', lift: 0.045, built: false, kerb: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
const STAHL = () => new MeshStandardMaterial({ color: 0x8a877f, roughness: 0.55, metalness: 0.62 });
|
const STAHL = () => new MeshStandardMaterial({ color: 0x8a877f, roughness: 0.55, metalness: 0.62 });
|
||||||
@@ -100,6 +106,14 @@ export function buildSurfaces(
|
|||||||
tops.push(surfaceGeometryMulti([s.polygon], height, {
|
tops.push(surfaceGeometryMulti([s.polygon], height, {
|
||||||
offset: def.lift, tile: belag.tile, maxEdge: 0.28,
|
offset: def.lift, tile: belag.tile, maxEdge: 0.28,
|
||||||
}));
|
}));
|
||||||
|
if (def.kerb) {
|
||||||
|
sides.push(surfaceEdgeGeometry(
|
||||||
|
s.polygon,
|
||||||
|
(x, z) => height(x, z) + def.lift,
|
||||||
|
(x, z) => height(x, z) - 0.22,
|
||||||
|
belag.tile,
|
||||||
|
));
|
||||||
|
}
|
||||||
if (def.edging) {
|
if (def.edging) {
|
||||||
edgingParts.push(edgingGeometry(s.polygon, (x, z) => height(x, z) + def.lift));
|
edgingParts.push(edgingGeometry(s.polygon, (x, z) => height(x, z) + def.lift));
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-1
@@ -11,7 +11,8 @@ export type Vec2 = [number, number];
|
|||||||
|
|
||||||
export type SurfaceType =
|
export type SurfaceType =
|
||||||
| 'travertin80' | 'dekorkies' | 'travertinstell' | 'keramik'
|
| 'travertin80' | 'dekorkies' | 'travertinstell' | 'keramik'
|
||||||
| 'verbundstein' | 'tegula' | 'rasen' | 'schotterwiese' | 'beet' | 'spielsand';
|
| 'verbundstein' | 'tegula' | 'rasen' | 'schotterwiese' | 'beet' | 'spielsand'
|
||||||
|
| 'randplatte';
|
||||||
|
|
||||||
export interface Surface {
|
export interface Surface {
|
||||||
type: SurfaceType;
|
type: SurfaceType;
|
||||||
@@ -105,6 +106,26 @@ export interface Stele {
|
|||||||
side: 'sued' | 'nord' | 'west';
|
side: 'sued' | 'nord' | 'west';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lineares Bauteil entlang einer Achse: Treppe mit Rampe, Gelaender oder
|
||||||
|
* Entwaesserungsrinne. Der Plan zeichnet diese nur als Linienbuendel.
|
||||||
|
*/
|
||||||
|
export interface Linear {
|
||||||
|
id: string;
|
||||||
|
kind: 'treppe' | 'gelaender' | 'rinne';
|
||||||
|
note?: string;
|
||||||
|
path: Vec2[];
|
||||||
|
/** Breite quer zur Achse (Treppe, Rinne). */
|
||||||
|
width?: number;
|
||||||
|
/** Hoehe an Anfang und Ende der Achse. */
|
||||||
|
fromY?: number;
|
||||||
|
toY?: number;
|
||||||
|
/** Anzahl Blockstufen. */
|
||||||
|
steps?: number;
|
||||||
|
/** Gelaenderhoehe ueber dem Lauf. */
|
||||||
|
height?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Opening {
|
export interface Opening {
|
||||||
face: 'north' | 'south' | 'east' | 'west';
|
face: 'north' | 'south' | 'east' | 'west';
|
||||||
a: number;
|
a: number;
|
||||||
@@ -174,6 +195,7 @@ export interface SiteData {
|
|||||||
lamps: Lamp[];
|
lamps: Lamp[];
|
||||||
walls: Wall[];
|
walls: Wall[];
|
||||||
props: Prop[];
|
props: Prop[];
|
||||||
|
linear: Linear[];
|
||||||
stelen: Stele[];
|
stelen: Stele[];
|
||||||
building: Building;
|
building: Building;
|
||||||
neighbourBuildings: NeighbourBuilding[];
|
neighbourBuildings: NeighbourBuilding[];
|
||||||
|
|||||||
+7
-1
@@ -90,6 +90,7 @@ export function setupHud(opts: HudOptions): Hud {
|
|||||||
travertin80: 'Travertinplatten 80×80×3 cm',
|
travertin80: 'Travertinplatten 80×80×3 cm',
|
||||||
dekorkies: 'Dekorkies',
|
dekorkies: 'Dekorkies',
|
||||||
travertinstell: 'Travertinstellplatten',
|
travertinstell: 'Travertinstellplatten',
|
||||||
|
randplatte: 'Rasenrandplatten',
|
||||||
keramik: 'Keramikplatten 60×60×2 cm',
|
keramik: 'Keramikplatten 60×60×2 cm',
|
||||||
verbundstein: 'Verbundsteine (Nachbar)',
|
verbundstein: 'Verbundsteine (Nachbar)',
|
||||||
tegula: 'Tegula-Pflastersteine grau',
|
tegula: 'Tegula-Pflastersteine grau',
|
||||||
@@ -140,7 +141,12 @@ export function setupHud(opts: HudOptions): Hud {
|
|||||||
<li>${site.walls.length} Mauern und Wände</li>
|
<li>${site.walls.length} Mauern und Wände</li>
|
||||||
<li>${site.lamps.length} Leuchten – sie schalten sich abends automatisch ein</li>
|
<li>${site.lamps.length} Leuchten – sie schalten sich abends automatisch ein</li>
|
||||||
<li>${site.slabs.length} lose Travertinplatten 40 × 60 × 3 cm im Dekorkies</li>
|
<li>${site.slabs.length} lose Travertinplatten 40 × 60 × 3 cm im Dekorkies</li>
|
||||||
<li>Gartenhaus 1,8 × 1,8 m, Spielhaus, Flammengrill mit Holzscheiterwand</li>
|
<li>Rasenrandplatten als 6 cm schmale Stellplatten entlang der Rasenkante</li>
|
||||||
|
<li>Gartenhaus 1,8 × 1,8 m, Spielhaus, Flammengrill mit zwei Holzscheiterwänden
|
||||||
|
(je 150 × 200 cm)</li>
|
||||||
|
<li>Zwei schwebende Travertinstufen am Gartentor, Stabgittertor 100 cm</li>
|
||||||
|
<li>${site.linear.filter((l) => l.kind === 'treppe').length} Treppen mit Rampe
|
||||||
|
(Ost- und Westzugang), Geländer und Entwässerungsrinnen</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>Bedienung</h3>
|
<h3>Bedienung</h3>
|
||||||
|
|||||||
+105
-9
@@ -25,7 +25,7 @@ export const SURFACE_BY_COLOR = {
|
|||||||
'#bbbbbb': 'verbundstein', // Anpassung Verbundsteine Nachbar
|
'#bbbbbb': 'verbundstein', // Anpassung Verbundsteine Nachbar
|
||||||
'#ddba99': 'beet',
|
'#ddba99': 'beet',
|
||||||
'#c0bab4': 'spielsand',
|
'#c0bab4': 'spielsand',
|
||||||
'#ffaa00': 'tegula', // Tegula-Pflastersteine grau (im Plan orange markiert)
|
'#ffaa00': 'holzscheiter', // orange markiert: die beiden Holzscheiterwaende im Sichtschutz
|
||||||
'#ddd299': 'travertinstell',
|
'#ddd299': 'travertinstell',
|
||||||
'#73e900': 'ligusterhecke',
|
'#73e900': 'ligusterhecke',
|
||||||
'#cccccc': 'winkelelement',
|
'#cccccc': 'winkelelement',
|
||||||
@@ -137,6 +137,22 @@ export const WALLS = [
|
|||||||
note: 'Schenkel der Travertinsitzmauer laengs des Rasens, gleicher Hoehensprung',
|
note: 'Schenkel der Travertinsitzmauer laengs des Rasens, gleicher Hoehensprung',
|
||||||
path: [[-18.44, 0.55], [-18.44, 3.94]], t: 0.40, top: -0.02, base: -0.55, seating: true,
|
path: [[-18.44, 0.55], [-18.44, 3.94]], t: 0.40, top: -0.02, base: -0.55, seating: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Der Plan beschriftet die Linien an der Ostgrenze mit "Winkelelemente":
|
||||||
|
// 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',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'betonmauer-nachbar', type: 'beton',
|
id: 'betonmauer-nachbar', type: 'beton',
|
||||||
note: 'Bestehende Betonmauer des Suednachbarn entlang der Grenze',
|
note: 'Bestehende Betonmauer des Suednachbarn entlang der Grenze',
|
||||||
@@ -147,15 +163,23 @@ export const WALLS = [
|
|||||||
note: 'Rueckspringender Schenkel der Nachbarmauer',
|
note: 'Rueckspringender Schenkel der Nachbarmauer',
|
||||||
path: [[2.09, 6.07], [2.09, 9.05]], t: 0.20, top: 1.10, base: -0.60,
|
path: [[2.09, 6.07], [2.09, 9.05]], t: 0.20, top: 1.10, base: -0.60,
|
||||||
},
|
},
|
||||||
|
// Der Plan zeigt zwei orange markierte Felder von 1.50 x 0.46 m in der
|
||||||
|
// Sichtschutzlinie; die Beschriftung "Holzscheiterwand 150 cm x 200 cm"
|
||||||
|
// steht zwischen ihnen und gilt fuer beide.
|
||||||
{
|
{
|
||||||
id: 'holzscheiterwand', type: 'holzscheiter',
|
id: 'holzscheiterwand-west', type: 'holzscheiter',
|
||||||
note: 'Holzscheiterwand 150 cm breit, 200 cm hoch, hinter dem Flammengrill',
|
note: 'Holzscheiterwand 150 cm breit, 200 cm hoch, hinter dem Flammengrill',
|
||||||
path: [[-5.75, 4.35], [-4.25, 4.35]], t: 0.45, top: 2.26, base: 0.20,
|
path: [[-6.08, 4.18], [-4.58, 4.18]], t: 0.46, top: 2.32, base: 0.22,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'stufe-gartentor', type: 'travertin',
|
id: 'holzscheiterwand-ost', type: 'holzscheiter',
|
||||||
note: 'Blockstufen beim Gartentor an der Suedwestecke',
|
note: 'Zweite Holzscheiterwand 150 cm breit, 200 cm hoch',
|
||||||
path: [[-21.05, 2.15], [-21.05, 3.75]], t: 0.80, top: -0.18, base: -0.80,
|
path: [[-8.74, 4.18], [-7.24, 4.18]], t: 0.46, top: 2.28, base: 0.18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'stufe-nordwest', type: 'travertin',
|
||||||
|
note: 'Stufe an der Nordwestecke, vom Sitzplatz auf die Schotterwiese',
|
||||||
|
path: [[-20.90, -7.29], [-20.10, -7.29]], t: 0.42, top: -0.33, base: -0.95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'lamellenwand', type: 'lamellen',
|
id: 'lamellenwand', type: 'lamellen',
|
||||||
@@ -179,7 +203,9 @@ export const PROPS = [
|
|||||||
{ id: 'wasserhahn', type: 'wasserhahn', x: -13.90, z: -4.60, w: 0.12, d: 0.12, h: 0.85, rot: 0 },
|
{ id: 'wasserhahn', type: 'wasserhahn', x: -13.90, z: -4.60, w: 0.12, d: 0.12, h: 0.85, rot: 0 },
|
||||||
{ id: 'schlaufschacht', type: 'schacht', x: -14.35, z: -3.95, w: 0.30, d: 0.30, h: 0.02, rot: 0,
|
{ id: 'schlaufschacht', type: 'schacht', x: -14.35, z: -3.95, w: 0.30, d: 0.30, h: 0.02, rot: 0,
|
||||||
note: 'Schlaufschacht D = 30 cm' },
|
note: 'Schlaufschacht D = 30 cm' },
|
||||||
{ id: 'gartentor', type: 'tor', x: -21.60, z: 3.30, w: 1.00, d: 0.06, h: 1.10, rot: 0 },
|
// 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' },
|
||||||
// Zwei schwebende Travertinplatten fuehren vom gedeckten Sitzplatz (-0.02)
|
// Zwei schwebende Travertinplatten fuehren vom gedeckten Sitzplatz (-0.02)
|
||||||
// auf den Hauptsitzplatz (-0.33). Lage und Format stehen im Plan, die
|
// auf den Hauptsitzplatz (-0.33). Lage und Format stehen im Plan, die
|
||||||
// Ausbildung als auskragende Platte zeigt das Referenzbild des Planers.
|
// Ausbildung als auskragende Platte zeigt das Referenzbild des Planers.
|
||||||
@@ -187,6 +213,13 @@ export const PROPS = [
|
|||||||
top: -0.17, note: 'Schwebende Travertinstufe, Tritthoehe -0.17' },
|
top: -0.17, note: 'Schwebende Travertinstufe, Tritthoehe -0.17' },
|
||||||
{ id: 'stufe-unten', type: 'stufe', x: -14.880, z: -1.480, w: 0.80, d: 3.20, h: 0.08, rot: 0,
|
{ 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.31, note: 'Schwebende Travertinstufe, Tritthoehe -0.31' },
|
||||||
|
// 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).
|
||||||
|
{ id: 'stufe-tor-oben', type: 'stufe', x: -21.05, z: 3.35, w: 0.80, d: 0.80, h: 0.08, rot: 0,
|
||||||
|
top: -0.13, note: 'Schwebende Travertinstufe am Gartentor, Tritthoehe -0.13' },
|
||||||
|
{ id: 'stufe-tor-unten', type: 'stufe', x: -21.05, z: 2.55, w: 0.80, d: 0.80, h: 0.08, rot: 0,
|
||||||
|
top: -0.32, note: 'Schwebende Travertinstufe am Gartentor, Tritthoehe -0.32' },
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,8 +234,27 @@ export const EXTRA_SURFACES = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'tegula',
|
type: 'tegula',
|
||||||
note: 'Feuerwehrzufahrt und Zugangsweg, Tegula-Pflastersteine grau',
|
note: 'Feuerwehrzufahrt und Zugangsweg westlich der Grenze, Tegula-Pflastersteine grau',
|
||||||
polygon: [[-25.60, -9.00], [-22.10, -9.00], [-22.10, 6.40], [-25.60, 6.40]],
|
polygon: [[-25.60, -13.50], [-22.10, -13.50], [-22.10, 6.40], [-25.60, 6.40]],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 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]],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 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]],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tegula',
|
||||||
|
note: 'Stellplatte und Sitzplatz des Nachbarn im Nordosten',
|
||||||
|
polygon: [[0.40, -11.20], [4.60, -11.20], [4.60, -7.30], [0.40, -7.30]],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Die schwebenden Stufen im Plan: eine Zwischenebene zwischen dem
|
// Die schwebenden Stufen im Plan: eine Zwischenebene zwischen dem
|
||||||
@@ -214,6 +266,50 @@ export const EXTRA_SURFACES = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lineare Bauteile, die der Plan benennt, aber nur als Linienbuendel zeichnet:
|
||||||
|
* Treppen mit Rampe, die zugehoerigen Gelaender und die Entwaesserungsrinnen.
|
||||||
|
*
|
||||||
|
* `path` ist die Achse in Laufrichtung, `fromY`/`toY` die Hoehe an Anfang und
|
||||||
|
* Ende. Die Hoehen stammen aus den Koten beidseits des jeweiligen Bauteils.
|
||||||
|
*/
|
||||||
|
export const LINEAR = [
|
||||||
|
{
|
||||||
|
// Der Plan zeichnet acht Trittlinien bei z -4.18 bis -6.52 ueber die
|
||||||
|
// Breite x 2.17 bis 3.78; die Koten daneben lauten -0.37 und -1.57.
|
||||||
|
id: 'treppe-ost', kind: 'treppe',
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gelaender-ost', kind: 'gelaender',
|
||||||
|
note: 'Gelaender laengs der Osttreppe',
|
||||||
|
path: [[2.17, -4.18], [2.17, -6.52]], fromY: -0.37, toY: -1.57, height: 0.95,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 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.
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'rinne-gartentor', kind: 'rinne',
|
||||||
|
note: 'Entwaesserungsrinne vor dem Gartentor',
|
||||||
|
path: [[-23.34, 2.295], [-21.74, 2.295]], width: 0.15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'edelstahlrinne', kind: 'rinne',
|
||||||
|
note: 'Edelstahlrinne laengs der Fassade am Hauptsitzplatz',
|
||||||
|
path: [[-13.98, -6.55], [-13.98, -2.95]], width: 0.13,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hoehe des Sichtschutzes. Er besteht aus aufrechten Travertinstelen
|
* Hoehe des Sichtschutzes. Er besteht aus aufrechten Travertinstelen
|
||||||
* (50 x 6 cm im Grundriss) und Eibengruppen, die sich abwechseln und
|
* (50 x 6 cm im Grundriss) und Eibengruppen, die sich abwechseln und
|
||||||
|
|||||||
+17
-2
@@ -8,7 +8,7 @@ import fs from 'node:fs';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import {
|
import {
|
||||||
SURFACE_BY_COLOR, PLANT_BY_COLOR, TREES, WALLS, PROPS, LEVELS, STELE_HEIGHTS,
|
SURFACE_BY_COLOR, PLANT_BY_COLOR, TREES, WALLS, PROPS, LEVELS, STELE_HEIGHTS, LINEAR,
|
||||||
BUILDING, NEIGHBOUR_BUILDINGS, TERRAIN_ZONES, EXTRA_SPOTS, EXTRA_SURFACES,
|
BUILDING, NEIGHBOUR_BUILDINGS, TERRAIN_ZONES, EXTRA_SPOTS, EXTRA_SURFACES,
|
||||||
START_VIEWS, SITE_LOCATION,
|
START_VIEWS, SITE_LOCATION,
|
||||||
} from './authoring.mjs';
|
} from './authoring.mjs';
|
||||||
@@ -85,7 +85,7 @@ function fitPlane(points) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Bauteile, die als Mauer modelliert werden - nicht als begehbare Flaeche. */
|
/** Bauteile, die als Mauer modelliert werden - nicht als begehbare Flaeche. */
|
||||||
const NOT_A_SURFACE = new Set(['travertinmauer', 'winkelelement', 'spielhaus', 'gartenhaus', 'roboter', 'ligusterhecke']);
|
const NOT_A_SURFACE = new Set(['travertinmauer', 'winkelelement', 'spielhaus', 'gartenhaus', 'roboter', 'ligusterhecke', 'holzscheiter']);
|
||||||
|
|
||||||
const bboxOf = (pts) => {
|
const bboxOf = (pts) => {
|
||||||
let x0 = Infinity, z0 = Infinity, x1 = -Infinity, z1 = -Infinity;
|
let x0 = Infinity, z0 = Infinity, x1 = -Infinity, z1 = -Infinity;
|
||||||
@@ -133,6 +133,19 @@ for (const s of raw.shapes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rasenrandplatten: derselbe Ton wie die Stelen, aber als durchgehendes
|
||||||
|
// Band von 6 cm Breite entlang der Rasenkante. Sie stehen hochkant und
|
||||||
|
// trennen den Rasen vom Kiesbett des Sichtschutzes.
|
||||||
|
const laenge = Math.max(b.w, b.d);
|
||||||
|
if (s.hex === '#ddd299' && laenge > 0.8 && s.area / laenge < 0.15) {
|
||||||
|
const poly = polygonArea(s.pts) < 0 ? [...s.pts].reverse() : s.pts;
|
||||||
|
surfaces.push({
|
||||||
|
type: 'randplatte', area: round(s.area, 2),
|
||||||
|
polygon: poly.map((p) => [round(p[0]), round(p[1])]),
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Der Plan verwendet denselben hellen Ton fuer Dekorkies und fuer die
|
// Der Plan verwendet denselben hellen Ton fuer Dekorkies und fuer die
|
||||||
// Keramikterrasse des Nachbarn; unterschieden wird ueber die Lage.
|
// Keramikterrasse des Nachbarn; unterschieden wird ueber die Lage.
|
||||||
if (type === 'dekorkies' && cz < -6.6) type = 'keramik';
|
if (type === 'dekorkies' && cz < -6.6) type = 'keramik';
|
||||||
@@ -466,6 +479,7 @@ const site = {
|
|||||||
lamps,
|
lamps,
|
||||||
walls,
|
walls,
|
||||||
props: PROPS,
|
props: PROPS,
|
||||||
|
linear: LINEAR,
|
||||||
stelen,
|
stelen,
|
||||||
building: BUILDING,
|
building: BUILDING,
|
||||||
neighbourBuildings: NEIGHBOUR_BUILDINGS,
|
neighbourBuildings: NEIGHBOUR_BUILDINGS,
|
||||||
@@ -487,6 +501,7 @@ console.log(`Lose Travertinplatten 40x60: ${slabs.length}`);
|
|||||||
console.log(`Pflanzen ${plants.length}, Baeume ${trees.length}, Leuchten ${lamps.length}, Mauern ${walls.length}, Objekte ${PROPS.length}`);
|
console.log(`Pflanzen ${plants.length}, Baeume ${trees.length}, Leuchten ${lamps.length}, Mauern ${walls.length}, Objekte ${PROPS.length}`);
|
||||||
const bySide = {};
|
const bySide = {};
|
||||||
for (const s of stelen) bySide[s.side] = (bySide[s.side] ?? 0) + 1;
|
for (const s of stelen) bySide[s.side] = (bySide[s.side] ?? 0) + 1;
|
||||||
|
console.log(`Lineare Bauteile ${LINEAR.length}: ` + LINEAR.map((l) => l.kind).join(', '));
|
||||||
console.log(`Travertinstelen ${stelen.length}: ` + Object.entries(bySide).map(([k, v]) => `${k} ${v} Stk auf ${STELE_HEIGHTS.find((r) => r.id === k)?.height ?? '?'} m`).join(', '));
|
console.log(`Travertinstelen ${stelen.length}: ` + Object.entries(bySide).map(([k, v]) => `${k} ${v} Stk auf ${STELE_HEIGHTS.find((r) => r.id === k)?.height ?? '?'} m`).join(', '));
|
||||||
console.log(`Hoehenraster ${nx} x ${nz} (${CELL} m), Koten ${terrainSpots.length}`);
|
console.log(`Hoehenraster ${nx} x ${nz} (${CELL} m), Koten ${terrainSpots.length}`);
|
||||||
const hs = Array.from(smoothed);
|
const hs = Array.from(smoothed);
|
||||||
|
|||||||
Reference in New Issue
Block a user