Files
bubblechambersimart/tools/qft-variations-craft.mjs
2026-05-29 15:40:42 -04:00

374 lines
15 KiB
JavaScript

/* ============================================================
qft-variations-craft.mjs — sketch 06.
Each variation isolates ONE craft idea so the effect is
readable on its own. Five blocks:
A · Cubic origin plays — push origin off-page or into the
viewer's space along the (isometric) Z axis
B · Multi-rosettes — offset origins, varied scales
C · Lighter-on-midtone — paper at MID tone so a brighter
rosette can actually read as lighter-than-ground
D · Plain white on light — bold white strokes on warm paper
E · Schlegel plays — weight / origin / shifted axes
(outerR · innerR · rot3D)
============================================================ */
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/sketch06';
const SIZE = 1800;
mkdirSync(OUT_DIR, { recursive: true });
const F = (hueStart, hueEnd, saturation, lightness, opacity) =>
({ hueStart, hueEnd, saturation, lightness, opacity });
const paper = (flat, glowInDelta = [18, 16, 14], glowOutDelta = [-25, -22, -20]) => ({
flat,
glowIn: [flat[0] + glowInDelta[0], flat[1] + glowInDelta[1], flat[2] + glowInDelta[2]],
glowOut: [flat[0] + glowOutDelta[0], flat[1] + glowOutDelta[1], flat[2] + glowOutDelta[2]],
});
const WHITE = (op = 0.95) => F(0, 0, 0, 0.97, op);
const variations = [
// ─────────────────────── BLOCK A · cubic origin plays ───────────────────────
{
name: 'A1_cubic-origin-far-right',
label: 'A1 · cubic origin pushed off-page right — orthogonal mesh streams in from the right edge',
overrides: {
paperOverride: paper([225, 215, 200]),
vignOverride: [70, 60, 45], featureOverride: [60, 45, 30],
stroke: 1.8, gradientMode: 'linear-x',
cubicScale: 1.4, cubicOriginX: 1.45, cubicOriginY: 0, // mostly off-screen to the right
schlegelScale: 0.65,
distStrength: 0, // disable distortion when origin is offset
e8Origins: [{ x: -0.45, y: 0, scale: 0.42 }], // counterweight rosette left
linkCount: 4,
fields: {
cubic: F(0.55, 0.08, 0.65, 0.35, 0.78),
schlegel: F(0.10, 0.08, 0.45, 0.40, 0.55),
e8: F(0.10, 0.04, 0.65, 0.45, 0.88),
links: F(0.05, 0.10, 0.70, 0.40, 0.55),
},
},
},
{
name: 'A2_cubic-zoom-toward-viewer',
label: 'A2 · cubic zoomed in (Z toward viewer) — we are INSIDE the lattice',
overrides: {
paperOverride: paper([225, 215, 198]),
vignOverride: [60, 50, 35], featureOverride: [70, 55, 30],
stroke: 2.4,
cubicScale: 3.2, // huge — fills well past the frame
schlegelScale: 0,
distStrength: 0,
e8Origins: [{ x: 0.55, y: 0.55, scale: 0.32 }], // single small rosette one corner
linkCount: 3,
fields: {
cubic: F(0.04, 0.13, 0.65, 0.32, 0.80),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.05, 0.12, 0.65, 0.45, 0.88),
links: F(0.06, 0.12, 0.65, 0.40, 0.55),
},
},
},
{
name: 'A3_cubic-origin-upper-left',
label: 'A3 · cubic origin pushed upper-left, rest of canvas opens to lower-right',
overrides: {
paperOverride: paper([232, 222, 205]),
vignOverride: [70, 55, 40], featureOverride: [60, 50, 35],
stroke: 1.6, gradientMode: 'radial',
cubicScale: 1.6, cubicOriginX: -0.85, cubicOriginY: -0.85,
schlegelScale: 0.55, schlegelOriginX: -0.30, schlegelOriginY: -0.30,
distStrength: 0,
e8Origins: [{ x: 0.40, y: 0.40, scale: 0.45 }], // rosette in opposing corner
linkCount: 4,
fields: {
cubic: F(0.55, 0.05, 0.65, 0.36, 0.80),
schlegel: F(0.58, 0.10, 0.45, 0.40, 0.60),
e8: F(0.06, 0.12, 0.70, 0.45, 0.88),
links: F(0.05, 0.10, 0.65, 0.40, 0.55),
},
},
},
// ─────────────────────── BLOCK B · multi-rosettes ────────────────────────────
{
name: 'B1_three-rosettes-horizontal',
label: 'B1 · three rosettes along a horizontal axis',
overrides: {
paperOverride: paper([232, 222, 205]),
vignOverride: [60, 50, 35], featureOverride: [55, 45, 30],
stroke: 1.8,
cubicScale: 1.3, schlegelScale: 0,
e8Origins: [
{ x: -0.55, y: 0, scale: 0.32 },
{ x: 0.00, y: 0, scale: 0.42 },
{ x: 0.55, y: 0, scale: 0.32 },
],
linkCount: 6,
fields: {
cubic: F(0.50, 0.10, 0.45, 0.40, 0.35),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.55, 0.05, 0.55, 0.48, 0.85),
links: F(0.06, 0.12, 0.65, 0.42, 0.55),
},
},
},
{
name: 'B2_triangular-rosette-cluster',
label: 'B2 · three rosettes in a triangle · varied sizes',
overrides: {
paperOverride: paper([222, 210, 220]),
vignOverride: [55, 40, 60], featureOverride: [65, 45, 75],
stroke: 1.6,
cubicScale: 1.4, schlegelScale: 0.65,
e8Origins: [
{ x: 0.00, y: -0.45, scale: 0.50 },
{ x: -0.45, y: 0.30, scale: 0.32 },
{ x: 0.45, y: 0.30, scale: 0.32 },
],
linkCount: 6,
fields: {
cubic: F(0.78, 0.92, 0.50, 0.38, 0.40),
schlegel: F(0.82, 0.92, 0.40, 0.40, 0.50),
e8: F(0.82, 0.95, 0.55, 0.46, 0.85),
links: F(0.85, 0.96, 0.65, 0.42, 0.55),
},
},
},
{
name: 'B3_five-rosettes-quincunx',
label: 'B3 · quincunx: 4 corners + centre · five rosettes total',
overrides: {
paperOverride: paper([232, 222, 205]),
vignOverride: [60, 50, 35], featureOverride: [55, 45, 30],
stroke: 1.6,
cubicScale: 1.3, schlegelScale: 0,
e8Origins: [
{ x: -0.50, y: -0.50, scale: 0.26 },
{ x: 0.50, y: -0.50, scale: 0.26 },
{ x: 0.00, y: 0.00, scale: 0.42 },
{ x: -0.50, y: 0.50, scale: 0.26 },
{ x: 0.50, y: 0.50, scale: 0.26 },
],
linkCount: 8,
fields: {
cubic: F(0.10, 0.16, 0.40, 0.40, 0.35),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.10, 0.16, 0.60, 0.45, 0.85),
links: F(0.06, 0.12, 0.65, 0.42, 0.55),
},
},
},
{
name: 'B4_central-large-plus-orbit',
label: 'B4 · one large central rosette + smaller orbiting satellites',
overrides: {
paperOverride: paper([225, 215, 198]),
vignOverride: [60, 50, 35], featureOverride: [55, 45, 30],
stroke: 1.6, gradientMode: 'radial',
cubicScale: 1.4, schlegelScale: 0,
e8Origins: [
{ x: 0.00, y: 0.00, scale: 0.70 },
{ x: 0.55, y: -0.55, scale: 0.18 },
{ x: -0.55, y: -0.55, scale: 0.18 },
{ x: 0.00, y: 0.62, scale: 0.18 },
],
linkCount: 6,
fields: {
cubic: F(0.55, 0.05, 0.55, 0.38, 0.35),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.55, 0.05, 0.55, 0.46, 0.85),
links: F(0.06, 0.12, 0.70, 0.42, 0.55),
},
},
},
// ────────────────── BLOCK C · lighter-on-midtone ─────────────────────────────
{
name: 'C1_lighter-on-deep-cream',
label: 'C1 · lighter rosette on DEEP-cream midtone',
overrides: {
paperOverride: paper([180, 165, 140]), // mid-tone deep cream
vignOverride: [90, 70, 45], featureOverride: [240, 230, 215],
stroke: 2.4,
cubicScale: 1.45, schlegelScale: 0.75,
e8Origins: [{ x: 0, y: 0, scale: 0.78 }],
fields: {
cubic: F(0.06, 0.13, 0.30, 0.38, 0.55),
schlegel: F(0.08, 0.10, 0.30, 0.38, 0.50),
e8: F(0.08, 0.13, 0.20, 0.88, 0.80), // LIGHTER than the midtone paper
links: F(0.06, 0.12, 0.40, 0.92, 0.85),
},
},
},
{
name: 'C2_lighter-on-warm-grey',
label: 'C2 · lighter rosette on warm-grey midtone',
overrides: {
paperOverride: paper([155, 150, 140]), // warm grey midtone
vignOverride: [80, 70, 55], featureOverride: [235, 230, 220],
stroke: 2.6,
cubicScale: 1.55, schlegelScale: 0,
e8Origins: [{ x: -0.20, y: 0, scale: 0.55 },
{ x: 0.20, y: 0, scale: 0.55 }],
fields: {
cubic: F(0.06, 0.12, 0.20, 0.40, 0.55),
schlegel: F(0, 0, 0, 0.5, 0),
e8: F(0.06, 0.12, 0.18, 0.90, 0.80), // pale ivory on grey
links: F(0.06, 0.12, 0.30, 0.94, 0.85),
},
},
},
{
name: 'C3_lighter-on-sage',
label: 'C3 · lighter rosette on sage-grey midtone',
overrides: {
paperOverride: paper([155, 165, 145]), // sage midtone
vignOverride: [55, 70, 50], featureOverride: [225, 235, 215],
stroke: 2.4,
cubicScale: 1.4, schlegelScale: 0.75,
e8Origins: [{ x: 0, y: 0, scale: 0.82 }],
fields: {
cubic: F(0.40, 0.48, 0.30, 0.42, 0.55),
schlegel: F(0.42, 0.48, 0.25, 0.40, 0.50),
e8: F(0.40, 0.48, 0.22, 0.90, 0.80),
links: F(0.40, 0.50, 0.35, 0.94, 0.85),
},
},
},
// ────────────────── BLOCK D · plain white on light ───────────────────────────
{
name: 'D1_thick-white-on-cream',
label: 'D1 · bold WHITE strokes on warm cream — embossed',
overrides: {
paperOverride: paper([222, 210, 188]),
vignOverride: [70, 55, 35], featureOverride: [60, 50, 35],
stroke: 3.4, // thick
cubicScale: 1.45, schlegelScale: 1.20,
e8Origins: [{ x: 0, y: 0, scale: 0.72 }],
fields: {
cubic: WHITE(0.85),
schlegel: WHITE(0.65),
e8: WHITE(0.92),
links: WHITE(0.78),
},
},
},
{
name: 'D2_white-on-pale-rose',
label: 'D2 · plain white rosettes on pale rose',
overrides: {
paperOverride: paper([232, 210, 210]),
vignOverride: [80, 55, 55], featureOverride: [90, 55, 55],
stroke: 3.2,
cubicScale: 1.35, schlegelScale: 0,
e8Origins: [{ x: 0, y: 0, scale: 0.95 },
{ x: 0, y: 0, scale: 0.60 },
{ x: 0, y: 0, scale: 0.30 }],
linkCount: 0,
fields: {
cubic: WHITE(0.40), // very subtle cubic atmosphere
schlegel: F(0, 0, 0, 0.5, 0),
e8: WHITE(0.95), // strong solid white rosettes
links: WHITE(0.5),
},
},
},
{
name: 'D3_white-on-pale-blue',
label: 'D3 · white schlegel + white rosette on pale blue',
overrides: {
paperOverride: paper([190, 215, 230]),
vignOverride: [40, 55, 70], featureOverride: [30, 50, 75],
stroke: 3.0,
cubicScale: 1.0, schlegelScale: 1.50, // schlegel prominent
e8Origins: [{ x: 0, y: 0, scale: 0.65 }],
fields: {
cubic: WHITE(0.45),
schlegel: WHITE(0.85), // bold white tesseract
e8: WHITE(0.92),
links: WHITE(0.55),
},
},
},
// ────────────────── BLOCK E · schlegel plays ─────────────────────────────────
{
name: 'E1_schlegel-thin-nested',
label: 'E1 · Schlegel: deep nesting (tiny inner cube) · thin lines',
overrides: {
paperOverride: paper([232, 222, 200]),
vignOverride: [60, 50, 40], featureOverride: [60, 50, 35],
stroke: 1.4,
cubicScale: 0.55, schlegelScale: 1.40,
schlegelInnerR: 0.08, // very small inner cube → strong perspective
schlegelRot3D: 0.20,
e8Origins: [{ x: 0, y: 0, scale: 0.42 }],
fields: {
cubic: F(0.58, 0.62, 0.30, 0.45, 0.40),
schlegel: F(0.60, 0.68, 0.50, 0.32, 0.78), // schlegel is the subject
e8: F(0.55, 0.65, 0.50, 0.45, 0.78),
links: F(0.58, 0.66, 0.65, 0.42, 0.55),
},
},
},
{
name: 'E2_schlegel-shifted-axis-thick',
label: 'E2 · Schlegel with strongly rotated 4D axis · thick lines',
overrides: {
paperOverride: paper([222, 212, 195]),
vignOverride: [70, 55, 40], featureOverride: [60, 45, 30],
stroke: 3.0, // thick
cubicScale: 0.65, schlegelScale: 1.55,
schlegelInnerR: 0.42, // larger inner cube — less perspective, more parallel
schlegelRot3D: 1.10, // strong rotation — looking at the tesseract from different angle
schlegelOriginX: 0.15,
e8Origins: [{ x: -0.45, y: -0.45, scale: 0.32 }],
fields: {
cubic: F(0.06, 0.12, 0.35, 0.40, 0.40),
schlegel: F(0.06, 0.13, 0.55, 0.32, 0.85),
e8: F(0.08, 0.16, 0.65, 0.45, 0.85),
links: F(0.06, 0.12, 0.65, 0.40, 0.55),
},
},
},
{
name: 'E3_dual-schlegels-rotated',
label: 'E3 · BIG schlegel offset · faint thin counter-rotated cubic',
overrides: {
paperOverride: paper([200, 200, 215]), // pale lavender-grey midtone
vignOverride: [60, 55, 75], featureOverride: [40, 40, 60],
stroke: 2.6,
cubicScale: 1.6, cubicRot: 0.80, // cubic strongly rotated
schlegelScale: 1.85, // schlegel very large bleeding
schlegelInnerR: 0.55, // inner cube larger
schlegelRot3D: -0.30, // negative rotation — opposite angle
schlegelOriginX: -0.10, schlegelOriginY: 0.10,
e8Origins: [{ x: 0.40, y: -0.40, scale: 0.32 },
{ x: -0.40, y: 0.40, scale: 0.32 }],
fields: {
cubic: F(0.62, 0.70, 0.30, 0.55, 0.35),
schlegel: F(0.62, 0.72, 0.55, 0.32, 0.78),
e8: F(0.65, 0.75, 0.60, 0.45, 0.85),
links: F(0.62, 0.78, 0.70, 0.42, 0.55),
},
},
},
];
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}`);
}
console.log(`\nrendered ${variations.length} variations → ${OUT_DIR}/`);