index.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /**
  2. * 自动布局插件
  3. * 依赖flowPath插件
  4. * 未完善
  5. */
  6. var __assign = (this && this.__assign) || function () {
  7. __assign = Object.assign || function(t) {
  8. for (var s, i = 1, n = arguments.length; i < n; i++) {
  9. s = arguments[i];
  10. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  11. t[p] = s[p];
  12. }
  13. return t;
  14. };
  15. return __assign.apply(this, arguments);
  16. };
  17. var POSITION_TYPE = {
  18. LEFT_TOP: -1,
  19. LEFT: 0,
  20. LEFT_BOTTOM: 1,
  21. };
  22. var AutoLayout = /** @class */ (function () {
  23. function AutoLayout(_a) {
  24. var _this = this;
  25. var lf = _a.lf;
  26. this.lf = lf;
  27. /**
  28. * 用于记录上一次调用layout时计算出的trunk
  29. * 当旧trunk和新trunk长度一致时,用于选择旧trunk,
  30. * a->b->c->d
  31. * |->e
  32. * e后面新增f节点时候,旧逻辑会返回新trunk[a,b,e,f]
  33. * 界面布局变成
  34. * a->b->e->f
  35. * |->c->d
  36. * 其实只想要这样 尽量少变化
  37. * a->b->c->d
  38. * |->e->f
  39. * */
  40. this.trunk = [];
  41. // 给lf添加方法
  42. lf.layout = function (startNodeType) {
  43. var data = _this.lf.getGraphRawData();
  44. _this.lf.setStartNodeType(startNodeType);
  45. var path = _this.lf.getPathes();
  46. _this.levelHeight = [];
  47. _this.newNodeMap = new Map();
  48. return _this.layout(data, path);
  49. };
  50. }
  51. // 1) 将所有节点和边的坐标删除。节点上的文本改成偏移量。
  52. // 2) 找到长度最长的路径,作为基准路径。
  53. // 3) 依次计算
  54. // 拿到最长的路径。
  55. // nodes: [], edges: [],
  56. AutoLayout.prototype.layout = function (data, path) {
  57. var _this = this;
  58. var trunk = [];
  59. path.forEach(function (p) {
  60. var elements = p.elements;
  61. if (elements.length > trunk.length) {
  62. trunk = elements;
  63. }
  64. else if (elements.length === trunk.length) {
  65. // 考虑是否替换为旧的trunk
  66. if (JSON.stringify(elements) === JSON.stringify(_this.trunk)) {
  67. trunk = _this.trunk;
  68. }
  69. }
  70. });
  71. // 记录上一次trunk
  72. this.trunk = trunk;
  73. var nodeMap = this.formatData(data);
  74. var newGraphData = {
  75. nodes: [],
  76. edges: [],
  77. };
  78. // 从后向前布局
  79. for (var i = trunk.length - 1; i >= 0; i--) {
  80. this.setNodePosition(trunk[i], nodeMap, newGraphData, i, 1);
  81. }
  82. this.lf.graphModel.graphDataToModel(newGraphData);
  83. };
  84. // 1) 需要知道下一层级已占高度。
  85. // 2) 基于自己的高度,判断下一个层级的高度
  86. AutoLayout.prototype.setNodePosition = function (nodeId, nodeMap, newGraphData, xLevel, yLevel) {
  87. var _this = this;
  88. var n = nodeMap[nodeId];
  89. var text = n.text, type = n.type, next = n.next, properties = n.properties;
  90. var x = xLevel * 160 + 40;
  91. var y = yLevel * 120;
  92. var nodeData = {
  93. id: nodeId,
  94. x: x,
  95. text: text,
  96. y: y,
  97. type: type,
  98. properties: properties,
  99. };
  100. if (text && typeof text === 'object') {
  101. nodeData.text = __assign(__assign({}, text), { x: x + text.x, y: y + text.y });
  102. }
  103. this.newNodeMap.set(nodeData.id, {
  104. x: nodeData.x,
  105. y: nodeData.y,
  106. type: type,
  107. });
  108. newGraphData.nodes.push(nodeData);
  109. n.isFixed = true;
  110. this.addLevelHeight(xLevel, 1);
  111. if (next && next.length > 0) {
  112. next.forEach(function (nextInfo) {
  113. // 如果下一个节点还没有被定位,那么设置其定位
  114. var n1 = nodeMap[nextInfo.nodeId];
  115. if (!n1.isFixed) {
  116. var nextYLevel = _this.getLevelHeight(xLevel + 1);
  117. _this.addLevelHeight(xLevel, 1);
  118. _this.setNodePosition(nextInfo.nodeId, nodeMap, newGraphData, xLevel + 1, nextYLevel + 1);
  119. }
  120. else {
  121. // todo: 如果下一个节点是已经定位的,则需要考虑边的规避
  122. }
  123. // 设置连接到下一个节点的边
  124. // 1) 起始位置为source节点的下方,结束位置为target节点左边。
  125. // 2) 计算折线
  126. newGraphData.edges.push(__assign({ id: nextInfo.edgeId, type: nextInfo.edgeType, sourceNodeId: nodeId, targetNodeId: nextInfo.nodeId, properties: nextInfo.properties, text: nextInfo.text }, _this.getEdgeDataPoints(nodeId, nextInfo.nodeId)));
  127. });
  128. }
  129. return nodeData;
  130. };
  131. /**
  132. * 1. 处理边上的文本
  133. * 2. 主干节点之间直接的边
  134. * 3. 一个节点被多个连接作为目标节点,合理分配锚点位置。
  135. */
  136. AutoLayout.prototype.getEdgeDataPoints = function (sourceNodeId, targetNodeId) {
  137. var source = this.newNodeMap.get(sourceNodeId);
  138. var target = this.newNodeMap.get(targetNodeId);
  139. var _a = this.getShape(sourceNodeId), width = _a.width, height = _a.height;
  140. var _b = this.getShape(targetNodeId), targetWidth = _b.width, targetHeight = _b.height;
  141. var positionType = this.getRelativePosition(source, target);
  142. var startPoint = {
  143. x: source.x,
  144. y: source.y,
  145. };
  146. var endPoint = {
  147. x: target.x,
  148. y: target.y,
  149. };
  150. switch (positionType) {
  151. case POSITION_TYPE.LEFT:
  152. startPoint.x = source.x + width / 2;
  153. endPoint.x = target.x - targetWidth / 2;
  154. break;
  155. case POSITION_TYPE.LEFT_TOP:
  156. startPoint.y = source.y + height / 2;
  157. endPoint.x = target.x - targetWidth / 2;
  158. break;
  159. case POSITION_TYPE.LEFT_BOTTOM:
  160. startPoint.x = source.x + width / 2;
  161. endPoint.y = target.y + targetHeight / 2;
  162. break;
  163. default:
  164. break;
  165. }
  166. return {
  167. startPoint: startPoint,
  168. endPoint: endPoint,
  169. };
  170. };
  171. /**
  172. * 获取边的连接节点相对位置。
  173. * source一定在target左边。
  174. * 1. 如果source和target在同一x, y坐标内容。
  175. * 2. 如果source在target左上方。
  176. * 3. 如果source在target左下方。
  177. */
  178. AutoLayout.prototype.getRelativePosition = function (source, target) {
  179. var y = source.y;
  180. var y1 = target.y;
  181. var positionType;
  182. if (y < y1) {
  183. positionType = -1;
  184. }
  185. else if (y === y1) {
  186. positionType = 0;
  187. }
  188. else {
  189. positionType = 1;
  190. }
  191. return positionType;
  192. };
  193. /**
  194. * 获取边节点图形的宽高。
  195. */
  196. AutoLayout.prototype.getShape = function (nodeId) {
  197. var nodeModel = this.lf.getNodeModelById(nodeId);
  198. return {
  199. height: nodeModel.height,
  200. width: nodeModel.width,
  201. };
  202. };
  203. AutoLayout.prototype.formatData = function (data) {
  204. var nodeMap = data.nodes.reduce(function (nMap, node) {
  205. var type = node.type, properties = node.properties, text = node.text, x = node.x, y = node.y;
  206. if (text && typeof text === 'object') {
  207. // 坐标转换为偏移量
  208. text.x = text.x - x;
  209. text.y = text.y - y;
  210. }
  211. nMap[node.id] = {
  212. type: type,
  213. properties: properties,
  214. text: text,
  215. prev: [],
  216. next: [],
  217. };
  218. return nMap;
  219. }, {});
  220. data.edges.forEach(function (edge) {
  221. var sourceNodeId = edge.sourceNodeId, targetNodeId = edge.targetNodeId, id = edge.id, properties = edge.properties, text = edge.text;
  222. var newText = text;
  223. if (typeof text === 'object') {
  224. newText = text.value;
  225. }
  226. nodeMap[sourceNodeId].next.push({
  227. edgeId: id,
  228. nodeId: targetNodeId,
  229. edgeType: edge.type,
  230. properties: properties,
  231. text: newText,
  232. });
  233. nodeMap[targetNodeId].prev.push({
  234. edgeId: id,
  235. nodeId: sourceNodeId,
  236. properties: properties,
  237. text: newText,
  238. });
  239. });
  240. return nodeMap;
  241. };
  242. AutoLayout.prototype.addLevelHeight = function (level, height, isNegative) {
  243. if (height === void 0) { height = 1; }
  244. if (isNegative === void 0) { isNegative = false; }
  245. var l = this.levelHeight[level];
  246. if (!l) {
  247. l = {
  248. positiveHeight: 0,
  249. negativeHeight: 0,
  250. };
  251. this.levelHeight[level] = l;
  252. }
  253. isNegative ? (l.negativeHeight -= height) : (l.positiveHeight += height);
  254. };
  255. AutoLayout.prototype.getLevelHeight = function (level, isNegative) {
  256. if (isNegative === void 0) { isNegative = false; }
  257. var val = this.levelHeight[level];
  258. if (!val) {
  259. return 0;
  260. }
  261. return isNegative ? val.negativeHeight : val.positiveHeight;
  262. };
  263. AutoLayout.pluginName = 'AutoLayout';
  264. return AutoLayout;
  265. }());
  266. export { AutoLayout };