@graphty/layout 1.2.8 → 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.
Files changed (185) hide show
  1. package/README.md +394 -404
  2. package/dist/layout.js +881 -821
  3. package/dist/layout.js.map +1 -1
  4. package/dist/src/algorithms/index.d.ts +2 -2
  5. package/dist/src/algorithms/index.js +2 -2
  6. package/dist/src/algorithms/index.js.map +1 -1
  7. package/dist/src/algorithms/optimization/index.d.ts +4 -4
  8. package/dist/src/algorithms/optimization/index.js +3 -3
  9. package/dist/src/algorithms/optimization/index.js.map +1 -1
  10. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts +2 -5
  11. package/dist/src/algorithms/optimization/kamada-kawai-solver.js +6 -9
  12. package/dist/src/algorithms/optimization/kamada-kawai-solver.js.map +1 -1
  13. package/dist/src/algorithms/optimization/lbfgs.d.ts +0 -1
  14. package/dist/src/algorithms/optimization/lbfgs.js +3 -5
  15. package/dist/src/algorithms/optimization/lbfgs.js.map +1 -1
  16. package/dist/src/algorithms/optimization/line-search.d.ts +0 -1
  17. package/dist/src/algorithms/optimization/line-search.js +0 -1
  18. package/dist/src/algorithms/optimization/line-search.js.map +1 -1
  19. package/dist/src/algorithms/optimization/types.d.ts +1 -1
  20. package/dist/src/algorithms/planarity/check.d.ts +3 -3
  21. package/dist/src/algorithms/planarity/check.js +7 -7
  22. package/dist/src/algorithms/planarity/check.js.map +1 -1
  23. package/dist/src/algorithms/planarity/embedding.d.ts +3 -5
  24. package/dist/src/algorithms/planarity/embedding.js +20 -16
  25. package/dist/src/algorithms/planarity/embedding.js.map +1 -1
  26. package/dist/src/algorithms/planarity/index.d.ts +4 -4
  27. package/dist/src/algorithms/planarity/index.js +4 -4
  28. package/dist/src/algorithms/planarity/index.js.map +1 -1
  29. package/dist/src/algorithms/planarity/lr-test.d.ts +3 -3
  30. package/dist/src/algorithms/planarity/lr-test.js +5 -5
  31. package/dist/src/algorithms/planarity/lr-test.js.map +1 -1
  32. package/dist/src/algorithms/planarity/special-graphs.d.ts +1 -4
  33. package/dist/src/algorithms/planarity/special-graphs.js +18 -15
  34. package/dist/src/algorithms/planarity/special-graphs.js.map +1 -1
  35. package/dist/src/generators/basic.d.ts +1 -1
  36. package/dist/src/generators/basic.js +4 -4
  37. package/dist/src/generators/basic.js.map +1 -1
  38. package/dist/src/generators/bipartite.d.ts +1 -1
  39. package/dist/src/generators/bipartite.js +2 -2
  40. package/dist/src/generators/bipartite.js.map +1 -1
  41. package/dist/src/generators/grid.d.ts +1 -1
  42. package/dist/src/generators/grid.js +1 -1
  43. package/dist/src/generators/grid.js.map +1 -1
  44. package/dist/src/generators/index.d.ts +5 -5
  45. package/dist/src/generators/index.js +5 -5
  46. package/dist/src/generators/index.js.map +1 -1
  47. package/dist/src/generators/random.d.ts +1 -1
  48. package/dist/src/generators/random.js +2 -2
  49. package/dist/src/generators/random.js.map +1 -1
  50. package/dist/src/generators/scale-free.d.ts +1 -1
  51. package/dist/src/generators/scale-free.js +4 -4
  52. package/dist/src/generators/scale-free.js.map +1 -1
  53. package/dist/src/index.d.ts +4 -4
  54. package/dist/src/index.js +4 -4
  55. package/dist/src/layouts/basic/index.d.ts +1 -1
  56. package/dist/src/layouts/basic/index.js +1 -1
  57. package/dist/src/layouts/basic/random.d.ts +1 -2
  58. package/dist/src/layouts/basic/random.js +3 -4
  59. package/dist/src/layouts/basic/random.js.map +1 -1
  60. package/dist/src/layouts/force-directed/arf.d.ts +1 -2
  61. package/dist/src/layouts/force-directed/arf.js +13 -8
  62. package/dist/src/layouts/force-directed/arf.js.map +1 -1
  63. package/dist/src/layouts/force-directed/forceatlas2.d.ts +1 -2
  64. package/dist/src/layouts/force-directed/forceatlas2.js +58 -35
  65. package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -1
  66. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +11 -12
  67. package/dist/src/layouts/force-directed/fruchterman-reingold.js +17 -17
  68. package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -1
  69. package/dist/src/layouts/force-directed/index.d.ts +5 -5
  70. package/dist/src/layouts/force-directed/index.js +5 -5
  71. package/dist/src/layouts/force-directed/index.js.map +1 -1
  72. package/dist/src/layouts/force-directed/kamada-kawai.d.ts +2 -3
  73. package/dist/src/layouts/force-directed/kamada-kawai.js +12 -10
  74. package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -1
  75. package/dist/src/layouts/force-directed/spring.d.ts +11 -12
  76. package/dist/src/layouts/force-directed/spring.js +11 -12
  77. package/dist/src/layouts/force-directed/spring.js.map +1 -1
  78. package/dist/src/layouts/geometric/circular.d.ts +1 -2
  79. package/dist/src/layouts/geometric/circular.js +10 -9
  80. package/dist/src/layouts/geometric/circular.js.map +1 -1
  81. package/dist/src/layouts/geometric/index.d.ts +3 -3
  82. package/dist/src/layouts/geometric/index.js +3 -3
  83. package/dist/src/layouts/geometric/shell.d.ts +1 -2
  84. package/dist/src/layouts/geometric/shell.js +5 -5
  85. package/dist/src/layouts/geometric/shell.js.map +1 -1
  86. package/dist/src/layouts/geometric/spiral.d.ts +1 -2
  87. package/dist/src/layouts/geometric/spiral.js +5 -9
  88. package/dist/src/layouts/geometric/spiral.js.map +1 -1
  89. package/dist/src/layouts/hierarchical/bfs.d.ts +2 -3
  90. package/dist/src/layouts/hierarchical/bfs.js +5 -6
  91. package/dist/src/layouts/hierarchical/bfs.js.map +1 -1
  92. package/dist/src/layouts/hierarchical/bipartite.d.ts +2 -3
  93. package/dist/src/layouts/hierarchical/bipartite.js +8 -9
  94. package/dist/src/layouts/hierarchical/bipartite.js.map +1 -1
  95. package/dist/src/layouts/hierarchical/index.d.ts +3 -3
  96. package/dist/src/layouts/hierarchical/index.js +3 -3
  97. package/dist/src/layouts/hierarchical/index.js.map +1 -1
  98. package/dist/src/layouts/hierarchical/multipartite.d.ts +2 -3
  99. package/dist/src/layouts/hierarchical/multipartite.js +7 -8
  100. package/dist/src/layouts/hierarchical/multipartite.js.map +1 -1
  101. package/dist/src/layouts/index.d.ts +5 -5
  102. package/dist/src/layouts/index.js +5 -5
  103. package/dist/src/layouts/index.js.map +1 -1
  104. package/dist/src/layouts/specialized/index.d.ts +2 -2
  105. package/dist/src/layouts/specialized/index.js +2 -2
  106. package/dist/src/layouts/specialized/index.js.map +1 -1
  107. package/dist/src/layouts/specialized/planar.d.ts +3 -3
  108. package/dist/src/layouts/specialized/planar.js +8 -8
  109. package/dist/src/layouts/specialized/planar.js.map +1 -1
  110. package/dist/src/layouts/specialized/spectral.d.ts +1 -2
  111. package/dist/src/layouts/specialized/spectral.js +30 -20
  112. package/dist/src/layouts/specialized/spectral.js.map +1 -1
  113. package/dist/src/types/embedding.d.ts +1 -1
  114. package/dist/src/types/index.d.ts +3 -3
  115. package/dist/src/types/index.js +3 -3
  116. package/dist/src/types/index.js.map +1 -1
  117. package/dist/src/types/layout.d.ts +1 -1
  118. package/dist/src/utils/graph.d.ts +1 -5
  119. package/dist/src/utils/graph.js +4 -6
  120. package/dist/src/utils/graph.js.map +1 -1
  121. package/dist/src/utils/index.d.ts +5 -5
  122. package/dist/src/utils/index.js +5 -5
  123. package/dist/src/utils/index.js.map +1 -1
  124. package/dist/src/utils/numpy.js +22 -16
  125. package/dist/src/utils/numpy.js.map +1 -1
  126. package/dist/src/utils/params.d.ts +1 -2
  127. package/dist/src/utils/params.js +0 -1
  128. package/dist/src/utils/params.js.map +1 -1
  129. package/dist/src/utils/random.d.ts +17 -0
  130. package/dist/src/utils/random.js +18 -1
  131. package/dist/src/utils/random.js.map +1 -1
  132. package/dist/src/utils/rescale.d.ts +1 -3
  133. package/dist/src/utils/rescale.js +12 -10
  134. package/dist/src/utils/rescale.js.map +1 -1
  135. package/dist/vitest.config.js +14 -14
  136. package/dist/vitest.config.js.map +1 -1
  137. package/package.json +29 -24
  138. package/src/algorithms/index.ts +2 -2
  139. package/src/algorithms/optimization/index.ts +4 -8
  140. package/src/algorithms/optimization/kamada-kawai-solver.ts +170 -179
  141. package/src/algorithms/optimization/lbfgs.ts +41 -48
  142. package/src/algorithms/optimization/line-search.ts +27 -28
  143. package/src/algorithms/optimization/types.ts +2 -2
  144. package/src/algorithms/planarity/check.ts +22 -21
  145. package/src/algorithms/planarity/embedding.ts +159 -165
  146. package/src/algorithms/planarity/index.ts +4 -8
  147. package/src/algorithms/planarity/lr-test.ts +46 -45
  148. package/src/algorithms/planarity/special-graphs.ts +73 -83
  149. package/src/generators/basic.ts +57 -57
  150. package/src/generators/bipartite.ts +37 -31
  151. package/src/generators/grid.ts +28 -28
  152. package/src/generators/index.ts +5 -11
  153. package/src/generators/random.ts +26 -25
  154. package/src/generators/scale-free.ts +56 -55
  155. package/src/index.ts +5 -5
  156. package/src/layouts/basic/index.ts +1 -1
  157. package/src/layouts/basic/random.ts +20 -16
  158. package/src/layouts/force-directed/arf.ts +107 -105
  159. package/src/layouts/force-directed/forceatlas2.ts +365 -351
  160. package/src/layouts/force-directed/fruchterman-reingold.ts +133 -134
  161. package/src/layouts/force-directed/index.ts +5 -5
  162. package/src/layouts/force-directed/kamada-kawai.ts +78 -80
  163. package/src/layouts/force-directed/spring.ts +24 -25
  164. package/src/layouts/geometric/circular.ts +63 -57
  165. package/src/layouts/geometric/index.ts +3 -3
  166. package/src/layouts/geometric/shell.ts +58 -53
  167. package/src/layouts/geometric/spiral.ts +60 -64
  168. package/src/layouts/hierarchical/bfs.ts +58 -59
  169. package/src/layouts/hierarchical/bipartite.ts +62 -63
  170. package/src/layouts/hierarchical/index.ts +3 -3
  171. package/src/layouts/hierarchical/multipartite.ts +62 -63
  172. package/src/layouts/index.ts +5 -5
  173. package/src/layouts/specialized/index.ts +2 -2
  174. package/src/layouts/specialized/planar.ts +42 -41
  175. package/src/layouts/specialized/spectral.ts +110 -99
  176. package/src/types/embedding.ts +5 -5
  177. package/src/types/graph.ts +4 -4
  178. package/src/types/index.ts +3 -3
  179. package/src/types/layout.ts +2 -2
  180. package/src/utils/graph.ts +31 -35
  181. package/src/utils/index.ts +5 -5
  182. package/src/utils/numpy.ts +96 -92
  183. package/src/utils/params.ts +14 -11
  184. package/src/utils/random.ts +57 -40
  185. 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 '../../types';
