@statelyai/layout 0.3.0 → 0.4.1
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/NOTICE.md +4 -2
- package/README.md +150 -11
- package/dist/elkjs/index.cjs +2 -2
- package/dist/elkjs/index.d.cts +2 -0
- package/dist/elkjs/index.d.mts +2 -0
- package/dist/elkjs/index.mjs +2 -2
- package/dist/elkjs/main.cjs +1 -1
- package/dist/elkjs/main.mjs +1 -1
- package/dist/elkjs/worker.cjs +2 -2
- package/dist/elkjs/worker.mjs +2 -2
- package/dist/elkjs-BBfKN4Dw.mjs +3075 -0
- package/dist/elkjs-DNfXDPup.cjs +3104 -0
- package/dist/{index-BEA_pzeT.d.cts → index-B6cWNrMw.d.cts} +9 -1
- package/dist/{index-DNTLRsDl.d.mts → index-CJQLUKD9.d.mts} +9 -1
- package/dist/{index-DNixtE3h.d.mts → index-DT6lPMfS.d.cts} +85 -5
- package/dist/{index-D9dhxrRl.d.cts → index-F1U-fCTu.d.mts} +85 -5
- package/dist/index.cjs +6 -3
- package/dist/index.d.cts +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +4 -4
- package/dist/layered/index.cjs +3 -1
- package/dist/layered/index.d.cts +2 -2
- package/dist/layered/index.d.mts +2 -2
- package/dist/layered/index.mjs +2 -2
- package/dist/{layered-B2buJqMf.cjs → layered-5RxQhryb.cjs} +11184 -2932
- package/dist/{layered-DGFTeQSl.mjs → layered-DSesbctm.mjs} +12059 -3903
- package/dist/{layout-engine-Dn1PiFwP.cjs → layout-engine-CLFySz3R.cjs} +5 -18
- package/dist/{layout-engine-Cs5tatA6.mjs → layout-engine-GpBIX6ng.mjs} +3 -10
- package/dist/routing/index.cjs +2 -1
- package/dist/routing/index.d.cts +2 -2
- package/dist/routing/index.d.mts +2 -2
- package/dist/routing/index.mjs +2 -2
- package/dist/{routing-A2hrK9MP.mjs → routing-CR8flv8b.mjs} +977 -593
- package/dist/{routing-ujEP7Qh6.cjs → routing-QIdGGFBe.cjs} +986 -596
- package/package.json +8 -2
- package/src/authoring/coordinates.ts +28 -8
- package/src/authoring/partial.ts +7 -2
- package/src/authoring/routing.ts +4 -1
- package/src/constraints.ts +174 -6
- package/src/elkjs/index.ts +1476 -449
- package/src/elkjs/layout-quality.ts +267 -0
- package/src/elkjs/opposing-tracks.ts +161 -0
- package/src/elkjs/orthogonal-junctions.ts +174 -0
- package/src/elkjs/statechart.ts +9 -3
- package/src/elkjs/types.ts +2 -0
- package/src/index.ts +2 -0
- package/src/internal/elkjs-compatibility.ts +4 -1
- package/src/internal/layered-trace.ts +48 -0
- package/src/internal/layout-engine.ts +2 -1
- package/src/java-random.ts +6 -0
- package/src/layered/bk-node-placement.ts +403 -92
- package/src/layered/center-labels.ts +398 -0
- package/src/layered/compaction-bounds.ts +26 -0
- package/src/layered/compaction-errors.ts +2 -0
- package/src/layered/compaction-scanline.ts +63 -0
- package/src/layered/compound-boundaries.ts +99 -0
- package/src/layered/compound.ts +35 -7
- package/src/layered/constraint-orientation.ts +141 -0
- package/src/layered/crossing-constraints.ts +243 -0
- package/src/layered/crossing-counter.ts +233 -0
- package/src/layered/crossing-graph.ts +401 -0
- package/src/layered/cycle-random.ts +22 -0
- package/src/layered/elk-port-lists.ts +243 -0
- package/src/layered/end-labels.ts +124 -0
- package/src/layered/external-port-dummy.ts +137 -0
- package/src/layered/feedback-chains.ts +114 -0
- package/src/layered/fixed-self-loop.ts +150 -0
- package/src/layered/greedy-switch-decider.ts +356 -0
- package/src/layered/grouped-compaction.ts +787 -0
- package/src/layered/gwt-sort.ts +53 -0
- package/src/layered/hierarchical-port-phases.ts +242 -0
- package/src/layered/hierarchical-port-restoration.ts +233 -0
- package/src/layered/hierarchical-port-routing.ts +179 -0
- package/src/layered/hierarchy-crossing.ts +121 -0
- package/src/layered/hierarchy-sweepiness.ts +79 -0
- package/src/layered/hints.ts +371 -0
- package/src/layered/hyperedge-dummy-merger.ts +195 -0
- package/src/layered/index.ts +875 -161
- package/src/layered/initial-model-order.ts +404 -0
- package/src/layered/inverted-ports.ts +207 -0
- package/src/layered/label-dummy-switcher.ts +200 -0
- package/src/layered/long-edges.ts +135 -44
- package/src/layered/loop-envelopes.ts +184 -0
- package/src/layered/native-routing.ts +275 -0
- package/src/layered/network-simplex.ts +11 -15
- package/src/layered/node-margins.ts +59 -0
- package/src/layered/north-south-ports.ts +271 -0
- package/src/layered/opposing-tracks.ts +1044 -0
- package/src/layered/orthogonal-cycle-order.ts +106 -0
- package/src/layered/orthogonal-hypersegments.ts +154 -0
- package/src/layered/orthogonal-junctions.ts +202 -0
- package/src/layered/orthogonal-segments.ts +243 -0
- package/src/layered/placement-orientation.ts +22 -0
- package/src/layered/port-distributor.ts +192 -0
- package/src/layered/port-edge-order.ts +20 -0
- package/src/layered/replace-routing.ts +45 -0
- package/src/layered/routing-coordinates.ts +32 -0
- package/src/layered/separate-exterior-labels.ts +24 -6
- package/src/layered/spacing.ts +34 -12
- package/src/layered/splitter-order.ts +106 -0
- package/src/layered/strategies.ts +2124 -886
- package/src/layered/types.ts +13 -0
- package/src/layered/weighted-compaction.ts +84 -0
- package/src/routing/adapters.ts +32 -0
- package/src/routing/algorithms.ts +433 -21
- package/src/routing/coordination.ts +8 -3
- package/src/routing/index.ts +1 -0
- package/src/routing/search.ts +93 -11
- package/dist/elkjs-DaHMISnQ.mjs +0 -1677
- package/dist/elkjs-dMghiiWW.cjs +0 -1706
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { GraphEdge } from "@statelyai/graph";
|
|
2
|
+
import type { PortEdgeList } from "./elk-port-lists";
|
|
3
|
+
import type { LongEdgeExpansion } from "./long-edges";
|
|
4
|
+
import type { LayeredPhaseInput } from "./types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ELK's LongEdgeSplitter appends each dummy to the next layer while visiting
|
|
8
|
+
* the current layer's nodes, their ports and each port's outgoing edges. That
|
|
9
|
+
* insertion order is the initial crossing-minimization order, so seed it here.
|
|
10
|
+
*/
|
|
11
|
+
export function withLongEdgeSplitterOrder(
|
|
12
|
+
unsplit: LayeredPhaseInput,
|
|
13
|
+
expansion: LongEdgeExpansion,
|
|
14
|
+
baseOrder: readonly string[],
|
|
15
|
+
portLists: ReadonlyMap<string, readonly PortEdgeList[]>,
|
|
16
|
+
): LongEdgeExpansion {
|
|
17
|
+
const { input, orientation, assignment } = expansion;
|
|
18
|
+
const authoredIds = new Set(unsplit.graph.nodes.map((node) => node.id));
|
|
19
|
+
const dummyIds = new Set(
|
|
20
|
+
input.graph.nodes.map((node) => node.id).filter((id) => !authoredIds.has(id)),
|
|
21
|
+
);
|
|
22
|
+
if (dummyIds.size === 0) return expansion;
|
|
23
|
+
const layerOf = (id: string) => assignment.layerByNodeId.get(id) ?? 0;
|
|
24
|
+
const placed = new Set<string>();
|
|
25
|
+
const layers: string[][] = [];
|
|
26
|
+
for (const id of [...baseOrder, ...authoredIds]) {
|
|
27
|
+
if (placed.has(id) || !authoredIds.has(id)) continue;
|
|
28
|
+
placed.add(id);
|
|
29
|
+
(layers[layerOf(id)] ??= []).push(id);
|
|
30
|
+
}
|
|
31
|
+
const reversed = (edge: GraphEdge) => orientation.reversedEdgeIds.has(edge.id);
|
|
32
|
+
const outgoing = new Map<string, GraphEdge[]>();
|
|
33
|
+
for (const edge of input.graph.edges) {
|
|
34
|
+
const source = reversed(edge) ? edge.targetId : edge.sourceId;
|
|
35
|
+
if (edge.sourceId === edge.targetId) continue;
|
|
36
|
+
if (!outgoing.has(source)) outgoing.set(source, []);
|
|
37
|
+
outgoing.get(source)!.push(edge);
|
|
38
|
+
}
|
|
39
|
+
// Visit ports and their outgoing edges in ELK's simulated list order, which
|
|
40
|
+
// records reversal history. Expanded segment ids keep the original edge id.
|
|
41
|
+
const original = (id: string) => id.split("::")[0]!;
|
|
42
|
+
const targetsInPortOrder = (id: string): string[] => {
|
|
43
|
+
const edges = outgoing.get(id) ?? [];
|
|
44
|
+
const lists = portLists.get(id);
|
|
45
|
+
if (!lists) return edges.map((edge) => (reversed(edge) ? edge.sourceId : edge.targetId));
|
|
46
|
+
const rank = new Map<string, number>();
|
|
47
|
+
for (const port of lists) for (const edgeId of port.outgoing) rank.set(edgeId, rank.size);
|
|
48
|
+
return edges
|
|
49
|
+
.map((edge, index) => ({
|
|
50
|
+
edge,
|
|
51
|
+
index,
|
|
52
|
+
rank: rank.get(original(edge.id)) ?? rank.size + index,
|
|
53
|
+
}))
|
|
54
|
+
.sort((a, b) => a.rank - b.rank || a.index - b.index)
|
|
55
|
+
.map(({ edge }) => (reversed(edge) ? edge.sourceId : edge.targetId));
|
|
56
|
+
};
|
|
57
|
+
for (let index = 0; index < layers.length; index++) {
|
|
58
|
+
const layer = layers[index] ?? [];
|
|
59
|
+
for (let position = 0; position < layer.length; position++) {
|
|
60
|
+
for (const target of targetsInPortOrder(layer[position]!)) {
|
|
61
|
+
if (!dummyIds.has(target) || placed.has(target) || layerOf(target) !== index + 1) continue;
|
|
62
|
+
placed.add(target);
|
|
63
|
+
(layers[index + 1] ??= []).push(target);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const seedOrder = layers.flatMap((layer) => layer ?? []);
|
|
68
|
+
for (const id of dummyIds) if (!placed.has(id)) seedOrder.push(id);
|
|
69
|
+
return { ...expansion, assignment: { ...assignment, seedOrder } };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Each split re-targets a long edge through a new segment appended to the
|
|
74
|
+
* target port's incoming edges. Long edges therefore follow retained short
|
|
75
|
+
* edges, in the order of their final split.
|
|
76
|
+
*/
|
|
77
|
+
export function incomingAfterLongEdgeSplitting(
|
|
78
|
+
lists: ReadonlyMap<string, readonly PortEdgeList[]>,
|
|
79
|
+
expansion: LongEdgeExpansion,
|
|
80
|
+
): Map<string, PortEdgeList[]> {
|
|
81
|
+
const { input, orientation, assignment } = expansion;
|
|
82
|
+
const seed = new Map((assignment.seedOrder ?? []).map((id, index) => [id, index]));
|
|
83
|
+
const listed = (id: string) => id.replace(/::segment:\d+$/, "");
|
|
84
|
+
const finalSplit = new Map<string, number>();
|
|
85
|
+
for (const edge of input.graph.edges) {
|
|
86
|
+
const reversed = orientation.reversedEdgeIds.has(edge.id);
|
|
87
|
+
const [source, target] = reversed
|
|
88
|
+
? [edge.targetId, edge.sourceId]
|
|
89
|
+
: [edge.sourceId, edge.targetId];
|
|
90
|
+
const at = seed.get(source);
|
|
91
|
+
if (source.startsWith("__layout_dummy:") && at !== undefined)
|
|
92
|
+
finalSplit.set(`${target}\0${listed(edge.id)}`, at);
|
|
93
|
+
}
|
|
94
|
+
return new Map(
|
|
95
|
+
[...lists].map(([id, ports]) => [
|
|
96
|
+
id,
|
|
97
|
+
ports.map((port) => ({
|
|
98
|
+
...port,
|
|
99
|
+
incoming: port.incoming
|
|
100
|
+
.map((edgeId, index) => ({ edgeId, index, split: finalSplit.get(`${id}\0${edgeId}`) }))
|
|
101
|
+
.sort((a, b) => (a.split ?? -1) - (b.split ?? -1) || a.index - b.index)
|
|
102
|
+
.map(({ edgeId }) => edgeId),
|
|
103
|
+
})),
|
|
104
|
+
]),
|
|
105
|
+
);
|
|
106
|
+
}
|