/* ============================================================ qft-variations-positions.mjs — sketch 03. Hold the palette CONSTANT (per-field monochrome — each field one clear hue so the eye can track its position) and vary only origin / scale per field. Some fields deliberately extend past the canvas edge (scale > 1). Output → output/qft/sketch03/ ============================================================ */ 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/sketch03'; const SIZE = 1800; mkdirSync(OUT_DIR, { recursive: true }); const F = (hueStart, hueEnd, saturation, lightness, opacity) => ({ hueStart, hueEnd, saturation, lightness, opacity }); // HELD CONSTANT: per-field monochrome palette so each field's position is legible // cyan cubic · magenta Schlegel · amber E8 · green cross-field links const COLORS = { cubic: F(0.55, 0.55, 0.55, 0.58, 0.72), schlegel: F(0.88, 0.88, 0.55, 0.55, 0.68), e8: F(0.12, 0.12, 0.75, 0.62, 0.92), links: F(0.34, 0.34, 0.80, 0.55, 0.95), }; const SUBSTRATE = 'vintage'; const BASE_OVERRIDES = { substrate: SUBSTRATE, gradientMode: 'along-edge', fields: COLORS }; const variations = [ { name: '01_centered-reference', label: 'Reference · everything centred at default scale', overrides: {}, // no position/scale overrides — defaults }, { name: '02_cubic-left-schlegel-right', label: 'Cubic to the left, Schlegel to the right', overrides: { cubicOriginX: -0.45, cubicOriginY: 0, schlegelOriginX: 0.40, schlegelOriginY: 0, e8Count: 3, e8OriginRadius: 0.30, }, }, { name: '03_huge-cubic-bleed', label: 'Cubic blown up beyond the frame · others normal', overrides: { cubicScale: 1.85, schlegelScale: 0.65, e8Count: 3, e8Scale: 0.14, e8OriginRadius: 0.55, }, }, { name: '04_huge-schlegel-bleed', label: 'Schlegel dominating · cube-in-cube extends past edges', overrides: { cubicScale: 0.45, schlegelScale: 1.85, e8Count: 2, e8Scale: 0.14, e8OriginRadius: 0.30, }, }, { name: '05_tiny-lots-of-paper', label: 'Tiny lattices, lots of paper — a small island', overrides: { cubicScale: 0.42, schlegelScale: 0.32, e8Count: 2, e8Scale: 0.09, e8OriginRadius: 0.18, }, }, { name: '06_e8-cluster-lower-right', label: 'A single large E8 cluster in the lower-right', overrides: { cubicScale: 0.85, cubicOriginX: -0.25, cubicOriginY: -0.20, schlegelScale: 0.70, schlegelOriginX: -0.30, schlegelOriginY: -0.25, e8Origins: [{ x: 0.45, y: 0.40, scale: 0.42 }], }, }, { name: '07_e8-quartet-at-corners', label: 'Four E8 clusters near the corners', overrides: { cubicScale: 0.85, schlegelScale: 0.70, e8Origins: [ { x: -0.65, y: -0.65, scale: 0.20 }, { x: 0.65, y: -0.65, scale: 0.20 }, { x: -0.65, y: 0.65, scale: 0.20 }, { x: 0.65, y: 0.65, scale: 0.20 }, ], linkCount: 12, }, }, { name: '08_composition-shifted-upper-left', label: 'Whole composition pushed to upper-left · negative space lower-right', overrides: { compositionOffsetX: -0.32, compositionOffsetY: -0.32, cubicScale: 0.85, schlegelScale: 0.78, e8Count: 3, e8Scale: 0.16, e8OriginRadius: 0.55, }, }, { name: '09_vertical-stack', label: 'Vertical stack · cubic top, Schlegel middle, E8 bottom', overrides: { cubicScale: 0.55, cubicOriginX: 0, cubicOriginY: -0.45, schlegelScale: 0.55, schlegelOriginX: 0, schlegelOriginY: 0, e8Origins: [{ x: -0.25, y: 0.50, scale: 0.18 }, { x: 0.25, y: 0.50, scale: 0.18 }], linkCount: 10, }, }, { name: '10_triptych-corners', label: 'Triptych · each field in its own corner', overrides: { cubicScale: 0.55, cubicOriginX: -0.40, cubicOriginY: -0.40, schlegelScale: 0.55, schlegelOriginX: 0.40, schlegelOriginY: -0.40, e8Origins: [{ x: 0, y: 0.45, scale: 0.25 }], linkCount: 12, }, }, { name: '11_inverse-scales', label: 'Bonus · inverted scales (tiny cubic, giant Schlegel)', overrides: { cubicScale: 0.30, schlegelScale: 1.70, e8Count: 3, e8Scale: 0.20, e8OriginRadius: 0.65, }, }, ]; const base = paramsFromSeed(BASE_SEED); for (const v of variations) { const params = { ...base, ...BASE_OVERRIDES, ...v.overrides }; params.fields = COLORS; 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}/`);