@logicflow/layout
Version:
LogicFlow layout
381 lines • 15.7 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
import dagre, { graphlib } from 'dagre';
/**
* Dagre布局类 - LogicFlow自动布局插件
* 基于dagre.js提供图的自动布局能力
*/
var Dagre = /** @class */ (function () {
function Dagre() {
}
/**
* 插件初始化方法,由LogicFlow自动调用
* @param lf - LogicFlow实例
*/
Dagre.prototype.render = function (lf) {
this.lf = lf;
};
/**
* 执行布局算法,重新排列图中的节点和边
* @param option - 布局配置选项
*/
Dagre.prototype.layout = function (option) {
var _this = this;
if (option === void 0) { option = {}; }
var _a = this.lf.graphModel, nodes = _a.nodes, edges = _a.edges, gridSize = _a.gridSize;
// 根据网格大小调整节点间距
var nodesep = 100;
var ranksep = 150;
if (gridSize > 20) {
nodesep = gridSize * 2;
ranksep = gridSize * 2;
}
// 合并默认配置和用户配置
this.option = __assign({
// 默认从左到右布局
rankdir: 'LR',
// 默认右下角对齐
align: 'UL',
// 紧凑树形排名算法
ranker: 'tight-tree',
// 层级间距
ranksep: ranksep,
// 同层节点间距
nodesep: nodesep,
// 图的水平边距
marginx: 120,
// 图的垂直边距
marginy: 120 }, option);
// 创建dagre图实例
var g = new graphlib.Graph();
g.setGraph(this.option);
g.setDefaultEdgeLabel(function () { return ({}); });
// 将LogicFlow节点添加到dagre图中
nodes.forEach(function (node) {
g.setNode(node.id, {
width: node.width || 150,
height: node.height || 50,
id: node.id,
});
});
// 将LogicFlow边添加到dagre图中
edges.forEach(function (edge) {
g.setEdge(edge.sourceNodeId, edge.targetNodeId, {
id: edge.id,
});
});
// 执行dagre布局算法
dagre.layout(g);
// 存储新的节点和边数据
var newNodes = [];
var newEdges = [];
// 更新节点位置
nodes.forEach(function (node) {
var _a;
var _b = g.node(node.id), x = _b.x, y = _b.y;
var lfNode = node.getData();
if (!lfNode) {
throw new Error("\u5E03\u5C40\u9519\u8BEF\uFF1A\u627E\u4E0D\u5230ID\u4E3A ".concat(node.id, " \u7684\u8282\u70B9"));
}
// 更新节点坐标
lfNode.x = x;
lfNode.y = y;
// 更新节点文本位置
if ((_a = lfNode === null || lfNode === void 0 ? void 0 : lfNode.text) === null || _a === void 0 ? void 0 : _a.x) {
lfNode.text.x = x;
lfNode.text.y = y;
}
newNodes.push(lfNode);
});
// 处理边的路径和锚点
edges.forEach(function (edge) {
var lfEdge = edge.getData();
if (!lfEdge) {
return;
}
if (!option.isDefaultAnchor) {
// 自定义锚点,不调整边的关联锚点,只清除路径相关数据,让LogicFlow自动计算
delete lfEdge.pointsList;
delete lfEdge.startPoint;
delete lfEdge.endPoint;
}
else {
// 默认锚点,重新计算路径以及边的起点和终点(节点默认锚点为上下左右)
delete lfEdge.pointsList;
delete lfEdge.startPoint;
delete lfEdge.endPoint;
delete lfEdge.sourceAnchorId;
delete lfEdge.targetAnchorId;
var model = _this.lf.getEdgeModelById(edge.id);
if (model) {
// 计算自定义折线路径
lfEdge.pointsList = _this.calcPointsList(model, newNodes);
}
if (lfEdge.pointsList) {
// 设置边的起点和终点
var first = lfEdge.pointsList[0];
var last = lfEdge.pointsList[lfEdge.pointsList.length - 1];
lfEdge.startPoint = { x: first.x, y: first.y };
lfEdge.endPoint = { x: last.x, y: last.y };
// 调整边标签位置
if (lfEdge.text && lfEdge.text.value) {
lfEdge.text = {
x: last.x - _this.getBytesLength(lfEdge.text.value) * 6 - 10,
y: last.y,
value: lfEdge.text.value,
};
}
}
else if (lfEdge.text && lfEdge.text.value) {
// 没有自定义路径时保留文本内容
lfEdge.text = lfEdge.text.value;
}
}
newEdges.push(lfEdge);
});
// 将计算好的布局数据应用到画布
this.lf.renderRawData({
nodes: newNodes,
edges: newEdges,
});
};
/**
* 计算字符串显示宽度(用于文本定位)
* @param word - 要计算的文本
* @returns 估算的文本像素宽度
*/
Dagre.prototype.getBytesLength = function (word) {
if (!word) {
return 0;
}
var totalLength = 0;
for (var i = 0; i < word.length; i++) {
var c = word.charCodeAt(i);
// 大写字母宽度加权
if (word.match(/[A-Z]/)) {
totalLength += 1.5;
}
// ASCII字符和半角字符
else if ((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) {
totalLength += 1;
}
// 其他字符(如中文)
else {
totalLength += 2;
}
}
return totalLength;
};
/**
* 优化折线路径点,移除冗余点
* @param points - 原始路径点数组
* @returns 优化后的路径点数组
*/
Dagre.prototype.pointFilter = function (points) {
var allPoints = __spreadArray([], points, true); // 创建副本避免修改原始数据
var i = 1;
// 删除直线上的中间点(保持路径简洁)
while (i < allPoints.length - 1) {
var pre = allPoints[i - 1];
var current = allPoints[i];
var next = allPoints[i + 1];
// 如果三点共线,移除中间点
if ((pre.x === current.x && current.x === next.x) || // 垂直线上的点
(pre.y === current.y && current.y === next.y)) {
// 水平线上的点
allPoints.splice(i, 1);
}
else {
i++;
}
}
return allPoints;
};
/**
* 计算边的折线路径点
* @param model - 边模型
* @param nodes - 节点数据数组
* @returns 计算后的路径点数组,如果无法计算则返回undefined
*/
Dagre.prototype.calcPointsList = function (model, nodes) {
var pointsList = [];
// 获取源节点和目标节点的模型与布局数据
var sourceNodeModel = this.lf.getNodeModelById(model.sourceNodeId);
var targetNodeModel = this.lf.getNodeModelById(model.targetNodeId);
var newSourceNodeData = nodes.find(function (node) { return node.id === model.sourceNodeId; });
var newTargetNodeData = nodes.find(function (node) { return node.id === model.targetNodeId; });
// 数据验证
if (!sourceNodeModel ||
!targetNodeModel ||
!newSourceNodeData ||
!newTargetNodeData) {
return undefined;
}
// 折线偏移量(用于创建合适的转折点)
var offset = Number(model.offset) || 50;
// 处理从左到右(LR)布局的边路径
if (this.option.rankdir === 'LR' && model.modelType === 'polyline-edge') {
// 正向连线:源节点在目标节点左侧
if (newSourceNodeData.x < newTargetNodeData.x) {
// 从源节点右侧中心出发
pointsList.push({
x: newSourceNodeData.x + sourceNodeModel.width / 2,
y: newSourceNodeData.y,
});
// 向右延伸一段距离
pointsList.push({
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
y: newSourceNodeData.y,
});
// 垂直移动到目标节点的高度
pointsList.push({
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
y: newTargetNodeData.y,
});
// 连接到目标节点左侧中心
pointsList.push({
x: newTargetNodeData.x - targetNodeModel.width / 2,
y: newTargetNodeData.y,
});
return this.pointFilter(pointsList);
}
// 反向连线:源节点在目标节点右侧
if (newSourceNodeData.x > newTargetNodeData.x) {
// 根据节点相对Y轴位置选择不同路径
if (newSourceNodeData.y >= newTargetNodeData.y) {
// 源节点在目标节点的右下方,从源节点上方出发
pointsList.push({
x: newSourceNodeData.x,
y: newSourceNodeData.y + sourceNodeModel.height / 2,
});
pointsList.push({
x: newSourceNodeData.x,
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
});
pointsList.push({
x: newTargetNodeData.x,
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
});
pointsList.push({
x: newTargetNodeData.x,
y: newTargetNodeData.y + targetNodeModel.height / 2,
});
}
else {
// 源节点在目标节点的右上方,从源节点下方出发
pointsList.push({
x: newSourceNodeData.x,
y: newSourceNodeData.y - sourceNodeModel.height / 2,
});
pointsList.push({
x: newSourceNodeData.x,
y: newSourceNodeData.y - sourceNodeModel.height / 2 - offset,
});
pointsList.push({
x: newTargetNodeData.x,
y: newSourceNodeData.y - sourceNodeModel.height / 2 - offset,
});
pointsList.push({
x: newTargetNodeData.x,
y: newTargetNodeData.y - targetNodeModel.height / 2,
});
}
return this.pointFilter(pointsList);
}
}
// 处理从上到下(TB)布局的边路径
if (this.option.rankdir === 'TB' && model.modelType === 'polyline-edge') {
// 正向连线:源节点在目标节点上方
if (newSourceNodeData.y < newTargetNodeData.y) {
// 从源节点底部中心出发
pointsList.push({
x: newSourceNodeData.x,
y: newSourceNodeData.y + sourceNodeModel.height / 2,
});
// 向下延伸一段距离
pointsList.push({
x: newSourceNodeData.x,
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
});
// 水平移动到目标节点的位置
pointsList.push({
x: newTargetNodeData.x,
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
});
// 连接到目标节点顶部中心
pointsList.push({
x: newTargetNodeData.x,
y: newTargetNodeData.y - targetNodeModel.height / 2,
});
return this.pointFilter(pointsList);
}
// 反向连线:源节点在目标节点下方
if (newSourceNodeData.y > newTargetNodeData.y) {
if (newSourceNodeData.x >= newTargetNodeData.x) {
// 源节点在目标节点右下方,从源节点右侧出发
pointsList.push({
x: newSourceNodeData.x + sourceNodeModel.width / 2,
y: newSourceNodeData.y,
});
pointsList.push({
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
y: newSourceNodeData.y,
});
pointsList.push({
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
y: newTargetNodeData.y,
});
pointsList.push({
x: newTargetNodeData.x + targetNodeModel.width / 2,
y: newTargetNodeData.y,
});
}
else {
// 源节点在目标节点左下方,从源节点左侧出发
pointsList.push({
x: newSourceNodeData.x - sourceNodeModel.width / 2,
y: newSourceNodeData.y,
});
pointsList.push({
x: newSourceNodeData.x - sourceNodeModel.width / 2 - offset,
y: newSourceNodeData.y,
});
pointsList.push({
x: newSourceNodeData.x - sourceNodeModel.width / 2 - offset,
y: newTargetNodeData.y,
});
pointsList.push({
x: newTargetNodeData.x - targetNodeModel.width / 2,
y: newTargetNodeData.y,
});
}
return this.pointFilter(pointsList);
}
}
// 无法确定路径时返回undefined,让LogicFlow自行处理
return undefined;
};
/** 插件名称,用于在LogicFlow中注册 */
Dagre.pluginName = 'dagre';
return Dagre;
}());
export { Dagre };
//# sourceMappingURL=dagre.js.map