v0.7.6: geen wolfsdak bij een overkapping (veranda, inpandig of uitpandig: het dak van een unit steekt buiten zijn vloer, HouseModel.hasCover): de keuze verdwijnt in configurator en light, een bestaand wolfsdak wordt een zadeldak met dezelfde goot en nok; afdekker op een stenen trasraam onder rabat, felsplaten of stuc (WallModel.TRAS_CAP): zetwerk achter de bekleding, 5° aflopend, neus 25 mm over de steen, onderbroken bij openingen, bij een buitenhoek door tot voorbij de buursteen, zwart/antraciet/wit (WallModel.TRIM_COLORS), in 3D, PDF-aanzichten, materialen, rapport en kosten; ventilatierooster van 40 mm op de gevel direct onder de onderplaat van alle boeidelen (RoofModel.VENT, niet boven een veranda of tegen een hogere gevel), sleuven in de textuur, aan/uit en kleur bij Dak › Boeideel, in 3D, PDF, rapport en kosten; light-versie: boeidelen of daktrim met kleur boeidelen (Dak) en één zetwerk-kleur voor afdekker en rooster (Gevel); 3D zijwaarts schuiven ook met de middelste muisknop, Shift/Ctrl/⌘ + slepen en de pijltjestoetsen met de muis boven de weergave (configurator en light, hints en sneltoetsenoverzicht bijgewerkt)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-10-14 13:45:26 +02:00
co-authored by Claude Opus 5.5
parent 432e0f494a
commit 118ccf9098
12 changed files with 279 additions and 43 deletions
+23
View File
@@ -271,6 +271,12 @@ window.HousePdf = (() => {
const trColor = WallModel.isStoneFinish(tr.finish) ? (WallModel.STONE_COLORS[tr.finish] || '#a0724f') : VIEW_COLORS.cementplaat;
const zT = p.place.thickness / 2 + (WallModel.isStoneFinish(tr.finish) ? WallModel.STONE.THICK_MM : 12);
faces.push({ kind: 'trasraam', pts: band.map(q => toWorld(p, q.x, q.y, zT)), n, color: trColor, bias: 1 });
// Afdekker: neus over de bovenkant van de steen (alleen in 3D onderbroken bij openingen).
if (p.stats && p.stats.trasraamCapM > 0) {
const C = WallModel.TRAS_CAP, yLo = tr.top - C.DROP_MM, yHi = tr.top + C.THICK_MM;
faces.push({ kind: 'trasraam', pts: [[-W / 2, yLo], [W / 2, yLo], [W / 2, yHi], [-W / 2, yHi]].map(([lx, ly]) => toWorld(p, lx, ly, zT + C.GAP_MM + C.THICK_MM)),
n, color: WallModel.trimHex(tr.capColor), bias: 1.5 });
}
}
(p.config.openings || []).filter(op => op.type !== 'schoorsteen').forEach(op => {
if (WallModel.openingTopMax(op) <= y0) return;
@@ -328,6 +334,17 @@ window.HousePdf = (() => {
top.push({ x: O.x, y: Math.max(tI, tO), z: O.z });
bottom.push({ x: O.x, y: Math.min(tI, tO) - h, z: O.z });
}
// Rooster op de gevel, onder de onderplaat.
if (sg.fascia && sg.facadeUnder && edge.vent) {
const V = RoofModel.VENT, d = RoofModel.ventFace(sg) + V.THICK_MM - (sg.a.x * sg.normal.x + sg.a.z * sg.normal.z);
const vTop = [];
for (let k = 0; k <= n; k++) {
const I = lerp(sg.a, sg.b, k / n), q = { x: I.x + sg.normal.x * d, z: I.z + sg.normal.z * d };
vTop.push({ x: q.x, y: sg.topAt(q) - h, z: q.z });
}
faces.push({ kind: 'dakrand', pts: vTop.concat(vTop.map(q => ({ x: q.x, y: q.y - V.HEIGHT_MM, z: q.z })).reverse()),
n: { x: sg.normal.x, z: sg.normal.z }, color: WallModel.trimHex(edge.ventColor), bias: 2.5 });
}
faces.push({ kind: 'dakrand', pts: top.concat(bottom.reverse()), n: { x: sg.normal.x, z: sg.normal.z }, color: RoofModel.edgeColor(edge, sg), bias: 3 });
});
}
@@ -371,7 +388,11 @@ window.HousePdf = (() => {
items.push(WallModel.isStoneFinish(tf)
? { label: `Trasraam (${st.trasraam.height} mm)`, value: `Steen ${tf}`, thumb: WallModel.stoneThumbUrl(tf, 64), color: WallModel.STONE_COLORS[tf] }
: { label: `Trasraam (${st.trasraam.height} mm)`, value: 'Cementplaat', color: VIEW_COLORS.cementplaat });
const tc = WallModel.TRIM_COLORS[st.trasraam.capColor];
if (parts && tc && HouseModel.allWallPieces(parts).some(p => p.stats && p.stats.trasraamCapM > 0)) items.push({ label: 'Afdekker trasraam', value: tc.label, color: tc.hex });
}
const vc = parts && parts.roofEdge && parts.roofEdge.vent && parts.roofEdge.ventLengthMM > 0 && WallModel.TRIM_COLORS[parts.roofEdge.ventColor];
if (vc) items.push({ label: 'Ventilatierooster', value: vc.label, color: vc.hex });
(parts ? parts.units : state.units).forEach(u => {
const cov = parts ? u.roof.covering : (u.roof.covering || 'dakleer');
const c = RoofModel.COVERINGS[cov] || RoofModel.COVERINGS.dakleer;
@@ -618,6 +639,8 @@ window.HousePdf = (() => {
rows.push(['Onderplaat boeideel', fmt(e.soffitM2, 2) + ' m2']);
}
if (e.trimLengthM > 0) rows.push(['Daktrim', `${fmt(e.trimLengthM, 2)} m, ${e.trimHeight} mm hoog`]);
if (e.ventLengthM > 0) rows.push(['Ventilatierooster', `${fmt(e.ventLengthM, 2)} m op de gevel onder de boeidelen, ${RoofModel.VENT.HEIGHT_MM} mm hoog, ${e.ventColor.toLowerCase()}`]);
if (totals.trasraamCap && totals.trasraamCap.lengthM > 0) rows.push(['Afdekker trasraam', `${fmt(totals.trasraamCap.lengthM, 2)} m, ${totals.trasraamCap.color.toLowerCase()}`]);
rows.push(['Boeideel-spots', e.spots + ' stuks']);
rows.push(['Gevellampen', `${L.total} stuks (boven+onder ${L.both}, onder ${L.down}, boven ${L.up})`]);
rows.push(['Binnenspots', `${L.indoor} stuks (plafond)`]);