@graphty/layout 1.2.3 → 1.2.5

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 (203) hide show
  1. package/dist/src/algorithms/index.d.ts +5 -0
  2. package/dist/src/algorithms/index.js +6 -0
  3. package/dist/src/algorithms/index.js.map +1 -0
  4. package/dist/src/algorithms/optimization/index.d.ts +7 -0
  5. package/dist/src/algorithms/optimization/index.js +7 -0
  6. package/dist/src/algorithms/optimization/index.js.map +1 -0
  7. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts +32 -0
  8. package/dist/src/algorithms/optimization/kamada-kawai-solver.js +176 -0
  9. package/dist/src/algorithms/optimization/kamada-kawai-solver.js.map +1 -0
  10. package/dist/src/algorithms/optimization/lbfgs.d.ts +13 -0
  11. package/dist/src/algorithms/optimization/lbfgs.js +56 -0
  12. package/dist/src/algorithms/optimization/lbfgs.js.map +1 -0
  13. package/dist/src/algorithms/optimization/line-search.d.ts +15 -0
  14. package/dist/src/algorithms/optimization/line-search.js +37 -0
  15. package/dist/src/algorithms/optimization/line-search.js.map +1 -0
  16. package/dist/src/algorithms/optimization/types.d.ts +5 -0
  17. package/dist/src/algorithms/optimization/types.js +5 -0
  18. package/dist/src/algorithms/optimization/types.js.map +1 -0
  19. package/dist/src/algorithms/planarity/check.d.ts +17 -0
  20. package/dist/src/algorithms/planarity/check.js +30 -0
  21. package/dist/src/algorithms/planarity/check.js.map +1 -0
  22. package/dist/src/algorithms/planarity/embedding.d.ts +29 -0
  23. package/dist/src/algorithms/planarity/embedding.js +179 -0
  24. package/dist/src/algorithms/planarity/embedding.js.map +1 -0
  25. package/dist/src/algorithms/planarity/index.d.ts +7 -0
  26. package/dist/src/algorithms/planarity/index.js +8 -0
  27. package/dist/src/algorithms/planarity/index.js.map +1 -0
  28. package/dist/src/algorithms/planarity/lr-test.d.ts +15 -0
  29. package/dist/src/algorithms/planarity/lr-test.js +54 -0
  30. package/dist/src/algorithms/planarity/lr-test.js.map +1 -0
  31. package/dist/src/algorithms/planarity/special-graphs.d.ts +28 -0
  32. package/dist/src/algorithms/planarity/special-graphs.js +108 -0
  33. package/dist/src/algorithms/planarity/special-graphs.js.map +1 -0
  34. package/dist/src/generators/basic.d.ts +28 -0
  35. package/dist/src/generators/basic.js +79 -0
  36. package/dist/src/generators/basic.js.map +1 -0
  37. package/dist/src/generators/bipartite.d.ts +16 -0
  38. package/dist/src/generators/bipartite.js +40 -0
  39. package/dist/src/generators/bipartite.js.map +1 -0
  40. package/dist/src/generators/grid.d.ts +11 -0
  41. package/dist/src/generators/grid.js +37 -0
  42. package/dist/src/generators/grid.js.map +1 -0
  43. package/dist/src/generators/index.d.ts +8 -0
  44. package/dist/src/generators/index.js +9 -0
  45. package/dist/src/generators/index.js.map +1 -0
  46. package/dist/src/generators/random.d.ts +12 -0
  47. package/dist/src/generators/random.js +34 -0
  48. package/dist/src/generators/random.js.map +1 -0
  49. package/dist/src/generators/scale-free.d.ts +12 -0
  50. package/dist/src/generators/scale-free.js +62 -0
  51. package/dist/src/generators/scale-free.js.map +1 -0
  52. package/dist/src/index.d.ts +10 -0
  53. package/dist/src/index.js +15 -0
  54. package/dist/src/index.js.map +1 -0
  55. package/dist/src/layouts/basic/index.d.ts +4 -0
  56. package/dist/src/layouts/basic/index.js +5 -0
  57. package/dist/src/layouts/basic/index.js.map +1 -0
  58. package/dist/src/layouts/basic/random.d.ts +14 -0
  59. package/dist/src/layouts/basic/random.js +27 -0
  60. package/dist/src/layouts/basic/random.js.map +1 -0
  61. package/dist/src/layouts/force-directed/arf.d.ts +13 -0
  62. package/dist/src/layouts/force-directed/arf.js +103 -0
  63. package/dist/src/layouts/force-directed/arf.js.map +1 -0
  64. package/dist/src/layouts/force-directed/forceatlas2.d.ts +22 -0
  65. package/dist/src/layouts/force-directed/forceatlas2.js +340 -0
  66. package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -0
  67. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +19 -0
  68. package/dist/src/layouts/force-directed/fruchterman-reingold.js +131 -0
  69. package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -0
  70. package/dist/src/layouts/force-directed/index.d.ts +8 -0
  71. package/dist/src/layouts/force-directed/index.js +9 -0
  72. package/dist/src/layouts/force-directed/index.js.map +1 -0
  73. package/dist/src/layouts/force-directed/kamada-kawai.d.ts +15 -0
  74. package/dist/src/layouts/force-directed/kamada-kawai.js +87 -0
  75. package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -0
  76. package/dist/src/layouts/force-directed/spring.d.ts +19 -0
  77. package/dist/src/layouts/force-directed/spring.js +23 -0
  78. package/dist/src/layouts/force-directed/spring.js.map +1 -0
  79. package/dist/src/layouts/geometric/circular.d.ts +14 -0
  80. package/dist/src/layouts/geometric/circular.js +67 -0
  81. package/dist/src/layouts/geometric/circular.js.map +1 -0
  82. package/dist/src/layouts/geometric/index.d.ts +6 -0
  83. package/dist/src/layouts/geometric/index.js +7 -0
  84. package/dist/src/layouts/geometric/index.js.map +1 -0
  85. package/dist/src/layouts/geometric/shell.d.ts +15 -0
  86. package/dist/src/layouts/geometric/shell.js +67 -0
  87. package/dist/src/layouts/geometric/shell.js.map +1 -0
  88. package/dist/src/layouts/geometric/spiral.d.ts +16 -0
  89. package/dist/src/layouts/geometric/spiral.js +73 -0
  90. package/dist/src/layouts/geometric/spiral.js.map +1 -0
  91. package/dist/src/layouts/hierarchical/bfs.d.ts +12 -0
  92. package/dist/src/layouts/hierarchical/bfs.js +62 -0
  93. package/dist/src/layouts/hierarchical/bfs.js.map +1 -0
  94. package/dist/src/layouts/hierarchical/bipartite.d.ts +16 -0
  95. package/dist/src/layouts/hierarchical/bipartite.js +72 -0
  96. package/dist/src/layouts/hierarchical/bipartite.js.map +1 -0
  97. package/dist/src/layouts/hierarchical/index.d.ts +6 -0
  98. package/dist/src/layouts/hierarchical/index.js +7 -0
  99. package/dist/src/layouts/hierarchical/index.js.map +1 -0
  100. package/dist/src/layouts/hierarchical/multipartite.d.ts +12 -0
  101. package/dist/src/layouts/hierarchical/multipartite.js +73 -0
  102. package/dist/src/layouts/hierarchical/multipartite.js.map +1 -0
  103. package/dist/src/layouts/index.d.ts +8 -0
  104. package/dist/src/layouts/index.js +9 -0
  105. package/dist/src/layouts/index.js.map +1 -0
  106. package/dist/src/layouts/specialized/index.d.ts +5 -0
  107. package/dist/src/layouts/specialized/index.js +6 -0
  108. package/dist/src/layouts/specialized/index.js.map +1 -0
  109. package/dist/src/layouts/specialized/planar.d.ts +14 -0
  110. package/dist/src/layouts/specialized/planar.js +47 -0
  111. package/dist/src/layouts/specialized/planar.js.map +1 -0
  112. package/dist/src/layouts/specialized/spectral.d.ts +14 -0
  113. package/dist/src/layouts/specialized/spectral.js +106 -0
  114. package/dist/src/layouts/specialized/spectral.js.map +1 -0
  115. package/dist/src/types/embedding.d.ts +9 -0
  116. package/dist/src/types/embedding.js +5 -0
  117. package/dist/src/types/embedding.js.map +1 -0
  118. package/dist/src/types/graph.d.ts +10 -0
  119. package/dist/src/types/graph.js +5 -0
  120. package/dist/src/types/graph.js.map +1 -0
  121. package/dist/src/types/index.d.ts +6 -0
  122. package/dist/src/types/index.js +7 -0
  123. package/dist/src/types/index.js.map +1 -0
  124. package/dist/src/types/layout.d.ts +6 -0
  125. package/dist/src/types/layout.js +5 -0
  126. package/dist/src/types/layout.js.map +1 -0
  127. package/dist/src/utils/graph.d.ts +34 -0
  128. package/dist/src/utils/graph.js +69 -0
  129. package/dist/src/utils/graph.js.map +1 -0
  130. package/dist/src/utils/index.d.ts +8 -0
  131. package/dist/src/utils/index.js +9 -0
  132. package/dist/src/utils/index.js.map +1 -0
  133. package/dist/src/utils/numpy.d.ts +17 -0
  134. package/dist/src/utils/numpy.js +101 -0
  135. package/dist/src/utils/numpy.js.map +1 -0
  136. package/dist/src/utils/params.d.ts +17 -0
  137. package/dist/src/utils/params.js +22 -0
  138. package/dist/src/utils/params.js.map +1 -0
  139. package/dist/src/utils/random.d.ts +14 -0
  140. package/dist/src/utils/random.js +42 -0
  141. package/dist/src/utils/random.js.map +1 -0
  142. package/dist/src/utils/rescale.d.ts +21 -0
  143. package/dist/src/utils/rescale.js +194 -0
  144. package/dist/src/utils/rescale.js.map +1 -0
  145. package/dist/vitest.config.js +1 -1
  146. package/dist/vitest.config.js.map +1 -1
  147. package/package.json +19 -4
  148. package/src/layouts/force-directed/forceatlas2.ts +23 -4
  149. package/src/utils/rescale.ts +90 -15
  150. package/.env.example +0 -11
  151. package/.github/workflows/ci.yml +0 -101
  152. package/.releaserc.json +0 -22
  153. package/CHANGELOG.md +0 -52
  154. package/CLAUDE.md +0 -104
  155. package/CONTRIBUTING.md +0 -1
  156. package/DEPLOYMENT.md +0 -59
  157. package/build-examples-script.js +0 -26
  158. package/commitlint.config.js +0 -37
  159. package/examples/3d-force-directed.html +0 -611
  160. package/examples/3d-kamada-kawai.html +0 -379
  161. package/examples/3d-layout-comparison.html +0 -448
  162. package/examples/3d-spherical-layout.html +0 -319
  163. package/examples/arf-layout.html +0 -524
  164. package/examples/bfs-layout.html +0 -487
  165. package/examples/bipartite-layout.html +0 -382
  166. package/examples/circular-layout.html +0 -292
  167. package/examples/forceatlas2-layout.html +0 -588
  168. package/examples/index.html +0 -241
  169. package/examples/kamada-kawai-layout.html +0 -425
  170. package/examples/layout-helpers.js +0 -23
  171. package/examples/layout.js +0 -2304
  172. package/examples/multipartite-layout.html +0 -410
  173. package/examples/planar-layout.html +0 -388
  174. package/examples/random-layout.html +0 -328
  175. package/examples/shell-layout.html +0 -323
  176. package/examples/spectral-layout.html +0 -335
  177. package/examples/spiral-layout.html +0 -447
  178. package/examples/spring-layout.html +0 -313
  179. package/test/arf-layout.test.ts +0 -443
  180. package/test/bfs-layout.test.ts +0 -427
  181. package/test/bipartite-layout.test.ts +0 -344
  182. package/test/circular-layout.test.ts +0 -442
  183. package/test/forceatlas2-layout.test.ts +0 -405
  184. package/test/fruchterman-reingold-layout.test.ts +0 -477
  185. package/test/graph-generators.test.ts +0 -450
  186. package/test/kamada-kawai-layout.test.ts +0 -623
  187. package/test/multipartite-layout.test.ts +0 -404
  188. package/test/planar-layout.test.ts +0 -266
  189. package/test/random-layout.test.ts +0 -254
  190. package/test/rescale-layout.test.ts +0 -373
  191. package/test/shell-layout.test.ts +0 -347
  192. package/test/spectral-layout.test.ts +0 -378
  193. package/test/spiral-layout.test.ts +0 -338
  194. package/test/spring-layout.test.ts +0 -241
  195. package/test/test-utils.ts +0 -34
  196. package/test/utils-graph.test.ts +0 -155
  197. package/test/utils-index.test.ts +0 -77
  198. package/test/utils-numpy.test.ts +0 -272
  199. package/test/utils-params.test.ts +0 -99
  200. package/test/utils-random.test.ts +0 -229
  201. package/tsconfig.json +0 -16
  202. package/vite.config.js +0 -36
  203. package/vitest.config.ts +0 -30
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Random number generator for seed-based randomization
3
+ * Maintains exact same functionality as original implementation
4
+ */
5
+ export declare class RandomNumberGenerator {
6
+ private seed;
7
+ private m;
8
+ private a;
9
+ private c;
10
+ private _state;
11
+ constructor(seed?: number);
12
+ _next(): number;
13
+ rand(shape?: number | number[] | null): number | number[] | number[][];
14
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Random number generator for seed-based randomization
3
+ * Maintains exact same functionality as original implementation
4
+ */
5
+ export class RandomNumberGenerator {
6
+ constructor(seed) {
7
+ this.seed = seed || Math.floor(Math.random() * 1000000);
8
+ this.m = 2 ** 35 - 31;
9
+ this.a = 185852;
10
+ this.c = 1;
11
+ this._state = this.seed % this.m;
12
+ }
13
+ _next() {
14
+ this._state = (this.a * this._state + this.c) % this.m;
15
+ return this._state / this.m;
16
+ }
17
+ rand(shape = null) {
18
+ if (shape === null) {
19
+ return this._next();
20
+ }
21
+ if (typeof shape === 'number') {
22
+ const result = [];
23
+ for (let i = 0; i < shape; i++) {
24
+ result.push(this._next());
25
+ }
26
+ return result;
27
+ }
28
+ if (shape.length === 1) {
29
+ const result = [];
30
+ for (let i = 0; i < shape[0]; i++) {
31
+ result.push(this._next());
32
+ }
33
+ return result;
34
+ }
35
+ const result = [];
36
+ for (let i = 0; i < shape[0]; i++) {
37
+ result.push(this.rand(shape.slice(1)));
38
+ }
39
+ return result;
40
+ }
41
+ }
42
+ //# sourceMappingURL=random.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"random.js","sourceRoot":"","sources":["../../../src/utils/random.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,OAAO,qBAAqB;IAOhC,YAAY,IAAa;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC;QACxD,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC;QAChB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACX,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,CAAC,QAAkC,IAAI;QACzC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC/B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAC5B,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAClC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAC5B,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,MAAM,GAAU,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Layout rescaling utilities
3
+ */
4
+ import { PositionMap } from '../types';
5
+ /**
6
+ * Returns scaled position array/dict to (-scale, scale) in all axes.
7
+ *
8
+ * @param pos - Position dictionary or array
9
+ * @param scale - Scale factor for positions
10
+ * @param center - Coordinate pair around which to center the layout
11
+ * @returns Rescaled positions dictionary
12
+ */
13
+ export declare function rescaleLayout(pos: PositionMap | number[][], scale?: number, center?: number[] | null): PositionMap | number[][];
14
+ /**
15
+ * Return a dictionary of scaled positions centered at (0, 0).
16
+ *
17
+ * @param pos - Dictionary of positions
18
+ * @param scale - Scale factor for positions
19
+ * @returns Dictionary of scaled positions
20
+ */
21
+ export declare function rescaleLayoutDict(pos: PositionMap, scale?: number): PositionMap;
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Layout rescaling utilities
3
+ */
4
+ /**
5
+ * Returns scaled position array/dict to (-scale, scale) in all axes.
6
+ *
7
+ * @param pos - Position dictionary or array
8
+ * @param scale - Scale factor for positions
9
+ * @param center - Coordinate pair around which to center the layout
10
+ * @returns Rescaled positions dictionary
11
+ */
12
+ export function rescaleLayout(pos, scale = 1, center = null) {
13
+ // Check if pos is empty
14
+ if (Array.isArray(pos)) {
15
+ if (pos.length === 0)
16
+ return [];
17
+ }
18
+ else {
19
+ if (Object.keys(pos).length === 0)
20
+ return {};
21
+ }
22
+ // Extract position values
23
+ const posValues = Array.isArray(pos) ? pos : Object.values(pos);
24
+ const dim = posValues[0].length;
25
+ // Fix Bug #1: Default center should match dimensionality
26
+ if (!center) {
27
+ center = Array(dim).fill(0);
28
+ }
29
+ // Use the maximum dimension between positions and center
30
+ const targetDim = Math.max(dim, center.length);
31
+ // Calculate center of positions, handling NaN values (Bug #3)
32
+ const posCenter = Array(dim).fill(0);
33
+ const counts = Array(dim).fill(0);
34
+ for (const p of posValues) {
35
+ for (let i = 0; i < dim; i++) {
36
+ if (!isNaN(p[i])) {
37
+ posCenter[i] += p[i];
38
+ counts[i]++;
39
+ }
40
+ }
41
+ }
42
+ // Average by actual count, not total length
43
+ for (let i = 0; i < dim; i++) {
44
+ posCenter[i] = counts[i] > 0 ? posCenter[i] / counts[i] : 0;
45
+ }
46
+ // Center positions
47
+ let centeredPos = {};
48
+ if (Array.isArray(pos)) {
49
+ centeredPos = pos.map(p => {
50
+ const centered = Array(targetDim).fill(0);
51
+ for (let i = 0; i < targetDim; i++) {
52
+ centered[i] = (i < p.length ? p[i] : 0) - (i < posCenter.length ? posCenter[i] : 0);
53
+ }
54
+ return centered;
55
+ });
56
+ }
57
+ else {
58
+ for (const [node, p] of Object.entries(pos)) {
59
+ const centered = Array(targetDim).fill(0);
60
+ for (let i = 0; i < targetDim; i++) {
61
+ centered[i] = (i < p.length ? p[i] : 0) - (i < posCenter.length ? posCenter[i] : 0);
62
+ }
63
+ centeredPos[node] = centered;
64
+ }
65
+ }
66
+ // Find maximum distance from center, handling NaN values (Bug #3)
67
+ let maxDistance = 0;
68
+ const centeredValues = Array.isArray(centeredPos) ? centeredPos : Object.values(centeredPos);
69
+ for (const p of centeredValues) {
70
+ // Calculate distance, treating NaN as 0 for distance calculation
71
+ let sumSquares = 0;
72
+ for (const val of p) {
73
+ if (!isNaN(val)) {
74
+ sumSquares += val * val;
75
+ }
76
+ }
77
+ const distance = Math.sqrt(sumSquares);
78
+ if (!isNaN(distance)) {
79
+ maxDistance = Math.max(maxDistance, distance);
80
+ }
81
+ }
82
+ // Rescale
83
+ let scaledPos = Array.isArray(pos) ? [] : {};
84
+ if (maxDistance > 0) {
85
+ const scaleFactor = scale / maxDistance;
86
+ if (Array.isArray(pos)) {
87
+ scaledPos = centeredPos.map((p, idx) => p.map((val, i) => {
88
+ // Check if this dimension existed in the original position
89
+ const origPos = pos[idx];
90
+ if (i >= origPos.length) {
91
+ // This dimension was added, use center value
92
+ return center[i];
93
+ }
94
+ // Preserve NaN values (Bug #3)
95
+ if (isNaN(val))
96
+ return NaN;
97
+ return val * scaleFactor + center[i];
98
+ }));
99
+ }
100
+ else {
101
+ for (const [node, p] of Object.entries(centeredPos)) {
102
+ const origPos = pos[node];
103
+ scaledPos[node] = p.map((val, i) => {
104
+ // Check if this dimension existed in the original position
105
+ if (i >= origPos.length) {
106
+ // This dimension was added, use center value
107
+ return center[i];
108
+ }
109
+ // Preserve NaN values (Bug #3)
110
+ if (isNaN(val))
111
+ return NaN;
112
+ return val * scaleFactor + center[i];
113
+ });
114
+ }
115
+ }
116
+ }
117
+ else {
118
+ // All nodes at the same position - extend to target dimensionality
119
+ if (Array.isArray(pos)) {
120
+ scaledPos = pos.map(p => {
121
+ const result = Array(targetDim);
122
+ for (let i = 0; i < targetDim; i++) {
123
+ if (i < p.length) {
124
+ result[i] = isNaN(p[i]) ? NaN : center[i];
125
+ }
126
+ else {
127
+ result[i] = center[i];
128
+ }
129
+ }
130
+ return result;
131
+ });
132
+ }
133
+ else {
134
+ for (const [node, p] of Object.entries(pos)) {
135
+ const result = Array(targetDim);
136
+ for (let i = 0; i < targetDim; i++) {
137
+ if (i < p.length) {
138
+ result[i] = isNaN(p[i]) ? NaN : center[i];
139
+ }
140
+ else {
141
+ result[i] = center[i];
142
+ }
143
+ }
144
+ scaledPos[node] = result;
145
+ }
146
+ }
147
+ }
148
+ return scaledPos;
149
+ }
150
+ /**
151
+ * Return a dictionary of scaled positions centered at (0, 0).
152
+ *
153
+ * @param pos - Dictionary of positions
154
+ * @param scale - Scale factor for positions
155
+ * @returns Dictionary of scaled positions
156
+ */
157
+ export function rescaleLayoutDict(pos, scale = 1) {
158
+ if (Object.keys(pos).length === 0) {
159
+ return {};
160
+ }
161
+ // Extract positions as array
162
+ const posArray = Object.values(pos);
163
+ // Find center of positions
164
+ const center = [];
165
+ for (let d = 0; d < posArray[0].length; d++) {
166
+ center[d] = posArray.reduce((sum, p) => sum + p[d], 0) / posArray.length;
167
+ }
168
+ // Center positions
169
+ const centeredPos = {};
170
+ for (const [node, p] of Object.entries(pos)) {
171
+ centeredPos[node] = p.map((val, d) => val - center[d]);
172
+ }
173
+ // Find maximum distance from center
174
+ let maxDist = 0;
175
+ for (const p of Object.values(centeredPos)) {
176
+ const dist = Math.sqrt(p.reduce((sum, val) => sum + val * val, 0));
177
+ maxDist = Math.max(maxDist, dist);
178
+ }
179
+ // Scale positions
180
+ const scaledPos = {};
181
+ if (maxDist > 0) {
182
+ for (const [node, p] of Object.entries(centeredPos)) {
183
+ scaledPos[node] = p.map(val => val * scale / maxDist);
184
+ }
185
+ }
186
+ else {
187
+ // All points at the center
188
+ for (const node of Object.keys(centeredPos)) {
189
+ scaledPos[node] = Array(centeredPos[node].length).fill(0);
190
+ }
191
+ }
192
+ return scaledPos;
193
+ }
194
+ //# sourceMappingURL=rescale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rescale.js","sourceRoot":"","sources":["../../../src/utils/rescale.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@ export default defineConfig({
18
18
  statements: 65
19
19
  }