6
- import { _processParams } from '../../utils/params';
7
- import { getNodesFromGraph } from '../../utils/graph';
8
- import { rescaleLayout } from '../../utils/rescale';
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
- G: Graph,
23
- nodes: Node[] | null = null,
24
- align: 'vertical' | 'horizontal' = 'vertical',
25
- scale: number = 1,
26
- center: number[] | null = null,
27
- aspectRatio: number = 4 / 3
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
- if (align !== 'vertical' && align !== 'horizontal') {
30
- throw new Error("align must be either vertical or horizontal");
31
- }
28
+ if (align !== "vertical" && align !== "horizontal") {
29
+ throw new Error("align must be either vertical or horizontal");
30
+ }
32
31
 
33
- const processed = _processParams(G, center || [0, 0], 2);
34
- const graph = processed.G;
35
- center = processed.center;
32
+ const processed = _processParams(G, center || [0, 0], 2);
33
+ const graph = processed.G;
34
+ center = processed.center;
36
35
 
37
- const allNodes = getNodesFromGraph(graph);
36
+ const allNodes = getNodesFromGraph(graph);
38
37
 
39
- if (allNodes.length === 0) {
40
- return {};
41
- }
38
+ if (allNodes.length === 0) {
39
+ return {};
40
+ }
42
41
 
