Public
Edited
Mar 31, 2024
Fork of Simple SVG
Insert cell
Insert cell
{
const svg = d3.select(DOM.svg(width, height));

svg
.append("rect")
.attr("id", "background-rect")
.attr("width", width)
.attr("height", height)
.style("fill", "steelblue");

const grid = svg
.append("g")
.attr("transform", `translate(${width / 2}, ${height / 2})`);

grid
.selectAll("circle")
.data([...combine(alive)].map((d) => d.split(",")))
.enter()
.append("circle")
.attr("cx", (d) => d[0] * squareSide)
.attr("cy", (d) => d[1] * squareSide)
.attr("r", circleRadius)
.style("fill", "white");

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
## Dots
Insert cell
glider = new Set(['-1,-1', '0,-1', '1,-1', '1,0', '0,1']) // https://conwaylife.com/wiki/Glider
Insert cell
alive = {
let gliders = this || [];
let i = 0;
while (true) {
if (fps === 0) return yield gliders;
yield Promises.tick(1000 / fps, (gliders = next(gliders)));
}
}
Insert cell
Insert cell
combine = (arrayOfSets) =>
arrayOfSets.reduce((a, b) => new Set([...a, ...b]), new Set())
Insert cell
## Dropper
Insert cell
drop = () => {
/*const angle = angles[Math.floor(Math.random() * angles.length)];
const dx = (() => Math.floor(dist() * widthBound))();
const dy = (() => Math.floor(dist() * heightBound))();*/

const dropSide =
dropLocations[Math.floor(Math.random() * dropLocations.length)];

const angle = dropAngles[dropSide][Math.floor(Math.random() * 2)];
const dx = dropTranslations[dropSide][0]();
const dy = dropTranslations[dropSide][1]();

const coords = coordinateSetToArray(glider)
.map((coordinate) => rotate(degreesToRadians(angle), coordinate))
.map((coordinate) => translate(dx, dy, coordinate));

return new Set(coords.map((a) => a.toString()));
}
Insert cell
angles = [0, 90, 180, 270]
Insert cell
dropLocations = ['N', 'E', 'S', 'W']
Insert cell
dropAngles = ({
N: [90, 180],
E: [180, 270],
S: [270, 0],
W: [0, 90]
})
Insert cell
dropTranslations = ({
N: [
() => Math.floor(dist() * widthBound),
() => -(heightBound + boardMargin)
],
E: [() => widthBound + boardMargin, () => Math.floor(dist() * heightBound)],
S: [() => Math.floor(dist() * widthBound), () => heightBound + boardMargin],
W: [() => -(widthBound + boardMargin), () => Math.floor(dist() * heightBound)]
})
Insert cell
dist = () =>
(new Array(f)
.fill(0)
.map(() => Math.random())
.reduce((a, b) => a + b) /
f) *
2 -
1
Insert cell
## Dimensions
Insert cell
squareSide = (circleRadius + circleMargin) * 2
Insert cell
boardWidth = oddStep(Math.ceil(width / squareSide))
Insert cell
boardHeight = oddStep(Math.ceil(height / squareSide))
Insert cell
widthBound = Math.floor(boardWidth / 2)
Insert cell
heightBound = Math.floor(boardHeight / 2)
Insert cell
boardMargin = 3 // a glider is 3 x 3
Insert cell
inBounds = (coordinateSet) => {
return coordinateSetToArray(coordinateSet).every(
(c) =>
Math.abs(c[0]) < widthBound + 2 * boardMargin &&
Math.abs(c[1]) < heightBound + 2 * boardMargin
);
}
Insert cell
## State transition functions

taken from [Visnu Pitiyanuvath](https://observablehq.com/@visnup)'s implementation of [Conway’s Game of Life](https://observablehq.com/@visnup/game-of-life)
Insert cell
function tick(cells) {
const counts = {}
for (const cell of cells)
for (const n of neighbors(cell.split(",").map(Number)))
counts[n] = (counts[n] ?? 0) + 1

const next = new Set()
for (const [cell, count] of Object.entries(counts))
if (count === 3 || (count == 2 && cells.has(cell))) next.add(cell)

return next
}
Insert cell
function* neighbors([x, y]) {
for (const dx of [-1, 0, 1])
for (const dy of [-1, 0, 1])
if (dx !== 0 || dy !== 0) yield [x + dx, y + dy]
}
Insert cell
## Set and array utility
Insert cell
coordinateSetToArray = (set) =>
[...set].map((coordinate) => coordinate.split(","))
Insert cell
oddStep = (x) => x + (x % 2 === 0 ? 1 : 0)
Insert cell
Insert cell
## Rotation
Insert cell
rotate = function (φ, [x, y]) {
return [x * cos(φ) - y * sin(φ), x * sin(φ) + y * cos(φ)].map(round);
}
Insert cell
degreesToRadians = (degrees) => (degrees * π) / 180
Insert cell
π = Math.PI
Insert cell
sin = Math.sin
Insert cell
cos = Math.cos
Insert cell
round = Math.round
Insert cell
## Translation
Insert cell
translate = (δx, δy, [x, y]) => [x + δx, y + δy]
Insert cell
## View height
Insert cell
height = width / 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