Published
Edited
Oct 25, 2018
4 stars
Insert cell
Insert cell
Insert cell
{
let k = (cellHeight - 1) * cellWidth;
const width = cellWidth * (cellSize + cellSpacing) + cellSpacing;
const height = cellHeight * (cellSize + cellSpacing) + cellSpacing;
const parent = Object.assign(new Array(cellHeight * cellWidth), {[k]: -1});
const context = DOM.context2d(width, height, 1);
context.canvas.style.imageRendering = "pixelated";
context.fillStyle = "#777";
context.fillRect(0, 0, width, height);
context.fillStyle = "#fff";
fillCells(context);
let iter = 0;
for (const [i, j] of explore(k, parent)) {
context.fillStyle = sinebow(Date.now() / 10000);
fillPath(context, k, parent);
context.fillStyle = "#000";
fillPath(context, k = j, parent);
if (iter % 10 === 0) {
yield context.canvas;
}
iter++;
if (score(k) === 0) break;
}
yield context.canvas;
}
Insert cell
function score(i) {
return Math.random();
}
Insert cell
function* explore(start, parent) {
const frontier = new Queue([start], (a, b) => score(a) - score(b));
let i, j;
while ((i = frontier.pop()) !== undefined) {
if (cells[i] & E && parent[j = i + 1] === undefined) {
parent[j] = i;
frontier.push(j);
yield [i, j];
}
if (cells[i] & W && parent[j = i - 1] === undefined) {
parent[j] = i;
frontier.push(j);
yield [i, j];
}
if (cells[i] & S && parent[j = i + cellWidth] === undefined) {
parent[j] = i;
frontier.push(j);
yield [i, j];
}
if (cells[i] & N && parent[j = i - cellWidth] === undefined) {
parent[j] = i;
frontier.push(j);
yield [i, j];
}
}
}
Insert cell
function generate(cellWidth, cellHeight) {
const heap = new Queue([], (a, b) => a.value - b.value);
const cells = new Uint8Array(cellWidth * cellHeight);
let edge;
heap.push({index: 0, direction: N, value: Math.random()});
heap.push({index: 0, direction: E, value: Math.random()});
while (edge = heap.pop()) {
let i0 = edge.index, i1;
let d0 = edge.direction, d1;
let x0 = i0 % cellWidth, x1;
let y0 = i0 / cellWidth | 0, y1;
if (d0 === N) i1 = i0 - cellWidth, x1 = x0, y1 = y0 - 1, d1 = S;
else if (d0 === S) i1 = i0 + cellWidth, x1 = x0, y1 = y0 + 1, d1 = N;
else if (d0 === W) i1 = i0 - 1, x1 = x0 - 1, y1 = y0, d1 = E;
else i1 = i0 + 1, x1 = x0 + 1, y1 = y0, d1 = W;
if (cells[i1] === 0) {
cells[i0] |= d0, cells[i1] |= d1;
if (y1 > 0 && cells[i1 - cellWidth] === 0) {
heap.push({index: i1, direction: N, value: Math.random()});
}
if (y1 < cellHeight - 1 && cells[i1 + cellWidth] === 0) {
heap.push({index: i1, direction: S, value: Math.random()});
}
if (x1 > 0 && cells[i1 - 1] === 0) {
heap.push({index: i1, direction: W, value: Math.random()});
}
if (x1 < cellWidth - 1 && cells[i1 + 1] === 0) {
heap.push({index: i1, direction: E, value: Math.random()});
}
}
}
return cells;
}
Insert cell
function fillCells(context) {
for (let y = 0, i = 0; y < cellHeight; ++y) {
for (let x = 0; x < cellWidth; ++x, ++i) {
fillCell(context, i);
if (cells[i] & S) fillEdge(context, i, i + cellWidth);
if (cells[i] & E) fillEdge(context, i, i + 1);
}
}
}
Insert cell
function fillCell(context, i) {
const x = i % cellWidth;
const y = i / cellWidth | 0;
context.fillRect(
x * cellSize + (x + 1) * cellSpacing,
y * cellSize + (y + 1) * cellSpacing,
cellSize, cellSize
);
}
Insert cell
function fillEdge(context, i, j) {
if (j < i) [j, i] = [i, j];
const x = i % cellWidth;
const y = i / cellWidth | 0;
if (i === j - 1) {
context.fillRect(
(x + 1) * (cellSize + cellSpacing),
y * cellSize + (y + 1) * cellSpacing,
cellSpacing, cellSize
);
} else {
context.fillRect(
x * cellSize + (x + 1) * cellSpacing,
(y + 1) * (cellSize + cellSpacing),
cellSize, cellSpacing
);
}
}
Insert cell
function fillPath(context, i1, parent) {
while (true) {
fillCell(context, i1);
const i0 = parent[i1];
if (i0 < 0) return;
fillEdge(context, i1, i0);
i1 = i0;
}
}
Insert cell
cells = replay, generate(cellWidth, cellHeight)
Insert cell
cellSize = 3
Insert cell
cellSpacing = 1
Insert cell
cellWidth = Math.floor((width - cellSpacing) / (cellSize + cellSpacing))
Insert cell
cellHeight = Math.floor((600 - cellSpacing) / (cellSize + cellSpacing))
Insert cell
N = 1 << 0
Insert cell
S = 1 << 1
Insert cell
W = 1 << 2
Insert cell
E = 1 << 3
Insert cell
function sinebow(t) {
return "#" + [0, 1, 2].map(i => {
const v = 255 * Math.sin(Math.PI * (t + i / 3)) ** 2;
return Math.round(v).toString(16).padStart(2, "0");
}).join("");
}
Insert cell
Queue = require("tinyqueue@2")
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