function computeVoronoiMap(w, h, isSite) {
const n = w * h,
voromap = new Int32Array(n),
infty = n + 1;
function index(x, y) {
return x + y * w;
}
function getX(pos) {
return pos % w;
}
function getY(pos) {
return Math.floor(pos / w);
}
function closest(x, y, siteA, siteB) {
const sAx = getX(siteA),
sAy = getY(siteA),
sBx = getX(siteB),
sBy = getY(siteB);
return (
(x - sAx) * (x - sAx) + (y - sAy) * (y - sAy) >
(x - sBx) * (x - sBx) + (y - sBy) * (y - sBy)
);
}
function hiddenBy(A, B, C, slabX) {
const sAx = getX(A),
sAy = getY(A),
sBx = getX(B),
sBy = getY(B),
sCx = getX(C),
sCy = getY(C),
a = sBy - sAy,
b = sCy - sBy,
c = a + b,
dA = (sAx - slabX) * (sAx - slabX),
dB = (sBx - slabX) * (sBx - slabX),
dC = (sCx - slabX) * (sCx - slabX);
return c * dB - b * dA - a * dC - a * b * c > 0;
}
for (let y = 0; y < h; ++y) {
let prev = infty;
let firstSite = true;
for (let x = 0; x < w; ++x) {
voromap[index(x, y)] = Math.min(infty, index(prev, y));
if (isSite[index(x, y)]) {
const site = index(x, y);
voromap[index(x, y)] = site;
if (firstSite) {
firstSite = false;
for (let xx = 0; xx < x; ++xx) voromap[index(xx, y)] = site;
}
else
for (let xx = Math.floor((prev + x) / 2) + 1; xx < x; ++xx)
voromap[index(xx, y)] = site;
prev = x;
}
}
}
for (let x = 0; x < w; ++x) {
var sites = [];
for (let y = 0; y < h; ++y)
if (voromap[index(x, y)] != infty) {
while (
sites.length >= 2 &&
hiddenBy(
sites[sites.length - 2],
sites[sites.length - 1],
voromap[index(x, y)],
x
)
)
sites.pop();
sites.push(voromap[index(x, y)]);
}
let pos = 0;
if (sites.length == 1)
for (let y = 0; y < h; ++y) voromap[index(x, y)] = sites[0];
else
for (let y = 0; y < h; ++y) {
while (
pos < sites.length - 1 &&
closest(x, y, sites[pos], sites[pos + 1])
)
pos++;
voromap[index(x, y)] = sites[pos];
}
}
return voromap;
}