Configurator voor Van Emous Mantelzorgwoningen: wanden, vloeren, units, woningen (L-vorm), plat dak met dakrand, buitenverlichting, latwerk op vochtdoek, trasraam, sandwichpanelen en PDF-tekenbladen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
4.3 KiB
JavaScript
90 lines
4.3 KiB
JavaScript
/*!
|
|
* Tectum
|
|
* Copyright © 2026 Machiel Heinen.
|
|
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
|
* Zie LICENTIE.md.
|
|
*/
|
|
// ============================================================
|
|
// sandwich/sandwich-model.js — sandwichpanelen rondom de woning (SandwichModel).
|
|
// De vloer staat gedeeltelijk in de grond; de panelen werken de vloerrand af.
|
|
// Ze beginnen op Y=0 van de vloer (het beloopbare vlak = bovenkant vloer) en
|
|
// lopen DEPTH_MM naar beneden, tegen de buitenkant van de vloerrand, in één
|
|
// doorlopende rand (in verstek) om de vereniging van alle unit-vloeren.
|
|
// Plain script (geen ES-module), geladen na core/geometry.js.
|
|
// ============================================================
|
|
|
|
window.SandwichModel = (() => {
|
|
const DEPTH_MM = 600; // vanaf het loopvlak naar beneden (de grond in)
|
|
const THICK_MM = 40; // paneeldikte
|
|
// Handelsmaat plaat: 495 breed x 1000 lang x 40 dik, 9 kg per plaat.
|
|
// Verticaal geplaatst (495 = werkende breedte langs de gevel), lengte
|
|
// 1000 op DEPTH_MM afgezaagd: één plaat per 495 mm gevel.
|
|
const PANEL_WIDTH_MM = 495;
|
|
const PANEL_LENGTH_MM = 1000;
|
|
const PANEL_KG = 9.0;
|
|
|
|
function createMaterials() {
|
|
return {
|
|
panelMaterial: new THREE.MeshStandardMaterial({ color: 0x3b3e42, roughness: 0.55, metalness: 0.2 }),
|
|
seamMaterial: new THREE.LineBasicMaterial({ color: 0x24262a })
|
|
};
|
|
}
|
|
|
|
// rects: unit-vloeren { x0, x1, z0, z1 } (mm, woning-assen); topY =
|
|
// loopvlak (mm). Per ribbe van elke omtreklus een vierhoek (vloerrand ->
|
|
// buitenkant paneel, in verstek, ook bij binnenhoeken).
|
|
function compute(rects, topY) {
|
|
if (!rects.length) return null;
|
|
const segments = [];
|
|
rectUnionLoops(rects).forEach(loop => {
|
|
const base = loop.pts, off = offsetRectilinearLoop(base, THICK_MM), n = base.length;
|
|
base.forEach((a, i) => {
|
|
const j = (i + 1) % n;
|
|
segments.push({ a, b: base[j], outerA: off[i], outerB: off[j] });
|
|
});
|
|
});
|
|
// Lengte gemeten over de hartlijn van het paneel.
|
|
const lengthMM = segments.reduce((t, sg) => t + (Math.hypot(sg.b.x - sg.a.x, sg.b.z - sg.a.z) + Math.hypot(sg.outerB.x - sg.outerA.x, sg.outerB.z - sg.outerA.z)) / 2, 0);
|
|
const areaM2 = lengthMM * DEPTH_MM / 1e6;
|
|
const panelCount = segments.reduce((t, sg) => t + Math.ceil(Math.hypot(sg.outerB.x - sg.outerA.x, sg.outerB.z - sg.outerA.z) / PANEL_WIDTH_MM - 1e-9), 0);
|
|
return {
|
|
segments, top: topY, bottom: topY - DEPTH_MM, depth: DEPTH_MM, thick: THICK_MM,
|
|
lengthMM, areaM2, panelCount, kg: panelCount * PANEL_KG, // inkoop: hele platen
|
|
wasteMM: PANEL_LENGTH_MM - DEPTH_MM // afzaagstuk per plaat
|
|
};
|
|
}
|
|
|
|
// opts: { visible (volgt "Beplating") }
|
|
function build3D(data, materials, opts) {
|
|
const o = opts || {};
|
|
const toM = 0.001;
|
|
const group = new THREE.Group();
|
|
if (!data) return group;
|
|
data.segments.forEach(sg => {
|
|
const pts = [sg.a, sg.b, sg.outerB, sg.outerA];
|
|
const shape = new THREE.Shape(pts.map(p => new THREE.Vector2(p.x * toM, p.z * toM)));
|
|
const geo = new THREE.ExtrudeGeometry(shape, { steps: 1, depth: data.depth * toM, bevelEnabled: false });
|
|
const mesh = new THREE.Mesh(geo, materials.panelMaterial);
|
|
mesh.rotation.x = Math.PI / 2; // shape-y -> wereld-Z, extrusie naar beneden
|
|
mesh.position.y = data.top * toM;
|
|
group.add(mesh);
|
|
|
|
// Paneelnaden op de buitenkant, elke PANEL_WIDTH_MM vanaf ribbe-start.
|
|
const len = Math.hypot(sg.outerB.x - sg.outerA.x, sg.outerB.z - sg.outerA.z);
|
|
const pts3 = [];
|
|
for (let d = PANEL_WIDTH_MM; d < len - 1; d += PANEL_WIDTH_MM) {
|
|
const t = d / len, eps = 0.5; // net buiten het paneelvlak
|
|
const nx = sg.outerA.x - sg.a.x, nz = sg.outerA.z - sg.a.z, nl = Math.hypot(nx, nz) || 1;
|
|
const x = (sg.outerA.x + (sg.outerB.x - sg.outerA.x) * t + nx / nl * eps) * toM;
|
|
const z = (sg.outerA.z + (sg.outerB.z - sg.outerA.z) * t + nz / nl * eps) * toM;
|
|
pts3.push(new THREE.Vector3(x, data.top * toM, z), new THREE.Vector3(x, data.bottom * toM, z));
|
|
}
|
|
if (pts3.length) group.add(new THREE.LineSegments(new THREE.BufferGeometry().setFromPoints(pts3), materials.seamMaterial));
|
|
});
|
|
group.visible = o.visible !== false;
|
|
return group;
|
|
}
|
|
|
|
return { DEPTH_MM, THICK_MM, PANEL_WIDTH_MM, PANEL_LENGTH_MM, PANEL_KG, createMaterials, compute, build3D };
|
|
})();
|