Templates-Meta Art
This commit is contained in:
62
tools/liberation.mjs
Normal file
62
tools/liberation.mjs
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ============================================================
|
||||
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`);
|
||||
59
tools/templates.mjs
Normal file
59
tools/templates.mjs
Normal file
@@ -0,0 +1,59 @@
|
||||
/* templates.mjs — render the composition templates (famous-composition skeletons)
|
||||
to SVGs + .mjs configs + a contact sheet + an archive-wall MATRIX.
|
||||
Usage: node tools/templates.mjs [size] [outDir] [seedSalt]
|
||||
seedSalt → appended to each seed for a fresh batch (e.g. set 2). */
|
||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { TEMPLATES, buildTemplate } from '../src/compose/templates.js';
|
||||
import { renderComposition } from '../src/compose/composition.js';
|
||||
|
||||
const SIZE = +(process.argv[2] || 1300);
|
||||
const OUT = process.argv[3] || 'output/templates';
|
||||
const SALT = process.argv[4] || '';
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
|
||||
let n = 0;
|
||||
const items = [];
|
||||
for (const t of TEMPLATES) {
|
||||
for (let i = 0; i < t.reps; i++) {
|
||||
const comp = buildTemplate(t, i);
|
||||
if (SALT) comp.seed = comp.seed + '-' + SALT; // fresh event content, same grammar
|
||||
const name = `${String(++n).padStart(2, '0')}_${t.id}_v${i + 1}`;
|
||||
writeFileSync(`${OUT}/${name}.svg`, renderComposition(comp, SIZE));
|
||||
writeFileSync(`${OUT}/${name}.mjs`, `/* ${t.name} (${t.source}) · variation ${i + 1} */\nexport const composition = ${JSON.stringify(comp, null, 2)};\n`);
|
||||
items.push({ name, t, seed: comp.seed });
|
||||
console.log(` ${name} ${t.name} · ${t.source} · ${comp.seed}`);
|
||||
}
|
||||
}
|
||||
console.log(`templates — ${n} compositions → ${OUT}/`);
|
||||
|
||||
// plain contact sheet
|
||||
writeFileSync(`${OUT}/m.html`, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Composition templates · ${n}</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:13px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||
<h1>Composition templates (${n})</h1>
|
||||
<div class="grid">${items.map(it => `<figure><img src="${it.name}.svg"><figcaption>${it.t.name} · ${it.t.source}</figcaption></figure>`).join('')}</div></body></html>`);
|
||||
|
||||
// ARCHIVE-WALL MATRIX — the typology hung as a gallery wall (Becher energy):
|
||||
// warm wall, consistent matting + plate labels, catalogue numbers.
|
||||
const cols = 5;
|
||||
const matrix = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Traces of the Invisible — a catalogue of compositions</title>
|
||||
<style>
|
||||
html,body{margin:0;background:#17150f;color:#cabfa6}
|
||||
body{padding:60px 54px 72px;font:12px/1.5 ui-monospace,Menlo,monospace}
|
||||
.head{letter-spacing:.34em;text-transform:uppercase;font-size:14px;color:#9fb7af;margin:0 0 4px}
|
||||
.sub{color:#6f6a5c;font-size:12px;margin:0 0 40px;letter-spacing:.06em}
|
||||
.wall{display:grid;grid-template-columns:repeat(${cols},1fr);gap:46px 38px}
|
||||
figure{margin:0}
|
||||
.mat{background:#f3ecdb;padding:14px;box-shadow:0 2px 0 #0008,0 20px 44px -20px #000c;border:1px solid #000}
|
||||
.mat img{display:block;width:100%;aspect-ratio:1/1;object-fit:cover;background:#fff}
|
||||
figcaption{margin-top:10px;display:flex;justify-content:space-between;gap:8px;align-items:baseline}
|
||||
.cat{color:#9fb7af;letter-spacing:.1em}.src{color:#6f6a5c;font-style:italic}
|
||||
.ttl{color:#cabfa6;text-align:center;flex:1}
|
||||
</style></head><body>
|
||||
<p class="head">Traces of the Invisible — a catalogue of compositions</p>
|
||||
<p class="sub">the grammar of great pictures, applied to one instrument · ${n} plates${SALT ? ' · series ' + SALT : ''}</p>
|
||||
<div class="wall">
|
||||
${items.map((it, k) => `<figure><div class="mat"><img src="${it.name}.svg"></div>
|
||||
<figcaption><span class="cat">${String(k + 1).padStart(2, '0')}</span><span class="ttl">${it.t.name}</span><span class="src">${it.t.source}</span></figcaption></figure>`).join('\n')}
|
||||
</div></body></html>`;
|
||||
writeFileSync(`${OUT}/matrix.html`, matrix);
|
||||
console.log(`-> ${OUT}/m.html + ${OUT}/matrix.html`);
|
||||
Reference in New Issue
Block a user