v0.4.6: binnenspots per unit, omgeving als keuzemenu (Ontwerp/Dag/Nacht), lampen 15% zachter

- LightingModel.computeIndoor: plafondraster per unit (800-1500 h.o.h., 600 van wand/naad)
- 3D, plattegrond, render, totalen en PDF
- tegelvloer 20% groter; gras volledig verwijderd

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-09-30 12:44:06 +02:00
co-authored by Claude Opus 5.5
parent 7ae76fe390
commit 5ecf3e0c2a
9 changed files with 142 additions and 38 deletions
+27 -13
View File
@@ -5,7 +5,7 @@
* Zie LICENTIE.md.
*/
// ============================================================
// core/skybox.js — omgeving voor de 3D-weergaven ("Lucht": geen/dag/nacht).
// 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
@@ -28,6 +28,7 @@ window.Skybox = (() => {
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 RIM_HEIGHT_M = 0.10, RIM_THICK_M = 0.025;
const NIGHT_FACTOR = 0.15; // omgevingslicht 's nachts
const SUN_OFFSET = { x: 8, y: 14, z: 6 };
@@ -130,7 +131,7 @@ window.Skybox = (() => {
if (box.isEmpty()) return;
const c = box.getCenter(new THREE.Vector3());
group.position.set(c.x, groundY, c.z);
setTerrace(Math.hypot(box.max.x - box.min.x, box.max.z - box.min.z) / 2 + MARGIN_M);
setTerrace((Math.hypot(box.max.x - box.min.x, box.max.z - box.min.z) / 2 + MARGIN_M) * SIZE_FACTOR);
}
};
}
@@ -176,26 +177,39 @@ window.Skybox = (() => {
if (o.shadowRoot) o.shadowRoot.traverse(obj => { if (obj.isMesh) obj.castShadow = on; });
}
// Knop in een 3D-werkbalk die door geen -> dag -> nacht wisselt.
const LABELS = { none: ['cloud', 'Lucht'], day: ['sun', 'Dag'], night: ['moon-stars', 'Nacht'] };
// 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) {
const btn = document.createElement('button');
btn.type = 'button';
btn.title = 'Omgeving: geen / dag / nacht';
let m = getMode();
const wrap = document.createElement('div');
wrap.className = 'view-filter';
wrap.innerHTML = `<button type="button" class="btn-3d-toggle vf-btn" title="Omgeving"></button>
<div class="vf-menu">${MODES.map(k => `<div class="vf-item vf-option" data-mode="${k}"><i class="bi bi-${LABELS[k][0]}"></i>${LABELS[k][1]}</div>`).join('')}</div>`;
toolbar.appendChild(wrap);
const btn = wrap.querySelector('.vf-btn'), menu = wrap.querySelector('.vf-menu');
const sync = () => {
btn.className = 'btn-3d-toggle' + (m !== 'none' ? ' active' : '');
btn.innerHTML = `<i class="bi bi-${LABELS[m][0]}"></i>${LABELS[m][1]}`;
btn.innerHTML = `<i class="bi bi-${LABELS[m][0]}"></i>${LABELS[m][1]}<i class="bi bi-chevron-down"></i>`;
menu.querySelectorAll('.vf-option').forEach(o => o.classList.toggle('active', o.dataset.mode === m));
};
btn.addEventListener('click', () => {
m = MODES[(MODES.indexOf(m) + 1) % MODES.length];
btn.addEventListener('click', (e) => { e.stopPropagation(); menu.classList.toggle('open'); });
menu.addEventListener('click', (e) => {
e.stopPropagation();
const opt = e.target.closest('.vf-option');
if (!opt) return;
menu.classList.remove('open');
if (opt.dataset.mode === m) return;
m = opt.dataset.mode;
remember(m);
sync();
onChange(m);
});
const closeOnOutside = () => {
if (!document.body.contains(wrap)) { document.removeEventListener('click', closeOnOutside); return; }
menu.classList.remove('open');
};
document.addEventListener('click', closeOnOutside);
sync();
toolbar.appendChild(btn);
return btn;
return wrap;
}
return { CAMERA_HEIGHT_M, NIGHT_FACTOR, createEnvironment, apply, getMode, lightFactor, createToggle };