v0.4.13: opbouw buitenwand boven lager dak, latwerk-raster per gevel, dakrand 80 mm boven dakleer, lage rand loopt door

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-09-30 15:33:47 +02:00
co-authored by Claude Opus 5.5
parent 373c2c2fb7
commit 94a5215f82
8 changed files with 207 additions and 46 deletions
+32 -11
View File
@@ -28,7 +28,9 @@
// dakrand = standaard de buitenkant gevel), standaard 300 mm hoog.
// Spots in de onderplaat staan op 40% van de overstek (spotLine); welke
// spots er komen bepaalt LightingModel.
// - zonder boeidelen: een daktrim van 80 mm hoog.
// - zonder boeidelen: een daktrim van 80 mm hoog (onder het dakleer).
// De bovenkant van boeideel en daktrim ligt EDGE_ABOVE_MM boven het dakleer
// (opstand rondom), dus het dakleer ligt altijd lager dan de rand.
//
// Plain script, geladen na floor-model.js en core/geometry.js.
// ============================================================
@@ -37,7 +39,7 @@ window.RoofModel = (() => {
const DAKLEER_MM = 2; // visuele dikte van het dakleer
const DENSITY_DAKLEER_KGM2 = 1.4; // EPDM ~1,4 kg/m2
const TRIM_THICK_MM = 25; // daktrim-profiel, uitstekend buiten de dakrand
const TRIM_ABOVE_MM = 30; // daktrim steekt zoveel boven het dakleer uit
const EDGE_ABOVE_MM = 80; // bovenkant boeideel/daktrim ligt zoveel boven het dakleer
const SPOT_RATIO = 0.4; // spots op 40% van de overstek, gemeten vanaf de gevel
const OVERHANG_MIN = 200, OVERHANG_MAX = 400;
@@ -357,17 +359,35 @@ window.RoofModel = (() => {
return {
a, b, owner: owner.unitId, fascia, normal: rectilinearNormal(a, b),
outerA: off[i], outerB: off[j], spotA: offSpot[i], spotB: offSpot[j],
topAt: (p) => owner.topAt(p.x, p.z), ridges: owner.ridges || [],
// Bovenkant van de rand (dakleer + opstand).
topAt: (p) => owner.topAt(p.x, p.z) + EDGE_ABOVE_MM, ridges: owner.ridges || [],
overRoof: !!nb && !dropped, dropped
};
});
// Naast een vervallen ribbe loopt de rand recht door tot de gevel.
// Naast een vervallen ribbe (tegen een hoger dak):
// - ligt het verlengde van de rand in het hoge deel, dan loopt hij
// recht door tot die gevel (spot op spotafstand van de gevel);
// - ligt het verlengde ernaast (de rand loopt langs de zijgevel van
// het hoge deel), dan loopt hij recht door tot de overstek, onder
// de rand van het hoge dak (van bovenaf overlappen ze).
const insideOther = (p) => others.some(r => p.x > r.rect.x0 + 0.5 && p.x < r.rect.x1 - 0.5 && p.z > r.rect.z0 + 0.5 && p.z < r.rect.z1 - 0.5);
segs.forEach((sg, i) => {
if (sg.dropped) return;
const prev = segs[(i - 1 + n) % n], next = segs[(i + 1) % n];
const alongZ = Math.abs(sg.a.x - sg.b.x) < 0.5;
const square = (key, v) => { sg[key] = alongZ ? { x: sg[key].x, z: v.z } : { x: v.x, z: sg[key].z }; };
if (!sg.dropped && prev.dropped) square('outerA', sg.a);
if (!sg.dropped && next.dropped) square('outerB', sg.b);
const at = (q, v) => alongZ ? { x: q.x, z: v.z } : { x: v.x, z: q.z };
[['a', 'outerA', 'spotA', 'b', prev], ['b', 'outerB', 'spotB', 'a', next]].forEach(([k, outerK, spotK, otherK, nb]) => {
if (!nb.dropped) return;
const v = sg[k], corner = sg[outerK];
if (insideOther({ x: (v.x + corner.x) / 2, z: (v.z + corner.z) / 2 })) {
sg[outerK] = at(corner, v);
const dir = Math.sign(alongZ ? sg[otherK].z - v.z : sg[otherK].x - v.x);
const inset = dir * s.overhang * SPOT_RATIO;
sg[spotK] = alongZ ? { x: sg[spotK].x, z: v.z + inset } : { x: v.x + inset, z: sg[spotK].z };
} else {
sg[k] = at(v, corner);
}
});
});
segs.filter(sg => !sg.dropped).forEach(sg => segments.push(sg));
});
@@ -377,7 +397,7 @@ window.RoofModel = (() => {
return {
segments, overhang: s.overhang, spotRatio: SPOT_RATIO,
fasciaHeight: s.fasciaHeight, trimHeight: s.trimHeight,
top, trimAbove: TRIM_ABOVE_MM,
top, edgeAbove: EDGE_ABOVE_MM,
fasciaLengthMM: segments.filter(sg => sg.fascia).reduce((t, sg) => t + segLen(sg), 0),
trimLengthMM: segments.filter(sg => !sg.fascia).reduce((t, sg) => t + segLen(sg), 0),
soffitM2: segments.filter(sg => sg.fascia).reduce((t, sg) => t + quadArea([sg.a, sg.b, sg.outerB, sg.outerA]), 0) / 1e6,
@@ -431,10 +451,11 @@ window.RoofModel = (() => {
const f = (p) => r.axis === 'x' ? p.x - r.value : p.z - r.value;
polys = polys.flatMap(pl => [clipPoly(pl, f), clipPoly(pl, p => -f(p))]).filter(pl => pl.length >= 3);
});
const h = sg.fascia ? edge.fasciaHeight : edge.trimHeight;
const topFn = sg.fascia ? sg.topAt : (p) => sg.topAt(p) + edge.trimAbove;
// Boeideel: totale hoogte fasciaHeight vanaf de bovenkant van de rand;
// daktrim: trimHeight onder het dakleer + de opstand erboven.
const h = sg.fascia ? edge.fasciaHeight : edge.trimHeight + edge.edgeAbove;
polys.forEach(pl => {
const mesh = new THREE.Mesh(prismGeometry(pl, topFn, h), sg.fascia ? materials.fasciaMaterial : materials.trimMaterial);
const mesh = new THREE.Mesh(prismGeometry(pl, sg.topAt, h), sg.fascia ? materials.fasciaMaterial : materials.trimMaterial);
mesh.visible = o.edgeVisible !== false;
group.add(mesh);
});