Public
Edited
Sep 5, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
// Dimensions and color scale setup.
const width = 928;
const height = 600;
const color = d3.scaleOrdinal(d3.schemeCategory10);

// Create a simulation.
const simulation = d3
.forceSimulation(nodes)
.force(
"link",
d3.forceLink(links).id((d) => d.id)
)
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", draw);

// Create and configure the canvas.
const dpi = devicePixelRatio;
const canvas = d3
.create("canvas")
.attr("width", dpi * width)
.attr("height", dpi * height)
.attr("style", `width: ${width}px; max-width: 100%; height: auto;`)
.node();

const context = canvas.getContext("2d");
context.scale(dpi, dpi);

function draw() {
context.clearRect(0, 0, width, height);
// Draw links
context.save();
context.globalAlpha = 1.0;
context.strokeStyle = "#000000";
context.beginPath();
links.forEach(drawLink);
context.stroke();
context.restore();

// Draw nodes - only draw nodes that have links
const activeNodes = new Set(
links.flatMap((d) => [d.source.id, d.target.id])
);
context.save();
context.strokeStyle = "#fff";
context.globalAlpha = 1;
nodes.forEach((node) => {
if (activeNodes.has(node.id)) {
context.beginPath();
drawNode(node);
context.fillStyle = color(node.group);
context.fill();
context.stroke();
}
});
context.restore();
}

function drawLink(d) {
context.globalAlpha = d.confidence;
context.beginPath(); // Start a new path for each link
if (d.confidence < 1) {
context.setLineDash([5, 5]); // Set the line to be dashed
} else {
context.setLineDash([]); // Set the line to be solid
}
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke(); // Apply the stroke to this specific link
}

function drawNode(d) {
context.moveTo(d.x + 5, d.y);
context.arc(d.x, d.y, 5, 0, 2 * Math.PI);
}

// Drag behavior setup
d3.select(canvas).call(
d3
.drag()
.subject((event) => {
const [px, py] = d3.pointer(event, canvas);
return d3.least(nodes, ({ x, y }) => {
const dist2 = (x - px) ** 2 + (y - py) ** 2;
if (dist2 < 400) return dist2;
});
})
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);

// Drag event handlers
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}

function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}

function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}

// Handle cell reevaluation
invalidation.then(() => simulation.stop());

return canvas;
}
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