/*! * Tectum * Copyright © 2026 Machiel Heinen. * Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming. * Zie LICENTIE.md. */ window.FloorPdf = (() => { // Bewust altijd één doorlopende module (de voorbeelden in examples/ splitsen grote vloeren). function drawSheet(doc, calcData, resolvedOpenings, opts) { const { width, depth, joists, joistW, joistH, underlayThick } = calcData; const PAGE_H = doc.internal.pageSize.getHeight(); const { RED, BLUE, BLACK, GREY } = PdfSheet.COLORS; doc.setFont('helvetica', 'normal'); // Eén groep: alle vloerbalken hebben dezelfde doorsnede en lengte. const tblX = [8, 26, 42, 58, 76, 92]; let ty = 14; doc.setTextColor(...BLACK); doc.setFontSize(9); doc.text('Frame materiaal', tblX[0], ty); ty += 6; doc.setFontSize(6.5); ['Posnum', 'Dikte', 'Hoogte', 'Lengte', 'Aantal'].forEach((h, i) => doc.text(h, tblX[i], ty)); ty += 1.5; doc.setDrawColor(...GREY); doc.setLineWidth(0.15); doc.line(tblX[0], ty, tblX[4] + 12, ty); ty += 4; const rims = calcData.rims || {}, joistLen = calcData.joistLen || depth; const rimCount = (rims.front ? 1 : 0) + (rims.back ? 1 : 0); [String(1), String(joistW), String(joistH), String(Math.round(joistLen)), String(joists.length)] .forEach((v, i) => doc.text(v, tblX[i], ty)); if (rimCount) { ty += 4.3; [String(2), String(joistW), String(joistH), String(Math.round(width)), String(rimCount)] .forEach((v, i) => doc.text(v, tblX[i], ty)); } let my = 240; doc.setFontSize(8.5); doc.text('Plaat materiaal', tblX[0], my); my += 4.5; doc.setFontSize(6.5); ['Dikte', 'Hoogte', 'Lengte', 'Materiaal', 'Aantal'].forEach((h, i) => doc.text(h, tblX[i], my)); my += 4; [String(underlayThick), String(Math.round(depth)), String(Math.round(width)), opts.roof ? 'OSB (plafond)' : 'OSB', '1'] .forEach((v, i) => doc.text(v, tblX[i], my)); if (opts.roof) { my += 4; [String(RoofModel.DAKLEER_MM), String(Math.round(depth)), String(Math.round(width)), 'Dakleer', '1'] .forEach((v, i) => doc.text(v, tblX[i], my)); } my += 6; doc.text('Isolatie: leegte tussen de balken (spouwvullend)', tblX[0], my); PdfSheet.titleBlock(doc, opts.project, { titel: opts.titel, element: opts.element, blad: opts.blad }); const drawX0 = 95, drawX1 = 410, drawY0 = 15, drawY1 = 178; const marginSides = 25, marginTop = 15, marginBottom = 30; const availW = (drawX1 - drawX0) - marginSides * 2; const availH = (drawY1 - drawY0) - marginTop - marginBottom; const scale = Math.min(availW / width, availH / depth); const originX = drawX0 + marginSides; const topY = drawY0 + marginTop; const px = (xmm) => originX + xmm * scale; const py = (ymm) => topY + ymm * scale; doc.setDrawColor(...GREY); doc.setLineWidth(0.25); doc.rect(px(0), py(0), width * scale, depth * scale, 'S'); doc.setDrawColor(...RED); doc.setLineWidth(0.3); doc.setTextColor(...BLACK); doc.setFontSize(5); const jz0 = rims.front ? joistW : 0; joists.forEach(x => { const xLeft = x + width / 2 - joistW / 2; doc.rect(px(xLeft), py(jz0), joistW * scale, joistLen * scale, 'S'); doc.text('1', px(xLeft + joistW / 2), py(depth / 2), { align: 'center' }); }); [['front', 0], ['back', depth - joistW]].filter(([k]) => rims[k]).forEach(([, z]) => { doc.rect(px(0), py(z), width * scale, joistW * scale, 'S'); doc.text('2', px(width / 2), py(z + joistW / 2) + 1.2, { align: 'center' }); }); doc.setDrawColor(...RED); doc.setLineWidth(0.2); resolvedOpenings.filter(o => !o.removed).forEach(o => { const cx = px(o.x + width / 2), cy = py(o.y); doc.circle(cx, cy, o.r * scale, 'S'); doc.setTextColor(...BLACK); doc.setFontSize(5); doc.text('#' + o.id, cx, cy - o.r * scale - 2, { align: 'center' }); }); if (opts.overlay) opts.overlay(doc, px, py); const drawDimLine = (x1, y1, x2, y2) => PdfSheet.dimLine(doc, x1, y1, x2, y2); doc.setDrawColor(...BLUE); doc.setTextColor(...BLUE); doc.setLineWidth(0.15); doc.setFontSize(7); const dimTopY = py(0) - 7; drawDimLine(px(0), dimTopY, px(width), dimTopY); doc.text(String(Math.round(width)), (px(0) + px(width)) / 2, dimTopY - 1.5, { align: 'center' }); const dimLeftX = px(0) - 7; drawDimLine(dimLeftX, py(0), dimLeftX, py(depth)); doc.text(String(Math.round(depth)), dimLeftX - 2, (py(0) + py(depth)) / 2, { align: 'center', angle: 90 }); const joistEdges = new Set([0, width]); joists.forEach(x => joistEdges.add(Math.round(x + width / 2 - joistW / 2))); const cumPoints = [...joistEdges].filter(v => v >= 0 && v <= width).sort((a, b) => a - b); doc.setDrawColor(...BLACK); doc.setTextColor(...BLACK); doc.setLineWidth(0.1); doc.setFontSize(5.5); const segY = py(depth) + 9, cumY = py(depth) + 17; for (let i = 0; i < cumPoints.length - 1; i++) { const mid = (cumPoints[i] + cumPoints[i + 1]) / 2; doc.text(String(cumPoints[i + 1] - cumPoints[i]), px(mid), segY, { align: 'center' }); } cumPoints.forEach(v => { doc.text(String(v), px(v), cumY, { align: 'center' }); doc.line(px(v), cumY - 2.5, px(v), py(depth) + 2); }); const secY0 = Math.min(PAGE_H - 20, py(depth) + 30); const secScale = Math.min((drawX1 - drawX0 - 40) / width, 18 / (joistH + underlayThick)); const secOriginX = drawX0 + 20; const secFloorY = secY0 + (joistH + underlayThick) * secScale; const spx = (xmm) => secOriginX + xmm * secScale; const spy = (ymm) => secFloorY - ymm * secScale; const joistY0 = opts.roof ? underlayThick : 0; const osbY0 = opts.roof ? 0 : joistH; doc.setDrawColor(...RED); doc.setLineWidth(0.2); joists.forEach(x => { doc.rect(spx(x + width / 2 - joistW / 2), spy(joistY0 + joistH), joistW * secScale, joistH * secScale, 'S'); }); doc.setDrawColor(...GREY); doc.rect(spx(0), spy(osbY0 + underlayThick), width * secScale, underlayThick * secScale, 'S'); doc.setTextColor(...BLACK); doc.setFontSize(6); if (opts.roof) { doc.setLineWidth(0.5); doc.line(spx(0), spy(joistH + underlayThick), spx(width), spy(joistH + underlayThick)); doc.text('Dakleer', spx(width / 2), spy(joistH + underlayThick) - 3, { align: 'center' }); doc.text(`${underlayThick}mm OSB (plafond) — isolatie tussen de balken`, spx(width / 2), spy(0) + 6, { align: 'center' }); } else { doc.text(`${underlayThick}mm OSB`, spx(width / 2), spy(joistH + underlayThick) - 3, { align: 'center' }); doc.text('Isolatie: leegte tussen de balken', spx(width / 2), spy(0) + 6, { align: 'center' }); } } return { drawSheet }; })();