UNPKG

@logicflow/layout

Version:
381 lines 15.7 kB
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