@graphty/layout 1.2.9 → 1.4.0

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 (237) hide show
  1. package/README.md +400 -409
  2. package/dist/layout.js +884 -817
  3. package/dist/layout.js.map +1 -1
  4. package/dist/src/algorithms/index.d.ts +3 -2
  5. package/dist/src/algorithms/index.d.ts.map +1 -0
  6. package/dist/src/algorithms/index.js +2 -2
  7. package/dist/src/algorithms/index.js.map +1 -1
  8. package/dist/src/algorithms/optimization/index.d.ts +5 -4
  9. package/dist/src/algorithms/optimization/index.d.ts.map +1 -0
  10. package/dist/src/algorithms/optimization/index.js +3 -3
  11. package/dist/src/algorithms/optimization/index.js.map +1 -1
  12. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts +3 -5
  13. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts.map +1 -0
  14. package/dist/src/algorithms/optimization/kamada-kawai-solver.js +6 -9
  15. package/dist/src/algorithms/optimization/kamada-kawai-solver.js.map +1 -1
  16. package/dist/src/algorithms/optimization/lbfgs.d.ts +3 -3
  17. package/dist/src/algorithms/optimization/lbfgs.d.ts.map +1 -0
  18. package/dist/src/algorithms/optimization/lbfgs.js +5 -7
  19. package/dist/src/algorithms/optimization/lbfgs.js.map +1 -1
  20. package/dist/src/algorithms/optimization/line-search.d.ts +1 -1
  21. package/dist/src/algorithms/optimization/line-search.d.ts.map +1 -0
  22. package/dist/src/algorithms/optimization/line-search.js +0 -1
  23. package/dist/src/algorithms/optimization/line-search.js.map +1 -1
  24. package/dist/src/algorithms/optimization/types.d.ts +2 -1
  25. package/dist/src/algorithms/optimization/types.d.ts.map +1 -0
  26. package/dist/src/algorithms/planarity/check.d.ts +5 -4
  27. package/dist/src/algorithms/planarity/check.d.ts.map +1 -0
  28. package/dist/src/algorithms/planarity/check.js +6 -6
  29. package/dist/src/algorithms/planarity/check.js.map +1 -1
  30. package/dist/src/algorithms/planarity/embedding.d.ts +5 -6
  31. package/dist/src/algorithms/planarity/embedding.d.ts.map +1 -0
  32. package/dist/src/algorithms/planarity/embedding.js +15 -14
  33. package/dist/src/algorithms/planarity/embedding.js.map +1 -1
  34. package/dist/src/algorithms/planarity/index.d.ts +5 -4
  35. package/dist/src/algorithms/planarity/index.d.ts.map +1 -0
  36. package/dist/src/algorithms/planarity/index.js +4 -4
  37. package/dist/src/algorithms/planarity/index.js.map +1 -1
  38. package/dist/src/algorithms/planarity/lr-test.d.ts +3 -2
  39. package/dist/src/algorithms/planarity/lr-test.d.ts.map +1 -0
  40. package/dist/src/algorithms/planarity/lr-test.js +2 -2
  41. package/dist/src/algorithms/planarity/lr-test.js.map +1 -1
  42. package/dist/src/algorithms/planarity/special-graphs.d.ts +2 -4
  43. package/dist/src/algorithms/planarity/special-graphs.d.ts.map +1 -0
  44. package/dist/src/algorithms/planarity/special-graphs.js +18 -15
  45. package/dist/src/algorithms/planarity/special-graphs.js.map +1 -1
  46. package/dist/src/generators/basic.d.ts +2 -1
  47. package/dist/src/generators/basic.d.ts.map +1 -0
  48. package/dist/src/generators/basic.js +4 -4
  49. package/dist/src/generators/basic.js.map +1 -1
  50. package/dist/src/generators/bipartite.d.ts +2 -1
  51. package/dist/src/generators/bipartite.d.ts.map +1 -0
  52. package/dist/src/generators/bipartite.js +2 -2
  53. package/dist/src/generators/bipartite.js.map +1 -1
  54. package/dist/src/generators/grid.d.ts +2 -1
  55. package/dist/src/generators/grid.d.ts.map +1 -0
  56. package/dist/src/generators/grid.js +1 -1
  57. package/dist/src/generators/grid.js.map +1 -1
  58. package/dist/src/generators/index.d.ts +6 -5
  59. package/dist/src/generators/index.d.ts.map +1 -0
  60. package/dist/src/generators/index.js +5 -5
  61. package/dist/src/generators/index.js.map +1 -1
  62. package/dist/src/generators/random.d.ts +2 -1
  63. package/dist/src/generators/random.d.ts.map +1 -0
  64. package/dist/src/generators/random.js +2 -2
  65. package/dist/src/generators/random.js.map +1 -1
  66. package/dist/src/generators/scale-free.d.ts +2 -1
  67. package/dist/src/generators/scale-free.d.ts.map +1 -0
  68. package/dist/src/generators/scale-free.js +4 -4
  69. package/dist/src/generators/scale-free.js.map +1 -1
  70. package/dist/src/index.d.ts +5 -4
  71. package/dist/src/index.d.ts.map +1 -0
  72. package/dist/src/index.js +4 -4
  73. package/dist/src/layouts/basic/index.d.ts +2 -1
  74. package/dist/src/layouts/basic/index.d.ts.map +1 -0
  75. package/dist/src/layouts/basic/index.js +1 -1
  76. package/dist/src/layouts/basic/random.d.ts +2 -2
  77. package/dist/src/layouts/basic/random.d.ts.map +1 -0
  78. package/dist/src/layouts/basic/random.js +4 -5
  79. package/dist/src/layouts/basic/random.js.map +1 -1
  80. package/dist/src/layouts/force-directed/arf.d.ts +2 -2
  81. package/dist/src/layouts/force-directed/arf.d.ts.map +1 -0
  82. package/dist/src/layouts/force-directed/arf.js +13 -8
  83. package/dist/src/layouts/force-directed/arf.js.map +1 -1
  84. package/dist/src/layouts/force-directed/forceatlas2.d.ts +4 -4
  85. package/dist/src/layouts/force-directed/forceatlas2.d.ts.map +1 -0
  86. package/dist/src/layouts/force-directed/forceatlas2.js +83 -51
  87. package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -1
  88. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +12 -12
  89. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts.map +1 -0
  90. package/dist/src/layouts/force-directed/fruchterman-reingold.js +18 -18
  91. package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -1
  92. package/dist/src/layouts/force-directed/index.d.ts +6 -5
  93. package/dist/src/layouts/force-directed/index.d.ts.map +1 -0
  94. package/dist/src/layouts/force-directed/index.js +5 -5
  95. package/dist/src/layouts/force-directed/index.js.map +1 -1
  96. package/dist/src/layouts/force-directed/kamada-kawai.d.ts +3 -3
  97. package/dist/src/layouts/force-directed/kamada-kawai.d.ts.map +1 -0
  98. package/dist/src/layouts/force-directed/kamada-kawai.js +13 -11
  99. package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -1
  100. package/dist/src/layouts/force-directed/spring.d.ts +12 -12
  101. package/dist/src/layouts/force-directed/spring.d.ts.map +1 -0
  102. package/dist/src/layouts/force-directed/spring.js +11 -12
  103. package/dist/src/layouts/force-directed/spring.js.map +1 -1
  104. package/dist/src/layouts/geometric/circular.d.ts +2 -2
  105. package/dist/src/layouts/geometric/circular.d.ts.map +1 -0
  106. package/dist/src/layouts/geometric/circular.js +11 -10
  107. package/dist/src/layouts/geometric/circular.js.map +1 -1
  108. package/dist/src/layouts/geometric/index.d.ts +4 -3
  109. package/dist/src/layouts/geometric/index.d.ts.map +1 -0
  110. package/dist/src/layouts/geometric/index.js +3 -3
  111. package/dist/src/layouts/geometric/shell.d.ts +2 -2
  112. package/dist/src/layouts/geometric/shell.d.ts.map +1 -0
  113. package/dist/src/layouts/geometric/shell.js +6 -6
  114. package/dist/src/layouts/geometric/shell.js.map +1 -1
  115. package/dist/src/layouts/geometric/spiral.d.ts +2 -2
  116. package/dist/src/layouts/geometric/spiral.d.ts.map +1 -0
  117. package/dist/src/layouts/geometric/spiral.js +6 -10
  118. package/dist/src/layouts/geometric/spiral.js.map +1 -1
  119. package/dist/src/layouts/hierarchical/bfs.d.ts +3 -3
  120. package/dist/src/layouts/hierarchical/bfs.d.ts.map +1 -0
  121. package/dist/src/layouts/hierarchical/bfs.js +6 -7
  122. package/dist/src/layouts/hierarchical/bfs.js.map +1 -1
  123. package/dist/src/layouts/hierarchical/bipartite.d.ts +3 -3
  124. package/dist/src/layouts/hierarchical/bipartite.d.ts.map +1 -0
  125. package/dist/src/layouts/hierarchical/bipartite.js +9 -10
  126. package/dist/src/layouts/hierarchical/bipartite.js.map +1 -1
  127. package/dist/src/layouts/hierarchical/index.d.ts +4 -3
  128. package/dist/src/layouts/hierarchical/index.d.ts.map +1 -0
  129. package/dist/src/layouts/hierarchical/index.js +3 -3
  130. package/dist/src/layouts/hierarchical/index.js.map +1 -1
  131. package/dist/src/layouts/hierarchical/multipartite.d.ts +3 -3
  132. package/dist/src/layouts/hierarchical/multipartite.d.ts.map +1 -0
  133. package/dist/src/layouts/hierarchical/multipartite.js +9 -10
  134. package/dist/src/layouts/hierarchical/multipartite.js.map +1 -1
  135. package/dist/src/layouts/index.d.ts +6 -5
  136. package/dist/src/layouts/index.d.ts.map +1 -0
  137. package/dist/src/layouts/index.js +5 -5
  138. package/dist/src/layouts/index.js.map +1 -1
  139. package/dist/src/layouts/specialized/index.d.ts +3 -2
  140. package/dist/src/layouts/specialized/index.d.ts.map +1 -0
  141. package/dist/src/layouts/specialized/index.js +2 -2
  142. package/dist/src/layouts/specialized/index.js.map +1 -1
  143. package/dist/src/layouts/specialized/planar.d.ts +2 -2
  144. package/dist/src/layouts/specialized/planar.d.ts.map +1 -0
  145. package/dist/src/layouts/specialized/planar.js +6 -7
  146. package/dist/src/layouts/specialized/planar.js.map +1 -1
  147. package/dist/src/layouts/specialized/spectral.d.ts +2 -2
  148. package/dist/src/layouts/specialized/spectral.d.ts.map +1 -0
  149. package/dist/src/layouts/specialized/spectral.js +31 -21
  150. package/dist/src/layouts/specialized/spectral.js.map +1 -1
  151. package/dist/src/types/embedding.d.ts +2 -1
  152. package/dist/src/types/embedding.d.ts.map +1 -0
  153. package/dist/src/types/graph.d.ts +2 -1
  154. package/dist/src/types/graph.d.ts.map +1 -0
  155. package/dist/src/types/index.d.ts +4 -3
  156. package/dist/src/types/index.d.ts.map +1 -0
  157. package/dist/src/types/index.js +3 -3
  158. package/dist/src/types/index.js.map +1 -1
  159. package/dist/src/types/layout.d.ts +2 -1
  160. package/dist/src/types/layout.d.ts.map +1 -0
  161. package/dist/src/utils/graph.d.ts +2 -5
  162. package/dist/src/utils/graph.d.ts.map +1 -0
  163. package/dist/src/utils/graph.js +4 -6
  164. package/dist/src/utils/graph.js.map +1 -1
  165. package/dist/src/utils/index.d.ts +6 -5
  166. package/dist/src/utils/index.d.ts.map +1 -0
  167. package/dist/src/utils/index.js +5 -5
  168. package/dist/src/utils/index.js.map +1 -1
  169. package/dist/src/utils/numpy.d.ts +5 -4
  170. package/dist/src/utils/numpy.d.ts.map +1 -0
  171. package/dist/src/utils/numpy.js +22 -16
  172. package/dist/src/utils/numpy.js.map +1 -1
  173. package/dist/src/utils/params.d.ts +2 -2
  174. package/dist/src/utils/params.d.ts.map +1 -0
  175. package/dist/src/utils/params.js +0 -1
  176. package/dist/src/utils/params.js.map +1 -1
  177. package/dist/src/utils/random.d.ts +18 -0
  178. package/dist/src/utils/random.d.ts.map +1 -0
  179. package/dist/src/utils/random.js +18 -1
  180. package/dist/src/utils/random.js.map +1 -1
  181. package/dist/src/utils/rescale.d.ts +2 -3
  182. package/dist/src/utils/rescale.d.ts.map +1 -0
  183. package/dist/src/utils/rescale.js +12 -10
  184. package/dist/src/utils/rescale.js.map +1 -1
  185. package/dist/tsconfig.tsbuildinfo +1 -0
  186. package/package.json +35 -34
  187. package/src/algorithms/index.ts +2 -2
  188. package/src/algorithms/optimization/index.ts +4 -8
  189. package/src/algorithms/optimization/kamada-kawai-solver.ts +170 -179
  190. package/src/algorithms/optimization/lbfgs.ts +42 -49
  191. package/src/algorithms/optimization/line-search.ts +27 -28
  192. package/src/algorithms/optimization/types.ts +2 -2
  193. package/src/algorithms/planarity/check.ts +23 -23
  194. package/src/algorithms/planarity/embedding.ts +160 -171
  195. package/src/algorithms/planarity/index.ts +4 -8
  196. package/src/algorithms/planarity/lr-test.ts +46 -46
  197. package/src/algorithms/planarity/special-graphs.ts +73 -83
  198. package/src/generators/basic.ts +57 -57
  199. package/src/generators/bipartite.ts +37 -31
  200. package/src/generators/grid.ts +28 -28
  201. package/src/generators/index.ts +5 -11
  202. package/src/generators/random.ts +26 -25
  203. package/src/generators/scale-free.ts +56 -55
  204. package/src/index.ts +5 -5
  205. package/src/layouts/basic/index.ts +1 -1
  206. package/src/layouts/basic/random.ts +20 -16
  207. package/src/layouts/force-directed/arf.ts +107 -105
  208. package/src/layouts/force-directed/forceatlas2.ts +374 -352
  209. package/src/layouts/force-directed/fruchterman-reingold.ts +133 -134
  210. package/src/layouts/force-directed/index.ts +5 -5
  211. package/src/layouts/force-directed/kamada-kawai.ts +77 -80
  212. package/src/layouts/force-directed/spring.ts +24 -25
  213. package/src/layouts/geometric/circular.ts +63 -57
  214. package/src/layouts/geometric/index.ts +3 -3
  215. package/src/layouts/geometric/shell.ts +58 -53
  216. package/src/layouts/geometric/spiral.ts +60 -64
  217. package/src/layouts/hierarchical/bfs.ts +58 -59
  218. package/src/layouts/hierarchical/bipartite.ts +62 -63
  219. package/src/layouts/hierarchical/index.ts +3 -3
  220. package/src/layouts/hierarchical/multipartite.ts +62 -63
  221. package/src/layouts/index.ts +5 -5
  222. package/src/layouts/specialized/index.ts +2 -2
  223. package/src/layouts/specialized/planar.ts +41 -42
  224. package/src/layouts/specialized/spectral.ts +110 -99
  225. package/src/types/embedding.ts +5 -5
  226. package/src/types/graph.ts +4 -4
  227. package/src/types/index.ts +3 -3
  228. package/src/types/layout.ts +2 -2
  229. package/src/utils/graph.ts +31 -35
  230. package/src/utils/index.ts +5 -5
  231. package/src/utils/numpy.ts +96 -92
  232. package/src/utils/params.ts +14 -11
  233. package/src/utils/random.ts +57 -40
  234. package/src/utils/rescale.ts +169 -174
  235. package/dist/vitest.config.d.ts +0 -2
  236. package/dist/vitest.config.js +0 -30
  237. package/dist/vitest.config.js.map +0 -1
