60 lines
2.6 KiB
JavaScript
60 lines
2.6 KiB
JavaScript
|
|
/* ============================================================
|
||
|
|
qft-bc-composite.mjs — quick play: a QFT field plate UNDERNEATH
|
||
|
|
a bubble-chamber plate. Both render to standalone SVG; we embed
|
||
|
|
each as a self-contained data-URI <image> in one outer SVG (no
|
||
|
|
defs/id collisions), and put the bubble chamber on top with
|
||
|
|
mix-blend-mode:multiply — the lightbox metaphor: the field glows
|
||
|
|
underneath, the dark ink darkens it, the light paper drops out.
|
||
|
|
|
||
|
|
Usage:
|
||
|
|
node tools/qft-bc-composite.mjs [qftSeed] [bcSeed] [out.html] [size]
|
||
|
|
============================================================ */
|
||
|
|
import { writeFileSync } from 'node:fs';
|
||
|
|
|
||
|
|
// --- QFT side ---
|
||
|
|
import { generateQFTScene } from '../src/qft/scene.js';
|
||
|
|
import { paramsFromSeed as qftParams } from '../src/qft/params.js';
|
||
|
|
import { renderQFTSVG } from '../src/qft/renderer.js';
|
||
|
|
|
||
|
|
// --- bubble chamber side ---
|
||
|
|
import { generateScene } from '../src/scene/scene.js';
|
||
|
|
import { renderSVG } from '../src/render/svgVector.js';
|
||
|
|
import { paramsFromSeed as bcParams } from '../src/scene/params.js';
|
||
|
|
import { GROUPS, TOGGLES, FIXED } from '../src/ui/controls.js';
|
||
|
|
|
||
|
|
const argv = process.argv.slice(2);
|
||
|
|
const qftSeed = argv[0] || 'VACUUM-5113';
|
||
|
|
const bcSeed = argv[1] || 'LAMBDA-2648';
|
||
|
|
const out = argv[2] || '/tmp/qft-bc.html';
|
||
|
|
const SIZE = +(argv[3] || 1600);
|
||
|
|
|
||
|
|
// pale QFT substrate so it reads as a luminous ground under the ink
|
||
|
|
const qp = qftParams(qftSeed);
|
||
|
|
qp.substrate = 'cream'; // force a light ground so the multiply'd BC ink reads
|
||
|
|
qp.glow = 0.6;
|
||
|
|
|
||
|
|
// bubble chamber: default mono, light cream paper → multiply lets the field through
|
||
|
|
const bp = { ...FIXED, ...bcParams(bcSeed) };
|
||
|
|
for (const g of GROUPS) for (const c of g.controls) if (!(c.id in bp)) bp[c.id] = c.value;
|
||
|
|
for (const t of TOGGLES) if (!(t.id in bp)) bp[t.id] = t.value;
|
||
|
|
|
||
|
|
const qftSvg = renderQFTSVG(generateQFTScene(qp), qp, SIZE);
|
||
|
|
const bcSvg = renderSVG(generateScene(bp), bp, SIZE);
|
||
|
|
|
||
|
|
const dataUri = (svg) => 'data:image/svg+xml;base64,' + Buffer.from(svg).toString('base64');
|
||
|
|
|
||
|
|
const composite =
|
||
|
|
`<svg xmlns="http://www.w3.org/2000/svg" width="${SIZE}" height="${SIZE}" viewBox="0 0 ${SIZE} ${SIZE}">
|
||
|
|
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${dataUri(qftSvg)}"/>
|
||
|
|
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${dataUri(bcSvg)}" style="mix-blend-mode:multiply"/>
|
||
|
|
</svg>`;
|
||
|
|
|
||
|
|
const html =
|
||
|
|
`<!doctype html><meta charset="utf-8">
|
||
|
|
<style>html,body{margin:0;background:#222}img,svg{display:block}</style>
|
||
|
|
${composite}`;
|
||
|
|
|
||
|
|
writeFileSync(out, html);
|
||
|
|
writeFileSync(out.replace(/\.html$/, '.svg'), composite);
|
||
|
|
console.log(`composite -> ${out} (qft=${qftSeed} ${qp.archetype}, bc=${bcSeed}, ${SIZE}px)`);
|