20
20
  },
21
- include: ['test/**/*.test.ts'],
21
+ include: ['test/**/*.test.ts', 'test/**/*.test.js'],
22
22
  reporters: ['verbose']
23
23
  },
24
24
  resolve: {
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.config.js","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAe,YAAY,CAAC;IAC1B,IAAI,EAAE;QACJ,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,WAAW;QACxB,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,KAAK;QAClB,QAAQ,EAAE;YACR,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC1C,OAAO,EAAE,CAAC,aAAa,CAAC;YACxB,OAAO,EAAE,CAAC,WAAW,EAAE,cAAc,CAAC;YACtC,GAAG,EAAE,IAAI;YACT,UAAU,EAAE;gBACV,KAAK,EAAE,EAAE;gBACT,SAAS,EAAE,EAAE;gBACb,QAAQ,EAAE,EAAE;gBACZ,UAAU,EAAE,EAAE;aACf;SACF;QACD,OAAO,EAAE,CAAC,mBAAmB,CAAC;QAC9B,SAAS,EAAE,CAAC,SAAS,CAAC;KACvB;IACD,OAAO,EAAE;QACP,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;SACZ;KACF;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"vitest.config.js","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAe,YAAY,CAAC;IAC1B,IAAI,EAAE;QACJ,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,WAAW;QACxB,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,KAAK;QAClB,QAAQ,EAAE;YACR,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC1C,OAAO,EAAE,CAAC,aAAa,CAAC;YACxB,OAAO,EAAE,CAAC,WAAW,EAAE,cAAc,CAAC;YACtC,GAAG,EAAE,IAAI;YACT,UAAU,EAAE;gBACV,KAAK,EAAE,EAAE;gBACT,SAAS,EAAE,EAAE;gBACb,QAAQ,EAAE,EAAE;gBACZ,UAAU,EAAE,EAAE;aACf;SACF;QACD,OAAO,EAAE,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;QACnD,SAAS,EAAE,CAAC,SAAS,CAAC;KACvB;IACD,OAAO,EAAE;QACP,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;SACZ;KACF;CACF,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,10 +1,23 @@
1
1
  {
2
2
  "name": "@graphty/layout",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "graph layout algorithms based on networkx",
5
5
  "author": "Adam Powers <apowers@ato.ms>",
6
6
  "main": "dist/src/index.js",
7
7
  "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/src/index.js",
11
+ "types": "./dist/src/index.d.ts"
12
+ }
13
+ },
14
+ "types": "dist/src/index.d.ts",
15
+ "files": [
16
+ "dist/",
17
+ "src/",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
8
21
  "directories": {
9
22
  "example": "examples"
10
23
  },
@@ -15,12 +28,13 @@
15
28
  "test:coverage": "vitest run --coverage",
16
29
  "prepare": "husky",
17
30
  "build": "tsc",
18
- "build:examples": "npm run build && node build-examples-script.js",
31
+ "build:bundle": "node scripts/build-bundle.js",
32
+ "build:all": "npm run build && npm run build:bundle",
19
33
  "watch": "tsc --watch",
20
34
  "dev": "tsc --watch",
21
35
  "commit": "cz",
22
- "serve": "npm run build:examples && vite",
23
- "examples": "npm run build:examples && vite"
36
+ "serve": "npm run build:bundle && vite",
37
+ "examples": "npm run build:bundle && vite"
24
38
  },
