@antv/layout 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. package/dist/layout.min.js +1 -1
  2. package/dist/layout.min.js.LICENSE.txt +0 -9
  3. package/dist/layout.min.js.map +1 -1
  4. package/es/layout/circular.js.map +1 -1
  5. package/es/layout/comboCombined.js +3 -19
  6. package/es/layout/comboCombined.js.map +1 -1
  7. package/es/layout/concentric.js.map +1 -1
  8. package/es/layout/dagre/graph.d.ts +91 -0
  9. package/es/layout/dagre/graph.js +4 -0
  10. package/es/layout/dagre/graph.js.map +1 -0
  11. package/es/layout/dagre/index.d.ts +3 -4
  12. package/es/layout/dagre/index.js +0 -2
  13. package/es/layout/dagre/index.js.map +1 -1
  14. package/es/layout/dagre/src/acyclic.d.ts +1 -2
  15. package/es/layout/dagre/src/acyclic.js +7 -7
  16. package/es/layout/dagre/src/acyclic.js.map +1 -1
  17. package/es/layout/dagre/src/add-border-segments.d.ts +1 -2
  18. package/es/layout/dagre/src/add-border-segments.js +5 -5
  19. package/es/layout/dagre/src/add-border-segments.js.map +1 -1
  20. package/es/layout/dagre/src/coordinate-system.d.ts +1 -2
  21. package/es/layout/dagre/src/coordinate-system.js +15 -5
  22. package/es/layout/dagre/src/coordinate-system.js.map +1 -1
  23. package/es/layout/dagre/src/data/list.d.ts +9 -5
  24. package/es/layout/dagre/src/data/list.js +25 -27
  25. package/es/layout/dagre/src/data/list.js.map +1 -1
  26. package/es/layout/dagre/src/debug.d.ts +2 -3
  27. package/es/layout/dagre/src/debug.js +3 -5
  28. package/es/layout/dagre/src/debug.js.map +1 -1
  29. package/es/layout/dagre/src/greedy-fas.d.ts +2 -3
  30. package/es/layout/dagre/src/greedy-fas.js +16 -15
  31. package/es/layout/dagre/src/greedy-fas.js.map +1 -1
  32. package/es/layout/dagre/src/layout.d.ts +2 -3
  33. package/es/layout/dagre/src/layout.js +170 -91
  34. package/es/layout/dagre/src/layout.js.map +1 -1
  35. package/es/layout/dagre/src/nesting-graph.d.ts +1 -2
  36. package/es/layout/dagre/src/nesting-graph.js +15 -10
  37. package/es/layout/dagre/src/nesting-graph.js.map +1 -1
  38. package/es/layout/dagre/src/normalize.d.ts +1 -2
  39. package/es/layout/dagre/src/normalize.js +12 -11
  40. package/es/layout/dagre/src/normalize.js.map +1 -1
  41. package/es/layout/dagre/src/order/add-subgraph-constraints.d.ts +1 -2
  42. package/es/layout/dagre/src/order/add-subgraph-constraints.js.map +1 -1
  43. package/es/layout/dagre/src/order/barycenter.d.ts +1 -2
  44. package/es/layout/dagre/src/order/barycenter.js.map +1 -1
  45. package/es/layout/dagre/src/order/build-layer-graph.d.ts +2 -3
  46. package/es/layout/dagre/src/order/build-layer-graph.js +12 -8
  47. package/es/layout/dagre/src/order/build-layer-graph.js.map +1 -1
  48. package/es/layout/dagre/src/order/cross-count.d.ts +2 -3
  49. package/es/layout/dagre/src/order/cross-count.js +13 -12
  50. package/es/layout/dagre/src/order/cross-count.js.map +1 -1
  51. package/es/layout/dagre/src/order/index.d.ts +2 -3
  52. package/es/layout/dagre/src/order/index.js +17 -15
  53. package/es/layout/dagre/src/order/index.js.map +1 -1
  54. package/es/layout/dagre/src/order/init-data-order.d.ts +1 -2
  55. package/es/layout/dagre/src/order/init-data-order.js +3 -5
  56. package/es/layout/dagre/src/order/init-data-order.js.map +1 -1
  57. package/es/layout/dagre/src/order/init-order.d.ts +2 -3
  58. package/es/layout/dagre/src/order/init-order.js +1 -2
  59. package/es/layout/dagre/src/order/init-order.js.map +1 -1
  60. package/es/layout/dagre/src/order/resolve-conflicts.d.ts +18 -3
  61. package/es/layout/dagre/src/order/resolve-conflicts.js +9 -29
  62. package/es/layout/dagre/src/order/resolve-conflicts.js.map +1 -1
  63. package/es/layout/dagre/src/order/sort-subgraph.d.ts +6 -3
  64. package/es/layout/dagre/src/order/sort-subgraph.js +19 -14
  65. package/es/layout/dagre/src/order/sort-subgraph.js.map +1 -1
  66. package/es/layout/dagre/src/order/sort.d.ts +6 -1
  67. package/es/layout/dagre/src/order/sort.js +2 -2
  68. package/es/layout/dagre/src/order/sort.js.map +1 -1
  69. package/es/layout/dagre/src/parent-dummy-chains.d.ts +1 -2
  70. package/es/layout/dagre/src/parent-dummy-chains.js +47 -44
  71. package/es/layout/dagre/src/parent-dummy-chains.js.map +1 -1
  72. package/es/layout/dagre/src/position/bk.d.ts +22 -31
  73. package/es/layout/dagre/src/position/bk.js +49 -83
  74. package/es/layout/dagre/src/position/bk.js.map +1 -1
  75. package/es/layout/dagre/src/position/index.d.ts +1 -2
  76. package/es/layout/dagre/src/position/index.js +12 -14
  77. package/es/layout/dagre/src/position/index.js.map +1 -1
  78. package/es/layout/dagre/src/rank/feasible-tree.d.ts +5 -6
  79. package/es/layout/dagre/src/rank/feasible-tree.js +1 -2
  80. package/es/layout/dagre/src/rank/feasible-tree.js.map +1 -1
  81. package/es/layout/dagre/src/rank/index.d.ts +2 -3
  82. package/es/layout/dagre/src/rank/index.js.map +1 -1
  83. package/es/layout/dagre/src/rank/network-simplex.d.ts +8 -11
  84. package/es/layout/dagre/src/rank/network-simplex.js +18 -31
  85. package/es/layout/dagre/src/rank/network-simplex.js.map +1 -1
  86. package/es/layout/dagre/src/rank/util.d.ts +4 -5
  87. package/es/layout/dagre/src/rank/util.js +37 -20
  88. package/es/layout/dagre/src/rank/util.js.map +1 -1
  89. package/es/layout/dagre/src/util.d.ts +29 -48
  90. package/es/layout/dagre/src/util.js +91 -101
  91. package/es/layout/dagre/src/util.js.map +1 -1
  92. package/es/layout/dagre.d.ts +1 -1
  93. package/es/layout/dagre.js +28 -24
  94. package/es/layout/dagre.js.map +1 -1
  95. package/es/layout/fruchterman.js.map +1 -1
  96. package/es/layout/gForce.js +14 -6
  97. package/es/layout/gForce.js.map +1 -1
  98. package/lib/index.js +5 -1
  99. package/lib/index.js.map +1 -1
  100. package/lib/layout/circular.js.map +1 -1
  101. package/lib/layout/comboCombined.js +2 -18
  102. package/lib/layout/comboCombined.js.map +1 -1
  103. package/lib/layout/comboForce.js +5 -5
  104. package/lib/layout/comboForce.js.map +1 -1
  105. package/lib/layout/concentric.js.map +1 -1
  106. package/lib/layout/dagre/graph.d.ts +91 -0
  107. package/lib/layout/dagre/graph.js +28 -0
  108. package/lib/layout/dagre/graph.js.map +1 -0
  109. package/lib/layout/dagre/index.d.ts +3 -4
  110. package/lib/layout/dagre/index.js +0 -2
  111. package/lib/layout/dagre/index.js.map +1 -1
  112. package/lib/layout/dagre/src/acyclic.d.ts +1 -2
  113. package/lib/layout/dagre/src/acyclic.js +7 -7
  114. package/lib/layout/dagre/src/acyclic.js.map +1 -1
  115. package/lib/layout/dagre/src/add-border-segments.d.ts +1 -2
  116. package/lib/layout/dagre/src/add-border-segments.js +5 -8
  117. package/lib/layout/dagre/src/add-border-segments.js.map +1 -1
  118. package/lib/layout/dagre/src/coordinate-system.d.ts +1 -2
  119. package/lib/layout/dagre/src/coordinate-system.js +15 -5
  120. package/lib/layout/dagre/src/coordinate-system.js.map +1 -1
  121. package/lib/layout/dagre/src/data/list.d.ts +9 -5
  122. package/lib/layout/dagre/src/data/list.js +25 -26
  123. package/lib/layout/dagre/src/data/list.js.map +1 -1
  124. package/lib/layout/dagre/src/debug.d.ts +2 -3
  125. package/lib/layout/dagre/src/debug.js +6 -11
  126. package/lib/layout/dagre/src/debug.js.map +1 -1
  127. package/lib/layout/dagre/src/greedy-fas.d.ts +2 -3
  128. package/lib/layout/dagre/src/greedy-fas.js +41 -15
  129. package/lib/layout/dagre/src/greedy-fas.js.map +1 -1
  130. package/lib/layout/dagre/src/layout.d.ts +2 -3
  131. package/lib/layout/dagre/src/layout.js +171 -100
  132. package/lib/layout/dagre/src/layout.js.map +1 -1
  133. package/lib/layout/dagre/src/nesting-graph.d.ts +1 -2
  134. package/lib/layout/dagre/src/nesting-graph.js +15 -13
  135. package/lib/layout/dagre/src/nesting-graph.js.map +1 -1
  136. package/lib/layout/dagre/src/normalize.d.ts +1 -2
  137. package/lib/layout/dagre/src/normalize.js +12 -14
  138. package/lib/layout/dagre/src/normalize.js.map +1 -1
  139. package/lib/layout/dagre/src/order/add-subgraph-constraints.d.ts +1 -2
  140. package/lib/layout/dagre/src/order/add-subgraph-constraints.js.map +1 -1
  141. package/lib/layout/dagre/src/order/barycenter.d.ts +1 -2
  142. package/lib/layout/dagre/src/order/barycenter.js.map +1 -1
  143. package/lib/layout/dagre/src/order/build-layer-graph.d.ts +2 -3
  144. package/lib/layout/dagre/src/order/build-layer-graph.js +13 -12
  145. package/lib/layout/dagre/src/order/build-layer-graph.js.map +1 -1
  146. package/lib/layout/dagre/src/order/cross-count.d.ts +2 -3
  147. package/lib/layout/dagre/src/order/cross-count.js +14 -13
  148. package/lib/layout/dagre/src/order/cross-count.js.map +1 -1
  149. package/lib/layout/dagre/src/order/index.d.ts +2 -3
  150. package/lib/layout/dagre/src/order/index.js +15 -13
  151. package/lib/layout/dagre/src/order/index.js.map +1 -1
  152. package/lib/layout/dagre/src/order/init-data-order.d.ts +1 -2
  153. package/lib/layout/dagre/src/order/init-data-order.js +3 -5
  154. package/lib/layout/dagre/src/order/init-data-order.js.map +1 -1
  155. package/lib/layout/dagre/src/order/init-order.d.ts +2 -3
  156. package/lib/layout/dagre/src/order/init-order.js +1 -2
  157. package/lib/layout/dagre/src/order/init-order.js.map +1 -1
  158. package/lib/layout/dagre/src/order/resolve-conflicts.d.ts +18 -3
  159. package/lib/layout/dagre/src/order/resolve-conflicts.js +9 -29
  160. package/lib/layout/dagre/src/order/resolve-conflicts.js.map +1 -1
  161. package/lib/layout/dagre/src/order/sort-subgraph.d.ts +6 -3
  162. package/lib/layout/dagre/src/order/sort-subgraph.js +16 -11
  163. package/lib/layout/dagre/src/order/sort-subgraph.js.map +1 -1
  164. package/lib/layout/dagre/src/order/sort.d.ts +6 -1
  165. package/lib/layout/dagre/src/order/sort.js +2 -5
  166. package/lib/layout/dagre/src/order/sort.js.map +1 -1
  167. package/lib/layout/dagre/src/parent-dummy-chains.d.ts +1 -2
  168. package/lib/layout/dagre/src/parent-dummy-chains.js +47 -44
  169. package/lib/layout/dagre/src/parent-dummy-chains.js.map +1 -1
  170. package/lib/layout/dagre/src/position/bk.d.ts +22 -31
  171. package/lib/layout/dagre/src/position/bk.js +75 -85
  172. package/lib/layout/dagre/src/position/bk.js.map +1 -1
  173. package/lib/layout/dagre/src/position/index.d.ts +1 -2
  174. package/lib/layout/dagre/src/position/index.js +14 -17
  175. package/lib/layout/dagre/src/position/index.js.map +1 -1
  176. package/lib/layout/dagre/src/rank/feasible-tree.d.ts +5 -6
  177. package/lib/layout/dagre/src/rank/feasible-tree.js +3 -7
  178. package/lib/layout/dagre/src/rank/feasible-tree.js.map +1 -1
  179. package/lib/layout/dagre/src/rank/index.d.ts +2 -3
  180. package/lib/layout/dagre/src/rank/index.js.map +1 -1
  181. package/lib/layout/dagre/src/rank/network-simplex.d.ts +8 -11
  182. package/lib/layout/dagre/src/rank/network-simplex.js +27 -35
  183. package/lib/layout/dagre/src/rank/network-simplex.js.map +1 -1
  184. package/lib/layout/dagre/src/rank/util.d.ts +4 -5
  185. package/lib/layout/dagre/src/rank/util.js +36 -19
  186. package/lib/layout/dagre/src/rank/util.js.map +1 -1
  187. package/lib/layout/dagre/src/util.d.ts +29 -48
  188. package/lib/layout/dagre/src/util.js +80 -92
  189. package/lib/layout/dagre/src/util.js.map +1 -1
  190. package/lib/layout/dagre.d.ts +1 -1
  191. package/lib/layout/dagre.js +27 -23
  192. package/lib/layout/dagre.js.map +1 -1
  193. package/lib/layout/er/core.js +5 -1
  194. package/lib/layout/er/core.js.map +1 -1
  195. package/lib/layout/force/force-in-a-box.js +7 -3
  196. package/lib/layout/force/force-in-a-box.js.map +1 -1
  197. package/lib/layout/force/force.js +5 -1
  198. package/lib/layout/force/force.js.map +1 -1
  199. package/lib/layout/force/index.js +5 -1
  200. package/lib/layout/force/index.js.map +1 -1
  201. package/lib/layout/fruchterman.js.map +1 -1
  202. package/lib/layout/gForce.js +10 -2
  203. package/lib/layout/gForce.js.map +1 -1
  204. package/lib/layout/grid.js +2 -2
  205. package/lib/layout/grid.js.map +1 -1
  206. package/lib/layout/index.js +5 -1
  207. package/lib/layout/index.js.map +1 -1
  208. package/lib/layout/radial/index.js +5 -1
  209. package/lib/layout/radial/index.js.map +1 -1
  210. package/lib/registy/index.js +1 -1
  211. package/lib/registy/index.js.map +1 -1
  212. package/lib/util/index.js +5 -1
  213. package/lib/util/index.js.map +1 -1
  214. package/package.json +3 -2
  215. package/src/index.ts +7 -0
  216. package/src/layout/base.ts +54 -0
  217. package/src/layout/circular.ts +369 -0
  218. package/src/layout/comboCombined.ts +390 -0
  219. package/src/layout/comboForce.ts +873 -0
  220. package/src/layout/concentric.ts +289 -0
  221. package/src/layout/constants.ts +21 -0
  222. package/src/layout/dagre/graph.ts +104 -0
  223. package/src/layout/dagre/index.ts +31 -0
  224. package/src/layout/dagre/src/acyclic.ts +58 -0
  225. package/src/layout/dagre/src/add-border-segments.ts +47 -0
  226. package/src/layout/dagre/src/coordinate-system.ts +77 -0
  227. package/src/layout/dagre/src/data/list.ts +60 -0
  228. package/src/layout/dagre/src/debug.ts +30 -0
  229. package/src/layout/dagre/src/greedy-fas.ts +144 -0
  230. package/src/layout/dagre/src/layout.ts +580 -0
  231. package/src/layout/dagre/src/nesting-graph.ts +143 -0
  232. package/src/layout/dagre/src/normalize.ts +96 -0
  233. package/src/layout/dagre/src/order/add-subgraph-constraints.ts +29 -0
  234. package/src/layout/dagre/src/order/barycenter.ts +26 -0
  235. package/src/layout/dagre/src/order/build-layer-graph.ts +82 -0
  236. package/src/layout/dagre/src/order/cross-count.ts +77 -0
  237. package/src/layout/dagre/src/order/index.ts +105 -0
  238. package/src/layout/dagre/src/order/init-data-order.ts +27 -0
  239. package/src/layout/dagre/src/order/init-order.ts +56 -0
  240. package/src/layout/dagre/src/order/resolve-conflicts.ts +152 -0
  241. package/src/layout/dagre/src/order/sort-subgraph.ts +105 -0
  242. package/src/layout/dagre/src/order/sort.ts +76 -0
  243. package/src/layout/dagre/src/parent-dummy-chains.ts +102 -0
  244. package/src/layout/dagre/src/position/bk.ts +494 -0
  245. package/src/layout/dagre/src/position/index.ts +82 -0
  246. package/src/layout/dagre/src/rank/feasible-tree.ts +165 -0
  247. package/src/layout/dagre/src/rank/index.ts +54 -0
  248. package/src/layout/dagre/src/rank/network-simplex.ts +225 -0
  249. package/src/layout/dagre/src/rank/util.ts +157 -0
  250. package/src/layout/dagre/src/util.ts +308 -0
  251. package/src/layout/dagre.ts +423 -0
  252. package/src/layout/dagreCompound.ts +518 -0
  253. package/src/layout/er/core.ts +117 -0
  254. package/src/layout/er/forceGrid.ts +95 -0
  255. package/src/layout/er/grid.ts +185 -0
  256. package/src/layout/er/index.ts +68 -0
  257. package/src/layout/er/mysqlWorkbench.ts +345 -0
  258. package/src/layout/er/type.ts +39 -0
  259. package/src/layout/force/force-in-a-box.ts +400 -0
  260. package/src/layout/force/force.ts +391 -0
  261. package/src/layout/force/index.ts +1 -0
  262. package/src/layout/forceAtlas2/body.ts +115 -0
  263. package/src/layout/forceAtlas2/index.ts +556 -0
  264. package/src/layout/forceAtlas2/quad.ts +115 -0
  265. package/src/layout/forceAtlas2/quadTree.ts +107 -0
  266. package/src/layout/fruchterman.ts +361 -0
  267. package/src/layout/gForce.ts +487 -0
  268. package/src/layout/gpu/fruchterman.ts +314 -0
  269. package/src/layout/gpu/fruchtermanShader.ts +204 -0
  270. package/src/layout/gpu/gForce.ts +406 -0
  271. package/src/layout/gpu/gForceShader.ts +221 -0
  272. package/src/layout/grid.ts +391 -0
  273. package/src/layout/index.ts +45 -0
  274. package/src/layout/layout.ts +75 -0
  275. package/src/layout/mds.ts +140 -0
  276. package/src/layout/radial/index.ts +1 -0
  277. package/src/layout/radial/mds.ts +51 -0
  278. package/src/layout/radial/radial.ts +500 -0
  279. package/src/layout/radial/radialNonoverlapForce.ts +189 -0
  280. package/src/layout/random.ts +75 -0
  281. package/src/layout/types.ts +421 -0
  282. package/src/registy/index.ts +43 -0
  283. package/src/util/array.ts +1 -0
  284. package/src/util/function.ts +64 -0
  285. package/src/util/gpu.ts +254 -0
  286. package/src/util/index.ts +6 -0
  287. package/src/util/math.ts +158 -0
  288. package/src/util/number.ts +8 -0
  289. package/src/util/object.ts +28 -0
  290. package/src/util/string.ts +18 -0
  291. package/CHANGELOG.md +0 -78
  292. package/es/layout/dagre/src/graphlib.d.ts +0 -2
  293. package/es/layout/dagre/src/graphlib.js +0 -51
  294. package/es/layout/dagre/src/graphlib.js.map +0 -1
  295. package/lib/layout/dagre/src/graphlib.d.ts +0 -2
  296. package/lib/layout/dagre/src/graphlib.js +0 -56
  297. package/lib/layout/dagre/src/graphlib.js.map +0 -1
