@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,7 +2,7 @@
2
2
  * Scale-free graph generation function
3
3
  */
4
4
 
5
- import { Graph, Node, Edge } from '../types';
5
+ import { Edge,Graph, Node } from "../types";
6
6
 
7
7
  /**
8
8
  * Create a scale-free graph using Barabási-Albert model
@@ -12,61 +12,62 @@ import { Graph, Node, Edge } from '../types';
12
12
  * @returns Graph object with scale-free properties
13
13
  */
14
14
  export function scaleFreeGraph(n: number, m: number, seed?: number): Graph {
15
- if (m >= n) {
16
- throw new Error('m must be less than n');
17
- }
18
-
19
- const nodes: Node[] = Array.from({ length: n }, (_, i) => i);
20
- const edges: Edge[] = [];
21
- const degrees = new Array(n).fill(0);
22
-
23
- // Simple deterministic pseudo-random if seed provided
24
- let currentSeed = seed;
25
- let random = seed !== undefined
26
- ? () => {
27
- currentSeed = (currentSeed! * 9301 + 49297) % 233280;
28
- return currentSeed / 233280;
29
- }
30
- : Math.random;
31
-
32
- // Start with complete graph of m+1 nodes
33
- for (let i = 0; i <= m; i++) {
34
- for (let j = i + 1; j <= m; j++) {
35
- edges.push([i, j]);
36
- degrees[i]++;
37
- degrees[j]++;
15
+ if (m >= n) {
16
+ throw new Error("m must be less than n");
38
17
  }
39
- }
40
-
41
- // Add remaining nodes
42
- for (let i = m + 1; i < n; i++) {
43
- const targets = new Set<number>();
44
- const totalDegree = degrees.reduce((sum, d) => sum + d, 0);
45
-
46
- // Choose m targets based on preferential attachment
47
- while (targets.size < m) {
48
- let r = random() * totalDegree;
49
- let cumSum = 0;
50
-
51
- for (let j = 0; j < i; j++) {
52
- cumSum += degrees[j];
53
- if (r <= cumSum && !targets.has(j)) {
54
- targets.add(j);
55
- break;
18
+
19
+ const nodes: Node[] = Array.from({ length: n }, (_, i) => i);
20
+ const edges: Edge[] = [];
21
+ const degrees = new Array(n).fill(0);
22
+
23
+ // Simple deterministic pseudo-random if seed provided
24
+ let currentSeed = seed;
25
+ const random =
26
+ seed !== undefined
27
+ ? () => {
28
+ currentSeed = ((currentSeed as number) * 9301 + 49297) % 233280;
29
+ return currentSeed / 233280;
30
+ }
31
+ : Math.random;
32
+
33
+ // Start with complete graph of m+1 nodes
34
+ for (let i = 0; i <= m; i++) {
35
+ for (let j = i + 1; j <= m; j++) {
36
+ edges.push([i, j]);
37
+ degrees[i]++;
38
+ degrees[j]++;
56
39
  }
57
- }
58
40
  }
59
-
60
- // Add edges to targets
61
- for (const target of targets) {
62
- edges.push([i, target]);
63
- degrees[i]++;
64
- degrees[target]++;
41
+
42
+ // Add remaining nodes
43
+ for (let i = m + 1; i < n; i++) {
44
+ const targets = new Set<number>();
45
+ const totalDegree = degrees.reduce((sum: number, d: number) => sum + d, 0);
46
+
47
+ // Choose m targets based on preferential attachment
48
+ while (targets.size < m) {
49
+ const r = random() * totalDegree;
50
+ let cumSum = 0;
51
+
52
+ for (let j = 0; j < i; j++) {
53
+ cumSum += degrees[j] as number;
54
+ if (r <= cumSum && !targets.has(j)) {
55
+ targets.add(j);
56
+ break;
57
+ }
58
+ }
59
+ }
60
+
61
+ // Add edges to targets
62
+ for (const target of targets) {
63
+ edges.push([i, target]);
64
+ degrees[i]++;
65
+ degrees[target]++;
66
+ }
65
67
  }
66
- }
67
-
68
- return {
69
- nodes: () => nodes,
70
- edges: () => edges
71
- };
72
- }
68
+
69
+ return {
70
+ nodes: () => nodes,
71
+ edges: () => edges,
72
+ };
73
+ }
package/src/index.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  /**
2
2
  * Main entry point for @graphty/layout
3
- *
3
+ *
4
4
  * This file exports the complete public API, maintaining
5
5
  * compatibility with the original layout.ts file.
6
6
  */
7
7
 
8
8
  // Re-export all types
9
- export * from './types';
9
+ export * from "./types";
10
10
 
11
11
  // Re-export utilities that are part of the public API
12
- export { rescaleLayout, rescaleLayoutDict } from './utils/rescale';
12
+ export { rescaleLayout, rescaleLayoutDict } from "./utils/rescale";
13
13
 
14
14
  // Re-export all layout algorithms
15
- export * from './layouts';
15
+ export * from "./layouts";
16
16
 
17
17
  // Re-export all graph generation functions
18
- export * from './generators';
18
+ export * from "./generators";
@@ -2,4 +2,4 @@
2
2
  * Basic layout algorithms
3
3
  */
4
4
 
5
- export { randomLayout } from './random';
5
+ export { randomLayout } from "./random";
@@ -2,31 +2,35 @@
2
2
  * Random layout algorithm
3
3
  */
4
4
 
5
- import { Graph, Node, PositionMap } from '../../types';
6
- import { _processParams } from '../../utils/params';
7
- import { getNodesFromGraph } from '../../utils/graph';
8
- import { RandomNumberGenerator } from '../../utils/random';
5
+ import { Graph, Node, PositionMap } from "../../types";
6
+ import { getNodesFromGraph } from "../../utils/graph";
7
+ import { _processParams } from "../../utils/params";
8
+ import { RandomNumberGenerator } from "../../utils/random";
9
9
 
10
10
  /**
11
11
  * Position nodes uniformly at random in the unit square.
12
- *
13
12
  * @param G - Graph or list of nodes
14
13
  * @param center - Coordinate pair around which to center the layout
15
14
  * @param dim - Dimension of layout
16
15
  * @param seed - Random seed for reproducible layouts
17
16
  * @returns Positions dictionary keyed by node
18
17
  */
19
- export function randomLayout(G: Graph | Node[], center: number[] | null = null, dim: number = 2, seed: number | null = null): PositionMap {
20
- const processed = _processParams(G, center, dim);
21
- const nodes = getNodesFromGraph(processed.G);
22
- center = processed.center;
18
+ export function randomLayout(
19
+ G: Graph | Node[],
20
+ center: number[] | null = null,
21
+ dim: number = 2,
22
+ seed: number | null = null,
23
+ ): PositionMap {
24
+ const processed = _processParams(G, center, dim);
25
+ const nodes = getNodesFromGraph(processed.G);
26
+ ({ center } = processed);
23
27
 
24
- const rng = new RandomNumberGenerator(seed ?? undefined);
25
- const pos: PositionMap = {};
28
+ const rng = new RandomNumberGenerator(seed ?? undefined);
29
+ const pos: PositionMap = {};
26
30
 
27
- nodes.forEach((node: Node) => {
28
- pos[node] = (rng.rand(dim) as number[]).map((val: number, i: number) => val + center[i]);
29
- });
31
+ nodes.forEach((node: Node) => {
32
+ pos[node] = (rng.rand(dim) as number[]).map((val: number, i: number) => val + center[i]);
33
+ });
30
34
 
31
- return pos;
32
- }
35
+ return pos;
36
+ }
@@ -1,11 +1,10 @@
1
- import type { Graph, Node, PositionMap } from '../../types';
2
- import { getNodesFromGraph, getEdgesFromGraph } from '../../utils/graph';
3
- import { RandomNumberGenerator } from '../../utils/random';
4
- import { randomLayout } from '../basic/random';
1
+ import type { Graph, Node, PositionMap } from "../../types";
2
+ import { getEdgesFromGraph,getNodesFromGraph } from "../../utils/graph";
3
+ import { RandomNumberGenerator } from "../../utils/random";
4
+ import { randomLayout } from "../basic/random";
5
5
 
6
6
  /**
7
7
  * Layout algorithm with attractive and repulsive forces (ARF).
8
- *
9
8
  * @param G - Graph
10
9
  * @param pos - Initial positions for nodes
11
10
  * @param scaling - Scale factor for positions
@@ -15,116 +14,119 @@ import { randomLayout } from '../basic/random';
15
14
  * @returns Positions dictionary keyed by node
16
15
  */
17
16
  export function arfLayout(
18
- G: Graph,
19
- pos: PositionMap | null = null,
20
- scaling: number = 1,
21
- a: number = 1.1,
22
- maxIter: number = 1000,
23
- seed: number | null = null
17
+ G: Graph,
18
+ pos: PositionMap | null = null,
19
+ scaling: number = 1,
20
+ a: number = 1.1,
21
+ maxIter: number = 1000,
22
+ seed: number | null = null,
24
23
  ): PositionMap {
25
- if (a <= 1) {
26
- throw new Error("The parameter a should be larger than 1");
27
- }
28
-
29
- const nodes = getNodesFromGraph(G);
30
- const edges = getEdgesFromGraph(G);
31
-
32
- if (nodes.length === 0) {
33
- return {};
34
- }
35
-
36
- // Initialize positions if not provided
37
- if (!pos) {
38
- pos = randomLayout(G, null, 2, seed);
39
- } else {
40
- // Make sure all nodes have positions
41
- const rng = new RandomNumberGenerator(seed ?? undefined);
42
- const defaultPos: PositionMap = {};
43
- nodes.forEach((node: Node) => {
44
- if (!pos![node]) {
45
- defaultPos[node] = [(rng.rand() as number), (rng.rand() as number)];
46
- }
24
+ if (a <= 1) {
25
+ throw new Error("The parameter a should be larger than 1");
26
+ }
27
+
28
+ const nodes = getNodesFromGraph(G);
29
+ const edges = getEdgesFromGraph(G);
30
+
31
+ if (nodes.length === 0) {
32
+ return {};
33
+ }
34
+
35
+ // Initialize positions if not provided
36
+ if (!pos) {
37
+ pos = randomLayout(G, null, 2, seed);
38
+ } else {
39
+ // Make sure all nodes have positions
40
+ const rng = new RandomNumberGenerator(seed ?? undefined);
41
+ const defaultPos: PositionMap = {};
42
+ nodes.forEach((node: Node) => {
43
+ if (!(pos as PositionMap)[node]) {
44
+ defaultPos[node] = [rng.rand() as number, rng.rand() as number];
45
+ }
46
+ });
47
+ pos = { ...pos, ...defaultPos };
48
+ }
49
+
50
+ // Create node index mapping
51
+ const nodeIndex: Record<Node, number> = {};
52
+ nodes.forEach((node: Node, i: number) => {
53
+ nodeIndex[node] = i;
47
54
  });
48
- pos = { ...pos, ...defaultPos };
49
- }
50
-
51
- // Create node index mapping
52
- const nodeIndex: Record<Node, number> = {};
53
- nodes.forEach((node: Node, i: number) => {
54
- nodeIndex[node] = i;
55
- });
56
-
57
- // Create positions array
58
- const positions: number[][] = nodes.map((node: Node) => [...pos![node]]);
59
-
60
- // Initialize spring constant matrix
61
- const N = nodes.length;
62
- const K = Array(N).fill(0).map(() => Array(N).fill(1));
63
-
64
- // Set diagonal to zero (no self-attraction)
65
- for (let i = 0; i < N; i++) {
66
- K[i][i] = 0;
67
- }
68
-
69
- // Set stronger attraction between connected nodes
70
- for (const [source, target] of edges) {
71
- if (source === target) continue;
72
-
73
- const i = nodeIndex[source];
74
- const j = nodeIndex[target];
75
- K[i][j] = a;
76
- K[j][i] = a;
77
- }
78
-
79
- // Calculate rho (scale factor)
80
- const rho = scaling * Math.sqrt(N);
81
-
82
- // Optimization loop
83
- const dt = 1e-3; // Time step
84
- const etol = 1e-6; // Error tolerance
85
- let error = etol + 1;
86
- let nIter = 0;
87
-
88
- while (error > etol && nIter < maxIter) {
89
- // Calculate changes for each node
90
- const change = Array(N).fill(0).map(() => [0, 0]);
91
55
 
56
+ // Create positions array
57
+ const positions: number[][] = nodes.map((node: Node) => [...pos[node]]);
58
+
59
+ // Initialize spring constant matrix
60
+ const N = nodes.length;
61
+ const K = Array(N)
62
+ .fill(0)
63
+ .map(() => Array(N).fill(1));
64
+
65
+ // Set diagonal to zero (no self-attraction)
92
66
  for (let i = 0; i < N; i++) {
93
- for (let j = 0; j < N; j++) {
94
- if (i === j) continue;
67
+ K[i][i] = 0;
68
+ }
69
+
70
+ // Set stronger attraction between connected nodes
71
+ for (const [source, target] of edges) {
72
+ if (source === target) {continue;}
95
73
 
96
- // Calculate difference vector
97
- const diff = positions[i].map((coord, dim) => coord - positions[j][dim]);
74
+ const i = nodeIndex[source];
75
+ const j = nodeIndex[target];
76
+ K[i][j] = a;
77
+ K[j][i] = a;
78
+ }
98
79
 
99
- // Calculate distance (with minimum to avoid division by zero)
100
- const dist = Math.sqrt(diff.reduce((sum, d) => sum + d * d, 0)) || 0.01;
80
+ // Calculate rho (scale factor)
81
+ const rho = scaling * Math.sqrt(N);
101
82
 
102
- // Calculate attractive and repulsive forces
103
- for (let d = 0; d < diff.length; d++) {
104
- change[i][d] += K[i][j] * diff[d] - (rho / dist) * diff[d];
83
+ // Optimization loop
84
+ const dt = 1e-3; // Time step
85
+ const etol = 1e-6; // Error tolerance
86
+ let error = etol + 1;
87
+ let nIter = 0;
88
+
89
+ while (error > etol && nIter < maxIter) {
90
+ // Calculate changes for each node
91
+ const change = Array(N)
92
+ .fill(0)
93
+ .map(() => [0, 0]);
94
+
95
+ for (let i = 0; i < N; i++) {
96
+ for (let j = 0; j < N; j++) {
97
+ if (i === j) {continue;}
98
+
99
+ // Calculate difference vector
100
+ const diff = positions[i].map((coord, dim) => coord - positions[j][dim]);
101
+
102
+ // Calculate distance (with minimum to avoid division by zero)
103
+ const dist = Math.sqrt(diff.reduce((sum, d) => sum + d * d, 0)) || 0.01;
104
+
105
+ // Calculate attractive and repulsive forces
106
+ for (let d = 0; d < diff.length; d++) {
107
+ change[i][d] += K[i][j] * diff[d] - (rho / dist) * diff[d];
108
+ }
109
+ }
105
110
  }
106
- }
107
- }
108
111
 
109
- // Update positions
110
- for (let i = 0; i < N; i++) {
111
- for (let d = 0; d < positions[i].length; d++) {
112
- positions[i][d] += change[i][d] * dt;
113
- }
114
- }
112
+ // Update positions
113
+ for (let i = 0; i < N; i++) {
114
+ for (let d = 0; d < positions[i].length; d++) {
115
+ positions[i][d] += change[i][d] * dt;
116
+ }
117
+ }
115
118
 
116
- // Calculate error (sum of force magnitudes)
117
- error = change.reduce((sum, c) =>
118
- sum + Math.sqrt(c.reduce((s, v) => s + v * v, 0)), 0);
119
+ // Calculate error (sum of force magnitudes)
120
+ error = change.reduce((sum, c) => sum + Math.sqrt(c.reduce((s, v) => s + v * v, 0)), 0);
119
121
 
120
- nIter++;
121
- }
122
+ nIter++;
123
+ }
122
124
 
123
- // Convert positions array back to object
124
- const finalPos: PositionMap = {};
125
- nodes.forEach((node: Node, i: number) => {
126
- finalPos[node] = positions[i];
127
- });
125
+ // Convert positions array back to object
126
+ const finalPos: PositionMap = {};
127
+ nodes.forEach((node: Node, i: number) => {
128
+ finalPos[node] = positions[i];
129
+ });
128
130
 
129
- return finalPos;
130
- }
131
+ return finalPos;
132
+ }