# Reads a dither's noise field back from real WebGL (headless Chromium, SwiftShader), 16 bits
# per pixel: the shader writes floor(h*255) and the next 8 bits, so h = (r + g/255) / 255.
# usage: python3 probe.py <name> <W> <H> <out.npy>; names in HASHES.
import sys, json, subprocess, tempfile, numpy as np
HASHES = {
    'sin': 'float h() { return fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453); }',
    'sinmod': 'float h() { float dt = dot(gl_FragCoord.xy, vec2(12.9898, 78.233)); return fract(sin(mod(dt, 3.14159265)) * 43758.5453); }',
    'ign': 'float h() { return fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715)))); }',
}
def probe(name, W, H):
    frag = HASHES[name] + ' void main() { float v = h() * 255.; float r = floor(v); gl_FragColor = vec4(r / 255., floor(fract(v) * 255.) / 255., 0., 1.); }'
    S = tempfile.mkdtemp()
    json.dump(dict(W=W, H=H, radius=0, quads=[dict(frag=frag, uniforms={}, x=W / 2, y=H / 2, w=W, h=H)]), open(S + '/p.json', 'w'))
    subprocess.run(['node', '--input-type=module', '-', S + '/p.json', S + '/p'], stdin=open('webgl.mjs.txt'), check=True, stderr=subprocess.DEVNULL)
    t = np.fromfile(S + '/p.target.rgba', np.uint8).reshape(H, W, 4).astype(float)
    return (t[..., 0] + t[..., 1] / 255.) / 255.
if __name__ == '__main__':
    name, W, H, out = sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), sys.argv[4]
    np.save(out, probe(name, W, H).astype(np.float32))
