const relaxIntensity = 1.0;
const closest = new Float64Array(total * 2);
const weights = new Float64Array(total);
img = loadImage('monalisa.jpg');
createCanvas(img.width*2, img.height);
cachedBrightness = new Float64Array(img.width * img.height);
for (var y = 0, i = 0; y < img.height; y++) {
for (var x = 0; x < img.width; x++) {
var index = y * img.width + x;
cachedBrightness[index] = 1 - red(img.get(x, y)) / 255;
points = new Float64Array(total * 2);
delaunay = new Delaunay(points);
voronoi = delaunay.voronoi([0, 0, img.width, img.height]);
rect(0, 0, img.width, img.height);
for(var i = 0; i < total; i++){
circle(points[i*2], points[i*2+1], 3);
rect(0, 0, img.width, img.height);
var boundary = new Rectangle(0, 0, img.width, img.height);
var qtree = new QuadTree(boundary, 10);
for(var i = 0; i < total; i++){
qtree.insert(new Point(points[i*2], points[i*2+1]));
while(added < total && ++tries < 1000000){
var x = round(random(img.width));
var y = round(random(img.height));
var index = y * img.width + x;
if(Math.random() < cachedBrightness[index]){
points[added * 2 + 0] = x;
points[added * 2 + 1] = y;
for (let y = 0, i = 0; y < img.height; ++y) {
for (let x = 0; x < img.width; ++x) {
const w = cachedBrightness[y * img.width + x];
i = delaunay.find(x + 0.5, y + 0.5, i);
closest[i * 2] += w * (x + 0.5);
closest[i * 2 + 1] += w * (y + 0.5);
const w = Math.pow(frameCount + 1, -0.8) * 10;
for (let i = 0; i < total; ++i) {
const x0 = points[i * 2];
const y0 = points[i * 2 + 1];
const x1 = weights[i] ? closest[i * 2] / weights[i] : x0;
const y1 = weights[i] ? closest[i * 2 + 1] / weights[i] : y0;
points[i * 2] = x0 + (x1 - x0) * relaxIntensity + (Math.random() - 0.5) * w;
points[i * 2 + 1] = y0 + (y1 - y0) * relaxIntensity + (Math.random() - 0.5) * w;