// Geometric family: a Voronoi tessellation — every point in the frame belongs
// to whichever seed is nearest, so the plates are irregular, they meet in clean
// straight seams, and they cannot overlap or leave a gap.
//
// Truchet Fold is the other tiling here and it is a regular grid wearing a
// pattern: the cells are square whatever is drawn in them. These cells have no
// fixed shape at all — each one is whatever its neighbours leave it — so when
// the seeds move, every plate in the frame changes shape at once and the seams
// slide along themselves. The seeds jump to new positions on the phrase, which
// re-cuts the whole plane in one frame without anything sliding.
//
// No declared slow axis. The re-cut is the point of the scene and it puts the
// ten-second average 0.127 away from itself between any two windows — the
// highest noise floor of anything in the library — so every candidate axis
// measured at 0.03x to 0.1x. Nothing a parameter can do competes with the plane
// being re-cut, which is the honest description of the scene.

export const voronoiShatter = {
    name: 'Voronoi Shatter',
    family: 'geometric',
    kind: 'fragment',
    texture: 0.5,
    consumes: ['ink'],
    traits: ['camera', 'style'],

    params: {
        cells:   { type: 'float', range: [1.2, 8],     default: 3.0,  uniform: 'u_cells', bias: 'density' },
        jitter:  { type: 'float', range: [0, 1],       default: 0.75, uniform: 'u_jitter' },
        seam:    { type: 'float', range: [0.01, 0.14], default: 0.05, uniform: 'u_seam' },
        recut:   { type: 'float', range: [0, 1],       default: 0.4,  uniform: 'u_recut' },
        relief:  { type: 'float', range: [0, 1],       default: 0.5,  uniform: 'u_relief' },
        // How hard the shatter gathers around the song's focal points. At zero
        // this is the old uniform tiling, which measured a layout distance of
        // 0.004 between songs — edge-to-edge cells sit in the same place
        // however they fall, so there was no arrangement for the song to change.
        focus:   { type: 'float', range: [0, 1],       default: 0.55, uniform: 'u_focus', bias: 'density' },
        drift:   { type: 'float', range: [0.01, 0.4],  default: 0.09, uniform: 'u_drift', bias: 'motion', rate: true },
        palette: { type: 'palette', count: 5 },
    },

    reactive: {
        jitter: { feature: 'bandLow', amount: 0.2,  response: 'smooth' },
    },

    shader: `
vec4 scene(vec2 uv, vec2 p) {
    float t = u_time * u_drift + u_seed;
    p = sigCamera(p);

    // Tile in a warped coordinate: the cells crowd toward the focal points, or
    // pull away from them, so the field has somewhere to be about.
    vec2 g = focusWarp(p, u_focus) * u_cells;
    vec2 base = floor(g);
    vec2 f = g - base;

    // The plane is re-cut on the phrase: the seeds do not travel to their new
    // positions, they are simply somewhere else on the next frame. A tiling
    // that slides is a texture; a tiling that jumps is an event.
    float era = floor(u_phrasePhase * 2.0) * u_recut;

    // Nearest and second-nearest seed. The difference between the two is the
    // distance to the seam, which is what makes the borders even in width
    // instead of pinching at the corners.
    float d1 = 8.0, d2 = 8.0;
    vec2 id1 = vec2(0.0);
    // lint: fixed-cost — the 3x3 neighbourhood a Voronoi cell can reach
    for (int j = -1; j <= 1; j++) {
        for (int i = -1; i <= 1; i++) {
            vec2 cell = base + vec2(float(i), float(j));
            vec2 h = hash22(cell + era * 7.3 + u_seed);
            vec2 seed = vec2(float(i), float(j)) +
                        mix(vec2(0.5), h + 0.15 * sin(t * 2.0 + h * 6.2831), u_jitter);
            float d = length(f - seed);
            if (d < d1) { d2 = d1; d1 = d; id1 = cell; }
            else if (d < d2) { d2 = d; }
        }
    }

    float border = (d2 - d1) / u_cells;      // scene units to the seam
    // Seams tighten where the field gathers, so the focus reads as a change in
    // the SHATTER rather than as a brightness blob laid over it.
    border *= 1.0 - focusField(p) * u_focus * 0.55;

    // Plates: each one flat, tinted by its own identity, and tilted a little so
    // the tessellation reads as broken glass rather than as a colour chart.
    float who = hash12(id1 * 1.7 + era);
    vec3 plate = palRamp(0.15 + who * 0.5);
    float tilt = mix(1.0, 0.55 + who * 0.9, u_relief);

    vec3 col = plate * 0.28 * tilt;

    // Seams: drawn in the track's hand, brightest where three plates meet.
    float seam = smoothstep(u_seam, u_seam * 0.15, border);
    col += pal(4) * seam * (0.4 + 0.6 * 0.8);
    col += pal(3) * inkStroke(border - u_seam) * 0.4;

    col *= 0.75 + 0.25 * exp(-dot(p, p) * 0.25);
    return vec4(inkValue(col), 1.0);
}
`,
};

export default voronoiShatter;
