/*! * Tectum * Copyright © 2026 Machiel Heinen. * Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming. * Zie LICENTIE.md. */ // ============================================================ // elektra/elektra-pdf.js — ElektraPdf: elektra in de woning-PDF. // drawPlan : meterkast, schakelaars (letter), wcd's (groep), lamp- // letters en leidingen op de plattegrond (blad Plattegrond) // drawSheet : eigen blad met groepenverklaring, schakelaars, strekkende // meters per leidingtype, draad, buis en materiaal. // Alle gegevens komen uit ElektraModel.compute (parts.elektra). // Plain script, geladen na pdf-sheet.js en elektra-model.js. // ============================================================ window.ElektraPdf = (() => { const fmt = (n, d = 1) => Number(n).toFixed(d).replace('.', ','); // Kleur per leidingtype (ook de legenda). const RUN_COLORS = { licht: [215, 160, 0], wcd: [205, 55, 35], buiten: [40, 140, 60], schakel: [150, 95, 20] }; const runColor = (type) => RUN_COLORS[type] || RUN_COLORS.schakel; // px/py: woning-mm -> blad-mm. function drawPlan(doc, el, px, py) { if (!el || !el.enabled) return; const BLACK = PdfSheet.COLORS.BLACK; // Leidingen (gestippeld, plattegrond-projectie van het pad). doc.setLineWidth(0.15); doc.setLineDashPattern([0.8, 0.6], 0); el.runs.forEach(r => { doc.setDrawColor(...runColor(r.type)); r.path.slice(1).forEach((b, i) => { const a = r.path[i]; if (Math.hypot(px(b.x) - px(a.x), py(b.z) - py(a.z)) > 0.05) doc.line(px(a.x), py(a.z), px(b.x), py(b.z)); }); }); doc.setLineDashPattern([], 0); // Meterkast const k = el.meterkast; if (k) { doc.setDrawColor(...BLACK); doc.setFillColor(90, 90, 90); doc.setLineWidth(0.2); const c = k.corners; doc.lines(c.slice(1).map((p, i) => [px(p.x) - px(c[i].x), py(p.z) - py(c[i].z)]), px(c[0].x), py(c[0].z), [1, 1], 'FD', true); doc.setFontSize(5); doc.setTextColor(...BLACK); const m = { x: (c[0].x + c[2].x) / 2 + k.face.n.x * 350, z: (c[0].z + c[2].z) / 2 + k.face.n.z * 350 }; doc.text('MK', px(m.x), py(m.z) + 0.8, { align: 'center' }); } // Verdeeldozen (plafond): klein vierkantje. doc.setDrawColor(...runColor('schakel')); doc.setLineWidth(0.15); el.boxes.forEach(b => doc.rect(px(b.x) - 0.6, py(b.z) - 0.6, 1.2, 1.2, 'S')); // Schakelaars: kadertje met de letters, iets de kamer in. doc.setFontSize(5); el.combos.forEach(c => { const q = { x: c.pos.x + c.face.n.x * 260, z: c.pos.z + c.face.n.z * 260 }; const text = c.switches.map(s => s.letter).join(' '); const w = 1.6 + text.length * 1.0; doc.setDrawColor(...BLACK); doc.setFillColor(255, 255, 255); doc.setLineWidth(0.2); doc.rect(px(q.x) - w / 2, py(q.z) - 1.2, w, 2.4, 'FD'); doc.setTextColor(...BLACK); doc.text(text, px(q.x), py(q.z) + 0.7, { align: 'center' }); }); // Wcd: halve cirkel tegen de wand + groepnummer. el.wcds.forEach(w => { const q = { x: w.pos.x + w.face.n.x * 120, z: w.pos.z + w.face.n.z * 120 }; doc.setDrawColor(...RUN_COLORS.wcd); doc.setFillColor(255, 255, 255); doc.setLineWidth(0.2); doc.circle(px(q.x), py(q.z), 0.9, 'FD'); doc.setTextColor(...RUN_COLORS.wcd); doc.setFontSize(4); doc.text(String(w.group), px(q.x + w.face.n.x * 250), py(q.z + w.face.n.z * 250) + 0.6, { align: 'center' }); }); // Apparaten: kadertje met de code (KK, WM, ...) + groepnummer. el.apps.forEach(a => { const it = el.items.find(x => x.key === a.key), q = it.marker; doc.setDrawColor(...RUN_COLORS.wcd); doc.setFillColor(255, 255, 255); doc.setLineWidth(0.2); doc.rect(px(q.x) - 1.6, py(q.z) - 1.1, 3.2, 2.2, 'FD'); doc.setTextColor(...RUN_COLORS.wcd); doc.setFontSize(3.6); doc.text(a.code, px(q.x), py(q.z) + 0.5, { align: 'center' }); doc.text(String(a.group), px(q.x + a.face.n.x * 330), py(q.z + a.face.n.z * 330) + 0.5, { align: 'center' }); }); // Lampletters (zelfde letter als de schakelaar). doc.setFontSize(3.6); doc.setTextColor(...BLACK); el.switches.forEach(s => s.lamps.forEach(l => doc.text(s.letter, px(l.x) + 0.9, py(l.z) - 0.7))); } // Twee regels (doc.text met een array). const PLAN_LEGEND = [ 'Elektra: MK = meterkast, kadertje = schakelaar(s) met letter (dezelfde letter staat bij de lampen erop), rood rondje = dubbele wcd (groepnummer), vierkantje = verdeeldoos in plafond;', 'rood kadertje = apparaat (KK kookgroep, WM wasmachine, DR droger, WP warmtepomp, OV oven, VW vaatwasser) met groepnummer; gestippeld = leidingen (geel licht, bruin schakeldraad, rood wcd/apparaat, groen buiten).' ]; function drawSheet(doc, el, sheetOpts) { const BLACK = PdfSheet.COLORS.BLACK; doc.setFont('helvetica', 'normal'); doc.setTextColor(...BLACK); doc.setFontSize(14); doc.text('Elektra', 15, 18); const t = (title) => ({ title, fontSize: 7, rowH: 4.4, lastColW: 16 }); let y = 30; y = PdfSheet.table(doc, 15, y, [0, 12, 62, 74, 88, 104, 140, 156], ['Groep', 'Omschrijving', 'Fase', 'Aardlek', 'Automaat', 'Leiding', 'Punten', 'Lengte'], el.groups.map(g => [String(g.nr), g.label, g.phase, `AL${g.rcd}`, g.breaker, ElektraModel.cableInfo(g.cable).label.replace(/ in buis.*/, ''), g.kind === 'app' ? 'apparaat' : `${g.points} ${g.kind === 'wcd' ? 'wcd' : 'lamp'}`, fmt(g.meters) + ' m']), t('Groepenverklaring (aansluiting 3×25 A)')); y += 4; y = PdfSheet.table(doc, 15, y, [0, 14, 60, 76, 92], ['Letter', 'Functie', 'Unit', 'Groep', 'Lampen'], el.switches.map(s => [s.letter, s.label, s.unitId, String(s.group || '-'), `${s.lamps.length} (${[['binnenspot', 'binnenspot'], ['boeideelspot', 'boeideel-spot'], ['gevellamp', 'gevellamp']] .map(([k, l]) => [s.lamps.filter(x => x.kind === k).length, l]).filter(([n]) => n).map(([n, l]) => `${n} ${l}`).join(', ') || '-'})`]), t('Schakelaars (letter staat ook bij de lampen op de plattegrond)')); y += 4; const k = el.meterkast; const notes = [ k ? `Meterkast ${k.w}×${k.h}×${k.d} mm (b×h×d) in unit ${k.unitId}, wand ${k.element}, onderkant ${k.y} mm boven vloer.` : 'Geen meterkast geplaatst.', `Schakelaars op ${el.settings.switchHeight} mm, wcd op ${el.settings.wcdHeight} mm (hart, boven bovenkant vloer).`, `Leidinglengte = route via stijlen/plafond + ${el.settings.reserve} mm reserve (meterkast +${el.settings.meterkastReserve} mm), + ${el.settings.slack}% marge.`, 'Groepen per unit: licht B10 (1,5 mm²), wcd B16 (2,5 mm²); buitenlicht eigen groep B10 (XMvK), zelfde aardlek als het binnenlicht in dezelfde schakeldoos.', 'Per schakeldoos één verdeeldoos per groep; fasen en aardlekschakelaars om en om.', 'Apparaten (kookgroep, wasmachine, ...) automatisch geplaatst zolang er geen keuken/bijkeuken is; verplaatsbaar in de plattegrond.', 'Indicatief eerste ontwerp; uitvoering en controle volgens NEN 1010 door de installateur.' ].concat(el.warnings.map(w => 'Let op: ' + w)); doc.setFontSize(7); notes.forEach(n => { doc.text(n, 15, y); y += 4.2; }); let y2 = 30; y2 = PdfSheet.table(doc, 205, y2, [0, 46, 102], ['Leidingtype', 'Gebruik', 'Lengte'], el.cables.map(c => [c.label, c.use, fmt(c.meters) + ' m']), t('Leidingen (strekkende meter)')); y2 += 4; y2 = PdfSheet.table(doc, 205, y2, [0, 102], ['Draad', 'Lengte'], el.wires.map(w => [w.label, fmt(w.meters) + ' m']), t('Draad (per ader)')); y2 += 4; PdfSheet.table(doc, 205, y2, [0, 102], ['Buis', 'Lengte'], el.conduits.map(c => [c.label, fmt(c.meters) + ' m']), t('Installatiebuis')); PdfSheet.table(doc, 330, 30, [0, 66], ['Onderdeel', 'Aantal'], el.materials.map(m => [m.naam, `${m.aantal} ${m.eenheid}`]), Object.assign(t('Materiaal'), { maxY: 240 })); PdfSheet.titleBlock(doc, sheetOpts.project, sheetOpts); } return { RUN_COLORS, drawPlan, drawSheet, PLAN_LEGEND }; })();