Files

63 lines
7.0 KiB
JavaScript
Raw Permalink Normal View History

2026-06-02 20:32:41 -04:00
/* ============================================================
liberation.mjs severing the anchors. The sea need not sit at the
bottom; the sun and event need not stay on-canvas; the horizon need
not be level. Each composition breaks a default we'd quietly started
treating as a law. Built through the schema (proving it affords this).
Usage: node tools/liberation.mjs [size]
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { DEFAULT_COMPOSITION } from '../src/compose/schema.js';
import { renderComposition } from '../src/compose/composition.js';
const SIZE = +(process.argv[2] || 1300);
const OUT = 'output/liberation';
mkdirSync(OUT, { recursive: true });
const merge = (a, b) => { const o = { ...a }; for (const k in b) o[k] = (b[k] && typeof b[k] === 'object' && !Array.isArray(b[k])) ? merge(a[k] || {}, b[k]) : b[k]; return o; };
const C = (seed, over) => { const c = merge(JSON.parse(JSON.stringify(DEFAULT_COMPOSITION)), over); c.seed = seed; return c; };
const V = [
// sea off the bottom ------------------------------------------------------
{ name: '01_sea-on-top', note: 'sea hung from the top (rotated 180°)',
comp: C('INVERT-7741', { fieldSea: { transform: { rotation: 180 } }, disk: { transform: { x: 0, y: 0.4, scale: 0.7 } }, bubble: { transform: { x: 0.28, y: 0.46 } }, fiduciaries: { target: [0.28, 0.46] } }) },
{ name: '02_sea-vertical-stripe', note: 'sea as a vertical stripe down the middle',
comp: C('COLUMN-2208', { fieldSea: { transform: { rotation: 90, scale: 1.0 }, clip: [0.37, 0, 0.63, 1] }, fieldGrid: { enabled: false }, disk: { transform: { x: -0.55, y: -0.2, scale: 0.55 } }, bubble: { transform: { x: 0.55, y: 0.2, scale: 0.55 } } }) },
{ name: '03_mid-band-sea', note: 'sea confined to a band across the vertical middle',
comp: C('STRATA-0613', { fieldSea: { transform: { y: -0.32 }, clip: [0, 0.36, 1, 0.64], horizonY: 0.2 }, fieldGrid: { enabled: false }, disk: { transform: { x: -0.1, y: -0.55, scale: 0.5 } }, bubble: { transform: { x: 0.2, y: 0.55, scale: 0.5 } } }) },
{ name: '04_tilted-horizon', note: 'the horizon listing 8° — a world off true',
comp: C('LISTING-1900', { fieldSea: { transform: { rotation: 8 } }, fieldGrid: { transform: { rotation: 8 } }, disk: { transform: { x: -0.1, y: -0.22 } } }) },
{ name: '05_corner-sea', note: 'sea as a patch in one corner, the rest void',
comp: C('QUADRANT-44', { fieldSea: { transform: { scale: 0.62, x: -0.42, y: -0.42 }, clip: [0, 0, 0.58, 0.58] }, fieldGrid: { enabled: false }, disk: { transform: { x: 0.4, y: 0.3, scale: 0.55 } }, bubble: { transform: { x: 0.5, y: -0.4, scale: 0.5 } } }) },
// sun / event off-canvas --------------------------------------------------
{ name: '06_moon-cropped-top', note: 'the sun cropped by the top edge — a rising disk',
comp: C('LUNA-3380', { disk: { transform: { x: 0.12, y: -1.02, scale: 1.4 }, pressure: 0.9 }, bubble: { transform: { x: -0.1, y: 0.2, scale: 0.6 } }, fiduciaries: { target: [-0.1, 0.2] } }) },
{ name: '07_event-off-corner', note: 'the collision bleeding off the lower-right corner',
comp: C('SPILL-9052', { bubble: { transform: { x: 1.08, y: 0.95, rotation: 0, scale: 1.15 } }, disk: { transform: { x: -0.4, y: -0.35, scale: 0.5 } }, fiduciaries: { target: [0.8, 0.7], from: [-0.2, -0.2] } }) },
{ name: '08_eclipse-overfill', note: 'the sun overfilling the frame — only the rim on-canvas',
comp: C('ECLIPSE-0001', { disk: { transform: { x: 0.05, y: 0.0, scale: 2.4 }, pressure: 0.95, hue: 0.0 }, bubble: { transform: { x: 0.7, y: -0.55, scale: 0.4 } }, fieldSea: { layerOpacity: 0.5 }, fiduciaries: { target: [0.7, -0.55], from: [0.2, -0.5] } }) },
// hierarchy / ground severed ---------------------------------------------
{ name: '09_dominant-grid', note: 'the grid is the subject; the sea a faint accent',
comp: C('LATTICE-8810', { fieldGrid: { opacity: 0.62, transform: { scale: 1.5 }, layerOpacity: 1 }, fieldSea: { layerOpacity: 0.4 }, disk: { transform: { scale: 0.55, y: -0.3 } } }) },
{ name: '10_crossing-fields', note: 'sea horizontal, grid rotated vertical — a woven space',
comp: C('WEAVE-1221', { fieldGrid: { transform: { rotation: 90 }, opacity: 0.42, pos: 'over' }, disk: { transform: { x: -0.15, y: -0.2, scale: 0.6 } }, bubble: { transform: { x: 0.3, y: 0.25 } } }) },
{ name: '11_night-plate', note: 'inverted ground — a dark deep-sky plate',
comp: C('NOCTURNE-2330', { background: { color: 'rgb(20,20,28)', glow: { strength: 0.35, followSun: true }, vignette: { strength: 0.2 }, film: { opacity: 0.3 }, grain: { opacity: 0.3 } }, fieldSea: { color: { hueBack: 0.58, hueFront: 0.54, sat: 0.4 } }, disk: { hue: 0.08, pressure: 0.4, intensity: 0.95 } }) },
{ name: '12_double-horizon', note: 'two horizons — a high grid line over the sea',
comp: C('STRATUM-7007', { fieldGrid: { originY: -0.22, pitch: 0.22, opacity: 0.4, pos: 'over' }, fieldSea: { horizonY: 0.58 }, disk: { transform: { x: 0.0, y: -0.62, scale: 0.45 } }, bubble: { transform: { x: 0.25, y: 0.2 } } }) },
// pure void / extreme placement ------------------------------------------
{ name: '13_vast-void', note: 'everything banished to the edges; a held emptiness',
comp: C('EMPTY-0000', { fieldSea: { transform: { rotation: 180, y: 0.0 }, clip: [0, 0, 1, 0.16], layerOpacity: 0.7 }, disk: { transform: { x: -0.92, y: -0.78, scale: 0.34 } }, bubble: { transform: { x: 0.9, y: 0.85, scale: 0.34 } }, fieldGrid: { enabled: false }, fiduciaries: { target: [0.9, 0.85], from: [0.4, 0.6] } }) },
{ name: '14_diagonal-world', note: 'the whole field canted into a diagonal sweep',
comp: C('CANT-1789', { fieldSea: { transform: { rotation: 22 } }, fieldGrid: { transform: { rotation: 22, scale: 1.2 }, yaw: 0.6, opacity: 0.4 }, disk: { transform: { x: -0.35, y: -0.35, scale: 0.55 } }, bubble: { transform: { x: 0.35, y: 0.35, rotation: 28, scale: 0.6 } } }) },
];
console.log(`liberation — ${V.length} compositions → ${OUT}/`);
for (const v of V) { writeFileSync(`${OUT}/${v.name}.svg`, renderComposition(v.comp, SIZE)); console.log(` ${v.name}${v.note}`); }
writeFileSync(`${OUT}/m.html`, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Liberation — severing the anchors</title>
<style>html,body{margin:0;background:#15140f;color:#cabfa6;font:12px ui-monospace,monospace}h1{font-weight:400;letter-spacing:.2em;text-transform:uppercase;color:#9fb7af;padding:18px 14px 0}.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;padding:14px}figure{margin:0;position:relative;background:#fff;overflow:hidden}img{width:100%;display:block}figcaption{position:absolute;left:0;bottom:0;right:0;padding:6px 9px;font:12px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
<h1>Liberation severing the anchors (${V.length})</h1>
<div class="grid">${V.map(v => `<figure><img src="${v.name}.svg"><figcaption>${v.note}</figcaption></figure>`).join('')}</div></body></html>`);
console.log(`-> ${OUT}/m.html`);