@aranzatech/diagrams-bpmn 0.3.5 → 0.3.6
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/dist/{catalog-DNIyjHbl.d.ts → catalog-CYZHikuU.d.ts} +1 -1
- package/dist/{catalog-DG-sz0VM.d.cts → catalog-C_S9hyyn.d.cts} +1 -1
- package/dist/{chunk-JFYWN6QH.js → chunk-EMTO53AN.js} +52 -73
- package/dist/chunk-EMTO53AN.js.map +1 -0
- package/dist/chunk-G22XQD6H.js +64 -0
- package/dist/chunk-G22XQD6H.js.map +1 -0
- package/dist/{chunk-NYIYQUGX.js → chunk-OFOTX3LA.js} +124 -20
- package/dist/chunk-OFOTX3LA.js.map +1 -0
- package/dist/{chunk-YAYZW45I.js → chunk-SRUWPELT.js} +80 -3
- package/dist/chunk-SRUWPELT.js.map +1 -0
- package/dist/edges/index.cjs +73 -35
- package/dist/edges/index.cjs.map +1 -1
- package/dist/edges/index.js +2 -1
- package/dist/elements/index.d.cts +3 -3
- package/dist/elements/index.d.ts +3 -3
- package/dist/extensions/index.d.cts +2 -2
- package/dist/extensions/index.d.ts +2 -2
- package/dist/index.cjs +194 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/layout/index.cjs +1146 -872
- package/dist/layout/index.cjs.map +1 -1
- package/dist/layout/index.d.cts +4 -4
- package/dist/layout/index.d.ts +4 -4
- package/dist/layout/index.js +560 -89
- package/dist/layout/index.js.map +1 -1
- package/dist/modeling/index.cjs +78 -0
- package/dist/modeling/index.cjs.map +1 -1
- package/dist/modeling/index.d.cts +10 -5
- package/dist/modeling/index.d.ts +10 -5
- package/dist/modeling/index.js +1 -1
- package/dist/{types-nvF59RGF.d.cts → types--x9aoecw.d.cts} +5 -0
- package/dist/{types-nvF59RGF.d.ts → types--x9aoecw.d.ts} +5 -0
- package/dist/{types-dQUuSnV5.d.cts → types-CrFDTGo9.d.cts} +1 -1
- package/dist/{types-CuDL2YGL.d.ts → types-DoPv3m7u.d.ts} +2 -2
- package/dist/{types-X5FyP8oS.d.ts → types-DteJykQG.d.ts} +1 -1
- package/dist/{types-CDp9kWQ4.d.cts → types-YZ4sj3Ih.d.cts} +2 -2
- package/dist/validation/index.d.cts +3 -3
- package/dist/validation/index.d.ts +3 -3
- package/dist/xml/index.cjs +162 -18
- package/dist/xml/index.cjs.map +1 -1
- package/dist/xml/index.d.cts +4 -4
- package/dist/xml/index.d.ts +4 -4
- package/dist/xml/index.js +2 -1
- package/package.json +1 -1
- package/dist/chunk-FFWJA5BV.js +0 -163
- package/dist/chunk-FFWJA5BV.js.map +0 -1
- package/dist/chunk-JFYWN6QH.js.map +0 -1
- package/dist/chunk-NYIYQUGX.js.map +0 -1
- package/dist/chunk-YAYZW45I.js.map +0 -1
- package/dist/elk-QT7H4252.js +0 -6
- package/dist/elk-QT7H4252.js.map +0 -1
package/dist/layout/index.js
CHANGED
|
@@ -1,10 +1,166 @@
|
|
|
1
|
-
|
|
2
|
-
import { getBpmnNodeSize } from '../chunk-YAYZW45I.js';
|
|
1
|
+
import { getBpmnNodeSize } from '../chunk-SRUWPELT.js';
|
|
3
2
|
import '../chunk-RLAJNRF2.js';
|
|
4
3
|
import '../chunk-L5Z22RLX.js';
|
|
5
|
-
import { dagreLayout, applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
|
|
4
|
+
import { elkLayout, dagreLayout, applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
|
|
6
5
|
export { applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
|
|
7
6
|
|
|
7
|
+
var BPMN_CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
8
|
+
"Pool",
|
|
9
|
+
"Lane",
|
|
10
|
+
"SubProcess",
|
|
11
|
+
"Transaction",
|
|
12
|
+
"EventSubProcess",
|
|
13
|
+
"AdHocSubProcess"
|
|
14
|
+
]);
|
|
15
|
+
var CONTAINER_MIN_SIZE = {
|
|
16
|
+
Pool: { w: 720, h: 200 },
|
|
17
|
+
Lane: { w: 600, h: 160 },
|
|
18
|
+
SubProcess: { w: 240, h: 140 },
|
|
19
|
+
Transaction: { w: 240, h: 140 },
|
|
20
|
+
EventSubProcess: { w: 240, h: 140 },
|
|
21
|
+
AdHocSubProcess: { w: 240, h: 140 }
|
|
22
|
+
};
|
|
23
|
+
var CONTAINER_PADDING = {
|
|
24
|
+
Pool: "[top=30,left=60,bottom=30,right=50]",
|
|
25
|
+
Lane: "[top=40,left=70,bottom=40,right=50]",
|
|
26
|
+
SubProcess: "[top=30,left=40,bottom=30,right=40]",
|
|
27
|
+
Transaction: "[top=30,left=40,bottom=30,right=40]",
|
|
28
|
+
EventSubProcess: "[top=30,left=40,bottom=30,right=40]",
|
|
29
|
+
AdHocSubProcess: "[top=30,left=40,bottom=30,right=40]"
|
|
30
|
+
};
|
|
31
|
+
async function bpmnElkLayout(nodes, edges) {
|
|
32
|
+
const poolsWithLanes = /* @__PURE__ */ new Set();
|
|
33
|
+
for (const node of nodes) {
|
|
34
|
+
if (node.data.elementType === "Lane" && node.parentId) {
|
|
35
|
+
poolsWithLanes.add(node.parentId);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const result = await elkLayout(
|
|
39
|
+
nodes,
|
|
40
|
+
edges,
|
|
41
|
+
{
|
|
42
|
+
direction: "LR",
|
|
43
|
+
getNodeSize: (node) => {
|
|
44
|
+
const bNode = node;
|
|
45
|
+
if (BPMN_CONTAINER_TYPES.has(bNode.data.elementType)) return void 0;
|
|
46
|
+
return getBpmnNodeSize(bNode);
|
|
47
|
+
},
|
|
48
|
+
isContainerNode: (node) => BPMN_CONTAINER_TYPES.has(node.data.elementType),
|
|
49
|
+
getNodeLayoutOptions: (node) => {
|
|
50
|
+
const bNode = node;
|
|
51
|
+
const type = bNode.data.elementType;
|
|
52
|
+
if (!BPMN_CONTAINER_TYPES.has(type)) return void 0;
|
|
53
|
+
const min = CONTAINER_MIN_SIZE[type];
|
|
54
|
+
const isPoolWithLanes = type === "Pool" && poolsWithLanes.has(node.id);
|
|
55
|
+
const dir = isPoolWithLanes ? "DOWN" : "RIGHT";
|
|
56
|
+
return {
|
|
57
|
+
"elk.direction": dir,
|
|
58
|
+
"elk.nodeSize.constraints": "MINIMUM_SIZE",
|
|
59
|
+
// Each container handles its own layout pass so cross-lane edges
|
|
60
|
+
// participate in the internal layout without the global direction
|
|
61
|
+
// forcing Lane containers to align sideways.
|
|
62
|
+
"elk.hierarchyHandling": "INCLUDE_CHILDREN",
|
|
63
|
+
// Lanes within a Pool share a border — zero gap between them.
|
|
64
|
+
// Other containers keep the tighter inner spacing.
|
|
65
|
+
...isPoolWithLanes ? { "elk.spacing.nodeNode": "0", "elk.spacing.componentComponent": "0" } : {
|
|
66
|
+
"elk.layered.spacing.nodeNodeBetweenLayers": "100",
|
|
67
|
+
"elk.layered.spacing.edgeNodeBetweenLayers": "45",
|
|
68
|
+
"elk.spacing.nodeNode": "50"
|
|
69
|
+
},
|
|
70
|
+
...min ? { "elk.nodeSize.minimum": `[w=${min.w},h=${min.h}]` } : {},
|
|
71
|
+
...CONTAINER_PADDING[type] ? { "elk.padding": CONTAINER_PADDING[type] } : {}
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
elk: {
|
|
75
|
+
"elk.algorithm": "layered",
|
|
76
|
+
"elk.direction": "RIGHT",
|
|
77
|
+
// INHERIT: each container handles its own INCLUDE_CHILDREN pass so
|
|
78
|
+
// the global RIGHT direction does not force Lane containers sideways.
|
|
79
|
+
"elk.hierarchyHandling": "INHERIT",
|
|
80
|
+
"elk.edgeRouting": "ORTHOGONAL",
|
|
81
|
+
// Global spacing — generous values for visual breathing room.
|
|
82
|
+
"elk.layered.spacing.nodeNodeBetweenLayers": "120",
|
|
83
|
+
"elk.layered.spacing.edgeNodeBetweenLayers": "50",
|
|
84
|
+
"elk.spacing.nodeNode": "70",
|
|
85
|
+
"elk.spacing.componentComponent": "100"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
const nodeMap = new Map(result.nodes.map((n) => [n.id, n]));
|
|
90
|
+
const edgeMap = new Map(result.edges.map((e) => [e.id, e]));
|
|
91
|
+
const COMPONENT_GAP = 100;
|
|
92
|
+
const rootNodeOrder = nodes.filter((n) => !n.parentId).map((n) => n.id);
|
|
93
|
+
const rootHeights = /* @__PURE__ */ new Map();
|
|
94
|
+
for (const id of rootNodeOrder) {
|
|
95
|
+
const laid = nodeMap.get(id);
|
|
96
|
+
const orig = nodes.find((n) => n.id === id);
|
|
97
|
+
rootHeights.set(id, laid?.height ?? orig?.height ?? 200);
|
|
98
|
+
}
|
|
99
|
+
let stackY = 0;
|
|
100
|
+
const rootY = /* @__PURE__ */ new Map();
|
|
101
|
+
for (const id of rootNodeOrder) {
|
|
102
|
+
rootY.set(id, stackY);
|
|
103
|
+
stackY += (rootHeights.get(id) ?? 200) + COMPONENT_GAP;
|
|
104
|
+
}
|
|
105
|
+
const updatedNodes = nodes.map((node) => {
|
|
106
|
+
const laid = nodeMap.get(node.id);
|
|
107
|
+
if (!laid) return node;
|
|
108
|
+
const pos = node.parentId ? laid.position : { x: laid.position.x, y: rootY.get(node.id) ?? laid.position.y };
|
|
109
|
+
return { ...node, position: pos, width: laid.width, height: laid.height };
|
|
110
|
+
});
|
|
111
|
+
const lanesByPool = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const node of updatedNodes) {
|
|
113
|
+
if (node.data.elementType === "Lane" && node.parentId) {
|
|
114
|
+
const arr = lanesByPool.get(node.parentId) ?? [];
|
|
115
|
+
arr.push(node.id);
|
|
116
|
+
lanesByPool.set(node.parentId, arr);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const nodeIndex = new Map(updatedNodes.map((n, i) => [n.id, i]));
|
|
120
|
+
for (const laneIds of lanesByPool.values()) {
|
|
121
|
+
const widths = laneIds.map((id) => updatedNodes[nodeIndex.get(id)]?.width ?? 0);
|
|
122
|
+
const maxWidth = Math.max(...widths);
|
|
123
|
+
if (maxWidth <= 0) continue;
|
|
124
|
+
for (const id of laneIds) {
|
|
125
|
+
const idx = nodeIndex.get(id);
|
|
126
|
+
if (idx !== void 0) {
|
|
127
|
+
updatedNodes[idx] = { ...updatedNodes[idx], width: maxWidth };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const nodeRootContainer = /* @__PURE__ */ new Map();
|
|
132
|
+
for (const node of updatedNodes) {
|
|
133
|
+
nodeRootContainer.set(node.id, node.parentId ?? null);
|
|
134
|
+
}
|
|
135
|
+
const getRootContainer = (nodeId) => {
|
|
136
|
+
let current = nodeId;
|
|
137
|
+
const visited = /* @__PURE__ */ new Set();
|
|
138
|
+
while (current) {
|
|
139
|
+
if (visited.has(current)) break;
|
|
140
|
+
visited.add(current);
|
|
141
|
+
const parent = nodeRootContainer.get(current);
|
|
142
|
+
if (!parent) return current;
|
|
143
|
+
current = parent;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
};
|
|
147
|
+
const updatedEdges = edges.map((edge) => {
|
|
148
|
+
const laid = edgeMap.get(edge.id);
|
|
149
|
+
if (!laid) return edge;
|
|
150
|
+
const nextData = { ...edge.data };
|
|
151
|
+
const sourceRoot = getRootContainer(edge.source);
|
|
152
|
+
const targetRoot = getRootContainer(edge.target);
|
|
153
|
+
const isCrossPool = sourceRoot !== targetRoot;
|
|
154
|
+
if (!isCrossPool && laid.points && laid.points.length > 0) {
|
|
155
|
+
nextData.routingPoints = laid.points;
|
|
156
|
+
} else {
|
|
157
|
+
delete nextData.routingPoints;
|
|
158
|
+
}
|
|
159
|
+
return { ...edge, data: nextData };
|
|
160
|
+
});
|
|
161
|
+
return { nodes: updatedNodes, edges: updatedEdges };
|
|
162
|
+
}
|
|
163
|
+
|
|
8
164
|
// src/layout/bpmn-layout-graph.ts
|
|
9
165
|
var LAYOUT_CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
10
166
|
"Pool",
|
|
@@ -22,15 +178,21 @@ var LAYOUT_GATEWAY_TYPES = /* @__PURE__ */ new Set([
|
|
|
22
178
|
"ComplexGateway"
|
|
23
179
|
]);
|
|
24
180
|
function detectBackEdges(nodeIds, seqEdges) {
|
|
181
|
+
const sortedIds = [...nodeIds].sort();
|
|
25
182
|
const backEdgeIds = /* @__PURE__ */ new Set();
|
|
26
183
|
const state = /* @__PURE__ */ new Map();
|
|
27
|
-
for (const id of
|
|
184
|
+
for (const id of sortedIds) state.set(id, 0);
|
|
28
185
|
const adj = /* @__PURE__ */ new Map();
|
|
29
|
-
for (const id of
|
|
186
|
+
for (const id of sortedIds) adj.set(id, []);
|
|
30
187
|
for (const e of seqEdges) {
|
|
31
188
|
adj.get(e.source)?.push({ target: e.target, edgeId: e.id });
|
|
32
189
|
}
|
|
33
|
-
for (const
|
|
190
|
+
for (const list of adj.values()) {
|
|
191
|
+
list.sort(
|
|
192
|
+
(a, b) => a.target < b.target ? -1 : a.target > b.target ? 1 : a.edgeId < b.edgeId ? -1 : a.edgeId > b.edgeId ? 1 : 0
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
for (const startId of sortedIds) {
|
|
34
196
|
if (state.get(startId) !== 0) continue;
|
|
35
197
|
const stack = [{ id: startId, idx: 0 }];
|
|
36
198
|
state.set(startId, 1);
|
|
@@ -107,9 +269,7 @@ function detectGatewayPairs(nodes, forwardEdges) {
|
|
|
107
269
|
}
|
|
108
270
|
const order = topologicalSort(nodeIds, forwardEdges);
|
|
109
271
|
const topoPos = new Map(order.map((id, i) => [id, i]));
|
|
110
|
-
const splits = nodes.filter(
|
|
111
|
-
(n) => LAYOUT_GATEWAY_TYPES.has(n.data.elementType) && (succs.get(n.id)?.length ?? 0) > 1
|
|
112
|
-
);
|
|
272
|
+
const splits = nodes.filter((n) => (succs.get(n.id)?.length ?? 0) > 1);
|
|
113
273
|
for (const split of splits) {
|
|
114
274
|
const branches = succs.get(split.id) ?? [];
|
|
115
275
|
if (branches.length < 2) continue;
|
|
@@ -161,12 +321,18 @@ function buildHandleMap(edges) {
|
|
|
161
321
|
}
|
|
162
322
|
return map;
|
|
163
323
|
}
|
|
324
|
+
function branchPullBias(nodeIds, nodeBias) {
|
|
325
|
+
if (!nodeBias) return null;
|
|
326
|
+
let sum = 0;
|
|
327
|
+
for (const id of nodeIds) sum += nodeBias.get(id) ?? 0;
|
|
328
|
+
if (sum === 0) return null;
|
|
329
|
+
return sum > 0 ? 1 : -1;
|
|
330
|
+
}
|
|
164
331
|
function pickMainBranch(branchData) {
|
|
165
332
|
if (branchData.length === 0) return void 0;
|
|
166
333
|
return [...branchData].sort((a, b) => {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
if (aRight !== bRight) return bRight - aRight;
|
|
334
|
+
const rank = (branch) => branch.bias === 0 ? 2 : branch.bias === null ? 1 : 0;
|
|
335
|
+
if (rank(a) !== rank(b)) return rank(b) - rank(a);
|
|
170
336
|
if (a.nodeIds.length !== b.nodeIds.length) return b.nodeIds.length - a.nodeIds.length;
|
|
171
337
|
return a.start.localeCompare(b.start);
|
|
172
338
|
})[0]?.start;
|
|
@@ -207,7 +373,7 @@ function assignBranchRowsAroundMain(branchData, splitRow) {
|
|
|
207
373
|
}
|
|
208
374
|
return assigned;
|
|
209
375
|
}
|
|
210
|
-
function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
|
|
376
|
+
function assignRows(nodes, forwardEdges, columns, gatewayPairs, nodeBias) {
|
|
211
377
|
const rows = new Map(nodes.map((n) => [n.id, 0]));
|
|
212
378
|
const succs = new Map(nodes.map((n) => [n.id, []]));
|
|
213
379
|
for (const e of forwardEdges) {
|
|
@@ -215,7 +381,7 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
|
|
|
215
381
|
}
|
|
216
382
|
const handleMap = buildHandleMap(forwardEdges);
|
|
217
383
|
const sortedPairs = [...gatewayPairs.entries()].sort(
|
|
218
|
-
(a, b) => (columns.get(
|
|
384
|
+
(a, b) => (columns.get(a[0]) ?? 0) - (columns.get(b[0]) ?? 0)
|
|
219
385
|
);
|
|
220
386
|
for (const [splitId, mergeId] of sortedPairs) {
|
|
221
387
|
const branches = succs.get(splitId) ?? [];
|
|
@@ -235,11 +401,14 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
|
|
|
235
401
|
return result;
|
|
236
402
|
};
|
|
237
403
|
const splitHandles = handleMap.get(splitId);
|
|
238
|
-
const branchData = branches.map((start) =>
|
|
239
|
-
start
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
404
|
+
const branchData = branches.map((start) => {
|
|
405
|
+
const nodeIds = getBranchNodes(start);
|
|
406
|
+
return {
|
|
407
|
+
start,
|
|
408
|
+
nodeIds,
|
|
409
|
+
bias: handleToRowBias(splitHandles?.get(start) ?? null) ?? branchPullBias(nodeIds, nodeBias)
|
|
410
|
+
};
|
|
411
|
+
});
|
|
243
412
|
const assigned = assignBranchRowsAroundMain(branchData, splitRow);
|
|
244
413
|
for (const b of branchData) {
|
|
245
414
|
const row = assigned.get(b.start) ?? splitRow;
|
|
@@ -251,7 +420,7 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
|
|
|
251
420
|
}
|
|
252
421
|
const pairedSplits = new Set(gatewayPairs.keys());
|
|
253
422
|
const unpairedSplits = nodes.filter(
|
|
254
|
-
(n) =>
|
|
423
|
+
(n) => !pairedSplits.has(n.id) && (succs.get(n.id)?.length ?? 0) > 1
|
|
255
424
|
);
|
|
256
425
|
for (const split of unpairedSplits) {
|
|
257
426
|
const branches = succs.get(split.id) ?? [];
|
|
@@ -271,11 +440,14 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
|
|
|
271
440
|
return result;
|
|
272
441
|
};
|
|
273
442
|
const splitHandles2 = handleMap.get(split.id);
|
|
274
|
-
const branchData = branches.map((start) =>
|
|
275
|
-
start
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
443
|
+
const branchData = branches.map((start) => {
|
|
444
|
+
const nodeIds = getAllReachable(start);
|
|
445
|
+
return {
|
|
446
|
+
start,
|
|
447
|
+
nodeIds,
|
|
448
|
+
bias: handleToRowBias(splitHandles2?.get(start) ?? null) ?? branchPullBias(nodeIds, nodeBias)
|
|
449
|
+
};
|
|
450
|
+
});
|
|
279
451
|
const assigned2 = assignBranchRowsAroundMain(branchData, splitRow);
|
|
280
452
|
for (const b of branchData) {
|
|
281
453
|
const row = assigned2.get(b.start) ?? splitRow;
|
|
@@ -328,6 +500,17 @@ var LAYOUT_TASK_TYPES = /* @__PURE__ */ new Set([
|
|
|
328
500
|
]);
|
|
329
501
|
var TASK_LAYOUT_MIN_W = 130;
|
|
330
502
|
var TASK_LAYOUT_MIN_H = 80;
|
|
503
|
+
var SUBPROCESS_LAYOUT_MIN_W = 160;
|
|
504
|
+
var SUBPROCESS_LAYOUT_MIN_H = 80;
|
|
505
|
+
function layoutMinSize(node) {
|
|
506
|
+
if (LAYOUT_TASK_TYPES.has(node.data.elementType)) {
|
|
507
|
+
return { w: TASK_LAYOUT_MIN_W, h: TASK_LAYOUT_MIN_H };
|
|
508
|
+
}
|
|
509
|
+
if (COLLAPSED_SUBPROCESS_TYPES.has(node.data.elementType) && !node.data.isExpanded) {
|
|
510
|
+
return { w: SUBPROCESS_LAYOUT_MIN_W, h: SUBPROCESS_LAYOUT_MIN_H };
|
|
511
|
+
}
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
331
514
|
function nW(node) {
|
|
332
515
|
return node.width ?? node.measured?.width ?? 120;
|
|
333
516
|
}
|
|
@@ -335,18 +518,68 @@ function nH(node) {
|
|
|
335
518
|
return node.height ?? node.measured?.height ?? 60;
|
|
336
519
|
}
|
|
337
520
|
function layoutW(node) {
|
|
338
|
-
|
|
521
|
+
const min = layoutMinSize(node);
|
|
522
|
+
return min ? Math.max(nW(node), min.w) : nW(node);
|
|
339
523
|
}
|
|
340
524
|
function layoutH(node) {
|
|
341
|
-
|
|
525
|
+
const min = layoutMinSize(node);
|
|
526
|
+
return min ? Math.max(nH(node), min.h) : nH(node);
|
|
527
|
+
}
|
|
528
|
+
var LABEL_RESERVE_TYPES = /* @__PURE__ */ new Set([
|
|
529
|
+
...LAYOUT_GATEWAY_TYPES,
|
|
530
|
+
"StartEvent",
|
|
531
|
+
"EndEvent",
|
|
532
|
+
"IntermediateCatchEvent",
|
|
533
|
+
"IntermediateThrowEvent"
|
|
534
|
+
]);
|
|
535
|
+
var LABEL_CHAR_W = 6.5;
|
|
536
|
+
var LABEL_RESERVE_MAX = 150;
|
|
537
|
+
function columnW(node) {
|
|
538
|
+
const base = layoutW(node);
|
|
539
|
+
if (!LABEL_RESERVE_TYPES.has(node.data.elementType)) return base;
|
|
540
|
+
const label = typeof node.data.label === "string" ? node.data.label : "";
|
|
541
|
+
if (!label) return base;
|
|
542
|
+
const reserve = Math.min(Math.ceil(label.length * LABEL_CHAR_W), LABEL_RESERVE_MAX);
|
|
543
|
+
return Math.max(base, reserve);
|
|
342
544
|
}
|
|
343
545
|
function applyLayoutMinSize(node) {
|
|
344
|
-
if (!
|
|
546
|
+
if (!layoutMinSize(node)) return node;
|
|
345
547
|
const w = layoutW(node);
|
|
346
548
|
const h = layoutH(node);
|
|
347
549
|
if (w === nW(node) && h === nH(node)) return node;
|
|
348
550
|
return { ...node, width: w, height: h, measured: { width: w, height: h } };
|
|
349
551
|
}
|
|
552
|
+
function computeRowMetrics(contextNodes, rows, includeRowZero) {
|
|
553
|
+
const vals = contextNodes.map((n) => rows.get(n.id) ?? 0);
|
|
554
|
+
const base = includeRowZero ? [0, ...vals] : vals;
|
|
555
|
+
const minRow = Math.min(...base);
|
|
556
|
+
const maxRow = Math.max(...base);
|
|
557
|
+
const rowH = /* @__PURE__ */ new Map();
|
|
558
|
+
for (let r = minRow; r <= maxRow; r++) rowH.set(r, ROW_HEIGHT);
|
|
559
|
+
for (const node of contextNodes) {
|
|
560
|
+
const r = rows.get(node.id) ?? 0;
|
|
561
|
+
rowH.set(r, Math.max(rowH.get(r) ?? ROW_HEIGHT, layoutH(node)));
|
|
562
|
+
}
|
|
563
|
+
const rowY = /* @__PURE__ */ new Map();
|
|
564
|
+
let y = 0;
|
|
565
|
+
for (let r = minRow; r <= maxRow; r++) {
|
|
566
|
+
rowY.set(r, y);
|
|
567
|
+
y += (rowH.get(r) ?? ROW_HEIGHT) + ROW_GAP;
|
|
568
|
+
}
|
|
569
|
+
return { minRow, maxRow, rowH, rowY, contentH: y - ROW_GAP };
|
|
570
|
+
}
|
|
571
|
+
function reanchorBoundaryEdges(edges, boundaryEvents, contentIds) {
|
|
572
|
+
if (boundaryEvents.length === 0) return edges;
|
|
573
|
+
const hostByBoundaryId = /* @__PURE__ */ new Map();
|
|
574
|
+
for (const be of boundaryEvents) {
|
|
575
|
+
if (be.data.attachedToRef) hostByBoundaryId.set(be.id, be.data.attachedToRef);
|
|
576
|
+
}
|
|
577
|
+
if (hostByBoundaryId.size === 0) return edges;
|
|
578
|
+
return edges.map((edge) => {
|
|
579
|
+
const host = hostByBoundaryId.get(edge.source);
|
|
580
|
+
return host && contentIds.has(host) ? { ...edge, source: host, sourceHandle: "source-bottom" } : edge;
|
|
581
|
+
});
|
|
582
|
+
}
|
|
350
583
|
var BOUNDARY_SPACING = 10;
|
|
351
584
|
function repositionBoundaryEvents(boundaryEvents, positionedContent) {
|
|
352
585
|
if (boundaryEvents.length === 0) return [];
|
|
@@ -403,7 +636,8 @@ function layoutSubProcess(children, edges) {
|
|
|
403
636
|
};
|
|
404
637
|
}
|
|
405
638
|
const contentIds = new Set(mainChildren.map((n) => n.id));
|
|
406
|
-
const
|
|
639
|
+
const graphEdges = reanchorBoundaryEdges(edges, boundaryEvents, contentIds);
|
|
640
|
+
const seqEdges = extractSeqEdges(graphEdges, contentIds);
|
|
407
641
|
const backIds = detectBackEdges([...contentIds], seqEdges);
|
|
408
642
|
const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
|
|
409
643
|
const columns = assignColumns([...contentIds], fwdEdges);
|
|
@@ -414,7 +648,7 @@ function layoutSubProcess(children, edges) {
|
|
|
414
648
|
for (let c = 0; c <= maxCol; c++) colW.set(c, 0);
|
|
415
649
|
for (const node of mainChildren) {
|
|
416
650
|
const c = columns.get(node.id) ?? 0;
|
|
417
|
-
colW.set(c, Math.max(colW.get(c) ?? 0,
|
|
651
|
+
colW.set(c, Math.max(colW.get(c) ?? 0, columnW(node)));
|
|
418
652
|
}
|
|
419
653
|
const colX = /* @__PURE__ */ new Map();
|
|
420
654
|
let cumX = 0;
|
|
@@ -423,20 +657,16 @@ function layoutSubProcess(children, edges) {
|
|
|
423
657
|
cumX += (colW.get(c) ?? 120) + COL_GAP;
|
|
424
658
|
}
|
|
425
659
|
const contentW = cumX - COL_GAP;
|
|
426
|
-
const
|
|
427
|
-
const minRow = Math.min(0, ...rowVals);
|
|
428
|
-
const maxRow = Math.max(0, ...rowVals);
|
|
429
|
-
const rowCount = maxRow - minRow + 1;
|
|
430
|
-
const contentH = rowCount * ROW_HEIGHT + Math.max(0, rowCount - 1) * ROW_GAP;
|
|
660
|
+
const rowMetrics = computeRowMetrics(mainChildren, rows, true);
|
|
431
661
|
const spW = Math.max(280, SP_PAD * 2 + contentW);
|
|
432
|
-
const spH = Math.max(160, SP_PAD * 2 + contentH);
|
|
662
|
+
const spH = Math.max(160, SP_PAD * 2 + rowMetrics.contentH);
|
|
433
663
|
const positionedChildren = mainChildren.map((node) => {
|
|
434
664
|
const c = columns.get(node.id) ?? 0;
|
|
435
665
|
const r = rows.get(node.id) ?? 0;
|
|
436
666
|
const lw = layoutW(node);
|
|
437
667
|
const lh = layoutH(node);
|
|
438
668
|
const colOffset = (colX.get(c) ?? 0) + (colW.get(c) ?? 120) / 2 - lw / 2;
|
|
439
|
-
const rowOffset = (r
|
|
669
|
+
const rowOffset = (rowMetrics.rowY.get(r) ?? 0) + (rowMetrics.rowH.get(r) ?? ROW_HEIGHT) / 2 - lh / 2;
|
|
440
670
|
return applyLayoutMinSize({ ...node, position: { x: SP_PAD + colOffset, y: SP_PAD + rowOffset } });
|
|
441
671
|
});
|
|
442
672
|
const positionedBoundaries = repositionBoundaryEvents(boundaryEvents, positionedChildren);
|
|
@@ -479,7 +709,8 @@ function layoutPool(pool, lanes, content, allEdges) {
|
|
|
479
709
|
nodeLaneId.set(node.id, lId);
|
|
480
710
|
}
|
|
481
711
|
const contentIds = new Set(mainContent.map((n) => n.id));
|
|
482
|
-
const
|
|
712
|
+
const graphEdges = reanchorBoundaryEdges(allEdges, boundaryEvents, contentIds);
|
|
713
|
+
const seqEdges = extractSeqEdges(graphEdges, contentIds);
|
|
483
714
|
const backIds = detectBackEdges([...contentIds], seqEdges);
|
|
484
715
|
const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
|
|
485
716
|
const columns = assignColumns([...contentIds], fwdEdges);
|
|
@@ -490,28 +721,67 @@ function layoutPool(pool, lanes, content, allEdges) {
|
|
|
490
721
|
if (!contentByLane.has(lId)) contentByLane.set(lId, []);
|
|
491
722
|
contentByLane.get(lId).push(node);
|
|
492
723
|
}
|
|
724
|
+
const laneStackIndex = /* @__PURE__ */ new Map();
|
|
725
|
+
sortedLanes.forEach((laneNode, index) => laneStackIndex.set(laneNode.id, index));
|
|
726
|
+
laneStackIndex.set("_pool_", sortedLanes.length);
|
|
727
|
+
const crossLanePull = /* @__PURE__ */ new Map();
|
|
728
|
+
for (const e of fwdEdges) {
|
|
729
|
+
const sLane = nodeLaneId.get(e.source) ?? "_pool_";
|
|
730
|
+
const tLane = nodeLaneId.get(e.target) ?? "_pool_";
|
|
731
|
+
if (sLane === tLane) continue;
|
|
732
|
+
const dir = Math.sign((laneStackIndex.get(tLane) ?? 0) - (laneStackIndex.get(sLane) ?? 0));
|
|
733
|
+
crossLanePull.set(e.source, (crossLanePull.get(e.source) ?? 0) + dir);
|
|
734
|
+
crossLanePull.set(e.target, (crossLanePull.get(e.target) ?? 0) - dir);
|
|
735
|
+
}
|
|
493
736
|
for (const [, laneNodes] of contentByLane) {
|
|
494
737
|
const laneNodeIds = new Set(laneNodes.map((n) => n.id));
|
|
495
738
|
const intraEdges = fwdEdges.filter(
|
|
496
739
|
(e) => laneNodeIds.has(e.source) && laneNodeIds.has(e.target)
|
|
497
740
|
);
|
|
498
741
|
const lanePairs = detectGatewayPairs(laneNodes, intraEdges);
|
|
499
|
-
const laneRows = assignRows(laneNodes, intraEdges, columns, lanePairs);
|
|
742
|
+
const laneRows = assignRows(laneNodes, intraEdges, columns, lanePairs, crossLanePull);
|
|
500
743
|
for (const [id, row] of laneRows) rows.set(id, row);
|
|
501
744
|
}
|
|
502
|
-
const
|
|
745
|
+
const seqTouched = /* @__PURE__ */ new Set();
|
|
746
|
+
for (const e of seqEdges) {
|
|
747
|
+
seqTouched.add(e.source);
|
|
748
|
+
seqTouched.add(e.target);
|
|
749
|
+
}
|
|
750
|
+
for (const [, laneNodes] of contentByLane) {
|
|
751
|
+
const eventSPs = laneNodes.filter(
|
|
752
|
+
(n) => n.data.elementType === "EventSubProcess" && !seqTouched.has(n.id)
|
|
753
|
+
);
|
|
754
|
+
if (eventSPs.length === 0) continue;
|
|
755
|
+
const eventSpIds = new Set(eventSPs.map((n) => n.id));
|
|
756
|
+
const otherRows = laneNodes.filter((n) => !eventSpIds.has(n.id)).map((n) => rows.get(n.id) ?? 0);
|
|
757
|
+
const laneMaxRow = otherRows.length > 0 ? Math.max(...otherRows) : -1;
|
|
758
|
+
for (const sp of eventSPs) rows.set(sp.id, laneMaxRow + 1);
|
|
759
|
+
}
|
|
760
|
+
const hasPoolDirectContent = [...nodeLaneId.values()].includes("_pool_");
|
|
761
|
+
const laneIds = hasLanes ? [...sortedLanes.map((l) => l.id), ...hasPoolDirectContent ? ["_pool_"] : []] : ["_pool_"];
|
|
503
762
|
const laneStats = /* @__PURE__ */ new Map();
|
|
504
763
|
for (const laneId of laneIds) {
|
|
505
|
-
const
|
|
506
|
-
if (
|
|
507
|
-
laneStats.set(laneId, {
|
|
764
|
+
const laneNodes = mainContent.filter((n) => nodeLaneId.get(n.id) === laneId);
|
|
765
|
+
if (laneNodes.length === 0) {
|
|
766
|
+
laneStats.set(laneId, {
|
|
767
|
+
minRow: 0,
|
|
768
|
+
maxRow: 0,
|
|
769
|
+
height: LANE_MIN_H,
|
|
770
|
+
contentH: ROW_HEIGHT,
|
|
771
|
+
rowH: /* @__PURE__ */ new Map([[0, ROW_HEIGHT]]),
|
|
772
|
+
rowY: /* @__PURE__ */ new Map([[0, 0]])
|
|
773
|
+
});
|
|
508
774
|
} else {
|
|
509
|
-
const
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
775
|
+
const m = computeRowMetrics(laneNodes, rows, false);
|
|
776
|
+
const height = Math.max(LANE_MIN_H, LANE_V_PAD * 2 + m.contentH);
|
|
777
|
+
laneStats.set(laneId, {
|
|
778
|
+
minRow: m.minRow,
|
|
779
|
+
maxRow: m.maxRow,
|
|
780
|
+
height,
|
|
781
|
+
contentH: m.contentH,
|
|
782
|
+
rowH: m.rowH,
|
|
783
|
+
rowY: m.rowY
|
|
784
|
+
});
|
|
515
785
|
}
|
|
516
786
|
}
|
|
517
787
|
const laneColumnLayouts = /* @__PURE__ */ new Map();
|
|
@@ -524,7 +794,7 @@ function layoutPool(pool, lanes, content, allEdges) {
|
|
|
524
794
|
for (const col of usedCols) colW.set(col, 0);
|
|
525
795
|
for (const node of laneNodes) {
|
|
526
796
|
const col = columns.get(node.id) ?? 0;
|
|
527
|
-
colW.set(col, Math.max(colW.get(col) ?? 0,
|
|
797
|
+
colW.set(col, Math.max(colW.get(col) ?? 0, columnW(node)));
|
|
528
798
|
}
|
|
529
799
|
const colX = /* @__PURE__ */ new Map();
|
|
530
800
|
let laneCumX = 0;
|
|
@@ -571,9 +841,8 @@ function layoutPool(pool, lanes, content, allEdges) {
|
|
|
571
841
|
const lw = layoutW(node);
|
|
572
842
|
const lh = layoutH(node);
|
|
573
843
|
const colOffset = (laneLayout?.colX.get(c) ?? 0) + (laneLayout?.colW.get(c) ?? 120) / 2 - lw / 2;
|
|
574
|
-
const rowOffset = (
|
|
575
|
-
const
|
|
576
|
-
const vertTopOffset = Math.max(LANE_V_PAD, (stat.height - contentH_stat) / 2);
|
|
844
|
+
const rowOffset = (stat.rowY.get(r) ?? 0) + (stat.rowH.get(r) ?? ROW_HEIGHT) / 2 - lh / 2;
|
|
845
|
+
const vertTopOffset = Math.max(LANE_V_PAD, (stat.height - stat.contentH) / 2);
|
|
577
846
|
const isLaneChild = hasLanes && !!node.parentId && node.parentId !== pool.id;
|
|
578
847
|
const x = isLaneChild ? LANE_LABEL_W + LANE_H_PAD + colOffset : POOL_H_PAD + colOffset;
|
|
579
848
|
const y = isLaneChild ? vertTopOffset + rowOffset : lYOff + vertTopOffset + rowOffset;
|
|
@@ -709,6 +978,17 @@ function layoutPool(pool, lanes, content, allEdges) {
|
|
|
709
978
|
height: poolH
|
|
710
979
|
};
|
|
711
980
|
}
|
|
981
|
+
function layoutVirtualContext(contextNodes, allEdges, offsetY) {
|
|
982
|
+
if (contextNodes.length === 0) return [];
|
|
983
|
+
const virtualPool = {
|
|
984
|
+
id: "__bpmn_virtual_pool__"};
|
|
985
|
+
const result = layoutPool(virtualPool, [], contextNodes, allEdges);
|
|
986
|
+
if (offsetY === 0) return result.nodes;
|
|
987
|
+
return result.nodes.map((n) => ({
|
|
988
|
+
...n,
|
|
989
|
+
position: { x: n.position.x, y: n.position.y + offsetY }
|
|
990
|
+
}));
|
|
991
|
+
}
|
|
712
992
|
function absolutePos(nodeId, byId, cache) {
|
|
713
993
|
const cached = cache.get(nodeId);
|
|
714
994
|
if (cached) return cached;
|
|
@@ -789,9 +1069,75 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
|
|
|
789
1069
|
const cache = /* @__PURE__ */ new Map();
|
|
790
1070
|
const abs = (id) => absolutePos(id, byId, cache);
|
|
791
1071
|
const sortedLanes = [...laneIds].map((id) => byId.get(id)).filter((n) => !!n).sort((a, b) => abs(a.id).y - abs(b.id).y);
|
|
792
|
-
|
|
1072
|
+
const contentInfos = layoutNodes.filter((n) => !poolIds.has(n.id) && !laneIds.has(n.id)).map((n) => {
|
|
1073
|
+
const p = abs(n.id);
|
|
1074
|
+
return { id: n.id, x: p.x, y: p.y, w: nW(n), h: nH(n), poolId: poolOf(n, poolIds, byId) ?? null };
|
|
1075
|
+
});
|
|
1076
|
+
const findClearX = (desiredX, y0, y1, minX, maxX, excludeA, excludeB, poolId) => {
|
|
1077
|
+
if (minX > maxX) return desiredX;
|
|
1078
|
+
const clampX = (x) => Math.min(maxX, Math.max(minX, x));
|
|
1079
|
+
const blockers = contentInfos.filter(
|
|
1080
|
+
(c) => c.poolId === poolId && c.id !== excludeA && c.id !== excludeB && c.y < y1 && c.y + c.h > y0
|
|
1081
|
+
);
|
|
1082
|
+
const isFree = (x) => blockers.every((c) => x < c.x - 4 || x > c.x + c.w + 4);
|
|
1083
|
+
const clamped = clampX(desiredX);
|
|
1084
|
+
if (isFree(clamped)) return clamped;
|
|
1085
|
+
const candidates = [];
|
|
1086
|
+
for (const c of blockers) candidates.push(c.x - 8, c.x + c.w + 8);
|
|
1087
|
+
const free = candidates.map(clampX).filter(isFree).sort((a, b) => Math.abs(a - desiredX) - Math.abs(b - desiredX));
|
|
1088
|
+
return free.length > 0 ? free[0] : clamped;
|
|
1089
|
+
};
|
|
1090
|
+
const backEdgeOrder = /* @__PURE__ */ new Map();
|
|
1091
|
+
for (const edge of [...edges].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) {
|
|
1092
|
+
if (backEdgeIds.has(edge.id)) backEdgeOrder.set(edge.id, backEdgeOrder.size);
|
|
1093
|
+
}
|
|
1094
|
+
const routeMessageFlow = (edge) => {
|
|
1095
|
+
const src = byId.get(edge.source);
|
|
1096
|
+
const tgt = byId.get(edge.target);
|
|
1097
|
+
if (!src || !tgt) return null;
|
|
1098
|
+
const srcPoolId = poolOf(src, poolIds, byId);
|
|
1099
|
+
const tgtPoolId = poolOf(tgt, poolIds, byId);
|
|
1100
|
+
if (!srcPoolId || !tgtPoolId || srcPoolId === tgtPoolId) return null;
|
|
1101
|
+
const srcPoolNode = byId.get(srcPoolId);
|
|
1102
|
+
const tgtPoolNode = byId.get(tgtPoolId);
|
|
1103
|
+
if (!srcPoolNode || !tgtPoolNode) return null;
|
|
1104
|
+
const sAbs = abs(src.id);
|
|
1105
|
+
const tAbs = abs(tgt.id);
|
|
1106
|
+
const sCX = sAbs.x + nW(src) / 2;
|
|
1107
|
+
const tCX = tAbs.x + nW(tgt) / 2;
|
|
1108
|
+
const srcPoolAbs = abs(srcPoolId);
|
|
1109
|
+
const tgtPoolAbs = abs(tgtPoolId);
|
|
1110
|
+
const srcPoolBottom = srcPoolAbs.y + nH(srcPoolNode);
|
|
1111
|
+
const tgtPoolBottom = tgtPoolAbs.y + nH(tgtPoolNode);
|
|
1112
|
+
if (tgtPoolAbs.y >= srcPoolBottom) {
|
|
1113
|
+
const corridorY = (srcPoolBottom + tgtPoolAbs.y) / 2;
|
|
1114
|
+
return [
|
|
1115
|
+
{ x: sCX, y: sAbs.y + nH(src) },
|
|
1116
|
+
{ x: sCX, y: corridorY },
|
|
1117
|
+
{ x: tCX, y: corridorY },
|
|
1118
|
+
{ x: tCX, y: tAbs.y }
|
|
1119
|
+
];
|
|
1120
|
+
}
|
|
1121
|
+
if (srcPoolAbs.y >= tgtPoolBottom) {
|
|
1122
|
+
const corridorY = (tgtPoolBottom + srcPoolAbs.y) / 2;
|
|
1123
|
+
return [
|
|
1124
|
+
{ x: sCX, y: sAbs.y },
|
|
1125
|
+
{ x: sCX, y: corridorY },
|
|
1126
|
+
{ x: tCX, y: corridorY },
|
|
1127
|
+
{ x: tCX, y: tAbs.y + nH(tgt) }
|
|
1128
|
+
];
|
|
1129
|
+
}
|
|
1130
|
+
return null;
|
|
1131
|
+
};
|
|
1132
|
+
const routed = edges.map((edge) => {
|
|
793
1133
|
const edgeType = edge.data?.edgeType ?? edge.type;
|
|
794
1134
|
if (edgeType !== "sequenceFlow") {
|
|
1135
|
+
if (edgeType === "messageFlow") {
|
|
1136
|
+
const pts = routeMessageFlow(edge);
|
|
1137
|
+
if (pts) {
|
|
1138
|
+
return { ...edge, data: { ...edge.data, routingPoints: pts } };
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
795
1141
|
const d = { ...edge.data };
|
|
796
1142
|
delete d.routingPoints;
|
|
797
1143
|
return { ...edge, data: d };
|
|
@@ -816,7 +1162,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
|
|
|
816
1162
|
}
|
|
817
1163
|
let routingPoints;
|
|
818
1164
|
if (backEdgeIds.has(edge.id)) {
|
|
819
|
-
const
|
|
1165
|
+
const spanMinX = Math.min(tAbs.x, sAbs.x) - EDGE_ROUTE_PAD;
|
|
1166
|
+
const spanMaxX = Math.max(tAbs.x + tW, sAbs.x + sW) + EDGE_ROUTE_PAD;
|
|
1167
|
+
let topMost = Math.min(sAbs.y, tAbs.y);
|
|
1168
|
+
for (const cand of contentInfos) {
|
|
1169
|
+
if (cand.poolId !== srcPool) continue;
|
|
1170
|
+
if (cand.x + cand.w < spanMinX || cand.x > spanMaxX) continue;
|
|
1171
|
+
topMost = Math.min(topMost, cand.y);
|
|
1172
|
+
}
|
|
1173
|
+
const stagger = (backEdgeOrder.get(edge.id) ?? 0) * 14;
|
|
1174
|
+
const topY = topMost - BACK_EDGE_CLEARANCE - stagger;
|
|
820
1175
|
routingPoints = [
|
|
821
1176
|
{ x: sAbs.x + sW, y: sCY },
|
|
822
1177
|
{ x: sAbs.x + sW + EDGE_ROUTE_PAD, y: sCY },
|
|
@@ -830,7 +1185,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
|
|
|
830
1185
|
const tgtLane = byId.get(laneOf(tgt, laneIds, byId) ?? "");
|
|
831
1186
|
if (srcLane && tgtLane && laneIds.has(srcLane.id) && laneIds.has(tgtLane.id)) {
|
|
832
1187
|
const goingDown = tAbs.y > sAbs.y;
|
|
833
|
-
const sharedX =
|
|
1188
|
+
const sharedX = findClearX(
|
|
1189
|
+
gapMidX(sAbs, sW, tAbs, tW),
|
|
1190
|
+
Math.min(sCY, tCY),
|
|
1191
|
+
Math.max(sCY, tCY),
|
|
1192
|
+
sAbs.x + sW + 6,
|
|
1193
|
+
tAbs.x - EDGE_ROUTE_PAD - 4,
|
|
1194
|
+
src.id,
|
|
1195
|
+
tgt.id,
|
|
1196
|
+
srcPool
|
|
1197
|
+
);
|
|
834
1198
|
const srcLaneIdx = sortedLanes.findIndex((l) => l.id === srcLane.id);
|
|
835
1199
|
const tgtLaneIdx = sortedLanes.findIndex((l) => l.id === tgtLane.id);
|
|
836
1200
|
const pts = [];
|
|
@@ -865,7 +1229,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
|
|
|
865
1229
|
}
|
|
866
1230
|
routingPoints = pts;
|
|
867
1231
|
} else {
|
|
868
|
-
const sharedX =
|
|
1232
|
+
const sharedX = findClearX(
|
|
1233
|
+
gapMidX(sAbs, sW, tAbs, tW),
|
|
1234
|
+
Math.min(sCY, tCY),
|
|
1235
|
+
Math.max(sCY, tCY),
|
|
1236
|
+
sAbs.x + sW + 6,
|
|
1237
|
+
tAbs.x - 6,
|
|
1238
|
+
src.id,
|
|
1239
|
+
tgt.id,
|
|
1240
|
+
srcPool
|
|
1241
|
+
);
|
|
869
1242
|
routingPoints = [
|
|
870
1243
|
{ x: sAbs.x + sW, y: sCY },
|
|
871
1244
|
{ x: sharedX, y: sCY },
|
|
@@ -895,7 +1268,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
|
|
|
895
1268
|
const midX = routeMidX(sAbs, sW, tAbs, tW);
|
|
896
1269
|
const exitX = sAbs.x + sW;
|
|
897
1270
|
const entryX = tAbs.x;
|
|
898
|
-
const corridorX =
|
|
1271
|
+
const corridorX = findClearX(
|
|
1272
|
+
Math.max(midX, exitX + EDGE_ROUTE_PAD),
|
|
1273
|
+
Math.min(sCY, tCY),
|
|
1274
|
+
Math.max(sCY, tCY),
|
|
1275
|
+
exitX + 6,
|
|
1276
|
+
entryX - 6,
|
|
1277
|
+
src.id,
|
|
1278
|
+
tgt.id,
|
|
1279
|
+
srcPool
|
|
1280
|
+
);
|
|
899
1281
|
routingPoints = [
|
|
900
1282
|
{ x: exitX, y: sCY },
|
|
901
1283
|
{ x: corridorX, y: sCY },
|
|
@@ -906,9 +1288,81 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
|
|
|
906
1288
|
}
|
|
907
1289
|
return { ...edge, data: { ...edge.data, routingPoints } };
|
|
908
1290
|
});
|
|
1291
|
+
return spreadVerticalChannels(routed);
|
|
1292
|
+
}
|
|
1293
|
+
var CHANNEL_GAP = 10;
|
|
1294
|
+
var CHANNEL_CLUSTER_TOL = 8;
|
|
1295
|
+
function spreadVerticalChannels(edges) {
|
|
1296
|
+
const runs = [];
|
|
1297
|
+
edges.forEach((edge, edgeIdx) => {
|
|
1298
|
+
const pts = edge.data?.routingPoints;
|
|
1299
|
+
if (!pts || pts.length < 4) return;
|
|
1300
|
+
let i = 1;
|
|
1301
|
+
while (i < pts.length) {
|
|
1302
|
+
let j = i;
|
|
1303
|
+
while (j < pts.length && Math.abs(pts[j].x - pts[j - 1].x) <= 0.5) j++;
|
|
1304
|
+
if (j > i) {
|
|
1305
|
+
const fromIdx = i - 1;
|
|
1306
|
+
const toIdx = j - 1;
|
|
1307
|
+
if (fromIdx > 0 && toIdx < pts.length - 1) {
|
|
1308
|
+
const ys = pts.slice(fromIdx, toIdx + 1).map((p) => p.y);
|
|
1309
|
+
const y0 = Math.min(...ys);
|
|
1310
|
+
const y1 = Math.max(...ys);
|
|
1311
|
+
if (y1 - y0 >= 4) {
|
|
1312
|
+
runs.push({ edgeIdx, fromIdx, toIdx, x: pts[fromIdx].x, y0, y1 });
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
i = j + 1;
|
|
1316
|
+
} else {
|
|
1317
|
+
i++;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
if (runs.length < 2) return edges;
|
|
1322
|
+
runs.sort((a, b) => a.x - b.x);
|
|
1323
|
+
const clusters = [];
|
|
1324
|
+
for (const run of runs) {
|
|
1325
|
+
const last = clusters[clusters.length - 1];
|
|
1326
|
+
if (last && run.x - last[0].x <= CHANNEL_CLUSTER_TOL) last.push(run);
|
|
1327
|
+
else clusters.push([run]);
|
|
1328
|
+
}
|
|
1329
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
1330
|
+
for (const cluster of clusters) {
|
|
1331
|
+
if (cluster.length < 2) continue;
|
|
1332
|
+
const colliding = cluster.filter(
|
|
1333
|
+
(run) => cluster.some((other) => other !== run && run.y0 < other.y1 && other.y0 < run.y1)
|
|
1334
|
+
);
|
|
1335
|
+
if (colliding.length < 2) continue;
|
|
1336
|
+
const ordered = [...colliding].sort((a, b) => {
|
|
1337
|
+
const idA = edges[a.edgeIdx].id;
|
|
1338
|
+
const idB = edges[b.edgeIdx].id;
|
|
1339
|
+
return idA < idB ? -1 : idA > idB ? 1 : a.fromIdx - b.fromIdx;
|
|
1340
|
+
});
|
|
1341
|
+
ordered.forEach((run, k) => {
|
|
1342
|
+
const off = Math.round((k - (ordered.length - 1) / 2) * CHANNEL_GAP);
|
|
1343
|
+
if (off !== 0) offsets.set(run, off);
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
if (offsets.size === 0) return edges;
|
|
1347
|
+
const runsByEdge = /* @__PURE__ */ new Map();
|
|
1348
|
+
for (const [run, off] of offsets) {
|
|
1349
|
+
if (!runsByEdge.has(run.edgeIdx)) runsByEdge.set(run.edgeIdx, []);
|
|
1350
|
+
runsByEdge.get(run.edgeIdx).push(run);
|
|
1351
|
+
}
|
|
1352
|
+
return edges.map((edge, edgeIdx) => {
|
|
1353
|
+
const edgeRuns = runsByEdge.get(edgeIdx);
|
|
1354
|
+
if (!edgeRuns) return edge;
|
|
1355
|
+
const pts = (edge.data?.routingPoints).map((p) => ({ ...p }));
|
|
1356
|
+
for (const run of edgeRuns) {
|
|
1357
|
+
const off = offsets.get(run);
|
|
1358
|
+
for (let p = run.fromIdx; p <= run.toIdx; p++) pts[p].x += off;
|
|
1359
|
+
}
|
|
1360
|
+
return { ...edge, data: { ...edge.data, routingPoints: pts } };
|
|
1361
|
+
});
|
|
909
1362
|
}
|
|
910
1363
|
var ARTIFACT_ABOVE_GAP = 16;
|
|
911
1364
|
var ARTIFACT_H_SPACING = 12;
|
|
1365
|
+
var ARTIFACT_EDGE_PAD = 4;
|
|
912
1366
|
function positionArtifacts(artifacts, resultNodes, edges, originalNodes) {
|
|
913
1367
|
if (artifacts.length === 0) return [];
|
|
914
1368
|
const byId = new Map(resultNodes.map((n) => [n.id, n]));
|
|
@@ -949,11 +1403,21 @@ function positionArtifacts(artifacts, resultNodes, edges, originalNodes) {
|
|
|
949
1403
|
let desiredAbsX = connAbsP.x + nW(connNode) / 2 - totalW / 2;
|
|
950
1404
|
for (const artifact of arts) {
|
|
951
1405
|
const parentAbsP = artifact.parentId ? absPos(artifact.parentId) : { x: 0, y: 0 };
|
|
1406
|
+
let relY = desiredAbsY - parentAbsP.y;
|
|
1407
|
+
const parentNode = artifact.parentId ? byId.get(artifact.parentId) : void 0;
|
|
1408
|
+
if (parentNode) {
|
|
1409
|
+
if (relY < ARTIFACT_EDGE_PAD) {
|
|
1410
|
+
const belowAbsY = connAbsP.y + nH(connNode) + ARTIFACT_ABOVE_GAP;
|
|
1411
|
+
relY = belowAbsY - parentAbsP.y;
|
|
1412
|
+
}
|
|
1413
|
+
relY = Math.min(relY, nH(parentNode) - nH(artifact) - ARTIFACT_EDGE_PAD);
|
|
1414
|
+
relY = Math.max(relY, ARTIFACT_EDGE_PAD);
|
|
1415
|
+
}
|
|
952
1416
|
positioned.push({
|
|
953
1417
|
...artifact,
|
|
954
1418
|
position: {
|
|
955
1419
|
x: desiredAbsX - parentAbsP.x,
|
|
956
|
-
y:
|
|
1420
|
+
y: relY
|
|
957
1421
|
}
|
|
958
1422
|
});
|
|
959
1423
|
desiredAbsX += nW(artifact) + ARTIFACT_H_SPACING;
|
|
@@ -1018,9 +1482,17 @@ async function bpmnCustomLayout(nodes, edges) {
|
|
|
1018
1482
|
const mainNodes = nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType));
|
|
1019
1483
|
const pools = mainNodes.filter((n) => n.data.elementType === "Pool");
|
|
1020
1484
|
const lanes = mainNodes.filter((n) => n.data.elementType === "Lane");
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
)
|
|
1485
|
+
const nodeDepthById = /* @__PURE__ */ new Map();
|
|
1486
|
+
const mainById = new Map(mainNodes.map((n) => [n.id, n]));
|
|
1487
|
+
const depthOf = (node) => {
|
|
1488
|
+
const cached = nodeDepthById.get(node.id);
|
|
1489
|
+
if (cached != null) return cached;
|
|
1490
|
+
const parent = node.parentId ? mainById.get(node.parentId) : void 0;
|
|
1491
|
+
const depth = parent ? depthOf(parent) + 1 : 0;
|
|
1492
|
+
nodeDepthById.set(node.id, depth);
|
|
1493
|
+
return depth;
|
|
1494
|
+
};
|
|
1495
|
+
const expandedSubProcesses = mainNodes.filter((n) => COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType) && n.data.isExpanded).sort((a, b) => depthOf(b) - depthOf(a));
|
|
1024
1496
|
const subProcessLayouts = /* @__PURE__ */ new Map();
|
|
1025
1497
|
const workingMainNodes = [...mainNodes];
|
|
1026
1498
|
for (const sp of expandedSubProcesses) {
|
|
@@ -1038,15 +1510,30 @@ async function bpmnCustomLayout(nodes, edges) {
|
|
|
1038
1510
|
}
|
|
1039
1511
|
}
|
|
1040
1512
|
const content = workingMainNodes.filter((n) => {
|
|
1513
|
+
if (n.data.elementType === "BoundaryEvent") return false;
|
|
1041
1514
|
if (!LAYOUT_CONTAINER_TYPES.has(n.data.elementType)) return true;
|
|
1042
1515
|
if (COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType)) return true;
|
|
1043
1516
|
return false;
|
|
1044
1517
|
});
|
|
1045
1518
|
if (pools.length === 0) {
|
|
1046
|
-
const
|
|
1047
|
-
const
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1519
|
+
const isSpChild = (n) => !!n.parentId && subProcessLayouts.has(n.parentId);
|
|
1520
|
+
const freeContent = content.filter((n) => !isSpChild(n));
|
|
1521
|
+
const freeBoundaries = workingMainNodes.filter(
|
|
1522
|
+
(n) => n.data.elementType === "BoundaryEvent" && !isSpChild(n)
|
|
1523
|
+
);
|
|
1524
|
+
const resultNodes2 = layoutVirtualContext([...freeContent, ...freeBoundaries], edges, 0);
|
|
1525
|
+
for (const [, spLayout] of subProcessLayouts) {
|
|
1526
|
+
for (const child of spLayout.children) resultNodes2.push(child);
|
|
1527
|
+
}
|
|
1528
|
+
const contentIds = new Set(freeContent.map((n) => n.id));
|
|
1529
|
+
const seqEdges = extractSeqEdges(edges, contentIds);
|
|
1530
|
+
const backIds = detectBackEdges([...contentIds], seqEdges);
|
|
1531
|
+
const routedEdges2 = routeEdges(edges, resultNodes2, backIds, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set());
|
|
1532
|
+
const posArtifacts = positionArtifacts(artifacts, resultNodes2, edges, nodes);
|
|
1533
|
+
return {
|
|
1534
|
+
nodes: sortNodesParentFirst([...resultNodes2, ...posArtifacts]),
|
|
1535
|
+
edges: routedEdges2
|
|
1536
|
+
};
|
|
1050
1537
|
}
|
|
1051
1538
|
const poolIds = new Set(pools.map((p) => p.id));
|
|
1052
1539
|
const allLaneIds = new Set(lanes.map((l) => l.id));
|
|
@@ -1089,30 +1576,14 @@ async function bpmnCustomLayout(nodes, edges) {
|
|
|
1089
1576
|
const layoutted = new Set(resultNodes.map((n) => n.id));
|
|
1090
1577
|
const freeNodes = workingMainNodes.filter((n) => !layoutted.has(n.id));
|
|
1091
1578
|
if (freeNodes.length > 0) {
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
(e) => freeNodeIds.has(e.source) && freeNodeIds.has(e.target)
|
|
1579
|
+
const freeFlow = freeNodes.filter(
|
|
1580
|
+
(n) => n.data.elementType === "BoundaryEvent" || !LAYOUT_CONTAINER_TYPES.has(n.data.elementType) || COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType)
|
|
1095
1581
|
);
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
const offsetY = stackY;
|
|
1100
|
-
for (const node of elkFree.nodes) {
|
|
1101
|
-
resultNodes.push({
|
|
1102
|
-
...node,
|
|
1103
|
-
position: { x: node.position.x, y: node.position.y + offsetY }
|
|
1104
|
-
});
|
|
1105
|
-
}
|
|
1106
|
-
const freeEdgeIds = new Set(freeEdges.map((e) => e.id));
|
|
1107
|
-
const elkEdgeMap = new Map(elkFree.edges.map((e) => [e.id, e]));
|
|
1108
|
-
for (let i = 0; i < edges.length; i++) {
|
|
1109
|
-
if (freeEdgeIds.has(edges[i].id) && elkEdgeMap.has(edges[i].id)) {
|
|
1110
|
-
edges[i] = elkEdgeMap.get(edges[i].id);
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
} catch {
|
|
1114
|
-
for (const node of freeNodes) resultNodes.push(node);
|
|
1582
|
+
const freeRest = freeNodes.filter((n) => !freeFlow.includes(n));
|
|
1583
|
+
for (const node of layoutVirtualContext(freeFlow, edges, stackY)) {
|
|
1584
|
+
resultNodes.push(node);
|
|
1115
1585
|
}
|
|
1586
|
+
for (const node of freeRest) resultNodes.push(node);
|
|
1116
1587
|
}
|
|
1117
1588
|
const routedEdges = routeEdges(edges, resultNodes, allBackEdgeIds, allLaneIds, poolIds);
|
|
1118
1589
|
const positionedArtifacts = positionArtifacts(artifacts, resultNodes, edges, nodes);
|
|
@@ -1133,6 +1604,6 @@ function applyBpmnLayoutResult(state, result) {
|
|
|
1133
1604
|
return applyLayoutResultToDiagram(state, result);
|
|
1134
1605
|
}
|
|
1135
1606
|
|
|
1136
|
-
export { applyBpmnLayoutResult, bpmnCustomLayout, bpmnDagreLayout };
|
|
1607
|
+
export { applyBpmnLayoutResult, bpmnCustomLayout, bpmnDagreLayout, bpmnElkLayout };
|
|
1137
1608
|
//# sourceMappingURL=index.js.map
|
|
1138
1609
|
//# sourceMappingURL=index.js.map
|