@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,500 @@
1
+ /**
2
+ * @fileOverview random layout
3
+ * @author shiwu.wyy@antfin.com
4
+ */
5
+
6
+ import {
7
+ PointTuple,
8
+ Node,
9
+ OutNode,
10
+ Edge,
11
+ Matrix,
12
+ RadialLayoutOptions
13
+ } from "../types";
14
+ import {
15
+ isNaN,
16
+ isArray,
17
+ isFunction,
18
+ isNumber,
19
+ isString,
20
+ floydWarshall,
21
+ getAdjMatrix,
22
+ isObject
23
+ } from "../../util";
24
+ import { Base } from "../base";
25
+ import MDS from "./mds";
26
+ import RadialNonoverlapForce, {
27
+ RadialNonoverlapForceParam
28
+ } from "./radialNonoverlapForce";
29
+
30
+ type INode = OutNode & {
31
+ size?: number | PointTuple;
32
+ };
33
+
34
+ function getWeightMatrix(M: Matrix[]) {
35
+ const rows = M.length;
36
+ const cols = M[0].length;
37
+ const result = [];
38
+ for (let i = 0; i < rows; i++) {
39
+ const row = [];
40
+ for (let j = 0; j < cols; j++) {
41
+ if (M[i][j] !== 0) {
42
+ row.push(1 / (M[i][j] * M[i][j]));
43
+ } else {
44
+ row.push(0);
45
+ }
46
+ }
47
+ result.push(row);
48
+ }
49
+ return result;
50
+ }
51
+
52
+ function getIndexById(array: any[], id: string) {
53
+ let index = -1;
54
+ array.forEach((a, i) => {
55
+ if (a.id === id) {
56
+ index = i;
57
+ }
58
+ });
59
+ return index;
60
+ }
61
+
62
+ function getEDistance(p1: PointTuple, p2: PointTuple) {
63
+ return Math.sqrt(
64
+ (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1])
65
+ );
66
+ }
67
+
68
+ /**
69
+ * 辐射状布局
70
+ */
71
+ export class RadialLayout extends Base {
72
+ /** 布局中心 */
73
+ public center: PointTuple;
74
+
75
+ /** 停止迭代的最大迭代数 */
76
+ public maxIteration: number = 1000;
77
+
78
+ /** 中心点,默认为数据中第一个点 */
79
+ public focusNode: string | Node | null = null;
80
+
81
+ /** 每一圈半径 */
82
+ public unitRadius: number | null = null;
83
+
84
+ /** 默认边长度 */
85
+ public linkDistance: number = 50;
86
+
87
+ /** 是否防止重叠 */
88
+ public preventOverlap: boolean = false;
89
+
90
+ /** 节点直径 */
91
+ public nodeSize: number | number[] | undefined;
92
+
93
+ /** 节点间距,防止节点重叠时节点之间的最小距离(两节点边缘最短距离) */
94
+ public nodeSpacing: number | Function | undefined;
95
+
96
+ /** 是否必须是严格的 radial 布局,即每一层的节点严格布局在一个环上。preventOverlap 为 true 时生效 */
97
+ public strictRadial: boolean = true;
98
+
99
+ /** 防止重叠步骤的最大迭代次数 */
100
+ public maxPreventOverlapIteration: number = 200;
101
+
102
+ public sortBy: string | undefined;
103
+
104
+ public sortStrength: number = 10;
105
+
106
+ public width: number | undefined;
107
+
108
+ public height: number | undefined;
109
+
110
+ private focusIndex: number | undefined;
111
+
112
+ private distances: Matrix[] | undefined;
113
+
114
+ private eIdealDistances: Matrix[] | undefined;
115
+
116
+ private weights: Matrix[] | undefined;
117
+
118
+ private radii: number[] | undefined;
119
+
120
+ public nodes: INode[] = [];
121
+
122
+ public edges: Edge[] = [];
123
+
124
+ public onLayoutEnd: () => void;
125
+
126
+ constructor(options?: RadialLayoutOptions) {
127
+ super();
128
+ this.updateCfg(options);
129
+ }
130
+
131
+ public getDefaultCfg() {
132
+ return {
133
+ maxIteration: 1000,
134
+ focusNode: null,
135
+ unitRadius: null,
136
+ linkDistance: 50,
137
+ preventOverlap: false,
138
+ nodeSize: undefined,
139
+ nodeSpacing: undefined,
140
+ strictRadial: true,
141
+ maxPreventOverlapIteration: 200,
142
+ sortBy: undefined,
143
+ sortStrength: 10
144
+ };
145
+ }
146
+
147
+ /**
148
+ * 执行布局
149
+ */
150
+ public execute() {
151
+ const self = this;
152
+ const nodes = self.nodes;
153
+ const edges = self.edges || [];
154
+ if (!nodes || nodes.length === 0) {
155
+ if (self.onLayoutEnd) self.onLayoutEnd();
156
+ return;
157
+ }
158
+
159
+ if (!self.width && typeof window !== "undefined") {
160
+ self.width = window.innerWidth;
161
+ }
162
+ if (!self.height && typeof window !== "undefined") {
163
+ self.height = window.innerHeight;
164
+ }
165
+ if (!self.center) {
166
+ self.center = [self.width! / 2, self.height! / 2];
167
+ }
168
+ const center = self.center;
169
+
170
+ if (nodes.length === 1) {
171
+ nodes[0].x = center[0];
172
+ nodes[0].y = center[1];
173
+ if (self.onLayoutEnd) self.onLayoutEnd();
174
+ return;
175
+ }
176
+ const linkDistance = self.linkDistance;
177
+ // layout
178
+ let focusNode: INode | null = null;
179
+ if (isString(self.focusNode)) {
180
+ let found = false;
181
+ for (let i = 0; i < nodes.length; i++) {
182
+ if (nodes[i].id === self.focusNode) {
183
+ focusNode = nodes[i];
184
+ self.focusNode = focusNode;
185
+ found = true;
186
+ i = nodes.length;
187
+ }
188
+ }
189
+ if (!found) {
190
+ focusNode = null;
191
+ }
192
+ } else {
193
+ focusNode = self.focusNode as INode;
194
+ }
195
+ // default focus node
196
+ if (!focusNode) {
197
+ focusNode = nodes[0];
198
+ self.focusNode = focusNode;
199
+ }
200
+ // the index of the focusNode in data
201
+ let focusIndex = getIndexById(nodes, focusNode.id);
202
+ if (focusIndex < 0) focusIndex = 0;
203
+ self.focusIndex = focusIndex;
204
+
205
+ // the graph-theoretic distance (shortest path distance) matrix
206
+ const adjMatrix = getAdjMatrix({ nodes, edges }, false);
207
+ const D = floydWarshall(adjMatrix);
208
+ const maxDistance = self.maxToFocus(D, focusIndex);
209
+ // replace first node in unconnected component to the circle at (maxDistance + 1)
210
+ self.handleInfinity(D, focusIndex, maxDistance + 1);
211
+ self.distances = D;
212
+
213
+ // the shortest path distance from each node to focusNode
214
+ const focusNodeD = D[focusIndex];
215
+ const width = self.width || 500;
216
+ const height = self.height || 500;
217
+ let semiWidth =
218
+ width - center[0] > center[0] ? center[0] : width - center[0];
219
+ let semiHeight =
220
+ height - center[1] > center[1] ? center[1] : height - center[1];
221
+ if (semiWidth === 0) {
222
+ semiWidth = width / 2;
223
+ }
224
+ if (semiHeight === 0) {
225
+ semiHeight = height / 2;
226
+ }
227
+ // the maxRadius of the graph
228
+ const maxRadius = semiHeight > semiWidth ? semiWidth : semiHeight;
229
+ const maxD = Math.max(...focusNodeD);
230
+ // the radius for each nodes away from focusNode
231
+ const radii: number[] = [];
232
+ focusNodeD.forEach((value, i) => {
233
+ if (!self.unitRadius) {
234
+ self.unitRadius = maxRadius / maxD;
235
+ }
236
+ radii[i] = value * self.unitRadius;
237
+ });
238
+ self.radii = radii;
239
+
240
+ const eIdealD = self.eIdealDisMatrix();
241
+ // const eIdealD = scaleMatrix(D, linkDistance);
242
+ self.eIdealDistances = eIdealD;
243
+ // the weight matrix, Wij = 1 / dij^(-2)
244
+ const W = getWeightMatrix(eIdealD);
245
+ self.weights = W;
246
+
247
+ // the initial positions from mds
248
+ const mds = new MDS({ linkDistance, distances: eIdealD });
249
+ let positions = mds.layout();
250
+ positions.forEach((p: PointTuple) => {
251
+ if (isNaN(p[0])) {
252
+ p[0] = Math.random() * linkDistance;
253
+ }
254
+ if (isNaN(p[1])) {
255
+ p[1] = Math.random() * linkDistance;
256
+ }
257
+ });
258
+ self.positions = positions;
259
+ positions.forEach((p: PointTuple, i: number) => {
260
+ nodes[i].x = p[0] + center[0];
261
+ nodes[i].y = p[1] + center[1];
262
+ });
263
+ // move the graph to origin, centered at focusNode
264
+ positions.forEach((p: PointTuple) => {
265
+ p[0] -= positions[focusIndex][0];
266
+ p[1] -= positions[focusIndex][1];
267
+ });
268
+ self.run();
269
+ const preventOverlap = self.preventOverlap;
270
+ const nodeSize = self.nodeSize;
271
+ let nodeSizeFunc;
272
+ const strictRadial = self.strictRadial;
273
+ // stagger the overlapped nodes
274
+ if (preventOverlap) {
275
+ const nodeSpacing = self.nodeSpacing;
276
+ let nodeSpacingFunc: Function;
277
+ if (isNumber(nodeSpacing)) {
278
+ nodeSpacingFunc = () => nodeSpacing;
279
+ } else if (isFunction(nodeSpacing)) {
280
+ nodeSpacingFunc = nodeSpacing;
281
+ } else {
282
+ nodeSpacingFunc = () => 0;
283
+ }
284
+ if (!nodeSize) {
285
+ nodeSizeFunc = (d: INode) => {
286
+ if (d.size) {
287
+ if (isArray(d.size)) {
288
+ const res = d.size[0] > d.size[1] ? d.size[0] : d.size[1];
289
+ return res + nodeSpacingFunc(d);
290
+ } if (isObject(d.size)) {
291
+ const res = d.size.width > d.size.height ? d.size.width : d.size.height;
292
+ return res + nodeSpacingFunc(d);
293
+ }
294
+ return d.size + nodeSpacingFunc(d);
295
+ }
296
+ return 10 + nodeSpacingFunc(d);
297
+ };
298
+ } else if (isArray(nodeSize)) {
299
+ nodeSizeFunc = (d: INode) => {
300
+ const res = nodeSize[0] > nodeSize[1] ? nodeSize[0] : nodeSize[1];
301
+ return res + nodeSpacingFunc(d);
302
+ };
303
+ } else {
304
+ nodeSizeFunc = (d: INode) => nodeSize + nodeSpacingFunc(d);
305
+ }
306
+ const nonoverlapForceParams: RadialNonoverlapForceParam = {
307
+ nodes,
308
+ nodeSizeFunc,
309
+ adjMatrix,
310
+ positions,
311
+ radii,
312
+ height,
313
+ width,
314
+ strictRadial,
315
+ focusID: focusIndex,
316
+ iterations: self.maxPreventOverlapIteration || 200,
317
+ k: positions.length / 4.5
318
+ };
319
+ const nonoverlapForce = new RadialNonoverlapForce(nonoverlapForceParams);
320
+ positions = nonoverlapForce.layout();
321
+ }
322
+ // move the graph to center
323
+ positions.forEach((p: PointTuple, i: number) => {
324
+ nodes[i].x = p[0] + center[0];
325
+ nodes[i].y = p[1] + center[1];
326
+ });
327
+
328
+ if (self.onLayoutEnd) self.onLayoutEnd();
329
+
330
+ return {
331
+ nodes,
332
+ edges
333
+ };
334
+ }
335
+
336
+ public run() {
337
+ const self = this;
338
+ const maxIteration = self.maxIteration;
339
+ const positions = self.positions || [];
340
+ const W = self.weights || [];
341
+ const eIdealDis = self.eIdealDistances || [];
342
+ const radii = self.radii || [];
343
+ for (let i = 0; i <= maxIteration; i++) {
344
+ const param = i / maxIteration;
345
+ self.oneIteration(param, positions, radii, eIdealDis, W);
346
+ }
347
+ }
348
+
349
+ private oneIteration(
350
+ param: number,
351
+ positions: PointTuple[],
352
+ radii: number[],
353
+ D: Matrix[],
354
+ W: Matrix[]
355
+ ) {
356
+ const self = this;
357
+ const vparam = 1 - param;
358
+ const focusIndex = self.focusIndex;
359
+ positions.forEach((v: PointTuple, i: number) => {
360
+ // v
361
+ const originDis = getEDistance(v, [0, 0]);
362
+ const reciODis = originDis === 0 ? 0 : 1 / originDis;
363
+ if (i === focusIndex) {
364
+ return;
365
+ }
366
+ let xMolecule = 0;
367
+ let yMolecule = 0;
368
+ let denominator = 0;
369
+ positions.forEach((u, j) => {
370
+ // u
371
+ if (i === j) {
372
+ return;
373
+ }
374
+ // the euclidean distance between v and u
375
+ const edis = getEDistance(v, u);
376
+ const reciEdis = edis === 0 ? 0 : 1 / edis;
377
+ const idealDis = D[j][i];
378
+ // same for x and y
379
+ denominator += W[i][j];
380
+ // x
381
+ xMolecule += W[i][j] * (u[0] + idealDis * (v[0] - u[0]) * reciEdis);
382
+ // y
383
+ yMolecule += W[i][j] * (u[1] + idealDis * (v[1] - u[1]) * reciEdis);
384
+ });
385
+ const reciR = radii[i] === 0 ? 0 : 1 / radii[i];
386
+ denominator *= vparam;
387
+ denominator += param * reciR * reciR;
388
+ // x
389
+ xMolecule *= vparam;
390
+ xMolecule += param * reciR * v[0] * reciODis;
391
+ v[0] = xMolecule / denominator;
392
+ // y
393
+ yMolecule *= vparam;
394
+ yMolecule += param * reciR * v[1] * reciODis;
395
+ v[1] = yMolecule / denominator;
396
+ });
397
+ }
398
+
399
+ private eIdealDisMatrix(): Matrix[] {
400
+ const self = this;
401
+ const nodes = self.nodes;
402
+ if (!nodes) return [];
403
+ const D = self.distances;
404
+ const linkDis = self.linkDistance;
405
+ const radii = self.radii || [];
406
+ const unitRadius = self.unitRadius || 50;
407
+ const result: Matrix[] = [];
408
+ if (D) {
409
+ D.forEach((row, i) => {
410
+ const newRow: Matrix = [];
411
+ row.forEach((v, j) => {
412
+ if (i === j) {
413
+ newRow.push(0);
414
+ } else if (radii[i] === radii[j]) {
415
+ // i and j are on the same circle
416
+ if (self.sortBy === "data") {
417
+ // sort the nodes on the same circle according to the ordering of the data
418
+ newRow.push(
419
+ (v * (Math.abs(i - j) * self.sortStrength)) /
420
+ (radii[i] / unitRadius)
421
+ );
422
+ } else if (self.sortBy) {
423
+ // sort the nodes on the same circle according to the attributes
424
+ let iValue: number | string =
425
+ ((nodes[i] as any)[self.sortBy] as number | string) || 0;
426
+ let jValue: number | string =
427
+ ((nodes[j] as any)[self.sortBy] as number | string) || 0;
428
+ if (isString(iValue)) {
429
+ iValue = iValue.charCodeAt(0);
430
+ }
431
+ if (isString(jValue)) {
432
+ jValue = jValue.charCodeAt(0);
433
+ }
434
+ newRow.push(
435
+ (v * (Math.abs(iValue - jValue) * self.sortStrength)) /
436
+ (radii[i] / unitRadius)
437
+ );
438
+ } else {
439
+ newRow.push((v * linkDis) / (radii[i] / unitRadius));
440
+ }
441
+ } else {
442
+ // i and j are on different circle
443
+ // i and j are on different circle
444
+ const link = (linkDis + unitRadius) / 2;
445
+ newRow.push(v * link);
446
+ }
447
+ });
448
+ result.push(newRow);
449
+ });
450
+ }
451
+ return result;
452
+ }
453
+
454
+ private handleInfinity(matrix: Matrix[], focusIndex: number, step: number) {
455
+ const length = matrix.length;
456
+ // 遍历 matrix 中遍历 focus 对应行
457
+ for (let i = 0; i < length; i++) {
458
+ // matrix 关注点对应行的 Inf 项
459
+ if (matrix[focusIndex][i] === Infinity) {
460
+ matrix[focusIndex][i] = step;
461
+ matrix[i][focusIndex] = step;
462
+ // 遍历 matrix 中的 i 行,i 行中非 Inf 项若在 focus 行为 Inf,则替换 focus 行的那个 Inf
463
+ for (let j = 0; j < length; j++) {
464
+ if (matrix[i][j] !== Infinity && matrix[focusIndex][j] === Infinity) {
465
+ matrix[focusIndex][j] = step + matrix[i][j];
466
+ matrix[j][focusIndex] = step + matrix[i][j];
467
+ }
468
+ }
469
+ }
470
+ }
471
+ // 处理其他行的 Inf。根据该行对应点与 focus 距离以及 Inf 项点 与 focus 距离,决定替换值
472
+ for (let i = 0; i < length; i++) {
473
+ if (i === focusIndex) {
474
+ continue;
475
+ }
476
+ for (let j = 0; j < length; j++) {
477
+ if (matrix[i][j] === Infinity) {
478
+ let minus = Math.abs(matrix[focusIndex][i] - matrix[focusIndex][j]);
479
+ minus = minus === 0 ? 1 : minus;
480
+ matrix[i][j] = minus;
481
+ }
482
+ }
483
+ }
484
+ }
485
+
486
+ private maxToFocus(matrix: Matrix[], focusIndex: number): number {
487
+ let max = 0;
488
+ for (let i = 0; i < matrix[focusIndex].length; i++) {
489
+ if (matrix[focusIndex][i] === Infinity) {
490
+ continue;
491
+ }
492
+ max = matrix[focusIndex][i] > max ? matrix[focusIndex][i] : max;
493
+ }
494
+ return max;
495
+ }
496
+
497
+ public getType() {
498
+ return "radial";
499
+ }
500
+ }
@@ -0,0 +1,189 @@
1
+ import { Matrix, PointTuple, Point } from '../types';
2
+
3
+ const SPEED_DIVISOR = 800;
4
+
5
+ export type RadialNonoverlapForceParam = {
6
+ positions: PointTuple[];
7
+ adjMatrix: Matrix[];
8
+ focusID: number;
9
+ radii: number[];
10
+ iterations?: number;
11
+ height?: number;
12
+ width?: number;
13
+ speed?: number;
14
+ gravity?: number;
15
+ nodeSizeFunc: (node: any) => number;
16
+ k: number;
17
+ strictRadial: boolean;
18
+ nodes: any[];
19
+ };
20
+
21
+ export default class RadialNonoverlapForce {
22
+ /** node positions */
23
+ public positions: PointTuple[];
24
+
25
+ /** adjacency matrix */
26
+ public adjMatrix: Matrix[];
27
+
28
+ /** focus node */
29
+ public focusID: number;
30
+
31
+ /** radii */
32
+ public radii: number[];
33
+
34
+ /** the number of iterations */
35
+ public iterations: number;
36
+
37
+ /** the height of the canvas */
38
+ public height: number;
39
+
40
+ /** the width of the canvas */
41
+ public width: number;
42
+
43
+ /** the moving speed */
44
+ public speed: number;
45
+
46
+ /** the gravity */
47
+ public gravity: number;
48
+
49
+ /** the node size */
50
+ public nodeSizeFunc: (node: any) => number;
51
+
52
+ /** the strength of forces */
53
+ public k: number;
54
+
55
+ /** if each circle can be separated into subcircles to avoid overlappings */
56
+ public strictRadial: boolean;
57
+
58
+ /** the nodes data */
59
+ public nodes: any[];
60
+
61
+ private maxDisplace: number | undefined;
62
+
63
+ private disp: Point[] = [];
64
+
65
+ constructor(params: RadialNonoverlapForceParam) {
66
+ this.positions = params.positions;
67
+ this.adjMatrix = params.adjMatrix;
68
+ this.focusID = params.focusID;
69
+ this.radii = params.radii;
70
+ this.iterations = params.iterations || 10;
71
+ this.height = params.height || 10;
72
+ this.width = params.width || 10;
73
+ this.speed = params.speed || 100;
74
+ this.gravity = params.gravity || 10;
75
+ this.nodeSizeFunc = params.nodeSizeFunc;
76
+ this.k = params.k || 5;
77
+ this.strictRadial = params.strictRadial;
78
+ this.nodes = params.nodes;
79
+ }
80
+
81
+ public layout(): PointTuple[] {
82
+ const self = this;
83
+ const positions = self.positions;
84
+ const disp: Point[] = [];
85
+ const iterations = self.iterations;
86
+ const maxDisplace = self.width / 10;
87
+ self.maxDisplace = maxDisplace;
88
+ self.disp = disp;
89
+ for (let i = 0; i < iterations; i++) {
90
+ positions.forEach((_, k) => {
91
+ disp[k] = { x: 0, y: 0 };
92
+ });
93
+ // 给重叠的节点增加斥力
94
+ self.getRepulsion();
95
+ self.updatePositions();
96
+ }
97
+ return positions;
98
+ }
99
+
100
+ private getRepulsion() {
101
+ const self = this;
102
+ const positions = self.positions;
103
+ const nodes = self.nodes;
104
+ const disp = self.disp;
105
+ const k = self.k;
106
+ const radii = self.radii || [];
107
+
108
+ positions.forEach((v: PointTuple, i: number) => {
109
+ disp[i] = { x: 0, y: 0 };
110
+ positions.forEach((u: PointTuple, j: number) => {
111
+ if (i === j) {
112
+ return;
113
+ }
114
+ // v and u are not on the same circle, return
115
+ if (radii[i] !== radii[j]) {
116
+ return;
117
+ }
118
+ let vecx = v[0] - u[0];
119
+ let vecy = v[1] - u[1];
120
+ let vecLength = Math.sqrt(vecx * vecx + vecy * vecy);
121
+ if (vecLength === 0) {
122
+ vecLength = 1;
123
+ const sign = i > j ? 1 : -1;
124
+ vecx = 0.01 * sign;
125
+ vecy = 0.01 * sign;
126
+ }
127
+ // these two nodes overlap
128
+ if (vecLength < self.nodeSizeFunc(nodes[i]) / 2 + self.nodeSizeFunc(nodes[j]) / 2) {
129
+ const common = (k * k) / vecLength;
130
+ disp[i].x += (vecx / vecLength) * common;
131
+ disp[i].y += (vecy / vecLength) * common;
132
+ }
133
+ });
134
+ });
135
+ }
136
+
137
+ private updatePositions() {
138
+ const self = this;
139
+ const positions = self.positions;
140
+ const disp = self.disp;
141
+ const speed = self.speed;
142
+ const strictRadial = self.strictRadial;
143
+ const f = self.focusID;
144
+ const maxDisplace = self.maxDisplace || self.width / 10;
145
+
146
+ if (strictRadial) {
147
+ disp.forEach((di, i) => {
148
+ const vx = positions[i][0] - positions[f][0];
149
+ const vy = positions[i][1] - positions[f][1];
150
+ const vLength = Math.sqrt(vx * vx + vy * vy);
151
+ let vpx = vy / vLength;
152
+ let vpy = -vx / vLength;
153
+ const diLength = Math.sqrt(di.x * di.x + di.y * di.y);
154
+ let alpha = Math.acos((vpx * di.x + vpy * di.y) / diLength);
155
+ if (alpha > Math.PI / 2) {
156
+ alpha -= Math.PI / 2;
157
+ vpx *= -1;
158
+ vpy *= -1;
159
+ }
160
+ const tdispLength = Math.cos(alpha) * diLength;
161
+ di.x = vpx * tdispLength;
162
+ di.y = vpy * tdispLength;
163
+ });
164
+ }
165
+
166
+ // move
167
+ const radii = self.radii;
168
+ positions.forEach((n, i) => {
169
+ if (i === f) {
170
+ return;
171
+ }
172
+ const distLength = Math.sqrt(disp[i].x * disp[i].x + disp[i].y * disp[i].y);
173
+ if (distLength > 0 && i !== f) {
174
+ const limitedDist = Math.min(maxDisplace * (speed / SPEED_DIVISOR), distLength);
175
+ n[0] += (disp[i].x / distLength) * limitedDist;
176
+ n[1] += (disp[i].y / distLength) * limitedDist;
177
+ if (strictRadial) {
178
+ let vx = n[0] - positions[f][0];
179
+ let vy = n[1] - positions[f][1];
180
+ const nfDis = Math.sqrt(vx * vx + vy * vy);
181
+ vx = (vx / nfDis) * radii[i];
182
+ vy = (vy / nfDis) * radii[i];
183
+ n[0] = positions[f][0] + vx;
184
+ n[1] = positions[f][1] + vy;
185
+ }
186
+ }
187
+ });
188
+ }
189
+ }