Files
bubblechambersimart/src/compose/templates.js
2026-06-02 20:32:41 -04:00

154 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
compose/templates.js — composition TEMPLATES: the geometric
skeletons of famous pictures, applied to our vocabulary. Each
template is a coherent point in the 7-axis composition space
(horizon ratio · focal placement · thrust · symmetry · figure:void
· atmosphere · palette). build(seed, i) → a full composition config;
the seed supplies content, the template supplies structure.
See docs/COMPOSITION-TEMPLATES.md.
============================================================ */
import { DEFAULT_COMPOSITION } from './schema.js';
const clone = (o) => JSON.parse(JSON.stringify(o));
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;
};
// variation per index: 0 = as-authored · 1 = mirror across X · 2 = tonal/horizon drift
function vary(c, i) {
if (i === 1) {
for (const g of ['disk', 'bubble', 'fiduciaries', 'fieldGrid', 'fieldSea'])
if (c[g] && c[g].transform) c[g].transform.x = -(c[g].transform.x || 0);
if (c.fieldGrid) { if (c.fieldGrid.yaw != null) c.fieldGrid.yaw = -c.fieldGrid.yaw; if (c.fieldGrid.originX != null) c.fieldGrid.originX = -c.fieldGrid.originX; }
if (c.fiduciaries && c.fiduciaries.from) c.fiduciaries.from = [-c.fiduciaries.from[0], c.fiduciaries.from[1]];
} else if (i === 2) {
if (c.fieldSea) {
c.fieldSea.horizonY = Math.min(0.74, (c.fieldSea.horizonY ?? 0.36) + 0.05);
if (c.fieldSea.color) { c.fieldSea.color.hueBack = (c.fieldSea.color.hueBack ?? 0.5) + 0.04; c.fieldSea.color.hueFront = (c.fieldSea.color.hueFront ?? 0.5) + 0.04; }
}
if (c.disk && c.disk.transform) c.disk.transform.y = (c.disk.transform.y || 0) - 0.04;
}
return c;
}
export const TEMPLATES = [
// 1 — HOKUSAI · dynamic asymmetry: low horizon, swelling sea, tiny low event,
// small high sun; indigo/Prussian. ×3
{
id: 'great-wave', name: 'Great Wave', source: 'Hokusai', reps: 3,
seeds: ['HOKUSAI-3311', 'KANAGAWA-1820', 'SURGE-7782'],
over: {
background: { color: 'rgb(224,223,213)', glow: { strength: 0.42, followSun: true }, vignette: { strength: 0.34 } },
fieldSea: { color: { hueBack: 0.62, hueFront: 0.58, sat: 0.55 }, chaos: 0.5, blips: 1.2, mound: 0.62, horizonY: 0.5, lines: 54 },
fieldGrid: { opacity: 0.3, pos: 'behind', color: { hue: 0.6, hue2: 0.58, sat: 0.3, lightNear: 0.34, lightFar: 0.62 }, pitch: 0.42, yaw: 0.5, dist: 3.0 },
disk: { transform: { x: 0.42, y: -0.5, rotation: 0, scale: 0.5 }, hue: 0.05, sat: 0.85, size: 0.13, pressure: 0.7 },
bubble: { transform: { x: -0.24, y: 0.3, rotation: -12, scale: 0.55 }, palette: 'magentarise', primaries: 16, sweepers: 6, eloss: 0.32 },
fiduciaries: { label: 'auto', from: [0.3, -0.4] },
},
},
// 2 — FRIEDRICH · the sublime void: horizon sliver low, one tiny cool sun, ~85%
// empty, no grid/fiduciaries. ×2
{
id: 'monk-by-sea', name: 'Monk by the Sea', source: 'Friedrich', reps: 2,
seeds: ['CASPAR-1810', 'RUCKEN-0042'],
over: {
background: { color: 'rgb(214,216,215)', glow: { strength: 0.5, followSun: false }, vignette: { strength: 0.28 } },
fieldSea: { color: { hueBack: 0.6, hueFront: 0.58, sat: 0.26 }, chaos: 0.18, blips: 0.4, mound: 0.12, horizonY: 0.72, lines: 40, layerOpacity: 0.82 },
fieldGrid: { enabled: false },
disk: { transform: { x: -0.06, y: 0.46, rotation: 0, scale: 0.32 }, hue: 0.58, sat: 0.42, size: 0.12, pressure: 0.4 },
bubble: { transform: { x: 0.12, y: 0.5, rotation: 0, scale: 0.3 }, palette: 'mono', primaries: 8, sweepers: 2, cosmics: 2, deltaRate: 0.4, layerOpacity: 0.8 },
fiduciaries: { enabled: false },
},
},
// 3 — ROTHKO · colour fields: flat band over a contrasting ground, centred seam,
// strong glow + vignette, no figure. ×2
{
id: 'rothko', name: 'Colour Fields', source: 'Rothko', reps: 2,
seeds: ['MARK-1957', 'SEAGRAM-0015'],
over: {
background: { color: 'rgb(178,108,94)', glow: { strength: 0.6, followSun: false }, vignette: { strength: 0.56 } },
fieldSea: { color: { hueBack: 0.99, hueFront: 0.02, sat: 0.5 }, chaos: 0.1, blips: 0.18, mound: 0.0, horizonY: 0.52, lines: 32, layerOpacity: 0.9 },
fieldGrid: { enabled: false }, disk: { enabled: false }, bubble: { enabled: false }, fiduciaries: { enabled: false },
},
},
// 4 — TURNER · atmospheric dissolution: high warm glow, low opacities everywhere,
// bright high sun, dissolving warm grid; gold-haze. ×3
{
id: 'turner', name: 'Atmospheric Dissolution', source: 'Turner', reps: 3,
seeds: ['JMW-1844', 'RAIN-STEAM-09', 'NORHAM-1798'],
over: {
background: { color: 'rgb(233,222,196)', glow: { strength: 0.95, followSun: true }, vignette: { strength: 0.24 } },
fieldSea: { color: { hueBack: 0.1, hueFront: 0.08, sat: 0.35 }, chaos: 0.3, blips: 0.6, mound: 0.3, horizonY: 0.46, lines: 46, layerOpacity: 0.58 },
fieldGrid: { opacity: 0.22, color: { hue: 0.1, hue2: 0.08, sat: 0.2, lightNear: 0.5, lightFar: 0.68 }, pitch: 0.3, yaw: 0.3, ripple: { amp: 0.4 }, layerOpacity: 0.7 },
disk: { transform: { x: 0.08, y: -0.34, rotation: 0, scale: 0.6 }, hue: 0.1, sat: 0.8, size: 0.18, pressure: 0.3, intensity: 0.95 },
bubble: { transform: { x: 0.12, y: -0.04, rotation: 0, scale: 0.7 }, palette: 'kindrise', saturation: 0.9, primaries: 16, eloss: 0.3, layerOpacity: 0.68 },
fiduciaries: { enabled: false },
},
},
// 5 — HIROSHIGE / MA · asymmetric negative space: forms to one corner, a bold
// two-point diagonal, a large quiet void opposite. ×2
{
id: 'ma', name: 'Ma · Negative Space', source: 'Hiroshige', reps: 2,
seeds: ['ANDO-1857', 'EDO-0100'],
over: {
background: { color: 'rgb(228,224,210)', glow: { strength: 0.4, followSun: false }, vignette: { strength: 0.3 } },
fieldSea: { color: { hueBack: 0.5, hueFront: 0.52, sat: 0.3 }, chaos: 0.22, blips: 0.5, mound: 0.2, horizonY: 0.42, lines: 42, layerOpacity: 0.8 },
fieldGrid: { opacity: 0.4, pos: 'over', color: { hue: 0.55, hue2: 0.5, sat: 0.28, lightNear: 0.34, lightFar: 0.64 }, pitch: 0.3, yaw: 0.72, dist: 2.6, originX: 0.3 },
disk: { transform: { x: 0.55, y: -0.46, rotation: 0, scale: 0.4 }, hue: 0.06, sat: 0.82, size: 0.12, pressure: 0.7 },
bubble: { transform: { x: 0.5, y: 0.4, rotation: 8, scale: 0.5 }, palette: 'magentarise', primaries: 14, sweepers: 5 },
fiduciaries: { label: 'auto', from: [0.2, -0.05] },
},
},
// 6 — MALEVICH · single form on void: one bold dark/saturated disk off-centre,
// sea off, a single faint tilted grid plane. ×2
{
id: 'suprematist', name: 'Single Form on Void', source: 'Malevich', reps: 2,
seeds: ['KAZIMIR-1915', 'BLACK-SQ-00'],
over: {
background: { color: 'rgb(233,230,220)', glow: { strength: 0.3, followSun: true }, vignette: { strength: 0.2 } },
fieldSea: { enabled: false },
fieldGrid: { opacity: 0.26, pos: 'behind', transform: { x: 0, y: 0, rotation: 18, scale: 1.1 }, pitch: 0.18, yaw: 0.0, color: { hue: 0.0, hue2: 0.02, sat: 0.18, lightNear: 0.4, lightFar: 0.66 } },
disk: { transform: { x: -0.2, y: -0.1, rotation: 0, scale: 1.0 }, hue: 0.0, sat: 0.92, size: 0.2, pressure: 0.95, intensity: 0.9, striations: 0.15 },
bubble: { transform: { x: 0.34, y: 0.28, rotation: 0, scale: 0.34 }, palette: 'mono', primaries: 7, sweepers: 1, deltaRate: 0.5, layerOpacity: 0.85, showBoundary: false, instrument: 0 },
fiduciaries: { enabled: false },
},
},
// 7 — RENAISSANCE φ · classical balance: sun on a φ point, event on the opposing
// third, horizon on the lower third, a measured grid. ×3
{
id: 'golden-thirds', name: 'Golden Section', source: 'Renaissance φ', reps: 3,
seeds: ['VITRUVIAN-1490', 'PHI-1618', 'URBINO-1465'],
over: {
background: { color: 'rgb(230,224,206)', glow: { strength: 0.55, followSun: true }, vignette: { strength: 0.35 } },
fieldSea: { color: { hueBack: 0.52, hueFront: 0.47, sat: 0.5 }, chaos: 0.28, blips: 0.7, mound: 0.3, horizonY: 0.62, lines: 46 },
fieldGrid: { opacity: 0.34, pos: 'over', pitch: 0.4, yaw: 0.38, color: { hue: 0.54, hue2: 0.5, sat: 0.28, lightNear: 0.34, lightFar: 0.64 } },
disk: { transform: { x: -0.236, y: -0.382, rotation: 0, scale: 0.6 }, hue: 0.07, sat: 0.82, size: 0.15, pressure: 0.8 },
bubble: { transform: { x: 0.236, y: 0.1, rotation: 8, scale: 0.62 }, palette: 'magentarise', primaries: 18, sweepers: 5 },
fiduciaries: { label: 'auto' },
},
},
// 8 — FRANK LLOYD WRIGHT · organic rectilinearity: low strong horizon, bold near-
// frontal orthogonal grid, flat horizontal sea, hearth-anchor disk (Cherokee
// red, dark core) off-centre; earthy Taliesin palette. ×3
{
id: 'wright', name: 'Cantilevered Horizontal', source: 'Frank Lloyd Wright', reps: 3,
seeds: ['TALIESIN-1911', 'FALLINGWATER-37', 'PRAIRIE-1901'],
over: {
background: { color: 'rgb(226,218,196)', glow: { strength: 0.45, followSun: true }, vignette: { strength: 0.34 } },
fieldSea: { color: { hueBack: 0.1, hueFront: 0.13, sat: 0.4 }, chaos: 0.15, blips: 0.3, mound: 0.05, horizonY: 0.6, lines: 40, layerOpacity: 0.85 },
fieldGrid: { opacity: 0.5, pos: 'over', style: 'floor', pitch: 0.5, yaw: 0.08, persp: 1.0, dist: 2.8, nx: 18, nz: 20, originY: 0.3, color: { hue: 0.08, hue2: 0.06, sat: 0.3, lightNear: 0.32, lightFar: 0.6 } },
disk: { transform: { x: -0.32, y: 0.02, rotation: 0, scale: 0.8 }, hue: 0.03, sat: 0.85, size: 0.16, pressure: 0.9, striations: 0.4 },
bubble: { transform: { x: 0.3, y: -0.05, rotation: 0, scale: 0.7 }, palette: 'kind', saturation: 0.95, primaries: 16, sweepers: 3, bfield: 1.2 },
fiduciaries: { label: 'auto' },
},
},
];
// build the composition for a template at variation index i
export function buildTemplate(t, i) {
const c = merge(clone(DEFAULT_COMPOSITION), t.over);
c.seed = t.seeds[i % t.seeds.length];
return vary(c, i);
}