v0.6.0: ruimtes (verfemmer: functie, netto m², vloerafwerking, FIBO-wandpanelen, eigen verfkleur, vals plafond, ruimtestaat op de PDF), inrichting (sanitair, keuken, kookeiland, kasten, bed, zithoek, boiler; slepen met snappen op het wandvlak, draaien, kosten), kozijnen met deurrichting, glasdeur en schuifdeuren (inbouw/opbouw, deuren open in de loopmodus) plus dagkanten, gevel stucwerk en wandstukken splitsen met eigen gevel, dak over de hele woning met overkapping, geschilderd plafond, model Amstel als nieuw ontwerp, kosten en prijzen uit kosten/intern.csv en kosten/leveranciers.csv (CostCsv); easter egg verwijderd; overbodige comments opgeschoond (alleen copyright, uitzonderingen en valkuilen blijven)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-10-11 00:34:27 +02:00
co-authored by Claude Opus 5.5
parent b5510d56c7
commit ceda0b554d
45 changed files with 4117 additions and 3700 deletions
+10 -31
View File
@@ -4,33 +4,20 @@
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
* Zie LICENTIE.md.
*/
// ============================================================
// core/skybox.js — omgeving voor de 3D-weergaven (Ontwerp / Dag / Nacht).
// Een panorama (assets/skybox.webp of skybox-night.webp) wordt op de grond
// geprojecteerd, zodat het object op de grond staat; onder het object ligt
// een ronde tegelvloer (assets/tiles.jpg) met een witte opstaande rand, groot
// genoeg voor het hele object. Vloer en rand reageren op het licht en vangen
// de schaduw op. Nacht: omgevingslicht op NIGHT_FACTOR (lampen blijven 100%).
//
// Grondprojectie: port van three.js GroundedSkybox (r157+, MIT; wij draaien
// r128): een binnenstebuiten bol waarvan de onderhelft plat gedrukt wordt op
// een vloer op CAMERA_HEIGHT_M onder het midden van de bol.
//
// Vereist een webserver (texturen via TextureLoader). Plain script.
// ============================================================
// Grondprojectie: port van three.js GroundedSkybox (r157+, MIT); wij draaien r128.
window.Skybox = (() => {
const PREF_KEY = 'tectum.sky';
const MODES = ['none', 'day', 'night'];
const SKY_URLS = { day: 'assets/skybox.webp', night: 'assets/skybox-night.webp' };
const TILES_URL = 'assets/tiles.jpg';
const CAMERA_HEIGHT_M = 1.7; // hoogte van de panoramacamera boven de grond
const RADIUS_M = 60; // straal van de omgevingsbol
const TILE_M = 2.4; // één tegeltextuur (1024 px) beslaat zoveel meter
const MARGIN_M = 1.5; // vrije rand tussen object en opstaande rand
const SIZE_FACTOR = 1.2; // tegelvloer 20% ruimer dan object + marge
const CAMERA_HEIGHT_M = 1.7;
const RADIUS_M = 60;
const TILE_M = 2.4;
const MARGIN_M = 1.5;
const SIZE_FACTOR = 1.2;
const RIM_HEIGHT_M = 0.10, RIM_THICK_M = 0.025;
const NIGHT_FACTOR = 0.15; // omgevingslicht 's nachts
const NIGHT_FACTOR = 0.15;
const SUN_OFFSET = { x: 8, y: 14, z: 6 };
const cache = {};
@@ -51,7 +38,6 @@ window.Skybox = (() => {
for (let i = 0; i < pos.count; i++) {
v.fromBufferAttribute(pos, i);
if (v.y < 0) {
// Onderhelft plat op y = -h; vloeiende overgang naar de bol.
const f = v.y < y1 ? -h / v.y : (1 - v.y * v.y / (3 * y1 * y1));
v.multiplyScalar(f);
pos.setXYZ(i, v.x, v.y, v.z);
@@ -59,12 +45,11 @@ window.Skybox = (() => {
}
pos.needsUpdate = true;
const mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ map, depthWrite: false }));
mesh.position.y = h - 0.01; // vlakke vloer net onder de tegelvloer
mesh.position.y = h - 0.01;
mesh.renderOrder = -1;
return mesh;
}
// Ronde tegelvloer (straal r) + witte opstaande rand erlangs.
function buildTerrace(map, r) {
const group = new THREE.Group();
const tex = map.clone();
@@ -86,13 +71,12 @@ window.Skybox = (() => {
new THREE.ExtrudeGeometry(ring, { depth: RIM_HEIGHT_M, bevelEnabled: false, curveSegments: 128 }),
new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.5 })
);
rim.rotation.x = -Math.PI / 2; // extrusie omhoog
rim.rotation.x = -Math.PI / 2;
rim.castShadow = rim.receiveShadow = true;
group.add(rim);
return group;
}
// Omgeving voor één scene: { group, setMode(mode), fitTo(object3D, groundY) }.
function createEnvironment(scene) {
const group = new THREE.Group();
group.visible = false;
@@ -124,7 +108,6 @@ window.Skybox = (() => {
return {
group,
setMode(m) { mode = m; group.visible = m !== 'none'; showSky(); },
// Midden en straal uit de omhullende van het object (horizontaal), vloer op groundY (m).
fitTo(object3D, groundY) {
if (!object3D) return;
const box = new THREE.Box3().setFromObject(object3D);
@@ -144,14 +127,11 @@ window.Skybox = (() => {
} catch (e) { return 'none'; }
}
function remember(m) {
try { localStorage.setItem(PREF_KEY, m); } catch (e) { /* geen opslag: alleen deze sessie */ }
try { localStorage.setItem(PREF_KEY, m); } catch (e) { }
}
const lightFactor = (m) => m === 'night' ? NIGHT_FACTOR : 1;
// Omgeving toepassen: lucht/vloer, raster, camera niet onder de grond, zon
// met schaduw, en het licht (nacht: NIGHT_FACTOR).
// o: { env, grid, controls, sun, ambient, base: { ambient, sun }, shadowRoot }
function apply(m, o) {
const on = m !== 'none';
o.env.setMode(m);
@@ -177,7 +157,6 @@ window.Skybox = (() => {
if (o.shadowRoot) o.shadowRoot.traverse(obj => { if (obj.isMesh) obj.castShadow = on; });
}
// Keuzemenu in een 3D-werkbalk: Ontwerp (geen omgeving) / Dag / Nacht.
const LABELS = { none: ['pencil-square', 'Ontwerp'], day: ['sun', 'Dag'], night: ['moon-stars', 'Nacht'] };
function createToggle(toolbar, onChange) {
let m = getMode();