25
39
  "repository": {
26
40
  "type": "git",
@@ -54,6 +68,7 @@
54
68
  "husky": "^9.1.7",
55
69
  "semantic-release": "^24.2.7",
56
70
  "typescript": "^5.3.3",
71
+ "vite": "^7.0.5",
57
72
  "vitest": "^3.2.4"
58
73
  }
59
74
  }
@@ -66,7 +66,11 @@ export function forceatlas2Layout(
66
66
  // Use provided positions
67
67
  posArray = new Array(nodes.length);
68
68
  for (let i = 0; i < nodes.length; i++) {
69
- posArray[i] = [...pos[nodes[i]]];
69
+ const nodePos = pos[nodes[i]];
70
+ posArray[i] = new Array(dim);
71
+ for (let d = 0; d < dim; d++) {
72
+ posArray[i][d] = d < nodePos.length ? nodePos[d] : (rng.rand() as number * 2 - 1);
73
+ }
70
74
  }
71
75
  } else {
72
76
  // Some nodes don't have positions, initialize within the range of existing positions
@@ -75,9 +79,20 @@ export function forceatlas2Layout(
75
79
 
76
80
  // Find min and max of existing positions
77
81
  for (const node in pos) {
82
+ const nodePos = pos[node];
78
83
  for (let d = 0; d < dim; d++) {
79
- minPos[d] = Math.min(minPos[d], pos[node][d]);
80
- maxPos[d] = Math.max(maxPos[d], pos[node][d]);
84
+ if (d < nodePos.length) {
85
+ minPos[d] = Math.min(minPos[d], nodePos[d]);
86
+ maxPos[d] = Math.max(maxPos[d], nodePos[d]);
87
+ }
88
+ }
89
+ }
90
+
91
+ // If min/max are still infinity for some dimensions, use default range
92
+ for (let d = 0; d < dim; d++) {
93
+ if (!isFinite(minPos[d]) || !isFinite(maxPos[d])) {
94
+ minPos[d] = -1;
95
+ maxPos[d] = 1;
81
96
  }
82
97
  }
83
98
 
@@ -85,7 +100,11 @@ export function forceatlas2Layout(
85
100
  for (let i = 0; i < nodes.length; i++) {
86
101
  const node = nodes[i];
87
102
  if (pos[node]) {
88
- posArray[i] = [...pos[node]];
103
+ const nodePos = pos[node];
104
+ posArray[i] = new Array(dim);
105
+ for (let d = 0; d < dim; d++) {
106
+ posArray[i][d] = d < nodePos.length ? nodePos[d] : (rng.rand() as number * 2 - 1);
107
+ }
89
108
  } else {
90
109
  posArray[i] = Array(dim).fill(0).map((_, d) =>
91
110
  minPos[d] + (rng.rand() as number) * (maxPos[d] - minPos[d])
@@ -15,7 +15,7 @@ import { PositionMap } from '../types';
15
15
  export function rescaleLayout(
16
16
  pos: PositionMap | number[][],
17
17
  scale: number = 1,
18
- center: number[] = [0, 0]
18
+ center: number[] | null = null
19
19
  ): PositionMap | number[][] {
20
20
  // Check if pos is empty
21
21
  if (Array.isArray(pos)) {
@@ -28,30 +28,67 @@ export function rescaleLayout(
28
28
  const posValues: number[][] = Array.isArray(pos) ? pos : Object.values(pos);
29
29
  const dim = posValues[0].length;
30
30
 
31
- // Calculate center of positions
31
+ // Fix Bug #1: Default center should match dimensionality
32
+ if (!center) {
33
+ center = Array(dim).fill(0);
34
+ }
35
+
36
+ // Use the maximum dimension between positions and center
37
+ const targetDim = Math.max(dim, center.length);
38
+
39
+ // Calculate center of positions, handling NaN values (Bug #3)
32
40
  const posCenter = Array(dim).fill(0);
41
+ const counts = Array(dim).fill(0);
42
+
33
43
  for (const p of posValues) {
34
44
  for (let i = 0; i < dim; i++) {
35
- posCenter[i] += p[i] / posValues.length;
45
+ if (!isNaN(p[i])) {
46
+ posCenter[i] += p[i];
47
+ counts[i]++;
48
+ }
36
49
  }
37
50
  }
51
+
52
+ // Average by actual count, not total length
53
+ for (let i = 0; i < dim; i++) {
54
+ posCenter[i] = counts[i] > 0 ? posCenter[i] / counts[i] : 0;
55
+ }
38
56
 
39
57
  // Center positions
40
58
  let centeredPos: PositionMap | number[][] = {};
41
59
  if (Array.isArray(pos)) {
42
- centeredPos = pos.map(p => p.map((val, i) => val - posCenter[i]));
60
+ centeredPos = pos.map(p => {
61
+ const centered = Array(targetDim).fill(0);
62
+ for (let i = 0; i < targetDim; i++) {
63
+ centered[i] = (i < p.length ? p[i] : 0) - (i < posCenter.length ? posCenter[i] : 0);
64
+ }
65
+ return centered;
66
+ });
43
67
  } else {
44
68
  for (const [node, p] of Object.entries(pos)) {
45
- (centeredPos as PositionMap)[node] = p.map((val, i) => val - posCenter[i]);
69
+ const centered = Array(targetDim).fill(0);
70
+ for (let i = 0; i < targetDim; i++) {
71
+ centered[i] = (i < p.length ? p[i] : 0) - (i < posCenter.length ? posCenter[i] : 0);
72
+ }
73
+ (centeredPos as PositionMap)[node] = centered;
46
74
  }
47
75
  }
48
76
 
49
- // Find maximum distance from center
77
+ // Find maximum distance from center, handling NaN values (Bug #3)
50
78
  let maxDistance = 0;
51
79
  const centeredValues = Array.isArray(centeredPos) ? centeredPos : Object.values(centeredPos);
52
80
  for (const p of centeredValues) {
53
- const distance = Math.sqrt(p.reduce((sum, val) => sum + val * val, 0));
54
- maxDistance = Math.max(maxDistance, distance);
81
+ // Calculate distance, treating NaN as 0 for distance calculation
82
+ let sumSquares = 0;
83
+ for (const val of p) {
84
+ if (!isNaN(val)) {
85
+ sumSquares += val * val;
86
+ }
87
+ }
88
+ const distance = Math.sqrt(sumSquares);
89
+ if (!isNaN(distance)) {
90
+ maxDistance = Math.max(maxDistance, distance);
91
+ }
55
92
  }
56
93
 
57
94
  // Rescale
@@ -61,21 +98,59 @@ export function rescaleLayout(
61
98
  const scaleFactor = scale / maxDistance;
62
99
 
63
100
  if (Array.isArray(pos)) {
64
- (scaledPos as number[][]) = (centeredPos as number[][]).map(p =>
65
- p.map((val, i) => val * scaleFactor + center[i])
101
+ (scaledPos as number[][]) = (centeredPos as number[][]).map((p, idx) =>
102
+ p.map((val, i) => {
103
+ // Check if this dimension existed in the original position
104
+ const origPos = pos[idx];
105
+ if (i >= origPos.length) {
106
+ // This dimension was added, use center value
107
+ return center![i];
108
+ }
109
+ // Preserve NaN values (Bug #3)
110
+ if (isNaN(val)) return NaN;
111
+ return val * scaleFactor + center![i];
112
+ })
66
113
  );
67
114
  } else {
68
115
  for (const [node, p] of Object.entries(centeredPos as PositionMap)) {
69
- (scaledPos as PositionMap)[node] = p.map((val, i) => val * scaleFactor + center[i]);
116
+ const origPos = pos[node];
117
+ (scaledPos as PositionMap)[node] = p.map((val, i) => {
118
+ // Check if this dimension existed in the original position
119
+ if (i >= origPos.length) {
120
+ // This dimension was added, use center value
121
+ return center![i];
122
+ }
123
+ // Preserve NaN values (Bug #3)
124
+ if (isNaN(val)) return NaN;
125
+ return val * scaleFactor + center![i];
126
+ });
70
127
  }
71
128
  }
72
129
  } else {
73
- // All nodes at the same position
130
+ // All nodes at the same position - extend to target dimensionality
74
131
  if (Array.isArray(pos)) {
75
- (scaledPos as number[][]) = Array(pos.length).fill(0).map(() => [...center]);
132
+ (scaledPos as number[][]) = pos.map(p => {
133
+ const result = Array(targetDim);
134
+ for (let i = 0; i < targetDim; i++) {
135
+ if (i < p.length) {
136
+ result[i] = isNaN(p[i]) ? NaN : center![i];
137
+ } else {
138
+ result[i] = center![i];
139
+ }
140
+ }
141
+ return result;
142
+ });
76
143
  } else {
77
- for (const node of Object.keys(pos)) {
78
- (scaledPos as PositionMap)[node] = [...center];
144
+ for (const [node, p] of Object.entries(pos)) {
145
+ const result = Array(targetDim);
146
+ for (let i = 0; i < targetDim; i++) {
147
+ if (i < p.length) {
148
+ result[i] = isNaN(p[i]) ? NaN : center![i];
149
+ } else {
150
+ result[i] = center![i];
151
+ }
152
+ }
153
+ (scaledPos as PositionMap)[node] = result;
79
154
  }
80
155
  }
81
156
  }
package/.env.example DELETED
@@ -1,11 +0,0 @@
1
- # Development Server Configuration
2
- # Copy this file to .env and adjust values as needed
3
-
4
- # Server host (defaults to true for network exposure)
5
- # HOST=localhost
6
- # HOST=0.0.0.0
7
- # HOST=my.server.com
8
-
9
- # Server port (defaults to 3000)
10
- # PORT=3000
11
- # PORT=8080
@@ -1,101 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [ main, develop ]
6
- pull_request:
7
- branches: [ main ]
8
-
9
- jobs:
10
- test:
11
- name: Test
12
- runs-on: ubuntu-latest
13
-
14
- strategy:
15
- matrix:
16
- node-version: [18.x, 20.x, 22.x]
17
-
18
- steps:
19
- - name: Checkout code
20
- uses: actions/checkout@v4
21
-
22
- - name: Setup Node.js ${{ matrix.node-version }}
23
- uses: actions/setup-node@v4
24
- with:
25
- node-version: ${{ matrix.node-version }}
26
- cache: 'npm'
27
-
28
- - name: Install dependencies
29
- run: npm ci
30
-
31
- - name: Run TypeScript compiler
32
- run: npm run build
33
-
34
- - name: Run tests
35
- run: npm run test:run
36
-
37
- - name: Run tests with coverage
38
- run: npm run test:coverage
39
- if: matrix.node-version == '20.x'
40
-
41
- - name: Upload coverage reports to Coveralls
42
- uses: coverallsapp/github-action@v2
43
- if: matrix.node-version == '20.x'
44
- with:
45
- github-token: ${{ secrets.GITHUB_TOKEN }}
46
- file: ./coverage/lcov.info
47
-
48
- lint:
49
- name: Lint
50
- runs-on: ubuntu-latest
51
-
52
- steps:
53
- - name: Checkout code
54
- uses: actions/checkout@v4
55
-
56
- - name: Setup Node.js
57
- uses: actions/setup-node@v4
58
- with:
59
- node-version: '20.x'
60
- cache: 'npm'
61
-
62
- - name: Install dependencies
63
- run: npm ci
64
-
65
- - name: Check TypeScript types
66
- run: npx tsc --noEmit
67
-
68
- - name: Run commitlint on PR title
69
- if: github.event_name == 'pull_request'
70
- run: echo "${{ github.event.pull_request.title }}" | npx commitlint
71
-
72
- release:
73
- name: Release
74
- runs-on: ubuntu-latest
75
- needs: [test, lint]
76
- if: github.ref == 'refs/heads/main' && github.event_name == 'push'
77
-
78
- steps:
79
- - name: Checkout code
80
- uses: actions/checkout@v4
81
- with:
82
- fetch-depth: 0
83
- token: ${{ secrets.GITHUB_TOKEN }}
84
-
85
- - name: Setup Node.js
86
- uses: actions/setup-node@v4
87
- with:
88
- node-version: '20.x'
89
- cache: 'npm'
90
-
91
- - name: Install dependencies
92
- run: npm ci
93
-
94
- - name: Build package
95
- run: npm run build
96
-
97
- - name: Release
98
- env:
99
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
100
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
101
- run: npx semantic-release