@graphty/layout 1.2.9 → 1.3.2
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 +393 -403
- package/dist/layout.js +867 -808
- package/dist/layout.js.map +1 -1
- package/dist/src/algorithms/index.d.ts +2 -2
- 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 +4 -4
- 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 +2 -5
- 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 +0 -1
- package/dist/src/algorithms/optimization/lbfgs.js +3 -5
- package/dist/src/algorithms/optimization/lbfgs.js.map +1 -1
- package/dist/src/algorithms/optimization/line-search.d.ts +0 -1
- 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 +1 -1
- package/dist/src/algorithms/planarity/check.d.ts +2 -2
- package/dist/src/algorithms/planarity/check.js +4 -4
- package/dist/src/algorithms/planarity/check.js.map +1 -1
- package/dist/src/algorithms/planarity/embedding.d.ts +2 -4
- package/dist/src/algorithms/planarity/embedding.js +13 -12
- package/dist/src/algorithms/planarity/embedding.js.map +1 -1
- package/dist/src/algorithms/planarity/index.d.ts +4 -4
- 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 +2 -2
- 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 +1 -4
- 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 +1 -1
- package/dist/src/generators/basic.js +4 -4
- package/dist/src/generators/basic.js.map +1 -1
- package/dist/src/generators/bipartite.d.ts +1 -1
- package/dist/src/generators/bipartite.js +2 -2
- package/dist/src/generators/bipartite.js.map +1 -1
- package/dist/src/generators/grid.d.ts +1 -1
- package/dist/src/generators/grid.js +1 -1
- package/dist/src/generators/grid.js.map +1 -1
- package/dist/src/generators/index.d.ts +5 -5
- package/dist/src/generators/index.js +5 -5
- package/dist/src/generators/index.js.map +1 -1
- package/dist/src/generators/random.d.ts +1 -1
- 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 +1 -1
- 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 +4 -4
- package/dist/src/index.js +4 -4
- package/dist/src/layouts/basic/index.d.ts +1 -1
- package/dist/src/layouts/basic/index.js +1 -1
- package/dist/src/layouts/basic/random.d.ts +1 -2
- package/dist/src/layouts/basic/random.js +3 -4
- package/dist/src/layouts/basic/random.js.map +1 -1
- package/dist/src/layouts/force-directed/arf.d.ts +1 -2
- 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 +1 -2
- package/dist/src/layouts/force-directed/forceatlas2.js +58 -35
- package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -1
- package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +11 -12
- package/dist/src/layouts/force-directed/fruchterman-reingold.js +17 -17
- package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -1
- package/dist/src/layouts/force-directed/index.d.ts +5 -5
- 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 +2 -3
- package/dist/src/layouts/force-directed/kamada-kawai.js +12 -10
- package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -1
- package/dist/src/layouts/force-directed/spring.d.ts +11 -12
- 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 +1 -2
- package/dist/src/layouts/geometric/circular.js +10 -9
- package/dist/src/layouts/geometric/circular.js.map +1 -1
- package/dist/src/layouts/geometric/index.d.ts +3 -3
- package/dist/src/layouts/geometric/index.js +3 -3
- package/dist/src/layouts/geometric/shell.d.ts +1 -2
- package/dist/src/layouts/geometric/shell.js +5 -5
- package/dist/src/layouts/geometric/shell.js.map +1 -1
- package/dist/src/layouts/geometric/spiral.d.ts +1 -2
- package/dist/src/layouts/geometric/spiral.js +5 -9
- package/dist/src/layouts/geometric/spiral.js.map +1 -1
- package/dist/src/layouts/hierarchical/bfs.d.ts +2 -3
- package/dist/src/layouts/hierarchical/bfs.js +5 -6
- package/dist/src/layouts/hierarchical/bfs.js.map +1 -1
- package/dist/src/layouts/hierarchical/bipartite.d.ts +2 -3
- package/dist/src/layouts/hierarchical/bipartite.js +8 -9
- package/dist/src/layouts/hierarchical/bipartite.js.map +1 -1
- package/dist/src/layouts/hierarchical/index.d.ts +3 -3
- 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 +2 -3
- package/dist/src/layouts/hierarchical/multipartite.js +7 -8
- package/dist/src/layouts/hierarchical/multipartite.js.map +1 -1
- package/dist/src/layouts/index.d.ts +5 -5
- 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 +2 -2
- 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 +1 -2
- package/dist/src/layouts/specialized/planar.js +5 -6
- package/dist/src/layouts/specialized/planar.js.map +1 -1
- package/dist/src/layouts/specialized/spectral.d.ts +1 -2
- package/dist/src/layouts/specialized/spectral.js +30 -20
- package/dist/src/layouts/specialized/spectral.js.map +1 -1
- package/dist/src/types/embedding.d.ts +1 -1
- package/dist/src/types/index.d.ts +3 -3
- package/dist/src/types/index.js +3 -3
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/layout.d.ts +1 -1
- package/dist/src/utils/graph.d.ts +1 -5
- package/dist/src/utils/graph.js +4 -6
- package/dist/src/utils/graph.js.map +1 -1
- package/dist/src/utils/index.d.ts +5 -5
- package/dist/src/utils/index.js +5 -5
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/numpy.js +22 -16
- package/dist/src/utils/numpy.js.map +1 -1
- package/dist/src/utils/params.d.ts +1 -2
- package/dist/src/utils/params.js +0 -1
- package/dist/src/utils/params.js.map +1 -1
- package/dist/src/utils/random.d.ts +17 -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 +1 -3
- package/dist/src/utils/rescale.js +12 -10
- package/dist/src/utils/rescale.js.map +1 -1
- package/dist/vitest.config.js +14 -14
- package/dist/vitest.config.js.map +1 -1
- package/package.json +29 -24
- 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 +41 -48
- package/src/algorithms/optimization/line-search.ts +27 -28
- package/src/algorithms/optimization/types.ts +2 -2
- package/src/algorithms/planarity/check.ts +22 -22
- package/src/algorithms/planarity/embedding.ts +159 -170
- 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 +365 -351
- 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 +78 -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
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
* Bipartite layout algorithm
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { Graph, Node, PositionMap } from
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { rescaleLayout } from
|
|
5
|
+
import { Graph, Node, PositionMap } from "../../types";
|
|
6
|
+
import { getNodesFromGraph } from "../../utils/graph";
|
|
7
|
+
import { _processParams } from "../../utils/params";
|
|
8
|
+
import { rescaleLayout } from "../../utils/rescale";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Position nodes in two straight lines (bipartite layout).
|
|
12
|
-
*
|
|
13
12
|
* @param G - Graph or list of nodes
|
|
14
13
|
* @param nodes - Nodes in one node set of the graph
|
|
15
14
|
* @param align - The alignment of nodes: 'vertical' or 'horizontal'
|
|
@@ -19,76 +18,76 @@ import { rescaleLayout } from '../../utils/rescale';
|
|
|
19
18
|
* @returns Positions dictionary keyed by node
|
|
20
19
|
*/
|
|
21
20
|
export function bipartiteLayout(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
G: Graph,
|
|
22
|
+
nodes: Node[] | null = null,
|
|
23
|
+
align: "vertical" | "horizontal" = "vertical",
|
|
24
|
+
scale: number = 1,
|
|
25
|
+
center: number[] | null = null,
|
|
26
|
+
aspectRatio: number = 4 / 3,
|
|
28
27
|
): PositionMap {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
if (align !== "vertical" && align !== "horizontal") {
|
|
29
|
+
throw new Error("align must be either vertical or horizontal");
|
|
30
|
+
}
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const processed = _processParams(G, center || [0, 0], 2);
|
|
33
|
+
const graph = processed.G;
|
|
34
|
+
center = processed.center;
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
const allNodes = getNodesFromGraph(graph);
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if (allNodes.length === 0) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
// If nodes not provided, try to determine bipartite sets
|
|
43
|
+
if (!nodes) {
|
|
44
|
+
// A simple heuristic for bipartite detection: use nodes with even/odd indices
|
|
45
|
+
// This is a simplification, in Python NetworkX has bipartite.sets()
|
|
46
|
+
nodes = allNodes.filter((_: Node, i: number): boolean => i % 2 === 0);
|
|
47
|
+
}
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
const left = new Set(nodes);
|
|
50
|
+
const right: Set<Node> = new Set(allNodes.filter((n: Node) => !left.has(n)));
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const height = 1;
|
|
53
|
+
const width = aspectRatio * height;
|
|
54
|
+
const offset = [width / 2, height / 2];
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
const pos: PositionMap = {};
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
// Position nodes in the left set
|
|
59
|
+
const leftNodes = [...left];
|
|
60
|
+
leftNodes.forEach((node, i) => {
|
|
61
|
+
const x = 0;
|
|
62
|
+
const y = (i * height) / (leftNodes.length || 1);
|
|
63
|
+
pos[node] = [x, y];
|
|
64
|
+
});
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
// Position nodes in the right set
|
|
67
|
+
const rightNodes = [...right];
|
|
68
|
+
rightNodes.forEach((node, i) => {
|
|
69
|
+
const x = width;
|
|
70
|
+
const y = (i * height) / (rightNodes.length || 1);
|
|
71
|
+
pos[node] = [x, y];
|
|
72
|
+
});
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
// Center positions around the origin and apply offset
|
|
75
|
+
for (const node in pos) {
|
|
76
|
+
pos[node][0] -= offset[0];
|
|
77
|
+
pos[node][1] -= offset[1];
|
|
78
|
+
}
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
// Rescale positions
|
|
81
|
+
const scaledPos = rescaleLayout(pos, scale, center) as PositionMap;
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
// Handle horizontal alignment
|
|
84
|
+
if (align === "horizontal") {
|
|
85
|
+
for (const node in scaledPos) {
|
|
86
|
+
const temp = scaledPos[node][0];
|
|
87
|
+
scaledPos[node][0] = scaledPos[node][1];
|
|
88
|
+
scaledPos[node][1] = temp;
|
|
89
|
+
}
|
|
90
90
|
}
|
|
91
|
-
}
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
}
|
|
92
|
+
return scaledPos;
|
|
93
|
+
}
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Hierarchical layout algorithms
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
5
|
+
export { bfsLayout } from "./bfs";
|
|
6
|
+
export { bipartiteLayout } from "./bipartite";
|
|
7
|
+
export { multipartiteLayout } from "./multipartite";
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import type { Graph, Node, PositionMap } from
|
|
2
|
-
import { getNodesFromGraph
|
|
3
|
-
import { _processParams } from
|
|
4
|
-
import { rescaleLayout } from
|
|
1
|
+
import type { Graph, Node, PositionMap } from "../../types";
|
|
2
|
+
import { getNeighbors,getNodesFromGraph } from "../../utils/graph";
|
|
3
|
+
import { _processParams } from "../../utils/params";
|
|
4
|
+
import { rescaleLayout } from "../../utils/rescale";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Position nodes in layers of straight lines (multipartite layout).
|
|
8
|
-
*
|
|
9
8
|
* @param G - Graph or list of nodes
|
|
10
9
|
* @param subsetKey - Object mapping layers to node sets, or node attribute name
|
|
11
10
|
* @param align - The alignment of nodes: 'vertical' or 'horizontal'
|
|
@@ -14,75 +13,75 @@ import { rescaleLayout } from '../../utils/rescale';
|
|
|
14
13
|
* @returns Positions dictionary keyed by node
|
|
15
14
|
*/
|
|
16
15
|
export function multipartiteLayout(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
G: Graph,
|
|
17
|
+
subsetKey: Record<number | string, Node | Node[]> | string = "subset",
|
|
18
|
+
align: "vertical" | "horizontal" = "vertical",
|
|
19
|
+
scale: number = 1,
|
|
20
|
+
center: number[] | null = null,
|
|
22
21
|
): PositionMap {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
if (align !== "vertical" && align !== "horizontal") {
|
|
23
|
+
throw new Error("align must be either vertical or horizontal");
|
|
24
|
+
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const processed = _processParams(G, center || [0, 0], 2);
|
|
27
|
+
const graph = processed.G;
|
|
28
|
+
center = processed.center;
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
const allNodes = getNodesFromGraph(graph);
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
if (allNodes.length === 0) {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
36
|
+
// Convert subsetKey to a layer mapping if it's a string
|
|
37
|
+
let layers: Record<number | string, Node[]> = {};
|
|
38
|
+
if (typeof subsetKey === "string") {
|
|
39
|
+
// In JS we don't have access to node attributes directly
|
|
40
|
+
// This is a simplification - in a real implementation we would need
|
|
41
|
+
// to access node attributes from the graph
|
|
42
|
+
console.warn("Using string subsetKey requires node attributes, using default partitioning");
|
|
43
|
+
// Create a simple partitioning as fallback
|
|
44
|
+
layers = { 0: allNodes };
|
|
45
|
+
} else {
|
|
46
|
+
// subsetKey is already a mapping of layers to nodes
|
|
47
|
+
// Convert single nodes to arrays
|
|
48
|
+
for (const [key, value] of Object.entries(subsetKey)) {
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
layers[key] = value;
|
|
51
|
+
} else {
|
|
52
|
+
layers[key] = [value];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
55
|
}
|
|
56
|
-
}
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const layerCount = Object.keys(layers).length;
|
|
58
|
+
let pos: PositionMap = {};
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
// Process each layer
|
|
61
|
+
Object.entries(layers).forEach(([layer, nodes], layerIdx) => {
|
|
62
|
+
const layerNodes = Array.isArray(nodes) ? nodes : [nodes];
|
|
63
|
+
const layerSize = layerNodes.length;
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
layerNodes.forEach((node, nodeIdx) => {
|
|
66
|
+
// Place nodes in a grid: layerIdx determines x-coordinate (column)
|
|
67
|
+
// nodeIdx determines y-coordinate (row position within column)
|
|
68
|
+
const x = layerIdx - (layerCount - 1) / 2;
|
|
69
|
+
const y = nodeIdx - (layerSize - 1) / 2;
|
|
70
|
+
pos[node] = [x, y];
|
|
71
|
+
});
|
|
72
72
|
});
|
|
73
|
-
});
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
// Rescale positions
|
|
75
|
+
pos = rescaleLayout(pos, scale, center) as PositionMap;
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
// Handle horizontal alignment
|
|
78
|
+
if (align === "horizontal") {
|
|
79
|
+
for (const node in pos) {
|
|
80
|
+
const temp = pos[node][0];
|
|
81
|
+
pos[node][0] = pos[node][1];
|
|
82
|
+
pos[node][1] = temp;
|
|
83
|
+
}
|
|
84
84
|
}
|
|
85
|
-
}
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
}
|
|
86
|
+
return pos;
|
|
87
|
+
}
|
package/src/layouts/index.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Re-export all layout algorithms
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
5
|
+
export * from "./basic";
|
|
6
|
+
export * from "./force-directed";
|
|
7
|
+
export * from "./geometric";
|
|
8
|
+
export * from "./hierarchical";
|
|
9
|
+
export * from "./specialized";
|
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
* Planar layout algorithm
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { getNodesFromGraph
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { checkPlanarity, combinatorialEmbeddingToPos } from "../../algorithms/planarity";
|
|
6
|
+
import { Edge, Graph, Node, PositionMap } from "../../types";
|
|
7
|
+
import { getEdgesFromGraph,getNodesFromGraph } from "../../utils/graph";
|
|
8
|
+
import { _processParams } from "../../utils/params";
|
|
9
|
+
import { rescaleLayout } from "../../utils/rescale";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Position nodes without edge intersections (planar layout).
|
|
13
|
-
*
|
|
14
13
|
* @param G - Graph
|
|
15
14
|
* @param scale - Scale factor for positions
|
|
16
15
|
* @param center - Coordinate pair around which to center the layout
|
|
@@ -19,49 +18,49 @@ import { checkPlanarity, combinatorialEmbeddingToPos } from '../../algorithms/pl
|
|
|
19
18
|
* @returns Positions dictionary keyed by node
|
|
20
19
|
*/
|
|
21
20
|
export function planarLayout(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
G: Graph,
|
|
22
|
+
scale: number = 1,
|
|
23
|
+
center: number[] | null = null,
|
|
24
|
+
dim: number = 2,
|
|
25
|
+
seed: number | null = null,
|
|
27
26
|
): PositionMap {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
if (dim !== 2) {
|
|
28
|
+
throw new Error("can only handle 2 dimensions");
|
|
29
|
+
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Planar layout requires a proper Graph, not just a list of nodes
|
|
35
|
-
if (Array.isArray(processed.G)) {
|
|
36
|
-
throw new Error('Planar layout requires a Graph with edges, not just a list of nodes');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const graph = processed.G;
|
|
40
|
-
center = processed.center;
|
|
31
|
+
const processed = _processParams(G, center || [0, 0], dim);
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
// Planar layout requires a proper Graph, not just a list of nodes
|
|
34
|
+
if (Array.isArray(processed.G)) {
|
|
35
|
+
throw new Error("Planar layout requires a Graph with edges, not just a list of nodes");
|
|
36
|
+
}
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
38
|
+
const graph = processed.G;
|
|
39
|
+
center = processed.center;
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
const nodes = getNodesFromGraph(graph);
|
|
42
|
+
const edges = getEdgesFromGraph(graph);
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
if (nodes.length === 0) {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
48
|
+
// Check if graph is planar and get embedding
|
|
49
|
+
const { isPlanar, embedding } = checkPlanarity(graph, nodes, edges, seed);
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
if (!isPlanar) {
|
|
52
|
+
throw new Error("G is not planar.");
|
|
53
|
+
}
|
|
62
54
|
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
if (!embedding) {
|
|
56
|
+
throw new Error("Failed to generate planar embedding.");
|
|
57
|
+
}
|
|
65
58
|
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
// Convert embedding to positions
|
|
60
|
+
let pos = combinatorialEmbeddingToPos(embedding, nodes);
|
|
61
|
+
|
|
62
|
+
// Rescale the positions
|
|
63
|
+
pos = rescaleLayout(pos, scale, center) as PositionMap;
|
|
64
|
+
|
|
65
|
+
return pos;
|
|
66
|
+
}
|