Optimize node degree and size calculation for expanded graph nodes

This commit is contained in:
yangdx
2025-03-15 20:11:03 +08:00
parent 6555d5ac08
commit b9dd53b1e3

View File

@@ -407,10 +407,21 @@ const useLightrangeGraph = () => {
// Get existing node IDs // Get existing node IDs
const existingNodeIds = new Set(sigmaGraph.nodes()); const existingNodeIds = new Set(sigmaGraph.nodes());
// STEP 1: Identify nodes and edges to keep // Identify nodes and edges to keep
const nodesToAdd = new Set<string>(); const nodesToAdd = new Set<string>();
const edgesToAdd = new Set<string>(); const edgesToAdd = new Set<string>();
const nodesWithDiscardedEdges = new Set<string>();
// Get degree range from existing graph for size calculations
const minDegree = 1;
let maxDegree = 0;
sigmaGraph.forEachNode(node => {
const degree = sigmaGraph.degree(node);
maxDegree = Math.max(maxDegree, degree);
});
// Calculate size formula parameters
const range = maxDegree - minDegree || 1; // Avoid division by zero
const scale = Constants.maxNodeSize - Constants.minNodeSize;
// First identify connectable nodes (nodes connected to the expanded node) // First identify connectable nodes (nodes connected to the expanded node)
for (const node of processedNodes) { for (const node of processedNodes) {
@@ -430,69 +441,59 @@ const useLightrangeGraph = () => {
} }
} }
// If no new connectable nodes found, show toast and return // Calculate node degrees and track discarded edges in one pass
if (nodesToAdd.size === 0) { const nodeDegrees = new Map<string, number>();
toast.info(t('graphPanel.propertiesView.node.noNewNodes')); const nodesWithDiscardedEdges = new Set<string>();
return;
}
// Then identify valid edges (edges where both nodes exist in the graph)
for (const edge of processedEdges) { for (const edge of processedEdges) {
const sourceExists = existingNodeIds.has(edge.source) || nodesToAdd.has(edge.source); const sourceExists = existingNodeIds.has(edge.source) || nodesToAdd.has(edge.source);
const targetExists = existingNodeIds.has(edge.target) || nodesToAdd.has(edge.target); const targetExists = existingNodeIds.has(edge.target) || nodesToAdd.has(edge.target);
if (sourceExists && targetExists) { if (sourceExists && targetExists) {
edgesToAdd.add(edge.id); edgesToAdd.add(edge.id);
} else { // Add degrees for valid edges
// Mark nodes that had edges discarded
if (nodesToAdd.has(edge.source)) { if (nodesToAdd.has(edge.source)) {
nodesWithDiscardedEdges.add(edge.source); nodeDegrees.set(edge.source, (nodeDegrees.get(edge.source) || 0) + 1);
} }
if (nodesToAdd.has(edge.target)) { if (nodesToAdd.has(edge.target)) {
nodesWithDiscardedEdges.add(edge.target);
}
}
}
// STEP 2: Calculate node degrees and sizes
const nodeDegrees = new Map<string, number>();
// Calculate degrees from kept edges
for (const edgeId of edgesToAdd) {
const edge = processedEdges.find(e => e.id === edgeId)!;
nodeDegrees.set(edge.source, (nodeDegrees.get(edge.source) || 0) + 1);
nodeDegrees.set(edge.target, (nodeDegrees.get(edge.target) || 0) + 1); nodeDegrees.set(edge.target, (nodeDegrees.get(edge.target) || 0) + 1);
} }
} else {
// Add +1 to degree for nodes that had edges discarded // Track discarded edges for both new and existing nodes
for (const nodeId of nodesWithDiscardedEdges) { if (sigmaGraph.hasNode(edge.source)) {
nodeDegrees.set(nodeId, (nodeDegrees.get(nodeId) || 0) + 1); nodesWithDiscardedEdges.add(edge.source);
} else if (nodesToAdd.has(edge.source)) {
nodesWithDiscardedEdges.add(edge.source);
nodeDegrees.set(edge.source, (nodeDegrees.get(edge.source) || 0) + 1); // +1 for discarded edge
}
if (sigmaGraph.hasNode(edge.target)) {
nodesWithDiscardedEdges.add(edge.target);
} else if (nodesToAdd.has(edge.target)) {
nodesWithDiscardedEdges.add(edge.target);
nodeDegrees.set(edge.target, (nodeDegrees.get(edge.target) || 0) + 1); // +1 for discarded edge
}
}
} }
// Get degree range from existing graph for size calculations // If no new connectable nodes found, show toast and return
const minDegree = 1; if (nodesToAdd.size === 0) {
let maxDegree = 0; toast.info(t('graphPanel.propertiesView.node.noNewNodes'));
sigmaGraph.forEachNode(node => { return;
const degree = sigmaGraph.degree(node); }
maxDegree = Math.max(maxDegree, degree);
});
// Update maxDegree with new node degrees // Update maxDegree with new node degrees
for (const [, degree] of nodeDegrees.entries()) { for (const [, degree] of nodeDegrees.entries()) {
maxDegree = Math.max(maxDegree, degree); maxDegree = Math.max(maxDegree, degree);
} }
// Calculate size formula parameters // SAdd nodes and edges to the graph
const range = maxDegree - minDegree || 1; // Avoid division by zero
const scale = Constants.maxNodeSize - Constants.minNodeSize;
// STEP 3: Add nodes and edges to the graph
// Calculate camera ratio and spread factor once before the loop // Calculate camera ratio and spread factor once before the loop
const cameraRatio = useGraphStore.getState().sigmaInstance?.getCamera().ratio || 1; const cameraRatio = useGraphStore.getState().sigmaInstance?.getCamera().ratio || 1;
const spreadFactor = Math.max( const spreadFactor = Math.max(
Math.sqrt(nodeToExpand.size) * 4, // Base on node size Math.sqrt(nodeToExpand.size) * 4, // Base on node size
Math.sqrt(nodesToAdd.size) * 3 // Scale with number of nodes Math.sqrt(nodesToAdd.size) * 3 // Scale with number of nodes
) / cameraRatio; // Adjust for zoom level ) / cameraRatio; // Adjust for zoom level
seedrandom(Date.now().toString(), { global: true });
const randomAngle = Math.random() * 2 * Math.PI const randomAngle = Math.random() * 2 * Math.PI
console.log('nodeSize:', nodeToExpand.size, 'nodesToAdd:', nodesToAdd.size); console.log('nodeSize:', nodeToExpand.size, 'nodesToAdd:', nodesToAdd.size);
@@ -575,29 +576,25 @@ const useLightrangeGraph = () => {
searchCache.graph = null; searchCache.graph = null;
searchCache.searchEngine = null; searchCache.searchEngine = null;
// STEP 4: Update the expanded node's size // Update sizes for all nodes with discarded edges
for (const nodeId of nodesWithDiscardedEdges) {
if (sigmaGraph.hasNode(nodeId)) { if (sigmaGraph.hasNode(nodeId)) {
// Get the new degree of the expanded node // Get the new degree of the node
let expandedNodeDegree = sigmaGraph.degree(nodeId); let newDegree = sigmaGraph.degree(nodeId);
newDegree += 1; // Add +1 for discarded edges
// Check if the expanded node had any discarded edges // Calculate new size for the node
if (nodesWithDiscardedEdges.has(nodeId)) {
expandedNodeDegree += 1; // Add +1 for discarded edges
}
// Calculate new size for the expanded node
const newSize = Math.round( const newSize = Math.round(
Constants.minNodeSize + scale * Math.pow((expandedNodeDegree - minDegree) / range, 0.5) Constants.minNodeSize + scale * Math.pow((newDegree - minDegree) / range, 0.5)
); );
// Update the size in sigma graph // Get current size
sigmaGraph.setNodeAttribute(nodeId, 'size', newSize); const currentSize = sigmaGraph.getNodeAttribute(nodeId, 'size');
// Update the size in raw graph // Only update if new size is larger
const expandedNodeIndex = rawGraph.nodeIdMap[nodeId]; if (newSize > currentSize) {
if (expandedNodeIndex !== undefined) { sigmaGraph.setNodeAttribute(nodeId, 'size', newSize);
rawGraph.nodes[expandedNodeIndex].size = newSize; }
rawGraph.nodes[expandedNodeIndex].degree = expandedNodeDegree;
} }
} }