Published
Edited
Jun 23, 2020
Importers
3 stars
Hello, A5Chandrupatla’s root-finding methodSidi’s root-finding methodRegular numbersDruidJS workerNatural breaksDistance to a segmentRay out of a convex hullWord Tour: 40k words and their friendsHello, @thi.ng/grid-iteratorsHead/tail breaksPseudo-blue noise shaderHow fast does walk-on-spheres converge?AoC 12: shortest path under constraintsKDE estimationPlot: Correlation heatmapPoisson Finish 2Poisson disk sampling functionsWoS with transportSimple and surprising sortLocal medianTime series topological subsamplingUnion-FindLevel set experiment 1Mean value coordinatesPoisson potentialMiddle-squareWorld of squares (spherical)World of squaresLargest Inscribed SquareHello, PyWaveletsGeothmetic meandianHello, Reorder.jsGeometric MedianImage FFTTransport to a mapDisc TransportTP3: Power Diagram and Semi-Discrete Optimal TransportThe blue waveHello, genetic-jsSliced Optimal TransportDruidJSSelf-Organizing Maps meet DelaunayHello, polygon-clippingseedrandom, minimalWalk on Spheres 2Walk on SpheresHello, AutoencoderKaprekar’s numberVoronoiMap2DHello, ccwt.jsPolygon TriangulationQuantile.invert?Linear congruential generatorHue blurNeedle in a haystack
Moving average blur
Apollo 11 implementation of trigonometric functions, by Margaret H. Hamilton (march 1969)2D curves intersectionThe 2D approximate Newton-Raphson methodInverting Lee’s Tetrahedral projectionLinde–Buzo–Gray stipplingMean shift clustering with kd-tree2D point distributionsShortest pathKahan SummationHello, delatinDijkstra’s algorithm in gpu.jsLloyd’s relaxation on a graphManhattan DiameterManhattan VoronoiMobility landscapes — an introductionDijkstra’s shortest-path treeH3 odditiesProtein MatrixConvex Spectral WeightsSort stuff by similarityKrigingDelaunay.findTrianglen-dimensions binning?Travelling with a self-organizing mapUMAP-o-MaticMNIST & UMAP-jsHello UMAP-jsMean shift clusteringLevenshtein transitionRd quasi-random sequencesAutomated label placement (countries)Phyllotaxis explainedMotionrugsPlanar hull (Andrew’s monotone chain algorithm)South Africa’s medial axisTravelling salesperson approximation with t-SNEDistance to shoreWorkerngraph: pagerank, louvain…t-SNE VoronoiCloud ContoursCircular function drawingKruskal MazeMyceliumTravelling salesperson approximation on the globe, with t-SNEtsne.jstsne.js & worker
Also listed in…
Interpolation
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
source = Float32Array.from({ length: n * m }, Math.random)
Insert cell
show(
blur()
.width(n)
.radius(5)
) // new code
Insert cell
show(source => mmblur1(source, 5, n)) // calls the code extracted from d3-contour, for comparison
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
blurred = blur()
.radius(radius)
.iterations(iterations)(values)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
profile = blur()
.iterations(profile_iterations)
.radius(profile_radius)(Float32Array.from({ length: 59 }, (d, i) => i === 29))
Insert cell
d3.sum(profile)
Insert cell
Insert cell
blur()([0, 1, 2, 3, 8, 10, 12, 27])
Insert cell
blur()
.radius(2)
.iterations(1)
.width(4)([0, 1, 2, 3, 8, 10, 12, 27])
Insert cell
Insert cell
data = Array.from(values, (d, i) => ({ value: d, index: i }))
Insert cell
blur().value(d => Math.sin(d.value))(data)
Insert cell
Insert cell
blur()(new Set(values.slice().sort()))
Insert cell
Insert cell
Insert cell
show(blur()) // 1D horizontal
Insert cell
show(
blur()
.width(n)
.direction("y")
)
Insert cell
// compose a heavy "x" blur with a light "y" blur
show(data =>
blur()
.width(n)
.radius(1)
.iterations(1)
.direction("y")(
blur()
.width(n)
.radius(20)
.iterations(4)
.direction("x")(data)
)
)
Insert cell
Insert cell
show(
blur()
.width(n)
.radius(.5)
)
Insert cell
blur = {
const { floor, max, min, round } = Math;

function blurTransfer(V, r, n, vertical) {
if (!r) return; // radius 0 is a noop

const [source, target] = V,
rc = Math.ceil(r),
frac = (rc - r) / rc,
frac_1 = r / rc,
m = floor(source.length / n),
w = 2 * rc + 1,
w1 = 1 / w,
ki = vertical ? m : 1,
kj = vertical ? 1 : n,
W = w * ki,
R = rc * ki;

for (let j = 0; j < m; ++j) {
const k0 = kj * j,
kn = k0 + ki * (n - 1);
// faster loop for integer radius
if (!frac) {
for (let i = 0, sr = w * source[k0]; i < n + rc; ++i) {
const k = ki * i + kj * j;
sr += source[min(k, kn)] - source[max(k - W, k0)];
target[max(k - R, k0)] = sr * w1;
}
} else {
for (let i = 0, sr = w * source[k0]; i < n + rc; ++i) {
const k = ki * i + kj * j,
index = max(k - R, k0);
sr += source[min(k, kn)] - source[max(k - W, k0)];
target[index] = frac_1 * sr * w1 + frac * source[index];
}
}
}
V.reverse(); // target becomes V[0] and will be used as source in the next iteration
}

return function blur() {
var radius = 5,
accessor,
width,
iterations = 3,
direction = null;

function blur(data) {
const n = width || data.length,
m = floor(data.length / n),
V = [
accessor
? Float32Array.from(data, accessor)
: Float32Array.from(data),
new Float32Array(data.length)
];

for (var i = 0; i < iterations; i++) {
if (direction !== "y" && n > 1) {
blurTransfer(V, radius, n, false);
}
if (direction !== "x" && m > 1) {
blurTransfer(V, radius, m, true);
}
}

V[0].width = n;
V[0].height = m;
return V[0];
}

blur.radius = _ => (_ === undefined ? radius : ((radius = +_), blur));
blur.width = _ => (_ === undefined ? width : ((width = round(+_)), blur));
blur.iterations = _ =>
_ === undefined ? iterations : ((iterations = +_), blur);
blur.direction = _ =>
_ === undefined ? direction : ((direction = _), blur);
blur.value = _ =>
typeof _ === "function" ? ((accessor = _), blur) : accessor;
return blur;
};
}
Insert cell
// original code from D3
mmblur1 = {
function blurX(source, target, r) {
var n = source.width,
m = source.height,
w = (r << 1) + 1;
for (var j = 0; j < m; ++j) {
for (var i = 0, sr = 0; i < n + r; ++i) {
if (i < n) {
sr += source.data[i + j * n];
}
if (i >= r) {
if (i >= w) {
sr -= source.data[i - w + j * n];
}
target.data[i - r + j * n] = sr / Math.min(i + 1, n - 1 + w - i, w);
}
}
}
}

// TODO Optimize edge cases.
// TODO Optimize index calculation.
// TODO Optimize arguments.
function blurY(source, target, r) {
var n = source.width,
m = source.height,
w = (r << 1) + 1;
for (var i = 0; i < n; ++i) {
for (var j = 0, sr = 0; j < m + r; ++j) {
if (j < m) {
sr += source.data[i + j * n];
}
if (j >= r) {
if (j >= w) {
sr -= source.data[i + (j - w) * n];
}
target.data[i + (j - r) * n] = sr / Math.min(j + 1, m - 1 + w - j, w);
}
}
}
}

return function blur(source, r, n) {
if (n === undefined) n = source.length;
const m = source.length / n;
var values0 = Float32Array.from(source),
values1 = new Float32Array(n * m);

blurX(
{ width: n, height: m, data: values0 },
{ width: n, height: m, data: values1 },
r
);
blurY(
{ width: n, height: m, data: values1 },
{ width: n, height: m, data: values0 },
r
);
blurX(
{ width: n, height: m, data: values0 },
{ width: n, height: m, data: values1 },
r
);
blurY(
{ width: n, height: m, data: values1 },
{ width: n, height: m, data: values0 },
r
);
blurX(
{ width: n, height: m, data: values0 },
{ width: n, height: m, data: values1 },
r
);
blurY(
{ width: n, height: m, data: values1 },
{ width: n, height: m, data: values0 },
r
);

return Object.assign(values0, { width: n, height: m });
};

// this is the true function in d3
/*
function blur(source, r) {
const k = 2;
return blur(source, r >> k);
};
*/
}
Insert cell
d3 = require("d3@5")
Insert cell
import { slider } from "@jashkenas/inputs"
Insert cell
embed = require("vega-embed@6")
Insert cell
html`<style>
.vega-embed-wrapper{
overflow:hidden;
}
</style>`
Insert cell
function show(blur, { nn = n, runs = 10 } = {}) {
const m = source.length / nn;
const scale = Math.pow(2, -Math.ceil(Math.log2(Math.max(nn, m) / 400)));

const w = scale * n,
h = scale * m;
const x = d3.scaleLinear([0, nn], [0, w]),
y = d3.scaleLinear([0, m], [0, h]);
const c = d3.scaleSequential(d3.interpolateGreys);

const context = DOM.context2d(width, h);

let blurred;
let t = performance.now();
for (let i = 0; i < runs; i++) blurred = blur(source);
t = (performance.now() - t) / runs;

// a rather inefficient way to show the output :-)
function draw(array) {
for (let k = 0; k < array.length; k++) {
const i = k % nn,
j = Math.floor(k / nn);
context.fillStyle = c(array[i + n * j]);
context.fillRect(x(i), y(j), x(i + 1) - x(i), y(j + 1) - y(j));
}
}

draw(source);
context.translate(x(nn) + 50, 0);
context.fillStyle = "red";
context.fillText(`${+t.toFixed(1)}ms`, -40, 10);
draw(blurred);

return context.canvas;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more