Public
Edited
Jan 22, 2023
Importers
18 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 Median
Image FFT
Transport 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 haystackMoving average blurApollo 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…
Image
Interpolation
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
diffScale = d3.scaleDiverging(d3.interpolatePiYG)
Insert cell
contours = {
const { Dre } = fwd;
const w = pixels.width,
h = pixels.height,
wh = Math.floor(w / 2),
hh = Math.floor(h / 2);

const values = Float32Array.from(Dre).fill(0);

// fftshift
for (let k = 0; k < values.length; k++) {
const i = k % w,
j = Math.floor(k / w);
const shifti = (i >= wh ? i - w : i) + wh;
const shiftj = (j >= hh ? j - h : j) + hh;
values[shifti + w * shiftj] = Math.log1p(Math.hypot(Dre[k]));
}

return d3.contours().size([pixels.width, pixels.height]).thresholds(8)(
d3.blur2({ data: values, width: pixels.width }, 4).data
);
}
Insert cell
N = Math.min(+maxwidth, original.naturalWidth)
Insert cell
M = Math.round((N * original.naturalHeight) / original.naturalWidth)
Insert cell
Insert cell
Insert cell
Insert cell
// FWD({ data: pixels.data, width: pixels.width, height: pixels.height })
Insert cell
fwd = Generators.observe(
worker(
FWD,
{ data: pixels.data, width: pixels.width, height: pixels.height },
"const FFT = " + FFTtext
)
)
Insert cell
function FWD({ data, width: w, height: h }) {
if (w === undefined) {
w = data.length;
h = 1;
}
const Dre = Float64Array.from({ length: h * w }),
Dim = Float64Array.from({ length: h * w });

// horizontal
{
const re = new Float64Array(w),
im = new Float64Array(w);
for (let j = 0; j < h; j++) {
for (let i = 0; i < w; i++) {
re[i] = data[i + w * j];
im[i] = 0;
}

FFT.transform(re, im);

for (let i = 0; i < w; i++) {
const k = i + w * j;
Dre[k] = re[i];
Dim[k] = im[i];
}
}
}

// vertical
{
const re = new Float64Array(h),
im = new Float64Array(h);
for (let i = 0; i < w; i++) {
for (let j = 0; j < h; j++) {
re[j] = Dre[i + w * j];
im[j] = Dim[i + w * j];
}

FFT.transform(re, im);

for (let j = 0; j < h; j++) {
const k = i + w * j;
Dre[k] = re[j];
Dim[k] = im[j];
}
}
}

return { Dre, Dim };
}
Insert cell
filtered = FILTERED(fwd)
Insert cell
function FILTERED(fwd) {
const Dre = fwd.Dre.slice(),
Dim = fwd.Dim.slice();
const w = pixels.width,
h = pixels.height,
wh = Math.floor(w / 2),
hh = Math.floor(h / 2);

// low-pass (blur)
const r = lowpass_radius;
if (r < wh) {
for (let k = 0; k < Dre.length; k++) {
const i = k % w,
j = Math.floor(k / w);
const shifti = i > wh ? i - w : i;
const shiftj = j > hh ? j - h : j;
if (Math.hypot(shifti, shiftj) > r) Dre[k] = Dim[k] = 0;
}
}

// manipulate D as needed

for (let i = 0; i < mouses.length; i++) {
const mouse = mouses[i];
const down = i === 0 && mouses.down;
for (let k = 0; k < Dre.length; k++) {
const i = k % w,
j = Math.floor(k / w);
const shifti = i > wh ? i - w : i;
const shiftj = j > hh ? j - h : j;
if (
(down && mouse.x < w) ^
(Math.hypot(shifti - mouse.x, shiftj - mouse.y) < mouse.radius ||
Math.hypot(shifti + mouse.x, shiftj + mouse.y) < mouse.radius)
)
Dre[k] = Dim[k] = 0;
}
}

return { Dre, Dim };
}
Insert cell
// INVERSE({ data: filtered, w: pixels.width, h: pixels.height })
Insert cell
inverse = Generators.observe(
worker(
INVERSE,
{ data: filtered, width: pixels.width, height: pixels.height },
"const FFT = " + FFTtext
)
)
Insert cell
function INVERSE({ data, width: w, height: h }) {
if (w === undefined) {
w = data.length;
h = 1;
}
const Dre = data.Dre.slice(),
Dim = data.Dim.slice();
const wh = Math.floor(w / 2),
hh = Math.floor(h / 2);

// horizontal
{
const re = new Float64Array(w),
im = new Float64Array(w);

for (let j = 0; j < h; j++) {
for (let i = 0; i < w; i++) {
re[i] = Dre[i + w * j];
im[i] = Dim[i + w * j];
}

FFT.inverseTransform(re, im);

for (let i = 0; i < re.length; i++) {
Dre[i + w * j] = re[i];
Dim[i + w * j] = im[i];
}
}
}

// vertical
{
const re = new Float64Array(h),
im = new Float64Array(h);
const a = 1 / (w * h); // normalization
for (let i = 0; i < w; i++) {
for (let j = 0; j < h; j++) {
re[j] = Dre[i + w * j];
im[j] = Dim[i + w * j];
}

FFT.inverseTransform(re, im);

for (let j = 0; j < re.length; j++) {
Dre[i + w * j] = re[j] * a;
}
}
}

return Dre; // we ditch the Dim
}
Insert cell
import { worker } from "@fil/worker"
Insert cell
// probably one of the worst ways to load code :)
FFTtext = d3
.text(
"https://raw.githubusercontent.com/nayuki/Nayuki-web-published-code/master/free-small-fft-in-multiple-languages/fft.js"
)
.then(
d => "(function() { " + d + "; return { transform, inverseTransform }; })()"
)
Insert cell
FFT = "🤘" // eval(FFTtext) if you want to use it without the worker
Insert cell
import { checkbox, file, select, slider } from "@jashkenas/inputs"
Insert cell
import { imageInput } from "@mbostock/file-input"
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