#!/usr/bin/env node /* stack-render.mjs — render an acrylic-stack plan to per-plane SVGs. usage: node tools/stack-render.mjs [outDir] Writes plane-NN--.svg + manifest.json for the simulator (fable/stack/simulator.html?c=). */ import { mkdirSync, writeFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { pathToFileURL } from 'node:url'; import { renderStack, planeDepths } from '../src/render/stack.js'; const [planPath, outArg] = process.argv.slice(2); if (!planPath) { console.error('usage: node tools/stack-render.mjs [outDir]'); process.exit(1); } const plan = (await import(pathToFileURL(resolve(planPath)).href)).default; const outDir = resolve(outArg || `fable/stack/candidates/${plan.id}`); mkdirSync(outDir, { recursive: true }); const { planes } = renderStack(plan); const zmm = planeDepths(plan); const manifest = { id: plan.id, title: plan.title || plan.id, seed: plan.seed, geometry: { thickness: plan.geometry?.thickness ?? 3, gap: plan.geometry?.gap ?? 6 }, planes: [], }; planes.forEach((p, i) => { const file = `plane-${String(i).padStart(2, '0')}-${p.sheet}-${p.face}.svg`; writeFileSync(`${outDir}/${file}`, p.svg); manifest.planes.push({ file, sheet: p.sheet, face: p.face, zmm: zmm[i], label: p.label }); console.log(` ${file} z=${zmm[i]}mm ${p.label}`); }); writeFileSync(`${outDir}/manifest.json`, JSON.stringify(manifest, null, 2)); console.log(`→ ${outDir} (${planes.length} planes)`);