Added QFT

This commit is contained in:
2026-05-29 15:40:42 -04:00
parent e59b024b62
commit 56c59a1f9c
148 changed files with 428741 additions and 7 deletions

View File

@@ -1329,6 +1329,7 @@ function renderCanvasPhoto(ctx, w, h, scene, params, opts = {}) {
const pt = paperTone(params, inv);
const pal = resolvePalette(params.palette, {
inv, sat: params.saturation ?? 1, hue: (params.hueShift ?? 0) * 360, cycles: params.hueCycles ?? 3,
traceHue: params.traceHue ?? 0, diskHue: params.diskHue ?? 0.06, diskSat: params.diskSat ?? 0.82,
baseInk: P.ink,
basePaper: { flat: pt.flat, glowIn: pt.glowIn, glowOut: pt.glowOut }, // rgb arrays
baseVign: pt.vign,
@@ -2181,7 +2182,10 @@ function kindColor(kind, inv) {
// purple→magenta→pink band (for the simplified "magenta family" feel)
// purple→magenta→pink band, nudged off the blue end (less blue in the purples)
const MAG_HUE = { primary: 0.90, delta: 0.95, cosmic: 0.82, sweep: 0.86, vdecay: 0.79 };
const burntOrange = (inv) => hslToRgb(0.06, 0.82, inv ? 0.42 : 0.54);
// the contrasting feature/disk accent — a single monochrome hue. Defaults to the
// burnt-orange that pairs with the magenta family; `magentarise` exposes hue/sat as
// dials (diskHue/diskSat) so the trace-vs-disk colour relationship is sweepable.
const featureHue = (inv, hue = 0.06, sat = 0.82) => hslToRgb(hue, sat, inv ? 0.42 : 0.54);
// lifecycle: colour follows the particle from birth (life 0) to death (life 1).
// birth = cool blue, ageing through cyan/green/gold, death = deep red.
function lifeColor(life, inv) {
@@ -2252,17 +2256,21 @@ const PALETTES = {
},
},
// simplified feel: type → a shade of purple/magenta/pink, intensity rises to
// glowing deaths (kindrise), with a monochrome burnt-orange disk as the contrast.
// a complementary "two-colour relationship": type → a shade within a trace family
// (default the purple/magenta/pink band), intensity rises to glowing deaths
// (kindrise), with a monochrome contrasting disk (default burnt orange). The whole
// trace family rotates by `traceHue` (keeping the by-type spread); the disk hue &
// saturation are `diskHue`/`diskSat` — so any trace-vs-disk pairing is sweepable.
magentarise: {
id: 'magentarise', label: 'Magenta by type · burnt-orange disk',
id: 'magentarise', label: 'Trace family · contrasting disk',
bubbleInk: (b, e) => {
const t = Math.round(clamp(b.life, 0, 1) * 16) / 16;
const lo = e.inv ? 0.82 : 0.18; // faint birth
const hi = e.inv ? 0.46 : 0.62; // glowing death
return hslToRgb(MAG_HUE[b.track.kind] ?? 0.88, 0.55 + 0.30 * t, lo + (hi - lo) * t);
const h = (((MAG_HUE[b.track.kind] ?? 0.88) + (e.traceHue ?? 0)) % 1 + 1) % 1;
return hslToRgb(h, 0.55 + 0.30 * t, lo + (hi - lo) * t);
},
feature: (e) => burntOrange(e.inv), // disk + furniture: burnt orange
feature: (e) => featureHue(e.inv, e.diskHue ?? 0.06, e.diskSat ?? 0.82), // disk + furniture
},
// a complete "chemistry": overrides paper + ink together (deep Prussian-blue
@@ -2369,6 +2377,7 @@ function renderSVG(scene, params, sizePx = 4800) {
const pt = paperTone(params, inv);
const pal = resolvePalette(params.palette, {
inv, sat: params.saturation ?? 1, hue: (params.hueShift ?? 0) * 360, cycles: params.hueCycles ?? 3,
traceHue: params.traceHue ?? 0, diskHue: params.diskHue ?? 0.06, diskSat: params.diskSat ?? 0.82,
baseInk, basePaper: { flat: pt.flat, glowIn: pt.glowIn, glowOut: pt.glowOut }, baseVign: pt.vign,
});
const paperRGB = pal.paper(); // {flat,glowIn,glowOut} rgb arrays
@@ -2722,6 +2731,7 @@ function buildPDF(scene, params, pageSize = 1728) {
const pt = paperTone(params, inv);
const pal = resolvePalette(params.palette, {
inv, sat: params.saturation ?? 1, hue: (params.hueShift ?? 0) * 360, cycles: params.hueCycles ?? 3,
traceHue: params.traceHue ?? 0, diskHue: params.diskHue ?? 0.06, diskSat: params.diskSat ?? 0.82,
baseInk: inv ? [28, 24, 20] : [233, 228, 214],
basePaper: { flat: pt.flat, glowIn: pt.glowIn, glowOut: pt.glowOut }, baseVign: [0, 0, 0],
});
@@ -3061,6 +3071,9 @@ const GROUPS = [
{ id: 'glow', label: 'Gas glow', min: 0, max: 1, step: 0.01, value: 0.5, mode: 'render' },
{ id: 'halo', label: 'Chromatic halo', min: 0, max: 1, step: 0.01, value: 0, mode: 'render' },
{ id: 'haloHue', label: 'Halo hue', min: 0, max: 1, step: 0.005, value: 0.55, mode: 'render' },
{ id: 'traceHue', label: 'Trace family hue (magentarise)', min: 0, max: 1, step: 0.005, value: 0, mode: 'render' },
{ id: 'diskHue', label: 'Disk accent hue (magentarise)', min: 0, max: 1, step: 0.005, value: 0.06, mode: 'render' },
{ id: 'diskSat', label: 'Disk accent saturation (magentarise)', min: 0, max: 1, step: 0.01, value: 0.82, mode: 'render' },
],
},
{
@@ -3075,7 +3088,7 @@ const GROUPS = [
const SELECTS = [
{
id: 'palette', label: 'Ink palette (colour feel)', value: 'mono', mode: 'render',
options: [['mono', 'Monochrome'], ['charge', 'Charge duotone'], ['beta', 'Velocity (β) spectral'], ['kind', 'By particle type'], ['kindlife', 'By type · faded by life'], ['kindrise', 'By type · intensify by life'], ['magentarise', 'Magenta family · burnt-orange disk'], ['lifecycle', 'Lifecycle (birth → death)'], ['psychedelic', 'Psychedelic (hue cycle)'], ['cyanotype', 'Cyanotype (blueprint)']],
options: [['mono', 'Monochrome'], ['charge', 'Charge duotone'], ['beta', 'Velocity (β) spectral'], ['kind', 'By particle type'], ['kindlife', 'By type · faded by life'], ['kindrise', 'By type · intensify by life'], ['magentarise', 'Trace family · contrasting disk'], ['lifecycle', 'Lifecycle (birth → death)'], ['psychedelic', 'Psychedelic (hue cycle)'], ['cyanotype', 'Cyanotype (blueprint)']],
},
{
id: 'paperTone', label: 'Paper tone', value: 'cream', mode: 'render',
@@ -3196,6 +3209,7 @@ function paramsFromSeed(seed) {
showFiducials: chance(0.85), showBoundary: chance(0.7), showHeader: chance(0.9),
invert: true, palette: 'mono', saturation: 1.0, // canonical look is B&W; colour is opt-in
hueShift: 0, hueCycles: 3, diskSpectrum: 0, halo: 0, haloHue: 0.55,
traceHue: 0, diskHue: 0.06, diskSat: 0.82, // magentarise: trace-family rotation + disk accent hue/sat
paperTone: 'cream', toneStrength: 1.0, paperBright: 1.0, glow: 0.5,
annotate: 0, reseau: 0, filmEdge: false, splice: false, // media & hand layer (opt-in)
};