vast ocean
450
output/_diag/bc04.svg
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
|
Before Width: | Height: | Size: 5.5 MiB After Width: | Height: | Size: 5.5 MiB |
|
Before Width: | Height: | Size: 5.7 MiB After Width: | Height: | Size: 5.7 MiB |
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 3.5 MiB |
|
Before Width: | Height: | Size: 12 MiB After Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 10 MiB After Width: | Height: | Size: 10 MiB |
|
Before Width: | Height: | Size: 5.7 MiB After Width: | Height: | Size: 5.7 MiB |
14
output/layering/composition/01_sunrise-on-horizon.svg
Normal file
|
After Width: | Height: | Size: 5.5 MiB |
14
output/layering/composition/02_sun-low-in-sea.svg
Normal file
|
After Width: | Height: | Size: 5.8 MiB |
14
output/layering/composition/03_high-sun-vast.svg
Normal file
|
After Width: | Height: | Size: 5.3 MiB |
14
output/layering/composition/04_offset-left.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/composition/05_offset-right-rise.svg
Normal file
|
After Width: | Height: | Size: 5.5 MiB |
13
output/layering/composition/06_centered-symmetry.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/composition/06_centered.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/composition/07_low-corner-sun.svg
Normal file
|
After Width: | Height: | Size: 5.8 MiB |
14
output/layering/composition/08_corner-dawn-left.svg
Normal file
|
After Width: | Height: | Size: 5.6 MiB |
13
output/layering/composition/08_lifted-rays.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
3
output/layering/composition/m.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid"><figure><img src="01_sunrise-on-horizon.svg"><figcaption>sunrise on horizon</figcaption></figure><figure><img src="02_sun-low-in-sea.svg"><figcaption>sun low in sea</figcaption></figure><figure><img src="03_high-sun-vast.svg"><figcaption>high sun vast</figcaption></figure><figure><img src="04_offset-left.svg"><figcaption>offset left</figcaption></figure><figure><img src="05_offset-right-rise.svg"><figcaption>offset right rise</figcaption></figure><figure><img src="06_centered.svg"><figcaption>centered</figcaption></figure><figure><img src="07_low-corner-sun.svg"><figcaption>low corner sun</figcaption></figure><figure><img src="08_corner-dawn-left.svg"><figcaption>corner dawn left</figcaption></figure></div></body></html>
|
||||||
14
output/layering/vast/01_distant-dawn.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/vast/02_small-sun-horizon.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/vast/03_corner-rise-left.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/vast/04_high-lonely.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/vast/05_mid-sea-jewel.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/vast/06_low-wide-sea.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
3
output/layering/vast/m.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid"><figure><img src="01_distant-dawn.svg"><figcaption>distant dawn</figcaption></figure><figure><img src="02_small-sun-horizon.svg"><figcaption>small sun horizon</figcaption></figure><figure><img src="03_corner-rise-left.svg"><figcaption>corner rise left</figcaption></figure><figure><img src="04_high-lonely.svg"><figcaption>high lonely</figcaption></figure><figure><img src="05_mid-sea-jewel.svg"><figcaption>mid sea jewel</figcaption></figure><figure><img src="06_low-wide-sea.svg"><figcaption>low wide sea</figcaption></figure></div></body></html>
|
||||||
14
output/layering/vast02/01_dawn-sweeping-arcs.svg
Normal file
|
After Width: | Height: | Size: 6.7 MiB |
14
output/layering/vast02/02_high-lonely-long.svg
Normal file
|
After Width: | Height: | Size: 6.6 MiB |
14
output/layering/vast02/03_corner-left-bigarcs.svg
Normal file
|
After Width: | Height: | Size: 5.3 MiB |
14
output/layering/vast02/04_low-wide-spirals.svg
Normal file
|
After Width: | Height: | Size: 7.1 MiB |
14
output/layering/vast02/05_mid-jewel-dense.svg
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
14
output/layering/vast02/06_right-rise-curl.svg
Normal file
|
After Width: | Height: | Size: 4.7 MiB |
14
output/layering/vast02/07_centered-long-reach.svg
Normal file
|
After Width: | Height: | Size: 6.5 MiB |
14
output/layering/vast02/08_distant-tiny-vast.svg
Normal file
|
After Width: | Height: | Size: 6.6 MiB |
14
output/layering/vast02/09_dawn-cosmic-streaks.svg
Normal file
|
After Width: | Height: | Size: 6.7 MiB |
14
output/layering/vast02/10_horizon-symmetry.svg
Normal file
|
After Width: | Height: | Size: 5.2 MiB |
3
output/layering/vast02/m.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid"><figure><img src="01_dawn-sweeping-arcs.svg"><figcaption>dawn sweeping arcs</figcaption></figure><figure><img src="02_high-lonely-long.svg"><figcaption>high lonely long</figcaption></figure><figure><img src="03_corner-left-bigarcs.svg"><figcaption>corner left bigarcs</figcaption></figure><figure><img src="04_low-wide-spirals.svg"><figcaption>low wide spirals</figcaption></figure><figure><img src="05_mid-jewel-dense.svg"><figcaption>mid jewel dense</figcaption></figure><figure><img src="06_right-rise-curl.svg"><figcaption>right rise curl</figcaption></figure><figure><img src="07_centered-long-reach.svg"><figcaption>centered long reach</figcaption></figure><figure><img src="08_distant-tiny-vast.svg"><figcaption>distant tiny vast</figcaption></figure><figure><img src="09_dawn-cosmic-streaks.svg"><figcaption>dawn cosmic streaks</figcaption></figure><figure><img src="10_horizon-symmetry.svg"><figcaption>horizon symmetry</figcaption></figure></div></body></html>
|
||||||
14
output/layering/vast03/01_burnt-orange-studied.svg
Normal file
|
After Width: | Height: | Size: 6.7 MiB |
14
output/layering/vast03/02_gold-sun-light-ring.svg
Normal file
|
After Width: | Height: | Size: 6.9 MiB |
14
output/layering/vast03/03_amber-reseau.svg
Normal file
|
After Width: | Height: | Size: 6.0 MiB |
14
output/layering/vast03/04_coral-red-spirals.svg
Normal file
|
After Width: | Height: | Size: 5.9 MiB |
14
output/layering/vast03/05_rose-pink-dawn.svg
Normal file
|
After Width: | Height: | Size: 5.1 MiB |
14
output/layering/vast03/06_copper-long-reach.svg
Normal file
|
After Width: | Height: | Size: 6.5 MiB |
14
output/layering/vast03/07_pale-white-hot.svg
Normal file
|
After Width: | Height: | Size: 6.6 MiB |
14
output/layering/vast03/08_cool-teal-sun.svg
Normal file
|
After Width: | Height: | Size: 5.4 MiB |
14
output/layering/vast03/09_magenta-monochrome.svg
Normal file
|
After Width: | Height: | Size: 5.2 MiB |
14
output/layering/vast03/10_deep-ember-studied.svg
Normal file
|
After Width: | Height: | Size: 7.6 MiB |
3
output/layering/vast03/m.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid"><figure><img src="01_burnt-orange-studied.svg"><figcaption>burnt orange studied</figcaption></figure><figure><img src="02_gold-sun-light-ring.svg"><figcaption>gold sun light ring</figcaption></figure><figure><img src="03_amber-reseau.svg"><figcaption>amber reseau</figcaption></figure><figure><img src="04_coral-red-spirals.svg"><figcaption>coral red spirals</figcaption></figure><figure><img src="05_rose-pink-dawn.svg"><figcaption>rose pink dawn</figcaption></figure><figure><img src="06_copper-long-reach.svg"><figcaption>copper long reach</figcaption></figure><figure><img src="07_pale-white-hot.svg"><figcaption>pale white hot</figcaption></figure><figure><img src="08_cool-teal-sun.svg"><figcaption>cool teal sun</figcaption></figure><figure><img src="09_magenta-monochrome.svg"><figcaption>magenta monochrome</figcaption></figure><figure><img src="10_deep-ember-studied.svg"><figcaption>deep ember studied</figcaption></figure></div></body></html>
|
||||||
14
output/layering/vast04/01_burnt-orange-pressure.svg
Normal file
|
After Width: | Height: | Size: 6.6 MiB |
14
output/layering/vast04/02_gold-mid-pressure.svg
Normal file
|
After Width: | Height: | Size: 6.8 MiB |
14
output/layering/vast04/03_amber-soft-core.svg
Normal file
|
After Width: | Height: | Size: 5.9 MiB |
14
output/layering/vast04/04_coral-deep-core.svg
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
14
output/layering/vast04/05_copper-pressure.svg
Normal file
|
After Width: | Height: | Size: 5.2 MiB |
14
output/layering/vast04/06_deep-ember-densecore.svg
Normal file
|
After Width: | Height: | Size: 5.5 MiB |
14
output/layering/vast04/07_rose-mid.svg
Normal file
|
After Width: | Height: | Size: 6.6 MiB |
14
output/layering/vast04/08_magenta-pressure.svg
Normal file
|
After Width: | Height: | Size: 6.5 MiB |
14
output/layering/vast04/09_graphite-on-orange.svg
Normal file
|
After Width: | Height: | Size: 5.3 MiB |
14
output/layering/vast04/10_graphite-on-ember.svg
Normal file
|
After Width: | Height: | Size: 5.9 MiB |
3
output/layering/vast04/m.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid"><figure><img src="01_burnt-orange-pressure.svg"><figcaption>burnt orange pressure</figcaption></figure><figure><img src="02_gold-mid-pressure.svg"><figcaption>gold mid pressure</figcaption></figure><figure><img src="03_amber-soft-core.svg"><figcaption>amber soft core</figcaption></figure><figure><img src="04_coral-deep-core.svg"><figcaption>coral deep core</figcaption></figure><figure><img src="05_copper-pressure.svg"><figcaption>copper pressure</figcaption></figure><figure><img src="06_deep-ember-densecore.svg"><figcaption>deep ember densecore</figcaption></figure><figure><img src="07_rose-mid.svg"><figcaption>rose mid</figcaption></figure><figure><img src="08_magenta-pressure.svg"><figcaption>magenta pressure</figcaption></figure><figure><img src="09_graphite-on-orange.svg"><figcaption>graphite on orange</figcaption></figure><figure><img src="10_graphite-on-ember.svg"><figcaption>graphite on ember</figcaption></figure></div></body></html>
|
||||||
97
output/qft/carpet/vertical/01_calm-curtain.svg
Normal file
|
After Width: | Height: | Size: 870 KiB |
109
output/qft/carpet/vertical/02_seething-strings.svg
Normal file
|
After Width: | Height: | Size: 985 KiB |
65
output/qft/carpet/vertical/03_rays-plate.svg
Normal file
|
After Width: | Height: | Size: 570 KiB |
3
output/qft/carpet/vertical/m.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:10px;width:2100px}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 10px;font:13px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid"><figure><img src="01_calm-curtain.svg"><figcaption>calm vertical curtain</figcaption></figure><figure><img src="02_seething-strings.svg"><figcaption>seething vertical strings</figcaption></figure><figure><img src="03_rays-plate.svg"><figcaption>fine vertical rays (plate)</figcaption></figure></div></body></html>
|
||||||
@@ -128,9 +128,12 @@ export function carpetSVG(size, opts = {}) {
|
|||||||
body += `<path d="${path}" fill="none" stroke="${col}" stroke-width="${sw.toFixed(2)}" stroke-opacity="${op}" stroke-linecap="round" stroke-linejoin="round"/>\n`;
|
body += `<path d="${path}" fill="none" stroke="${col}" stroke-width="${sw.toFixed(2)}" stroke-opacity="${op}" stroke-linecap="round" stroke-linejoin="round"/>\n`;
|
||||||
}
|
}
|
||||||
const bg = transparent ? '' : `<rect width="${W}" height="${H}" fill="${css(paper)}"/>`;
|
const bg = transparent ? '' : `<rect width="${W}" height="${H}" fill="${css(paper)}"/>`;
|
||||||
|
// orient: 'vertical' rotates the whole field 90° → lines run vertically and
|
||||||
|
// recede to a vanishing edge (a curtain / rays of vacuum strings).
|
||||||
|
const inner = o.orient === 'vertical' ? `<g transform="rotate(-90 ${(W / 2)} ${(H / 2)})">${body}</g>` : body;
|
||||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
|
<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
|
||||||
${bg}
|
${bg}
|
||||||
<g>${body}</g>
|
<g>${inner}</g>
|
||||||
</svg>`;
|
</svg>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ export function renderSVG(scene, params, sizePx = 4800) {
|
|||||||
const paperRGB = pal.paper(); // {flat,glowIn,glowOut} rgb arrays
|
const paperRGB = pal.paper(); // {flat,glowIn,glowOut} rgb arrays
|
||||||
const paperC = { flat: rgbHex(paperRGB.flat), glowIn: rgbHex(paperRGB.glowIn), glowOut: rgbHex(paperRGB.glowOut) };
|
const paperC = { flat: rgbHex(paperRGB.flat), glowIn: rgbHex(paperRGB.glowIn), glowOut: rgbHex(paperRGB.glowOut) };
|
||||||
const ink = rgbHex(pal.feature()); // non-particle marks (optics, disk, damage, header)
|
const ink = rgbHex(pal.feature()); // non-particle marks (optics, disk, damage, header)
|
||||||
|
// diskPressure: a darker, denser core (compressed gas = higher pressure)
|
||||||
|
const press = Math.max(0, Math.min(1, params.diskPressure ?? 0));
|
||||||
|
const coreInk = rgbHex(mix(pal.feature(), [0, 0, 0], press * 0.7));
|
||||||
const featKey = rgbKey(pal.feature());
|
const featKey = rgbKey(pal.feature());
|
||||||
const colorMap = new Map([[featKey, pal.feature()]]); // distinct bubble colours → gradients
|
const colorMap = new Map([[featKey, pal.feature()]]); // distinct bubble colours → gradients
|
||||||
|
|
||||||
@@ -52,7 +55,9 @@ export function renderSVG(scene, params, sizePx = 4800) {
|
|||||||
content ? `<g id="${id}" inkscape:groupmode="layer" inkscape:label="${attr(label)}" style="display:inline"${extra ? ' ' + extra : ''}>\n${content}\n</g>\n` : '';
|
content ? `<g id="${id}" inkscape:groupmode="layer" inkscape:label="${attr(label)}" style="display:inline"${extra ? ' ' + extra : ''}>\n${content}\n</g>\n` : '';
|
||||||
|
|
||||||
/* ---------- Background ---------- */
|
/* ---------- Background ---------- */
|
||||||
const bg = `<rect width="${w}" height="${h}" fill="${paperC.flat}"/>\n<rect width="${w}" height="${h}" fill="url(#paper)"/>`;
|
// transparentPaper: omit paper + vignette so the event can float as an object
|
||||||
|
// over another layer (e.g. the QFT carpet) without a paper rectangle.
|
||||||
|
const bg = params.transparentPaper ? '' : `<rect width="${w}" height="${h}" fill="${paperC.flat}"/>\n<rect width="${w}" height="${h}" fill="url(#paper)"/>`;
|
||||||
|
|
||||||
/* ---------- Chamber optics ---------- */
|
/* ---------- Chamber optics ---------- */
|
||||||
let optics = '';
|
let optics = '';
|
||||||
@@ -206,7 +211,7 @@ export function renderSVG(scene, params, sizePx = 4800) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Vignette ---------- */
|
/* ---------- Vignette ---------- */
|
||||||
const vign = params.vign > 0 ? `<rect width="${w}" height="${h}" fill="url(#vign)"/>` : '';
|
const vign = (params.vign > 0 && !params.transparentPaper) ? `<rect width="${w}" height="${h}" fill="url(#vign)"/>` : '';
|
||||||
|
|
||||||
/* ---------- Header ---------- */
|
/* ---------- Header ---------- */
|
||||||
let header = '';
|
let header = '';
|
||||||
@@ -258,7 +263,8 @@ export function renderSVG(scene, params, sizePx = 4800) {
|
|||||||
media += `</g>`;
|
media += `</g>`;
|
||||||
}
|
}
|
||||||
if (M.grease && M.grease.length) {
|
if (M.grease && M.grease.length) {
|
||||||
const ch = inv ? '#9c1e1e' : '#f0e296';
|
// annotateInk overrides the chinagraph colour (e.g. white pencil)
|
||||||
|
const ch = params.annotateInk || (inv ? '#9c1e1e' : '#f0e296');
|
||||||
let g = `<g stroke="${ch}" fill="${ch}" stroke-linecap="round" stroke-linejoin="round">`;
|
let g = `<g stroke="${ch}" fill="${ch}" stroke-linecap="round" stroke-linejoin="round">`;
|
||||||
for (const gm of M.grease) {
|
for (const gm of M.grease) {
|
||||||
if (gm.kind === 'ring' || gm.kind === 'arrow') {
|
if (gm.kind === 'ring' || gm.kind === 'arrow') {
|
||||||
@@ -288,7 +294,7 @@ export function renderSVG(scene, params, sizePx = 4800) {
|
|||||||
let s = `<?xml version="1.0" encoding="UTF-8"?>\n`;
|
let s = `<?xml version="1.0" encoding="UTF-8"?>\n`;
|
||||||
s += `<svg xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">\n`;
|
s += `<svg xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">\n`;
|
||||||
s += `<metadata>Bubble Chamber · seed=${params.seed} · hash=${cyrb53(params.seed)} · palette=${params.palette || 'mono'}</metadata>\n`;
|
s += `<metadata>Bubble Chamber · seed=${params.seed} · hash=${cyrb53(params.seed)} · palette=${params.palette || 'mono'}</metadata>\n`;
|
||||||
s += defs({ paperC, ink, baseVign: pal.vign(), params, u, colorMap });
|
s += defs({ paperC, ink, coreInk, press, baseVign: pal.vign(), params, u, colorMap });
|
||||||
s += layer('background', 'Background', bg);
|
s += layer('background', 'Background', bg);
|
||||||
s += layer('optics', 'Chamber optics', optics);
|
s += layer('optics', 'Chamber optics', optics);
|
||||||
s += layer('shock', 'Shock disk', shock, params.diskSoften > 0 ? 'filter="url(#soften)"' : '');
|
s += layer('shock', 'Shock disk', shock, params.diskSoften > 0 ? 'filter="url(#soften)"' : '');
|
||||||
@@ -302,7 +308,7 @@ export function renderSVG(scene, params, sizePx = 4800) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function defs({ paperC, ink, baseVign, params, u, colorMap }) {
|
function defs({ paperC, ink, coreInk, press = 0, baseVign, params, u, colorMap }) {
|
||||||
const soften = params.diskSoften > 0
|
const soften = params.diskSoften > 0
|
||||||
? `<filter id="soften" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur stdDeviation="${(params.diskSoften * u).toFixed(2)}"/></filter>`
|
? `<filter id="soften" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur stdDeviation="${(params.diskSoften * u).toFixed(2)}"/></filter>`
|
||||||
: '';
|
: '';
|
||||||
@@ -324,8 +330,9 @@ function defs({ paperC, ink, baseVign, params, u, colorMap }) {
|
|||||||
</radialGradient>
|
</radialGradient>
|
||||||
${bubGrads}
|
${bubGrads}
|
||||||
<radialGradient id="shockcore" cx="50%" cy="50%" r="50%">
|
<radialGradient id="shockcore" cx="50%" cy="50%" r="50%">
|
||||||
<stop offset="0%" stop-color="${ink}" stop-opacity="0.5"/>
|
<stop offset="0%" stop-color="${coreInk || ink}" stop-opacity="${(0.5 + press * 0.45).toFixed(2)}"/>
|
||||||
<stop offset="60%" stop-color="${ink}" stop-opacity="0.28"/>
|
<stop offset="28%" stop-color="${coreInk || ink}" stop-opacity="${(0.32 + press * 0.32).toFixed(2)}"/>
|
||||||
|
<stop offset="62%" stop-color="${ink}" stop-opacity="0.28"/>
|
||||||
<stop offset="100%" stop-color="${ink}" stop-opacity="0"/>
|
<stop offset="100%" stop-color="${ink}" stop-opacity="0"/>
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
<radialGradient id="shockstain" cx="50%" cy="50%" r="50%">
|
<radialGradient id="shockstain" cx="50%" cy="50%" r="50%">
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export function generateMedia(params, scene, rng) {
|
|||||||
/* ---- grease-pencil (chinagraph) hand marks ---- */
|
/* ---- grease-pencil (chinagraph) hand marks ---- */
|
||||||
if ((params.annotate ?? 0) > 0) {
|
if ((params.annotate ?? 0) > 0) {
|
||||||
const full = params.annotate >= 0.66; // restrained vs studied
|
const full = params.annotate >= 0.66; // restrained vs studied
|
||||||
|
const aw = params.annoWidth ?? 1; // line-weight multiplier (thinner = <1)
|
||||||
const v = scene.vertex || { x: 0, y: 0 };
|
const v = scene.vertex || { x: 0, y: 0 };
|
||||||
// ring a δ-ray curl if there is one, else the vertex region
|
// ring a δ-ray curl if there is one, else the vertex region
|
||||||
const deltas = scene.tracks.filter(t => t.kind === 'delta' && t.pts.length > 6);
|
const deltas = scene.tracks.filter(t => t.kind === 'delta' && t.pts.length > 6);
|
||||||
@@ -50,19 +51,22 @@ export function generateMedia(params, scene, rng) {
|
|||||||
const d = pick(rng, deltas), p = d.pts[Math.floor(d.pts.length * 0.4)];
|
const d = pick(rng, deltas), p = d.pts[Math.floor(d.pts.length * 0.4)];
|
||||||
cx = p.x; cy = p.y; cr = 0.05 + rng() * 0.035;
|
cx = p.x; cy = p.y; cr = 0.05 + rng() * 0.035;
|
||||||
} else { cx = v.x + gauss(rng) * 0.05; cy = v.y + gauss(rng) * 0.05; cr = 0.11 + rng() * 0.05; }
|
} else { cx = v.x + gauss(rng) * 0.05; cy = v.y + gauss(rng) * 0.05; cr = 0.11 + rng() * 0.05; }
|
||||||
out.grease.push({ kind: 'ring', pts: wobblyRing(cx, cy, cr, rng), width: 2.4 });
|
if (params.annoRing !== false) out.grease.push({ kind: 'ring', pts: wobblyRing(cx, cy, cr, rng), width: 2.4 * aw });
|
||||||
|
|
||||||
// arrow from open space toward the ring
|
// arrow from open space toward the ring/find
|
||||||
const side = cx <= 0 ? 1 : -1;
|
if (params.annoArrow !== false) {
|
||||||
const ax = cx + side * (0.34 + rng() * 0.12), ay = cy - (0.30 + rng() * 0.12);
|
const side = cx <= 0 ? 1 : -1;
|
||||||
const tipx = cx - side * cr * 1.25, tipy = cy - cr * 1.15;
|
const ax = cx + side * (0.34 + rng() * 0.12), ay = cy - (0.30 + rng() * 0.12);
|
||||||
const ang = Math.atan2(tipy - ay, tipx - ax);
|
const tipx = cx - side * cr * 1.25, tipy = cy - cr * 1.15;
|
||||||
out.grease.push({ kind: 'arrow', shaft: wobblyLine(ax, ay, tipx, tipy, rng), tip: { x: tipx, y: tipy, ang }, width: 2.0 });
|
const ang = Math.atan2(tipy - ay, tipx - ax);
|
||||||
|
out.grease.push({ kind: 'arrow', shaft: wobblyLine(ax, ay, tipx, tipy, rng), tip: { x: tipx, y: tipy, ang }, width: 2.0 * aw });
|
||||||
|
}
|
||||||
|
|
||||||
// event number by the ring
|
// event number by the ring/find
|
||||||
out.grease.push({ kind: 'text', x: cx + cr * 1.05, y: cy - cr * 1.05, text: `Nº ${scene.plate}`, size: 0.045, rot: -0.05 + gauss(rng) * 0.03 });
|
if (params.annoNum !== false)
|
||||||
|
out.grease.push({ kind: 'text', x: cx + cr * 1.05, y: cy - cr * 1.05, text: params.annoLabel ?? `Nº ${scene.plate}`, size: 0.045, rot: -0.05 + gauss(rng) * 0.03 });
|
||||||
|
|
||||||
if (full) {
|
if (full && params.annoExtras !== false) {
|
||||||
// angle arc + reading at the vertex
|
// angle arc + reading at the vertex
|
||||||
const a0 = rng() * TWO_PI, a1 = a0 + (0.5 + rng() * 0.6);
|
const a0 = rng() * TWO_PI, a1 = a0 + (0.5 + rng() * 0.6);
|
||||||
out.grease.push({ kind: 'arc', x: v.x, y: v.y, r: 0.12 + rng() * 0.05, a0, a1, width: 1.6 });
|
out.grease.push({ kind: 'arc', x: v.x, y: v.y, r: 0.12 + rng() * 0.05, a0, a1, width: 1.6 });
|
||||||
|
|||||||
@@ -96,8 +96,11 @@ export function generateScene(params) {
|
|||||||
const rng = makeRng(params.seed, 'scene');
|
const rng = makeRng(params.seed, 'scene');
|
||||||
const tracks = [];
|
const tracks = [];
|
||||||
|
|
||||||
// Foreground event, slightly off-centre — near the focal plane, current trigger
|
// Foreground event, slightly off-centre — near the focal plane, current trigger.
|
||||||
const fgVertex = { x: (rng() - 0.5) * 0.3, y: (rng() - 0.5) * 0.3 };
|
// eventX/eventY (in [-1,1] canvas coords) override the seed placement for
|
||||||
|
// deliberate composition; rng() is still consumed so the rest stays deterministic.
|
||||||
|
const rvx = (rng() - 0.5) * 0.3, rvy = (rng() - 0.5) * 0.3;
|
||||||
|
const fgVertex = { x: params.eventX ?? rvx, y: params.eventY ?? rvy };
|
||||||
const fgZ = gauss(rng) * 0.12;
|
const fgZ = gauss(rng) * 0.12;
|
||||||
tracks.push(...generateOneEvent(params, fgVertex, 1.0, 'fg', fgZ, 0));
|
tracks.push(...generateOneEvent(params, fgVertex, 1.0, 'fg', fgZ, 0));
|
||||||
|
|
||||||
|
|||||||
@@ -39,49 +39,99 @@ function grainSVG(o = {}) {
|
|||||||
</filter></defs><rect width="${SIZE}" height="${SIZE}" filter="url(#g)"/></svg>`;
|
</filter></defs><rect width="${SIZE}" height="${SIZE}" filter="url(#g)"/></svg>`;
|
||||||
}
|
}
|
||||||
// ---- bubble-chamber event ----
|
// ---- bubble-chamber event ----
|
||||||
function bcSVG(seed, over = {}) {
|
function bcSVG(seed, over = {}, float = false) {
|
||||||
const p = { ...FIXED, ...bcParams(seed) };
|
const p = { ...FIXED, ...bcParams(seed) };
|
||||||
for (const g of GROUPS) for (const c of g.controls) if (!(c.id in p)) p[c.id] = c.value;
|
for (const g of GROUPS) for (const c of g.controls) if (!(c.id in p)) p[c.id] = c.value;
|
||||||
for (const t of TOGGLES) if (!(t.id in p)) p[t.id] = t.value;
|
for (const t of TOGGLES) if (!(t.id in p)) p[t.id] = t.value;
|
||||||
p.invert = true; p.showHeader = false;
|
p.invert = true; p.showHeader = false;
|
||||||
|
if (float) p.transparentPaper = true; // no paper rect → event floats as an object
|
||||||
Object.assign(p, over);
|
Object.assign(p, over);
|
||||||
return renderSVG(generateScene(p), p, SIZE);
|
const scene = generateScene(p);
|
||||||
|
const sh = scene.shock ? { x: scene.shock.x, y: scene.shock.y } : { x: 0, y: 0 };
|
||||||
|
return { svg: renderSVG(scene, p, SIZE), sh }; // sh = disk centre (normalized) for anchoring
|
||||||
}
|
}
|
||||||
|
|
||||||
// build a 3-sheet carpet deck for a variation (hue gradient + chaos rising to front)
|
// AGING layer: sparse scratches + dust speckle + foxing/stain blotches. Seeded.
|
||||||
|
function agingSVG(o = {}) {
|
||||||
|
const { seed = 5, scratches = 7, dust = 0.5, foxing = 0.5, tone = [60, 48, 34] } = o;
|
||||||
|
const t = tone.map(v => (v / 255).toFixed(3));
|
||||||
|
let lcg = (seed * 9301 + 49297) % 233280; const rnd = () => (lcg = (lcg * 9301 + 49297) % 233280) / 233280;
|
||||||
|
let lines = '';
|
||||||
|
for (let i = 0; i < scratches; i++) {
|
||||||
|
const x = rnd() * SIZE, y0 = rnd() * SIZE * 0.4, len = (0.3 + rnd() * 0.6) * SIZE;
|
||||||
|
const x2 = x + (rnd() - 0.5) * 40, y2 = y0 + len;
|
||||||
|
const op = (0.05 + rnd() * 0.12).toFixed(3), wsc = (0.4 + rnd() * 0.8).toFixed(2);
|
||||||
|
lines += `<line x1="${x.toFixed(0)}" y1="${y0.toFixed(0)}" x2="${x2.toFixed(0)}" y2="${y2.toFixed(0)}" stroke="rgb(${tone.join(',')})" stroke-opacity="${op}" stroke-width="${wsc}"/>`;
|
||||||
|
}
|
||||||
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${SIZE}" height="${SIZE}" viewBox="0 0 ${SIZE} ${SIZE}">
|
||||||
|
<defs>
|
||||||
|
<filter id="dust" x="0" y="0" width="100%" height="100%"><feTurbulence type="turbulence" baseFrequency="0.5" numOctaves="2" seed="${seed + 5}" stitchTiles="stitch" result="n"/><feColorMatrix in="n" type="matrix" values="0 0 0 0 ${t[0]} 0 0 0 0 ${t[1]} 0 0 0 0 ${t[2]} 0 0 0 ${(dust * 0.5).toFixed(3)} -0.18"/></filter>
|
||||||
|
<filter id="fox" x="0" y="0" width="100%" height="100%"><feTurbulence type="fractalNoise" baseFrequency="0.004" numOctaves="3" seed="${seed + 9}" stitchTiles="stitch" result="n"/><feColorMatrix in="n" type="matrix" values="0 0 0 0 0.42 0 0 0 0 0.30 0 0 0 0 0.16 0 0 0 ${(foxing * 0.32).toFixed(3)} -0.12"/></filter>
|
||||||
|
</defs>
|
||||||
|
<rect width="${SIZE}" height="${SIZE}" filter="url(#fox)"/>
|
||||||
|
<rect width="${SIZE}" height="${SIZE}" filter="url(#dust)"/>
|
||||||
|
${lines}</svg>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a 3-sheet carpet deck for a variation.
|
||||||
|
// PROGRESSION back→front: thick lines (+ heavy blur in the composite) → fine, crisp.
|
||||||
function deck(c) {
|
function deck(c) {
|
||||||
const base = { mode: 'plate', rows: 46, horizon: c.horizon ?? 0.37, wFar: 0.58, wNear: 0.7,
|
const base = { mode: 'plate', rows: 46, horizon: c.horizon ?? 0.37, wFar: 0.58, wNear: 0.7,
|
||||||
overlap: c.overlap ?? 1.7, mound: c.mound ?? 0.35, sat: c.sat ?? 0.58, lightNear: 0.33, lightFar: 0.56, blips: c.blips ?? 1.0 };
|
overlap: c.overlap ?? 1.7, mound: c.mound ?? 0.35, sat: c.sat ?? 0.58, lightNear: 0.33, lightFar: 0.56, blips: c.blips ?? 1.0 };
|
||||||
const lerp = (a, b, t) => a + (b - a) * t;
|
const lerp = (a, b, t) => a + (b - a) * t;
|
||||||
|
// per-sheet stroke weights: back coarse, front fine
|
||||||
|
const strokes = [{ near: 2.6, far: 1.0 }, { near: 1.6, far: 0.6 }, { near: 1.0, far: 0.38 }];
|
||||||
return [0, 1, 2].map(i => {
|
return [0, 1, 2].map(i => {
|
||||||
const t = i / 2; // 0 back → 1 front
|
const t = i / 2; // 0 back → 1 front
|
||||||
return carpetSVG(SIZE, { ...base, salt: 'field' + i,
|
return carpetSVG(SIZE, { ...base, salt: 'field' + i,
|
||||||
hue: lerp(c.hueBack, c.hueFront, t), hue2: lerp(c.hueBack, c.hueFront, t) + 0.035,
|
hue: lerp(c.hueBack, c.hueFront, t), hue2: lerp(c.hueBack, c.hueFront, t) + 0.035,
|
||||||
chaos: lerp((c.chaos ?? 0.6) * 0.8, c.chaos ?? 0.6, t) });
|
chaos: lerp((c.chaos ?? 0.6) * 0.8, c.chaos ?? 0.6, t),
|
||||||
|
strokeNear: strokes[i].near, strokeFar: strokes[i].far });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function compose(v) {
|
function compose(v) {
|
||||||
const film = dataUri(filmSVG(v.film));
|
const film = dataUri(filmSVG(v.film));
|
||||||
const sheets = deck(v.carpet).map(dataUri);
|
const sheets = deck(v.carpet).map(dataUri);
|
||||||
const bc = dataUri(bcSVG(v.bcSeed, v.bcOver));
|
const float = !!v.bcFloat;
|
||||||
|
const { svg: bcRaw, sh } = bcSVG(v.bcSeed, v.bcOver, float);
|
||||||
|
const bc = dataUri(bcRaw);
|
||||||
const grain = dataUri(grainSVG(v.grain));
|
const grain = dataUri(grainSVG(v.grain));
|
||||||
|
const aging = v.aging ? dataUri(agingSVG(v.aging)) : null;
|
||||||
const base = v.base || 'rgb(226,219,199)';
|
const base = v.base || 'rgb(226,219,199)';
|
||||||
|
const blend = float ? 'normal' : 'multiply'; // floating event = transparent paper, normal blend
|
||||||
|
|
||||||
|
// BC placement. sunAt:[sx,sy] (frame coords -1..1) anchors on the DISK centre and
|
||||||
|
// scales the whole event about it → the sun lands exactly there, never clipped.
|
||||||
|
let bcEl;
|
||||||
|
if (v.sunAt) {
|
||||||
|
const s = v.bcScale ?? 1, scB = (SIZE / 2) * (1 - 0.02);
|
||||||
|
const dpx = SIZE / 2 + sh.x * scB, dpy = SIZE / 2 + sh.y * scB; // disk px in BC canvas
|
||||||
|
const fx = SIZE / 2 + v.sunAt[0] * (SIZE / 2), fy = SIZE / 2 + v.sunAt[1] * (SIZE / 2);
|
||||||
|
bcEl = `<g transform="translate(${(fx - s * dpx).toFixed(1)} ${(fy - s * dpy).toFixed(1)}) scale(${s})" style="mix-blend-mode:${blend}"><image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${bc}"/></g>`;
|
||||||
|
} else {
|
||||||
|
const s = v.bcScale ?? 1, dx = (v.bcDx || 0) * SIZE, dy = (v.bcDy || 0) * SIZE;
|
||||||
|
const bcW = SIZE * s, bcX = (SIZE - bcW) / 2 + dx, bcY = (SIZE - bcW) / 2 + dy;
|
||||||
|
bcEl = `<image x="${bcX.toFixed(0)}" y="${bcY.toFixed(0)}" width="${bcW.toFixed(0)}" height="${bcW.toFixed(0)}" href="${bc}" style="mix-blend-mode:${blend}"/>`;
|
||||||
|
}
|
||||||
|
const dir = v.dir ? `${OUT}/${v.dir}` : OUT;
|
||||||
|
if (v.dir) mkdirSync(dir, { recursive: true });
|
||||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${SIZE}" height="${SIZE}" viewBox="0 0 ${SIZE} ${SIZE}">
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${SIZE}" height="${SIZE}" viewBox="0 0 ${SIZE} ${SIZE}">
|
||||||
<defs>
|
<defs>
|
||||||
<filter id="b3" x="-5%" y="-5%" width="110%" height="110%"><feGaussianBlur stdDeviation="${(2.4 * u).toFixed(2)}"/></filter>
|
<filter id="b3" x="-8%" y="-8%" width="116%" height="116%"><feGaussianBlur stdDeviation="${(2.6 * u).toFixed(2)}"/></filter>
|
||||||
<filter id="b2" x="-5%" y="-5%" width="110%" height="110%"><feGaussianBlur stdDeviation="${(1.1 * u).toFixed(2)}"/></filter>
|
<filter id="b2" x="-6%" y="-6%" width="112%" height="112%"><feGaussianBlur stdDeviation="${(1.1 * u).toFixed(2)}"/></filter>
|
||||||
</defs>
|
</defs>
|
||||||
<rect width="${SIZE}" height="${SIZE}" fill="${base}"/>
|
<rect width="${SIZE}" height="${SIZE}" fill="${base}"/>
|
||||||
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${film}" opacity="0.6"/>
|
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${film}" opacity="0.6"/>
|
||||||
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${sheets[0]}" filter="url(#b3)" opacity="0.5"/>
|
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${sheets[0]}" filter="url(#b3)" opacity="0.5"/>
|
||||||
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${sheets[1]}" filter="url(#b2)" opacity="0.72"/>
|
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${sheets[1]}" filter="url(#b2)" opacity="0.72"/>
|
||||||
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${sheets[2]}" opacity="0.95"/>
|
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${sheets[2]}" opacity="0.95"/>
|
||||||
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${bc}" style="mix-blend-mode:multiply"/>
|
${bcEl}
|
||||||
|
${aging ? `<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${aging}" opacity="${v.agingOpacity ?? 0.6}" style="mix-blend-mode:multiply"/>` : ''}
|
||||||
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${grain}" opacity="${v.grainOpacity ?? 0.45}" style="mix-blend-mode:multiply"/>
|
<image x="0" y="0" width="${SIZE}" height="${SIZE}" href="${grain}" opacity="${v.grainOpacity ?? 0.45}" style="mix-blend-mode:multiply"/>
|
||||||
</svg>`;
|
</svg>`;
|
||||||
writeFileSync(`${OUT}/${v.name}.svg`, svg);
|
writeFileSync(`${dir}/${v.name}.svg`, svg);
|
||||||
console.log(` ${v.name} (bc=${v.bcSeed})`);
|
console.log(` ${v.dir ? v.dir + '/' : ''}${v.name} (bc=${v.bcSeed})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -123,6 +173,177 @@ const VARIATIONS = [
|
|||||||
console.log(`Layered piece — ${VARIATIONS.length} variations → ${OUT}/`);
|
console.log(`Layered piece — ${VARIATIONS.length} variations → ${OUT}/`);
|
||||||
for (const v of VARIATIONS) compose(v);
|
for (const v of VARIATIONS) compose(v);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// COMPOSITION study — fixed magenta/teal deck (the 02 look), only the
|
||||||
|
// BC event's COMPOSITION changes: vertex placement (eventX/Y), disk height
|
||||||
|
// (shockY), and placement in frame (bcScale/bcDy). Same palette / density /
|
||||||
|
// arc / disk style throughout. Chasing "sunrise over a vast ocean".
|
||||||
|
// ============================================================
|
||||||
|
const TEAL = { hueBack: 0.54, hueFront: 0.47, chaos: 0.42, blips: 1.0, mound: 0.35 };
|
||||||
|
const tealFilm = { seed: 8, density: 0.5 }, tealGrain = { amount: 0.4 };
|
||||||
|
// place the SUN (disk) and the event VERTEX together so tracks radiate from it
|
||||||
|
const sun = (name, px, py) => ({
|
||||||
|
name, dir: 'composition', base: 'rgb(228,221,201)', film: tealFilm, grain: tealGrain, carpet: TEAL,
|
||||||
|
bcSeed: 'MESON-5113',
|
||||||
|
bcOver: { palette: 'magentarise', saturation: 1.05, shockX: px, shockY: py, eventX: px, eventY: py * 0.9 },
|
||||||
|
});
|
||||||
|
const COMPOSITION = [
|
||||||
|
sun('01_sunrise-on-horizon', 0, -0.26), // sun resting on the horizon, rays up
|
||||||
|
sun('02_sun-low-in-sea', 0, 0.46), // low in the water — reflected/setting
|
||||||
|
sun('03_high-sun-vast', 0, -0.5), // small high sun, vast sea below
|
||||||
|
sun('04_offset-left', -0.46, -0.12),
|
||||||
|
sun('05_offset-right-rise', 0.44, -0.06),
|
||||||
|
sun('06_centered', 0, 0.0),
|
||||||
|
sun('07_low-corner-sun', 0.42, 0.34), // rising in the lower-right
|
||||||
|
sun('08_corner-dawn-left', -0.5, 0.28), // rising in the lower-left
|
||||||
|
];
|
||||||
|
console.log(`BC composition study — ${COMPOSITION.length} → ${OUT}/composition/`);
|
||||||
|
for (const v of COMPOSITION) compose(v);
|
||||||
|
{
|
||||||
|
const cap = (v) => v.name.replace(/^\d+_/, '').replace(/-/g, ' ');
|
||||||
|
writeFileSync(`${OUT}/composition/m.html`, `<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid">${COMPOSITION.map(v => `<figure><img src="${v.name}.svg"><figcaption>${cap(v)}</figcaption></figure>`).join('')}</div></body></html>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VAST — the floating event (transparent paper) shrunk + placed into a huge sea,
|
||||||
|
// so the sun is small and the ocean is vast. The event stays CENTRED in its own
|
||||||
|
// canvas (never clipped); (sx,sy) is its target position in the FINAL frame
|
||||||
|
// (-1..1), reached by translating the scaled image: bcD = s/2.
|
||||||
|
const vast = (name, sx, sy, scale) => ({
|
||||||
|
name, dir: 'vast', base: 'rgb(229,222,203)', film: tealFilm, grain: tealGrain, carpet: TEAL,
|
||||||
|
bcSeed: 'MESON-5113', bcFloat: true, bcScale: scale, bcDx: sx / 2, bcDy: sy / 2,
|
||||||
|
bcOver: { palette: 'magentarise', saturation: 1.05, shockX: 0, shockY: 0, eventX: 0, eventY: 0 },
|
||||||
|
});
|
||||||
|
const VAST = [
|
||||||
|
vast('01_distant-dawn', 0, -0.26, 0.6), // sun on the horizon
|
||||||
|
vast('02_small-sun-horizon', 0, -0.26, 0.5),
|
||||||
|
vast('03_corner-rise-left', -0.42, 0.02, 0.55),
|
||||||
|
vast('04_high-lonely', 0.14, -0.5, 0.42), // tiny sun high, immense sea
|
||||||
|
vast('05_mid-sea-jewel', 0, -0.04, 0.7),
|
||||||
|
vast('06_low-wide-sea', 0, 0.22, 0.62),
|
||||||
|
];
|
||||||
|
console.log(`VAST (floating sun) — ${VAST.length} → ${OUT}/vast/`);
|
||||||
|
for (const v of VAST) compose(v);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// VAST02 — small sun (≈04 disk size) but LARGER bubble traces; ripple-forward
|
||||||
|
// calm sea; now WITH film grain + an aging layer (scratches/dust/foxing).
|
||||||
|
// Disk anchored via sunAt (never clips). shockSize small + bcScale big → small
|
||||||
|
// disk, big traces. Each iteration varies the trace character.
|
||||||
|
// ============================================================
|
||||||
|
const RIPPLE = { hueBack: 0.54, hueFront: 0.47, chaos: 0.32, blips: 0.7, mound: 0.3, overlap: 1.55 };
|
||||||
|
const v2 = (name, sunAt, bcOver, scale = 0.78) => ({
|
||||||
|
name, dir: 'vast02', base: 'rgb(229,222,203)', film: { seed: 8, density: 0.5 },
|
||||||
|
grain: { amount: 0.42 }, aging: { seed: (name.charCodeAt(3) * 7) % 97, scratches: 6, dust: 0.5, foxing: 0.55 }, agingOpacity: 0.6,
|
||||||
|
carpet: RIPPLE, bcSeed: 'MESON-5113', bcFloat: true, bcScale: scale, sunAt,
|
||||||
|
bcOver: { palette: 'magentarise', saturation: 1.05, shockX: 0, shockY: 0, eventX: 0, eventY: 0, shockSize: 0.16, ...bcOver },
|
||||||
|
});
|
||||||
|
const VAST02 = [
|
||||||
|
v2('01_dawn-sweeping-arcs', [0, -0.26], { sweepers: 6, primaries: 18, eloss: 0.34, pspread: 0.8 }),
|
||||||
|
v2('02_high-lonely-long', [0.1, -0.5], { bfield: 0.7, eloss: 0.3, primaries: 16, cosmics: 7 }, 0.72),
|
||||||
|
v2('03_corner-left-bigarcs', [-0.4, -0.04], { sweepers: 7, primaries: 17, bfield: 0.9 }),
|
||||||
|
v2('04_low-wide-spirals', [0, 0.2], { deltaRate: 0.85, deltaTight: 0.5, primaries: 20, eloss: 0.4 }),
|
||||||
|
v2('05_mid-jewel-dense', [0, -0.05], { primaries: 24, burst: 0.82, pspread: 0.9 }, 0.74),
|
||||||
|
v2('06_right-rise-curl', [0.4, -0.06], { bfield: 1.7, primaries: 18, deltaRate: 0.7 }),
|
||||||
|
v2('07_centered-long-reach', [0, 0.0], { eloss: 0.28, pspread: 0.95, primaries: 16, shockSize: 0.15 }),
|
||||||
|
v2('08_distant-tiny-vast', [0.06, -0.42], { primaries: 18, sweepers: 5, eloss: 0.34 }, 0.6),
|
||||||
|
v2('09_dawn-cosmic-streaks', [-0.14, -0.2], { cosmics: 9, primaries: 15, bfield: 0.8, eloss: 0.3 }),
|
||||||
|
v2('10_horizon-symmetry', [0, -0.24], { sweepers: 4, primaries: 19, deltaRate: 0.7, shockSize: 0.17 }),
|
||||||
|
];
|
||||||
|
console.log(`VAST02 (small sun, big traces, grain+aging) — ${VAST02.length} → ${OUT}/vast02/`);
|
||||||
|
for (const v of VAST02) compose(v);
|
||||||
|
{
|
||||||
|
const cap = (v) => v.name.replace(/^\d+_/, '').replace(/-/g, ' ');
|
||||||
|
writeFileSync(`${OUT}/vast02/m.html`, `<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid">${VAST02.map(v => `<figure><img src="${v.name}.svg"><figcaption>${cap(v)}</figcaption></figure>`).join('')}</div></body></html>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// VAST03 — an "archive of dawns": vary SUN COLOUR (diskHue/diskSat) + slight
|
||||||
|
// SIZE, keep larger bubble traces, and bring back the ANNOTATING HAND
|
||||||
|
// (grease-pencil ring/№/angle via `annotate`; a faint réseau on a couple).
|
||||||
|
// Trace family stays magenta → a two-colour relationship against each sun.
|
||||||
|
// ============================================================
|
||||||
|
const v3 = (name, sunAt, sun, traces, ann, scale = 0.78) => ({
|
||||||
|
name, dir: 'vast03', base: 'rgb(229,222,203)', film: { seed: 8, density: 0.5 },
|
||||||
|
grain: { amount: 0.4 }, aging: { seed: (name.charCodeAt(2) * 7) % 97, scratches: 6, dust: 0.5, foxing: 0.55 }, agingOpacity: 0.58,
|
||||||
|
carpet: RIPPLE, bcSeed: 'MESON-5113', bcFloat: true, bcScale: scale, sunAt,
|
||||||
|
bcOver: {
|
||||||
|
palette: 'magentarise', saturation: 1.05, shockX: 0, shockY: 0, eventX: 0, eventY: 0,
|
||||||
|
diskHue: sun.h, diskSat: sun.s, shockSize: sun.size,
|
||||||
|
annotate: ann.a, reseau: ann.r || 0,
|
||||||
|
...traces,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const VAST03 = [
|
||||||
|
v3('01_burnt-orange-studied', [0, -0.25], { h: 0.06, s: 0.82, size: 0.16 }, { sweepers: 6, primaries: 18, eloss: 0.34 }, { a: 0.9 }),
|
||||||
|
v3('02_gold-sun-light-ring', [0.08, -0.42],{ h: 0.11, s: 0.85, size: 0.15 }, { primaries: 17, eloss: 0.3, cosmics: 6 }, { a: 0.6 }, 0.74),
|
||||||
|
v3('03_amber-reseau', [0, -0.1], { h: 0.085, s: 0.9, size: 0.17 }, { primaries: 20, sweepers: 5 }, { a: 0.7, r: 0.32 }),
|
||||||
|
v3('04_coral-red-spirals', [-0.1, 0.12], { h: 0.02, s: 0.85, size: 0.16 }, { deltaRate: 0.85, deltaTight: 0.5, primaries: 20 }, { a: 0.8 }),
|
||||||
|
v3('05_rose-pink-dawn', [0.36, -0.06],{ h: 0.95, s: 0.7, size: 0.15 }, { bfield: 1.4, primaries: 18 }, { a: 0.7 }),
|
||||||
|
v3('06_copper-long-reach', [0, 0.0], { h: 0.05, s: 0.72, size: 0.18 }, { eloss: 0.28, pspread: 0.95, primaries: 16 }, { a: 0.65 }),
|
||||||
|
v3('07_pale-white-hot', [0.05, -0.5], { h: 0.1, s: 0.22, size: 0.14 }, { primaries: 16, sweepers: 5, eloss: 0.32 }, { a: 0.55 }, 0.7),
|
||||||
|
v3('08_cool-teal-sun', [-0.4, -0.04],{ h: 0.5, s: 0.6, size: 0.16 }, { sweepers: 7, primaries: 17 }, { a: 0.7 }),
|
||||||
|
v3('09_magenta-monochrome', [0, -0.24], { h: 0.9, s: 0.78, size: 0.17 }, { primaries: 19, deltaRate: 0.7, sweepers: 4 }, { a: 0.8, r: 0.28 }),
|
||||||
|
v3('10_deep-ember-studied', [0.12, 0.18], { h: 0.0, s: 0.9, size: 0.19 }, { primaries: 22, burst: 0.82, eloss: 0.36 }, { a: 1.0 }, 0.76),
|
||||||
|
];
|
||||||
|
console.log(`VAST03 (archive of dawns: sun colour + annotation) — ${VAST03.length} → ${OUT}/vast03/`);
|
||||||
|
for (const v of VAST03) compose(v);
|
||||||
|
{
|
||||||
|
const cap = (v) => v.name.replace(/^\d+_/, '').replace(/-/g, ' ');
|
||||||
|
writeFileSync(`${OUT}/vast03/m.html`, `<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid">${VAST03.map(v => `<figure><img src="${v.name}.svg"><figcaption>${cap(v)}</figcaption></figure>`).join('')}</div></body></html>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// VAST04 — refined annotation: ONLY "No 001" + an arrow (no ring, no scrawl),
|
||||||
|
// THIN white pencil; the particle-collision VERTEX is OFFSET from the disk
|
||||||
|
// (eventX/Y ≠ shock); the disk gets colour + PRESSURE variation (dark dense
|
||||||
|
// core). Larger traces continue. Two graphite-pencil controls for comparison.
|
||||||
|
// ============================================================
|
||||||
|
const v4 = (name, sunAt, sun, ev, traces, pencil = '#f6f2ea', scale = 0.78) => ({
|
||||||
|
name, dir: 'vast04', base: 'rgb(229,222,203)', film: { seed: 8, density: 0.5 },
|
||||||
|
grain: { amount: 0.4 }, aging: { seed: (name.charCodeAt(2) * 7) % 97, scratches: 5, dust: 0.45, foxing: 0.5 }, agingOpacity: 0.55,
|
||||||
|
carpet: RIPPLE, bcSeed: 'MESON-5113', bcFloat: true, bcScale: scale, sunAt,
|
||||||
|
bcOver: {
|
||||||
|
palette: 'magentarise', saturation: 1.05,
|
||||||
|
shockX: 0, shockY: 0, eventX: ev[0], eventY: ev[1], // collision offset from the disk
|
||||||
|
diskHue: sun.h, diskSat: sun.s, shockSize: sun.size, diskPressure: sun.p,
|
||||||
|
annotate: 0.5, annoRing: false, annoExtras: false, annoArrow: true, annoNum: true,
|
||||||
|
annoLabel: 'No 001', annoWidth: 0.4, annotateInk: pencil,
|
||||||
|
...traces,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const VAST04 = [
|
||||||
|
v4('01_burnt-orange-pressure', [0, -0.25], { h: 0.06, s: 0.82, size: 0.16, p: 0.85 }, [0.26, -0.14], { sweepers: 6, primaries: 18, eloss: 0.34 }),
|
||||||
|
v4('02_gold-mid-pressure', [0.08, -0.42], { h: 0.11, s: 0.85, size: 0.15, p: 0.5 }, [-0.2, 0.16], { primaries: 17, eloss: 0.3, cosmics: 6 }, '#f6f2ea', 0.74),
|
||||||
|
v4('03_amber-soft-core', [0, -0.1], { h: 0.085, s: 0.9, size: 0.17, p: 0.25 }, [0.28, 0.12], { primaries: 20, sweepers: 5 }),
|
||||||
|
v4('04_coral-deep-core', [-0.1, 0.12], { h: 0.02, s: 0.85, size: 0.16, p: 0.8 }, [0.22, -0.18], { deltaRate: 0.85, deltaTight: 0.5, primaries: 20 }),
|
||||||
|
v4('05_copper-pressure', [0.36, -0.06], { h: 0.05, s: 0.72, size: 0.16, p: 0.55 }, [-0.24, -0.1], { bfield: 1.4, primaries: 18 }),
|
||||||
|
v4('06_deep-ember-densecore', [0, -0.24], { h: 0.0, s: 0.9, size: 0.18, p: 0.95 }, [0.2, 0.18], { primaries: 19, sweepers: 4 }),
|
||||||
|
v4('07_rose-mid', [0.05, -0.5], { h: 0.95, s: 0.7, size: 0.15, p: 0.5 }, [-0.18, 0.2], { primaries: 16, eloss: 0.3, cosmics: 5 }, '#f6f2ea', 0.7),
|
||||||
|
v4('08_magenta-pressure', [0, 0.0], { h: 0.9, s: 0.78, size: 0.17, p: 0.7 }, [0.3, -0.1], { eloss: 0.28, pspread: 0.95, primaries: 16 }),
|
||||||
|
v4('09_graphite-on-orange', [-0.4, -0.04], { h: 0.06, s: 0.82, size: 0.16, p: 0.8 }, [0.24, 0.14], { sweepers: 7, primaries: 17 }, '#39312a'),
|
||||||
|
v4('10_graphite-on-ember', [0.12, 0.18], { h: 0.0, s: 0.9, size: 0.19, p: 0.9 }, [-0.22, -0.16],{ primaries: 22, burst: 0.82 }, '#39312a', 0.76),
|
||||||
|
];
|
||||||
|
console.log(`VAST04 (No001 + arrow, white pencil, vertex offset, disk pressure) — ${VAST04.length} → ${OUT}/vast04/`);
|
||||||
|
for (const v of VAST04) compose(v);
|
||||||
|
{
|
||||||
|
const cap = (v) => v.name.replace(/^\d+_/, '').replace(/-/g, ' ');
|
||||||
|
writeFileSync(`${OUT}/vast04/m.html`, `<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid">${VAST04.map(v => `<figure><img src="${v.name}.svg"><figcaption>${cap(v)}</figcaption></figure>`).join('')}</div></body></html>`);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const cap = (v) => v.name.replace(/^\d+_/, '').replace(/-/g, ' ');
|
||||||
|
writeFileSync(`${OUT}/vast/m.html`, `<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px;width:2400px}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 10px;font:14px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid">${VAST.map(v => `<figure><img src="${v.name}.svg"><figcaption>${cap(v)}</figcaption></figure>`).join('')}</div></body></html>`);
|
||||||
|
}
|
||||||
|
|
||||||
const cap = (v) => v.name.replace(/^\d+_/, '').replace(/-/g, ' ');
|
const cap = (v) => v.name.replace(/^\d+_/, '').replace(/-/g, ' ');
|
||||||
writeFileSync(`${OUT}/index.html`, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Layered piece · film + QFT carpet + bubble chamber</title>
|
writeFileSync(`${OUT}/index.html`, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Layered piece · film + QFT carpet + bubble chamber</title>
|
||||||
<style>body{margin:0;background:#0b0b0b;color:#bbb;font:12px/1.5 ui-monospace,Menlo,monospace;padding:26px;max-width:1900px}
|
<style>body{margin:0;background:#0b0b0b;color:#bbb;font:12px/1.5 ui-monospace,Menlo,monospace;padding:26px;max-width:1900px}
|
||||||
|
|||||||
@@ -42,6 +42,19 @@ const deckBase = { mode: 'plate', rows: 46, horizon: 0.36, wFar: 0.58, wNear: 0.
|
|||||||
console.log(`carpet · plexi deck (${DECK.length}) → ${ROOT}/layers/`);
|
console.log(`carpet · plexi deck (${DECK.length}) → ${ROOT}/layers/`);
|
||||||
for (const v of DECK) { writeFileSync(`${ROOT}/layers/${v.name}.svg`, carpetSVG(SIZE, { ...deckBase, ...v })); console.log(` ${v.name}`); }
|
for (const v of DECK) { writeFileSync(`${ROOT}/layers/${v.name}.svg`, carpetSVG(SIZE, { ...deckBase, ...v })); console.log(` ${v.name}`); }
|
||||||
|
|
||||||
|
// VERTICAL field studies — lines run vertically, receding to a side edge
|
||||||
|
mkdirSync(`${ROOT}/vertical`, { recursive: true });
|
||||||
|
const VERT = [
|
||||||
|
{ name: '01_calm-curtain', label: 'calm vertical curtain', o: { orient: 'vertical', chaos: 0.28, blips: 0.6, rows: 46, overlap: 1.6, mode: 'solid' } },
|
||||||
|
{ name: '02_seething-strings', label: 'seething vertical strings', o: { orient: 'vertical', chaos: 0.75, blips: 1.3, rows: 52, overlap: 1.8, mode: 'solid', salt: 'vstr' } },
|
||||||
|
{ name: '03_rays-plate', label: 'fine vertical rays (plate)', o: { orient: 'vertical', chaos: 0.5, blips: 0.9, rows: 60, overlap: 1.5, mode: 'plate', hue: 0.5, hue2: 0.55, salt: 'vray' } },
|
||||||
|
];
|
||||||
|
console.log(`carpet · vertical (${VERT.length}) → ${ROOT}/vertical/`);
|
||||||
|
for (const v of VERT) { writeFileSync(`${ROOT}/vertical/${v.name}.svg`, carpetSVG(SIZE, v.o)); console.log(` ${v.name} — ${v.label}`); }
|
||||||
|
writeFileSync(`${ROOT}/vertical/m.html`, `<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||||
|
<style>html,body{margin:0;background:#222}.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:10px;width:2100px}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 10px;font:13px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
|
||||||
|
<div class="grid">${VERT.map(v => `<figure><img src="${v.name}.svg"><figcaption>${v.label}</figcaption></figure>`).join('')}</div></body></html>`);
|
||||||
|
|
||||||
writeFileSync(`${ROOT}/stack.html`, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>vacuum carpet · plexi deck</title>
|
writeFileSync(`${ROOT}/stack.html`, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>vacuum carpet · plexi deck</title>
|
||||||
<style>html,body{margin:0;background:#0c0c0c}.stage{position:relative;width:${SIZE}px;height:${SIZE}px;margin:30px auto;background:rgb(226,219,199);box-shadow:0 0 120px #000 inset}.stage img{position:absolute;inset:0;width:100%;height:100%}.L3{filter:blur(2.2px);opacity:.5;transform:translateY(-6px) scale(1.01)}.L2{filter:blur(1px);opacity:.72;transform:translateY(-2px)}.L1{opacity:.95}.cap{max-width:${SIZE}px;margin:0 auto;color:#888;font:12px ui-monospace,monospace;padding:0 4px}</style></head><body>
|
<style>html,body{margin:0;background:#0c0c0c}.stage{position:relative;width:${SIZE}px;height:${SIZE}px;margin:30px auto;background:rgb(226,219,199);box-shadow:0 0 120px #000 inset}.stage img{position:absolute;inset:0;width:100%;height:100%}.L3{filter:blur(2.2px);opacity:.5;transform:translateY(-6px) scale(1.01)}.L2{filter:blur(1px);opacity:.72;transform:translateY(-2px)}.L1{opacity:.95}.cap{max-width:${SIZE}px;margin:0 auto;color:#888;font:12px ui-monospace,monospace;padding:0 4px}</style></head><body>
|
||||||
<div class="stage"><img class="L3" src="layers/L3_back.svg"><img class="L2" src="layers/L2_mid.svg"><img class="L1" src="layers/L1_front.svg"></div>
|
<div class="stage"><img class="L3" src="layers/L3_back.svg"><img class="L2" src="layers/L2_mid.svg"><img class="L1" src="layers/L1_front.svg"></div>
|
||||||
|
|||||||