@@ -0,0 +1,95 @@
1
+ import Grid from './grid';
2
+ import { INode, IEdgeInfo } from './type';
3
+
4
+ export default function layout(data: {
5
+ nodes: INode[],
6
+ edges: IEdgeInfo[],
7
+ }, options: any) {
8
+ if (!data.nodes || data.nodes.length === 0) return data;
9
+ const width = options.width;
10
+ const height = options.height;
11
+ const nodeMinGap = options.nodeMinGap;
12
+
13
+ // 2. 网格布局
14
+ let CELL_W = 10000;
15
+ let CELL_H = 10000;
16
+ data.nodes.forEach((node) => {
17
+ const nodeWidth = node.size[0] || 50;
18
+ const nodeHeight = node.size[1] || 50;
19
+
20
+ CELL_W = Math.min(nodeWidth, CELL_W);
21
+ CELL_H = Math.min(nodeHeight, CELL_H);
22
+ });
23
+
24
+ const grid = new Grid();
25
+ grid.init(width, height, {
26
+ CELL_H,
27
+ CELL_W,
28
+ });
29
+
30
+ data.nodes.forEach((d) => {
31
+ const gridpoint = grid.occupyNearest(d);
32
+ if (gridpoint) {
33
+ gridpoint.node = {
34
+ id: d.id,
35
+ size: d.size,
36
+ };
37
+ d.x = gridpoint.x;
38
+ d.y = gridpoint.y;
39
+ d.dx = gridpoint.dx;
40
+ d.dy = gridpoint.dy;
41
+ }
42
+ });
43
+
44
+ // 加入节点size
45
+ for (let i = 0; i < data.nodes.length; i++) {
46
+ // 节点宽度大于网格宽度,则往当前网格的右边插入列
47
+ const node = data.nodes[i];
48
+ const result = grid.findGridByNodeId(node.id);
49
+ if (!result) throw new Error("can not find node cell");
50
+
51
+ const { column, row } = result;
52
+ if ((node.size[0] + nodeMinGap) > CELL_W) {
53
+ const addGridSize = Math.ceil((node.size[0] +nodeMinGap) / CELL_W) - 1;
54
+ let realAdd = addGridSize;
55
+ // 优化,假设同一列,不同行存在两个size为2的节点,遍历到第一个节点的时候,会往右插入两列,遍历到第二个节点,又往右插入。就会导致多余的网格
56
+ for(let j=0; j< addGridSize; j++) {
57
+ const hasColumn = grid.additionColumn.indexOf(column + j + 1) > -1;
58
+ if (hasColumn && !grid.cells[column + j + 1][row].node) {
59
+ realAdd --;
60
+ } else {
61
+ break;
62
+ }
63
+ }
64
+ grid.insertColumn(column, realAdd);
65
+ }
66
+ // 节点高度大于网格宽度,则往当前网格的下边插入行
67
+ if ((node.size[1] +nodeMinGap) > CELL_H) {
68
+ const addGridSize = Math.ceil((node.size[1]+nodeMinGap) / CELL_H) - 1;
69
+ let realAdd = addGridSize;
70
+ for(let j=0; j< addGridSize; j++) {
71
+ const hasColumn = grid.additionRow.indexOf(row + j + 1) > -1;
72
+ if (hasColumn && !grid.cells[column][row + j + 1].node) {
73
+ realAdd --;
74
+ } else {
75
+ break;
76
+ }
77
+ }
78
+ grid.insertRow(row, realAdd);
79
+ }
80
+ }
81
+
82
+ // 同步节点坐标
83
+ for(let i = 0; i < grid.columnNum; i++) {
84
+ for(let j = 0; j < grid.rowNum; j++) {
85
+ const cell = grid.cells[i][j];
86
+ if (cell.node) {
87
+ const node = data.nodes.find((node) => node.id === cell?.node?.id);
88
+ if (node) {
89
+ node.x = cell.x + node.size[0] / 2;
90
+ node.y = cell.y + node.size[1] / 2;
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,185 @@
1
+ import { ICell, INode } from './type';
2
+
3
+ export default class Grid {
4
+ public cells: ICell[][] = [];
5
+ public columnNum:number = 0;
6
+ public rowNum: number = 0;
7
+
8
+ public additionColumn: number[] = [];
9
+ public additionRow: number[] = [];
10
+ private static MIN_DIST = 50;
11
+ private static DEFAULT_CELL_W = 80;
12
+ private static DEFAULT_CELL_H = 80;
13
+ private CELL_W: number;
14
+ private CELL_H: number;
15
+
16
+ public init(width: number, height: number, gridSize: {
17
+ CELL_W: number,
18
+ CELL_H: number,
19
+ }) {
20
+ this.cells = [];
21
+ this.CELL_W = gridSize.CELL_W || Grid.DEFAULT_CELL_W;
22
+ this.CELL_H = gridSize.CELL_H || Grid.DEFAULT_CELL_H;
23
+ this.columnNum = Math.ceil(width / this.CELL_W);
24
+ this.rowNum = Math.ceil(height / this.CELL_H);
25
+ Grid.MIN_DIST = Math.pow(width, 2) + Math.pow(height, 2);
26
+
27
+ for(let i = 0; i < this.columnNum; i++) {
28
+ const tmp = [];
29
+ for(let j = 0; j < this.rowNum; j++) {
30
+ const cell = {
31
+ dx: i,
32
+ dy: j,
33
+ x : i * this.CELL_W,
34
+ y : j * this.CELL_H,
35
+ occupied : false
36
+ };
37
+ tmp.push(cell);
38
+ }
39
+ this.cells.push(tmp);
40
+ }
41
+ }
42
+
43
+ public findGridByNodeId(nodeId: string){
44
+ for(let i = 0; i < this.columnNum; i++) {
45
+ for(let j = 0; j < this.rowNum; j++) {
46
+ if(this.cells[i][j].node) {
47
+ if (this.cells[i][j]?.node?.id === nodeId) {
48
+ return {column: i, row: j};
49
+ }
50
+ }
51
+ }
52
+ }
53
+ return null;
54
+ }
55
+
56
+ public sqdist(a: any, b: any) {
57
+ return Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2);
58
+ }
59
+
60
+ public occupyNearest(p: INode) {
61
+ let minDist = Grid.MIN_DIST;
62
+ let d;
63
+ let candidate = null;
64
+ for(let i = 0; i < this.columnNum; i++) {
65
+ for(let j = 0; j < this.rowNum; j++) {
66
+ if(!this.cells[i][j].occupied && ( d = this.sqdist(p, this.cells[i][j])) < minDist) {
67
+ minDist = d;
68
+ candidate = this.cells[i][j];
69
+ }
70
+ }
71
+ }
72
+ if(candidate) {
73
+ candidate.occupied = true;
74
+ }
75
+ return candidate;
76
+ }
77
+
78
+ public insertColumn(columnIndex: number, length: number) {
79
+ if (length <= 0) return ;
80
+ // 插入空列
81
+ for (let i = 0; i < length; i++) {
82
+ this.cells[i + this.columnNum] = [];
83
+ for(let j = 0; j < this.rowNum; j++) {
84
+ this.cells[i + this.columnNum][j] = {
85
+ dx: i,
86
+ dy: j,
87
+ x : i * this.CELL_W,
88
+ y : j * this.CELL_H,
89
+ occupied : false,
90
+ node: null,
91
+ };
92
+ }
93
+ }
94
+ // 交换数据
95
+ for(let i = (this.columnNum - 1); i > columnIndex; i--) {
96
+ for (let j = 0; j < this.rowNum; j++) {
97
+ this.cells[i + length][j] = {
98
+ ...this.cells[i][j],
99
+ x: (i+length) * this.CELL_W,
100
+ y: j * this.CELL_H,
101
+ };
102
+ this.cells[i][j] = {
103
+ x : i * this.CELL_W,
104
+ y : j * this.CELL_H,
105
+ occupied : true,
106
+ node: null,
107
+ };
108
+ }
109
+ }
110
+ // 已有行列的处理
111
+ for (let j = 0; j < this.additionColumn.length; j++) {
112
+ if (this.additionColumn[j] >= columnIndex) {
113
+ this.additionColumn[j] += length;
114
+ }
115
+ }
116
+ // 记录新增的行列
117
+ for (let i = 0; i < length; i++) {
118
+ this.additionColumn.push(columnIndex + i + 1);
119
+ }
120
+ this.columnNum += length;
121
+ }
122
+
123
+ public insertRow(rowIndex: number, length: number) {
124
+ if (length <= 0) return ;
125
+ // 插入空行
126
+ for (let j = 0; j < length; j++) {
127
+ for(let i = 0; i < this.columnNum; i++) {
128
+ this.cells[i][j + this.rowNum] = {
129
+ dx: i,
130
+ dy: j,
131
+ x : i * this.CELL_W,
132
+ y : j * this.CELL_H,
133
+ occupied : false,
134
+ node: null,
135
+ };
136
+ }
137
+ }
138
+
139
+ // 交换数据
140
+ for(let i = 0; i < this.columnNum; i++) {
141
+ for (let j = (this.rowNum - 1); j > rowIndex; j--) {
142
+ this.cells[i][j+length] = {
143
+ ...this.cells[i][j],
144
+ dx: i,
145
+ dy: j + length,
146
+ x: i * this.CELL_W,
147
+ y: (j+length) * this.CELL_H,
148
+ };
149
+ this.cells[i][j] = {
150
+ dx: i,
151
+ dy: j,
152
+ x : i * this.CELL_W,
153
+ y : j *this.CELL_H,
154
+ occupied : false,
155
+ node: null,
156
+ };
157
+
158
+ }
159
+ }
160
+
161
+ // 已有行列的处理
162
+ for (let j = 0; j < this.additionRow.length; j++) {
163
+ if (this.additionRow[j] >= rowIndex) {
164
+ this.additionRow[j] += length;
165
+ }
166
+ }
167
+ // 记录新增的行列
168
+ for (let i = 0; i < length; i++) {
169
+ this.additionRow.push(rowIndex + i + 1);
170
+ }
171
+ this.rowNum += length;
172
+ }
173
+
174
+ public getNodes() {
175
+ const nodes = [];
176
+ for(let i = 0; i < this.columnNum; i++) {
177
+ for(let j = 0; j < this.rowNum; j++) {
178
+ if(this.cells[i][j].node) {
179
+ nodes.push(this.cells[i][j]);
180
+ }
181
+ }
182
+ }
183
+ return nodes;
184
+ }
185
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @fileOverview Force Layout Grid Align layout
3
+ * @author wenyanqi
4
+ */
5
+
6
+ import { Base } from "../base";
7
+ import layout from './core';
8
+ import { INode } from './type';
9
+
10
+ export interface ERLayoutOptions {
11
+ type: "er";
12
+ width?: number;
13
+ height?: number;
14
+ nodeMinGap?: number;
15
+ }
16
+
17
+ export class ERLayout extends Base {
18
+
19
+ public width: number = 300;
20
+ public height: number = 300;
21
+ public nodeMinGap: number = 50;
22
+
23
+ /** 迭代结束的回调函数 */
24
+ public onLayoutEnd: () => void = () => { };
25
+
26
+ constructor(options?: any) {
27
+ super();
28
+ if (options) {
29
+ this.updateCfg(options);
30
+ }
31
+ }
32
+
33
+ public getDefaultCfg() {
34
+ return {
35
+ width: 300,
36
+ height: 300,
37
+ nodeMinGap: 50,
38
+ };
39
+ }
40
+
41
+ /**
42
+ * 执行布局
43
+ */
44
+ public execute() {
45
+ const self = this;
46
+ const nodes = self.nodes;
47
+ const edges = self.edges;
48
+ // 节点初始化,size初始化
49
+ nodes?.forEach((node: INode) => {
50
+ if (!node.size) {
51
+ node.size = [50, 50];
52
+ }
53
+ });
54
+ return layout({
55
+ nodes, edges,
56
+ }, {
57
+ width: this.width,
58
+ height: this.height,
59
+ nodeMinGap: this.nodeMinGap,
60
+ }).then(() => {
61
+ if (self.onLayoutEnd) self.onLayoutEnd();
62
+ });
63
+ }
64
+
65
+ public getType() {
66
+ return "er";
67
+ }
68
+ }
@@ -0,0 +1,345 @@
1
+ import { IEdge, IMysqlNode } from './type';
2
+
3
+ const graphWidth = 1200;
4
+ const graphHeight = 800;
5
+ const OVERLAP_QUOT = 10000000;
6
+ const MIN_DIST = 10;
7
+ const M_PI = 3.14159265358979323846;
8
+ const M_PI_2 = 1.57079632679489661923;
9
+ const PI_38 = M_PI * 0.375;
10
+ const PI_58 = M_PI * 0.625;
11
+ const nodeEdgeMap = new Map();
12
+ const CELL_W = 10;
13
+ const CELL_H = 10;
14
+ let T = 0.8;
15
+ const T_MIN = 0.1;
16
+ const R = 0.5;
17
+
18
+ function distanceToNode(node1: IMysqlNode, node2: IMysqlNode, isHoriz: boolean) {
19
+ const x11 = node1.x - node1.size[0] / 2;
20
+ const y11 = node1.y - node1.size[1] / 2;
21
+ const x12 = node1.x + node1.size[0] / 2;
22
+ const y12 = node1.y + node1.size[1] / 2;
23
+ const x21 = node2.x - node2.size[0] / 2;
24
+ const y21 = node2.y - node2.size[1] / 2;
25
+ const x22 = node2.x + node2.size[0] / 2;
26
+ const y22 = node2.y + node2.size[1] / 2;
27
+
28
+ const cx1 = node1.x;
29
+ const cy1 = node1.y;
30
+ const cx2 = node2.x;
31
+ const cy2 = node2.y;
32
+ const dcx = cx2 - cx1;
33
+ // 两个节点间的方位角
34
+ const qr = Math.atan2(dcx, (cy2 - cy1));
35
+ let dx = 0;
36
+ let dy = 0;
37
+ let l1 = 0;
38
+ let l2 = 0;
39
+ if (qr > M_PI_2) {
40
+ dy = y11 - y22;
41
+ dx = x21 - x12;
42
+ l1 = parseFloat(dy ? (dy / Math.cos(qr)).toFixed(2) : (dx).toFixed(2));
43
+ l2 = parseFloat(dx ? (dx / Math.sin(qr)).toFixed(2) : (dy).toFixed(2));
44
+ } else if (0.0 < qr && qr <= M_PI_2) {
45
+ dy = y21 - y12;
46
+ dx = x21 - x12;
47
+ if (dy > dx) {
48
+ l1 = l2 = parseFloat(dy ? (dy / Math.cos(qr)).toFixed(2) : (dx).toFixed(2));
49
+ } else {
50
+ l1 = l2 = parseFloat(dx ? (dx / Math.sin(qr)).toFixed(2) : (dy).toFixed(2));
51
+ }
52
+
53
+ } else if (qr < -M_PI_2) {
54
+ dy = y11 - y22;
55
+ dx = -(x22 - x11);
56
+ if (dy > dx) {
57
+ l1 = l2 = parseFloat(dy ? (dy / Math.cos(qr)).toFixed(2) : (dx).toFixed(2));
58
+ } else {
59
+ l1 = l2 = parseFloat(dx ? (dx / Math.sin(qr)).toFixed(2) : (dy).toFixed(2));
60
+ }
61
+ }else {
62
+ dy = y21 - y12;
63
+ if (Math.abs(dcx) > (x12 - x11) / 2) {
64
+ dx = x11 - x22;
65
+ } else {
66
+ dx = dcx;
67
+ }
68
+
69
+ if (dy > dx) {
70
+ l1 = l2 = parseFloat(dy ? (dy / Math.cos(qr)).toFixed(2) : (dx).toFixed(2));
71
+ } else {
72
+ l1 = l2 = parseFloat((dx && qr !== 0.0) ? (dx / Math.sin(qr)).toFixed(2) : (dy).toFixed(2));
73
+ }
74
+
75
+ }
76
+ const aqr = parseFloat(qr.toFixed(2));
77
+ // 判断是否水平,角度
78
+ let newHoriz = isHoriz;
79
+ if (isHoriz) {
80
+ newHoriz = PI_38 < aqr && aqr < PI_58;
81
+ }
82
+ return {
83
+ distance: Math.abs(l1 < l2 ? l1 : l2),
84
+ isHoriz: newHoriz,
85
+ };
86
+ }
87
+
88
+ function calcNodePair(nodeA: IMysqlNode, nodeB: IMysqlNode) {
89
+ // 确定两个节点间是否存在连线
90
+ const edges = nodeEdgeMap.get(nodeA.id) || [];
91
+ const isLinked = edges.find((edge: IEdge) => {
92
+ return edge.source === nodeB.id || edge.target === nodeB.id;
93
+ });
94
+
95
+ const areaA = nodeA.size[0] * nodeA.size[1];
96
+ const areaB = nodeB.size[0] * nodeB.size[1];
97
+ const node1 = areaA > areaB ? nodeB : nodeA;
98
+ const node2 = areaA > areaB ? nodeA : nodeB;
99
+
100
+ const x11 = node1.x - node1.size[0] / 2;
101
+ const y11 = node1.y - node1.size[1] / 2;
102
+ const x12 = node1.x + node1.size[0] / 2;
103
+ const y12 = node1.y + node1.size[1] / 2;
104
+ const x21 = node2.x - node2.size[0] / 2;
105
+ const y21 = node2.y - node2.size[1] / 2;
106
+ const x22 = node2.x + node2.size[0] / 2;
107
+ const y22 = node2.y + node2.size[1] / 2;
108
+
109
+ const cx1 = node1.x;
110
+ const cy1 = node1.y;
111
+ const cx2 = node2.x;
112
+ const cy2 = node2.y;
113
+
114
+ // Detect if nodes overlap 检查节点之间是否存在覆盖问题
115
+ const isoverlap = ((x12 >= x21) && (x22 >= x11) && (y12 >= y21) && (y22 >= y11));
116
+ let e = 0;
117
+ let distance = 0;
118
+
119
+ if (isoverlap) {
120
+
121
+ distance = Math.sqrt(Math.pow((cx2 - cx1), 2) + Math.pow((cy2 - cy1), 2));
122
+
123
+ // calc area of overlap 计算重复区域的坐标和面积
124
+ const sx1 = x11 > x21 ? x11 : x21;
125
+ const sy1 = y11 > y21 ? y11 : y21;
126
+ const sx2 = x12 < x22 ? x12 : x22;
127
+ const sy2 = y12 < y22 ? y12 : y22;
128
+ const dsx = sx2 - sx1;
129
+ const dsy = sy2 - sy1;
130
+
131
+ const sov = dsx * dsy;
132
+
133
+ if (distance === 0.0) {
134
+ distance = 0.0000001;
135
+ }
136
+
137
+ e = MIN_DIST * 1 / distance * 100 + sov;
138
+ e *= OVERLAP_QUOT;
139
+ } else {
140
+ let isHoriz = false;
141
+ const res = distanceToNode(node1, node2, isHoriz);
142
+ distance = res.distance;
143
+ isHoriz = res.isHoriz;
144
+
145
+ if (distance <= MIN_DIST) {
146
+ if (distance !== 0) {
147
+ if (isLinked) {
148
+ e += MIN_DIST + OVERLAP_QUOT * 1 / distance;
149
+ }
150
+ else {
151
+ e += MIN_DIST + OVERLAP_QUOT * MIN_DIST / distance;
152
+ }
153
+ } else {
154
+ e += OVERLAP_QUOT;
155
+ }
156
+ } else {
157
+ e += distance;
158
+ if (isLinked) {
159
+ e += distance * distance;
160
+ }
161
+ }
162
+ }
163
+
164
+ return e;
165
+ }
166
+ function calcEnergy(nodes: any) {
167
+ let energy = 0;
168
+ for(let i = 0; i < nodes.length; i++) {
169
+ const node = nodes[i];
170
+ if ((node.x < 0) || (node.y < 0) || (node.x > graphWidth) || (node.y > graphHeight)) {
171
+ energy += 1000000000000;
172
+ }
173
+ for (let j = i + 1; j < nodes.length; j++) {
174
+ energy += calcNodePair(node, nodes[j]);
175
+ }
176
+ }
177
+
178
+ return energy;
179
+ }
180
+
181
+ function isCorrectPosition(node: IMysqlNode, newPosition: {
182
+ x: number, y: number
183
+ }, nodes: IMysqlNode[], edges: IEdge[]) {
184
+ const nodeIdxMap = new Map<string, IMysqlNode>();
185
+ nodes.forEach((o, i) => {
186
+ nodeIdxMap.set(o.id, o);
187
+ });
188
+ const relateEdges = edges.filter((edge) => edge.source === node.id || edge.target === node.id) || [];
189
+ const relateNodes: IMysqlNode[] = [];
190
+ relateEdges.forEach((edge) => {
191
+ const otherNodeId = edge.source === node.id ? edge.target : edge.source;
192
+ const otherNode = nodeIdxMap.get(otherNodeId);
193
+ if (otherNode) {
194
+ relateNodes.push(otherNode);
195
+ }
196
+ });
197
+
198
+ let flag = true;
199
+ for(let i = 0; i < relateNodes.length; i++) {
200
+ const item = relateNodes[i];
201
+ // 判断条件调整,节点的坐标不需要完全一致。可以根据节点间的夹角来判断
202
+ const delta = Math.atan((node.y - item.y) / (item.x - node.y)) * 180;
203
+ const newDelta = Math.atan((newPosition.y - item.y) / (item.x - newPosition.y)) * 180;
204
+ const isHor = delta < 30 || delta > 150;
205
+ const newIsHor = newDelta < 30 || newDelta > 150;
206
+ const isVer = delta > 70 && delta < 110;
207
+ const newIsVer = newDelta > 70 && newDelta < 110;
208
+ // 定义四个相似角度区间,0-15度,75-90度,90到105度,165到180度。
209
+ if (isHor && !newIsHor || ((delta * newDelta) < 0)) {
210
+ flag = false;
211
+ break;
212
+ } else if (isVer && !newIsVer || ((delta * newDelta) < 0)) {
213
+ flag = false;
214
+ break;
215
+ } else if ((item.x - node.x) * (item.x - newPosition.x) < 0) {
216
+ flag = false;
217
+ break;
218
+ }else if ((item.y - node.y) * (item.y - newPosition.y) < 0) {
219
+ flag = false;
220
+ break;
221
+ }
222
+ }
223
+ return flag;
224
+ }
225
+
226
+ function shuffle(nodes: IMysqlNode[], edges: IEdge[]) {
227
+ let foundSmallerEnergy = false;
228
+ // 多次测试发现step为1时的效果最佳。
229
+ const step = 1;
230
+ const wstep = CELL_W * step;
231
+ const hstep = CELL_H * step;
232
+ const wsteps = [ wstep, -wstep, 0, 0, ];
233
+ const hsteps = [ 0, 0, hstep, -hstep, ];
234
+ for (let i = 0; i < nodes.length; ++i) {
235
+ const node = nodes[i];
236
+ let nodeEnergy = calcNodeEnergy(node, nodes);
237
+ for (let ns = 0; ns < wsteps.length ; ns++) {
238
+ // 判断新位置与其他连线节点的位置关系是否违规
239
+ const flag = isCorrectPosition(node, { x: node.x + wsteps[ns], y: node.y + hsteps[ns] }, nodes, edges);
240
+ if (flag) {
241
+ // 节点朝上下左右四个方向移动,找到能量最小的那个位置
242
+ node.x += wsteps[ns];
243
+ node.y += hsteps[ns];
244
+
245
+ // 计算移动后节点的能量
246
+ const energy = calcNodeEnergy(node, nodes);
247
+ const rdm = Math.random();
248
+
249
+ if (energy < nodeEnergy) {
250
+ nodeEnergy = energy;
251
+ foundSmallerEnergy = true;
252
+
253
+ } else if (rdm < T && rdm > T_MIN) {
254
+ nodeEnergy = energy;
255
+ foundSmallerEnergy = true;
256
+
257
+ } else {
258
+ // 回归原位
259
+ node.x -= wsteps[ns];
260
+ node.y -= hsteps[ns];
261
+ }
262
+ }
263
+ }
264
+
265
+ }
266
+ if (T > T_MIN) {
267
+ T *= R;
268
+ }
269
+ // 重新计算图整体的能量
270
+ if (foundSmallerEnergy) {
271
+ return calcEnergy(nodes);
272
+ }
273
+ return 0;
274
+ }
275
+
276
+ // 计算节点的能量,
277
+ function calcNodeEnergy(node: IMysqlNode, nodes: IMysqlNode[]) {
278
+ let e = 0.0;
279
+ if ((node.x < 0) || (node.y < 0) ||
280
+ (node.x + node.size[0] + 20 > graphWidth) ||
281
+ (node.y + node.size[1] + 20 > graphHeight)
282
+ ) {
283
+ e += 1000000000000.0;
284
+ }
285
+
286
+ for (let i = 0; i < nodes.length; ++i) {
287
+ if (node.id !== nodes[i].id) {
288
+ e += calcNodePair(node, nodes[i]);
289
+ }
290
+ }
291
+ return e;
292
+ }
293
+
294
+ function layout(nodes: IMysqlNode[], edges: IEdge[]) {
295
+ if (nodes.length === 0) {
296
+ return { nodes, edges };
297
+ }
298
+ nodes.forEach((node: any) => {
299
+ const relateEdge = edges.filter((edge) => edge.source === node.id || edge.target === node.id);
300
+ nodeEdgeMap.set(node, relateEdge);
301
+ });
302
+
303
+ // 1. 初始化
304
+ // 将node按照连接数进行排序
305
+ nodes.sort((node1: IMysqlNode, node2: IMysqlNode) => {
306
+ return nodeEdgeMap.get(node1.id)?.length - nodeEdgeMap.get(node2.id)?.length;
307
+ });
308
+
309
+ // 2. 计算图能量
310
+ let minEnergy = calcEnergy(nodes);
311
+ let deSameCount = 20; // de=0 count
312
+ let de = 1; // energy delta
313
+ let prevEnergy = 0;
314
+ // 定义总的迭代次数。超过就停掉,防止死循环
315
+ const MAX_COUNT = 50;
316
+ let count = 0;
317
+ while (deSameCount > 0) {
318
+ count ++;
319
+ if (count >= MAX_COUNT) {
320
+ break;
321
+ }
322
+ const ea = shuffle(nodes, edges);
323
+ if (ea !== 0) {
324
+ prevEnergy = ea;
325
+ }
326
+ de = prevEnergy - minEnergy;
327
+ minEnergy = prevEnergy;
328
+ if (de === 0) {
329
+ --deSameCount;
330
+ } else {
331
+ deSameCount = 20;
332
+ }
333
+ }
334
+ nodes.forEach((node: IMysqlNode) => {
335
+ node.x = node.x - node.size[0] / 2;
336
+ node.y = node.y - node.size[1] / 2;
337
+ });
338
+
339
+ return {
340
+ nodes,
341
+ edges,
342
+ };
343
+ }
344
+
345
+ export default layout;