193 lines
8.4 KiB
JavaScript
193 lines
8.4 KiB
JavaScript
/*!
|
|
* Tectum
|
|
* Copyright © 2026 Machiel Heinen.
|
|
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
|
* Zie LICENTIE.md.
|
|
*/
|
|
// ============================================================
|
|
// floor/floor-pdf.js — ENIGE bron van het vloer-tekenblad (A3 liggend):
|
|
// frame- en plaatmateriaal, bovenaanzicht met balken/doorvoeren en
|
|
// maatketens, doorsnede en titelblok. Gebruikt door de losse vloer-editor en
|
|
// door de unit-PDF.
|
|
// Plain script, geladen na floor-model.js en core/pdf-sheet.js.
|
|
// ============================================================
|
|
|
|
window.FloorPdf = (() => {
|
|
// ------------------------------------------------------------
|
|
// Zelfde opzet als WallPdf (frame-materiaallijst, plaatmateriaallijst,
|
|
// titelblok, hoofdaanzicht met maatketens). Layout/kolommen zijn
|
|
// afgestemd op de bestaande "Vloer^V-x"-tekeningen (zie examples/) —
|
|
// die voorbeelden splitsen een grote vloer in meerdere modules; hier
|
|
// wordt bewust altijd maar één, doorlopende module getekend.
|
|
// ------------------------------------------------------------
|
|
|
|
// Tekent één volledig vloerblad op de HUIDIGE pagina van `doc`.
|
|
// opts: { project, titel, element, blad, roof }
|
|
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');
|
|
|
|
// --- Frame materiaal-tabel (linksboven) — één groep: alle vloerbalken
|
|
// hebben in dit model dezelfde doorsnede/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; // kopbalk(en) op een naad (FloorModel.computeFrame)
|
|
[String(2), String(joistW), String(joistH), String(Math.round(width)), String(rimCount)]
|
|
.forEach((v, i) => doc.text(v, tblX[i], ty));
|
|
}
|
|
|
|
// --- Plaatmateriaal + isolatie ---
|
|
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);
|
|
|
|
// --- Titelblok (rechtsonder) ---
|
|
PdfSheet.titleBlock(doc, opts.project, { titel: opts.titel, element: opts.element, blad: opts.blad });
|
|
|
|
// --- Hoofdaanzicht (bovenaanzicht) ---
|
|
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; // y=0 (voor) boven, groeit naar beneden
|
|
|
|
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' });
|
|
});
|
|
|
|
// Extra laag van de aanroeper (bijv. elektra, ElektraPdf): overlay(doc, px, py),
|
|
// px/py = mm vanaf linksvoor van het dek -> blad-mm.
|
|
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 });
|
|
|
|
// H.o.h.-segmenten + cumulatieve maten onder de tekening — zelfde
|
|
// opbouw als de stijl-maatketens in wall.js, nu op de balkposities.
|
|
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);
|
|
});
|
|
|
|
// --- Doorsnede (balkprofiel) — kleine profielweergave onderaan,
|
|
// analoog aan de bestaande 2D-doorsnede-canvas. ---
|
|
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;
|
|
|
|
// Dak (opts.roof): 180° gespiegeld — OSB onder (plafond), balken
|
|
// erboven, dakleer bovenop (RoofModel).
|
|
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 };
|
|
})();
|