@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.
- package/README.md +400 -409
- package/dist/layout.js +884 -817
- package/dist/layout.js.map +1 -1
- package/dist/src/algorithms/index.d.ts +3 -2
- package/dist/src/algorithms/index.d.ts.map +1 -0
- package/dist/src/algorithms/index.js +2 -2
- package/dist/src/algorithms/index.js.map +1 -1
- package/dist/src/algorithms/optimization/index.d.ts +5 -4
- package/dist/src/algorithms/optimization/index.d.ts.map +1 -0
- package/dist/src/algorithms/optimization/index.js +3 -3
- package/dist/src/algorithms/optimization/index.js.map +1 -1
- package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts +3 -5
- package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts.map +1 -0
- package/dist/src/algorithms/optimization/kamada-kawai-solver.js +6 -9
- package/dist/src/algorithms/optimization/kamada-kawai-solver.js.map +1 -1
- package/dist/src/algorithms/optimization/lbfgs.d.ts +3 -3
- package/dist/src/algorithms/optimization/lbfgs.d.ts.map +1 -0
- package/dist/src/algorithms/optimization/lbfgs.js +5 -7
- package/dist/src/algorithms/optimization/lbfgs.js.map +1 -1
- package/dist/src/algorithms/optimization/line-search.d.ts +1 -1
- package/dist/src/algorithms/optimization/line-search.d.ts.map +1 -0
- package/dist/src/algorithms/optimization/line-search.js +0 -1
- package/dist/src/algorithms/optimization/line-search.js.map +1 -1
- package/dist/src/algorithms/optimization/types.d.ts +2 -1
- package/dist/src/algorithms/optimization/types.d.ts.map +1 -0
- package/dist/src/algorithms/planarity/check.d.ts +5 -4
- package/dist/src/algorithms/planarity/check.d.ts.map +1 -0
- package/dist/src/algorithms/planarity/check.js +6 -6
- package/dist/src/algorithms/planarity/check.js.map +1 -1
- package/dist/src/algorithms/planarity/embedding.d.ts +5 -6
- package/dist/src/algorithms/planarity/embedding.d.ts.map +1 -0
- package/dist/src/algorithms/planarity/embedding.js +15 -14
- package/dist/src/algorithms/planarity/embedding.js.map +1 -1
- package/dist/src/algorithms/planarity/index.d.ts +5 -4
- package/dist/src/algorithms/planarity/index.d.ts.map +1 -0
- package/dist/src/algorithms/planarity/index.js +4 -4
- package/dist/src/algorithms/planarity/index.js.map +1 -1
- package/dist/src/algorithms/planarity/lr-test.d.ts +3 -2
- package/dist/src/algorithms/planarity/lr-test.d.ts.map +1 -0
- package/dist/src/algorithms/planarity/lr-test.js +2 -2
- package/dist/src/algorithms/planarity/lr-test.js.map +1 -1
- package/dist/src/algorithms/planarity/special-graphs.d.ts +2 -4
- package/dist/src/algorithms/planarity/special-graphs.d.ts.map +1 -0
- package/dist/src/algorithms/planarity/special-graphs.js +18 -15
- package/dist/src/algorithms/planarity/special-graphs.js.map +1 -1
- package/dist/src/generators/basic.d.ts +2 -1
- package/dist/src/generators/basic.d.ts.map +1 -0
- package/dist/src/generators/basic.js +4 -4
- package/dist/src/generators/basic.js.map +1 -1
- package/dist/src/generators/bipartite.d.ts +2 -1
- package/dist/src/generators/bipartite.d.ts.map +1 -0
- package/dist/src/generators/bipartite.js +2 -2
- package/dist/src/generators/bipartite.js.map +1 -1
- package/dist/src/generators/grid.d.ts +2 -1
- package/dist/src/generators/grid.d.ts.map +1 -0
- package/dist/src/generators/grid.js +1 -1
- package/dist/src/generators/grid.js.map +1 -1
- package/dist/src/generators/index.d.ts +6 -5
- package/dist/src/generators/index.d.ts.map +1 -0
- package/dist/src/generators/index.js +5 -5
- package/dist/src/generators/index.js.map +1 -1
- package/dist/src/generators/random.d.ts +2 -1
- package/dist/src/generators/random.d.ts.map +1 -0
- package/dist/src/generators/random.js +2 -2
- package/dist/src/generators/random.js.map +1 -1
- package/dist/src/generators/scale-free.d.ts +2 -1
- package/dist/src/generators/scale-free.d.ts.map +1 -0
- package/dist/src/generators/scale-free.js +4 -4
- package/dist/src/generators/scale-free.js.map +1 -1
- package/dist/src/index.d.ts +5 -4
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +4 -4
- package/dist/src/layouts/basic/index.d.ts +2 -1
- package/dist/src/layouts/basic/index.d.ts.map +1 -0
- package/dist/src/layouts/basic/index.js +1 -1
- package/dist/src/layouts/basic/random.d.ts +2 -2
- package/dist/src/layouts/basic/random.d.ts.map +1 -0
- package/dist/src/layouts/basic/random.js +4 -5
- package/dist/src/layouts/basic/random.js.map +1 -1
- package/dist/src/layouts/force-directed/arf.d.ts +2 -2
- package/dist/src/layouts/force-directed/arf.d.ts.map +1 -0
- package/dist/src/layouts/force-directed/arf.js +13 -8
- package/dist/src/layouts/force-directed/arf.js.map +1 -1
- package/dist/src/layouts/force-directed/forceatlas2.d.ts +4 -4
- package/dist/src/layouts/force-directed/forceatlas2.d.ts.map +1 -0
- package/dist/src/layouts/force-directed/forceatlas2.js +83 -51
- package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -1
- package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +12 -12
- package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts.map +1 -0
- package/dist/src/layouts/force-directed/fruchterman-reingold.js +18 -18
- package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -1
- package/dist/src/layouts/force-directed/index.d.ts +6 -5
- package/dist/src/layouts/force-directed/index.d.ts.map +1 -0
- package/dist/src/layouts/force-directed/index.js +5 -5
- package/dist/src/layouts/force-directed/index.js.map +1 -1
- package/dist/src/layouts/force-directed/kamada-kawai.d.ts +3 -3
- package/dist/src/layouts/force-directed/kamada-kawai.d.ts.map +1 -0
- package/dist/src/layouts/force-directed/kamada-kawai.js +13 -11
- package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -1
- package/dist/src/layouts/force-directed/spring.d.ts +12 -12
- package/dist/src/layouts/force-directed/spring.d.ts.map +1 -0
- package/dist/src/layouts/force-directed/spring.js +11 -12
- package/dist/src/layouts/force-directed/spring.js.map +1 -1
- package/dist/src/layouts/geometric/circular.d.ts +2 -2
- package/dist/src/layouts/geometric/circular.d.ts.map +1 -0
- package/dist/src/layouts/geometric/circular.js +11 -10
- package/dist/src/layouts/geometric/circular.js.map +1 -1
- package/dist/src/layouts/geometric/index.d.ts +4 -3
- package/dist/src/layouts/geometric/index.d.ts.map +1 -0
- package/dist/src/layouts/geometric/index.js +3 -3
- package/dist/src/layouts/geometric/shell.d.ts +2 -2
- package/dist/src/layouts/geometric/shell.d.ts.map +1 -0
- package/dist/src/layouts/geometric/shell.js +6 -6
- package/dist/src/layouts/geometric/shell.js.map +1 -1
- package/dist/src/layouts/geometric/spiral.d.ts +2 -2
- package/dist/src/layouts/geometric/spiral.d.ts.map +1 -0
- package/dist/src/layouts/geometric/spiral.js +6 -10
- package/dist/src/layouts/geometric/spiral.js.map +1 -1
- package/dist/src/layouts/hierarchical/bfs.d.ts +3 -3
- package/dist/src/layouts/hierarchical/bfs.d.ts.map +1 -0
- package/dist/src/layouts/hierarchical/bfs.js +6 -7
- package/dist/src/layouts/hierarchical/bfs.js.map +1 -1
- package/dist/src/layouts/hierarchical/bipartite.d.ts +3 -3
- package/dist/src/layouts/hierarchical/bipartite.d.ts.map +1 -0
- package/dist/src/layouts/hierarchical/bipartite.js +9 -10
- package/dist/src/layouts/hierarchical/bipartite.js.map +1 -1
- package/dist/src/layouts/hierarchical/index.d.ts +4 -3
- package/dist/src/layouts/hierarchical/index.d.ts.map +1 -0
- package/dist/src/layouts/hierarchical/index.js +3 -3
- package/dist/src/layouts/hierarchical/index.js.map +1 -1
- package/dist/src/layouts/hierarchical/multipartite.d.ts +3 -3
- package/dist/src/layouts/hierarchical/multipartite.d.ts.map +1 -0
- package/dist/src/layouts/hierarchical/multipartite.js +9 -10
- package/dist/src/layouts/hierarchical/multipartite.js.map +1 -1
- package/dist/src/layouts/index.d.ts +6 -5
- package/dist/src/layouts/index.d.ts.map +1 -0
- package/dist/src/layouts/index.js +5 -5
- package/dist/src/layouts/index.js.map +1 -1
- package/dist/src/layouts/specialized/index.d.ts +3 -2
- package/dist/src/layouts/specialized/index.d.ts.map +1 -0
- package/dist/src/layouts/specialized/index.js +2 -2
- package/dist/src/layouts/specialized/index.js.map +1 -1
- package/dist/src/layouts/specialized/planar.d.ts +2 -2
- package/dist/src/layouts/specialized/planar.d.ts.map +1 -0
- package/dist/src/layouts/specialized/planar.js +6 -7
- package/dist/src/layouts/specialized/planar.js.map +1 -1
- package/dist/src/layouts/specialized/spectral.d.ts +2 -2
- package/dist/src/layouts/specialized/spectral.d.ts.map +1 -0
- package/dist/src/layouts/specialized/spectral.js +31 -21
- package/dist/src/layouts/specialized/spectral.js.map +1 -1
- package/dist/src/types/embedding.d.ts +2 -1
- package/dist/src/types/embedding.d.ts.map +1 -0
- package/dist/src/types/graph.d.ts +2 -1
- package/dist/src/types/graph.d.ts.map +1 -0
- package/dist/src/types/index.d.ts +4 -3
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +3 -3
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/layout.d.ts +2 -1
- package/dist/src/types/layout.d.ts.map +1 -0
- package/dist/src/utils/graph.d.ts +2 -5
- package/dist/src/utils/graph.d.ts.map +1 -0
- package/dist/src/utils/graph.js +4 -6
- package/dist/src/utils/graph.js.map +1 -1
- package/dist/src/utils/index.d.ts +6 -5
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +5 -5
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/numpy.d.ts +5 -4
- package/dist/src/utils/numpy.d.ts.map +1 -0
- package/dist/src/utils/numpy.js +22 -16
- package/dist/src/utils/numpy.js.map +1 -1
- package/dist/src/utils/params.d.ts +2 -2
- package/dist/src/utils/params.d.ts.map +1 -0
- package/dist/src/utils/params.js +0 -1
- package/dist/src/utils/params.js.map +1 -1
- package/dist/src/utils/random.d.ts +18 -0
- package/dist/src/utils/random.d.ts.map +1 -0
- package/dist/src/utils/random.js +18 -1
- package/dist/src/utils/random.js.map +1 -1
- package/dist/src/utils/rescale.d.ts +2 -3
- package/dist/src/utils/rescale.d.ts.map +1 -0
- package/dist/src/utils/rescale.js +12 -10
- package/dist/src/utils/rescale.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +35 -34
- package/src/algorithms/index.ts +2 -2
- package/src/algorithms/optimization/index.ts +4 -8
- package/src/algorithms/optimization/kamada-kawai-solver.ts +170 -179
- package/src/algorithms/optimization/lbfgs.ts +42 -49
- package/src/algorithms/optimization/line-search.ts +27 -28
- package/src/algorithms/optimization/types.ts +2 -2
- package/src/algorithms/planarity/check.ts +23 -23
- package/src/algorithms/planarity/embedding.ts +160 -171
- package/src/algorithms/planarity/index.ts +4 -8
- package/src/algorithms/planarity/lr-test.ts +46 -46
- package/src/algorithms/planarity/special-graphs.ts +73 -83
- package/src/generators/basic.ts +57 -57
- package/src/generators/bipartite.ts +37 -31
- package/src/generators/grid.ts +28 -28
- package/src/generators/index.ts +5 -11
- package/src/generators/random.ts +26 -25
- package/src/generators/scale-free.ts +56 -55
- package/src/index.ts +5 -5
- package/src/layouts/basic/index.ts +1 -1
- package/src/layouts/basic/random.ts +20 -16
- package/src/layouts/force-directed/arf.ts +107 -105
- package/src/layouts/force-directed/forceatlas2.ts +374 -352
- package/src/layouts/force-directed/fruchterman-reingold.ts +133 -134
- package/src/layouts/force-directed/index.ts +5 -5
- package/src/layouts/force-directed/kamada-kawai.ts +77 -80
- package/src/layouts/force-directed/spring.ts +24 -25
- package/src/layouts/geometric/circular.ts +63 -57
- package/src/layouts/geometric/index.ts +3 -3
- package/src/layouts/geometric/shell.ts +58 -53
- package/src/layouts/geometric/spiral.ts +60 -64
- package/src/layouts/hierarchical/bfs.ts +58 -59
- package/src/layouts/hierarchical/bipartite.ts +62 -63
- package/src/layouts/hierarchical/index.ts +3 -3
- package/src/layouts/hierarchical/multipartite.ts +62 -63
- package/src/layouts/index.ts +5 -5
- package/src/layouts/specialized/index.ts +2 -2
- package/src/layouts/specialized/planar.ts +41 -42
- package/src/layouts/specialized/spectral.ts +110 -99
- package/src/types/embedding.ts +5 -5
- package/src/types/graph.ts +4 -4
- package/src/types/index.ts +3 -3
- package/src/types/layout.ts +2 -2
- package/src/utils/graph.ts +31 -35
- package/src/utils/index.ts +5 -5
- package/src/utils/numpy.ts +96 -92
- package/src/utils/params.ts +14 -11
- package/src/utils/random.ts +57 -40
- package/src/utils/rescale.ts +169 -174
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -30
- package/dist/vitest.config.js.map +0 -1
package/src/utils/random.ts
CHANGED
|
@@ -3,51 +3,68 @@
|
|
|
3
3
|
* Maintains exact same functionality as original implementation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* A deterministic random number generator based on a linear congruential generator (LCG) algorithm.
|
|
8
|
+
* Provides consistent random sequences when initialized with the same seed.
|
|
9
|
+
*/
|
|
6
10
|
export class RandomNumberGenerator {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
constructor(seed?: number) {
|
|
14
|
-
this.seed = seed || Math.floor(Math.random() * 1000000);
|
|
15
|
-
this.m = 2 ** 35 - 31;
|
|
16
|
-
this.a = 185852;
|
|
17
|
-
this.c = 1;
|
|
18
|
-
this._state = this.seed % this.m;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
_next(): number {
|
|
22
|
-
this._state = (this.a * this._state + this.c) % this.m;
|
|
23
|
-
return this._state / this.m;
|
|
24
|
-
}
|
|
11
|
+
private seed: number;
|
|
12
|
+
private m: number;
|
|
13
|
+
private a: number;
|
|
14
|
+
private c: number;
|
|
15
|
+
private _state: number;
|
|
25
16
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new random number generator instance.
|
|
19
|
+
* @param seed - Initial seed value for the generator. If not provided, uses a random seed.
|
|
20
|
+
*/
|
|
21
|
+
constructor(seed?: number) {
|
|
22
|
+
this.seed = seed || Math.floor(Math.random() * 1000000);
|
|
23
|
+
this.m = 2 ** 35 - 31;
|
|
24
|
+
this.a = 185852;
|
|
25
|
+
this.c = 1;
|
|
26
|
+
this._state = this.seed % this.m;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Generates the next random number in the sequence.
|
|
31
|
+
* @returns A random number between 0 and 1.
|
|
32
|
+
*/
|
|
33
|
+
_next(): number {
|
|
34
|
+
this._state = (this.a * this._state + this.c) % this.m;
|
|
35
|
+
return this._state / this.m;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Generates random numbers according to the specified shape.
|
|
40
|
+
* @param shape - Determines the output format: null for single number, number for 1D array, array for multi-dimensional array.
|
|
41
|
+
* @returns A random number, 1D array, or multi-dimensional array depending on the shape parameter.
|
|
42
|
+
*/
|
|
43
|
+
rand(shape: number | number[] | null = null): number | number[] | number[][] {
|
|
44
|
+
if (shape === null) {
|
|
45
|
+
return this._next();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof shape === "number") {
|
|
49
|
+
const result: number[] = [];
|
|
50
|
+
for (let i = 0; i < shape; i++) {
|
|
51
|
+
result.push(this._next());
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (shape.length === 1) {
|
|
57
|
+
const result: number[] = [];
|
|
58
|
+
for (let i = 0; i < shape[0]; i++) {
|
|
59
|
+
result.push(this._next());
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
46
63
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
64
|
+
const result: (number | number[] | number[][])[] = [];
|
|
65
|
+
for (let i = 0; i < shape[0]; i++) {
|
|
66
|
+
result.push(this.rand(shape.slice(1)));
|
|
67
|
+
}
|
|
68
|
+
return result as number[][];
|
|
50
69
|
}
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
}
|
|
70
|
+
}
|
package/src/utils/rescale.ts
CHANGED
|
@@ -2,211 +2,206 @@
|
|
|
2
2
|
* Layout rescaling utilities
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { PositionMap } from
|
|
5
|
+
import { PositionMap } from "../types";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Returns scaled position array/dict to (-scale, scale) in all axes.
|
|
9
|
-
*
|
|
10
9
|
* @param pos - Position dictionary or array
|
|
11
10
|
* @param scale - Scale factor for positions
|
|
12
11
|
* @param center - Coordinate pair around which to center the layout
|
|
13
12
|
* @returns Rescaled positions dictionary
|
|
14
13
|
*/
|
|
15
14
|
export function rescaleLayout(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
pos: PositionMap | number[][],
|
|
16
|
+
scale: number = 1,
|
|
17
|
+
center: number[] | null = null,
|
|
19
18
|
): PositionMap | number[][] {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Extract position values
|
|
28
|
-
const posValues: number[][] = Array.isArray(pos) ? pos : Object.values(pos);
|
|
29
|
-
const dim = posValues[0].length;
|
|
30
|
-
|
|
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)
|
|
40
|
-
const posCenter = Array(dim).fill(0);
|
|
41
|
-
const counts = Array(dim).fill(0);
|
|
42
|
-
|
|
43
|
-
for (const p of posValues) {
|
|
44
|
-
for (let i = 0; i < dim; i++) {
|
|
45
|
-
if (!isNaN(p[i])) {
|
|
46
|
-
posCenter[i] += p[i];
|
|
47
|
-
counts[i]++;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
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
|
-
}
|
|
56
|
-
|
|
57
|
-
// Center positions
|
|
58
|
-
let centeredPos: PositionMap | number[][] = {};
|
|
59
|
-
if (Array.isArray(pos)) {
|
|
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
|
-
});
|
|
67
|
-
} else {
|
|
68
|
-
for (const [node, p] of Object.entries(pos)) {
|
|
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;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Find maximum distance from center, handling NaN values (Bug #3)
|
|
78
|
-
let maxDistance = 0;
|
|
79
|
-
const centeredValues = Array.isArray(centeredPos) ? centeredPos : Object.values(centeredPos);
|
|
80
|
-
for (const p of centeredValues) {
|
|
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
|
-
}
|
|
19
|
+
// Check if pos is empty
|
|
20
|
+
if (Array.isArray(pos)) {
|
|
21
|
+
if (pos.length === 0) {return [];}
|
|
22
|
+
} else {
|
|
23
|
+
if (Object.keys(pos).length === 0) {return {};}
|
|
87
24
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
25
|
+
|
|
26
|
+
// Extract position values
|
|
27
|
+
const posValues: number[][] = Array.isArray(pos) ? pos : Object.values(pos);
|
|
28
|
+
const dim = posValues[0].length;
|
|
29
|
+
|
|
30
|
+
// Fix Bug #1: Default center should match dimensionality
|
|
31
|
+
if (!center) {
|
|
32
|
+
center = Array(dim).fill(0);
|
|
91
33
|
}
|
|
92
|
-
}
|
|
93
34
|
|
|
94
|
-
|
|
95
|
-
|
|
35
|
+
// Use the maximum dimension between positions and center
|
|
36
|
+
const targetDim = Math.max(dim, center.length);
|
|
37
|
+
|
|
38
|
+
// Calculate center of positions, handling NaN values (Bug #3)
|
|
39
|
+
const posCenter: number[] = Array(dim).fill(0);
|
|
40
|
+
const counts: number[] = Array(dim).fill(0);
|
|
96
41
|
|
|
97
|
-
|
|
98
|
-
|
|
42
|
+
for (const p of posValues) {
|
|
43
|
+
for (let i = 0; i < dim; i++) {
|
|
44
|
+
if (!isNaN(p[i])) {
|
|
45
|
+
posCenter[i] += p[i];
|
|
46
|
+
counts[i]++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Average by actual count, not total length
|
|
52
|
+
for (let i = 0; i < dim; i++) {
|
|
53
|
+
posCenter[i] = counts[i] > 0 ? posCenter[i] / counts[i] : 0;
|
|
54
|
+
}
|
|
99
55
|
|
|
56
|
+
// Center positions
|
|
57
|
+
let centeredPos: PositionMap | number[][] = {};
|
|
100
58
|
if (Array.isArray(pos)) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return center![i];
|
|
108
|
-
}
|
|
109
|
-
// Preserve NaN values (Bug #3)
|
|
110
|
-
if (isNaN(val)) return NaN;
|
|
111
|
-
return val * scaleFactor + center![i];
|
|
112
|
-
})
|
|
113
|
-
);
|
|
114
|
-
} else {
|
|
115
|
-
for (const [node, p] of Object.entries(centeredPos as PositionMap)) {
|
|
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];
|
|
59
|
+
centeredPos = pos.map((p) => {
|
|
60
|
+
const centered = Array(targetDim).fill(0);
|
|
61
|
+
for (let i = 0; i < targetDim; i++) {
|
|
62
|
+
centered[i] = (i < p.length ? p[i] : 0) - (i < posCenter.length ? posCenter[i] : 0);
|
|
63
|
+
}
|
|
64
|
+
return centered;
|
|
126
65
|
});
|
|
127
|
-
|
|
66
|
+
} else {
|
|
67
|
+
for (const [node, p] of Object.entries(pos)) {
|
|
68
|
+
const centered = Array(targetDim).fill(0);
|
|
69
|
+
for (let i = 0; i < targetDim; i++) {
|
|
70
|
+
centered[i] = (i < p.length ? p[i] : 0) - (i < posCenter.length ? posCenter[i] : 0);
|
|
71
|
+
}
|
|
72
|
+
(centeredPos)[node] = centered;
|
|
73
|
+
}
|
|
128
74
|
}
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
75
|
+
|
|
76
|
+
// Find maximum distance from center, handling NaN values (Bug #3)
|
|
77
|
+
let maxDistance = 0;
|
|
78
|
+
const centeredValues = Array.isArray(centeredPos) ? centeredPos : Object.values(centeredPos);
|
|
79
|
+
for (const p of centeredValues) {
|
|
80
|
+
// Calculate distance, treating NaN as 0 for distance calculation
|
|
81
|
+
let sumSquares = 0;
|
|
82
|
+
for (const val of p) {
|
|
83
|
+
if (!isNaN(val)) {
|
|
84
|
+
sumSquares += val * val;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const distance = Math.sqrt(sumSquares);
|
|
88
|
+
if (!isNaN(distance)) {
|
|
89
|
+
maxDistance = Math.max(maxDistance, distance);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Rescale
|
|
94
|
+
let scaledPos: PositionMap | number[][] = Array.isArray(pos) ? [] : {};
|
|
95
|
+
|
|
96
|
+
if (maxDistance > 0) {
|
|
97
|
+
const scaleFactor = scale / maxDistance;
|
|
98
|
+
|
|
99
|
+
if (Array.isArray(pos)) {
|
|
100
|
+
(scaledPos as number[][]) = (centeredPos as number[][]).map((p, idx) =>
|
|
101
|
+
p.map((val, i) => {
|
|
102
|
+
// Check if this dimension existed in the original position
|
|
103
|
+
const origPos = pos[idx];
|
|
104
|
+
if (i >= origPos.length) {
|
|
105
|
+
// This dimension was added, use center value
|
|
106
|
+
return center[i];
|
|
107
|
+
}
|
|
108
|
+
// Preserve NaN values (Bug #3)
|
|
109
|
+
if (isNaN(val)) {return NaN;}
|
|
110
|
+
return val * scaleFactor + center[i];
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
for (const [node, p] of Object.entries(centeredPos as PositionMap)) {
|
|
115
|
+
const origPos = pos[node];
|
|
116
|
+
(scaledPos as PositionMap)[node] = p.map((val, i) => {
|
|
117
|
+
// Check if this dimension existed in the original position
|
|
118
|
+
if (i >= origPos.length) {
|
|
119
|
+
// This dimension was added, use center value
|
|
120
|
+
return center[i];
|
|
121
|
+
}
|
|
122
|
+
// Preserve NaN values (Bug #3)
|
|
123
|
+
if (isNaN(val)) {return NaN;}
|
|
124
|
+
return val * scaleFactor + center[i];
|
|
125
|
+
});
|
|
126
|
+
}
|
|
140
127
|
}
|
|
141
|
-
return result;
|
|
142
|
-
});
|
|
143
128
|
} else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
129
|
+
// All nodes at the same position - extend to target dimensionality
|
|
130
|
+
if (Array.isArray(pos)) {
|
|
131
|
+
(scaledPos as number[][]) = pos.map((p) => {
|
|
132
|
+
const result = Array(targetDim);
|
|
133
|
+
for (let i = 0; i < targetDim; i++) {
|
|
134
|
+
if (i < p.length) {
|
|
135
|
+
result[i] = isNaN(p[i]) ? NaN : center[i];
|
|
136
|
+
} else {
|
|
137
|
+
result[i] = center[i];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
for (const [node, p] of Object.entries(pos)) {
|
|
144
|
+
const result = Array(targetDim);
|
|
145
|
+
for (let i = 0; i < targetDim; i++) {
|
|
146
|
+
if (i < p.length) {
|
|
147
|
+
result[i] = isNaN(p[i]) ? NaN : center[i];
|
|
148
|
+
} else {
|
|
149
|
+
result[i] = center[i];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
(scaledPos as PositionMap)[node] = result;
|
|
153
|
+
}
|
|
152
154
|
}
|
|
153
|
-
(scaledPos as PositionMap)[node] = result;
|
|
154
|
-
}
|
|
155
155
|
}
|
|
156
|
-
}
|
|
157
156
|
|
|
158
|
-
|
|
157
|
+
return scaledPos;
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
/**
|
|
162
161
|
* Return a dictionary of scaled positions centered at (0, 0).
|
|
163
|
-
*
|
|
164
162
|
* @param pos - Dictionary of positions
|
|
165
163
|
* @param scale - Scale factor for positions
|
|
166
164
|
* @returns Dictionary of scaled positions
|
|
167
165
|
*/
|
|
168
|
-
export function rescaleLayoutDict(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const centeredPos: PositionMap = {};
|
|
187
|
-
for (const [node, p] of Object.entries(pos)) {
|
|
188
|
-
centeredPos[node] = p.map((val, d) => val - center[d]);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Find maximum distance from center
|
|
192
|
-
let maxDist = 0;
|
|
193
|
-
for (const p of Object.values(centeredPos)) {
|
|
194
|
-
const dist = Math.sqrt(p.reduce((sum, val) => sum + val * val, 0));
|
|
195
|
-
maxDist = Math.max(maxDist, dist);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Scale positions
|
|
199
|
-
const scaledPos: PositionMap = {};
|
|
200
|
-
if (maxDist > 0) {
|
|
201
|
-
for (const [node, p] of Object.entries(centeredPos)) {
|
|
202
|
-
scaledPos[node] = p.map(val => val * scale / maxDist);
|
|
166
|
+
export function rescaleLayoutDict(pos: PositionMap, scale: number = 1): PositionMap {
|
|
167
|
+
if (Object.keys(pos).length === 0) {
|
|
168
|
+
return {};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Extract positions as array
|
|
172
|
+
const posArray = Object.values(pos);
|
|
173
|
+
|
|
174
|
+
// Find center of positions
|
|
175
|
+
const center: number[] = [];
|
|
176
|
+
for (let d = 0; d < posArray[0].length; d++) {
|
|
177
|
+
center[d] = posArray.reduce((sum, p) => sum + p[d], 0) / posArray.length;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Center positions
|
|
181
|
+
const centeredPos: PositionMap = {};
|
|
182
|
+
for (const [node, p] of Object.entries(pos)) {
|
|
183
|
+
centeredPos[node] = p.map((val, d) => val - center[d]);
|
|
203
184
|
}
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
185
|
+
|
|
186
|
+
// Find maximum distance from center
|
|
187
|
+
let maxDist = 0;
|
|
188
|
+
for (const p of Object.values(centeredPos)) {
|
|
189
|
+
const dist = Math.sqrt(p.reduce((sum, val) => sum + val * val, 0));
|
|
190
|
+
maxDist = Math.max(maxDist, dist);
|
|
208
191
|
}
|
|
209
|
-
}
|
|
210
192
|
|
|
211
|
-
|
|
212
|
-
}
|
|
193
|
+
// Scale positions
|
|
194
|
+
const scaledPos: PositionMap = {};
|
|
195
|
+
if (maxDist > 0) {
|
|
196
|
+
for (const [node, p] of Object.entries(centeredPos)) {
|
|
197
|
+
scaledPos[node] = p.map((val) => (val * scale) / maxDist);
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
// All points at the center
|
|
201
|
+
for (const node of Object.keys(centeredPos)) {
|
|
202
|
+
scaledPos[node] = Array(centeredPos[node].length).fill(0);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return scaledPos;
|
|
207
|
+
}
|
package/dist/vitest.config.d.ts
DELETED
package/dist/vitest.config.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
-
export default defineConfig({
|
|
3
|
-
test: {
|
|
4
|
-
globals: true,
|
|
5
|
-
environment: 'happy-dom',
|
|
6
|
-
pool: 'forks',
|
|
7
|
-
testTimeout: 30000,
|
|
8
|
-
coverage: {
|
|
9
|
-
provider: 'v8',
|
|
10
|
-
reporter: ['text', 'json', 'html', 'lcov'],
|
|
11
|
-
include: ['src/**/*.ts'],
|
|
12
|
-
exclude: ['**/*.d.ts', '**/*.test.ts'],
|
|
13
|
-
all: true,
|
|
14
|
-
thresholds: {
|
|
15
|
-
lines: 65,
|
|
16
|
-
functions: 60,
|
|
17
|
-
branches: 85,
|
|
18
|
-
statements: 65
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
include: ['test/**/*.test.ts', 'test/**/*.test.js'],
|
|
22
|
-
reporters: ['verbose']
|
|
23
|
-
},
|
|
24
|
-
resolve: {
|
|
25
|
-
alias: {
|
|
26
|
-
'@': '/src'
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
//# sourceMappingURL=vitest.config.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|