# Dithers through Starlock's painterly pass at pixel ratios 1, 2 and 3 (the pass's taps sit a CSS pixel
# apart, so it reads every 2nd or 3rd pixel), flat levels, RMS in 8-bit levels, by brute force
# (simulate.py). Prints out/strides.txt.
import numpy as np
import filters as F, simulate as S
from bluenoise import bluenoise

n = 1344  # canvas: a whole number of every tile here (64, 2, 3, 56, 112, 168), so nothing has a seam
def tile(t): return np.tile(t, (n // t.shape[0] + 1, n // t.shape[1] + 1))[:n, :n]
b2 = np.array([[0, 2], [3, 1]])
def bayer(m):  # the standard (dispersed) Bayer matrix: the finest 2x2 cell carries the top digit
    s = 2 ** m; i, j = np.mgrid[0:s, 0:s]; B = np.zeros((s, s), int)
    for k in range(m): B += 4 ** (m - 1 - k) * b2[(i >> k) & 1, (j >> k) & 1]
    return (B + .5) / s / s
blue = bluenoise(64)
blue4 = [bluenoise(64, seed) for seed in (1, 2, 3, 4)]
def coset_blue(s):  # an independent blue tile on each of the s x s cosets: blue as each stride sees it
    u = np.zeros((n, n)); k = 0
    for cy in range(s):
        for cx in range(s):
            sub = tile(blue4[k % 4])[: (n - cy + s - 1) // s, : (n - cx + s - 1) // s]; u[cy::s, cx::s] = sub; k += 1
    return u
def lat_for_stride(s):  # the best lattice for the decimated pass, (54,11)/56, spread over s x s pixels
    y, x = np.mgrid[0:n, 0:n]; return ((54 * x + 11 * y) % (56 * s) + .5) / (56 * s)

if __name__ == '__main__':
    print('flat-level RMS error (8-bit levels) through the painterly pass; no dither: 0.2887')
    print(f'{"dither":34s}' + ''.join(f'{"pixel ratio " + str(s):>16s}' for s in (1, 2, 3)))
    rows = [('white noise', lambda s: S.white(n)), ('Bayer 16x16', lambda s: tile(bayer(4))),
            ('blue noise (64x64 void-and-cluster)', lambda s: tile(blue)), ('blue noise, one tile per coset', coset_blue),
            ('interleaved gradient noise', lambda s: S.ign(n)), ('R2', lambda s: S.r2(n)),
            ('best lattice for this pass', lat_for_stride)]
    for name, make in rows:
        print(f'{name:34s}' + ''.join(f'{S.flat_rms(make(s), F.kuwahara(5, s)):16.4f}' for s in (1, 2, 3)))
