@graphty/layout 1.3.2 → 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 (168) hide show
  1. package/README.md +7 -6
  2. package/dist/layout.js +41 -33
  3. package/dist/layout.js.map +1 -1
  4. package/dist/src/algorithms/index.d.ts +1 -0
  5. package/dist/src/algorithms/index.d.ts.map +1 -0
  6. package/dist/src/algorithms/optimization/index.d.ts +1 -0
  7. package/dist/src/algorithms/optimization/index.d.ts.map +1 -0
  8. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts +2 -1
  9. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts.map +1 -0
  10. package/dist/src/algorithms/optimization/kamada-kawai-solver.js.map +1 -1
  11. package/dist/src/algorithms/optimization/lbfgs.d.ts +3 -2
  12. package/dist/src/algorithms/optimization/lbfgs.d.ts.map +1 -0
  13. package/dist/src/algorithms/optimization/lbfgs.js +2 -2
  14. package/dist/src/algorithms/optimization/lbfgs.js.map +1 -1
  15. package/dist/src/algorithms/optimization/line-search.d.ts +1 -0
  16. package/dist/src/algorithms/optimization/line-search.d.ts.map +1 -0
  17. package/dist/src/algorithms/optimization/types.d.ts +1 -0
  18. package/dist/src/algorithms/optimization/types.d.ts.map +1 -0
  19. package/dist/src/algorithms/planarity/check.d.ts +3 -2
  20. package/dist/src/algorithms/planarity/check.d.ts.map +1 -0
  21. package/dist/src/algorithms/planarity/check.js +2 -2
  22. package/dist/src/algorithms/planarity/check.js.map +1 -1
  23. package/dist/src/algorithms/planarity/embedding.d.ts +3 -2
  24. package/dist/src/algorithms/planarity/embedding.d.ts.map +1 -0
  25. package/dist/src/algorithms/planarity/embedding.js +2 -2
  26. package/dist/src/algorithms/planarity/embedding.js.map +1 -1
  27. package/dist/src/algorithms/planarity/index.d.ts +1 -0
  28. package/dist/src/algorithms/planarity/index.d.ts.map +1 -0
  29. package/dist/src/algorithms/planarity/lr-test.d.ts +1 -0
  30. package/dist/src/algorithms/planarity/lr-test.d.ts.map +1 -0
  31. package/dist/src/algorithms/planarity/special-graphs.d.ts +1 -0
  32. package/dist/src/algorithms/planarity/special-graphs.d.ts.map +1 -0
  33. package/dist/src/algorithms/planarity/special-graphs.js.map +1 -1
  34. package/dist/src/generators/basic.d.ts +1 -0
  35. package/dist/src/generators/basic.d.ts.map +1 -0
  36. package/dist/src/generators/bipartite.d.ts +1 -0
  37. package/dist/src/generators/bipartite.d.ts.map +1 -0
  38. package/dist/src/generators/bipartite.js.map +1 -1
  39. package/dist/src/generators/grid.d.ts +1 -0
  40. package/dist/src/generators/grid.d.ts.map +1 -0
  41. package/dist/src/generators/index.d.ts +1 -0
  42. package/dist/src/generators/index.d.ts.map +1 -0
  43. package/dist/src/generators/random.d.ts +1 -0
  44. package/dist/src/generators/random.d.ts.map +1 -0
  45. package/dist/src/generators/random.js.map +1 -1
  46. package/dist/src/generators/scale-free.d.ts +1 -0
  47. package/dist/src/generators/scale-free.d.ts.map +1 -0
  48. package/dist/src/generators/scale-free.js.map +1 -1
  49. package/dist/src/index.d.ts +1 -0
  50. package/dist/src/index.d.ts.map +1 -0
  51. package/dist/src/layouts/basic/index.d.ts +1 -0
  52. package/dist/src/layouts/basic/index.d.ts.map +1 -0
  53. package/dist/src/layouts/basic/random.d.ts +1 -0
  54. package/dist/src/layouts/basic/random.d.ts.map +1 -0
  55. package/dist/src/layouts/basic/random.js +1 -1
  56. package/dist/src/layouts/basic/random.js.map +1 -1
  57. package/dist/src/layouts/force-directed/arf.d.ts +1 -0
  58. package/dist/src/layouts/force-directed/arf.d.ts.map +1 -0
  59. package/dist/src/layouts/force-directed/arf.js.map +1 -1
  60. package/dist/src/layouts/force-directed/forceatlas2.d.ts +3 -2
  61. package/dist/src/layouts/force-directed/forceatlas2.d.ts.map +1 -0
  62. package/dist/src/layouts/force-directed/forceatlas2.js +31 -22
  63. package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -1
  64. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +2 -1
  65. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts.map +1 -0
  66. package/dist/src/layouts/force-directed/fruchterman-reingold.js +1 -1
  67. package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -1
  68. package/dist/src/layouts/force-directed/index.d.ts +1 -0
  69. package/dist/src/layouts/force-directed/index.d.ts.map +1 -0
  70. package/dist/src/layouts/force-directed/kamada-kawai.d.ts +2 -1
  71. package/dist/src/layouts/force-directed/kamada-kawai.d.ts.map +1 -0
  72. package/dist/src/layouts/force-directed/kamada-kawai.js +1 -1
  73. package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -1
  74. package/dist/src/layouts/force-directed/spring.d.ts +1 -0
  75. package/dist/src/layouts/force-directed/spring.d.ts.map +1 -0
  76. package/dist/src/layouts/geometric/circular.d.ts +1 -0
  77. package/dist/src/layouts/geometric/circular.d.ts.map +1 -0
  78. package/dist/src/layouts/geometric/circular.js +1 -1
  79. package/dist/src/layouts/geometric/circular.js.map +1 -1
  80. package/dist/src/layouts/geometric/index.d.ts +1 -0
  81. package/dist/src/layouts/geometric/index.d.ts.map +1 -0
  82. package/dist/src/layouts/geometric/shell.d.ts +1 -0
  83. package/dist/src/layouts/geometric/shell.d.ts.map +1 -0
  84. package/dist/src/layouts/geometric/shell.js +1 -1
  85. package/dist/src/layouts/geometric/shell.js.map +1 -1
  86. package/dist/src/layouts/geometric/spiral.d.ts +2 -1
  87. package/dist/src/layouts/geometric/spiral.d.ts.map +1 -0
  88. package/dist/src/layouts/geometric/spiral.js +1 -1
  89. package/dist/src/layouts/geometric/spiral.js.map +1 -1
  90. package/dist/src/layouts/hierarchical/bfs.d.ts +1 -0
  91. package/dist/src/layouts/hierarchical/bfs.d.ts.map +1 -0
  92. package/dist/src/layouts/hierarchical/bfs.js +1 -1
  93. package/dist/src/layouts/hierarchical/bfs.js.map +1 -1
  94. package/dist/src/layouts/hierarchical/bipartite.d.ts +1 -0
  95. package/dist/src/layouts/hierarchical/bipartite.d.ts.map +1 -0
  96. package/dist/src/layouts/hierarchical/bipartite.js +1 -1
  97. package/dist/src/layouts/hierarchical/bipartite.js.map +1 -1
  98. package/dist/src/layouts/hierarchical/index.d.ts +1 -0
  99. package/dist/src/layouts/hierarchical/index.d.ts.map +1 -0
  100. package/dist/src/layouts/hierarchical/multipartite.d.ts +1 -0
  101. package/dist/src/layouts/hierarchical/multipartite.d.ts.map +1 -0
  102. package/dist/src/layouts/hierarchical/multipartite.js +2 -2
  103. package/dist/src/layouts/hierarchical/multipartite.js.map +1 -1
  104. package/dist/src/layouts/index.d.ts +1 -0
  105. package/dist/src/layouts/index.d.ts.map +1 -0
  106. package/dist/src/layouts/specialized/index.d.ts +1 -0
  107. package/dist/src/layouts/specialized/index.d.ts.map +1 -0
  108. package/dist/src/layouts/specialized/planar.d.ts +2 -1
  109. package/dist/src/layouts/specialized/planar.d.ts.map +1 -0
  110. package/dist/src/layouts/specialized/planar.js +1 -1
  111. package/dist/src/layouts/specialized/planar.js.map +1 -1
  112. package/dist/src/layouts/specialized/spectral.d.ts +2 -1
  113. package/dist/src/layouts/specialized/spectral.d.ts.map +1 -0
  114. package/dist/src/layouts/specialized/spectral.js +1 -1
  115. package/dist/src/layouts/specialized/spectral.js.map +1 -1
  116. package/dist/src/types/embedding.d.ts +1 -0
  117. package/dist/src/types/embedding.d.ts.map +1 -0
  118. package/dist/src/types/graph.d.ts +2 -1
  119. package/dist/src/types/graph.d.ts.map +1 -0
  120. package/dist/src/types/index.d.ts +1 -0
  121. package/dist/src/types/index.d.ts.map +1 -0
  122. package/dist/src/types/layout.d.ts +1 -0
  123. package/dist/src/types/layout.d.ts.map +1 -0
  124. package/dist/src/utils/graph.d.ts +1 -0
  125. package/dist/src/utils/graph.d.ts.map +1 -0
  126. package/dist/src/utils/index.d.ts +1 -0
  127. package/dist/src/utils/index.d.ts.map +1 -0
  128. package/dist/src/utils/numpy.d.ts +5 -4
  129. package/dist/src/utils/numpy.d.ts.map +1 -0
  130. package/dist/src/utils/numpy.js.map +1 -1
  131. package/dist/src/utils/params.d.ts +1 -0
  132. package/dist/src/utils/params.d.ts.map +1 -0
  133. package/dist/src/utils/random.d.ts +1 -0
  134. package/dist/src/utils/random.d.ts.map +1 -0
  135. package/dist/src/utils/random.js.map +1 -1
  136. package/dist/src/utils/rescale.d.ts +1 -0
  137. package/dist/src/utils/rescale.d.ts.map +1 -0
  138. package/dist/src/utils/rescale.js.map +1 -1
  139. package/dist/tsconfig.tsbuildinfo +1 -0
  140. package/package.json +10 -14
  141. package/src/algorithms/optimization/kamada-kawai-solver.ts +6 -6
  142. package/src/algorithms/optimization/lbfgs.ts +2 -2
  143. package/src/algorithms/planarity/check.ts +2 -2
  144. package/src/algorithms/planarity/embedding.ts +3 -3
  145. package/src/algorithms/planarity/special-graphs.ts +1 -1
  146. package/src/generators/bipartite.ts +1 -1
  147. package/src/generators/random.ts +1 -1
  148. package/src/generators/scale-free.ts +3 -3
  149. package/src/layouts/basic/random.ts +1 -1
  150. package/src/layouts/force-directed/arf.ts +1 -1
  151. package/src/layouts/force-directed/forceatlas2.ts +58 -50
  152. package/src/layouts/force-directed/fruchterman-reingold.ts +2 -2
  153. package/src/layouts/force-directed/kamada-kawai.ts +2 -3
  154. package/src/layouts/geometric/circular.ts +1 -1
  155. package/src/layouts/geometric/shell.ts +1 -1
  156. package/src/layouts/geometric/spiral.ts +3 -3
  157. package/src/layouts/hierarchical/bfs.ts +2 -2
  158. package/src/layouts/hierarchical/bipartite.ts +1 -1
  159. package/src/layouts/hierarchical/multipartite.ts +3 -3
  160. package/src/layouts/specialized/planar.ts +2 -2
  161. package/src/layouts/specialized/spectral.ts +11 -11
  162. package/src/types/graph.ts +1 -1
  163. package/src/utils/numpy.ts +11 -11
  164. package/src/utils/random.ts +2 -2
  165. package/src/utils/rescale.ts +2 -2
  166. package/dist/vitest.config.d.ts +0 -2
  167. package/dist/vitest.config.js +0 -30
  168. package/dist/vitest.config.js.map +0 -1
