Published
Edited
Aug 4, 2022
Importers
18 stars
Insert cell
Insert cell
Insert cell
drawPath(ham, 10, 0.75)
Insert cell
function gridSpanningTree(ncols, nrows) {
const grid = new Array(ncols * nrows);
const col = (i) => i % ncols;
const row = (i) => ~~(i / ncols);
const index = (col, row) => row * ncols + col;
const edges = [];
const visit = (k) => {
grid[k] = true;
const [i, j] = [col(k), row(k)];
const neighbors = [];
if (i > 0) neighbors.push(k - 1);
if (j > 0) neighbors.push(k - ncols);
if (i + 1 < ncols) neighbors.push(k + 1);
if (j + 1 < nrows) neighbors.push(k + ncols);
d3.shuffle(neighbors);
for (let n of neighbors) {
if (!grid[n]) {
edges.push([k, n]);
visit(n);
}
}
};
visit(~~(Math.random() * ncols * nrows));
const connect = d3
.range(nrows * ncols)
.map((i) => ({ left: false, right: false, up: false, down: false }));
for (let [i, j] of edges) {
if (i > j) [i, j] = [j, i];
if (row(i) == row(j)) {
connect[i].right = connect[j].left = true;
} else {
connect[i].down = connect[j].up = true;
}
}
return { nrows, ncols, row, col, edges, connect };
}
Insert cell
st = {
randomize;
return gridSpanningTree(30, 20);
}
Insert cell
drawGraph = (st, cellSize = 10) => {
const { nrows, ncols, row, col, edges } = st;
const x = (i) => (col(i) + 0.5) * cellSize + 0.5;
const y = (i) => (row(i) + 0.5) * cellSize + 0.5;
const canvas = DOM.canvas(cellSize * ncols, cellSize * nrows);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#eee";
ctx.fillRect(0, 0, cellSize * ncols, cellSize * nrows);
ctx.strokeStyle = "black";
ctx.beginPath();
for (let [i, j] of edges) {
ctx.moveTo(x(i), y(i));
ctx.lineTo(x(j), y(j));
}
ctx.stroke();
return canvas;
}
Insert cell
drawGraph(st)
Insert cell
hamiltonianFromSpanningTree = (st) => {
const { nrows, ncols, row, col, edges, connect } = st;
const nrows2 = 2 * nrows;
const ncols2 = 2 * ncols;
const edges2 = [];
const grid2 = new Array(nrows2 * ncols2);
const index2 = (i, dcol, drow) =>
(row(i) * 2 + drow) * ncols2 + col(i) * 2 + dcol;
const col2 = (i) => i % ncols2;
const row2 = (i) => ~~(i / ncols2);
connect.forEach((cell, i) => {
let { left, right, up, down } = cell;
if (right) {
edges2.push([index2(i, 1, 0), index2(i, 2, 0)]);
edges2.push([index2(i, 1, 1), index2(i, 2, 1)]);
} else {
edges2.push([index2(i, 1, 0), index2(i, 1, 1)]);
}
if (!left) {
edges2.push([index2(i, 0, 0), index2(i, 0, 1)]);
}
if (down) {
edges2.push([index2(i, 0, 1), index2(i, 0, 2)]);
edges2.push([index2(i, 1, 1), index2(i, 1, 2)]);
} else {
edges2.push([index2(i, 0, 1), index2(i, 1, 1)]);
}
if (!up) {
edges2.push([index2(i, 0, 0), index2(i, 1, 0)]);
}
});
const link = d3.range(ncols2 * nrows2).map((x) => []);
for (let [i, j] of edges2) {
link[i].push(j);
link[j].push(i);
}
let j = 0;
const path = [];
const visited = [];
for (let k = edges2.length; k > 0; k--) {
path.push(j);
visited[j] = true;
j = visited[link[j][0]] ? link[j][1] : link[j][0];
}
return {
nrows: nrows2,
ncols: ncols2,
col: col2,
row: row2,
edges: edges2,
path
};
}
Insert cell
ham = hamiltonianFromSpanningTree(st)
Insert cell
drawGraph(ham)
Insert cell
drawPath = (ham, cellSize = 10, thick = 0.5) => {
const { nrows, ncols, row, col, path } = ham;
const x = (i) => (col(i) + 0.5) * cellSize + 0.5;
const y = (i) => (row(i) + 0.5) * cellSize + 0.5;
const canvas = DOM.canvas(cellSize * ncols, cellSize * nrows);
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "black";
ctx.lineJoin = "round";
ctx.lineWidth = cellSize * thick;
ctx.beginPath();
for (let i of path) {
ctx.lineTo(x(i), y(i));
}
ctx.closePath();
ctx.stroke();
return 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