v0.4.10: wandhoogtes gekoppeld, middenpunt (nok), binnenspots langs naadwanden

- WallModel: optioneel middenpunt (heightMid, topLine), bovenregels in verstek;
  stijlen, openingen, beplating, doek, latwerk, BOM, 2D en PDF volgen de knik
- wand-editor: schakelaar "Middenpunt (nok)" + nokhoogte, links/rechts = goothoogte
- UnitModel.syncWallHeights: tegenoverliggende wand gespiegeld, haakse wanden
  vlak op de hoekhoogtes; wallPieces met nok per stuk
- LightingModel.computeIndoor: raster vanaf binnenzijde eigen naadwanddeel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-09-30 13:35:21 +02:00
co-authored by Claude Opus 5.5
parent 9bd7faeaa6
commit 9126588df7
9 changed files with 295 additions and 135 deletions
+17 -22
View File
@@ -67,7 +67,7 @@ window.WallPdf = (() => {
// --- Materiaal binnen/buiten + isolatie ---
let my = 240;
const midH = Math.round((CONFIG.wallHeightLeft + CONFIG.wallHeightRight) / 2);
const midH = Math.round(WallModel.topLine(CONFIG).maxY);
doc.setFontSize(8.5);
doc.text('Materiaal binnen', tblX[0], my); my += 4.5;
doc.setFontSize(6.5);
@@ -103,7 +103,8 @@ window.WallPdf = (() => {
const availW = (drawX1 - drawX0) - marginSides * 2;
const availH = (drawY1 - drawY0) - marginTop - marginBottom;
const wallWidth = CONFIG.wallWidth, hLeft = CONFIG.wallHeightLeft, hRight = CONFIG.wallHeightRight;
const maxH = Math.max(hLeft, hRight);
const tl = WallModel.topLine(CONFIG);
const maxH = tl.maxY;
const scale = Math.min(availW / wallWidth, availH / maxH);
const originX = drawX0 + marginSides;
@@ -115,15 +116,18 @@ window.WallPdf = (() => {
doc.setLineWidth(0.25);
doc.line(px(-wallWidth / 2), py(0), px(wallWidth / 2), py(0));
doc.line(px(wallWidth / 2), py(0), px(wallWidth / 2), py(hRight));
doc.line(px(wallWidth / 2), py(hRight), px(-wallWidth / 2), py(hLeft));
for (let i = 0; i < tl.pts.length - 1; i++) doc.line(px(tl.pts[i].x), py(tl.pts[i].y), px(tl.pts[i + 1].x), py(tl.pts[i + 1].y));
doc.line(px(-wallWidth / 2), py(hLeft), px(-wallWidth / 2), py(0));
doc.setDrawColor(...RED);
doc.setLineWidth(0.3);
pieces.forEach(p => {
if (p.kind === 'bovenregel') return; // apart getekend hieronder, volgt de helling
const isVertical = p.kind === 'verticale balk' || p.kind === 'kreupele stijl';
if (isVertical) {
if (p.outline) {
// Werkelijke omtrek: schuine kop (volgt de daklijn, evt. met knik
// onder de nok) resp. bovenregel in verstek.
const c = p.outline.map(q => [px(q.x), py(q.y)]);
c.forEach((q, i) => { const n = c[(i + 1) % c.length]; doc.line(q[0], q[1], n[0], n[1]); });
} else if (p.kind === 'verticale balk' || p.kind === 'kreupele stijl') {
// Viervlak i.p.v. rechthoek: de bovenkant loopt schuin mee met de
// daklijn (yTopLeft/yTopRight kunnen verschillen), zodat zowel de
// hoogste als de laagste kant van de balk op de juiste plek komt.
@@ -143,22 +147,6 @@ window.WallPdf = (() => {
doc.text(String(p.posnum), px(p.x), py(p.y), { align: 'center' });
});
// Bovenregel: volgt de dakhelling, dus als schuin parallellogram
// getekend (niet als vlakke rechthoek zoals de rest hierboven).
const topPlatePiece = pieces.find(p => p.kind === 'bovenregel');
if (topPlatePiece) {
const slope = (hRight - hLeft) / wallWidth;
const vOff = CONFIG.plateThick * Math.sqrt(1 + slope * slope);
const tp = [
[px(-wallWidth / 2), py(hLeft)], [px(wallWidth / 2), py(hRight)],
[px(wallWidth / 2), py(hRight - vOff)], [px(-wallWidth / 2), py(hLeft - vOff)]
];
for (let i = 0; i < 4; i++) doc.line(tp[i][0], tp[i][1], tp[(i + 1) % 4][0], tp[(i + 1) % 4][1]);
doc.setTextColor(...BLACK);
doc.setFontSize(5);
doc.text(String(topPlatePiece.posnum), px(topPlatePiece.x), py(topPlatePiece.y), { align: 'center' });
}
doc.setDrawColor(...BLACK);
doc.setLineWidth(0.2);
CONFIG.openings.forEach(op => {
@@ -188,6 +176,13 @@ window.WallPdf = (() => {
doc.text(String(Math.round(hLeft)), px(-wallWidth / 2) - 9, (py(0) + py(hLeft)) / 2, { align: 'center', angle: 90 });
drawDimLine(px(wallWidth / 2) + 7, py(0), px(wallWidth / 2) + 7, py(hRight));
doc.text(String(Math.round(hRight)), px(wallWidth / 2) + 11, (py(0) + py(hRight)) / 2, { align: 'center', angle: 90 });
if (tl.peak) {
// Nokhoogte: stippellijn van vloer tot nok.
doc.setLineDashPattern([1, 1], 0);
doc.line(px(tl.peak.x), py(0), px(tl.peak.x), py(tl.peak.y));
doc.setLineDashPattern([], 0);
doc.text('nok ' + Math.round(tl.peak.y), px(tl.peak.x) + 1.5, py(tl.peak.y) + 4);
}
const studEdges = new Set([0, wallWidth]);
calcData.fullStuds.forEach(x => studEdges.add(Math.round(x - CONFIG.studWidth / 2 + wallWidth / 2)));