@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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export type ElkId = string | number;
|
|
2
|
+
|
|
3
|
+
export interface ElkGraphElement {
|
|
4
|
+
id?: ElkId;
|
|
5
|
+
layoutOptions?: Record<string, unknown>;
|
|
6
|
+
properties?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ElkPoint {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ElkShape extends ElkGraphElement {
|
|
15
|
+
x?: number;
|
|
16
|
+
y?: number;
|
|
17
|
+
width?: number;
|
|
18
|
+
height?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ElkLabel extends ElkShape {
|
|
22
|
+
text?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ElkPort extends ElkShape {
|
|
26
|
+
labels?: ElkLabel[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ElkEdgeSection extends ElkGraphElement {
|
|
30
|
+
startPoint: ElkPoint;
|
|
31
|
+
endPoint: ElkPoint;
|
|
32
|
+
bendPoints?: ElkPoint[];
|
|
33
|
+
incomingShape?: ElkId;
|
|
34
|
+
outgoingShape?: ElkId;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ElkEdge extends ElkGraphElement {
|
|
38
|
+
sources?: ElkId[];
|
|
39
|
+
targets?: ElkId[];
|
|
40
|
+
source?: ElkId;
|
|
41
|
+
target?: ElkId;
|
|
42
|
+
sourcePort?: ElkId;
|
|
43
|
+
targetPort?: ElkId;
|
|
44
|
+
labels?: ElkLabel[];
|
|
45
|
+
sections?: ElkEdgeSection[];
|
|
46
|
+
junctionPoints?: ElkPoint[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ElkNode extends ElkShape {
|
|
50
|
+
labels?: ElkLabel[];
|
|
51
|
+
ports?: ElkPort[];
|
|
52
|
+
children?: ElkNode[];
|
|
53
|
+
edges?: ElkEdge[];
|
|
54
|
+
logging?: ElkLogging;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ElkLogging {
|
|
58
|
+
name?: string;
|
|
59
|
+
executionTime?: number;
|
|
60
|
+
logs?: string[];
|
|
61
|
+
children?: ElkLogging[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ElkLayoutArguments {
|
|
65
|
+
layoutOptions?: Record<string, unknown>;
|
|
66
|
+
logging?: boolean;
|
|
67
|
+
measureExecutionTime?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ElkConstructorArguments {
|
|
71
|
+
defaultLayoutOptions?: Record<string, unknown>;
|
|
72
|
+
algorithms?: string[];
|
|
73
|
+
workerUrl?: string;
|
|
74
|
+
workerFactory?: (url?: string) => unknown;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ElkCommonDescription {
|
|
78
|
+
id?: string;
|
|
79
|
+
name?: string;
|
|
80
|
+
description?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface ElkLayoutAlgorithmDescription extends ElkCommonDescription {
|
|
84
|
+
category?: string;
|
|
85
|
+
knownOptions?: string[];
|
|
86
|
+
supportedFeatures?: string[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ElkLayoutOptionDescription extends ElkCommonDescription {
|
|
90
|
+
group?: string;
|
|
91
|
+
type?: string;
|
|
92
|
+
targets?: string[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface ElkLayoutCategoryDescription extends ElkCommonDescription {
|
|
96
|
+
knownLayouters?: string[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type LaidOutElkNode<T extends ElkNode> = Omit<T, "children" | "edges"> &
|
|
100
|
+
Omit<ElkNode, "children" | "edges"> & {
|
|
101
|
+
children?: Array<NonNullable<T["children"]>[number] & ElkNode>;
|
|
102
|
+
edges?: Array<NonNullable<T["edges"]>[number] & ElkEdge>;
|
|
103
|
+
};
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class LayoutError extends Error {
|
|
2
|
+
constructor(
|
|
3
|
+
message: string,
|
|
4
|
+
readonly code: string,
|
|
5
|
+
) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "LayoutError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class UnsupportedLayoutError extends LayoutError {
|
|
12
|
+
constructor(message: string) {
|
|
13
|
+
super(message, "UNSUPPORTED_LAYOUT");
|
|
14
|
+
this.name = "UnsupportedLayoutError";
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/fixed.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Graph, Point, VisualGraph, VisualNode } from "@statelyai/graph";
|
|
2
|
+
import { getNodeSize, type LayoutOptions } from "@statelyai/graph/layout";
|
|
3
|
+
import { getPolylineMidpoint, placePorts } from "./layered/strategies";
|
|
4
|
+
import type { LayoutAlgorithm } from "./types";
|
|
5
|
+
|
|
6
|
+
export interface FixedLayoutOptions extends Pick<LayoutOptions, "direction" | "measure"> {}
|
|
7
|
+
|
|
8
|
+
function defaultRoute(source: VisualNode, target: VisualNode): Point[] {
|
|
9
|
+
return [
|
|
10
|
+
{
|
|
11
|
+
x: source.x + source.width / 2,
|
|
12
|
+
y: source.y + source.height / 2,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
x: target.x + target.width / 2,
|
|
16
|
+
y: target.y + target.height / 2,
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Preserve authored positions and routes while completing visual geometry. */
|
|
22
|
+
export function getFixedLayout<N, E, G, P>(
|
|
23
|
+
graph: Graph<N, E, G, P> | VisualGraph<N, E, G, P>,
|
|
24
|
+
options: FixedLayoutOptions = {},
|
|
25
|
+
): VisualGraph<N, E, G, P> {
|
|
26
|
+
const direction = options.direction ?? graph.direction ?? "down";
|
|
27
|
+
const nodes = graph.nodes.map((node): VisualNode<N, P> => {
|
|
28
|
+
const size = getNodeSize(node, options);
|
|
29
|
+
const rect = {
|
|
30
|
+
x: node.x ?? 0,
|
|
31
|
+
y: node.y ?? 0,
|
|
32
|
+
...size,
|
|
33
|
+
};
|
|
34
|
+
const ports = placePorts(node.ports, rect, direction);
|
|
35
|
+
return {
|
|
36
|
+
...node,
|
|
37
|
+
...rect,
|
|
38
|
+
...(ports === undefined ? {} : { ports }),
|
|
39
|
+
} as VisualNode<N, P>;
|
|
40
|
+
});
|
|
41
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
42
|
+
const edges = graph.edges.map((edge) => {
|
|
43
|
+
const source = nodeById.get(edge.sourceId);
|
|
44
|
+
const target = nodeById.get(edge.targetId);
|
|
45
|
+
const points = edge.points
|
|
46
|
+
? [...edge.points]
|
|
47
|
+
: source && target
|
|
48
|
+
? defaultRoute(source, target)
|
|
49
|
+
: [];
|
|
50
|
+
const midpoint = getPolylineMidpoint(points);
|
|
51
|
+
const width = edge.width ?? 0;
|
|
52
|
+
const height = edge.height ?? 0;
|
|
53
|
+
return {
|
|
54
|
+
...edge,
|
|
55
|
+
x: edge.x ?? midpoint.x - width / 2,
|
|
56
|
+
y: edge.y ?? midpoint.y - height / 2,
|
|
57
|
+
width,
|
|
58
|
+
height,
|
|
59
|
+
points,
|
|
60
|
+
routing: edge.routing ?? ("polyline" as const),
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return { ...graph, direction, nodes, edges };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const fixedAlgorithm: LayoutAlgorithm<FixedLayoutOptions> = {
|
|
68
|
+
id: "fixed",
|
|
69
|
+
capabilities: {
|
|
70
|
+
full: true,
|
|
71
|
+
incremental: false,
|
|
72
|
+
partial: false,
|
|
73
|
+
routeOnly: false,
|
|
74
|
+
hierarchy: true,
|
|
75
|
+
ports: true,
|
|
76
|
+
},
|
|
77
|
+
layout(graph, options) {
|
|
78
|
+
return getFixedLayout(graph, options ?? {});
|
|
79
|
+
},
|
|
80
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export { LayoutError, UnsupportedLayoutError } from "./errors";
|
|
2
|
+
export { boxAlgorithm, getBoxLayout } from "./box";
|
|
3
|
+
export type { BoxLayoutOptions } from "./box";
|
|
4
|
+
export { fixedAlgorithm, getFixedLayout } from "./fixed";
|
|
5
|
+
export type { FixedLayoutOptions } from "./fixed";
|
|
6
|
+
export { getRectanglePackingLayout, rectanglePackingAlgorithm } from "./packing";
|
|
7
|
+
export type { RectanglePackingLayoutOptions } from "./packing";
|
|
8
|
+
export { getRandomLayout, randomAlgorithm } from "./random";
|
|
9
|
+
export type { RandomLayoutOptions } from "./random";
|
|
10
|
+
export {
|
|
11
|
+
getSporeCompactionLayout,
|
|
12
|
+
getSporeOverlapRemovalLayout,
|
|
13
|
+
sporeCompactionAlgorithm,
|
|
14
|
+
sporeOverlapRemovalAlgorithm,
|
|
15
|
+
} from "./spore";
|
|
16
|
+
export type { SporeLayoutOptions } from "./spore";
|
|
17
|
+
export { getLayout, getLayoutAlgorithm, registerLayoutAlgorithm } from "./layout";
|
|
18
|
+
export {
|
|
19
|
+
assignLayersByLongestPath,
|
|
20
|
+
assignLayersByLongestPathToSink,
|
|
21
|
+
assignLayersByBreadthFirstModelOrder,
|
|
22
|
+
assignLayersByDepthFirstModelOrder,
|
|
23
|
+
assignLayersInteractively,
|
|
24
|
+
assignLayersWithCoffmanGraham,
|
|
25
|
+
assignLayersWithNetworkSimplex,
|
|
26
|
+
assignLayersWithMinWidth,
|
|
27
|
+
assignLayersWithStretchWidth,
|
|
28
|
+
breakCyclesByModelOrder,
|
|
29
|
+
breakCyclesByStronglyConnectedConnectivity,
|
|
30
|
+
breakCyclesByStronglyConnectedNodeType,
|
|
31
|
+
breakCyclesWithDepthFirstSearch,
|
|
32
|
+
breakCyclesGreedily,
|
|
33
|
+
breakCyclesGreedilyByModelOrder,
|
|
34
|
+
breakCyclesInteractively,
|
|
35
|
+
breakCyclesWithModelOrderDepthFirstSearch,
|
|
36
|
+
breakCyclesWithModelOrderBreadthFirstSearch,
|
|
37
|
+
getLayeredLayout,
|
|
38
|
+
layeredAlgorithm,
|
|
39
|
+
minimizeCrossingsWithBarycenter,
|
|
40
|
+
minimizeCrossingsWithMedian,
|
|
41
|
+
minimizeCrossingsInteractively,
|
|
42
|
+
minimizeCrossingsWithModelOrder,
|
|
43
|
+
placeNodesInLayers,
|
|
44
|
+
placeNodesInteractively,
|
|
45
|
+
routeEdgesOrthogonally,
|
|
46
|
+
routeEdgesWithPolylines,
|
|
47
|
+
routeEdgesWithSplines,
|
|
48
|
+
elkLayeredEnumValues,
|
|
49
|
+
elkLayeredOptionDefinitions,
|
|
50
|
+
fromElkLayeredOptionId,
|
|
51
|
+
toElkLayeredOptions,
|
|
52
|
+
} from "./layered";
|
|
53
|
+
export type {
|
|
54
|
+
AnyGraph,
|
|
55
|
+
LayoutAlgorithm,
|
|
56
|
+
LayoutCapabilities,
|
|
57
|
+
LayoutDiagnostic,
|
|
58
|
+
LayoutDirection,
|
|
59
|
+
LayoutExecutionContext,
|
|
60
|
+
LayoutMetrics,
|
|
61
|
+
LayoutPhaseMetrics,
|
|
62
|
+
LayoutRequest,
|
|
63
|
+
LayoutResult,
|
|
64
|
+
LayoutScope,
|
|
65
|
+
} from "./types";
|
|
66
|
+
export type {
|
|
67
|
+
AcyclicOrientation,
|
|
68
|
+
CrossingMinimizer,
|
|
69
|
+
CycleBreaker,
|
|
70
|
+
EdgeRouter,
|
|
71
|
+
EdgeRoutes,
|
|
72
|
+
LayerAssigner,
|
|
73
|
+
LayerAssignment,
|
|
74
|
+
LayeredLayoutOptions,
|
|
75
|
+
LayeredAdvancedOptions,
|
|
76
|
+
ElkLayeredOptionId,
|
|
77
|
+
ElkLayeredOptionName,
|
|
78
|
+
ElkLayeredOptionValueByName,
|
|
79
|
+
CycleBreakingStrategy,
|
|
80
|
+
CrossingMinimizationStrategy,
|
|
81
|
+
EdgeRoutingStyle,
|
|
82
|
+
LayeringStrategy,
|
|
83
|
+
NodePlacementStrategy,
|
|
84
|
+
LayeredPhaseInput,
|
|
85
|
+
LayeredSpacing,
|
|
86
|
+
LayeredStrategies,
|
|
87
|
+
LayoutPadding,
|
|
88
|
+
LayerOrder,
|
|
89
|
+
NodePlacement,
|
|
90
|
+
NodePlacer,
|
|
91
|
+
NodeSize,
|
|
92
|
+
} from "./layered";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Java's 48-bit `java.util.Random`, used by ELK for seeded tie-breaking. */
|
|
2
|
+
export class JavaRandom {
|
|
3
|
+
static readonly #multiplier = 0x5deece66dn;
|
|
4
|
+
static readonly #addend = 0xbn;
|
|
5
|
+
static readonly #mask = (1n << 48n) - 1n;
|
|
6
|
+
#seed: bigint;
|
|
7
|
+
|
|
8
|
+
constructor(seed: number | bigint) {
|
|
9
|
+
this.#seed = (BigInt(seed) ^ JavaRandom.#multiplier) & JavaRandom.#mask;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#next(bits: number): number {
|
|
13
|
+
this.#seed = (this.#seed * JavaRandom.#multiplier + JavaRandom.#addend) & JavaRandom.#mask;
|
|
14
|
+
return Number(this.#seed >> BigInt(48 - bits));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
nextDouble(): number {
|
|
18
|
+
return (this.#next(26) * 2 ** 27 + this.#next(27)) / 2 ** 53;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
nextFloat(): number {
|
|
22
|
+
return this.#next(24) / 2 ** 24;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
nextBoolean(): boolean {
|
|
26
|
+
return this.#next(1) !== 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
nextLong(): bigint {
|
|
30
|
+
const high = BigInt.asIntN(32, BigInt(this.#next(32)));
|
|
31
|
+
const low = BigInt.asIntN(32, BigInt(this.#next(32)));
|
|
32
|
+
return BigInt.asIntN(64, (high << 32n) + low);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
nextInt(bound: number): number {
|
|
36
|
+
if (bound <= 0) throw new RangeError("bound must be positive");
|
|
37
|
+
if ((bound & -bound) === bound) return Math.floor((bound * this.#next(31)) / 2 ** 31);
|
|
38
|
+
let bits: number;
|
|
39
|
+
let value: number;
|
|
40
|
+
do {
|
|
41
|
+
bits = this.#next(31);
|
|
42
|
+
value = bits % bound;
|
|
43
|
+
} while (bits - value + (bound - 1) >= 2 ** 31);
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
}
|