@@ -10,14 +10,14 @@ import { isK5, isK33 } from "./special-graphs";
10
10
  /**
11
11
  * Check if graph is planar using a simplified version of Boyer-Myrvold algorithm.
12
12
  * Returns planarity and embedding information.
13
- * @param G - Graph
13
+ * @param _G - Graph (reserved for future use)
14
14
  * @param nodes - List of nodes
15
15
  * @param edges - List of edges
16
16
  * @param seed - Random seed for deterministic embedding generation
17
17
  * @returns Object containing isPlanar flag and embedding
18
18
  */
19
19
  export function checkPlanarity(
20
- G: Graph,
20
+ _G: Graph,
21
21
  nodes: Node[],
22
22
  edges: Edge[],
23
23
  seed: number | null = null,
@@ -90,11 +90,11 @@ export function createTriangulationEmbedding(nodes: Node[], edges: Edge[], seed:
90
90
  /**
91
91
  * Find a simple cycle in the graph (for outer face)
92
92
  * @param nodes - List of nodes
93
- * @param edges - List of edges
93
+ * @param _edges - List of edges (reserved for future use)
94
94
  * @param adjMap - Adjacency map
95
95
  * @returns Cycle as array of nodes, or null if none found
96
96
  */
97
- export function findCycle(nodes: Node[], edges: Edge[], adjMap: Record<Node, Set<Node>>): Node[] | null {
97
+ export function findCycle(nodes: Node[], _edges: Edge[], adjMap: Record<Node, Set<Node>>): Node[] | null {
98
98
  if (nodes.length === 0) {return null;}
99
99
  if (nodes.length <= 2) {return nodes;} // Not a real cycle but handle it
100
100
 
@@ -167,7 +167,7 @@ export function findCycle(nodes: Node[], edges: Edge[], adjMap: Record<Node, Set
167
167
  let current = u;
168
168
 
169
169
  while (parent[current] !== undefined && parent[current] !== v) {
170
- current = parent[current]!;
170
+ current = parent[current] as Node;
171
171
  cycle.push(current);
172
172
  }
173
173
 
@@ -86,7 +86,7 @@ export function tryFindBipartitePartition(nodes: Node[], edges: Edge[]): [Node[]
86
86
  colorMap[nodes[0]] = 0;
87
87
 
88
88
  while (queue.length > 0) {
89
- const node = queue.shift()!;
89
+ const node = queue.shift() as Node;
90
90
  const nodeColor = colorMap[node];
91
91
 
92
92
  for (const neighbor of adjList[node]) {
@@ -28,7 +28,7 @@ export function bipartiteGraph(
28
28
  const random =
29
29
  seed !== undefined
30
30
  ? () => {
31
- currentSeed = (currentSeed! * 9301 + 49297) % 233280;
31
+ currentSeed = ((currentSeed as number) * 9301 + 49297) % 233280;
32
32
  return currentSeed / 233280;
33
33
  }
34
34
  : Math.random;
@@ -20,7 +20,7 @@ export function randomGraph(n: number, p: number, seed?: number): Graph {
20
20
  const random =
21
21
  seed !== undefined
22
22
  ? () => {
23
- currentSeed = (currentSeed! * 9301 + 49297) % 233280;
23
+ currentSeed = ((currentSeed as number) * 9301 + 49297) % 233280;
24
24
  return currentSeed / 233280;
25
25
  }
26
26
  : Math.random;
@@ -25,7 +25,7 @@ export function scaleFreeGraph(n: number, m: number, seed?: number): Graph {
25
25
  const random =
26
26
  seed !== undefined
27
27
  ? () => {
28
- currentSeed = (currentSeed! * 9301 + 49297) % 233280;
28
+ currentSeed = ((currentSeed as number) * 9301 + 49297) % 233280;
29
29
  return currentSeed / 233280;
30
30
  }
31
31
  : Math.random;
@@ -42,7 +42,7 @@ export function scaleFreeGraph(n: number, m: number, seed?: number): Graph {
42
42
  // Add remaining nodes
43
43
  for (let i = m + 1; i < n; i++) {
44
44
  const targets = new Set<number>();
45
- const totalDegree = degrees.reduce((sum, d) => sum + d, 0);
45
+ const totalDegree = degrees.reduce((sum: number, d: number) => sum + d, 0);
46
46
 
47
47
  // Choose m targets based on preferential attachment
48
48
  while (targets.size < m) {
@@ -50,7 +50,7 @@ export function scaleFreeGraph(n: number, m: number, seed?: number): Graph {
50
50
  let cumSum = 0;
51
51
 
52
52
  for (let j = 0; j < i; j++) {
53
- cumSum += degrees[j];
53
+ cumSum += degrees[j] as number;
54
54
  if (r <= cumSum && !targets.has(j)) {
55
55
  targets.add(j);
56
56
  break;
@@ -23,7 +23,7 @@ export function randomLayout(
23
23
  ): PositionMap {
24
24
  const processed = _processParams(G, center, dim);
25
25
  const nodes = getNodesFromGraph(processed.G);
26
- center = processed.center;
26
+ ({ center } = processed);
27
27
 
28
28
  const rng = new RandomNumberGenerator(seed ?? undefined);
29
29
  const pos: PositionMap = {};
@@ -40,7 +40,7 @@ export function arfLayout(
40
40
  const rng = new RandomNumberGenerator(seed ?? undefined);
41
41
  const defaultPos: PositionMap = {};
42
42
  nodes.forEach((node: Node) => {
43
- if (!pos![node]) {
43
+ if (!(pos as PositionMap)[node]) {
44
44
  defaultPos[node] = [rng.rand() as number, rng.rand() as number];
45
45
  }
46
46
  });
@@ -1,5 +1,5 @@
1
1
  import type { Edge, Graph, Node, PositionMap } from "../../types";
2
- import { getEdgesFromGraph,getNodesFromGraph } from "../../utils/graph";
2
+ import { getNodesFromGraph } from "../../utils/graph";
3
3
  import { _processParams } from "../../utils/params";
4
4
  import { RandomNumberGenerator } from "../../utils/random";
5
5
  import { rescaleLayout } from "../../utils/rescale";
@@ -17,7 +17,7 @@ import { rescaleLayout } from "../../utils/rescale";
17
17
  * @param nodeMass - Dictionary mapping nodes to their masses
18
18
  * @param nodeSize - Dictionary mapping nodes to their sizes
19
19
  * @param weight - Edge attribute for weight
20
- * @param dissuadeHubs - Whether to prevent hub nodes from clustering
20
+ * @param _dissuadeHubs - Whether to prevent hub nodes from clustering (unused)
21
21
  * @param linlog - Whether to use logarithmic attraction
22
22
  * @param seed - Random seed for initial positions
23
23
  * @param dim - Dimension of layout
@@ -35,7 +35,7 @@ export function forceatlas2Layout(
35
35
  nodeMass: Record<Node, number> | null = null,
36
36
  nodeSize: Record<Node, number> | null = null,
37
37
  weight: string | null = null,
38
- dissuadeHubs: boolean = false,
38
+ _dissuadeHubs: boolean = false,
39
39
  linlog: boolean = false,
40
40
  seed: number | null = null,
41
41
  dim: number = 2,
@@ -75,8 +75,8 @@ export function forceatlas2Layout(
75
75
  }
76
76
  } else {
77
77
  // Some nodes don't have positions, initialize within the range of existing positions
78
- const minPos = Array(dim).fill(Number.POSITIVE_INFINITY);
79
- const maxPos = Array(dim).fill(Number.NEGATIVE_INFINITY);
78
+ const minPos: number[] = Array(dim).fill(Number.POSITIVE_INFINITY);
79
+ const maxPos: number[] = Array(dim).fill(Number.NEGATIVE_INFINITY);
80
80
 
81
81
  // Find min and max of existing positions
82
82
  for (const node in pos) {
@@ -125,17 +125,22 @@ export function forceatlas2Layout(
125
125
  // Set node masses and sizes
126
126
  for (let i = 0; i < nodes.length; i++) {
127
127
  const node = nodes[i];
128
- mass[i] =
129
- nodeMass && nodeMass[node] ? nodeMass[node] : Array.isArray(graph) ? 1 : getNodeDegree(graph, node) + 1;
128
+ if (nodeMass && nodeMass[node]) {
129
+ mass[i] = nodeMass[node];
130
+ } else if (Array.isArray(graph)) {
131
+ mass[i] = 1;
132
+ } else {
133
+ mass[i] = getNodeDegree(graph, node) + 1;
134
+ }
130
135
 
131
136
  size[i] = nodeSize && nodeSize[node] ? nodeSize[node] : 1;
132
137
  }
133
138
 
134
139
  // Create adjacency matrix
135
140
  const n = nodes.length;
136
- const A = Array(n)
141
+ const A: number[][] = Array(n)
137
142
  .fill(0)
138
- .map(() => Array(n).fill(0));
143
+ .map(() => Array(n).fill(0) as number[]);
139
144
 
140
145
  // Populate adjacency matrix with edge weights
141
146
  const edges = Array.isArray(graph) ? ([] as Edge[]) : graph.edges();
@@ -159,66 +164,69 @@ export function forceatlas2Layout(
159
164
  }
160
165
 
161
166
  // Initialize force arrays
162
- const gravities = Array(n)
167
+ const gravities: number[][] = Array(n)
163
168
  .fill(0)
164
- .map(() => Array(dim).fill(0));
165
- const attraction = Array(n)
169
+ .map(() => Array(dim).fill(0) as number[]);
170
+ const attraction: number[][] = Array(n)
166
171
  .fill(0)
167
- .map(() => Array(dim).fill(0));
168
- const repulsion = Array(n)
172
+ .map(() => Array(dim).fill(0) as number[]);
173
+ const repulsion: number[][] = Array(n)
169
174
  .fill(0)
170
- .map(() => Array(dim).fill(0));
175
+ .map(() => Array(dim).fill(0) as number[]);
171
176
 
172
177
  // Simulation parameters
173
178
  let speed = 1;
174
179
  let speedEfficiency = 1;
175
- const swing = 1;
176
- const traction = 1;
180
+ const _swing = 1; // Reserved for adaptive speed control
181
+ const _traction = 1; // Reserved for adaptive speed control
177
182
 
178
183
  // Helper function to estimate factor for force scaling
179
184
  function estimateFactor(
180
- n: number,
181
- swing: number,
182
- traction: number,
183
- speed: number,
184
- speedEfficiency: number,
185
- jitterTolerance: number,
185
+ nodeCount: number,
186
+ swingValue: number,
187
+ tractionValue: number,
188
+ currentSpeed: number,
189
+ currentSpeedEfficiency: number,
190
+ jitterToleranceParam: number,
186
191
  ): [number, number] {
192
+ let resultSpeed = currentSpeed;
193
+ let resultSpeedEfficiency = currentSpeedEfficiency;
194
+
187
195
  // Optimal jitter parameters
188
- const optJitter = 0.05 * Math.sqrt(n);
196
+ const optJitter = 0.05 * Math.sqrt(nodeCount);
189
197
  const minJitter = Math.sqrt(optJitter);
190
198
  const maxJitter = 10;
191
199
  const minSpeedEfficiency = 0.05;
192
200
 
193
201
  // Estimate jitter based on current state
194
- const other = Math.min(maxJitter, (optJitter * traction) / (n * n));
195
- let jitter = jitterTolerance * Math.max(minJitter, other);
202
+ const other = Math.min(maxJitter, (optJitter * tractionValue) / (nodeCount * nodeCount));
203
+ let jitter = jitterToleranceParam * Math.max(minJitter, other);
196
204
 
197
205
  // Adjust speed efficiency based on swing/traction ratio
198
- if (swing / traction > 2.0) {
199
- if (speedEfficiency > minSpeedEfficiency) {
200
- speedEfficiency *= 0.5;
206
+ if (swingValue / tractionValue > 2.0) {
207
+ if (resultSpeedEfficiency > minSpeedEfficiency) {
208
+ resultSpeedEfficiency *= 0.5;
201
209
  }
202
- jitter = Math.max(jitter, jitterTolerance);
210
+ jitter = Math.max(jitter, jitterToleranceParam);
203
211
  }
204
212
 
205
213
  // Calculate target speed
206
- const targetSpeed = swing === 0 ? Number.POSITIVE_INFINITY : (jitter * speedEfficiency * traction) / swing;
214
+ const targetSpeed = swingValue === 0 ? Number.POSITIVE_INFINITY : (jitter * resultSpeedEfficiency * tractionValue) / swingValue;
207
215
 
208
216
  // Further adjust speed efficiency
209
- if (swing > jitter * traction) {
210
- if (speedEfficiency > minSpeedEfficiency) {
211
- speedEfficiency *= 0.7;
217
+ if (swingValue > jitter * tractionValue) {
218
+ if (resultSpeedEfficiency > minSpeedEfficiency) {
219
+ resultSpeedEfficiency *= 0.7;
212
220
  }
213
- } else if (speed < 1000) {
214
- speedEfficiency *= 1.3;
221
+ } else if (resultSpeed < 1000) {
222
+ resultSpeedEfficiency *= 1.3;
215
223
  }
216
224
 
217
225
  // Limit the speed increase
218
226
  const maxRise = 0.5;
219
- speed = speed + Math.min(targetSpeed - speed, maxRise * speed);
227
+ resultSpeed = resultSpeed + Math.min(targetSpeed - resultSpeed, maxRise * resultSpeed);
220
228
 
221
- return [speed, speedEfficiency];
229
+ return [resultSpeed, resultSpeedEfficiency];
222
230
  }
223
231
 
224
232
  // Main simulation loop
@@ -233,17 +241,17 @@ export function forceatlas2Layout(
233
241
  }
234
242
 
235
243
  // Compute pairwise differences and distances
236
- const diff = Array(n)
244
+ const diff: number[][][] = Array(n)
237
245
  .fill(0)
238
246
  .map(() =>
239
247
  Array(n)
240
248
  .fill(0)
241
- .map(() => Array(dim).fill(0)),
249
+ .map(() => Array(dim).fill(0) as number[]),
242
250
  );
243
251
 
244
- const distance = Array(n)
252
+ const distance: number[][] = Array(n)
245
253
  .fill(0)
246
- .map(() => Array(n).fill(0));
254
+ .map(() => Array(n).fill(0) as number[]);
247
255
 
248
256
  for (let i = 0; i < n; i++) {
249
257
  for (let j = 0; j < n; j++) {
@@ -253,7 +261,7 @@ export function forceatlas2Layout(
253
261
  diff[i][j][d] = posArray[i][d] - posArray[j][d];
254
262
  }
255
263
 
256
- distance[i][j] = Math.sqrt(diff[i][j].reduce((sum, d) => sum + d * d, 0));
264
+ distance[i][j] = Math.sqrt(diff[i][j].reduce((sum: number, d: number) => sum + d * d, 0));
257
265
  // Prevent division by zero
258
266
  if (distance[i][j] < 0.01) {distance[i][j] = 0.01;}
259
267
  }
@@ -324,7 +332,7 @@ export function forceatlas2Layout(
324
332
 
325
333
  // Calculate gravity forces
326
334
  // First find the center of mass
327
- const centerOfMass = Array(dim).fill(0);
335
+ const centerOfMass: number[] = Array(dim).fill(0);
328
336
  for (let i = 0; i < n; i++) {
329
337
  for (let d = 0; d < dim; d++) {
330
338
  centerOfMass[d] += posArray[i][d] / n;
@@ -344,7 +352,7 @@ export function forceatlas2Layout(
344
352
  }
345
353
  } else {
346
354
  // Regular gravity model
347
- const dist = Math.sqrt(posCentered.reduce((sum, val) => sum + val * val, 0));
355
+ const dist = Math.sqrt(posCentered.reduce((sum: number, val: number) => sum + val * val, 0));
348
356
 
349
357
  if (dist > 0.01) {
350
358
  for (let d = 0; d < dim; d++) {
@@ -369,13 +377,13 @@ export function forceatlas2Layout(
369
377
 
370
378
  // Calculate swing and traction for this node
371
379
  const oldPos = [...posArray[i]];
372
- const newPos = oldPos.map((p, d) => p + update[i][d]);
380
+ const newPos = oldPos.map((p, d) => p + (update[i][d] as number));
373
381
 
374
382
  const swingVector = oldPos.map((p, d) => p - newPos[d]);
375
383
  const tractionVector = oldPos.map((p, d) => p + newPos[d]);
376
384
 
377
- const swingMagnitude = Math.sqrt(swingVector.reduce((sum, val) => sum + val * val, 0));
378
- const tractionMagnitude = Math.sqrt(tractionVector.reduce((sum, val) => sum + val * val, 0));
385
+ const swingMagnitude = Math.sqrt(swingVector.reduce((sum: number, val: number) => sum + val * val, 0));
386
+ const tractionMagnitude = Math.sqrt(tractionVector.reduce((sum: number, val: number) => sum + val * val, 0));
379
387
 
380
388
  totalSwing += mass[i] * swingMagnitude;
381
389
  totalTraction += 0.5 * mass[i] * tractionMagnitude;
@@ -399,7 +407,7 @@ export function forceatlas2Layout(
399
407
 
400
408
  if (adjustSizes) {
401
409
  // Calculate displacement magnitude
402
- const df = Math.sqrt(update[i].reduce((sum, val) => sum + val * val, 0));
410
+ const df = Math.sqrt(update[i].reduce((sum: number, val: number) => sum + val * val, 0));
403
411
  const swinging = mass[i] * df;
404
412
 
405
413
  // Determine scaling factor with size adjustments
@@ -407,7 +415,7 @@ export function forceatlas2Layout(
407
415
  factor = Math.min(factor * df, 10) / df;
408
416
  } else {
409
417
  // Standard scaling factor
410
- const swinging = mass[i] * Math.sqrt(update[i].reduce((sum, val) => sum + val * val, 0));
418
+ const swinging = (mass[i] as number) * Math.sqrt(update[i].reduce((sum: number, val: number) => sum + val * val, 0));
411
419
  factor = speed / (1 + Math.sqrt(speed * swinging));
412
420
  }
413
421
 
@@ -2,7 +2,7 @@
2
2
  * Fruchterman-Reingold force-directed layout algorithm
3
3
  */
4
4
 
5
- import { Edge, Graph, Node, PositionMap } from "../../types";
5
+ import type { Graph, Node, PositionMap } from "../../types";
6
6
  import { getEdgesFromGraph,getNodesFromGraph } from "../../utils/graph";
7
7
  import { _processParams } from "../../utils/params";
8
8
  import { RandomNumberGenerator } from "../../utils/random";
@@ -34,7 +34,7 @@ export function fruchtermanReingoldLayout(
34
34
  ): PositionMap {
35
35
  const processed = _processParams(G, center, dim);
36
36
  const graph = processed.G;
37
- center = processed.center;
37
+ ({ center } = processed);
38
38
 
39
39
  const nodes = getNodesFromGraph(graph);
40
40
  const edges = getEdgesFromGraph(graph);
@@ -1,5 +1,4 @@
1
- import type { DistanceMap } from "../../algorithms/optimization";
2
- import { _computeShortestPathDistances, _kamadaKawaiSolve } from "../../algorithms/optimization";
1
+ import { _computeShortestPathDistances, _kamadaKawaiSolve, type DistanceMap } from "../../algorithms/optimization";
3
2
  import type { Graph, Node, PositionMap } from "../../types";
4
3
  import { getNodesFromGraph } from "../../utils/graph";
5
4
  import { _processParams } from "../../utils/params";
@@ -28,7 +27,7 @@ export function kamadaKawaiLayout(
28
27
  ): PositionMap {
29
28
  const processed = _processParams(G, center, dim);
30
29
  const graph = processed.G;
31
- center = processed.center;
30
+ ({ center } = processed);
32
31
 
33
32
  const nodes = getNodesFromGraph(graph);
34
33
 
@@ -28,7 +28,7 @@ export function circularLayout(
28
28
 
29
29
  const processed = _processParams(G, center, dim);
30
30
  const nodes = getNodesFromGraph(processed.G);
31
- center = processed.center;
31
+ ({ center } = processed);
32
32
 
33
33
  const pos: PositionMap = {};
34
34
 
@@ -29,7 +29,7 @@ export function shellLayout(
29
29
 
30
30
  const processed = _processParams(G, center, dim);
31
31
  const nodes = getNodesFromGraph(processed.G);
32
- center = processed.center;
32
+ ({ center } = processed);
33
33
 
34
34
  const pos: PositionMap = {};
35
35
 
@@ -2,7 +2,7 @@
2
2
  * Spiral layout algorithm
3
3
  */
4
4
 
5
- import { Graph, Node, PositionMap } from "../../types";
5
+ import type { Graph, PositionMap } from "../../types";
6
6
  import { getNodesFromGraph } from "../../utils/graph";
7
7
  import { _processParams } from "../../utils/params";
8
8
  import { rescaleLayout } from "../../utils/rescale";
@@ -31,7 +31,7 @@ export function spiralLayout(
31
31
 
32
32
  const processed = _processParams(G, center || [0, 0], dim);
33
33
  const nodes = getNodesFromGraph(processed.G);
34
- center = processed.center;
34
+ ({ center } = processed);
35
35
 
36
36
  const pos: PositionMap = {};
37
37
 
@@ -74,7 +74,7 @@ export function spiralLayout(
74
74
  }
75
75
 
76
76
  // Rescale positions and add center offset
77
- const scaledPositions = rescaleLayout(posArray as any, scale) as any;
77
+ const scaledPositions = rescaleLayout(posArray, scale) as number[][];
78
78
  for (let i = 0; i < scaledPositions.length; i++) {
79
79
  scaledPositions[i][0] += center[0];
80
80
  scaledPositions[i][1] += center[1];
@@ -1,4 +1,4 @@
1
- import type { Edge, Graph, Node, PositionMap } from "../../types";
1
+ import type { Graph, Node, PositionMap } from "../../types";
2
2
  import { getNeighbors,getNodesFromGraph } from "../../utils/graph";
3
3
  import { _processParams } from "../../utils/params";
4
4
  import { multipartiteLayout } from "./multipartite";
@@ -27,7 +27,7 @@ export function bfsLayout(
27
27
  }
28
28
 
29
29
  const graph = processed.G;
30
- center = processed.center;
30
+ ({ center } = processed);
31
31
 
32
32
  const allNodes = getNodesFromGraph(graph);
33
33
 
@@ -31,7 +31,7 @@ export function bipartiteLayout(
31
31
 
32
32
  const processed = _processParams(G, center || [0, 0], 2);
33
33
  const graph = processed.G;
34
- center = processed.center;
34
+ ({ center } = processed);
35
35
 
36
36
  const allNodes = getNodesFromGraph(graph);
37
37
 
@@ -1,5 +1,5 @@
1
1
  import type { Graph, Node, PositionMap } from "../../types";
2
- import { getNeighbors,getNodesFromGraph } from "../../utils/graph";
2
+ import { getNodesFromGraph } from "../../utils/graph";
3
3
  import { _processParams } from "../../utils/params";
4
4
  import { rescaleLayout } from "../../utils/rescale";
5
5
 
@@ -25,7 +25,7 @@ export function multipartiteLayout(
25
25
 
26
26
  const processed = _processParams(G, center || [0, 0], 2);
27
27
  const graph = processed.G;
28
- center = processed.center;
28
+ ({ center } = processed);
29
29
 
30
30
  const allNodes = getNodesFromGraph(graph);
31
31
 
@@ -58,7 +58,7 @@ export function multipartiteLayout(
58
58
  let pos: PositionMap = {};
59
59
 
60
60
  // Process each layer
61
- Object.entries(layers).forEach(([layer, nodes], layerIdx) => {
61
+ Object.entries(layers).forEach(([_layer, nodes], layerIdx) => {
62
62
  const layerNodes = Array.isArray(nodes) ? nodes : [nodes];
63
63
  const layerSize = layerNodes.length;
64
64
 
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { checkPlanarity, combinatorialEmbeddingToPos } from "../../algorithms/planarity";
6
- import { Edge, Graph, Node, PositionMap } from "../../types";
6
+ import type { Graph, PositionMap } from "../../types";
7
7
  import { getEdgesFromGraph,getNodesFromGraph } from "../../utils/graph";
8
8
  import { _processParams } from "../../utils/params";
9
9
  import { rescaleLayout } from "../../utils/rescale";
@@ -36,7 +36,7 @@ export function planarLayout(
36
36
  }
37
37
 
38
38
  const graph = processed.G;
39
- center = processed.center;
39
+ ({ center } = processed);
40
40
 
41
41
  const nodes = getNodesFromGraph(graph);
42
42
  const edges = getEdgesFromGraph(graph);
@@ -2,7 +2,7 @@
2
2
  * Spectral layout algorithm using eigenvectors of the graph Laplacian
3
3
  */
4
4
 
5
- import { Edge, Graph, Node, PositionMap } from "../../types";
5
+ import type { Graph, Node, PositionMap } from "../../types";
6
6
  import { getEdgesFromGraph,getNodesFromGraph } from "../../utils/graph";
7
7
  import { _processParams } from "../../utils/params";
8
8
  import { rescaleLayout } from "../../utils/rescale";
@@ -23,7 +23,7 @@ export function spectralLayout(
23
23
  ): PositionMap {
24
24
  const processed = _processParams(G, center, dim);
25
25
  const graph = processed.G;
26
- center = processed.center;
26
+ ({ center } = processed);
27
27
 
28
28
  const nodes = getNodesFromGraph(graph);
29
29
 
@@ -47,9 +47,9 @@ export function spectralLayout(
47
47
  nodeIndices[node] = i;
48
48
  });
49
49
 
50
- const A = Array(N)
50
+ const A: number[][] = Array(N)
51
51
  .fill(0)
52
- .map(() => Array(N).fill(0));
52
+ .map(() => Array(N).fill(0) as number[]);
53
53
  const edges = getEdgesFromGraph(graph);
54
54
 
55
55
  for (const [source, target] of edges) {
@@ -60,12 +60,12 @@ export function spectralLayout(
60
60
  }
61
61
 
62
62
  // Create Laplacian matrix: L = D - A where D is degree matrix
63
- const L = Array(N)
63
+ const L: number[][] = Array(N)
64
64
  .fill(0)
65
- .map(() => Array(N).fill(0));
65
+ .map(() => Array(N).fill(0) as number[]);
66
66
  for (let i = 0; i < N; i++) {
67
67
  // Compute degree (sum of row)
68
- L[i][i] = A[i].reduce((sum, val) => sum + val, 0);
68
+ L[i][i] = A[i].reduce((sum: number, val: number) => sum + val, 0);
69
69
  for (let j = 0; j < N; j++) {
70
70
  L[i][j] -= A[i][j];
71
71
  }
@@ -95,7 +95,7 @@ export function spectralLayout(
95
95
  // This is a simplification of the actual algorithm
96
96
  for (let iter = 0; iter < 100; iter++) {
97
97
  // Apply Laplacian
98
- const newVec = Array(N).fill(0);
98
+ const newVec: number[] = Array(N).fill(0);
99
99
  for (let i = 0; i < N; i++) {
100
100
  for (let j = 0; j < N; j++) {
101
101
  newVec[i] += L[i][j] * vector[j];
@@ -103,13 +103,13 @@ export function spectralLayout(
103
103
  }
104
104
 
105
105
  // Orthogonalize against the constant vector (eigenvector with eigenvalue 0)
106
- const mean = newVec.reduce((acc, val) => acc + val, 0) / N;
106
+ const mean = newVec.reduce((acc: number, val: number) => acc + val, 0) / N;
107
107
  newVec.forEach((val, idx, arr) => {
108
108
  arr[idx] = val - mean;
109
109
  });
110
110
 
111
111
  // Normalize
112
- const newNorm = Math.sqrt(newVec.reduce((acc, val) => acc + val * val, 0));
112
+ const newNorm = Math.sqrt(newVec.reduce((acc: number, val: number) => acc + val * val, 0));
113
113
  if (newNorm < 1e-10) {continue;} // Skip if vector is close to zero
114
114
 
115
115
  vector = newVec.map((val) => val / newNorm);
@@ -129,7 +129,7 @@ export function spectralLayout(
129
129
  }
130
130
 
131
131
  // Rescale and create position dictionary
132
- const scaledPositions = rescaleLayout(positions as any, scale);
132
+ const scaledPositions = rescaleLayout(positions, scale);
133
133
  const pos: PositionMap = {};
134
134
  nodes.forEach((node: Node, i: number) => {
135
135
  pos[node] = (scaledPositions as number[][])[i].map((val: number, j: number) => val + center[j]);
@@ -9,5 +9,5 @@ export type Edge = [Node, Node];
9
9
  export type Graph = {
10
10
  nodes: () => Node[];
11
11
  edges: () => Edge[];
12
- getEdgeData?: (source: Node, target: Node, attr: string) => any;
12
+ getEdgeData?: (source: Node, target: Node, attr: string) => number | undefined;
13
13
  };
@@ -4,28 +4,28 @@
4
4
  */
5
5
 
6
6
  export const np = {
7
- zeros: function (shape: number | number[]): number | number[] | number[][] | any[] {
7
+ zeros: function (shape: number | number[]): number[] | number[][] {
8
8
  if (typeof shape === "number") {
9
- return Array(shape).fill(0);
9
+ return Array(shape).fill(0) as number[];
10
10
  }
11
11
  if (shape.length === 1) {
12
- return Array(shape[0]).fill(0);
12
+ return Array(shape[0]).fill(0) as number[];
13
13
  }
14
14
  return Array(shape[0])
15
15
  .fill(0)
16
- .map(() => this.zeros(shape.slice(1)));
16
+ .map(() => this.zeros(shape.slice(1))) as number[][];
17
17
  },
18
18
 
19
- ones: function (shape: number | number[]): number | any[] {
19
+ ones: function (shape: number | number[]): number[] | number[][] {
20
20
  if (typeof shape === "number") {
21
- return Array(shape).fill(1);
21
+ return Array(shape).fill(1) as number[];
22
22
  }
23
23
  if (shape.length === 1) {
24
- return Array(shape[0]).fill(1);
24
+ return Array(shape[0]).fill(1) as number[];
25
25
  }
26
26
  return Array(shape[0])
27
27
  .fill(1)
28
- .map(() => this.ones(shape.slice(1)));
28
+ .map(() => this.ones(shape.slice(1))) as number[][];
29
29
  },
30
30
 
31
31
  linspace: function (start: number, stop: number, num: number): number[] {
@@ -36,12 +36,12 @@ export const np = {
36
36
  return Array.from({ length: num }, (_, i) => start + i * step);
37
37
  },
38
38
 
39
- array: function (arr: any): any[] {
39
+ array: function (arr: number | number[]): number[] {
40
40
  return Array.isArray(arr) ? [...arr] : [arr];
41
41
  },
42
42
 
43
- repeat: function (a: any, repeats: number): any[] {
44
- const result: any[] = [];
43
+ repeat: function (a: number | number[], repeats: number): number[] {
44
+ const result: number[] = [];
45
45
  for (let i = 0; i < repeats; i++) {
46
46
  result.push(...np.array(a));
47
47
  }