@statelyai/layout 0.2.0 → 0.3.0
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 +71 -2
- package/dist/elkjs/index.cjs +3 -2
- package/dist/elkjs/index.d.cts +11 -2
- package/dist/elkjs/index.d.mts +11 -2
- package/dist/elkjs/index.mjs +3 -3
- package/dist/elkjs/main.cjs +2 -2
- package/dist/elkjs/main.d.cts +2 -2
- package/dist/elkjs/main.d.mts +2 -2
- package/dist/elkjs/main.mjs +2 -2
- package/dist/elkjs/worker-api.cjs +1 -1
- package/dist/elkjs/worker-api.d.cts +2 -2
- package/dist/elkjs/worker-api.d.mts +2 -2
- package/dist/elkjs/worker-api.mjs +1 -1
- package/dist/elkjs/worker.cjs +2 -2
- package/dist/elkjs/worker.mjs +2 -2
- package/dist/{elkjs-CFlfkEBz.mjs → elkjs-DaHMISnQ.mjs} +152 -3
- package/dist/{elkjs-BVnDwnBQ.cjs → elkjs-dMghiiWW.cjs} +157 -2
- package/dist/index-BEA_pzeT.d.cts +72 -0
- package/dist/{index-BSH2bloA.d.mts → index-D9dhxrRl.d.cts} +35 -3
- package/dist/index-DNTLRsDl.d.mts +72 -0
- package/dist/{index-CG712jzV.d.cts → index-DNixtE3h.d.mts} +35 -3
- package/dist/index.cjs +36 -9
- package/dist/index.d.cts +4 -3
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +4 -3
- package/dist/layered/index.cjs +1 -1
- package/dist/layered/index.d.cts +3 -2
- package/dist/layered/index.d.mts +3 -2
- package/dist/layered/index.mjs +1 -1
- package/dist/{layered-Cf9Rn8l7.cjs → layered-B2buJqMf.cjs} +417 -469
- package/dist/{layered-Bz2nWmVp.mjs → layered-DGFTeQSl.mjs} +391 -431
- package/dist/{layout-engine-WbC0YjjT.mjs → layout-engine-Cs5tatA6.mjs} +1 -1
- package/dist/{layout-engine-BNex9jxg.cjs → layout-engine-Dn1PiFwP.cjs} +1 -1
- package/dist/routing/index.cjs +28 -0
- package/dist/routing/index.d.cts +3 -0
- package/dist/routing/index.d.mts +3 -0
- package/dist/routing/index.mjs +3 -0
- package/dist/routing-A2hrK9MP.mjs +2396 -0
- package/dist/routing-ujEP7Qh6.cjs +2606 -0
- package/dist/types-CT2akdK0.d.mts +133 -0
- package/dist/types-uxPlGRof.d.cts +133 -0
- package/dist/{worker-client-DAT_z2OR.d.mts → worker-client-BwGGRD5y.d.mts} +1 -1
- package/dist/{worker-client-DVhklF6A.d.cts → worker-client-D4zNyPEI.d.cts} +1 -1
- package/package.json +7 -2
- package/src/authoring/coordinates.ts +10 -2
- package/src/elkjs/index.ts +5 -3
- package/src/elkjs/routes.ts +147 -0
- package/src/index.ts +6 -0
- package/src/layered/compound.ts +417 -0
- package/src/layered/index.ts +27 -102
- package/src/layered/types.ts +21 -0
- package/src/routing/adapters.ts +111 -0
- package/src/routing/algorithms.ts +676 -0
- package/src/routing/coordination.ts +86 -0
- package/src/routing/engine.ts +473 -0
- package/src/routing/index.ts +68 -0
- package/src/routing/layout-cache.ts +65 -0
- package/src/routing/model.ts +218 -0
- package/src/routing/path.ts +324 -0
- package/src/routing/persistent.ts +117 -0
- package/src/routing/search.ts +276 -0
- package/src/routing/spatial.ts +78 -0
- package/src/routing/types.ts +146 -0
- /package/dist/{public-types-B8rAALJq.d.mts → public-types-9NCPoqbp.d.mts} +0 -0
- /package/dist/{public-types-CVB0hXGk.d.cts → public-types-BcRZoJE3.d.cts} +0 -0
- /package/dist/{worker-client-DkwS4sHx.mjs → worker-client-BNjwT1AJ.mjs} +0 -0
- /package/dist/{worker-client-D3HMK33r.cjs → worker-client-D4oEvhAl.cjs} +0 -0
|
@@ -1,21 +1,6 @@
|
|
|
1
|
+
import { F as UnsupportedLayoutError, M as routeCrosses, N as routeOrthogonal, P as LayoutError, _ as routeToPolylines, b as recordRouteGeometry, c as orthogonalRouting, j as overlaps, v as nativeGeometry, y as worldGeometry } from "./routing-A2hrK9MP.mjs";
|
|
1
2
|
import { getNodeSize } from "@statelyai/graph/layout";
|
|
2
3
|
|
|
3
|
-
//#region src/errors.ts
|
|
4
|
-
var LayoutError = class extends Error {
|
|
5
|
-
constructor(message, code) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.code = code;
|
|
8
|
-
this.name = "LayoutError";
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
var UnsupportedLayoutError = class extends LayoutError {
|
|
12
|
-
constructor(message) {
|
|
13
|
-
super(message, "UNSUPPORTED_LAYOUT");
|
|
14
|
-
this.name = "UnsupportedLayoutError";
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
//#endregion
|
|
19
4
|
//#region src/java-random.ts
|
|
20
5
|
/** Java's 48-bit `java.util.Random`, used by ELK for seeded tie-breaking. */
|
|
21
6
|
var JavaRandom = class JavaRandom {
|
|
@@ -2229,15 +2214,15 @@ function implicitEdgeEndpoints(input, placement, orientation) {
|
|
|
2229
2214
|
const hypernode = nodeSettings?.hypernode;
|
|
2230
2215
|
const unzipping = (input.settings["layerUnzipping.strategy"] ?? "NONE") === "ALTERNATING";
|
|
2231
2216
|
const crossingStrategy = input.settings["crossingMinimization.strategy"] ?? "LAYER_SWEEP";
|
|
2232
|
-
const interactiveTargetOrder = crossingStrategy === "INTERACTIVE" && entries.every(({ endpoint
|
|
2233
|
-
const verticalNoneTargetOrder = !horizontal && crossingStrategy === "NONE" && input.settings["layering.strategy"] === "STRETCH_WIDTH" && entries.every(({ endpoint
|
|
2234
|
-
const interactiveLeftSourceOrder = crossingStrategy === "INTERACTIVE" && input.direction === "left" && entries.length >= 3 && entries.every(({ endpoint
|
|
2217
|
+
const interactiveTargetOrder = crossingStrategy === "INTERACTIVE" && entries.every(({ endpoint }) => endpoint === "target") && entries.some(({ edge }) => edge.sourceId.startsWith("__layout_dummy:"));
|
|
2218
|
+
const verticalNoneTargetOrder = !horizontal && crossingStrategy === "NONE" && input.settings["layering.strategy"] === "STRETCH_WIDTH" && entries.every(({ endpoint }) => endpoint === "target") && !input.graph.edges.some(({ sourceId }) => sourceId === nodeId) && entries.some(({ edge }) => edge.sourceId.startsWith("__layout_dummy:"));
|
|
2219
|
+
const interactiveLeftSourceOrder = crossingStrategy === "INTERACTIVE" && input.direction === "left" && entries.length >= 3 && entries.every(({ endpoint }) => endpoint === "source") ? [...entries].sort((left, right) => {
|
|
2235
2220
|
const leftRect = placement.rectByNodeId.get(left.edge.targetId);
|
|
2236
2221
|
const rightRect = placement.rectByNodeId.get(right.edge.targetId);
|
|
2237
2222
|
const leftCross = (leftRect?.y ?? 0) + (leftRect?.height ?? 0) / 2;
|
|
2238
2223
|
return (rightRect?.y ?? 0) + (rightRect?.height ?? 0) / 2 - leftCross || (edgeModelOrder.get(left.edge.id) ?? 0) - (edgeModelOrder.get(right.edge.id) ?? 0);
|
|
2239
2224
|
}).map(({ edge }) => edge.id) : void 0;
|
|
2240
|
-
const interactiveForwardLongEdgeSource = input.direction !== "left" && crossingStrategy === "INTERACTIVE" && entries.every(({ endpoint
|
|
2225
|
+
const interactiveForwardLongEdgeSource = input.direction !== "left" && crossingStrategy === "INTERACTIVE" && entries.every(({ endpoint }) => endpoint === "source") && entries.some(({ edge }) => edge.targetId.startsWith("__layout_dummy:"));
|
|
2241
2226
|
const layerOrdered = unzipping || interactiveTargetOrder || verticalNoneTargetOrder || input.settings.hierarchyHandling !== "INCLUDE_CHILDREN" && (crossingStrategy === "LAYER_SWEEP" || crossingStrategy === "MEDIAN_LAYER_SWEEP");
|
|
2242
2227
|
entries.sort((left, right) => {
|
|
2243
2228
|
if (interactiveLeftSourceOrder) {
|
|
@@ -2290,8 +2275,8 @@ function implicitEdgeEndpoints(input, placement, orientation) {
|
|
|
2290
2275
|
const constraints = nodeSettings?.portConstraints;
|
|
2291
2276
|
const portSpacing = Number(input.settings["spacing.portPort"] ?? 10);
|
|
2292
2277
|
const fixedWrapAnchor = nodeId.startsWith("__layout_dummy:wrap:");
|
|
2293
|
-
entries.forEach(({ edge, endpoint
|
|
2294
|
-
const ordinal = endpoint
|
|
2278
|
+
entries.forEach(({ edge, endpoint }, index) => {
|
|
2279
|
+
const ordinal = endpoint === "target" && input.settings["crossingMinimization.strategy"] !== "INTERACTIVE" && !layerOrdered && !(input.settings.directionCongruency === "ROTATION" && (input.direction === "down" || input.direction === "left")) ? entries.length - index : index + 1;
|
|
2295
2280
|
const orderedIndex = ordinal - 1;
|
|
2296
2281
|
const crossPosition = mergeEdges || hypernode === true ? axisSize / 2 : constraints === "FIXED_RATIO" || constraints === "FIXED_POS" ? 0 : alignment === "BEGIN" ? axisStart + orderedIndex * portSpacing : alignment === "END" ? axisStart + availableAxisSize - (entries.length - orderedIndex - 1) * portSpacing : alignment === "CENTER" ? axisStart + (availableAxisSize - (entries.length - 1) * portSpacing) / 2 + orderedIndex * portSpacing : alignment === "JUSTIFIED" ? entries.length === 1 ? axisStart + availableAxisSize / 2 : axisStart + orderedIndex * availableAxisSize / (entries.length - 1) : axisStart + availableAxisSize * ordinal / (entries.length + 1);
|
|
2297
2282
|
let point = horizontal ? {
|
|
@@ -2312,7 +2297,7 @@ function implicitEdgeEndpoints(input, placement, orientation) {
|
|
|
2312
2297
|
source: point,
|
|
2313
2298
|
target: point
|
|
2314
2299
|
};
|
|
2315
|
-
pair[endpoint
|
|
2300
|
+
pair[endpoint] = point;
|
|
2316
2301
|
result.set(edge.id, pair);
|
|
2317
2302
|
});
|
|
2318
2303
|
}
|
|
@@ -2497,12 +2482,12 @@ function routeEdges(style) {
|
|
|
2497
2482
|
flowLayerByNodeId.set(interval.id, flowLayers.length - 1);
|
|
2498
2483
|
}
|
|
2499
2484
|
const increasing = input.direction === "right" || input.direction === "down";
|
|
2500
|
-
const flowPortProtrusion = (edge, endpoint
|
|
2501
|
-
const node = nodeById.get(endpoint
|
|
2502
|
-
const portName = endpoint
|
|
2485
|
+
const flowPortProtrusion = (edge, endpoint) => {
|
|
2486
|
+
const node = nodeById.get(endpoint === "source" ? edge.sourceId : edge.targetId);
|
|
2487
|
+
const portName = endpoint === "source" ? edge.sourcePort : edge.targetPort;
|
|
2503
2488
|
const port = node?.ports?.find((candidate) => candidate.name === portName);
|
|
2504
2489
|
if (!node || !port) return 0;
|
|
2505
|
-
return input.portSettings?.(port, node)?.["port.side"] === (horizontal ? endpoint
|
|
2490
|
+
return input.portSettings?.(port, node)?.["port.side"] === (horizontal ? endpoint === "source" ? input.direction === "right" ? "EAST" : "WEST" : input.direction === "right" ? "WEST" : "EAST" : endpoint === "source" ? input.direction === "down" ? "SOUTH" : "NORTH" : input.direction === "down" ? "NORTH" : "SOUTH") ? horizontal ? port.width ?? 0 : port.height ?? 0 : 0;
|
|
2506
2491
|
};
|
|
2507
2492
|
const adjacentNodeIds = /* @__PURE__ */ new Map();
|
|
2508
2493
|
for (const edge of input.graph.edges) {
|
|
@@ -3821,108 +3806,355 @@ function getPolylineMidpoint(points) {
|
|
|
3821
3806
|
}
|
|
3822
3807
|
|
|
3823
3808
|
//#endregion
|
|
3824
|
-
//#region src/
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
y: 0
|
|
3840
|
-
};
|
|
3841
|
-
visiting.delete(id);
|
|
3842
|
-
offsets.set(id, origin);
|
|
3843
|
-
return origin;
|
|
3844
|
-
}
|
|
3845
|
-
const parent = nodes.get(parentId);
|
|
3846
|
-
const ancestor = offset(parentId);
|
|
3847
|
-
const result = {
|
|
3848
|
-
x: ancestor.x + parent.x,
|
|
3849
|
-
y: ancestor.y + parent.y
|
|
3809
|
+
//#region src/layered/compound.ts
|
|
3810
|
+
const paddingOf = (value) => ({
|
|
3811
|
+
top: typeof value === "number" ? value : value?.top ?? 12,
|
|
3812
|
+
right: typeof value === "number" ? value : value?.right ?? 12,
|
|
3813
|
+
bottom: typeof value === "number" ? value : value?.bottom ?? 12,
|
|
3814
|
+
left: typeof value === "number" ? value : value?.left ?? 12
|
|
3815
|
+
});
|
|
3816
|
+
/** Solve each scope's occupied envelope bottom-up; never repair returned sibling positions. */
|
|
3817
|
+
function layoutCompounds(graph, options, flat) {
|
|
3818
|
+
const nodes = graph.nodes.map((node) => {
|
|
3819
|
+
const size = options.measure?.(node);
|
|
3820
|
+
return {
|
|
3821
|
+
...node,
|
|
3822
|
+
width: size?.width ?? node.width ?? 0,
|
|
3823
|
+
height: size?.height ?? node.height ?? 0
|
|
3850
3824
|
};
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3825
|
+
});
|
|
3826
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
3827
|
+
const ancestry = (id) => {
|
|
3828
|
+
const chain = [];
|
|
3829
|
+
let current = id;
|
|
3830
|
+
while (current != null) {
|
|
3831
|
+
if (chain.includes(current)) throw new Error(`Cyclic compound hierarchy at ${current}`);
|
|
3832
|
+
chain.push(current);
|
|
3833
|
+
current = byId.get(current)?.parentId ?? null;
|
|
3834
|
+
}
|
|
3835
|
+
return [...chain, null];
|
|
3836
|
+
};
|
|
3837
|
+
const branch = (id, scope) => {
|
|
3838
|
+
const chain = ancestry(id);
|
|
3839
|
+
return chain[chain.indexOf(scope) - 1];
|
|
3840
|
+
};
|
|
3841
|
+
const owner = (edge) => {
|
|
3842
|
+
const a = ancestry(edge.sourceId), b = ancestry(edge.targetId);
|
|
3843
|
+
if (edge.sourceId !== edge.targetId && b.includes(edge.sourceId)) return edge.sourceId;
|
|
3844
|
+
if (edge.sourceId !== edge.targetId && a.includes(edge.targetId)) return edge.targetId;
|
|
3845
|
+
return a.slice(1).find((id) => b.slice(1).includes(id)) ?? null;
|
|
3846
|
+
};
|
|
3847
|
+
const owned = /* @__PURE__ */ new Map();
|
|
3848
|
+
for (const edge of graph.edges) {
|
|
3849
|
+
const scope = owner(edge);
|
|
3850
|
+
owned.set(scope, [...owned.get(scope) ?? [], edge]);
|
|
3854
3851
|
}
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3852
|
+
const edges = new Map(graph.edges.map((e) => [e.id, {
|
|
3853
|
+
...e,
|
|
3854
|
+
x: 0,
|
|
3855
|
+
y: 0,
|
|
3856
|
+
width: e.width ?? 0,
|
|
3857
|
+
height: e.height ?? 0
|
|
3858
|
+
}]));
|
|
3859
|
+
const localLabels = /* @__PURE__ */ new Map();
|
|
3860
|
+
const compoundGeometry = /* @__PURE__ */ new Map();
|
|
3861
|
+
const childScopes = [...new Set(nodes.flatMap((n) => n.parentId == null ? [] : [n.parentId]))].sort((a, b) => ancestry(b).length - ancestry(a).length);
|
|
3862
|
+
const solve = (scope) => {
|
|
3863
|
+
const children = nodes.filter((n) => (n.parentId ?? null) === scope);
|
|
3864
|
+
if (!children.length) return;
|
|
3865
|
+
const container = scope == null ? void 0 : byId.get(scope);
|
|
3866
|
+
const config = container && options.compound?.(container);
|
|
3867
|
+
const direction = config?.direction ?? options.direction ?? graph.direction ?? "right";
|
|
3868
|
+
const horizontal = direction === "left" || direction === "right";
|
|
3869
|
+
const padding = paddingOf(config?.padding ?? options.padding);
|
|
3870
|
+
const insets = { ...padding };
|
|
3871
|
+
const header = config?.header;
|
|
3872
|
+
const side = header?.side ?? "top";
|
|
3873
|
+
if (header) insets[side] += side === "top" || side === "bottom" ? header.height : header.width;
|
|
3874
|
+
const scopeEdges = owned.get(scope) ?? [];
|
|
3875
|
+
const boundary = /* @__PURE__ */ new Map();
|
|
3876
|
+
const projected = [];
|
|
3877
|
+
for (const edge of scopeEdges) if (edge.sourceId === scope || edge.targetId === scope) {
|
|
3878
|
+
const id = branch(edge.sourceId === scope ? edge.targetId : edge.sourceId, scope);
|
|
3879
|
+
boundary.set(id, [...boundary.get(id) ?? [], edge]);
|
|
3880
|
+
} else projected.push({
|
|
3881
|
+
...edge,
|
|
3882
|
+
sourceId: branch(edge.sourceId, scope),
|
|
3883
|
+
targetId: branch(edge.targetId, scope),
|
|
3884
|
+
points: void 0
|
|
3885
|
+
});
|
|
3886
|
+
const gap = options.settings?.["spacing.labelNode"] ?? options.spacing?.node ?? 20;
|
|
3887
|
+
const labelGap = options.settings?.["spacing.labelLabel"] ?? 8;
|
|
3888
|
+
const sizes = new Map(children.map((n) => {
|
|
3889
|
+
const labels$1 = boundary.get(n.id) ?? [];
|
|
3890
|
+
const labelWidth = Math.max(0, ...labels$1.map((e) => e.width ?? 0));
|
|
3891
|
+
const labelHeight = labels$1.reduce((sum, e) => sum + (e.height ?? 0), 0) + Math.max(0, labels$1.length - 1) * labelGap;
|
|
3892
|
+
return [n.id, {
|
|
3893
|
+
width: labels$1.length && !horizontal ? n.width + gap + labelWidth : Math.max(n.width, labelWidth),
|
|
3894
|
+
height: labels$1.length && horizontal ? n.height + gap + labelHeight : Math.max(n.height, labelHeight)
|
|
3864
3895
|
}];
|
|
3865
|
-
}))
|
|
3896
|
+
}));
|
|
3897
|
+
const labels = projected.filter((edge) => (edge.width ?? 0) > 0 && (edge.height ?? 0) > 0);
|
|
3898
|
+
const used = new Set([...graph.nodes, ...graph.edges].map((entity) => entity.id));
|
|
3899
|
+
const labelIds = /* @__PURE__ */ new Map();
|
|
3900
|
+
for (const edge of labels) {
|
|
3901
|
+
let id = `__layout_label_${edge.id}`;
|
|
3902
|
+
while ([
|
|
3903
|
+
id,
|
|
3904
|
+
`${id}_source`,
|
|
3905
|
+
`${id}_target`
|
|
3906
|
+
].some((key) => used.has(key))) id += "_";
|
|
3907
|
+
for (const key of [
|
|
3908
|
+
id,
|
|
3909
|
+
`${id}_source`,
|
|
3910
|
+
`${id}_target`
|
|
3911
|
+
]) used.add(key);
|
|
3912
|
+
labelIds.set(edge.id, id);
|
|
3913
|
+
sizes.set(id, {
|
|
3914
|
+
width: edge.width,
|
|
3915
|
+
height: edge.height
|
|
3916
|
+
});
|
|
3917
|
+
}
|
|
3918
|
+
const originalEdges = new Map(graph.edges.map((edge) => [edge.id, edge]));
|
|
3919
|
+
for (const edge of labels) {
|
|
3920
|
+
originalEdges.set(`${labelIds.get(edge.id)}_source`, edges.get(edge.id));
|
|
3921
|
+
originalEdges.set(`${labelIds.get(edge.id)}_target`, edges.get(edge.id));
|
|
3922
|
+
}
|
|
3923
|
+
const placementOrder = new Map(children.map((node, index) => [node.id, index]));
|
|
3924
|
+
for (const edge of labels) placementOrder.set(labelIds.get(edge.id), ((placementOrder.get(edge.sourceId) ?? 0) + (placementOrder.get(edge.targetId) ?? 0)) / 2 + (edge.sourceId === edge.targetId ? .25 : 0));
|
|
3925
|
+
const result$1 = flat({
|
|
3926
|
+
...graph,
|
|
3927
|
+
nodes: [...children.map((n) => ({
|
|
3928
|
+
...n,
|
|
3929
|
+
parentId: null
|
|
3930
|
+
})), ...labels.map((edge) => ({
|
|
3931
|
+
type: "node",
|
|
3932
|
+
id: labelIds.get(edge.id),
|
|
3933
|
+
data: void 0
|
|
3934
|
+
}))].sort((a, b) => placementOrder.get(a.id) - placementOrder.get(b.id)),
|
|
3935
|
+
edges: projected.flatMap((edge) => {
|
|
3936
|
+
const labelId = labelIds.get(edge.id);
|
|
3937
|
+
return labelId ? [{
|
|
3938
|
+
...edge,
|
|
3939
|
+
id: `${labelId}_source`,
|
|
3940
|
+
targetId: labelId,
|
|
3941
|
+
targetPort: void 0,
|
|
3942
|
+
width: 0,
|
|
3943
|
+
height: 0
|
|
3944
|
+
}, {
|
|
3945
|
+
...edge,
|
|
3946
|
+
id: `${labelId}_target`,
|
|
3947
|
+
sourceId: labelId,
|
|
3948
|
+
sourcePort: void 0,
|
|
3949
|
+
width: 0,
|
|
3950
|
+
height: 0
|
|
3951
|
+
}] : [edge];
|
|
3952
|
+
})
|
|
3953
|
+
}, {
|
|
3954
|
+
...options,
|
|
3955
|
+
compound: void 0,
|
|
3956
|
+
direction,
|
|
3957
|
+
padding: insets,
|
|
3958
|
+
measure: (node) => sizes.get(node.id),
|
|
3959
|
+
nodeSettings: (node) => {
|
|
3960
|
+
const original = byId.get(node.id);
|
|
3961
|
+
return original ? options.nodeSettings?.(original) : void 0;
|
|
3962
|
+
},
|
|
3963
|
+
edgeSettings: (edge) => options.edgeSettings?.(originalEdges.get(edge.id) ?? edge)
|
|
3964
|
+
});
|
|
3965
|
+
for (const placed of result$1.nodes) {
|
|
3966
|
+
const node = byId.get(placed.id);
|
|
3967
|
+
if (node) Object.assign(node, {
|
|
3968
|
+
x: placed.x,
|
|
3969
|
+
y: placed.y,
|
|
3970
|
+
ports: placed.ports
|
|
3971
|
+
});
|
|
3972
|
+
}
|
|
3973
|
+
for (const edge of projected) {
|
|
3974
|
+
const labelId = labelIds.get(edge.id);
|
|
3975
|
+
if (labelId) {
|
|
3976
|
+
const label = result$1.nodes.find((n) => n.id === labelId);
|
|
3977
|
+
localLabels.set(edge.id, {
|
|
3978
|
+
scope,
|
|
3979
|
+
rect: {
|
|
3980
|
+
x: label.x,
|
|
3981
|
+
y: label.y,
|
|
3982
|
+
width: label.width,
|
|
3983
|
+
height: label.height
|
|
3984
|
+
}
|
|
3985
|
+
});
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
for (const [id, labels$1] of boundary) {
|
|
3989
|
+
const node = byId.get(id);
|
|
3990
|
+
const totalHeight = labels$1.reduce((sum, e) => sum + (e.height ?? 0), 0) + Math.max(0, labels$1.length - 1) * labelGap;
|
|
3991
|
+
let cross = horizontal ? 0 : (Math.max(node.height, totalHeight) - totalHeight) / 2;
|
|
3992
|
+
for (const edge of labels$1) {
|
|
3993
|
+
const width = edge.width ?? 0, height = edge.height ?? 0;
|
|
3994
|
+
localLabels.set(edge.id, {
|
|
3995
|
+
scope,
|
|
3996
|
+
rect: {
|
|
3997
|
+
x: node.x + (horizontal ? (Math.max(node.width, width) - width) / 2 : node.width + gap),
|
|
3998
|
+
y: node.y + (horizontal ? node.height + gap : 0) + cross,
|
|
3999
|
+
width,
|
|
4000
|
+
height
|
|
4001
|
+
}
|
|
4002
|
+
});
|
|
4003
|
+
cross += height + labelGap;
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
const boxes = [...children.map((n) => ({
|
|
4007
|
+
x: n.x,
|
|
4008
|
+
y: n.y,
|
|
4009
|
+
...sizes.get(n.id)
|
|
4010
|
+
})), ...[...localLabels.values()].filter((l) => l.scope === scope).map((l) => l.rect)];
|
|
4011
|
+
let dx = Math.max(0, insets.left - Math.min(...boxes.map((b) => b.x)));
|
|
4012
|
+
let dy = Math.max(0, insets.top - Math.min(...boxes.map((b) => b.y)));
|
|
4013
|
+
if (header && (side === "top" || side === "bottom")) {
|
|
4014
|
+
const occupiedWidth = Math.max(...boxes.map((b) => b.x + b.width)) - Math.min(...boxes.map((b) => b.x)) + padding.left + padding.right;
|
|
4015
|
+
dx += Math.max(0, (header.width - occupiedWidth) / 2);
|
|
4016
|
+
}
|
|
4017
|
+
if (header && (side === "left" || side === "right")) {
|
|
4018
|
+
const occupiedHeight = Math.max(...boxes.map((b) => b.y + b.height)) - Math.min(...boxes.map((b) => b.y)) + padding.top + padding.bottom;
|
|
4019
|
+
dy += Math.max(0, (header.height - occupiedHeight) / 2);
|
|
4020
|
+
}
|
|
4021
|
+
const right = Math.max(...boxes.map((b) => b.x + dx + b.width));
|
|
4022
|
+
const bottom = Math.max(...boxes.map((b) => b.y + dy + b.height));
|
|
4023
|
+
for (const n of children) {
|
|
4024
|
+
n.x += dx;
|
|
4025
|
+
n.y += dy;
|
|
4026
|
+
}
|
|
4027
|
+
for (const l of localLabels.values()) if (l.scope === scope) {
|
|
4028
|
+
l.rect.x += dx;
|
|
4029
|
+
l.rect.y += dy;
|
|
4030
|
+
}
|
|
4031
|
+
if (container) {
|
|
4032
|
+
const width = Math.max(header?.width ?? 0, config?.minContentSize?.width ? config.minContentSize.width + insets.left + insets.right : 0, right + insets.right);
|
|
4033
|
+
const height = Math.max(header?.height ?? 0, config?.minContentSize?.height ? config.minContentSize.height + insets.top + insets.bottom : 0, bottom + insets.bottom);
|
|
4034
|
+
container.width = Math.max(container.width, width);
|
|
4035
|
+
container.height = Math.max(container.height, height);
|
|
4036
|
+
compoundGeometry.set(container.id, {
|
|
4037
|
+
bounds: {
|
|
4038
|
+
x: 0,
|
|
4039
|
+
y: 0,
|
|
4040
|
+
width: container.width,
|
|
4041
|
+
height: container.height
|
|
4042
|
+
},
|
|
4043
|
+
content: {
|
|
4044
|
+
x: insets.left,
|
|
4045
|
+
y: insets.top,
|
|
4046
|
+
width: container.width - insets.left - insets.right,
|
|
4047
|
+
height: container.height - insets.top - insets.bottom
|
|
4048
|
+
},
|
|
4049
|
+
...header ? { header: {
|
|
4050
|
+
x: side === "right" ? container.width - header.width : 0,
|
|
4051
|
+
y: side === "bottom" ? container.height - header.height : 0,
|
|
4052
|
+
width: side === "top" || side === "bottom" ? container.width : header.width,
|
|
4053
|
+
height: side === "left" || side === "right" ? container.height : header.height
|
|
4054
|
+
} } : {}
|
|
4055
|
+
});
|
|
4056
|
+
}
|
|
3866
4057
|
};
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
4058
|
+
for (const scope of childScopes) solve(scope);
|
|
4059
|
+
const roots = nodes.filter((n) => n.parentId == null);
|
|
4060
|
+
if (roots.length === 1 && compoundGeometry.has(roots[0].id) && !owned.get(null)?.length) Object.assign(roots[0], {
|
|
4061
|
+
x: 0,
|
|
4062
|
+
y: 0
|
|
4063
|
+
});
|
|
4064
|
+
else solve(null);
|
|
4065
|
+
const origin = (id) => {
|
|
4066
|
+
if (id == null) return {
|
|
4067
|
+
x: 0,
|
|
4068
|
+
y: 0
|
|
4069
|
+
};
|
|
4070
|
+
return ancestry(id).slice(0, -1).reduce((p, key) => ({
|
|
4071
|
+
x: p.x + byId.get(key).x,
|
|
4072
|
+
y: p.y + byId.get(key).y
|
|
4073
|
+
}), {
|
|
4074
|
+
x: 0,
|
|
4075
|
+
y: 0
|
|
4076
|
+
});
|
|
4077
|
+
};
|
|
4078
|
+
for (const [id, label] of localLabels) {
|
|
4079
|
+
const offset = origin(label.scope);
|
|
4080
|
+
Object.assign(edges.get(id), {
|
|
4081
|
+
...label.rect,
|
|
4082
|
+
x: label.rect.x + offset.x,
|
|
4083
|
+
y: label.rect.y + offset.y
|
|
4084
|
+
});
|
|
4085
|
+
}
|
|
4086
|
+
const attachmentSettings = {};
|
|
4087
|
+
for (const edge of edges.values()) {
|
|
4088
|
+
const sourceGeometry = compoundGeometry.get(edge.sourceId), targetGeometry = compoundGeometry.get(edge.targetId);
|
|
4089
|
+
const intent = options.edgeAttachment?.(edge);
|
|
4090
|
+
attachmentSettings[edge.id] = {
|
|
4091
|
+
...sourceGeometry && edge.sourceId !== edge.targetId && ancestry(edge.targetId).includes(edge.sourceId) ? { sourceAttachment: {
|
|
4092
|
+
bounds: intent?.source === "outer" ? {
|
|
4093
|
+
x: 0,
|
|
4094
|
+
y: 0,
|
|
4095
|
+
width: sourceGeometry.bounds.width,
|
|
4096
|
+
height: sourceGeometry.bounds.height
|
|
4097
|
+
} : sourceGeometry.content,
|
|
4098
|
+
facing: intent?.source === "outer" ? "outward" : "inward"
|
|
4099
|
+
} } : {},
|
|
4100
|
+
...targetGeometry && intent?.target === "content" ? { targetAttachment: {
|
|
4101
|
+
bounds: targetGeometry.content,
|
|
4102
|
+
facing: "inward"
|
|
4103
|
+
} } : {}
|
|
4104
|
+
};
|
|
4105
|
+
}
|
|
4106
|
+
const worldNodes = nodes.map((n) => ({
|
|
4107
|
+
...n,
|
|
4108
|
+
...origin(n.id)
|
|
4109
|
+
}));
|
|
4110
|
+
const usedIds = new Set(nodes.map((n) => n.id));
|
|
4111
|
+
const headers = [...compoundGeometry].flatMap(([id, geometry]) => {
|
|
4112
|
+
if (!geometry.header) return [];
|
|
4113
|
+
const p = origin(id);
|
|
4114
|
+
let key = `__layout_header_${id}`;
|
|
4115
|
+
while (usedIds.has(key)) key += "_";
|
|
4116
|
+
usedIds.add(key);
|
|
4117
|
+
return [{
|
|
4118
|
+
type: "node",
|
|
4119
|
+
id: key,
|
|
4120
|
+
parentId: id,
|
|
4121
|
+
data: void 0,
|
|
4122
|
+
...geometry.header,
|
|
4123
|
+
x: p.x + geometry.header.x,
|
|
4124
|
+
y: p.y + geometry.header.y
|
|
4125
|
+
}];
|
|
4126
|
+
});
|
|
4127
|
+
const snapshot = orthogonalRouting.route({
|
|
3871
4128
|
...graph,
|
|
3872
|
-
nodes:
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
})) } : {}
|
|
3890
|
-
};
|
|
3891
|
-
})
|
|
4129
|
+
nodes: [...worldNodes, ...headers],
|
|
4130
|
+
edges: [...edges.values()]
|
|
4131
|
+
}, {
|
|
4132
|
+
coordinateSpace: "world",
|
|
4133
|
+
edges: attachmentSettings
|
|
4134
|
+
});
|
|
4135
|
+
for (const [id, route] of snapshot.routes) {
|
|
4136
|
+
const polylines = routeToPolylines(route);
|
|
4137
|
+
Object.assign(edges.get(id), {
|
|
4138
|
+
points: polylines.flatMap((p) => p.map((point) => ({ ...point }))),
|
|
4139
|
+
routing: "orthogonal"
|
|
4140
|
+
});
|
|
4141
|
+
}
|
|
4142
|
+
for (const [id, geometry] of compoundGeometry) geometry.bounds = {
|
|
4143
|
+
...geometry.bounds,
|
|
4144
|
+
x: byId.get(id).x,
|
|
4145
|
+
y: byId.get(id).y
|
|
3892
4146
|
};
|
|
3893
|
-
|
|
3894
|
-
/** Ancestor frames stay fixed during authoring. Preserve untouched coordinates exactly. */
|
|
3895
|
-
function nativeGeometry(graph, original) {
|
|
3896
|
-
const offsets = frames(original);
|
|
3897
|
-
const world = worldGeometry(original);
|
|
3898
|
-
const originalNodes = new Map(original.nodes.map((n) => [n.id, n]));
|
|
3899
|
-
const originalEdges = new Map(original.edges.map((e) => [e.id, e]));
|
|
3900
|
-
const worldNodes = new Map(world.nodes.map((n) => [n.id, n]));
|
|
3901
|
-
const worldEdges = new Map(world.edges.map((e) => [e.id, e]));
|
|
3902
|
-
return {
|
|
4147
|
+
const result = {
|
|
3903
4148
|
...graph,
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
};
|
|
3911
|
-
}),
|
|
3912
|
-
edges: graph.edges.map((e) => {
|
|
3913
|
-
const p = offsets.edges.get(e.id), before = originalEdges.get(e.id), prior = worldEdges.get(e.id);
|
|
3914
|
-
const points = JSON.stringify(e.points) === JSON.stringify(prior.points) ? before.points : e.points?.map((v) => ({
|
|
3915
|
-
x: v.x - p.x,
|
|
3916
|
-
y: v.y - p.y
|
|
3917
|
-
}));
|
|
3918
|
-
return {
|
|
3919
|
-
...e,
|
|
3920
|
-
x: e.x === prior.x ? before.x : e.x - p.x,
|
|
3921
|
-
y: e.y === prior.y ? before.y : e.y - p.y,
|
|
3922
|
-
...points === void 0 ? {} : { points }
|
|
3923
|
-
};
|
|
3924
|
-
})
|
|
4149
|
+
edgeCoordinateSpace: "world",
|
|
4150
|
+
direction: options.direction ?? graph.direction ?? "right",
|
|
4151
|
+
nodes,
|
|
4152
|
+
edges: graph.edges.map((e) => edges.get(e.id)),
|
|
4153
|
+
compoundGeometry,
|
|
4154
|
+
compoundRoutes: snapshot.routes
|
|
3925
4155
|
};
|
|
4156
|
+
recordRouteGeometry(result, snapshot.routes);
|
|
4157
|
+
return result;
|
|
3926
4158
|
}
|
|
3927
4159
|
|
|
3928
4160
|
//#endregion
|
|
@@ -5431,173 +5663,6 @@ function solveGeometryConstraints(graph, constraints, movableNodes, movableLabel
|
|
|
5431
5663
|
};
|
|
5432
5664
|
}
|
|
5433
5665
|
|
|
5434
|
-
//#endregion
|
|
5435
|
-
//#region src/authoring/routing.ts
|
|
5436
|
-
function overlaps(a, b, gap = 0) {
|
|
5437
|
-
return a.width > 0 && a.height > 0 && b.width > 0 && b.height > 0 && a.x < b.x + b.width + gap && a.x + a.width + gap > b.x && a.y < b.y + b.height + gap && a.y + a.height + gap > b.y;
|
|
5438
|
-
}
|
|
5439
|
-
/** Open-interior intersection: travelling along a rectangle boundary is allowed. */
|
|
5440
|
-
function crossesRect(a, b, rect) {
|
|
5441
|
-
let low = 0, high = 1;
|
|
5442
|
-
for (const axis of ["x", "y"]) {
|
|
5443
|
-
const delta = b[axis] - a[axis];
|
|
5444
|
-
const min = rect[axis], max = min + (axis === "x" ? rect.width : rect.height);
|
|
5445
|
-
if (Math.abs(delta) < 1e-10) {
|
|
5446
|
-
if (a[axis] <= min || a[axis] >= max) return false;
|
|
5447
|
-
} else {
|
|
5448
|
-
const t1 = (min - a[axis]) / delta, t2 = (max - a[axis]) / delta;
|
|
5449
|
-
low = Math.max(low, Math.min(t1, t2));
|
|
5450
|
-
high = Math.min(high, Math.max(t1, t2));
|
|
5451
|
-
}
|
|
5452
|
-
}
|
|
5453
|
-
return low < high - 1e-10;
|
|
5454
|
-
}
|
|
5455
|
-
function routeCrosses(points, rect) {
|
|
5456
|
-
return points?.some((point, i) => i > 0 && crossesRect(points[i - 1], point, rect)) ?? false;
|
|
5457
|
-
}
|
|
5458
|
-
function endpoint(node, portName, direction, source) {
|
|
5459
|
-
if (portName !== void 0) {
|
|
5460
|
-
const port = node.ports?.find((p) => p.name === portName);
|
|
5461
|
-
if (port?.x !== void 0 && port.y !== void 0) return {
|
|
5462
|
-
x: node.x + port.x + (port.width ?? 0) / 2,
|
|
5463
|
-
y: node.y + port.y + (port.height ?? 0) / 2
|
|
5464
|
-
};
|
|
5465
|
-
throw new LayoutError(`Port ${portName} on ${node.id} requires positioned geometry`, "MISSING_GEOMETRY");
|
|
5466
|
-
}
|
|
5467
|
-
const horizontal = direction === "left" || direction === "right";
|
|
5468
|
-
const far = source === (direction === "right" || direction === "down");
|
|
5469
|
-
return horizontal ? {
|
|
5470
|
-
x: node.x + (far ? node.width : 0),
|
|
5471
|
-
y: node.y + node.height / 2
|
|
5472
|
-
} : {
|
|
5473
|
-
x: node.x + node.width / 2,
|
|
5474
|
-
y: node.y + (far ? node.height : 0)
|
|
5475
|
-
};
|
|
5476
|
-
}
|
|
5477
|
-
/** Deterministic orthogonal visibility-grid search around fixed rectangles. */
|
|
5478
|
-
function connect(start, end, obstacles, check) {
|
|
5479
|
-
if (start.x === end.x && start.y === end.y) return [start];
|
|
5480
|
-
for (let attempt = 0; attempt < 8; attempt++) {
|
|
5481
|
-
check();
|
|
5482
|
-
const margin = 64 * 2 ** attempt;
|
|
5483
|
-
const left = Math.min(start.x, end.x) - margin;
|
|
5484
|
-
const right = Math.max(start.x, end.x) + margin;
|
|
5485
|
-
const top = Math.min(start.y, end.y) - margin;
|
|
5486
|
-
const bottom = Math.max(start.y, end.y) + margin;
|
|
5487
|
-
const local = obstacles.filter((r) => r.x <= right && r.x + r.width >= left && r.y <= bottom && r.y + r.height >= top);
|
|
5488
|
-
const result = search(start, end, local, obstacles, check);
|
|
5489
|
-
if (result || local.length === obstacles.length) return result;
|
|
5490
|
-
}
|
|
5491
|
-
}
|
|
5492
|
-
function search(start, end, gridObstacles, obstacles, check) {
|
|
5493
|
-
const xs = [...new Set([
|
|
5494
|
-
start.x,
|
|
5495
|
-
end.x,
|
|
5496
|
-
...gridObstacles.flatMap((r) => [r.x, r.x + r.width])
|
|
5497
|
-
])].sort((a, b) => a - b);
|
|
5498
|
-
const ys = [...new Set([
|
|
5499
|
-
start.y,
|
|
5500
|
-
end.y,
|
|
5501
|
-
...gridObstacles.flatMap((r) => [r.y, r.y + r.height])
|
|
5502
|
-
])].sort((a, b) => a - b);
|
|
5503
|
-
if (xs.length * ys.length > 4e4) return void 0;
|
|
5504
|
-
const width = xs.length;
|
|
5505
|
-
const point = (id) => ({
|
|
5506
|
-
x: xs[id % width],
|
|
5507
|
-
y: ys[Math.floor(id / width)]
|
|
5508
|
-
});
|
|
5509
|
-
const from = ys.indexOf(start.y) * width + xs.indexOf(start.x);
|
|
5510
|
-
const to = ys.indexOf(end.y) * width + xs.indexOf(end.x);
|
|
5511
|
-
const distances = new Map([[from, 0]]);
|
|
5512
|
-
const previous = /* @__PURE__ */ new Map();
|
|
5513
|
-
const queue = [];
|
|
5514
|
-
function push(item) {
|
|
5515
|
-
queue.push(item);
|
|
5516
|
-
let i = queue.length - 1;
|
|
5517
|
-
while (i > 0) {
|
|
5518
|
-
const parent = i - 1 >> 1;
|
|
5519
|
-
if (queue[parent].cost <= item.cost) break;
|
|
5520
|
-
queue[i] = queue[parent];
|
|
5521
|
-
i = parent;
|
|
5522
|
-
}
|
|
5523
|
-
queue[i] = item;
|
|
5524
|
-
}
|
|
5525
|
-
function pop() {
|
|
5526
|
-
const first = queue[0];
|
|
5527
|
-
const last = queue.pop();
|
|
5528
|
-
if (queue.length) {
|
|
5529
|
-
let i = 0;
|
|
5530
|
-
while (i * 2 + 1 < queue.length) {
|
|
5531
|
-
let child = i * 2 + 1;
|
|
5532
|
-
if (child + 1 < queue.length && queue[child + 1].cost < queue[child].cost) child++;
|
|
5533
|
-
if (last.cost <= queue[child].cost) break;
|
|
5534
|
-
queue[i] = queue[child];
|
|
5535
|
-
i = child;
|
|
5536
|
-
}
|
|
5537
|
-
queue[i] = last;
|
|
5538
|
-
}
|
|
5539
|
-
return first;
|
|
5540
|
-
}
|
|
5541
|
-
push({
|
|
5542
|
-
id: from,
|
|
5543
|
-
cost: 0
|
|
5544
|
-
});
|
|
5545
|
-
let visits = 0;
|
|
5546
|
-
while (queue.length) {
|
|
5547
|
-
if (++visits % 128 === 0) check();
|
|
5548
|
-
const current = pop();
|
|
5549
|
-
if (current.cost !== distances.get(current.id)) continue;
|
|
5550
|
-
if (current.id === to) {
|
|
5551
|
-
const result = [end];
|
|
5552
|
-
let id = to;
|
|
5553
|
-
while (id !== from) {
|
|
5554
|
-
id = previous.get(id);
|
|
5555
|
-
result.push(point(id));
|
|
5556
|
-
}
|
|
5557
|
-
return result.reverse();
|
|
5558
|
-
}
|
|
5559
|
-
const x = current.id % width, y = Math.floor(current.id / width);
|
|
5560
|
-
const neighbors = [
|
|
5561
|
-
x > 0 ? current.id - 1 : -1,
|
|
5562
|
-
x + 1 < width ? current.id + 1 : -1,
|
|
5563
|
-
y > 0 ? current.id - width : -1,
|
|
5564
|
-
y + 1 < ys.length ? current.id + width : -1
|
|
5565
|
-
];
|
|
5566
|
-
const a = point(current.id);
|
|
5567
|
-
for (const next of neighbors) {
|
|
5568
|
-
if (next < 0) continue;
|
|
5569
|
-
const b = point(next);
|
|
5570
|
-
if (obstacles.some((rect) => crossesRect(a, b, rect))) continue;
|
|
5571
|
-
const cost = current.cost + Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
5572
|
-
if (cost >= (distances.get(next) ?? Infinity)) continue;
|
|
5573
|
-
distances.set(next, cost);
|
|
5574
|
-
previous.set(next, current.id);
|
|
5575
|
-
push({
|
|
5576
|
-
id: next,
|
|
5577
|
-
cost
|
|
5578
|
-
});
|
|
5579
|
-
}
|
|
5580
|
-
}
|
|
5581
|
-
}
|
|
5582
|
-
function routeOrthogonal(edge, source, target, direction, obstacles, waypoints, check) {
|
|
5583
|
-
const anchors = [
|
|
5584
|
-
endpoint(source, edge.sourcePort, direction, true),
|
|
5585
|
-
...waypoints,
|
|
5586
|
-
endpoint(target, edge.targetPort, direction, false)
|
|
5587
|
-
];
|
|
5588
|
-
const points = [];
|
|
5589
|
-
for (let i = 1; i < anchors.length; i++) {
|
|
5590
|
-
const segment = connect(anchors[i - 1], anchors[i], obstacles, check);
|
|
5591
|
-
if (!segment) return void 0;
|
|
5592
|
-
points.push(...points.length ? segment.slice(1) : segment);
|
|
5593
|
-
}
|
|
5594
|
-
return points.filter((p, i) => {
|
|
5595
|
-
if (i === 0 || i === points.length - 1) return true;
|
|
5596
|
-
const before = points[i - 1], after = points[i + 1];
|
|
5597
|
-
return !(before.x === p.x && p.x === after.x && (p.y - before.y) * (after.y - p.y) >= 0 || before.y === p.y && p.y === after.y && (p.x - before.x) * (after.x - p.x) >= 0);
|
|
5598
|
-
});
|
|
5599
|
-
}
|
|
5600
|
-
|
|
5601
5666
|
//#endregion
|
|
5602
5667
|
//#region src/authoring/partial.ts
|
|
5603
5668
|
const geometryKeys = [
|
|
@@ -10556,7 +10621,7 @@ function runCommentBoxPipeline(graph, options, context) {
|
|
|
10556
10621
|
x: horizontal ? point.x : point.x + crossShift,
|
|
10557
10622
|
y: horizontal ? point.y + crossShift : point.y
|
|
10558
10623
|
}));
|
|
10559
|
-
const endpoint
|
|
10624
|
+
const endpoint = (source) => {
|
|
10560
10625
|
if (horizontal) return {
|
|
10561
10626
|
x: direction === "right" === source ? (targetRect.x ?? 0) + (targetRect.width ?? 0) : targetRect.x ?? 0,
|
|
10562
10627
|
y: (targetRect.y ?? 0) + (targetRect.height ?? 0) / 2
|
|
@@ -10566,8 +10631,8 @@ function runCommentBoxPipeline(graph, options, context) {
|
|
|
10566
10631
|
y: direction === "down" === source ? (targetRect.y ?? 0) + (targetRect.height ?? 0) : targetRect.y ?? 0
|
|
10567
10632
|
};
|
|
10568
10633
|
};
|
|
10569
|
-
if (edge.sourceId === targetId && points.length > 0) points[0] = endpoint
|
|
10570
|
-
if (edge.targetId === targetId && points.length > 0) points[points.length - 1] = endpoint
|
|
10634
|
+
if (edge.sourceId === targetId && points.length > 0) points[0] = endpoint(true);
|
|
10635
|
+
if (edge.targetId === targetId && points.length > 0) points[points.length - 1] = endpoint(false);
|
|
10571
10636
|
return [edge.id, {
|
|
10572
10637
|
...edge,
|
|
10573
10638
|
x: horizontal ? edge.x : (edge.x ?? 0) + crossShift,
|
|
@@ -10769,126 +10834,16 @@ function runSeparatedComponents(graph, options, context) {
|
|
|
10769
10834
|
};
|
|
10770
10835
|
}
|
|
10771
10836
|
function runCompoundPipeline(graph, options, context) {
|
|
10772
|
-
const
|
|
10773
|
-
const
|
|
10774
|
-
|
|
10775
|
-
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
value++;
|
|
10781
|
-
parentId = nodeById.get(parentId)?.parentId;
|
|
10782
|
-
}
|
|
10783
|
-
return value;
|
|
10784
|
-
};
|
|
10785
|
-
const parentIds = [...new Set(nodes.flatMap((node) => node.parentId == null ? [] : [node.parentId]))].sort((left, right) => depth(nodeById.get(right)) - depth(nodeById.get(left)));
|
|
10786
|
-
const edgeById = new Map(graph.edges.map((edge) => [edge.id, { ...edge }]));
|
|
10787
|
-
const padding = typeof options.padding === "number" ? options.padding : 12;
|
|
10788
|
-
const layoutSiblings = (parentId) => {
|
|
10789
|
-
const siblings = nodes.filter((node) => (node.parentId ?? null) === parentId);
|
|
10790
|
-
if (siblings.length === 0) return;
|
|
10791
|
-
const siblingIds = new Set(siblings.map((node) => node.id));
|
|
10792
|
-
const siblingEdges = graph.edges.filter((edge) => siblingIds.has(edge.sourceId) && siblingIds.has(edge.targetId));
|
|
10793
|
-
const flatNodes = siblings.map((node) => ({
|
|
10794
|
-
...node,
|
|
10795
|
-
parentId: null
|
|
10796
|
-
}));
|
|
10797
|
-
const result = runLayeredPipeline({
|
|
10798
|
-
...graph,
|
|
10799
|
-
nodes: flatNodes,
|
|
10800
|
-
edges: siblingEdges
|
|
10801
|
-
}, options, context);
|
|
10802
|
-
for (const laidOut of result.nodes) {
|
|
10803
|
-
const node = nodeById.get(laidOut.id);
|
|
10804
|
-
if (!node) continue;
|
|
10805
|
-
Object.assign(node, laidOut, { parentId });
|
|
10806
|
-
}
|
|
10807
|
-
for (const edge of result.edges) edgeById.set(edge.id, edge);
|
|
10808
|
-
};
|
|
10809
|
-
for (const parentId of parentIds) {
|
|
10810
|
-
layoutSiblings(parentId);
|
|
10811
|
-
const parent = nodeById.get(parentId);
|
|
10812
|
-
if (!parent) continue;
|
|
10813
|
-
const children = nodes.filter((node) => node.parentId === parentId);
|
|
10814
|
-
const right = Math.max(0, ...children.map((node) => (node.x ?? 0) + (node.width ?? 0)));
|
|
10815
|
-
const bottom = Math.max(0, ...children.map((node) => (node.y ?? 0) + (node.height ?? 0)));
|
|
10816
|
-
parent.width = Math.max(parent.width ?? 0, right + padding);
|
|
10817
|
-
parent.height = Math.max(parent.height ?? 0, bottom + padding);
|
|
10818
|
-
}
|
|
10819
|
-
const roots = nodes.filter((node) => node.parentId == null);
|
|
10820
|
-
if (roots.length === 1 && parentIds.includes(roots[0].id)) Object.assign(roots[0], {
|
|
10821
|
-
x: 0,
|
|
10822
|
-
y: 0
|
|
10837
|
+
const result = layoutCompounds(graph, options, (scope, local) => runLayeredPipeline(scope, local, context));
|
|
10838
|
+
for (const route of result.compoundRoutes.values()) for (const diagnostic of route.diagnostics) context?.diagnostics.push({
|
|
10839
|
+
severity: "warning",
|
|
10840
|
+
code: diagnostic.code,
|
|
10841
|
+
message: diagnostic.message,
|
|
10842
|
+
entityIds: [route.edgeId],
|
|
10843
|
+
phase: "compound-routing",
|
|
10844
|
+
geometry: "routes"
|
|
10823
10845
|
});
|
|
10824
|
-
|
|
10825
|
-
const absoluteRect = (id) => {
|
|
10826
|
-
const node = nodeById.get(id);
|
|
10827
|
-
if (!node) return {
|
|
10828
|
-
x: 0,
|
|
10829
|
-
y: 0,
|
|
10830
|
-
width: 0,
|
|
10831
|
-
height: 0
|
|
10832
|
-
};
|
|
10833
|
-
let x = node.x ?? 0;
|
|
10834
|
-
let y = node.y ?? 0;
|
|
10835
|
-
let parentId = node.parentId;
|
|
10836
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10837
|
-
while (parentId != null && !seen.has(parentId)) {
|
|
10838
|
-
seen.add(parentId);
|
|
10839
|
-
const parent = nodeById.get(parentId);
|
|
10840
|
-
if (!parent) break;
|
|
10841
|
-
x += parent.x ?? 0;
|
|
10842
|
-
y += parent.y ?? 0;
|
|
10843
|
-
parentId = parent.parentId;
|
|
10844
|
-
}
|
|
10845
|
-
return {
|
|
10846
|
-
x,
|
|
10847
|
-
y,
|
|
10848
|
-
width: node.width ?? 0,
|
|
10849
|
-
height: node.height ?? 0
|
|
10850
|
-
};
|
|
10851
|
-
};
|
|
10852
|
-
for (const edge of graph.edges) {
|
|
10853
|
-
if (edgeById.get(edge.id)?.points !== void 0) continue;
|
|
10854
|
-
const source = absoluteRect(edge.sourceId);
|
|
10855
|
-
const target = absoluteRect(edge.targetId);
|
|
10856
|
-
const start = {
|
|
10857
|
-
x: source.x + source.width,
|
|
10858
|
-
y: source.y + source.height / 2
|
|
10859
|
-
};
|
|
10860
|
-
const end = {
|
|
10861
|
-
x: target.x,
|
|
10862
|
-
y: target.y + target.height / 2
|
|
10863
|
-
};
|
|
10864
|
-
const track = (start.x + end.x) / 2;
|
|
10865
|
-
const points = [
|
|
10866
|
-
start,
|
|
10867
|
-
{
|
|
10868
|
-
x: track,
|
|
10869
|
-
y: start.y
|
|
10870
|
-
},
|
|
10871
|
-
{
|
|
10872
|
-
x: track,
|
|
10873
|
-
y: end.y
|
|
10874
|
-
},
|
|
10875
|
-
end
|
|
10876
|
-
];
|
|
10877
|
-
edgeById.set(edge.id, {
|
|
10878
|
-
...edge,
|
|
10879
|
-
points,
|
|
10880
|
-
x: track,
|
|
10881
|
-
y: (start.y + end.y) / 2,
|
|
10882
|
-
width: edge.width ?? 0,
|
|
10883
|
-
height: edge.height ?? 0,
|
|
10884
|
-
routing: "orthogonal"
|
|
10885
|
-
});
|
|
10886
|
-
}
|
|
10887
|
-
return {
|
|
10888
|
-
...graph,
|
|
10889
|
-
nodes,
|
|
10890
|
-
edges: graph.edges.map((edge) => edgeById.get(edge.id) ?? edge)
|
|
10891
|
-
};
|
|
10846
|
+
return result;
|
|
10892
10847
|
}
|
|
10893
10848
|
function runLayeredPipeline(graph, options, context) {
|
|
10894
10849
|
if (options.settings?.noLayout) return {
|
|
@@ -11361,16 +11316,16 @@ function runLayeredPipeline(graph, options, context) {
|
|
|
11361
11316
|
const targetDelta = postCompactionNodeCrossDeltas.get(edge.targetId) ?? 0;
|
|
11362
11317
|
const shiftEndpoint = (endpointIndex, adjacentIndex, delta) => {
|
|
11363
11318
|
if (delta === 0) return;
|
|
11364
|
-
const endpoint
|
|
11319
|
+
const endpoint = points[endpointIndex];
|
|
11365
11320
|
const adjacent = points[adjacentIndex];
|
|
11366
|
-
if (!endpoint
|
|
11367
|
-
const originalCross = horizontalAntiparallelFlow ? endpoint
|
|
11321
|
+
if (!endpoint) return;
|
|
11322
|
+
const originalCross = horizontalAntiparallelFlow ? endpoint.y : endpoint.x;
|
|
11368
11323
|
points[endpointIndex] = horizontalAntiparallelFlow ? {
|
|
11369
|
-
...endpoint
|
|
11370
|
-
y: endpoint
|
|
11324
|
+
...endpoint,
|
|
11325
|
+
y: endpoint.y + delta
|
|
11371
11326
|
} : {
|
|
11372
|
-
...endpoint
|
|
11373
|
-
x: endpoint
|
|
11327
|
+
...endpoint,
|
|
11328
|
+
x: endpoint.x + delta
|
|
11374
11329
|
};
|
|
11375
11330
|
if (adjacent && (horizontalAntiparallelFlow ? adjacent.y : adjacent.x) === originalCross) points[adjacentIndex] = horizontalAntiparallelFlow ? {
|
|
11376
11331
|
...adjacent,
|
|
@@ -11460,14 +11415,14 @@ function runLayeredPipeline(graph, options, context) {
|
|
|
11460
11415
|
for (const edge of [...incoming, ...outgoing]) {
|
|
11461
11416
|
const points = [...mutableRouteMap.get(edge.id) ?? []];
|
|
11462
11417
|
const endpointIndex = edge.sourceId === node.id ? 0 : points.length - 1;
|
|
11463
|
-
const endpoint
|
|
11464
|
-
if (!endpoint
|
|
11418
|
+
const endpoint = points[endpointIndex];
|
|
11419
|
+
if (!endpoint) continue;
|
|
11465
11420
|
points[endpointIndex] = horizontal ? {
|
|
11466
|
-
...endpoint
|
|
11467
|
-
x: endpoint
|
|
11421
|
+
...endpoint,
|
|
11422
|
+
x: endpoint.x + delta
|
|
11468
11423
|
} : {
|
|
11469
|
-
...endpoint
|
|
11470
|
-
y: endpoint
|
|
11424
|
+
...endpoint,
|
|
11425
|
+
y: endpoint.y + delta
|
|
11471
11426
|
};
|
|
11472
11427
|
mutableRouteMap.set(edge.id, points);
|
|
11473
11428
|
}
|
|
@@ -11572,14 +11527,14 @@ function runLayeredPipeline(graph, options, context) {
|
|
|
11572
11527
|
if (points.length === 0) continue;
|
|
11573
11528
|
const index = edge.sourceId === nodeId ? 0 : edge.targetId === nodeId ? points.length - 1 : void 0;
|
|
11574
11529
|
if (index === void 0) continue;
|
|
11575
|
-
const endpoint
|
|
11576
|
-
const originalCross = horizontal ? endpoint
|
|
11530
|
+
const endpoint = points[index];
|
|
11531
|
+
const originalCross = horizontal ? endpoint.y : endpoint.x;
|
|
11577
11532
|
points[index] = horizontal ? {
|
|
11578
|
-
...endpoint
|
|
11579
|
-
y: endpoint
|
|
11533
|
+
...endpoint,
|
|
11534
|
+
y: endpoint.y + delta
|
|
11580
11535
|
} : {
|
|
11581
|
-
...endpoint
|
|
11582
|
-
x: endpoint
|
|
11536
|
+
...endpoint,
|
|
11537
|
+
x: endpoint.x + delta
|
|
11583
11538
|
};
|
|
11584
11539
|
const adjacentIndex = index === 0 ? 1 : points.length - 2;
|
|
11585
11540
|
const adjacent = points[adjacentIndex];
|
|
@@ -11777,7 +11732,12 @@ function runLayeredPipeline(graph, options, context) {
|
|
|
11777
11732
|
* custom phase strategies, and all layered edge-routing styles.
|
|
11778
11733
|
*/
|
|
11779
11734
|
function getLayeredLayout(graph, options = {}) {
|
|
11780
|
-
|
|
11735
|
+
const result = runLayeredPipeline(graph, options);
|
|
11736
|
+
return {
|
|
11737
|
+
...result,
|
|
11738
|
+
compoundGeometry: result.compoundGeometry ?? /* @__PURE__ */ new Map(),
|
|
11739
|
+
compoundRoutes: result.compoundRoutes ?? /* @__PURE__ */ new Map()
|
|
11740
|
+
};
|
|
11781
11741
|
}
|
|
11782
11742
|
const layeredAlgorithm = {
|
|
11783
11743
|
id: "layered",
|
|
@@ -11807,4 +11767,4 @@ const layeredAlgorithm = {
|
|
|
11807
11767
|
};
|
|
11808
11768
|
|
|
11809
11769
|
//#endregion
|
|
11810
|
-
export { minimizeCrossingsInteractively as A, JavaRandom as B, breakCyclesGreedily as C, breakCyclesWithModelOrderBreadthFirstSearch as D, breakCyclesWithDepthFirstSearch as E, placeNodesInteractively as F,
|
|
11770
|
+
export { minimizeCrossingsInteractively as A, JavaRandom as B, breakCyclesGreedily as C, breakCyclesWithModelOrderBreadthFirstSearch as D, breakCyclesWithDepthFirstSearch as E, placeNodesInteractively as F, placePorts as I, routeEdgesOrthogonally as L, minimizeCrossingsWithMedian as M, minimizeCrossingsWithModelOrder as N, breakCyclesWithModelOrderDepthFirstSearch as O, placeNodesInLayers as P, routeEdgesWithPolylines as R, breakCyclesByStronglyConnectedNodeType as S, breakCyclesInteractively as T, assignLayersByLongestPathToSink as _, elkLayeredEnumValues as a, breakCyclesByModelOrder as b, placeNodesWithLinearSegments as c, assignLayersWithMinWidth as d, assignLayersWithNetworkSimplex as f, assignLayersByLongestPath as g, assignLayersByDepthFirstModelOrder as h, toElkLayeredOptions as i, minimizeCrossingsWithBarycenter as j, getPolylineMidpoint as k, placeNodesWithBrandesKoepf as l, assignLayersByBreadthFirstModelOrder as m, layeredAlgorithm as n, elkLayeredOptionDefinitions as o, c as p, fromElkLayeredOptionId as r, placeNodesWithNetworkSimplex as s, getLayeredLayout as t, assignLayersWithStretchWidth as u, assignLayersInteractively as v, breakCyclesGreedilyByModelOrder as w, breakCyclesByStronglyConnectedConnectivity as x, assignLayersWithCoffmanGraham as y, routeEdgesWithSplines as z };
|