import { LayoutEngine, LayoutNode, LayoutOptions, LayoutResult, MindmapNode, MindmapTree, } from '@/lib/mindmap/types'; interface PositionedNode { node: MindmapNode; depth: number; index: number; } export class LogicalStructureLayout implements LayoutEngine { readonly name = 'logical' as const; compute(tree: MindmapTree, options: LayoutOptions = {}): LayoutResult { const horizontalSpacing = options.horizontalSpacing ?? 280; const verticalSpacing = options.verticalSpacing ?? 96; const positioned: PositionedNode[] = []; walk(tree, 0, positioned); const nodes: LayoutNode[] = positioned.map((item) => ({ id: item.node.id, parentId: item.node.parentId, depth: item.depth, data: item.node.data, position: { x: item.depth * horizontalSpacing, y: item.index * verticalSpacing, }, })); const edges = nodes .filter((n) => n.parentId) .map((n) => ({ id: `${n.parentId}-${n.id}`, source: n.parentId!, target: n.id, })); const bounds = { width: Math.max(...nodes.map((n) => n.position.x), 0) + horizontalSpacing, height: Math.max(...nodes.map((n) => n.position.y), 0) + verticalSpacing, }; return { nodes, edges, bounds }; } } function walk(node: MindmapNode, depth: number, acc: PositionedNode[], nextIndex = { value: 0 }) { acc.push({ node, depth, index: nextIndex.value++ }); node.children?.forEach((child) => { child.parentId = node.id; walk(child, depth + 1, acc, nextIndex); }); }