63 lines
1.5 KiB
TypeScript
63 lines
1.5 KiB
TypeScript
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);
|
|
});
|
|
}
|
|
|