155 lines
8.0 KiB
JavaScript
155 lines
8.0 KiB
JavaScript
/*!
|
||
* 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 };
|
||
})();
|