Public
Edited
Feb 13, 2023
Importers
20 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 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-tree
2D point distributions
Shortest 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…
Hello
Insert cell
Insert cell
Insert cell
N = 4000
Insert cell
pick2d(width, height, N, "random")
Insert cell
Insert cell
{
await visibility();
const context = DOM.context2d(width, height);
yield context.canvas;

let i = 0;
const radius = Math.sqrt((0.63 * (width * height)) / N);
for (const p of random2d(width, height, N)) {
context.beginPath();
context.arc(p[0], p[1], radius / 2, 0, 2 * Math.PI);
context.stroke();
if (i++ % 20 === 0) yield context.canvas;
}
}
Insert cell
random2d(width, height, N)
Insert cell
function random2d(width, height, N) {
const points = [];
for (let i = 0; i < N; i++) points.push([width * rng(), height * rng()]);
return points;
}
Insert cell
Insert cell
Insert cell
function poissonDisc2d(width, height, N) {
const radius = Math.sqrt((0.63 * (width * height)) / N);
return DiscSampler(width, height, radius);
}
Insert cell
// Code by Jason Davies, Mike Bostock, Martin Roberts, and Jacob Rus
// see https://observablehq.com/@fil/poisson-distribution-generators for details
function* DiscSampler(width, height, radius) {
const k = 13; // maximum number of samples before rejection
const m = 4; // a number mutually prime to k
const radius2 = radius * radius;
const cellSize = 1 / (radius * Math.SQRT1_2);
const gridWidth = Math.ceil(width * cellSize) + 4; // pad grid by 2 on each side
const gridHeight = Math.ceil(height * cellSize) + 4;
const grid = new Float64Array(2 * gridWidth * gridHeight).fill(Infinity);
const queue = [];
const rotx = Math.cos((2 * Math.PI * m) / k);
const roty = Math.sin((2 * Math.PI * m) / k);

// Pick the first sample.
yield sample(0.1 * width * (4.5 + rng()), 0.1 * height * (4.5 + rng()), null);

// Pick a random existing sample from the queue.
pick: while (queue.length) {
const i = (rng() * queue.length) | 0;
const parent = queue[i];
const t = tanpi_2(2 * rng() - 1),
q = 1 / (1 + t * t);
const epsilon = 1e-6;
let dw, dx, dy;
dx = q ? (1 - t * t) * q : -1; // [dx, dy] = random unit vector
dy = q ? 2 * t * q : 0;

// Make a new candidate.
for (let j = 0; j < k; ++j) {
dw = dx * rotx - dy * roty; // temporary name for dx
dy = dx * roty + dy * rotx;
dx = dw;
const rand0 = rng();
const rand1 = rng();
const r = (radius + epsilon) * (1 + 0.65 * rand0 * rand1);
const x = parent[0] + r * dx;
const y = parent[1] + r * dy;

// Accept candidates that are inside the allowed extent
// and farther than 2 * radius to all existing samples.
if ((0 <= x) & (x < width) & (0 <= y) & (y < height) & far(x, y)) {
yield sample(x, y);
continue pick;
}
}

// If none of k candidates were accepted, remove it from the queue.
const r = queue.pop();
if (i < queue.length) queue[i] = r;
// yield {remove: parent};
}

// fast approximation of tan(πx/2)
function tanpi_2(a) {
let b = 1 - a * a;
return a * (-0.0187108 * b + 0.31583526 + 1.27365776 / b);
}

function far(x, y) {
const j0 = (y * cellSize) | 0;
const i0 = (x * cellSize) | 0;
for (let j = j0; j < j0 + 5; ++j) {
const index0 = 2 * (j * gridWidth + i0);
for (let i = index0; i < index0 + 10; i += 2) {
const dx = grid[i] - x;
const dy = grid[i + 1] - y;
if (dx * dx + dy * dy < radius2) return false;
}
}
return true;
}

function sample(x, y, parent) {
const s = [x, y];
// offset grid cell by 2 in each direction to account for border
const i = (x * cellSize + 2) | 0;
const j = (y * cellSize + 2) | 0;
const index = 2 * (gridWidth * j + i);
grid[index] = x;
grid[index + 1] = y;
queue.push(s);
return s;
}
}
Insert cell
// keep for ascending compatibility
function poissonDiscSampler(width, height, radius) {
return Array.from(poissonDisc2d(width, height, N));
}
Insert cell
Insert cell
{
await visibility();
const context = DOM.context2d(width, height);
yield context.canvas;

let i = 0;
const radius = Math.sqrt((width * height) / N / 2.5);
for (const p of hexgrid2d(width, height, N)) {
context.beginPath();
context.arc(p[0], p[1], radius / 2, 0, 2 * Math.PI);
context.stroke();
if (i++ % 20 === 0) yield context.canvas;
}
}
Insert cell
function hexgrid2d(width, height, N) {
const h = Math.sqrt((width * height * (Math.sqrt(5) / 2)) / N),
v = h * (2 / Math.sqrt(5)),
a = [];
for (let i = 0; i < width / h; i++)
for (let j = 0.5; j < height / v; j++)
a.push([(i - (j % 2) / 2) * h, j * v]);
return a;
}
Insert cell
Insert cell
{
await visibility();
const context = DOM.context2d(width, height);
yield context.canvas;

let i = 0;
const radius = Math.sqrt((0.3 * (width * height)) / N);
for (const p of blue2d(width, height, N)) {
context.beginPath();
context.arc(p[0], p[1], radius / 2, 0, 2 * Math.PI);
context.stroke();
if (i++ % 20 === 0) yield context.canvas;
}
}
Insert cell
Insert cell
{
await visibility();
const context = DOM.context2d(width, height);
yield context.canvas;

let i = 0;
const radius = Math.sqrt((0.3 * (width * height)) / N);
for (const p of pseudoblue2d(width, height, N)) {
context.beginPath();
context.arc(p[0], p[1], radius / 2, 0, 2 * Math.PI);
context.stroke();
if (i++ % 20 === 0) yield context.canvas;
}
}
Insert cell
function pseudoblue2d(width, height, N) {
const a = [];
const r = N / (width * height);
for (let i = 0; i < width; i++)
for (let j = 0.5; j < height; j++) if (pseudoblue(i, j) < r) a.push([i, j]);
return a;
}
Insert cell
function blue2d(width, height, N) {
const a = [];
const r = N / (width * height);
for (let i = 0; i < width; i++)
for (let j = 0.5; j < height; j++) if (blue(i, j) < r) a.push([i, j]);
return a;
}
Insert cell
import { sorted_1024 as blue, pseudoblue } from "@fil/blue"
Insert cell
Insert cell
{
await visibility();
const context = DOM.context2d(width, height);
yield context.canvas;

let i = 0;
const radius = Math.sqrt((width * height) / N / 2.5);
for (const p of grid2d(width, height, N)) {
context.beginPath();
context.arc(p[0], p[1], radius / 2, 0, 2 * Math.PI);
context.stroke();
if (i++ % 20 === 0) yield context.canvas;
}
}
Insert cell
function grid2d(width, height, N) {
const radius = Math.sqrt((width * height) / N);
const points = [];
for (let i = 0; i < width / radius; i++)
for (let j = 0; j < height / radius; j++)
points.push([i * radius, j * radius]);
return points;
}
Insert cell
Insert cell
{
await visibility();
const context = DOM.context2d(width, height);
yield context.canvas;

let i = 0;
const radius = Math.sqrt((width * height) / N / 2.5);
for (const p of normal2d(width, height, N)) {
context.beginPath();
context.arc(p[0], p[1], radius / 2, 0, 2 * Math.PI);
context.stroke();
if (i++ % 20 === 0) yield context.canvas;
}
}
Insert cell
function normal2d(width, height, N) {
const random = d3.randomNormal(0, Math.min(width, height) / 6);
const points = [];
for (let i = 0; i < N; i++)
points.push([width / 2 + random(), height / 2 + random()]);
return points;
}
Insert cell
Insert cell
function pick2d(width, height, N, type) {
switch (type) {
case "poisson":
case "poissonDisc":
return poissonDisc2d(width, height, N);
case "pseudoblue":
return pseudoblue2d(width, height, N);
case "hex":
case "hexagons":
case "hexgrid":
return hexgrid2d(width, height, N);
case "square":
case "grid":
return grid2d(width, height, N);
case "normal":
return normal2d(width, height, N);
case "random":
case "uniform":
default:
return random2d(width, height, N);
}
}
Insert cell
rng = Math.random
Insert cell
height = 500
Insert cell
// adapt this code for your application
viewof distributionType = Inputs.select([
"poisson",
"pseudoblue",
"hex",
"grid",
"random",
"normal"
])
Insert cell
pick2d(width, height, N, distributionType)
Insert cell
d3 = require("d3-random@3")
Insert cell
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