Published
Edited
Apr 12, 2020
Insert cell
Insert cell
{
const height = 425;
const svg = d3.select(DOM.svg(width, height));

let container = svg
.append("g")
.attr("transform-origin", "350px 155px")
.attr("transform", "rotate(45)");

let elements = 54;
let size = 20;

let config = {
dx: size,
dy: size,
rows: elements / 2,
cols: elements / 2,
margin: 0,
padding: 2
};

let matrix = {};
for (let y = 0; y <= config["rows"]; y++) {
matrix[y] = {};
for (let x = 0; x <= config["cols"]; x++) {
matrix[y][x] = {
x: config["margin"] + x * config["dx"],
y: config["margin"] + y * config["dy"]
};
}
}

var data = { nodes: [], links: [] };

for (let x = 0; x <= config["cols"] - 1; x++) {
for (let y = 0; y <= config["rows"] - 1; y++) {
let line = [];
line.push(matrix[x][y]);
line.push(matrix[x + 1][y]);
line.push(matrix[x + 1][y + 1]);
line.push(matrix[x][y + 1]);
line.push(matrix[x][y]);

line = line.map(d => {
return {
x: d.x + config['padding'] * y,
y: d.y + config['padding'] * x
};
});

let tl = line[0];
let c = d3.polygonCentroid(line.map(d => [d.x, d.y]));
data.nodes.push({
id: data.nodes.length + 1,
path: line,
x1: tl.x,
y1: tl.y,
x: c[0],
y: c[1]
});
}
}

let voronoi = d3
.voronoi()
.x(d => d.x)
.y(d => d.y);

voronoi.links(data.nodes).forEach(l => {
var dx = l.source.x - l.target.x,
dy = l.source.y - l.target.y;
l.distance = Math.sqrt(dx * dx + dy * dy);

// only connect neighbors
if (l.distance <= config["dx"] + config["dy"]) {
data.links.push({
distance: Math.sqrt(dx * dx + dy * dy),
source: l.source,
target: l.target
});
}
});

var gLines = container
.append("g")
.attr("class", "lines")
.selectAll("g.lines");

var link = gLines
.data(data.links)
.enter()
.append("line")
.attr(
"style",
(_, i) => `stroke-width: 4px; stroke: ${d3.schemeCategory10[3]}`
);

var g = container
.selectAll("g.nodes")
.data(data.nodes)
.enter()
.append("g")
.attr("class", "nodes");

var node = g;

g.append("path")
.attr("class", "poly")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("style", (_, i) => `fill: white; stroke: ${d3.schemeCategory10[3]}`)
.attr("d", d => "M" + d.path.map((d, i) => `${d.x},${d.y}`).join("L"));

var simulation = d3
.forceSimulation()
.force("link", d3.forceLink().id(d => d.id))
.force("charge", d3.forceManyBody());

simulation.nodes(data.nodes);

simulation.force("link").links(data.links);

function tick(e) {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);

node.attr("transform", d => `translate(${d.x},${d.y})`);
}
simulation.on("tick", tick);

simulation.stop();
tick();

return svg.node();
}
Insert cell
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