Published
Edited
Sep 7, 2022
Insert cell
Insert cell
Insert cell
{
const bgColor = "#FFFDE7";

const svg = d3
.create("svg")
.attr("width", 480)
.attr("height", 280)
.attr("shape-rendering", "geometricPrecision")
.style("background-color", bgColor)
.style("border", "2px solid #D7CCC8")
.style("border-radius", "8px");

const node = [
[20, 70],
[120, 20],
[120, 40],
[120, 60],
[120, 70],
[120, 90],
[120, 110],
[120, 120],
[220, 70]
].map((d) => [2 * d[0], 2 * d[1]]);

const link = [
[0, 1],
[0, 3],
[0, 5],
[0, 7],
[8, 1],
[8, 2],
[8, 4],
[8, 7],
[1, 4],
[3, 2],
[3, 5],
[7, 2],
[6, 3]
].map((d) => ({
from: d[0],
to: d[1],
link: createLink(d)
}));

function createLink(d) {
if (node[d[0]][0] !== node[d[1]][0])
return d3.linkHorizontal()({
source: node[d[0]],
target: node[d[1]]
});

const angle = node[d[1]][1] > node[d[0]][1] ? Math.PI / 2 : -Math.PI / 2,
radius = Math.abs((node[d[0]][1] - node[d[1]][1]) / 2);

return `M${[node[d[0]][0], node[d[0]][1]]}A${[
radius,
radius,
angle,
1,
1,
node[d[1]][0],
node[d[1]][1]
]}`;
}

const g = svg.append("g");

g.selectAll(".link")
.data(link)
.enter()
.each(function (d) {
d3.select(this)
.append("path")
.attr("class", "link")
.attr("id", (_, i) => `n${i}`)
.attr("d", (d) => d.link)
.attr("fill", "none")
.attr("stroke-width", 4)
.attr("stroke", bgColor)
.append("title")
.text(`link from #${d.from} to #${d.to}`);

d3.select(this)
.append("path")
.attr("class", "link")
.attr("id", (_, i) => `n${i}`)
.attr("d", (d) => d.link)
.attr("fill", "none")
.attr("stroke-width", 2)
.attr("stroke", "limegreen")
.append("title")
.text(`link from #${d.from} to #${d.to}`);
});

g.selectAll(".node")
.data(node)
.join("circle")
.attr("class", "node")
.attr("id", (_, i) => `n${i}`)
.attr("cx", (d) => d[0])
.attr("cy", (d) => d[1])
.attr("r", 8)
.attr("fill", "cyan")
.attr("stroke", "blue")
.append("title")
.text((_, i) => `node #${i}`);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
drawChart = () => {
d3.selectAll("#nodes_chart svg").remove();
generateData();

const svg = d3
.select("#nodes_chart")
.append("svg")
.attr("width", WIDTH)
.attr("height", HEIGHT)
.attr("shape-rendering", "geometricPrecision")
.style("background-color", "#FFFDE7")
.style("border", "2px solid #D7CCC8")
.style("border-radius", "8px");

addRoomsTo(svg);

const links = svg
.append("g")
.selectAll()
.data(dataLinks)
.join("path")
.attr("class", "link")
.attr("id", (_, i) => linkId(i))
.attr("from", (d) => `${d.from}`)
.attr("to", (d) => `${d.to}`)
.attr("d", (d) =>
linkType === "Horizontal"
? d3.linkHorizontal()(d.link)
: d3.linkVertical()(d.link)
)
.attr("fill", "none")
.attr("stroke", (d) => linkColor[d.master])
.on("mouseover", function () {
const link = d3.select(this).raise();
link.attr("stroke-width", strong.stroke);
let node = d3.select(link.attr("from")).raise();
node.attr("r", strong.r * UNIT);
node.attr("stroke-width", strong.stroke);
node = d3.select(link.attr("to")).raise();
node.attr("r", strong.r * UNIT);
node.attr("stroke-width", strong.stroke);
})
.on("mouseout", function (d) {
const link = d3.select(this);
d3.select(link.attr("from")).attr("stroke-width", 1).attr("r", UNIT);
link.attr("stroke-width", 1);
d3.select(link.attr("to")).attr("stroke-width", 1).attr("r", UNIT);
})
.append("title")
.text((d, i) => `${linkId(i)} :: from ${d.from} to ${d.to}`);

const nodes = svg
.append("g")
.selectAll()
.data(dataNodes)
.join("circle")
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
.attr("r", UNIT)
.attr("class", "node")
.attr("id", (d) => d.id)
.attr("fill", (_, i) => d3.schemePastel1[i % d3.schemePastel1.length])
.attr(
"stroke",
(_, i) => d3.schemeCategory10[i % d3.schemeCategory10.length]
)
.on("mouseover", function () {
const node = d3
.select(this)
.raise()
.attr("r", strong.r * UNIT)
.attr("stroke-width", strong.stroke);
d3.selectAll(`[from=${node.attr("id")}],[to=${node.attr("id")}]`).each(
function (d, i) {
const link = d3
.select(this)
.raise()
.attr("stroke-width", strong.stroke);
d3.select(
`#${
link.attr("from") === node.attr("id")
? link.attr("to")
: link.attr("from")
}`
)
.raise()
.attr("r", strong.r * UNIT)
.attr("stroke-width", strong.stroke);
}
);
})
.on("mouseout", function () {
const node = d3.select(this).attr("r", UNIT).attr("stroke-width", 1);
d3.selectAll(`[from=${node.attr("id")}],[to=${node.attr("id")}]`).each(
function (d, i) {
const link = d3.select(this).attr("stroke-width", 1);
d3.select(
`#${
link.attr("from") === node.attr("id")
? link.attr("to")
: link.attr("from")
}`
)
.attr("r", UNIT)
.attr("stroke-width", 1);
}
);
})
.append("title")
.text((d, i) => `${d.id} :: [${d.x}, ${d.y}]`);

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