feat: smaller subpaths in endpoint graph

This commit is contained in:
Patrick Balsiger
2025-10-31 15:52:13 +01:00
parent 16ef2059cd
commit 338dcc27ac

View File

@@ -24,8 +24,24 @@ function renderGraph(containerSelector, graph, options = {}) {
const nodes = graph.nodes.map(d => Object.assign({}, d));
const endpoints = Array.isArray(graph.endpoints) ? graph.endpoints : [];
// Identify leaf nodes (last segment of endpoints) vs intermediate nodes
const leafNodeIds = new Set();
endpoints.forEach(ep => {
const path = ep.path || '';
const segments = path.split('/').filter(Boolean);
if (segments.length > 0) {
const lastSegment = segments[segments.length - 1];
leafNodeIds.add(lastSegment);
}
});
// Assign radius based on whether node is a leaf or intermediate
nodes.forEach(node => {
node.isLeaf = leafNodeIds.has(node.id);
node.radius = node.isLeaf ? 22 : 14; // Bigger for endpoints, smaller for sub-paths
});
// Visual constants
const nodeRadius = 16;
const labelOffset = 12; // below the circle
// Compute left-to-right depth strictly from path segment indices
@@ -55,12 +71,12 @@ function renderGraph(containerSelector, graph, options = {}) {
.attr('class', 'node');
node.append('circle')
.attr('r', nodeRadius)
.attr('r', d => d.radius || 14)
.call(drag(simulation()));
node.append('text')
.attr('x', 0)
.attr('y', nodeRadius + labelOffset)
.attr('y', d => (d.radius || 14) + labelOffset)
.attr('text-anchor', 'middle')
.text(d => d.id);
@@ -120,11 +136,13 @@ function renderGraph(containerSelector, graph, options = {}) {
function ticked() {
// Clamp nodes vertically to avoid drifting too far down/up
const minY = paddingY + nodeRadius;
const maxY = height - paddingY - nodeRadius - labelOffset - 2;
// Use the maximum radius to ensure all nodes stay within bounds
const maxRadius = Math.max(...nodes.map(n => n.radius || 14));
const minY = paddingY + maxRadius;
const maxY = height - paddingY - maxRadius - labelOffset - 2;
// Push nodes away from links they don't belong to
const minDistFromLink = nodeRadius + 8; // minimum distance from unrelated links
const minDistFromLink = maxRadius + 8; // minimum distance from unrelated links
for (const n of nodes) {
const connectedLinks = nodeLinks.get(n.id) || new Set();
for (const l of links) {
@@ -175,7 +193,7 @@ function renderGraph(containerSelector, graph, options = {}) {
.velocityDecay(0.35)
.force('link', d3.forceLink().id(d => d.id).distance(40).strength(1.1).iterations(2))
.force('charge', d3.forceManyBody().strength(-420))
.force('collision', d3.forceCollide().radius(nodeRadius + 50).strength(0.96))
.force('collision', d3.forceCollide().radius(d => (d.radius || 14) + 50).strength(0.96))
.force('x', fx)
.force('y', fy);
}