function heatmap(
V,
{
_V = typeof V.flat === "function" ? Float64Array.from(V.flat()) : V,
color = d3.interpolateGreys,
extent = d3.extent(_V),
domain = extent,
clamp = false,
dither = true,
width = V.width || (_V.length !== V.length ? V[0].length : null) || 960,
height = _V.length / width,
contours = false,
pixelated = true,
upscale = 1,
context
} = {}
) {
context = context !== undefined ? context : DOM.context2d(width, height, 1);
const im = context.getImageData(0, 0, width, height);
const sl = d3.scaleLinear().domain(domain).range([0, 255]).clamp(clamp);
const c = d3.quantize(color, 256).map((x) => d3.rgb(x));
for (let i = 0; i < _V.length; i++) {
const s = sl(_V[i]),
s0 = Math.floor(s);
const v = dither && Math.random() < s - s0 ? c[s0 + 1] : c[s0];
if (!v) continue;
im.data[4 * i] = v.r;
im.data[4 * i + 1] = v.g;
im.data[4 * i + 2] = v.b;
im.data[4 * i + 3] = 255;
}
context.putImageData(im, 0, 0);
context.canvas.value = V;
if (contours) {
const path = d3.geoPath().context(context);
context.beginPath();
for (const c of d3.contours().size([width, height])(
Float64Array.from(_V, (d) => d || 0)
))
path(c);
context.strokeStyle = typeof contours === "string" ? contours : "white";
context.stroke();
}
return Object.assign(context.canvas, {
style: `width:${width * upscale}px;height:${height * upscale}px;${
pixelated ? " image-rendering: pixelated;" : ""
}`
});
}