/* ============================================================ qft-variations.mjs — render a curated set of variations. Single base seed (geometry held constant); each variation overrides per-field hue / sat / light / opacity + gradient mode + substrate (or arbitrary paperOverride RGB) + cross-field link colours. Output → output/qft/sketch02/ ============================================================ */ import { writeFileSync, mkdirSync } from 'node:fs'; import { generateQFTScene } from '../src/qft/scene.js'; import { paramsFromSeed } from '../src/qft/params.js'; import { renderQFTSVG } from '../src/qft/renderer.js'; const BASE_SEED = 'FEYNMAN-7167'; const OUT_DIR = 'output/qft/sketch02'; const SIZE = 1800; mkdirSync(OUT_DIR, { recursive: true }); const F = (hueStart, hueEnd, saturation, lightness, opacity) => ({ hueStart, hueEnd, saturation, lightness, opacity }); const variations = [ { name: '01_anchor-vintage', label: 'Anchor · warm vintage muted', overrides: { substrate: 'vintage', gradientMode: 'along-edge', fields: { cubic: F(0.08, 0.05, 0.30, 0.52, 0.65), schlegel: F(0.10, 0.10, 0.20, 0.48, 0.55), e8: F(0.10, 0.16, 0.65, 0.60, 0.92), links: F(0.10, 0.04, 0.90, 0.62, 0.95), // bright amber }, }, }, { name: '02_ember-triad', label: 'Ember triad · warm fire band', overrides: { substrate: 'vintage', gradientMode: 'along-edge', fields: { cubic: F(0.04, 0.10, 0.65, 0.55, 0.75), schlegel: F(0.96, 0.03, 0.55, 0.50, 0.65), e8: F(0.12, 0.18, 0.85, 0.62, 0.90), links: F(0.02, 0.10, 0.95, 0.62, 0.95), // deep red→orange }, }, }, { name: '03_aurora-void', label: 'Aurora · cool spectral on void', overrides: { substrate: 'void', gradientMode: 'along-edge', vign: 0.55, fields: { cubic: F(0.50, 0.72, 0.70, 0.60, 0.85), schlegel: F(0.40, 0.55, 0.55, 0.58, 0.75), e8: F(0.78, 0.92, 0.85, 0.65, 0.90), links: F(0.55, 0.85, 0.95, 0.70, 0.95), // cyan → violet bright }, }, }, { name: '04_spectrum-cream', label: 'Spectrum · one rainbow field on cream', overrides: { substrate: 'cream', gradientMode: 'along-edge', fields: { cubic: F(0.00, 0.95, 0.80, 0.48, 0.80), schlegel: F(0.0, 0.0, 0.05, 0.30, 0.50), e8: F(0.0, 0.16, 0.55, 0.42, 0.75), links: F(0.55, 0.05, 0.85, 0.45, 0.95), // cyan → red — spans the spectrum too }, }, }, { name: '05_verdigris', label: 'Verdigris · oxidised copper / patina', overrides: { substrate: 'vintage', gradientMode: 'along-edge', fields: { cubic: F(0.42, 0.50, 0.50, 0.55, 0.75), schlegel: F(0.06, 0.02, 0.45, 0.45, 0.65), e8: F(0.32, 0.45, 0.55, 0.55, 0.85), links: F(0.04, 0.10, 0.85, 0.58, 0.95), // copper highlights }, }, }, { name: '06_radial-sunrise', label: 'Radial · cool centre → warm edges', overrides: { substrate: 'vintage', gradientMode: 'radial', fields: { cubic: F(0.55, 0.06, 0.60, 0.55, 0.78), schlegel: F(0.55, 0.06, 0.50, 0.55, 0.65), e8: F(0.55, 0.06, 0.70, 0.60, 0.88), links: F(0.55, 0.06, 0.90, 0.62, 0.95), // shares the radial gradient }, }, }, { name: '07_magenta-family', label: 'Magenta family on cream', overrides: { substrate: 'cream', gradientMode: 'along-edge', fields: { cubic: F(0.85, 0.93, 0.50, 0.45, 0.75), schlegel: F(0.78, 0.88, 0.42, 0.42, 0.65), e8: F(0.92, 0.00, 0.60, 0.48, 0.88), links: F(0.92, 0.78, 0.85, 0.40, 0.95), // saturated pink → purple }, }, }, { name: '08_per-field-mono', label: 'Per-field monochrome', overrides: { substrate: 'vintage', gradientMode: 'along-edge', fields: { cubic: F(0.55, 0.55, 0.55, 0.55, 0.70), schlegel: F(0.88, 0.88, 0.55, 0.55, 0.70), e8: F(0.12, 0.12, 0.75, 0.60, 0.92), links: F(0.34, 0.34, 0.80, 0.55, 0.95), // mono green accent — a 4th distinct hue }, }, }, { name: '09_cyanotype', label: 'Cyanotype · Anna Atkins', overrides: { substrate: 'cyanotype', gradientMode: 'along-edge', fields: { cubic: F(0.55, 0.50, 0.30, 0.80, 0.85), schlegel: F(0.55, 0.55, 0.20, 0.85, 0.75), e8: F(0.48, 0.42, 0.50, 0.78, 0.92), links: F(0.55, 0.50, 0.30, 0.92, 0.95), // near-white connectors }, }, }, { name: '10_single-bright-void', label: 'Single bright · E8 rainbow on void', overrides: { substrate: 'void', gradientMode: 'along-edge', vign: 0.55, fields: { cubic: F(0.0, 0.0, 0.05, 0.40, 0.45), schlegel: F(0.0, 0.0, 0.05, 0.42, 0.40), e8: F(0.0, 0.95, 0.95, 0.62, 0.98), links: F(0.0, 0.95, 0.95, 0.70, 0.98), // bright rainbow connectors }, }, }, { name: '11_mauve-fog-custom', label: 'Bonus · custom mauve paper', overrides: { gradientMode: 'along-edge', paperOverride: { flat: [82, 76, 92], glowIn: [108, 98, 118], glowOut: [58, 52, 70] }, vignOverride: [22, 18, 28], featureOverride: [200, 190, 215], fields: { cubic: F(0.00, 0.95, 0.50, 0.72, 0.80), schlegel: F(0.0, 0.0, 0.05, 0.85, 0.55), e8: F(0.55, 0.85, 0.65, 0.72, 0.92), links: F(0.55, 0.85, 0.85, 0.78, 0.95), // pastel cyan → violet }, }, }, ]; const base = paramsFromSeed(BASE_SEED); for (const v of variations) { const params = { ...base, ...v.overrides }; if (v.overrides.fields) params.fields = v.overrides.fields; const svg = renderQFTSVG(generateQFTScene(params), params, SIZE); const path = `${OUT_DIR}/${v.name}.svg`; writeFileSync(path, svg); console.log(`ok ${v.name} · ${v.label}`); } console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);