diff --git a/README.md b/README.md index 1c063ac..9748b56 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,13 @@ assets/ logo (preloader) + bestemmingsmap voor toekomstige unitrand bij een naad); spots te dicht bij een binnenwand vervallen. Woning-instellingen › Verlichting › Binnenspots. In 3D (filter Lampen), plattegrond (open cirkeltje), render en totalen. +- **Startpunt gevellampen (v0.4.8).** Een gevel telt vanaf de zichtbare, + afgewerkte hoek: elk uiteinde + afwerking van de haakse wand (52 mm bij + vochtdoek), plus de bedekte kopse kant (skinExt) bij tussenpassende wanden. + Delen van een gevel achter een andere wand (bijv. een wandstuk dat de naad + in loopt om een binnenhoek te sluiten, zoals A-W2 bij een L) worden + afgetrokken via de voetafdruk van die wand; de gevel begint dan bij diens + afgewerkte buitenkant. - **Lichtbronnen (v0.4.7).** Geen echte schaduw per lamp (één schaduwkaart per lamp overschrijdt de browserlimiet); licht door muren wordt geometrisch voorkomen: buitenlampen en boeideel-spots zijn half hun bundelhoek naar diff --git a/js/app.js b/js/app.js index 0d4eeb5..acb3bae 100644 --- a/js/app.js +++ b/js/app.js @@ -4,7 +4,7 @@ * Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming. * Zie LICENTIE.md. */ -const APP_VERSION = '0.4.7'; +const APP_VERSION = '0.4.8'; // 0.0.X per kleine iteratie, 0.X.0 per mijlpaal, X.0.0 bij live-gang. let tabs = []; diff --git a/js/core/geometry.js b/js/core/geometry.js index 8442909..5bc5dc8 100644 --- a/js/core/geometry.js +++ b/js/core/geometry.js @@ -329,7 +329,7 @@ function offsetRectilinearLoop(pts, d) { } // Gevels: groepeert lijnstukken in de plattegrond tot doorlopende gevels -// (zelfde as + normaal + gevelvlak, aansluitend). segs: [{ axis ('x'|'z'), +// (zelfde as + normaal + gevelvlak, aansluitend of overlappend). segs: [{ axis ('x'|'z'), // normal {x, z}, line (positie op de andere as), s0, s1 (s0 < s1) }, ...]. // Geeft [{ axis, normal, line, s0, s1, segs (op s0 gesorteerd) }]. // Gebruikt door de woning (latwerk/doek-patroon) en de buitenverlichting. @@ -338,7 +338,7 @@ function groupFacadeRuns(segs, opts) { const runs = []; segs.slice().sort((a, b) => a.s0 - b.s0).forEach(sg => { const run = runs.find(r => r.axis === sg.axis && Math.abs(r.normal.x - sg.normal.x) < 1e-6 && Math.abs(r.normal.z - sg.normal.z) < 1e-6 && - Math.abs(r.line - sg.line) < lineTol && Math.abs(r.s1 - sg.s0) < gapTol); + Math.abs(r.line - sg.line) < lineTol && sg.s0 <= r.s1 + gapTol); // aansluitend of overlappend if (run) { run.segs.push(sg); run.s1 = Math.max(run.s1, sg.s1); } else runs.push(Object.assign({}, sg, { s0: sg.s0, s1: sg.s1, segs: [sg] })); }); diff --git a/js/lighting/lighting-model.js b/js/lighting/lighting-model.js index daac5c8..fd1d9e8 100644 --- a/js/lighting/lighting-model.js +++ b/js/lighting/lighting-model.js @@ -126,9 +126,23 @@ window.LightingModel = (() => { const axis = Math.abs(normal.z) > 0.5 ? 'x' : 'z'; const perp = axis === 'x' ? 'z' : 'x'; const q0 = UnitModel.localToWorld(p.place, -W / 2, T / 2), q1 = UnitModel.localToWorld(p.place, W / 2, T / 2); + // Gevel loopt tot de afgewerkte hoek, zodat endOffset altijd vanaf de + // zichtbare hoek telt: elk uiteinde + afwerking (latwerk + bekleding van + // de haakse wand die eroverheen loopt), en waar dit stuk de kopse kant + // van een doorlopende wand bedekt (skinExt) ook nog die wanddikte. + // Binnenhoeken en naden: zie de voetafdrukken in 3 en de overlap in 2. + const c = WallModel.claddingOffset(p.buitenMateriaal), e = p.skinExt || {}; + const extL = (e.left > 0 ? e.left : 0) + c, extR = (e.right > 0 ? e.right : 0) + c; + const up = q1[axis] >= q0[axis]; + // Voetafdruk van dit stuk incl. afwerking (om bedekte delen van andere + // gevels af te trekken, zie 3). + const corners = [[-W / 2, -p.place.thickness / 2], [W / 2, -p.place.thickness / 2], [-W / 2, T / 2], [W / 2, T / 2]] + .map(([lx, lz]) => UnitModel.localToWorld(p.place, lx, lz)); segs.push({ piece: p, normal, axis, perp, line: q0[perp], - sAt0: q0[axis], sAtW: q1[axis], s0: Math.min(q0[axis], q1[axis]), s1: Math.max(q0[axis], q1[axis]), + sAt0: q0[axis], sAtW: q1[axis], + s0: up ? q0[axis] - extL : q1[axis] - extR, s1: up ? q1[axis] + extR : q0[axis] + extL, + foot: { x0: Math.min(...corners.map(q => q.x)), x1: Math.max(...corners.map(q => q.x)), z0: Math.min(...corners.map(q => q.z)), z1: Math.max(...corners.map(q => q.z)) }, override: overrides[u.id][w.side] || 'auto' }); }))); @@ -153,6 +167,16 @@ window.LightingModel = (() => { runs.forEach(run => { const blocked = []; const pos = []; // lampposities langs deze gevel (wereld-mm) + // Delen van deze gevel die achter een andere wand liggen (bijv. het + // stuk van een wand dat de naad in loopt om een binnenhoek te sluiten) + // zijn geen zichtbare gevel: de gevel begint pas bij de afgewerkte + // buitenkant van die andere wand. + segs.filter(o => !run.segs.includes(o)).forEach(o => { + const f = o.foot; + const pMin = run.axis === 'x' ? f.z0 : f.x0, pMax = run.axis === 'x' ? f.z1 : f.x1; + const aMin = run.axis === 'x' ? f.x0 : f.z0, aMax = run.axis === 'x' ? f.x1 : f.z1; + if (run.line > pMin + 1 && run.line < pMax - 1 && aMax > run.s0 && aMin < run.s1) blocked.push([Math.max(run.s0, aMin), Math.min(run.s1, aMax)]); + }); run.segs.forEach(sg => { const cfg = sg.piece.config, sW = cfg.studWidth; const low = Math.min(cfg.wallHeightLeft, cfg.wallHeightRight) < H + 100;