diff --git a/index.js b/index.js index 88043ba..3715099 100644 --- a/index.js +++ b/index.js @@ -9812,7 +9812,6 @@ function queueGraphPersistToIndexedDb( ) { const normalizedChatId = normalizeChatIdCandidate(chatId); if (!normalizedChatId || !graph) return; - const graphSnapshot = cloneGraphForPersistence(graph, normalizedChatId); const normalizedRevision = normalizeIndexedDbRevision(revision); const latestQueuedRevision = normalizeIndexedDbRevision( @@ -9843,6 +9842,7 @@ function queueGraphPersistToIndexedDb( revision: normalizedRevision, }; } + const graphSnapshot = cloneGraphForPersistence(graph, normalizedChatId); return await saveGraphToIndexedDb(normalizedChatId, graphSnapshot, { revision: normalizedRevision, reason, diff --git a/ui/graph-renderer.js b/ui/graph-renderer.js index abdfee3..005b5a6 100644 --- a/ui/graph-renderer.js +++ b/ui/graph-renderer.js @@ -44,6 +44,17 @@ const DEFAULT_LAYOUT_CONFIG = { neuralMinGap: 12, }; +const ADAPTIVE_NEURAL_LAYOUT_POLICY = Object.freeze({ + reduceIterationsNodes: 220, + reduceIterationsEdges: 1200, + reduceIterationsCap: 56, + strongReduceNodes: 360, + strongReduceEdges: 2200, + strongReduceCap: 24, + skipSimulationNodes: 520, + skipSimulationEdges: 3600, +}); + const MIN_USABLE_CANVAS_DIMENSION = 48; /** 兼容旧版 forceConfig(召回卡片等) */ @@ -296,7 +307,10 @@ export class GraphRenderer { const parts = partitionNodesByScope(this.nodes, this._userPovAliasSet); this._regionPanels = this._computeRegionPanels(W, H, parts); this._layoutAllPartitions(parts); - this._simulateNeuralWithinRegions(this.config.neuralIterations); + const neuralPlan = this._resolveNeuralSimulationPlan(); + if (!neuralPlan.skip && neuralPlan.iterations > 0) { + this._simulateNeuralWithinRegions(neuralPlan.iterations); + } if (prevSelectedId) { this.selectedNode = this.nodeMap.get(prevSelectedId) || null; @@ -575,6 +589,47 @@ export class GraphRenderer { return ideal; } + _resolveNeuralSimulationPlan() { + const nodeCount = Array.isArray(this.nodes) ? this.nodes.length : 0; + const edgeCount = Array.isArray(this.edges) ? this.edges.length : 0; + const baseIterations = Math.max( + 8, + Math.min(220, Number(this.config.neuralIterations) || 80), + ); + + let iterations = baseIterations; + let skip = false; + + if ( + nodeCount >= ADAPTIVE_NEURAL_LAYOUT_POLICY.skipSimulationNodes || + edgeCount >= ADAPTIVE_NEURAL_LAYOUT_POLICY.skipSimulationEdges + ) { + skip = true; + iterations = 0; + } else if ( + nodeCount >= ADAPTIVE_NEURAL_LAYOUT_POLICY.strongReduceNodes || + edgeCount >= ADAPTIVE_NEURAL_LAYOUT_POLICY.strongReduceEdges + ) { + iterations = Math.min( + iterations, + ADAPTIVE_NEURAL_LAYOUT_POLICY.strongReduceCap, + ); + } else if ( + nodeCount >= ADAPTIVE_NEURAL_LAYOUT_POLICY.reduceIterationsNodes || + edgeCount >= ADAPTIVE_NEURAL_LAYOUT_POLICY.reduceIterationsEdges + ) { + iterations = Math.min( + iterations, + ADAPTIVE_NEURAL_LAYOUT_POLICY.reduceIterationsCap, + ); + } + + return { + skip, + iterations, + }; + } + /** * 分区内一次性力导向:斥力 + 同区边弹簧 + 弱向心,稳定后停止(无帧循环) */