viewof image0 = {
const V = new Float32Array(width * height).fill(NaN);
const delaunay = d3.Delaunay.from(points);
for (let i = 0; i < delaunay.triangles.length; i += 3) {
const [a, b, c] = delaunay.triangles.slice(i, i + 3);
const [pa, pb, pc] = Array.from([a, b, c], (i) => P[i]);
if (isNaN(pa) || isNaN(pb) || isNaN(pc)) continue;
const [Ax, Bx, Cx] = Array.from([a, b, c], (i) => points[i][0]);
const [Ay, By, Cy] = Array.from([a, b, c], (i) => points[i][1]);
const [x0, x1] = d3.extent([Ax, Bx, Cx]);
const [y0, y1] = d3.extent([Ay, By, Cy]);
const z = (By - Cy) * (Ax - Cx) + (Ay - Cy) * (Cx - Bx);
if (!z) continue;
for (let x = Math.floor(x0); x < x1; x++) {
for (let y = Math.floor(y0); y < y1; y++) {
if (x < 0 || x >= width || y < 0 || y >= height) continue;
const ga = ((By - Cy) * (x - Cx) + (y - Cy) * (Cx - Bx)) / z;
const gb = ((Cy - Ay) * (x - Cx) + (y - Cy) * (Ax - Cx)) / z;
const gc = 1 - ga - gb;
if (ga >= 0 && gb >= 0 && gc >= 0)
V[x + width * y] = ga * pa + gb * pb + gc * pc;
}
}
}
const values = points
.map((d, i) => [...d, P[i]])
.filter(([, , p]) => !isNaN(p));
const quad = d3.quadtree().addAll(values);
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
if (isNaN(V[x + width * y])) V[x + width * y] = quad.find(x, y)[2];
}
}
return heatmap(V, {
width,
color: sourcesOnly ? d3.interpolateInferno : d3.interpolateRdYlBu
});
}