/*! * Tectum * Copyright © 2026 Machiel Heinen. * Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming. * Zie LICENTIE.md. */ window.SandwichModel = (() => { const DEPTH_MM = 600; const THICK_MM = 40; 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 }) }; } 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] }); }); }); 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, wasteMM: PANEL_LENGTH_MM - DEPTH_MM }; } 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; mesh.position.y = data.top * toM; group.add(mesh); 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; 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 }; })();