@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,580 @@
1
+ import acyclic from "./acyclic";
2
+ import normalize from "./normalize";
3
+ import rank from "./rank";
4
+ import {
5
+ normalizeRanks,
6
+ removeEmptyRanks,
7
+ time as usetime,
8
+ notime,
9
+ asNonCompoundGraph,
10
+ addDummyNode,
11
+ intersectRect,
12
+ buildLayerMatrix,
13
+ } from "./util";
14
+ import parentDummyChains from "./parent-dummy-chains";
15
+ import nestingGraph from "./nesting-graph";
16
+ import addBorderSegments from "./add-border-segments";
17
+ import coordinateSystem from "./coordinate-system";
18
+ import order from "./order";
19
+ import position from "./position";
20
+ import initDataOrder from "./order/init-data-order";
21
+ import { Graph, Node } from "../graph";
22
+
23
+ const layout = (g: Graph, opts?: any) => {
24
+ const time = opts && opts.debugTiming ? usetime : notime;
25
+ time("layout", () => {
26
+ // 如果在原图基础上修改,继承原图的order结果
27
+ if (opts && !opts.keepNodeOrder && opts.prevGraph) {
28
+ time(" inheritOrder", () => {
29
+ inheritOrder(g, opts.prevGraph);
30
+ });
31
+ }
32
+ const layoutGraph: any = time(" buildLayoutGraph", () => {
33
+ return buildLayoutGraph(g);
34
+ });
35
+ // 控制是否为边的label留位置(这会影响是否在边中间添加dummy node)
36
+ if (!(opts && opts.edgeLabelSpace === false)) {
37
+ time(" makeSpaceForEdgeLabels", () => {
38
+ makeSpaceForEdgeLabels(layoutGraph);
39
+ });
40
+ }
41
+ // TODO: 暂时处理层级设置不正确时的异常报错,提示设置正确的层级
42
+ try {
43
+ time(" runLayout", () => {
44
+ runLayout(layoutGraph, time, opts);
45
+ });
46
+ } catch (e) {
47
+ if (
48
+ e.message ===
49
+ "Not possible to find intersection inside of the rectangle"
50
+ ) {
51
+ console.error(
52
+ "The following error may be caused by improper layer setting, please make sure your manual layer setting does not violate the graph's structure:\n",
53
+ e
54
+ );
55
+ return;
56
+ }
57
+ throw e;
58
+ }
59
+ time(" updateInputGraph", () => {
60
+ updateInputGraph(g, layoutGraph);
61
+ });
62
+ });
63
+ };
64
+
65
+ const runLayout = (g: Graph, time: any, opts: any) => {
66
+ time(" removeSelfEdges", () => {
67
+ removeSelfEdges(g);
68
+ });
69
+ time(" acyclic", () => {
70
+ acyclic.run(g);
71
+ });
72
+ time(" nestingGraph.run", () => {
73
+ nestingGraph.run(g);
74
+ });
75
+ time(" rank", () => {
76
+ rank(asNonCompoundGraph(g));
77
+ });
78
+ time(" injectEdgeLabelProxies", () => {
79
+ injectEdgeLabelProxies(g);
80
+ });
81
+ time(" removeEmptyRanks", () => {
82
+ removeEmptyRanks(g);
83
+ });
84
+ time(" nestingGraph.cleanup", () => {
85
+ nestingGraph.cleanup(g);
86
+ });
87
+ time(" normalizeRanks", () => {
88
+ normalizeRanks(g);
89
+ });
90
+ time(" assignRankMinMax", () => {
91
+ assignRankMinMax(g);
92
+ });
93
+ time(" removeEdgeLabelProxies", () => {
94
+ removeEdgeLabelProxies(g);
95
+ });
96
+ time(" normalize.run", () => {
97
+ normalize.run(g);
98
+ });
99
+ time(" parentDummyChains", () => {
100
+ parentDummyChains(g);
101
+ });
102
+ time(" addBorderSegments", () => {
103
+ addBorderSegments(g);
104
+ });
105
+ if (opts && opts.keepNodeOrder) {
106
+ time(" initDataOrder", () => {
107
+ initDataOrder(g, opts.nodeOrder);
108
+ });
109
+ }
110
+ time(" order", () => {
111
+ order(g);
112
+ });
113
+ time(" insertSelfEdges", () => {
114
+ insertSelfEdges(g);
115
+ });
116
+ time(" adjustCoordinateSystem", () => {
117
+ coordinateSystem.adjust(g);
118
+ });
119
+ time(" position", () => {
120
+ position(g);
121
+ });
122
+ time(" positionSelfEdges", () => {
123
+ positionSelfEdges(g);
124
+ });
125
+ time(" removeBorderNodes", () => {
126
+ removeBorderNodes(g);
127
+ });
128
+ time(" normalize.undo", () => {
129
+ normalize.undo(g);
130
+ });
131
+ time(" fixupEdgeLabelCoords", () => {
132
+ fixupEdgeLabelCoords(g);
133
+ });
134
+ time(" undoCoordinateSystem", () => {
135
+ coordinateSystem.undo(g);
136
+ });
137
+ time(" translateGraph", () => {
138
+ translateGraph(g);
139
+ });
140
+ time(" assignNodeIntersects", () => {
141
+ assignNodeIntersects(g);
142
+ });
143
+ time(" reversePoints", () => {
144
+ reversePointsForReversedEdges(g);
145
+ });
146
+ time(" acyclic.undo", () => {
147
+ acyclic.undo(g);
148
+ });
149
+ };
150
+
151
+ /**
152
+ * 继承上一个布局中的order,防止翻转
153
+ * TODO: 暂时没有考虑涉及层级变动的布局,只保证原来布局层级和相对顺序不变
154
+ */
155
+ const inheritOrder = (currG: Graph, prevG: Graph) => {
156
+ currG.nodes().forEach((n: string) => {
157
+ const node = currG.node(n)!;
158
+ const prevNode = prevG.node(n)!;
159
+ if (prevNode !== undefined) {
160
+ node.fixorder = prevNode._order;
161
+ delete prevNode._order;
162
+ } else {
163
+ delete node.fixorder;
164
+ }
165
+ });
166
+ };
167
+
168
+ /*
169
+ * Copies final layout information from the layout graph back to the input
170
+ * graph. This process only copies whitelisted attributes from the layout graph
171
+ * to the input graph, so it serves as a good place to determine what
172
+ * attributes can influence layout.
173
+ */
174
+ const updateInputGraph = (inputGraph: Graph, layoutGraph: Graph) => {
175
+ inputGraph.nodes().forEach((v) => {
176
+ const inputLabel = inputGraph.node(v);
177
+
178
+ if (inputLabel) {
179
+ const layoutLabel = layoutGraph.node(v)!;
180
+ inputLabel.x = layoutLabel.x;
181
+ inputLabel.y = layoutLabel.y;
182
+ inputLabel._order = layoutLabel.order;
183
+ inputLabel._rank = layoutLabel.rank;
184
+
185
+ if (layoutGraph.children(v)?.length) {
186
+ inputLabel.width = layoutLabel.width;
187
+ inputLabel.height = layoutLabel.height;
188
+ }
189
+ }
190
+ });
191
+
192
+ inputGraph.edges().forEach((e) => {
193
+ const inputLabel = inputGraph.edge(e)!;
194
+ const layoutLabel = layoutGraph.edge(e)!;
195
+
196
+ inputLabel.points = layoutLabel.points;
197
+ if (layoutLabel.hasOwnProperty("x")) {
198
+ inputLabel.x = layoutLabel.x;
199
+ inputLabel.y = layoutLabel.y;
200
+ }
201
+ });
202
+
203
+ inputGraph.graph().width = layoutGraph.graph().width;
204
+ inputGraph.graph().height = layoutGraph.graph().height;
205
+ };
206
+
207
+ const graphNumAttrs = ["nodesep", "edgesep", "ranksep", "marginx", "marginy"];
208
+ const graphDefaults = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: "tb" };
209
+ const graphAttrs = ["acyclicer", "ranker", "rankdir", "align"];
210
+ const nodeNumAttrs = ["width", "height", "layer", "fixorder"]; // 需要传入layer, fixOrder作为参数参考
211
+ const nodeDefaults = { width: 0, height: 0 };
212
+ const edgeNumAttrs = ["minlen", "weight", "width", "height", "labeloffset"];
213
+ const edgeDefaults = {
214
+ minlen: 1,
215
+ weight: 1,
216
+ width: 0,
217
+ height: 0,
218
+ labeloffset: 10,
219
+ labelpos: "r",
220
+ };
221
+ const edgeAttrs = ["labelpos"];
222
+
223
+ /*
224
+ * Constructs a new graph from the input graph, which can be used for layout.
225
+ * This process copies only whitelisted attributes from the input graph to the
226
+ * layout graph. Thus this function serves as a good place to determine what
227
+ * attributes can influence layout.
228
+ */
229
+ const buildLayoutGraph = (inputGraph: Graph) => {
230
+ const g = new Graph({ multigraph: true, compound: true });
231
+ const graph = canonicalize(inputGraph.graph());
232
+
233
+ const pickedProperties: any = {};
234
+ graphAttrs?.forEach((key) => {
235
+ if (graph[key] !== undefined) pickedProperties[key] = graph[key];
236
+ });
237
+
238
+ g.setGraph(
239
+ Object.assign(
240
+ {},
241
+ graphDefaults,
242
+ selectNumberAttrs(graph, graphNumAttrs),
243
+ pickedProperties
244
+ )
245
+ );
246
+
247
+ inputGraph.nodes().forEach((v) => {
248
+ const node = canonicalize(inputGraph.node(v));
249
+ const defaultNode = {
250
+ ...nodeDefaults,
251
+ ...node,
252
+ } as Node;
253
+ const defaultAttrs = selectNumberAttrs(defaultNode, nodeNumAttrs) as Node;
254
+
255
+ g.setNode(v, defaultAttrs);
256
+ g.setParent(v, inputGraph.parent(v));
257
+ });
258
+
259
+ inputGraph.edges().forEach((e) => {
260
+ const edge = canonicalize(inputGraph.edge(e));
261
+
262
+ const pickedProperties: any = {};
263
+ edgeAttrs?.forEach((key) => {
264
+ if (edge[key] !== undefined) pickedProperties[key] = edge[key];
265
+ });
266
+
267
+ g.setEdgeObj(
268
+ e,
269
+ Object.assign(
270
+ {},
271
+ edgeDefaults,
272
+ selectNumberAttrs(edge, edgeNumAttrs),
273
+ pickedProperties
274
+ )
275
+ );
276
+ });
277
+
278
+ return g;
279
+ };
280
+
281
+ /*
282
+ * This idea comes from the Gansner paper: to account for edge labels in our
283
+ * layout we split each rank in half by doubling minlen and halving ranksep.
284
+ * Then we can place labels at these mid-points between nodes.
285
+ *
286
+ * We also add some minimal padding to the width to push the label for the edge
287
+ * away from the edge itself a bit.
288
+ */
289
+ const makeSpaceForEdgeLabels = (g: Graph) => {
290
+ const graph = g.graph();
291
+ if (!graph.ranksep) graph.ranksep = 0;
292
+ graph.ranksep /= 2;
293
+ g.nodes().forEach((n) => {
294
+ const node = g.node(n)!;
295
+ if (!isNaN(node.layer as any)) {
296
+ if (!node.layer) node.layer = 0;
297
+ else node.layer *= 2; // TODO: 因为默认的rank变为两倍,设定的layer也*2
298
+ }
299
+ });
300
+ g.edges().forEach((e) => {
301
+ const edge = g.edge(e)!;
302
+ edge.minlen! *= 2;
303
+ if (edge.labelpos?.toLowerCase() !== "c") {
304
+ if (graph.rankdir === "TB" || graph.rankdir === "BT") {
305
+ edge.width += edge.labeloffset;
306
+ } else {
307
+ edge.height += edge.labeloffset;
308
+ }
309
+ }
310
+ });
311
+ };
312
+
313
+ /*
314
+ * Creates temporary dummy nodes that capture the rank in which each edge's
315
+ * label is going to, if it has one of non-zero width and height. We do this
316
+ * so that we can safely remove empty ranks while preserving balance for the
317
+ * label's position.
318
+ */
319
+ const injectEdgeLabelProxies = (g: Graph) => {
320
+ g.edges().forEach((e) => {
321
+ const edge = g.edge(e)!;
322
+ if (edge.width && edge.height) {
323
+ const v = g.node(e.v)!;
324
+ const w = g.node(e.w)!;
325
+ const label = {
326
+ e,
327
+ rank:
328
+ ((w.rank as number) - (v.rank as number)) / 2 + (v.rank as number),
329
+ };
330
+ addDummyNode(g, "edge-proxy", label, "_ep");
331
+ }
332
+ });
333
+ };
334
+
335
+ const assignRankMinMax = (g: Graph) => {
336
+ let maxRank = 0;
337
+ g.nodes().forEach((v) => {
338
+ const node = g.node(v)!;
339
+ if (node.borderTop) {
340
+ node.minRank = g.node(node.borderTop)?.rank;
341
+ node.maxRank = g.node(node.borderBottom)?.rank;
342
+ maxRank = Math.max(maxRank, node.maxRank || -Infinity);
343
+ }
344
+ });
345
+ g.graph().maxRank = maxRank;
346
+ };
347
+
348
+ const removeEdgeLabelProxies = (g: Graph) => {
349
+ g.nodes().forEach((v) => {
350
+ const node = g.node(v)!;
351
+ if (node.dummy === "edge-proxy") {
352
+ g.edge((node as any).e)!.labelRank = node.rank;
353
+ g.removeNode(v);
354
+ }
355
+ });
356
+ };
357
+
358
+ const translateGraph = (g: Graph) => {
359
+ let minX: number;
360
+ let maxX = 0;
361
+ let minY: number;
362
+ let maxY = 0;
363
+ const graphLabel = g.graph();
364
+ const marginX = graphLabel.marginx || 0;
365
+ const marginY = graphLabel.marginy || 0;
366
+
367
+ const getExtremes = (attrs: any) => {
368
+ if (!attrs) return;
369
+ const x = attrs.x;
370
+ const y = attrs.y;
371
+ const w = attrs.width;
372
+ const h = attrs.height;
373
+ if (!isNaN(x) && !isNaN(w)) {
374
+ if (minX === undefined) {
375
+ minX = x - w / 2;
376
+ }
377
+ minX = Math.min(minX, x - w / 2);
378
+ maxX = Math.max(maxX, x + w / 2);
379
+ }
380
+ if (!isNaN(y) && !isNaN(h)) {
381
+ if (minY === undefined) {
382
+ minY = y - h / 2;
383
+ }
384
+ minY = Math.min(minY, y - h / 2);
385
+ maxY = Math.max(maxY, y + h / 2);
386
+ }
387
+ };
388
+
389
+ g.nodes().forEach((v) => {
390
+ getExtremes(g.node(v));
391
+ });
392
+ g.edges().forEach((e) => {
393
+ const edge = g.edge(e)!;
394
+ if (edge?.hasOwnProperty("x")) {
395
+ getExtremes(edge);
396
+ }
397
+ });
398
+
399
+ minX! -= marginX;
400
+ minY! -= marginY;
401
+
402
+ g.nodes().forEach((v) => {
403
+ const node = g.node(v);
404
+ if (node) {
405
+ node.x! -= minX;
406
+ node.y! -= minY;
407
+ }
408
+ });
409
+
410
+ g.edges().forEach((e) => {
411
+ const edge = g.edge(e)!;
412
+ edge.points?.forEach((p) => {
413
+ p.x -= minX;
414
+ p.y -= minY;
415
+ });
416
+ if (edge.hasOwnProperty("x")) {
417
+ edge.x -= minX;
418
+ }
419
+ if (edge.hasOwnProperty("y")) {
420
+ edge.y -= minY;
421
+ }
422
+ });
423
+
424
+ graphLabel.width = maxX - minX! + marginX;
425
+ graphLabel.height = maxY - minY! + marginY;
426
+ };
427
+
428
+ const assignNodeIntersects = (g: Graph) => {
429
+ g.edges().forEach((e) => {
430
+ const edge = g.edge(e)!;
431
+ const nodeV = g.node(e.v)!;
432
+ const nodeW = g.node(e.w)!;
433
+ let p1;
434
+ let p2;
435
+ if (!edge.points) {
436
+ edge.points = [];
437
+ p1 = nodeW;
438
+ p2 = nodeV;
439
+ } else {
440
+ p1 = edge.points[0];
441
+ p2 = edge.points[edge.points.length - 1];
442
+ }
443
+ edge.points.unshift(intersectRect(nodeV, p1));
444
+ edge.points.push(intersectRect(nodeW, p2));
445
+ });
446
+ };
447
+
448
+ const fixupEdgeLabelCoords = (g: Graph) => {
449
+ g.edges().forEach((e) => {
450
+ const edge = g.edge(e)!;
451
+ if (edge?.hasOwnProperty("x")) {
452
+ if (edge.labelpos === "l" || edge.labelpos === "r") {
453
+ edge.width! -= edge.labeloffset;
454
+ }
455
+ switch (edge.labelpos) {
456
+ case "l":
457
+ edge.x -= edge.width! / 2 + edge.labeloffset;
458
+ break;
459
+ case "r":
460
+ edge.x += edge.width! / 2 + edge.labeloffset;
461
+ break;
462
+ }
463
+ }
464
+ });
465
+ };
466
+
467
+ const reversePointsForReversedEdges = (g: Graph) => {
468
+ g.edges().forEach((e) => {
469
+ const edge = g.edge(e)!;
470
+ if (edge.reversed) {
471
+ edge.points?.reverse();
472
+ }
473
+ });
474
+ };
475
+
476
+ const removeBorderNodes = (g: Graph) => {
477
+ g.nodes().forEach((v) => {
478
+ if (g.children(v)?.length) {
479
+ const node = g.node(v)!;
480
+ const t = g.node(node.borderTop);
481
+ const b = g.node(node.borderBottom);
482
+ const l = g.node(node.borderLeft[node.borderLeft?.length - 1]);
483
+ const r = g.node(node.borderRight[node.borderRight?.length - 1]);
484
+
485
+ node.width = Math.abs(r?.x! - l?.x!) || 10;
486
+ node.height = Math.abs(b?.y! - t?.y!) || 10;
487
+ node.x = (l?.x || 0) + node.width / 2;
488
+ node.y = (t?.y || 0) + node.height / 2;
489
+ }
490
+ });
491
+
492
+ g.nodes().forEach((v) => {
493
+ if (g.node(v)?.dummy === "border") {
494
+ g.removeNode(v);
495
+ }
496
+ });
497
+ };
498
+
499
+ const removeSelfEdges = (g: Graph) => {
500
+ g.edges().forEach((e) => {
501
+ if (e.v === e.w) {
502
+ const node = g.node(e.v)!;
503
+ if (!node.selfEdges) {
504
+ node.selfEdges = [];
505
+ }
506
+ node.selfEdges.push({ e, label: g.edge(e) });
507
+ g.removeEdgeObj(e);
508
+ }
509
+ });
510
+ };
511
+
512
+ const insertSelfEdges = (g: Graph) => {
513
+ const layers = buildLayerMatrix(g);
514
+ layers?.forEach((layer: string[]) => {
515
+ let orderShift = 0;
516
+ layer?.forEach((v: string, i: number) => {
517
+ const node = g.node(v)!;
518
+ node.order = i + orderShift;
519
+ node.selfEdges?.forEach((selfEdge: any) => {
520
+ addDummyNode(
521
+ g,
522
+ "selfedge",
523
+ {
524
+ width: selfEdge.label.width,
525
+ height: selfEdge.label.height,
526
+ rank: node.rank,
527
+ order: i + ++orderShift,
528
+ e: selfEdge.e,
529
+ label: selfEdge.label,
530
+ },
531
+ "_se"
532
+ );
533
+ });
534
+ delete node.selfEdges;
535
+ });
536
+ });
537
+ };
538
+
539
+ const positionSelfEdges = (g: Graph) => {
540
+ g.nodes().forEach((v) => {
541
+ const node = g.node(v)!;
542
+ if (node.dummy === "selfedge") {
543
+ const selfNode = g.node(node.e.v)!;
544
+ const x = selfNode.x! + selfNode.width! / 2;
545
+ const y = selfNode.y!;
546
+ const dx = node.x! - x;
547
+ const dy = selfNode.height! / 2;
548
+ g.setEdgeObj(node.e, node.label);
549
+ g.removeNode(v);
550
+ node.label.points = [
551
+ { x: x + (2 * dx) / 3, y: y - dy },
552
+ { x: x + (5 * dx) / 6, y: y - dy },
553
+ { y, x: x + dx },
554
+ { x: x + (5 * dx) / 6, y: y + dy },
555
+ { x: x + (2 * dx) / 3, y: y + dy },
556
+ ];
557
+ node.label.x = node.x;
558
+ node.label.y = node.y;
559
+ }
560
+ });
561
+ };
562
+
563
+ const selectNumberAttrs = (obj: Record<string, any>, attrs: string[]) => {
564
+ const pickedProperties: Record<string, any> = {};
565
+ attrs?.forEach((key: string) => {
566
+ if (obj[key] === undefined) return;
567
+ pickedProperties[key] = (+obj[key]);
568
+ });
569
+ return pickedProperties;
570
+ };
571
+
572
+ const canonicalize = (attrs: Record<string, any> = {}) => {
573
+ const newAttrs: Record<string, any> = {};
574
+ Object.keys(attrs).forEach((k: string) => {
575
+ newAttrs[k.toLowerCase()] = attrs[k];
576
+ });
577
+ return newAttrs;
578
+ };
579
+
580
+ export default layout;
@@ -0,0 +1,143 @@
1
+ import { Graph } from "../graph";
2
+ import { addBorderNode, addDummyNode } from "./util";
3
+
4
+ /*
5
+ * A nesting graph creates dummy nodes for the tops and bottoms of subgraphs,
6
+ * adds appropriate edges to ensure that all cluster nodes are placed between
7
+ * these boundries, and ensures that the graph is connected.
8
+ *
9
+ * In addition we ensure, through the use of the minlen property, that nodes
10
+ * and subgraph border nodes to not end up on the same rank.
11
+ *
12
+ * Preconditions:
13
+ *
14
+ * 1. Input graph is a DAG
15
+ * 2. Nodes in the input graph has a minlen attribute
16
+ *
17
+ * Postconditions:
18
+ *
19
+ * 1. Input graph is connected.
20
+ * 2. Dummy nodes are added for the tops and bottoms of subgraphs.
21
+ * 3. The minlen attribute for nodes is adjusted to ensure nodes do not
22
+ * get placed on the same rank as subgraph border nodes.
23
+ *
24
+ * The nesting graph idea comes from Sander, "Layout of Compound Directed
25
+ * Graphs."
26
+ */
27
+ const run = (g: Graph) => {
28
+ const root = addDummyNode(g, "root", {}, "_root");
29
+ const depths = treeDepths(g);
30
+ let maxDepth = Math.max(...Object.values(depths));
31
+
32
+ if (Math.abs(maxDepth) === Infinity) {
33
+ maxDepth = 1;
34
+ }
35
+
36
+ const height = maxDepth - 1; // Note: depths is an Object not an array
37
+ const nodeSep = 2 * height + 1;
38
+
39
+ g.graph().nestingRoot = root;
40
+
41
+ // Multiply minlen by nodeSep to align nodes on non-border ranks.
42
+ g.edges().forEach((e) => {
43
+ g.edge(e)!.minlen! *= nodeSep;
44
+ });
45
+
46
+ // Calculate a weight that is sufficient to keep subgraphs vertically compact
47
+ const weight = sumWeights(g) + 1;
48
+
49
+ // Create border nodes and link them up
50
+ g.children()?.forEach((child) => {
51
+ dfs(g, root, nodeSep, weight, height, depths, child);
52
+ });
53
+
54
+ // Save the multiplier for node layers for later removal of empty border
55
+ // layers.
56
+ g.graph().nodeRankFactor = nodeSep;
57
+ };
58
+
59
+ const dfs = (
60
+ g: Graph,
61
+ root: string,
62
+ nodeSep: number,
63
+ weight: number,
64
+ height: number,
65
+ depths:Record<string, number>,
66
+ v: string
67
+ ) => {
68
+ const children = g.children(v);
69
+ if (!children?.length) {
70
+ if (v !== root) {
71
+ g.setEdge(root, v, { weight: 0, minlen: nodeSep });
72
+ }
73
+ return;
74
+ }
75
+
76
+ const top = addBorderNode(g, "_bt");
77
+ const bottom = addBorderNode(g, "_bb");
78
+ const label = g.node(v)!;
79
+
80
+ g.setParent(top, v);
81
+ label.borderTop = top;
82
+ g.setParent(bottom, v);
83
+ label.borderBottom = bottom;
84
+
85
+ children?.forEach((child) => {
86
+ dfs(g, root, nodeSep, weight, height, depths, child);
87
+
88
+ const childNode = g.node(child)!;
89
+ const childTop = childNode.borderTop ? childNode.borderTop : child;
90
+ const childBottom = childNode.borderBottom ? childNode.borderBottom : child;
91
+ const thisWeight = childNode.borderTop ? weight : 2 * weight;
92
+ const minlen = childTop !== childBottom ? 1 : height - depths[v] + 1;
93
+
94
+ g.setEdge(top, childTop, {
95
+ minlen,
96
+ weight: thisWeight,
97
+ nestingEdge: true,
98
+ });
99
+
100
+ g.setEdge(childBottom, bottom, {
101
+ minlen,
102
+ weight: thisWeight,
103
+ nestingEdge: true,
104
+ });
105
+ });
106
+
107
+ if (!g.parent(v)) {
108
+ g.setEdge(root, top, { weight: 0, minlen: height + depths[v] });
109
+ }
110
+ };
111
+
112
+ const treeDepths = (g: Graph) => {
113
+ const depths: Record<string, number> = {};
114
+ const dfs = (v: string, depth: number) => {
115
+ const children = g.children(v);
116
+ children?.forEach((child) => dfs(child, depth + 1));
117
+ depths[v] = depth;
118
+ };
119
+ g.children()?.forEach((v) => dfs(v, 1));
120
+ return depths;
121
+ };
122
+
123
+ const sumWeights = (g: Graph) => {
124
+ let result = 0;
125
+ g.edges().forEach((e) => {
126
+ result += g.edge(e)!.weight!;
127
+ });
128
+ return result;
129
+ };
130
+
131
+ const cleanup = (g: Graph) => {
132
+ const graphLabel = g.graph();
133
+ graphLabel.nestingRoot && g.removeNode(graphLabel.nestingRoot);
134
+ delete graphLabel.nestingRoot;
135
+ g.edges().forEach((e: any) => {
136
+ const edge = g.edge(e)!;
137
+ if (edge.nestingEdge) {
138
+ g.removeEdgeObj(e);
139
+ }
140
+ });
141
+ };
142
+
143
+ export default { run, cleanup };