@statelyai/layout 0.0.1 → 0.0.3
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 +1 -3
- package/dist/elkjs/index.mjs +15 -11
- package/dist/{index-D2RodsZY.d.mts → index-8xYkohbz.d.mts} +2 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/layered/index.d.mts +1 -1
- package/dist/layered/index.mjs +1 -1
- package/dist/{layered-Dd868WZY.mjs → layered-f_3mmGHr.mjs} +717 -62
- package/dist/{spore-fTgSoRLP.mjs → spore-BfTduMDc.mjs} +1 -1
- package/package.json +20 -6
- package/src/box.ts +135 -0
- package/src/elkjs/index.ts +1832 -0
- package/src/elkjs/types.ts +103 -0
- package/src/errors.ts +16 -0
- package/src/fixed.ts +80 -0
- package/src/index.ts +92 -0
- package/src/java-random.ts +46 -0
- package/src/layered/bk-node-placement.ts +715 -0
- package/src/layered/elk-enum-values.ts +171 -0
- package/src/layered/elk-options.generated.ts +315 -0
- package/src/layered/elk-options.ts +98 -0
- package/src/layered/flexible-ports.ts +11 -0
- package/src/layered/high-degree.ts +127 -0
- package/src/layered/index.ts +2555 -0
- package/src/layered/layer-unzipping.ts +217 -0
- package/src/layered/linear-segments-node-placement.ts +447 -0
- package/src/layered/long-edges.ts +406 -0
- package/src/layered/min-width.ts +159 -0
- package/src/layered/multi-edge-wrapping.ts +460 -0
- package/src/layered/network-simplex-node-placement.ts +500 -0
- package/src/layered/network-simplex.ts +346 -0
- package/src/layered/node-promotion.ts +197 -0
- package/src/layered/spacing.ts +37 -0
- package/src/layered/spline-bezier.ts +102 -0
- package/src/layered/strategies.ts +5403 -0
- package/src/layered/stretch-width.ts +136 -0
- package/src/layered/types.ts +111 -0
- package/src/layout.ts +202 -0
- package/src/packing.ts +74 -0
- package/src/random.ts +142 -0
- package/src/spore.ts +103 -0
- package/src/types.ts +84 -0
package/src/spore.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Graph, VisualGraph, VisualNode } from "@statelyai/graph";
|
|
2
|
+
import { getNodeSize, type LayoutOptions } from "@statelyai/graph/layout";
|
|
3
|
+
import { getFixedLayout } from "./fixed";
|
|
4
|
+
import type { LayoutPadding } from "./layered";
|
|
5
|
+
import type { LayoutAlgorithm } from "./types";
|
|
6
|
+
|
|
7
|
+
export interface SporeLayoutOptions extends Pick<LayoutOptions, "direction" | "measure"> {
|
|
8
|
+
spacing?: number;
|
|
9
|
+
padding?: number | Partial<LayoutPadding>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getPadding(value: SporeLayoutOptions["padding"]): LayoutPadding {
|
|
13
|
+
if (typeof value === "number") {
|
|
14
|
+
return { top: value, right: value, bottom: value, left: value };
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
top: value?.top ?? 0,
|
|
18
|
+
right: value?.right ?? 0,
|
|
19
|
+
bottom: value?.bottom ?? 0,
|
|
20
|
+
left: value?.left ?? 0,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getSporeLayout<N, E, G, P>(
|
|
25
|
+
graph: Graph<N, E, G, P> | VisualGraph<N, E, G, P>,
|
|
26
|
+
options: SporeLayoutOptions,
|
|
27
|
+
compact: boolean,
|
|
28
|
+
): VisualGraph<N, E, G, P> {
|
|
29
|
+
const spacing = options.spacing ?? 20;
|
|
30
|
+
const padding = getPadding(options.padding);
|
|
31
|
+
const nodes: VisualNode<N, P>[] = [];
|
|
32
|
+
|
|
33
|
+
graph.nodes.forEach((node, index) => {
|
|
34
|
+
const size = getNodeSize(node, options);
|
|
35
|
+
const previous = graph.nodes[index - 1];
|
|
36
|
+
const placedPrevious = nodes[index - 1];
|
|
37
|
+
if (!previous || !placedPrevious) {
|
|
38
|
+
nodes.push({
|
|
39
|
+
...node,
|
|
40
|
+
x: padding.left,
|
|
41
|
+
y: padding.top,
|
|
42
|
+
...size,
|
|
43
|
+
} as VisualNode<N, P>);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const deltaX = (node.x ?? 0) - (previous.x ?? 0);
|
|
47
|
+
const deltaY = (node.y ?? 0) - (previous.y ?? 0);
|
|
48
|
+
const requiredX = placedPrevious.width + spacing;
|
|
49
|
+
const requiredY = placedPrevious.height + spacing;
|
|
50
|
+
const distance = (delta: number, required: number): number => {
|
|
51
|
+
if (delta === 0) return 0;
|
|
52
|
+
const magnitude = compact ? required : Math.max(Math.abs(delta), required);
|
|
53
|
+
return Math.sign(delta) * magnitude;
|
|
54
|
+
};
|
|
55
|
+
nodes.push({
|
|
56
|
+
...node,
|
|
57
|
+
x: placedPrevious.x + distance(deltaX, requiredX),
|
|
58
|
+
y: placedPrevious.y + distance(deltaY, requiredY),
|
|
59
|
+
...size,
|
|
60
|
+
} as VisualNode<N, P>);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return getFixedLayout({ ...graph, nodes }, { direction: options.direction ?? graph.direction });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Compact an existing layout while preserving its relative directions. */
|
|
67
|
+
export function getSporeCompactionLayout<N, E, G, P>(
|
|
68
|
+
graph: Graph<N, E, G, P> | VisualGraph<N, E, G, P>,
|
|
69
|
+
options: SporeLayoutOptions = {},
|
|
70
|
+
): VisualGraph<N, E, G, P> {
|
|
71
|
+
return getSporeLayout(graph, options, true);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Remove overlap while preserving existing distances that already fit. */
|
|
75
|
+
export function getSporeOverlapRemovalLayout<N, E, G, P>(
|
|
76
|
+
graph: Graph<N, E, G, P> | VisualGraph<N, E, G, P>,
|
|
77
|
+
options: SporeLayoutOptions = {},
|
|
78
|
+
): VisualGraph<N, E, G, P> {
|
|
79
|
+
return getSporeLayout(graph, options, false);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function algorithm(
|
|
83
|
+
id: "sporeCompaction" | "sporeOverlap",
|
|
84
|
+
compact: boolean,
|
|
85
|
+
): LayoutAlgorithm<SporeLayoutOptions> {
|
|
86
|
+
return {
|
|
87
|
+
id,
|
|
88
|
+
capabilities: {
|
|
89
|
+
full: true,
|
|
90
|
+
incremental: false,
|
|
91
|
+
partial: false,
|
|
92
|
+
routeOnly: false,
|
|
93
|
+
hierarchy: false,
|
|
94
|
+
ports: true,
|
|
95
|
+
},
|
|
96
|
+
layout(graph, options) {
|
|
97
|
+
return getSporeLayout(graph, options ?? {}, compact);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const sporeCompactionAlgorithm = algorithm("sporeCompaction", true);
|
|
103
|
+
export const sporeOverlapRemovalAlgorithm = algorithm("sporeOverlap", false);
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Graph, GraphPatch, VisualGraph } from "@statelyai/graph";
|
|
2
|
+
|
|
3
|
+
export type AnyGraph = Graph<unknown, unknown, unknown, unknown>;
|
|
4
|
+
|
|
5
|
+
export type LayoutDirection = "up" | "down" | "left" | "right";
|
|
6
|
+
|
|
7
|
+
export type LayoutScope =
|
|
8
|
+
| { mode: "full" }
|
|
9
|
+
| {
|
|
10
|
+
mode: "incremental";
|
|
11
|
+
previous: VisualGraph;
|
|
12
|
+
}
|
|
13
|
+
| {
|
|
14
|
+
mode: "partial";
|
|
15
|
+
previous: VisualGraph;
|
|
16
|
+
nodeIds: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
| {
|
|
19
|
+
mode: "route-only";
|
|
20
|
+
previous: VisualGraph;
|
|
21
|
+
edgeIds?: readonly string[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface LayoutDiagnostic {
|
|
25
|
+
severity: "info" | "warning" | "error";
|
|
26
|
+
code: string;
|
|
27
|
+
message: string;
|
|
28
|
+
entityIds?: readonly string[];
|
|
29
|
+
phase?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface LayoutPhaseMetrics {
|
|
33
|
+
id: string;
|
|
34
|
+
durationMs: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface LayoutMetrics {
|
|
38
|
+
durationMs: number;
|
|
39
|
+
nodeCount: number;
|
|
40
|
+
edgeCount: number;
|
|
41
|
+
phases: readonly LayoutPhaseMetrics[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface LayoutCapabilities {
|
|
45
|
+
full: boolean;
|
|
46
|
+
incremental: boolean;
|
|
47
|
+
partial: boolean;
|
|
48
|
+
routeOnly: boolean;
|
|
49
|
+
hierarchy: boolean;
|
|
50
|
+
ports: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface LayoutAlgorithm<Options = unknown> {
|
|
54
|
+
readonly id: string;
|
|
55
|
+
readonly capabilities: LayoutCapabilities;
|
|
56
|
+
layout<N, E, G, P>(
|
|
57
|
+
graph: Graph<N, E, G, P> | VisualGraph<N, E, G, P>,
|
|
58
|
+
options: Options,
|
|
59
|
+
context: LayoutExecutionContext,
|
|
60
|
+
): VisualGraph<N, E, G, P> | Promise<VisualGraph<N, E, G, P>>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface LayoutExecutionContext {
|
|
64
|
+
readonly scope: LayoutScope;
|
|
65
|
+
readonly signal?: AbortSignal;
|
|
66
|
+
readonly diagnostics: LayoutDiagnostic[];
|
|
67
|
+
measurePhase<T>(id: string, run: () => T): T;
|
|
68
|
+
throwIfAborted(): void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface LayoutRequest<N = unknown, E = unknown, G = unknown, P = unknown, O = unknown> {
|
|
72
|
+
graph: Graph<N, E, G, P> | VisualGraph<N, E, G, P>;
|
|
73
|
+
algorithm?: string | LayoutAlgorithm<O>;
|
|
74
|
+
options?: O;
|
|
75
|
+
scope?: LayoutScope;
|
|
76
|
+
signal?: AbortSignal;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface LayoutResult<N = unknown, E = unknown, G = unknown, P = unknown> {
|
|
80
|
+
graph: VisualGraph<N, E, G, P>;
|
|
81
|
+
patches: readonly GraphPatch<N, E>[];
|
|
82
|
+
diagnostics: readonly LayoutDiagnostic[];
|
|
83
|
+
metrics: LayoutMetrics;
|
|
84
|
+
}
|