2026-05-20 16:53:23 -04:00
|
|
|
/* ============================================================
|
|
|
|
|
params.js — derive a full, tasteful parameter set from a seed.
|
|
|
|
|
This makes the seed the COMPLETE fingerprint of a plate: the
|
|
|
|
|
same seed always yields the same parameters, so an inspiration
|
|
|
|
|
thumbnail and its print-resolution master are identical. Pass
|
|
|
|
|
a seed anywhere and get the same image at any size.
|
|
|
|
|
|
|
|
|
|
A seed first selects a weighted "archetype" (overall character)
|
|
|
|
|
then jitters within it, so the gallery is varied but every
|
|
|
|
|
plate stays plausible.
|
|
|
|
|
============================================================ */
|
|
|
|
|
import { makeRng } from '../rng.js';
|
|
|
|
|
|
|
|
|
|
const ARCHETYPES = [
|
|
|
|
|
{ name: 'archival', w: 0.42 },
|
|
|
|
|
{ name: 'dense', w: 0.20 },
|
|
|
|
|
{ name: 'cosmic', w: 0.16 },
|
|
|
|
|
{ name: 'clean', w: 0.12 },
|
|
|
|
|
{ name: 'negative', w: 0.10 },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
function pickArchetype(rng) {
|
|
|
|
|
const total = ARCHETYPES.reduce((s, a) => s + a.w, 0);
|
|
|
|
|
let t = rng() * total;
|
|
|
|
|
for (const a of ARCHETYPES) { if ((t -= a.w) <= 0) return a.name; }
|
|
|
|
|
return 'archival';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function archetypeOf(seed) {
|
|
|
|
|
return pickArchetype(makeRng(seed, 'params'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function paramsFromSeed(seed) {
|
|
|
|
|
const rng = makeRng(seed, 'params');
|
|
|
|
|
const arch = pickArchetype(rng);
|
|
|
|
|
const r = (lo, hi) => lo + (hi - lo) * rng();
|
|
|
|
|
const ri = (lo, hi) => Math.round(r(lo, hi));
|
|
|
|
|
const chance = (p) => rng() < p;
|
|
|
|
|
|
|
|
|
|
// base (archival) defaults
|
|
|
|
|
const p = {
|
|
|
|
|
seed,
|
|
|
|
|
primaries: ri(12, 24), burst: r(0.55, 0.92), vdecay: ri(2, 6),
|
|
|
|
|
cosmics: ri(4, 10), sweepers: ri(2, 7),
|
|
|
|
|
bfield: r(0.8, 1.6), eloss: r(0.45, 0.75), pspread: r(0.6, 0.85),
|
|
|
|
|
deltaRate: r(0.6, 0.95), deltaTight: r(0.6, 1.0),
|
2026-05-21 05:59:44 -04:00
|
|
|
shock: true, diskBubbles: false, shockIntensity: r(0.6, 0.9), shockSize: r(0.26, 0.4),
|
2026-05-20 16:53:23 -04:00
|
|
|
shockStriations: r(0.45, 0.85), shockY: r(0.4, 0.6), shockX: r(-0.12, 0.12),
|
|
|
|
|
shockStain: Math.pow(rng(), 1.5), // skew cleaner, allow grime
|
2026-05-21 05:59:44 -04:00
|
|
|
diskSoften: r(0.4, 1.1), // gaussian softening of disk edges
|
2026-05-20 16:53:23 -04:00
|
|
|
instrument: r(0.25, 0.6),
|
|
|
|
|
bgEvents: ri(3, 8), bgIntensity: r(0.35, 0.5),
|
|
|
|
|
density: r(1.0, 1.35), size: r(0.9, 1.15),
|
|
|
|
|
bloom: r(0.4, 0.65), mottle: r(0.4, 0.7), grain: r(0.4, 0.6),
|
|
|
|
|
vign: r(0.35, 0.6), artifacts: r(0.4, 0.8),
|
|
|
|
|
showFiducials: chance(0.85), showBoundary: chance(0.7), showHeader: chance(0.9),
|
|
|
|
|
invert: true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (arch === 'dense') {
|
|
|
|
|
Object.assign(p, {
|
|
|
|
|
primaries: ri(26, 40), burst: r(0.85, 1.0), vdecay: ri(4, 8),
|
|
|
|
|
cosmics: ri(8, 14), sweepers: ri(5, 9), bfield: r(1.4, 2.4),
|
|
|
|
|
deltaRate: r(0.85, 1.0), deltaTight: r(0.8, 1.2),
|
|
|
|
|
bgEvents: ri(7, 11), density: r(1.25, 1.5), shockIntensity: r(0.6, 0.85),
|
|
|
|
|
});
|
|
|
|
|
} else if (arch === 'cosmic') {
|
|
|
|
|
Object.assign(p, {
|
|
|
|
|
primaries: ri(4, 10), burst: r(0.25, 0.55), vdecay: ri(0, 2),
|
|
|
|
|
cosmics: ri(10, 16), sweepers: ri(7, 11), bfield: r(0.6, 1.1),
|
|
|
|
|
pspread: r(0.8, 0.95), deltaRate: r(0.35, 0.6), instrument: r(0.45, 0.7),
|
|
|
|
|
shock: chance(0.4), shockIntensity: r(0.3, 0.7), bgEvents: ri(2, 5),
|
|
|
|
|
density: r(0.85, 1.1),
|
|
|
|
|
});
|
|
|
|
|
} else if (arch === 'clean') {
|
|
|
|
|
Object.assign(p, {
|
|
|
|
|
primaries: ri(8, 14), burst: r(0.4, 0.65), vdecay: ri(1, 3),
|
|
|
|
|
cosmics: ri(1, 4), sweepers: ri(1, 4), deltaRate: r(0.35, 0.6),
|
|
|
|
|
deltaTight: r(0.55, 0.8), shock: chance(0.6), shockIntensity: r(0.3, 0.7),
|
|
|
|
|
shockStain: Math.pow(rng(), 3), // mostly clean disk
|
|
|
|
|
instrument: r(0.1, 0.3), bgEvents: ri(0, 3), bgIntensity: r(0.2, 0.35),
|
|
|
|
|
bloom: r(0.25, 0.45), mottle: r(0.15, 0.4), grain: r(0.12, 0.35),
|
|
|
|
|
vign: r(0.2, 0.4), artifacts: r(0.1, 0.4),
|
|
|
|
|
});
|
|
|
|
|
} else if (arch === 'negative') {
|
|
|
|
|
Object.assign(p, { invert: false, vign: r(0.4, 0.6), shockStain: Math.pow(rng(), 1.3) });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.archetype = arch;
|
|
|
|
|
return p;
|
|
|
|
|
}
|