43
- // If nodes not provided, try to determine bipartite sets
44
- if (!nodes) {
45
- // A simple heuristic for bipartite detection: use nodes with even/odd indices
46
- // This is a simplification, in Python NetworkX has bipartite.sets()
47
- nodes = allNodes.filter((_: Node, i: number): boolean => i % 2 === 0);
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
- const left = new Set(nodes);
51
- const right: Set<Node> = new Set(allNodes.filter((n: Node) => !left.has(n)));
49
+ const left = new Set(nodes);
50
+ const right: Set<Node> = new Set(allNodes.filter((n: Node) => !left.has(n)));
52
51
 
53
- const height = 1;
54
- const width = aspectRatio * height;
55
- const offset = [width / 2, height / 2];
52
+ const height = 1;
53
+ const width = aspectRatio * height;
54
+ const offset = [width / 2, height / 2];
56
55
 
57
- const pos: PositionMap = {};
56
+ const pos: PositionMap = {};
58
57
 
59
- // Position nodes in the left set
60
- const leftNodes = [...left];
61
- leftNodes.forEach((node, i) => {
62
- const x = 0;
63
- const y = i * height / (leftNodes.length || 1);
64
- pos[node] = [x, y];
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
- // Position nodes in the right set
68
- const rightNodes = [...right];
69
- rightNodes.forEach((node, i) => {
70
- const x = width;
71
- const y = i * height / (rightNodes.length || 1);
72
- pos[node] = [x, y];
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
- // Center positions around the origin and apply offset
76
- for (const node in pos) {
77
- pos[node][0] -= offset[0];
78
- pos[node][1] -= offset[1];
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
- // Rescale positions
82
- const scaledPos = rescaleLayout(pos, scale, center) as PositionMap;
80
+ // Rescale positions
81
+ const scaledPos = rescaleLayout(pos, scale, center) as PositionMap;
83
82
 
84
- // Handle horizontal alignment
85
- if (align === 'horizontal') {
86
- for (const node in scaledPos) {
87
- const temp = scaledPos[node][0];
88
- scaledPos[node][0] = scaledPos[node][1];
89
- scaledPos[node][1] = temp;
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
- return scaledPos;
94
- }
92
+ return scaledPos;
93
+ }
@@ -2,6 +2,6 @@
2
2
  * Hierarchical layout algorithms
3
3
  */
4
4
 
5
- export { bipartiteLayout } from './bipartite';
6
- export { multipartiteLayout } from './multipartite';
7
- export { bfsLayout } from './bfs';
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 '../../types';
2
- import { getNodesFromGraph, getNeighbors } from '../../utils/graph';
3
- import { _processParams } from '../../utils/params';
4
- import { rescaleLayout } from '../../utils/rescale';
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
- G: Graph,
18
- subsetKey: Record<number | string, Node | Node[]> | string = 'subset',
19
- align: 'vertical' | 'horizontal' = 'vertical',
20
- scale: number = 1,
21
- center: number[] | null = null
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
- if (align !== 'vertical' && align !== 'horizontal') {
24
- throw new Error("align must be either vertical or horizontal");
25
- }
22
+ if (align !== "vertical" && align !== "horizontal") {
23
+ throw new Error("align must be either vertical or horizontal");
24
+ }
26
25
 
27
- const processed = _processParams(G, center || [0, 0], 2);
28
- const graph = processed.G;
29
- center = processed.center;
26
+ const processed = _processParams(G, center || [0, 0], 2);
27
+ const graph = processed.G;
28
+ center = processed.center;
30
29
 
31
- const allNodes = getNodesFromGraph(graph);
30
+ const allNodes = getNodesFromGraph(graph);
32
31
 
33
- if (allNodes.length === 0) {
34
- return {};
35
- }
32
+ if (allNodes.length === 0) {
33
+ return {};
34
+ }
36
35
 
37
- // Convert subsetKey to a layer mapping if it's a string
38
- let layers: Record<number | string, Node[]> = {};
39
- if (typeof subsetKey === 'string') {
40
- // In JS we don't have access to node attributes directly
41
- // This is a simplification - in a real implementation we would need
42
- // to access node attributes from the graph
43
- console.warn("Using string subsetKey requires node attributes, using default partitioning");
44
- // Create a simple partitioning as fallback
45
- layers = { 0: allNodes };
46
- } else {
47
- // subsetKey is already a mapping of layers to nodes
48
- // Convert single nodes to arrays
49
- for (const [key, value] of Object.entries(subsetKey)) {
50
- if (Array.isArray(value)) {
51
- layers[key] = value;
52
- } else {
53
- layers[key] = [value];
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
- const layerCount = Object.keys(layers).length;
59
- let pos: PositionMap = {};
57
+ const layerCount = Object.keys(layers).length;
58
+ let pos: PositionMap = {};
60
59
 
61
- // Process each layer
62
- Object.entries(layers).forEach(([layer, nodes], layerIdx) => {
63
- const layerNodes = Array.isArray(nodes) ? nodes : [nodes];
64
- const layerSize = layerNodes.length;
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
- layerNodes.forEach((node, nodeIdx) => {
67
- // Place nodes in a grid: layerIdx determines x-coordinate (column)
68
- // nodeIdx determines y-coordinate (row position within column)
69
- const x = layerIdx - (layerCount - 1) / 2;
70
- const y = nodeIdx - (layerSize - 1) / 2;
71
- pos[node] = [x, y];
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
- // Rescale positions
76
- pos = rescaleLayout(pos, scale, center) as PositionMap;
74
+ // Rescale positions
75
+ pos = rescaleLayout(pos, scale, center) as PositionMap;
77
76
 
78
- // Handle horizontal alignment
79
- if (align === 'horizontal') {
80
- for (const node in pos) {
81
- const temp = pos[node][0];
82
- pos[node][0] = pos[node][1];
83
- pos[node][1] = temp;
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
- return pos;
88
- }
86
+ return pos;
87
+ }
@@ -2,8 +2,8 @@
2
2
  * Re-export all layout algorithms
3
3
  */
4
4
 
5
- export * from './basic';
6
- export * from './geometric';
7
- export * from './force-directed';
8
- export * from './hierarchical';
9
- export * from './specialized';
5
+ export * from "./basic";
6
+ export * from "./force-directed";
7
+ export * from "./geometric";
8
+ export * from "./hierarchical";
9
+ export * from "./specialized";
@@ -2,5 +2,5 @@
2
2
  * Specialized layout algorithms
3
3
  */
4
4
 
5
- export { spectralLayout } from './spectral';
6
- export { planarLayout } from './planar';
5
+ export { planarLayout } from "./planar";
6
+ export { spectralLayout } from "./spectral";
@@ -2,64 +2,65 @@
2
2
  * Planar layout algorithm
3
3
  */
4
4
 
5
- import { Graph, Node, Edge, PositionMap } from '../../types';
6
- import { _processParams } from '../../utils/params';
7
- import { getNodesFromGraph, getEdgesFromGraph } from '../../utils/graph';
8
- import { rescaleLayout } from '../../utils/rescale';
9
- import { checkPlanarity, combinatorialEmbeddingToPos } from '../../algorithms/planarity';
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
17
16
  * @param dim - Dimension of layout (must be 2)
17
+ * @param seed - Random seed for reproducible layouts
18
18
  * @returns Positions dictionary keyed by node
19
19
  */
20
20
  export function planarLayout(
21
- G: Graph,
22
- scale: number = 1,
23
- center: number[] | null = null,
24
- dim: number = 2
21
+ G: Graph,
22
+ scale: number = 1,
23
+ center: number[] | null = null,
24
+ dim: number = 2,
25
+ seed: number | null = null,
25
26
  ): PositionMap {
26
- if (dim !== 2) {
27
- throw new Error("can only handle 2 dimensions");
28
- }
27
+ if (dim !== 2) {
28
+ throw new Error("can only handle 2 dimensions");
29
+ }
29
30
 
30
- const processed = _processParams(G, center || [0, 0], dim);
31
-
32
- // Planar layout requires a proper Graph, not just a list of nodes
33
- if (Array.isArray(processed.G)) {
34
- throw new Error('Planar layout requires a Graph with edges, not just a list of nodes');
35
- }
36
-
37
- const graph = processed.G;
38
- center = processed.center;
31
+ const processed = _processParams(G, center || [0, 0], dim);
39
32
 
40
- const nodes = getNodesFromGraph(graph);
41
- const edges = getEdgesFromGraph(graph);
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
+ }
42
37
 
43
- if (nodes.length === 0) {
44
- return {};
45
- }
38
+ const graph = processed.G;
39
+ center = processed.center;
46
40
 
47
- // Check if graph is planar and get embedding
48
- const { isPlanar, embedding } = checkPlanarity(graph, nodes, edges);
41
+ const nodes = getNodesFromGraph(graph);
42
+ const edges = getEdgesFromGraph(graph);
49
43
 
50
- if (!isPlanar) {
51
- throw new Error("G is not planar.");
52
- }
44
+ if (nodes.length === 0) {
45
+ return {};
46
+ }
53
47
 
54
- if (!embedding) {
55
- throw new Error("Failed to generate planar embedding.");
56
- }
48
+ // Check if graph is planar and get embedding
49
+ const { isPlanar, embedding } = checkPlanarity(graph, nodes, edges, seed);
57
50
 
58
- // Convert embedding to positions
59
- let pos = combinatorialEmbeddingToPos(embedding, nodes);
51
+ if (!isPlanar) {
52
+ throw new Error("G is not planar.");
53
+ }
60
54
 
61
- // Rescale the positions
62
- pos = rescaleLayout(pos, scale, center) as PositionMap;
55
+ if (!embedding) {
56
+ throw new Error("Failed to generate planar embedding.");
57
+ }
63
58
 
64
- return pos;
65
- }
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
+ }