fix: enhance recomputeInsideNodes to support nested group processing with visited set

This commit is contained in:
Rizumu Ayaka
2026-01-24 15:15:32 +08:00
parent bef751a591
commit 4e34d2a93f
2 changed files with 78 additions and 4 deletions

View File

@@ -1,6 +1,6 @@
import { describe, expect } from 'vitest'
import { LGraphGroup } from '@/lib/litegraph/src/litegraph'
import { LGraph, LGraphGroup } from '@/lib/litegraph/src/litegraph'
import { test } from './__fixtures__/testExtensions'
@@ -9,4 +9,72 @@ describe('LGraphGroup', () => {
const link = new LGraphGroup('title', 929)
expect(link.serialize()).toMatchSnapshot('Basic')
})
describe('recomputeInsideNodes', () => {
test('uses visited set to avoid redundant computation', () => {
const graph = new LGraph()
// Create 4 nested groups: outer -> mid1 -> mid2 -> inner
const outer = new LGraphGroup('outer')
outer.pos = [0, 0]
outer.size = [400, 400]
graph.add(outer)
const mid1 = new LGraphGroup('mid1')
mid1.pos = [10, 10]
mid1.size = [300, 300]
graph.add(mid1)
const mid2 = new LGraphGroup('mid2')
mid2.pos = [20, 20]
mid2.size = [200, 200]
graph.add(mid2)
const inner = new LGraphGroup('inner')
inner.pos = [30, 30]
inner.size = [100, 100]
graph.add(inner)
// Track the visited set to verify each group is only fully processed once
const visited = new Set<number>()
outer.recomputeInsideNodes(100, visited)
// All nested groups should be in the visited set
expect(visited.has(outer.id)).toBe(true)
expect(visited.has(mid1.id)).toBe(true)
expect(visited.has(mid2.id)).toBe(true)
expect(visited.has(inner.id)).toBe(true)
expect(visited.size).toBe(4)
// Verify children relationships are correct
expect(outer.children.has(mid1)).toBe(true)
expect(outer.children.has(mid2)).toBe(true)
expect(outer.children.has(inner)).toBe(true)
expect(mid1.children.has(mid2)).toBe(true)
expect(mid1.children.has(inner)).toBe(true)
expect(mid2.children.has(inner)).toBe(true)
})
test('respects maxDepth limit', () => {
const graph = new LGraph()
const outer = new LGraphGroup('outer')
outer.pos = [0, 0]
outer.size = [300, 300]
graph.add(outer)
const inner = new LGraphGroup('inner')
inner.pos = [10, 10]
inner.size = [100, 100]
graph.add(inner)
// With maxDepth=1, inner group is added as child but not processed
outer.recomputeInsideNodes(1)
// outer should have inner as a child
expect(outer.children.has(inner)).toBe(true)
// inner should not have computed its own children (it was never processed)
expect(inner.children.size).toBe(0)
})
})
})

View File

@@ -245,10 +245,16 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
* Recomputes which items (nodes, reroutes, nested groups) are inside this group.
* Recursively processes nested groups to ensure their children are also computed.
* @param maxDepth Maximum recursion depth for nested groups. Use 1 to skip nested group computation.
* @param visited Set of already visited group IDs to prevent redundant computation.
*/
recomputeInsideNodes(maxDepth: number = 100): void {
recomputeInsideNodes(
maxDepth: number = 100,
visited: Set<number> = new Set()
): void {
if (!this.graph) throw new NullGraphError()
if (maxDepth <= 0) return
if (maxDepth <= 0 || visited.has(this.id)) return
visited.add(this.id)
const { nodes, reroutes, groups } = this.graph
const children = this._children
@@ -277,7 +283,7 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
}
}
for (const group of containedGroups)
group.recomputeInsideNodes(maxDepth - 1)
group.recomputeInsideNodes(maxDepth - 1, visited)
groups.sort((a, b) => {
if (a === this) {