// Minimal family: one hairline drawing a harmonograph figure — two pendulums
// per axis, slightly out of tune with each other — over its own slowly fading
// ghost. Nothing else is in the frame.
//
// Silk Ribbon is the other minimal with a single moving element, and it is a
// wide band: an area with two edges. This is a line with no width to speak of,
// and it is the only scene in the library where what you are looking at is the
// HISTORY of a moving point rather than the point. The figure never repeats
// exactly, because the two frequencies are deliberately not a whole ratio, so
// the drawing precesses instead of closing.

export const pendulumTrace = {
    name: 'Pendulum Trace',
    family: 'minimal',
    kind: 'fragment',
    // Fine line work; grain only furs it up.
    texture: 0.25,
    traits: ['camera', 'style'],

    params: {
        ratio:   { type: 'float', range: [1.5, 5.5],   default: 3.01, uniform: 'u_ratio' },
        detune:  { type: 'float', range: [0, 0.08],    default: 0.03,  uniform: 'u_detune' },
        size:    { type: 'float', range: [0.5, 1.5],   default: 1.05, uniform: 'u_size' },
        decay:   { type: 'float', range: [0, 0.5],     default: 0.15, uniform: 'u_decay' },
        weight:  { type: 'float', range: [0.002, 0.02], default: 0.006, uniform: 'u_weight' },
        arc:     { type: 'float', range: [1.0, 8],     default: 3.5,  uniform: 'u_arc', bias: 'density' },
        persist: { type: 'float', range: [0.9, 0.996], default: 0.992, uniform: 'u_persist', slowAxis: true },
        speed:   { type: 'float', range: [0.05, 0.9],  default: 0.3,  uniform: 'u_speed', bias: 'motion', rate: true },
        glow:    { type: 'float', range: [0, 1.4],     default: 0.6,  uniform: 'u_glow', bias: 'energy' },
        palette: { type: 'palette', count: 5 },
    },

    reactive: {
        glow:   { feature: 'loudness', amount: 0.3,  response: 'smooth' },
        weight: { feature: 'beat',     amount: 0.15, response: 'spike' },
        arc:    { feature: 'bandMid',  amount: 0.2,  response: 'smooth' },
    },

    shader: `
// Where the pen is at time s. Two pendulums per axis, the second slightly
// detuned from a whole-number ratio — which is the entire reason a harmonograph
// figure precesses instead of retracing one closed loop forever.
vec2 pen(float s) {
    // The swing loses energy and is pushed again on the phrase. A real
    // exponential decay in absolute time would be correct and useless: five
    // minutes in, the figure would have shrunk to a dot.
    float damp = 1.0 - u_decay * (1.0 - cos(u_phrasePhase * 6.28318530718)) * 0.5;
    float x = sin(s) + 0.6 * sin(s * u_ratio + 1.7);
    float y = cos(s * (1.0 + u_detune)) + 0.6 * cos(s * u_ratio * (1.0 - u_detune) + 0.4);
    return vec2(x, y) * 0.5 * u_size * damp;
}

vec4 scene(vec2 uv, vec2 p) {
    float t = u_time * u_speed + u_seed;
    p = sigCamera(p);

    vec3 col = pal(0) * 0.07;

    // Vignette wash, so the empty frame is a lit space rather than black.
    col += pal(1) * 0.13 * exp(-dot(p, p) * 0.6);

    // The live stroke: the last u_arc radians of travel, sampled at a fixed
    // resolution and reduced to the nearest point on the curve.
    float best = 1e9;
    float head = 0.0;
    // lint: fixed-cost — 48 samples along a fixed arc, not a search
    for (int i = 0; i < 48; i++) {
        float f = float(i) / 47.0;
        float s = t * 20.0 - u_arc * (1.0 - f);
        float d = length(p - pen(s));
        if (d < best) { best = d; head = f; }
    }

    float w = u_weight * (0.4 + u_sigLine * 2.5);
    float line = smoothstep(w, w * 0.2, best);

    // The tip is brightest and the tail falls away, so the line has a direction
    // of travel even in a still frame.
    vec3 ink = palRamp(0.2 + head * 0.35);
    col += ink * line * (0.5 + head * 0.9) * (0.6 + u_glow * 0.8);
    col += ink * exp(-best * 40.0) * u_glow * 0.25;

    // The ghost: everything the pen has already drawn, fading. Sampled straight
    // rather than smeared, so old strokes stay in place and thin out instead of
    // sliding around the frame.
    col = max(col, prev(uv) * u_persist);

    col += sigGrain(uv);
    return vec4(col, 1.0);
}
`,
};

export default pendulumTrace;