@@ -2,163 +2,162 @@
2
2
  * Fruchterman-Reingold force-directed layout algorithm
3
3
  */
4
4
 
5
- import { Graph, Node, Edge, PositionMap } from '../../types';
6
- import { _processParams } from '../../utils/params';
7
- import { getNodesFromGraph, getEdgesFromGraph } from '../../utils/graph';
8
- import { RandomNumberGenerator } from '../../utils/random';
9
- import { rescaleLayout } from '../../utils/rescale';
5
+ import type { Graph, Node, PositionMap } from "../../types";
6
+ import { getEdgesFromGraph,getNodesFromGraph } from "../../utils/graph";
7
+ import { _processParams } from "../../utils/params";
8
+ import { RandomNumberGenerator } from "../../utils/random";
9
+ import { rescaleLayout } from "../../utils/rescale";
10
10
 
11
11
  /**
12
12
  * Position nodes using Fruchterman-Reingold force-directed algorithm.
13
- *
14
- * @param {Object} G - Graph or list of nodes
15
- * @param {number} k - Optimal distance between nodes
16
- * @param {Object} pos - Initial positions for nodes
17
- * @param {Array} fixed - Nodes to keep fixed at initial position
18
- * @param {number} iterations - Maximum number of iterations
19
- * @param {number} scale - Scale factor for positions
20
- * @param {Array|null} center - Coordinate pair around which to center the layout
21
- * @param {number} dim - Dimension of layout
22
- * @param {number} seed - Random seed for initial positions
23
- * @returns {Object} Positions dictionary keyed by node
13
+ * @param G - Graph or list of nodes
14
+ * @param k - Optimal distance between nodes
15
+ * @param pos - Initial positions for nodes
16
+ * @param fixed - Nodes to keep fixed at initial position
17
+ * @param iterations - Maximum number of iterations
18
+ * @param scale - Scale factor for positions
19
+ * @param center - Coordinate pair around which to center the layout
20
+ * @param dim - Dimension of layout
21
+ * @param seed - Random seed for initial positions
22
+ * @returns Positions dictionary keyed by node
24
23
  */
