{
const ctx = DOM.context2d(w, h, 1);
Object.assign(ctx.canvas.style, {width: w * (2 ** zoom) + 'px', imageRendering: 'pixelated'});
ctx.putImageData(initialImgData, 0, 0); if (animate) yield ctx.canvas;
const imgData = ctx.getImageData(0, 0, w, h);
const data = new Uint32Array(imgData.data.buffer);
const closest = new Uint32Array(w * h).fill(65535);
for (let i = 0; i < closest.length; i++) if (data[i]) closest[i] = i;
let numUpdates = 0;
const maxStep = 2 ** (Math.ceil(Math.log2(Math.max(w, h))) - 1);
if (preflood) yield* pass(1);
for (let step = maxStep; step >= 1; step >>= 1) {
yield* pass(step);
}
ctx.putImageData(imgData, 0, 0); yield ctx.canvas;
function* pass(step) {
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const k = y * w + x;
let bestSeed = -1;
for (let i = -1, bestDist = Infinity; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
const y0 = y + i * step;
const x0 = x + j * step;
const m = y0 * w + x0;
if (!data[m] || x0 < 0 || x0 >= w || y0 < 0 || y0 >= h) continue;
const seed = closest[m];
const d = (x - seed % w) ** 2 + (y - seed / w | 0) ** 2;
if (d < bestDist) {
bestDist = d;
bestSeed = seed;
}
}
}
if (bestSeed === -1) continue;
closest[k] = bestSeed;
data[k] = data[bestSeed];
const newFrame = animate && ++numUpdates % (viewof iterationsPerFrame).value === 0;
if (newFrame) { ctx.putImageData(imgData, 0, 0); yield ctx.canvas; }
}
}
}
}