// Glitch family: video feedback. A camera pointed at its own monitor — the
// frame is redrawn inside itself slightly smaller and slightly turned, so a
// small motif at the centre becomes an endless corridor of copies of itself.
//
// Time Smear is the other feedback glitch and it TRANSLATES the previous frame,
// which leaves a comet trail behind a moving subject. This scales it. The
// difference is the whole scene: a translated feedback drifts off the edge and
// clears, a scaled one never clears, because everything the frame has ever
// contained is still in there, one ring further in.
//
// A live motif is drawn every frame, so the image exists before the loop has
// anything in it and comes back after a seek.
//
// No declared slow axis. What the corridor holds depends on where the loop has
// been, so two ten-second windows of the same parameters are 0.020 apart on
// their own and the persistence axis moved 0.004 — the same measurement Turing
// Bloom and Voronoi Shatter record. A feedback scene's history is its structure,
// and Phase 11 cannot separate a parameter from it.

export const drosteFeedback = {
    name: 'Droste Feedback',
    family: 'glitch',
    kind: 'fragment',
    texture: 0.5,
    consumes: ['ink'],
    traits: ['camera', 'style'],

    params: {
        zoom:    { type: 'float', range: [0.93, 1.02],  default: 0.975, uniform: 'u_zoomStep' },
        turn:    { type: 'float', range: [-0.12, 0.12], default: 0.04,  uniform: 'u_turn' },
        persist: { type: 'float', range: [0.9, 0.995], default: 0.985, uniform: 'u_persist' },
        motif:   { type: 'float', range: [0.04, 0.35],  default: 0.14,  uniform: 'u_motif' },
        tint:    { type: 'float', range: [0, 0.5],      default: 0.15,  uniform: 'u_tint' },
        pace:    { type: 'float', range: [0.05, 0.8],   default: 0.25,  uniform: 'u_pace', bias: 'motion', rate: true },
        palette: { type: 'palette', count: 5 },
    },

    reactive: {
        motif: { feature: 'beat',    amount: 0.2,  response: 'spike' },
    },

    shader: `
vec4 scene(vec2 uv, vec2 p) {
    float t = u_time * u_pace + u_seed;
    // Folded as well as filmed: a feedback loop through a mirror is still a
    // feedback loop, and the fold is the track's, not this scene's.
    p = sigFolded(sigCamera(p));

    // The motif. Small, live, and always drawn: this is the thing the loop eats.
    vec2 at = vec2(sin(t * 0.6), cos(t * 0.47)) * 0.42;
    float d = length(p - at) - u_motif;
    float ring = abs(d) - u_motif * 0.35;

    vec3 col = pal(0) * 0.05;
    col += palRamp(0.2 + fbm(p * 2.0 + t, 3) * 0.2) * smoothstep(0.02, -0.02, ring) * (0.5 + 0.6 * 0.8);
    col += pal(4) * inkStroke(ring) * (0.3 + 0.6 * 0.9);

    // The loop: read the previous frame from a slightly LARGER, slightly turned
    // sample of itself, so last frame's image comes back shrunk toward the
    // middle. Contracting is what nests — expanding pushes every copy off the
    // edge and leaves a plume instead of a corridor. Scaling about the centre in
    // screen space is what keeps the copies concentric rather than sliding.
    vec2 c = uv - 0.5;
    c.x *= u_aspect;
    c = rot(u_turn) * c / max(u_zoomStep, 0.5);
    c.x /= u_aspect;
    vec3 past = prev(c + 0.5);

    // Each generation is tinted a step further round the palette, so depth into
    // the corridor is legible as colour rather than only as size.
    past = mix(past, palRamp(0.6) * (past.r + past.g + past.b) * 0.5, u_tint);

    col = max(col, past * u_persist);

    // Vignette, which also stops the corner content being recycled forever.
    col *= 0.75 + 0.25 * exp(-dot(p, p) * 0.35);
    return vec4(inkValue(col), 1.0);
}
`,
};

export default drosteFeedback;
