// Glitch family: a four-colour press run with the plates out of register. Each
// ink is screened at its own angle, and each one lands a little off, so the
// image is fringed and the dots moiré against each other.
//
// Every other glitch scene here corrupts a signal — tearing it, freezing it,
// blocking it up. This one is a printing fault rather than an electronic one:
// the image is never damaged, it is simply separated and reassembled wrong.
// Nothing else in the library is made of dots, and the dots are what carry it.

export const halftoneMisprint = {
    name: 'Halftone Misprint',
    family: 'glitch',
    kind: 'fragment',
    // The paper's tooth is the point; take the track's grain in full.
    texture: 1.2,
    consumes: ['ink'],
    traits: ['camera', 'style'],

    params: {
        screen:   { type: 'float', range: [18, 90],     default: 42,   uniform: 'u_screen', bias: 'density' },
        slip:     { type: 'float', range: [0, 0.06],    default: 0.02, uniform: 'u_slip' },
        angle:    { type: 'float', range: [0, 1.6],     default: 0.5,  uniform: 'u_angle' },
        ink:      { type: 'float', range: [0.3, 1.5],   default: 0.9,  uniform: 'u_ink', slowAxis: true },
        art:      { type: 'float', range: [0.5, 4],     default: 1.6,  uniform: 'u_art', bias: 'density' },
        press:    { type: 'float', range: [0.02, 0.4],  default: 0.09, uniform: 'u_press', bias: 'motion', rate: true },
        palette:  { type: 'palette', count: 5 },
    },

    reactive: {
        slip: { feature: 'flux',    amount: 0.3,  response: 'spike' },
        ink:  { feature: 'bandLow', amount: 0.25, response: 'smooth' },
    },

    shader: `
// The artwork being printed: a soft, slow field, deliberately simple. What the
// scene is about is the separation, not the picture.
float artwork(vec2 q, float t) {
    float a = fbm(q * u_art + vec2(t * 0.5, -t * 0.3), 4);
    float b = 0.5 + 0.5 * sin(q.x * u_art * 2.0 + a * 4.0 + t);
    return sat(a * 0.7 + b * 0.5);
}

// One screened separation: sample the artwork where THIS plate landed, then
// threshold it against a rotated dot grid. The dot grows with ink coverage,
// which is what a halftone is.
float plate(vec2 q, float ang, vec2 slip, float t, float gain) {
    float density = sat(artwork(q + slip, t) * gain);

    vec2 r = rot(ang) * (q + slip);
    // Ruling is a count of dots ACROSS THE FRAME, not a size in pixels — which
    // is what keeps a 720p preview and a 4K export the same print rather than
    // the same dot pitch on a bigger sheet.
    vec2 cell = fract(r * u_screen * 0.5) - 0.5;

    // Dot edge hardness is the track's: a sharp track prints a crisp dot on
    // coated stock, a soft one lets the ink spread into the fibre.
    float soft = 0.04 + u_sigSoft * 0.45;
    float radius = sqrt(max(density, 0.0)) * 0.62;
    return smoothstep(radius, radius - soft, length(cell));
}

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

    // Registration error. It steps on the bar rather than drifting: a press
    // slips, settles, and slips again, and a continuously crawling offset reads
    // as a wobble rather than as a fault.
    float era = floor(u_barPhase * 4.0) + floor(t * 2.0) * 4.0;
    vec2 slipC = (hash22(vec2(era, 1.0)) - 0.5) * u_slip;
    vec2 slipM = (hash22(vec2(era, 2.0)) - 0.5) * u_slip;
    vec2 slipY = (hash22(vec2(era, 3.0)) - 0.5) * u_slip;

    // Classic screen angles: 15°, 75°, 0°, spread by the angle parameter so a
    // track can have them in near-register or wildly rosetted.
    float a1 = 0.26 * u_angle, a2 = 1.31 * u_angle, a3 = 0.0;

    float c = plate(p, a1, slipC, t, u_ink);
    float m = plate(p, a2, slipM, t, u_ink);
    float y = plate(p, a3, slipY, t, u_ink);

    // Subtractive-ish: each ink takes light out of the paper, in its own hue.
    vec3 col = pal(1) * 0.85;
    col -= pal(2) * c * 0.5;
    col -= pal(3) * m * 0.5;
    col -= pal(4) * y * 0.45;

    // The black plate, printed last and in register, which is what stops the
    // whole thing turning to mud.
    float k = plate(p, 0.65 * u_angle, vec2(0.0), t, u_ink * 0.7);
    col = mix(col, pal(0) * 0.12, k * 0.55);

    // Ink edges take the track's line weight — a hard press or a soft one.
    col += pal(0) * sigEdge(0.5 - c) * (0.05 + u_sigLine * 0.35);

    col = max(col, vec3(0.0));
    return vec4(inkValue(col), 1.0);
}
`,
};

export default halftoneMisprint;
