v0.4.8: gevellampen tellen vanaf de zichtbare afgewerkte hoek

- bedekte gevelstukken (wandstuk in de naad, binnenhoek L) afgetrokken via voetafdruk
- uiteinden verlengd met afwerking (+ skinExt bij tussenpassende wanden)
- groupFacadeRuns accepteert overlap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-09-30 12:54:41 +02:00
co-authored by Claude Opus 5.5
parent 448c2b1289
commit f163f4deb1
4 changed files with 35 additions and 4 deletions
+1 -1
View File
@@ -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 = [];
+2 -2
View File
@@ -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] }));
});
+25 -1
View File
@@ -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;