Files
erp/apps/web/src/pages/graph/graphLayout.ts
iven 0a57cd7030 refactor(web): 拆分 PluginGraphPage 为 graph 子模块 — 每个文件 < 800 行
- graphTypes.ts (39 行) — GraphNode/GraphEdge/GraphConfig/NodePosition/HoverState
- graphLayout.ts (41 行) — computeCircularLayout 环形布局算法
- graphRenderer.ts (293 行) — Canvas 绘制函数 + 常量 + helper
- PluginGraphPage.tsx (758 行) — 组件壳:state/effects/event handlers/JSX
2026-04-17 12:51:32 +08:00

42 lines
1.0 KiB
TypeScript

/**
* 关系图谱 — 布局算法
*
* 纯函数模块,不依赖 React。
*/
import type { GraphNode, NodePosition } from './graphTypes';
/**
* 计算环形布局位置。
*
* 节点均匀分布在以 (centerX, centerY) 为圆心、radius 为半径的圆周上。
* 单个节点退化为圆心;两个及以上节点按角度排列,起始角在正上方 (-PI/2)。
*/
export function computeCircularLayout(
nodes: GraphNode[],
centerX: number,
centerY: number,
radius: number,
): Map<string, NodePosition> {
const positions = new Map<string, NodePosition>();
const count = nodes.length;
if (count === 0) return positions;
if (count === 1) {
positions.set(nodes[0].id, { x: centerX, y: centerY, vx: 0, vy: 0 });
return positions;
}
nodes.forEach((node, i) => {
const angle = (2 * Math.PI * i) / count - Math.PI / 2;
positions.set(node.id, {
x: centerX + radius * Math.cos(angle),
y: centerY + radius * Math.sin(angle),
vx: 0,
vy: 0,
});
});
return positions;
}