feat: scaffold mindmap core and layout engine
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user