optimizeCenters = function (points,imgdata,steps = 1) {
let data = imgdata.data;
let [w,h] = [imgdata.width,imgdata.height]
for (let step = 0; step < steps; step++) {
let cells = voronoiCells(w,h,points)
let vimage = voronoiImageGL(w,h,cells);
let vdata = imageData(vimage).data;
function index(x,y) { return (y*w+x)*4; }
function center(x,y) {
let i = index(x,y);
return vdata[i]+vdata[i+1]*256;
}
function getPixel(x,y) {
let i = index(~~x,~~y);
return [data[i],data[i+1],data[i+1]]
}
function pixelError (rgb1,rgb2) {
return (rgb1[0]-rgb2[0])**2+(rgb1[1]-rgb2[1])**2+(rgb1[2]-rgb2[2])**2
}
let error = points.map (p => ({ rgb : getPixel(p[0],p[1]), dir : [0,0,0,0,0,0,0,0] }));
let totalError = 0;
let dir = [[1,0],[-1,0],[1,1],[-1,-1],[0,1],[0,-1],[1,-1],[-1,1]];
for (let x = 1; x+1 < w; x++) {
for (let y = 1; y+1 < h; y++) {
let c = center(x,y);
let rgb = getPixel(x,y);
let curError = pixelError (error[c].rgb, rgb);
totalError += curError;
let count = 0;
for (let idir = 0; idir < 8; idir++) {
let [dx,dy] = dir [idir];
let neighbor = center(x+dx,y+dy);
let opposite = center(x-dx,y-dy);
if (neighbor == opposite) continue;
let dError = pixelError(error[opposite].rgb,rgb)-curError
error[neighbor].dir[idir] += dError
error[opposite].dir[idir] += dError
}
}
}
for (let i = 0; i < points.length; i++) {
let bestdir = -1;
let besterror = 0;
let errdir = error[i].dir;
for (let j = 0; j < 8; j++) {
if (errdir [j] < besterror) {
bestdir = j;
besterror = errdir[j]
}
}
if (bestdir >= 0) {
points [i][0] = Math.min(w-1, Math.max(0, points [i][0] + dir[bestdir][0]));
points [i][1] = Math.min(h-1, Math.max(0, points [i][1] + dir[bestdir][1]));
}
}
}
return points;
}