|
|
|
@ -208,7 +208,9 @@ |
|
|
|
svgGroup.attr("transform", event.transform); |
|
|
|
svgGroup.attr("transform", event.transform); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize with identity transform |
|
|
|
svgElement.call(zoomBehavior); |
|
|
|
svgElement.call(zoomBehavior); |
|
|
|
|
|
|
|
svgElement.call(zoomBehavior.transform, d3.zoomIdentity); |
|
|
|
|
|
|
|
|
|
|
|
// Set up arrow marker for links |
|
|
|
// Set up arrow marker for links |
|
|
|
const defs = svgElement.append("defs"); |
|
|
|
const defs = svgElement.append("defs"); |
|
|
|
@ -505,9 +507,10 @@ |
|
|
|
|
|
|
|
|
|
|
|
// Center the nodes when the simulation is done |
|
|
|
// Center the nodes when the simulation is done |
|
|
|
newSimulation.on("end", () => { |
|
|
|
newSimulation.on("end", () => { |
|
|
|
if (!starVisualization) { |
|
|
|
// Add a small delay to ensure the simulation has fully settled |
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
centerGraph(); |
|
|
|
centerGraph(); |
|
|
|
} |
|
|
|
}, 100); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Create drag handler |
|
|
|
// Create drag handler |
|
|
|
@ -1183,17 +1186,59 @@ |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function centerGraph() { |
|
|
|
function centerGraph() { |
|
|
|
if (svg && svgGroup && zoomBehavior) { |
|
|
|
if (svg && svgGroup && zoomBehavior) { |
|
|
|
const svgWidth = svg.clientWidth || width; |
|
|
|
debug("Centering graph", { width, height }); |
|
|
|
const svgHeight = svg.clientHeight || height; |
|
|
|
|
|
|
|
|
|
|
|
// Get all nodes to calculate bounds |
|
|
|
|
|
|
|
const nodes = svgGroup.selectAll('.node').data(); |
|
|
|
|
|
|
|
if (nodes.length === 0) { |
|
|
|
|
|
|
|
debug("No nodes found for centering"); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate bounds of all nodes |
|
|
|
|
|
|
|
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; |
|
|
|
|
|
|
|
nodes.forEach((node: NetworkNode) => { |
|
|
|
|
|
|
|
if (node.x != null && node.y != null) { |
|
|
|
|
|
|
|
minX = Math.min(minX, node.x); |
|
|
|
|
|
|
|
maxX = Math.max(maxX, node.x); |
|
|
|
|
|
|
|
minY = Math.min(minY, node.y); |
|
|
|
|
|
|
|
maxY = Math.max(maxY, node.y); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate the center of the graph content |
|
|
|
|
|
|
|
const graphCenterX = (minX + maxX) / 2; |
|
|
|
|
|
|
|
const graphCenterY = (minY + maxY) / 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate the viewBox center |
|
|
|
|
|
|
|
const viewBoxCenterX = width / 2; |
|
|
|
|
|
|
|
const viewBoxCenterY = height / 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate the translation needed to center the graph |
|
|
|
|
|
|
|
const translateX = viewBoxCenterX - graphCenterX; |
|
|
|
|
|
|
|
const translateY = viewBoxCenterY - graphCenterY; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
debug("Centering graph", { |
|
|
|
|
|
|
|
graphBounds: { minX, maxX, minY, maxY }, |
|
|
|
|
|
|
|
graphCenter: { graphCenterX, graphCenterY }, |
|
|
|
|
|
|
|
viewBoxCenter: { viewBoxCenterX, viewBoxCenterY }, |
|
|
|
|
|
|
|
translation: { translateX, translateY } |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Reset zoom and center |
|
|
|
// Apply the centering transform |
|
|
|
d3.select(svg) |
|
|
|
d3.select(svg) |
|
|
|
.transition() |
|
|
|
.transition() |
|
|
|
.duration(750) |
|
|
|
.duration(750) |
|
|
|
.call( |
|
|
|
.call( |
|
|
|
zoomBehavior.transform, |
|
|
|
zoomBehavior.transform, |
|
|
|
d3.zoomIdentity.translate(svgWidth / 2, svgHeight / 2).scale(0.8), |
|
|
|
d3.zoomIdentity.translate(translateX, translateY).scale(0.8), |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
debug("Cannot center graph - missing required elements", { |
|
|
|
|
|
|
|
hasSvg: !!svg, |
|
|
|
|
|
|
|
hasSvgGroup: !!svgGroup, |
|
|
|
|
|
|
|
hasZoomBehavior: !!zoomBehavior |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -1348,7 +1393,10 @@ |
|
|
|
outline |
|
|
|
outline |
|
|
|
size="lg" |
|
|
|
size="lg" |
|
|
|
class="network-control-button btn-leather rounded-lg p-2" |
|
|
|
class="network-control-button btn-leather rounded-lg p-2" |
|
|
|
onclick={centerGraph} |
|
|
|
onclick={() => { |
|
|
|
|
|
|
|
debug("Center button clicked"); |
|
|
|
|
|
|
|
centerGraph(); |
|
|
|
|
|
|
|
}} |
|
|
|
aria-label="Center graph" |
|
|
|
aria-label="Center graph" |
|
|
|
> |
|
|
|
> |
|
|
|
<svg |
|
|
|
<svg |
|
|
|
|