25
24
  export function fruchtermanReingoldLayout(
26
- G: Graph,
27
- k: number | null = null,
28
- pos: PositionMap | null = null,
29
- fixed: Node[] | null = null,
30
- iterations: number = 50,
31
- scale: number = 1,
32
- center: number[] | null = null,
33
- dim: number = 2,
34
- seed: number | null = null
25
+ G: Graph,
26
+ k: number | null = null,
27
+ pos: PositionMap | null = null,
28
+ fixed: Node[] | null = null,
29
+ iterations: number = 50,
30
+ scale: number = 1,
31
+ center: number[] | null = null,
32
+ dim: number = 2,
33
+ seed: number | null = null,
35
34
  ): PositionMap {
36
- const processed = _processParams(G, center, dim);
37
- let graph = processed.G;
38
- center = processed.center;
39
-
40
- const nodes = getNodesFromGraph(graph);
41
- const edges = getEdgesFromGraph(graph);
42
-
43
- if (nodes.length === 0) {
44
- return {};
45
- }
46
-
47
- if (nodes.length === 1) {
48
- const singlePos: PositionMap = {};
49
- singlePos[nodes[0]] = center;
50
- return singlePos;
51
- }
52
-
53
- // Set up initial positions
54
- let positions: PositionMap = {};
55
- if (pos) {
56
- // Use provided positions
57
- for (const node of nodes) {
58
- if (pos[node]) {
59
- positions[node] = [...pos[node]];
60
- } else {
61
- const rng = new RandomNumberGenerator(seed ?? undefined);
62
- positions[node] = rng.rand(dim) as number[];
63
- }
35
+ const processed = _processParams(G, center, dim);
36
+ const graph = processed.G;
37
+ ({ center } = processed);
38
+
39
+ const nodes = getNodesFromGraph(graph);
40
+ const edges = getEdgesFromGraph(graph);
41
+
42
+ if (nodes.length === 0) {
43
+ return {};
44
+ }
45
+
46
+ if (nodes.length === 1) {
47
+ const singlePos: PositionMap = {};
48
+ singlePos[nodes[0]] = center;
49
+ return singlePos;
64
50
  }
65
- } else {
66
- // Random initial positions
67
- const rng = new RandomNumberGenerator(seed ?? undefined);
68
- for (const node of nodes) {
69
- positions[node] = rng.rand(dim) as number[];
51
+
52
+ // Set up initial positions
53
+ let positions: PositionMap = {};
54
+ if (pos) {
55
+ // Use provided positions
56
+ for (const node of nodes) {
57
+ if (pos[node]) {
58
+ positions[node] = [...pos[node]];
59
+ } else {
60
+ const rng = new RandomNumberGenerator(seed ?? undefined);
61
+ positions[node] = rng.rand(dim) as number[];
62
+ }
63
+ }
64
+ } else {
65
+ // Random initial positions
66
+ const rng = new RandomNumberGenerator(seed ?? undefined);
67
+ for (const node of nodes) {
68
+ positions[node] = rng.rand(dim) as number[];
69
+ }
70
70
  }
71
- }
72
-
73
- // Set up fixed nodes
74
- const fixedNodes = new Set(fixed || []);
75
-
76
- // Optimal distance between nodes
77
- if (!k) {
78
- k = 1.0 / Math.sqrt(nodes.length);
79
- }
80
-
81
- // Initialize temperature
82
- let t = 0.1;
83
- // Calculate temperature reduction
84
- const dt = t / (iterations + 1);
85
-
86
- // Simple cooling schedule
87
- for (let i = 0; i < iterations; i++) {
88
- // Calculate repulsive forces
89
- const displacement: Record<Node, number[]> = {};
90
- for (const node of nodes) {
91
- displacement[node] = Array(dim).fill(0);
71
+
72
+ // Set up fixed nodes
73
+ const fixedNodes = new Set(fixed || []);
74
+
75
+ // Optimal distance between nodes
76
+ if (!k) {
77
+ k = 1.0 / Math.sqrt(nodes.length);
92
78
  }
93
79
 
94
- // Repulsive forces between nodes
95
- for (let v1i = 0; v1i < nodes.length; v1i++) {
96
- const v1 = nodes[v1i];
97
- for (let v2i = v1i + 1; v2i < nodes.length; v2i++) {
98
- const v2 = nodes[v2i];
80
+ // Initialize temperature
81
+ let t = 0.1;
82
+ // Calculate temperature reduction
83
+ const dt = t / (iterations + 1);
84
+
85
+ // Simple cooling schedule
86
+ for (let i = 0; i < iterations; i++) {
87
+ // Calculate repulsive forces
88
+ const displacement: Record<Node, number[]> = {};
89
+ for (const node of nodes) {
90
+ displacement[node] = Array(dim).fill(0);
91
+ }
92
+
93
+ // Repulsive forces between nodes
94
+ for (let v1i = 0; v1i < nodes.length; v1i++) {
95
+ const v1 = nodes[v1i];
96
+ for (let v2i = v1i + 1; v2i < nodes.length; v2i++) {
97
+ const v2 = nodes[v2i];
99
98
 
100
- // Difference vector
101
- const delta = positions[v1].map((p, i) => p - positions[v2][i]);
99
+ // Difference vector
100
+ const delta = positions[v1].map((p, i) => p - positions[v2][i]);
102
101
 
103
- // Distance
104
- const distance = Math.sqrt(delta.reduce((sum, d) => sum + d * d, 0)) || 0.1;
102
+ // Distance
103
+ const distance = Math.sqrt(delta.reduce((sum, d) => sum + d * d, 0)) || 0.1;
105
104
 
106
- // Force
107
- const force = (k * k) / distance;
105
+ // Force
106
+ const force = (k * k) / distance;
108
107
 
109
- // Add force to displacement
110
- for (let j = 0; j < dim; j++) {
111
- const direction = delta[j] / distance;
112
- displacement[v1][j] += direction * force;
113
- displacement[v2][j] -= direction * force;
108
+ // Add force to displacement
109
+ for (let j = 0; j < dim; j++) {
110
+ const direction = delta[j] / distance;
111
+ displacement[v1][j] += direction * force;
112
+ displacement[v2][j] -= direction * force;
113
+ }
114
+ }
114
115
  }
115
- }
116
- }
117
116
 
118
- // Attractive forces between connected nodes
119
- for (const [source, target] of edges) {
120
- // Difference vector
121
- const delta = positions[source].map((p, i) => p - positions[target][i]);
117
+ // Attractive forces between connected nodes
118
+ for (const [source, target] of edges) {
119
+ // Difference vector
120
+ const delta = positions[source].map((p, i) => p - positions[target][i]);
122
121
 
123
- // Distance
124
- const distance = Math.sqrt(delta.reduce((sum, d) => sum + d * d, 0)) || 0.1;
122
+ // Distance
123
+ const distance = Math.sqrt(delta.reduce((sum, d) => sum + d * d, 0)) || 0.1;
125
124
 
126
- // Force
127
- const force = (distance * distance) / k;
125
+ // Force
126
+ const force = (distance * distance) / k;
128
127
 
129
- // Add force to displacement
130
- for (let j = 0; j < dim; j++) {
131
- const direction = delta[j] / distance;
132
- displacement[source][j] -= direction * force;
133
- displacement[target][j] += direction * force;
134
- }
135
- }
128
+ // Add force to displacement
129
+ for (let j = 0; j < dim; j++) {
130
+ const direction = delta[j] / distance;
131
+ displacement[source][j] -= direction * force;
132
+ displacement[target][j] += direction * force;
133
+ }
134
+ }
136
135
 
137
- // Update positions
138
- for (const node of nodes) {
139
- if (fixedNodes.has(node)) continue;
136
+ // Update positions
137
+ for (const node of nodes) {
138
+ if (fixedNodes.has(node)) {continue;}
140
139
 
141
- // Calculate displacement magnitude
142
- const magnitude = Math.sqrt(displacement[node].reduce((sum, d) => sum + d * d, 0));
140
+ // Calculate displacement magnitude
141
+ const magnitude = Math.sqrt(displacement[node].reduce((sum, d) => sum + d * d, 0));
143
142
 
144
- // Limit maximum displacement by temperature
145
- const limitedMagnitude = Math.min(magnitude, t);
143
+ // Limit maximum displacement by temperature
144
+ const limitedMagnitude = Math.min(magnitude, t);
146
145
 
147
- // Update position
148
- for (let j = 0; j < dim; j++) {
149
- const direction = magnitude === 0 ? 0 : displacement[node][j] / magnitude;
150
- positions[node][j] += direction * limitedMagnitude;
151
- }
152
- }
146
+ // Update position
147
+ for (let j = 0; j < dim; j++) {
148
+ const direction = magnitude === 0 ? 0 : displacement[node][j] / magnitude;
149
+ positions[node][j] += direction * limitedMagnitude;
150
+ }
151
+ }
153
152
 
154
- // Cool temperature
155
- t -= dt;
156
- }
153
+ // Cool temperature
154
+ t -= dt;
155
+ }
157
156
 
158
- // Rescale positions
159
- if (!fixed) {
160
- positions = rescaleLayout(positions, scale, center) as PositionMap;
161
- }
157
+ // Rescale positions
158
+ if (!fixed) {
159
+ positions = rescaleLayout(positions, scale, center) as PositionMap;
160
+ }
162
161
 
163
- return positions;
164
- }
162
+ return positions;
163
+ }
@@ -2,8 +2,8 @@
2
2
  * Force-directed layout algorithms
3
3
  */
4
4
 
5
- export { springLayout } from './spring';
6
- export { fruchtermanReingoldLayout } from './fruchterman-reingold';
7
- export { kamadaKawaiLayout } from './kamada-kawai';
8
- export { forceatlas2Layout } from './forceatlas2';
9
- export { arfLayout } from './arf';
5
+ export { arfLayout } from "./arf";
6
+ export { forceatlas2Layout } from "./forceatlas2";
7
+ export { fruchtermanReingoldLayout } from "./fruchterman-reingold";
8
+ export { kamadaKawaiLayout } from "./kamada-kawai";
9
+ export { springLayout } from "./spring";
@@ -1,17 +1,12 @@
1
- import type { Graph, Node, PositionMap } from '../../types';
2
- import type { DistanceMap } from '../../algorithms/optimization';
3
- import { getNodesFromGraph } from '../../utils/graph';
4
- import { _processParams } from '../../utils/params';
5
- import { rescaleLayout } from '../../utils/rescale';
6
- import { circularLayout } from '../geometric/circular';
7
- import {
8
- _computeShortestPathDistances,
9
- _kamadaKawaiSolve
10
- } from '../../algorithms/optimization';
1
+ import { _computeShortestPathDistances, _kamadaKawaiSolve, type DistanceMap } from "../../algorithms/optimization";
2
+ import type { Graph, Node, PositionMap } from "../../types";
3
+ import { getNodesFromGraph } from "../../utils/graph";
4
+ import { _processParams } from "../../utils/params";
5
+ import { rescaleLayout } from "../../utils/rescale";
6
+ import { circularLayout } from "../geometric/circular";
11
7
 
12
8
  /**
13
9
  * Position nodes using Kamada-Kawai path-length cost-function.
14
- *
15
10
  * @param G - NetworkX graph or list of nodes
16
11
  * @param dist - A two-level dictionary of optimal distances between nodes
17
12
  * @param pos - Initial positions for nodes
@@ -22,91 +17,93 @@ import {
22
17
  * @returns Positions dictionary keyed by node
23
18
  */
24
19
  export function kamadaKawaiLayout(
25
- G: Graph,
26
- dist: DistanceMap | null = null,
27
- pos: PositionMap | null = null,
28
- weight: string = 'weight',
29
- scale: number = 1,
30
- center: number[] | null = null,
31
- dim: number = 2
20
+ G: Graph,
21
+ dist: DistanceMap | null = null,
22
+ pos: PositionMap | null = null,
23
+ weight: string = "weight",
24
+ scale: number = 1,
25
+ center: number[] | null = null,
26
+ dim: number = 2,
32
27
  ): PositionMap {
33
- const processed = _processParams(G, center, dim);
34
- const graph = processed.G;
35
- center = processed.center;
28
+ const processed = _processParams(G, center, dim);
29
+ const graph = processed.G;
30
+ ({ center } = processed);
36
31
 
37
- const nodes = getNodesFromGraph(graph);
32
+ const nodes = getNodesFromGraph(graph);
38
33
 
39
- if (nodes.length === 0) {
40
- return {};
41
- }
34
+ if (nodes.length === 0) {
35
+ return {};
36
+ }
42
37
 
43
- if (nodes.length === 1) {
44
- return { [nodes[0]]: center };
45
- }
38
+ if (nodes.length === 1) {
39
+ return { [nodes[0]]: center };
40
+ }
46
41
 
47
- // Initialize distance matrix
48
- if (!dist) {
49
- // Kamada-Kawai requires a proper Graph, not just a list of nodes
50
- if (Array.isArray(graph)) {
51
- throw new Error('Kamada-Kawai layout requires a Graph with edges, not just a list of nodes');
42
+ // Initialize distance matrix
43
+ if (!dist) {
44
+ // Kamada-Kawai requires a proper Graph, not just a list of nodes
45
+ if (Array.isArray(graph)) {
46
+ throw new Error("Kamada-Kawai layout requires a Graph with edges, not just a list of nodes");
47
+ }
48
+ dist = _computeShortestPathDistances(graph, weight);
52
49
  }
53
- dist = _computeShortestPathDistances(graph, weight);
54
- }
55
50
 
56
- // Convert distances to a matrix
57
- const nodesArray: Node[] = Array.from(nodes);
58
- const nNodes = nodesArray.length;
59
- const distMatrix: number[][] = Array(nNodes).fill(0).map(() => Array(nNodes).fill(1e6));
51
+ // Convert distances to a matrix
52
+ const nodesArray: Node[] = Array.from(nodes);
53
+ const nNodes = nodesArray.length;
54
+ const distMatrix: number[][] = Array(nNodes)
55
+ .fill(0)
56
+ .map(() => Array(nNodes).fill(1e6));
60
57
 
61
- for (let i = 0; i < nNodes; i++) {
62
- const nodeI = nodesArray[i];
63
- distMatrix[i][i] = 0;
58
+ for (let i = 0; i < nNodes; i++) {
59
+ const nodeI = nodesArray[i];
60
+ distMatrix[i][i] = 0;
64
61
 
65
- if (!dist[nodeI]) continue;
62
+ if (!dist[nodeI]) {continue;}
66
63
 
67
- for (let j = 0; j < nNodes; j++) {
68
- const nodeJ = nodesArray[j];
69
- if (dist[nodeI][nodeJ] !== undefined) {
70
- distMatrix[i][j] = dist[nodeI][nodeJ];
71
- }
64
+ for (let j = 0; j < nNodes; j++) {
65
+ const nodeJ = nodesArray[j];
66
+ if (dist[nodeI][nodeJ] !== undefined) {
67
+ distMatrix[i][j] = dist[nodeI][nodeJ];
68
+ }
69
+ }
72
70
  }
73
- }
74
71
 
75
- // Initialize positions if not provided
76
- if (!pos) {
77
- if (dim >= 2) {
78
- // Use circular/spherical layout for 2D and 3D
79
- pos = circularLayout(G, 1, center, dim);
80
- } else {
81
- // For 1D, use a linear layout
82
- const posArray: PositionMap = {};
83
- nodesArray.forEach((node, i) => {
84
- posArray[node] = [i / (nNodes - 1 || 1)];
85
- });
86
- pos = posArray;
72
+ // Initialize positions if not provided
73
+ if (!pos) {
74
+ if (dim >= 2) {
75
+ // Use circular/spherical layout for 2D and 3D
76
+ pos = circularLayout(G, 1, center, dim);
77
+ } else {
78
+ // For 1D, use a linear layout
79
+ const posArray: PositionMap = {};
80
+ nodesArray.forEach((node, i) => {
81
+ posArray[node] = [i / (nNodes - 1 || 1)];
82
+ });
83
+ pos = posArray;
84
+ }
87
85
  }
88
- }
89
86
 
90
- // Convert positions to array for computation
91
- const posArray: number[][] = new Array(nNodes);
92
- for (let i = 0; i < nNodes; i++) {
93
- const node = nodesArray[i];
94
- posArray[i] = pos[node] ? [...pos[node]] : Array(dim).fill(0);
87
+ // Convert positions to array for computation
88
+ const posArray: number[][] = new Array(nNodes);
89
+ for (let i = 0; i < nNodes; i++) {
90
+ const node = nodesArray[i];
91
+ posArray[i] = pos[node] ? [...pos[node]] : Array(dim).fill(0);
95
92
 
96
- // Ensure correct dimensionality
97
- while (posArray[i].length < dim) {
98
- posArray[i].push(0);
93
+ // Ensure correct dimensionality
94
+ while (posArray[i].length < dim) {
95
+ posArray[i].push(0);
96
+ }
99
97
  }
100
- }
101
98
 
102
- // Run the Kamada-Kawai algorithm
103
- const newPositions = _kamadaKawaiSolve(distMatrix, posArray, dim);
99
+ // Run the Kamada-Kawai algorithm
100
+ const newPositions = _kamadaKawaiSolve(distMatrix, posArray, dim);
104
101
 
105
- // Convert positions array back to dictionary and rescale
106
- const finalPos: PositionMap = {};
107
- for (let i = 0; i < nNodes; i++) {
108
- finalPos[nodesArray[i]] = newPositions[i];
109
- }
102
+ // Convert positions array back to dictionary and rescale
103
+ const finalPos: PositionMap = {};
104
+ for (let i = 0; i < nNodes; i++) {
105
+ finalPos[nodesArray[i]] = newPositions[i];
106
+ }
110
107
 
111
- return rescaleLayout(finalPos, scale, center) as PositionMap;
112
- }
108
+ return rescaleLayout(finalPos, scale, center) as PositionMap;
109
+ }
@@ -2,34 +2,33 @@
2
2
  * Spring layout algorithm (Fruchterman-Reingold variant)
3
3
  */
4
4
 
5
- import { Graph, Node, PositionMap } from '../../types';
6
- import { fruchtermanReingoldLayout } from './fruchterman-reingold';
5
+ import { Graph, Node, PositionMap } from "../../types";
6
+ import { fruchtermanReingoldLayout } from "./fruchterman-reingold";
7
7
 
8
8
  /**
9
9
  * Position nodes using Fruchterman-Reingold force-directed algorithm.
10
- *
11
- * @param {Object} G - Graph or list of nodes
12
- * @param {number} k - Optimal distance between nodes
13
- * @param {Object} pos - Initial positions for nodes
14
- * @param {Array} fixed - Nodes to keep fixed at initial position
15
- * @param {number} iterations - Maximum number of iterations
16
- * @param {number} scale - Scale factor for positions
17
- * @param {Array|null} center - Coordinate pair around which to center the layout
18
- * @param {number} dim - Dimension of layout
19
- * @param {number} seed - Random seed for initial positions
20
- * @returns {Object} Positions dictionary keyed by node
10
+ * @param G - Graph or list of nodes
11
+ * @param k - Optimal distance between nodes
12
+ * @param pos - Initial positions for nodes
13
+ * @param fixed - Nodes to keep fixed at initial position
14
+ * @param iterations - Maximum number of iterations
15
+ * @param scale - Scale factor for positions
16
+ * @param center - Coordinate pair around which to center the layout
17
+ * @param dim - Dimension of layout
18
+ * @param seed - Random seed for initial positions
19
+ * @returns Positions dictionary keyed by node
21
20
  */
22
21
  export function springLayout(
23
- G: Graph,
24
- k: number | null = null,
25
- pos: PositionMap | null = null,
26
- fixed: Node[] | null = null,
27
- iterations: number = 50,
28
- scale: number = 1,
29
- center: number[] | null = null,
30
- dim: number = 2,
31
- seed: number | null = null
22
+ G: Graph,
23
+ k: number | null = null,
24
+ pos: PositionMap | null = null,
25
+ fixed: Node[] | null = null,
26
+ iterations: number = 50,
27
+ scale: number = 1,
28
+ center: number[] | null = null,
29
+ dim: number = 2,
30
+ seed: number | null = null,
32
31
  ): PositionMap {
33
- // Legacy compatibility alias
34
- return fruchtermanReingoldLayout(G, k, pos, fixed, iterations, scale, center, dim, seed);
35
- }
32
+ // Legacy compatibility alias
33
+ return fruchtermanReingoldLayout(G, k, pos, fixed, iterations, scale, center, dim, seed);
34
+ }