feat: BFS-based left-to-right tree layout for scene graph
This commit is contained in:
81
editor/composables/useLayout.ts
Normal file
81
editor/composables/useLayout.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
interface NodeInfo {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
interface EdgeInfo {
|
||||
source: string
|
||||
target: string
|
||||
}
|
||||
|
||||
const H_GAP = 300
|
||||
const V_GAP = 140
|
||||
const PAD = 60
|
||||
|
||||
export function computePositions(
|
||||
nodes: NodeInfo[],
|
||||
edges: EdgeInfo[],
|
||||
startScene: string,
|
||||
): Map<string, { x: number; y: number }> {
|
||||
const positions = new Map<string, { x: number; y: number }>()
|
||||
|
||||
const adj = new Map<string, string[]>()
|
||||
for (const n of nodes) adj.set(n.id, [])
|
||||
for (const e of edges) {
|
||||
const list = adj.get(e.source)
|
||||
if (list) list.push(e.target)
|
||||
}
|
||||
|
||||
const level = new Map<string, number>()
|
||||
const visited = new Set<string>()
|
||||
const queue: string[] = []
|
||||
|
||||
if (startScene && adj.has(startScene)) {
|
||||
level.set(startScene, 0)
|
||||
visited.add(startScene)
|
||||
queue.push(startScene)
|
||||
}
|
||||
|
||||
while (queue.length > 0) {
|
||||
const id = queue.shift()!
|
||||
const cur = level.get(id)!
|
||||
for (const t of adj.get(id)!) {
|
||||
if (!visited.has(t)) {
|
||||
visited.add(t)
|
||||
level.set(t, cur + 1)
|
||||
queue.push(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let maxLevel = -1
|
||||
for (const l of level.values()) maxLevel = Math.max(maxLevel, l)
|
||||
|
||||
for (const n of nodes) {
|
||||
if (!level.has(n.id)) {
|
||||
maxLevel++
|
||||
level.set(n.id, maxLevel)
|
||||
}
|
||||
}
|
||||
|
||||
const byLevel = new Map<number, string[]>()
|
||||
for (const [id, lv] of level) {
|
||||
const arr = byLevel.get(lv) || []
|
||||
arr.push(id)
|
||||
byLevel.set(lv, arr)
|
||||
}
|
||||
|
||||
const levels = [...byLevel.entries()].sort((a, b) => a[0] - b[0])
|
||||
|
||||
for (const [lv, ids] of levels) {
|
||||
ids.sort()
|
||||
const count = ids.length
|
||||
for (let i = 0; i < count; i++) {
|
||||
const x = lv * H_GAP + PAD
|
||||
const y = i * V_GAP + PAD
|
||||
positions.set(ids[i], { x, y })
|
||||
}
|
||||
}
|
||||
|
||||
return positions
|
||||
}
|
||||
Reference in New Issue
Block a user