bpmn-elk-layout 1.1.2 → 1.2.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/dist/bin/bpmn-elk-layout.js +1835 -1832
- package/dist/bin/bpmn-elk-layout.js.map +1 -1
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2472 -2495
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2472 -2495
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.js +2468 -2491
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +2467 -2490
- package/dist/index.node.mjs.map +1 -1
- package/package.json +4 -1
|
@@ -26,10 +26,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
mod
|
|
27
27
|
));
|
|
28
28
|
|
|
29
|
-
// ../../node_modules/.bun/tsup@8.5.1+
|
|
29
|
+
// ../../node_modules/.bun/tsup@8.5.1+e293d8ed4487f686/node_modules/tsup/assets/cjs_shims.js
|
|
30
30
|
var getImportMetaUrl, importMetaUrl;
|
|
31
31
|
var init_cjs_shims = __esm({
|
|
32
|
-
"../../node_modules/.bun/tsup@8.5.1+
|
|
32
|
+
"../../node_modules/.bun/tsup@8.5.1+e293d8ed4487f686/node_modules/tsup/assets/cjs_shims.js"() {
|
|
33
33
|
"use strict";
|
|
34
34
|
getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
35
35
|
importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
@@ -42,7 +42,11 @@ function enableCliMode() {
|
|
|
42
42
|
}
|
|
43
43
|
function isDebugEnabled() {
|
|
44
44
|
if (cliMode) return false;
|
|
45
|
-
|
|
45
|
+
try {
|
|
46
|
+
return typeof process !== "undefined" && typeof process.env !== "undefined" && process.env?.["DEBUG"] === "true";
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
46
50
|
}
|
|
47
51
|
var cliMode;
|
|
48
52
|
var init_debug = __esm({
|
|
@@ -174,763 +178,103 @@ var init_size_calculator = __esm({
|
|
|
174
178
|
}
|
|
175
179
|
});
|
|
176
180
|
|
|
177
|
-
// src/layout/
|
|
178
|
-
function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
return length;
|
|
191
|
-
}
|
|
192
|
-
function segmentIntersectsRect(p1, p2, rect) {
|
|
193
|
-
const margin = 5;
|
|
194
|
-
const left = rect.x - margin;
|
|
195
|
-
const right = rect.x + rect.width + margin;
|
|
196
|
-
const top = rect.y - margin;
|
|
197
|
-
const bottom = rect.y + rect.height + margin;
|
|
198
|
-
if (p1.x < left && p2.x < left || p1.x > right && p2.x > right) return false;
|
|
199
|
-
if (p1.y < top && p2.y < top || p1.y > bottom && p2.y > bottom) return false;
|
|
200
|
-
if (Math.abs(p1.x - p2.x) < 1) {
|
|
201
|
-
const x = p1.x;
|
|
202
|
-
const minY = Math.min(p1.y, p2.y);
|
|
203
|
-
const maxY = Math.max(p1.y, p2.y);
|
|
204
|
-
return x >= left && x <= right && maxY >= top && minY <= bottom;
|
|
205
|
-
}
|
|
206
|
-
if (Math.abs(p1.y - p2.y) < 1) {
|
|
207
|
-
const y = p1.y;
|
|
208
|
-
const minX = Math.min(p1.x, p2.x);
|
|
209
|
-
const maxX = Math.max(p1.x, p2.x);
|
|
210
|
-
return y >= top && y <= bottom && maxX >= left && minX <= right;
|
|
211
|
-
}
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
function segmentCrossesNode(p1, p2, node) {
|
|
215
|
-
const margin = 5;
|
|
216
|
-
const nodeLeft = node.x - margin;
|
|
217
|
-
const nodeRight = node.x + node.width + margin;
|
|
218
|
-
const nodeTop = node.y - margin;
|
|
219
|
-
const nodeBottom = node.y + node.height + margin;
|
|
220
|
-
if (Math.abs(p1.y - p2.y) < 1) {
|
|
221
|
-
const segY = p1.y;
|
|
222
|
-
const segMinX = Math.min(p1.x, p2.x);
|
|
223
|
-
const segMaxX = Math.max(p1.x, p2.x);
|
|
224
|
-
if (segY > nodeTop && segY < nodeBottom) {
|
|
225
|
-
if (segMinX < nodeRight && segMaxX > nodeLeft) {
|
|
226
|
-
const interiorLeft = node.x + margin;
|
|
227
|
-
const interiorRight = node.x + node.width - margin;
|
|
228
|
-
if (segMinX < interiorRight && segMaxX > interiorLeft) {
|
|
229
|
-
return true;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (Math.abs(p1.x - p2.x) < 1) {
|
|
235
|
-
const segX = p1.x;
|
|
236
|
-
const segMinY = Math.min(p1.y, p2.y);
|
|
237
|
-
const segMaxY = Math.max(p1.y, p2.y);
|
|
238
|
-
if (segX > nodeLeft && segX < nodeRight) {
|
|
239
|
-
if (segMinY < nodeBottom && segMaxY > nodeTop) {
|
|
240
|
-
const interiorTop = node.y + margin;
|
|
241
|
-
const interiorBottom = node.y + node.height - margin;
|
|
242
|
-
if (segMinY < interiorBottom && segMaxY > interiorTop) {
|
|
243
|
-
return true;
|
|
181
|
+
// src/layout/post-processing/boundary-event/collector.ts
|
|
182
|
+
function collectBoundaryEventInfo(graph) {
|
|
183
|
+
const info = /* @__PURE__ */ new Map();
|
|
184
|
+
const edgeMap = /* @__PURE__ */ new Map();
|
|
185
|
+
const collectEdges = (node) => {
|
|
186
|
+
if (node.edges) {
|
|
187
|
+
for (const edge of node.edges) {
|
|
188
|
+
const source = edge.sources?.[0];
|
|
189
|
+
const target = edge.targets?.[0];
|
|
190
|
+
if (!source || !target) continue;
|
|
191
|
+
if (!edgeMap.has(source)) {
|
|
192
|
+
edgeMap.set(source, []);
|
|
244
193
|
}
|
|
194
|
+
edgeMap.get(source).push(target);
|
|
245
195
|
}
|
|
246
196
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
function scoreRoute(start, bendPoints, end, obstacles) {
|
|
251
|
-
let score = 0;
|
|
252
|
-
const crossingPenalty = 1e3;
|
|
253
|
-
const lengthWeight = 0.1;
|
|
254
|
-
const path = [start, ...bendPoints, end];
|
|
255
|
-
for (const obs of obstacles) {
|
|
256
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
257
|
-
const p1 = path[i];
|
|
258
|
-
const p2 = path[i + 1];
|
|
259
|
-
if (p1 && p2 && segmentIntersectsRect(p1, p2, obs)) {
|
|
260
|
-
score += crossingPenalty;
|
|
197
|
+
if (node.children) {
|
|
198
|
+
for (const child of node.children) {
|
|
199
|
+
collectEdges(child);
|
|
261
200
|
}
|
|
262
201
|
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
202
|
+
};
|
|
203
|
+
const collectBoundaryEvents = (node) => {
|
|
204
|
+
if (node.boundaryEvents) {
|
|
205
|
+
const totalBoundaries = node.boundaryEvents.length;
|
|
206
|
+
node.boundaryEvents.forEach((be, index) => {
|
|
207
|
+
const targets = edgeMap.get(be.id) || [];
|
|
208
|
+
info.set(be.id, {
|
|
209
|
+
attachedToRef: be.attachedToRef,
|
|
210
|
+
targets,
|
|
211
|
+
boundaryIndex: index,
|
|
212
|
+
totalBoundaries
|
|
213
|
+
});
|
|
214
|
+
});
|
|
269
215
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
function findClearVerticalPath(x, startY, endY, obstacles) {
|
|
274
|
-
const minY = Math.min(startY, endY);
|
|
275
|
-
const maxY = Math.max(startY, endY);
|
|
276
|
-
const margin = 10;
|
|
277
|
-
for (const obs of obstacles) {
|
|
278
|
-
const obsLeft = obs.x - margin;
|
|
279
|
-
const obsRight = obs.x + obs.width + margin;
|
|
280
|
-
const obsTop = obs.y;
|
|
281
|
-
const obsBottom = obs.y + obs.height;
|
|
282
|
-
if (x >= obsLeft && x <= obsRight) {
|
|
283
|
-
if (obsBottom > minY && obsTop < maxY) {
|
|
284
|
-
const spaceAbove = obsTop - minY;
|
|
285
|
-
const spaceBelow = maxY - obsBottom;
|
|
286
|
-
if (spaceBelow > spaceAbove && obsBottom + margin < maxY) {
|
|
287
|
-
return obsBottom + margin;
|
|
288
|
-
} else if (obsTop - margin > minY) {
|
|
289
|
-
return obsTop - margin;
|
|
290
|
-
}
|
|
216
|
+
if (node.children) {
|
|
217
|
+
for (const child of node.children) {
|
|
218
|
+
collectBoundaryEvents(child);
|
|
291
219
|
}
|
|
292
220
|
}
|
|
221
|
+
};
|
|
222
|
+
for (const child of graph.children ?? []) {
|
|
223
|
+
collectEdges(child);
|
|
224
|
+
collectBoundaryEvents(child);
|
|
293
225
|
}
|
|
294
|
-
return
|
|
295
|
-
}
|
|
296
|
-
function lineIntersection(p1, p2, p3, p4) {
|
|
297
|
-
const denom = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);
|
|
298
|
-
if (Math.abs(denom) < 1e-4) return null;
|
|
299
|
-
const ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom;
|
|
300
|
-
const ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom;
|
|
301
|
-
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
|
|
302
|
-
return {
|
|
303
|
-
x: p1.x + ua * (p2.x - p1.x),
|
|
304
|
-
y: p1.y + ua * (p2.y - p1.y)
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
return null;
|
|
226
|
+
return info;
|
|
308
227
|
}
|
|
309
|
-
var
|
|
310
|
-
"src/layout/
|
|
228
|
+
var init_collector = __esm({
|
|
229
|
+
"src/layout/post-processing/boundary-event/collector.ts"() {
|
|
311
230
|
"use strict";
|
|
312
231
|
init_cjs_shims();
|
|
313
232
|
}
|
|
314
233
|
});
|
|
315
234
|
|
|
316
|
-
// src/layout/
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
fix(graph) {
|
|
330
|
-
const nodesByContainer = /* @__PURE__ */ new Map();
|
|
331
|
-
const flowNodePatterns = [
|
|
332
|
-
/^task_/,
|
|
333
|
-
/^gateway_/,
|
|
334
|
-
/^start_/,
|
|
335
|
-
/^end_/,
|
|
336
|
-
/^subprocess_/,
|
|
337
|
-
/^call_/,
|
|
338
|
-
/^intermediate_/,
|
|
339
|
-
/^event_/,
|
|
340
|
-
/^catch_/
|
|
341
|
-
];
|
|
342
|
-
const boundaryEventPattern = /^boundary_/;
|
|
343
|
-
const poolPatterns = [
|
|
344
|
-
/^pool_/,
|
|
345
|
-
/^participant_/,
|
|
346
|
-
/^process_/
|
|
347
|
-
];
|
|
348
|
-
const collectNodePositions = (node, offsetX = 0, offsetY = 0, containerId = "root") => {
|
|
349
|
-
const id = node.id || "";
|
|
350
|
-
const isBoundaryEvent = boundaryEventPattern.test(id);
|
|
351
|
-
const isFlowNode = flowNodePatterns.some((pattern) => pattern.test(id));
|
|
352
|
-
const isPool = poolPatterns.some((pattern) => pattern.test(id));
|
|
353
|
-
const bpmn = node.bpmn;
|
|
354
|
-
if (isDebugEnabled() && (id.includes("lane") || id.includes("pool") || id.includes("end_fast") || id.includes("gateway_fast"))) {
|
|
355
|
-
console.log(`[BPMN] EdgeFixer.collectNodePositions: id=${id}, offsetX=${offsetX}, offsetY=${offsetY}, bpmn=${JSON.stringify(bpmn)}`);
|
|
356
|
-
console.log(`[BPMN] node.x=${node.x}, node.y=${node.y}, isFlowNode=${isFlowNode}, isPool=${isPool}`);
|
|
357
|
-
}
|
|
358
|
-
const currentContainerId = isPool ? id : containerId;
|
|
359
|
-
if (isFlowNode && !isBoundaryEvent && node.x !== void 0 && node.y !== void 0) {
|
|
360
|
-
const absX = offsetX + node.x;
|
|
361
|
-
const absY = offsetY + node.y;
|
|
362
|
-
if (!nodesByContainer.has(currentContainerId)) {
|
|
363
|
-
nodesByContainer.set(currentContainerId, /* @__PURE__ */ new Map());
|
|
364
|
-
}
|
|
365
|
-
nodesByContainer.get(currentContainerId).set(node.id, {
|
|
366
|
-
x: absX,
|
|
367
|
-
y: absY,
|
|
368
|
-
width: node.width ?? 100,
|
|
369
|
-
height: node.height ?? 80
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
const isExpandedSubprocess = bpmn?.isExpanded === true && (bpmn?.type === "subProcess" || bpmn?.type === "transaction" || bpmn?.type === "adHocSubProcess" || bpmn?.type === "eventSubProcess");
|
|
373
|
-
const isPoolOrLane = bpmn?.type === "participant" || bpmn?.type === "lane";
|
|
374
|
-
const isContainer = isExpandedSubprocess || isPoolOrLane;
|
|
375
|
-
if (node.children) {
|
|
376
|
-
const newOffsetX = isContainer ? offsetX + (node.x ?? 0) : offsetX;
|
|
377
|
-
const newOffsetY = isContainer ? offsetY + (node.y ?? 0) : offsetY;
|
|
378
|
-
for (const child of node.children) {
|
|
379
|
-
collectNodePositions(child, newOffsetX, newOffsetY, currentContainerId);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
collectNodePositions(graph);
|
|
384
|
-
const processEdges = (node, containerOffsetX = 0, containerOffsetY = 0, containerId = "root") => {
|
|
385
|
-
const bpmn = node.bpmn;
|
|
386
|
-
const isPool = poolPatterns.some((pattern) => pattern.test(node.id || ""));
|
|
387
|
-
const currentContainerId = isPool ? node.id : containerId;
|
|
388
|
-
if (node.edges) {
|
|
389
|
-
const containerNodes = nodesByContainer.get(currentContainerId) ?? /* @__PURE__ */ new Map();
|
|
390
|
-
for (const edge of node.edges) {
|
|
391
|
-
if (edge.sections && edge.sections.length > 0) {
|
|
392
|
-
const hasPoolRelativeCoords = edge._poolRelativeCoords === true;
|
|
393
|
-
if (hasPoolRelativeCoords) {
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
this.fixEdgeIfCrossing(edge, containerNodes, containerOffsetX, containerOffsetY);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
const isExpandedSubprocess = bpmn?.isExpanded === true && (bpmn?.type === "subProcess" || bpmn?.type === "transaction" || bpmn?.type === "adHocSubProcess" || bpmn?.type === "eventSubProcess");
|
|
401
|
-
const isPoolOrLane = bpmn?.type === "participant" || bpmn?.type === "lane";
|
|
402
|
-
const isContainer = isExpandedSubprocess || isPoolOrLane;
|
|
403
|
-
if (node.children) {
|
|
404
|
-
const newOffsetX = isContainer ? containerOffsetX + (node.x ?? 0) : containerOffsetX;
|
|
405
|
-
const newOffsetY = isContainer ? containerOffsetY + (node.y ?? 0) : containerOffsetY;
|
|
406
|
-
for (const child of node.children) {
|
|
407
|
-
processEdges(child, newOffsetX, newOffsetY, currentContainerId);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
processEdges(graph);
|
|
235
|
+
// src/layout/post-processing/boundary-event/mover.ts
|
|
236
|
+
function buildNodeAndEdgeMaps(graph, sizedGraph) {
|
|
237
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
238
|
+
const nodeTypeMap = /* @__PURE__ */ new Map();
|
|
239
|
+
const edgeMap = /* @__PURE__ */ new Map();
|
|
240
|
+
const reverseEdgeMap = /* @__PURE__ */ new Map();
|
|
241
|
+
const collectNodeTypes = (node) => {
|
|
242
|
+
if (node.bpmn?.type) {
|
|
243
|
+
nodeTypeMap.set(node.id, node.bpmn.type);
|
|
244
|
+
}
|
|
245
|
+
if (node.children) {
|
|
246
|
+
for (const child of node.children) {
|
|
247
|
+
collectNodeTypes(child);
|
|
412
248
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (!section) return;
|
|
419
|
-
const sourceId = edge.sources?.[0];
|
|
420
|
-
const targetId = edge.targets?.[0];
|
|
421
|
-
if (sourceId?.startsWith("boundary_")) {
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
const waypoints = [
|
|
425
|
-
{ x: containerOffsetX + section.startPoint.x, y: containerOffsetY + section.startPoint.y }
|
|
426
|
-
];
|
|
427
|
-
if (section.bendPoints) {
|
|
428
|
-
for (const bp of section.bendPoints) {
|
|
429
|
-
waypoints.push({ x: containerOffsetX + bp.x, y: containerOffsetY + bp.y });
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
waypoints.push({ x: containerOffsetX + section.endPoint.x, y: containerOffsetY + section.endPoint.y });
|
|
433
|
-
const crossedNodes = [];
|
|
434
|
-
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
435
|
-
const p1 = waypoints[i];
|
|
436
|
-
const p2 = waypoints[i + 1];
|
|
437
|
-
if (!p1 || !p2) continue;
|
|
438
|
-
for (const [nodeId, pos] of nodePositions) {
|
|
439
|
-
if (nodeId === sourceId || nodeId === targetId) continue;
|
|
440
|
-
if (segmentCrossesNode(p1, p2, pos)) {
|
|
441
|
-
crossedNodes.push(nodeId);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
const targetPos = targetId ? nodePositions.get(targetId) : void 0;
|
|
446
|
-
const sourcePos = sourceId ? nodePositions.get(sourceId) : void 0;
|
|
447
|
-
if (isDebugEnabled() && edge.id?.includes("back")) {
|
|
448
|
-
console.log(`[BPMN] Edge ${edge.id}: sourceId=${sourceId}, targetId=${targetId}`);
|
|
449
|
-
console.log(`[BPMN] Edge ${edge.id}: sourcePos=${JSON.stringify(sourcePos)}, targetPos=${JSON.stringify(targetPos)}`);
|
|
450
|
-
console.log(`[BPMN] Edge ${edge.id}: waypoints.length=${waypoints.length}`);
|
|
451
|
-
}
|
|
452
|
-
if (targetPos && sourcePos && waypoints.length >= 2) {
|
|
453
|
-
const lastWaypoint = waypoints[waypoints.length - 1];
|
|
454
|
-
const secondLastWaypoint = waypoints[waypoints.length - 2];
|
|
455
|
-
const isReturnEdge2 = targetPos.y + targetPos.height < sourcePos.y;
|
|
456
|
-
if (isDebugEnabled() && edge.id?.includes("back")) {
|
|
457
|
-
console.log(`[BPMN] Edge ${edge.id}: isReturnEdge=${isReturnEdge2}`);
|
|
458
|
-
if (lastWaypoint) {
|
|
459
|
-
console.log(`[BPMN] Edge ${edge.id}: lastWaypoint=(${lastWaypoint.x},${lastWaypoint.y})`);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
if (isReturnEdge2 && lastWaypoint && secondLastWaypoint) {
|
|
463
|
-
if (Math.abs(secondLastWaypoint.y - lastWaypoint.y) < 5) {
|
|
464
|
-
const segY = secondLastWaypoint.y;
|
|
465
|
-
const segMinX = Math.min(secondLastWaypoint.x, lastWaypoint.x);
|
|
466
|
-
const segMaxX = Math.max(secondLastWaypoint.x, lastWaypoint.x);
|
|
467
|
-
if (segY > targetPos.y && segY < targetPos.y + targetPos.height) {
|
|
468
|
-
if (segMinX < targetPos.x + targetPos.width && segMaxX > targetPos.x) {
|
|
469
|
-
crossedNodes.push(targetId + " (target)");
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
if (crossedNodes.length === 0) return;
|
|
476
|
-
if (isDebugEnabled()) {
|
|
477
|
-
console.log(`[BPMN] Edge ${edge.id} crosses nodes: ${crossedNodes.join(", ")}`);
|
|
478
|
-
}
|
|
479
|
-
if (!sourcePos || !targetPos) return;
|
|
480
|
-
const obstacles = [];
|
|
481
|
-
for (const [nodeId, pos] of nodePositions) {
|
|
482
|
-
if (nodeId === sourceId || nodeId === targetId) continue;
|
|
483
|
-
obstacles.push({ ...pos, id: nodeId });
|
|
484
|
-
}
|
|
485
|
-
const isReturnEdge = targetPos.y + targetPos.height < sourcePos.y;
|
|
486
|
-
const crossesThroughTarget = crossedNodes.some((n) => n.includes("(target)"));
|
|
487
|
-
if (isReturnEdge && crossesThroughTarget) {
|
|
488
|
-
const targetWidth = targetPos.width;
|
|
489
|
-
section.endPoint = {
|
|
490
|
-
x: section.endPoint.x + targetWidth,
|
|
491
|
-
y: section.endPoint.y
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
const originalStart = {
|
|
495
|
-
x: containerOffsetX + section.startPoint.x,
|
|
496
|
-
y: containerOffsetY + section.startPoint.y
|
|
497
|
-
};
|
|
498
|
-
const originalEnd = {
|
|
499
|
-
x: containerOffsetX + section.endPoint.x,
|
|
500
|
-
y: containerOffsetY + section.endPoint.y
|
|
501
|
-
};
|
|
502
|
-
const result = this.calculatePerpendicularAvoidingPath(
|
|
503
|
-
originalStart,
|
|
504
|
-
originalEnd,
|
|
505
|
-
sourcePos,
|
|
506
|
-
targetPos,
|
|
507
|
-
obstacles,
|
|
508
|
-
isReturnEdge
|
|
509
|
-
);
|
|
510
|
-
if (result.startPoint) {
|
|
511
|
-
section.startPoint = {
|
|
512
|
-
x: result.startPoint.x - containerOffsetX,
|
|
513
|
-
y: result.startPoint.y - containerOffsetY
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
if (result.endPoint) {
|
|
517
|
-
section.endPoint = {
|
|
518
|
-
x: result.endPoint.x - containerOffsetX,
|
|
519
|
-
y: result.endPoint.y - containerOffsetY
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
const relativeBendPoints = result.bendPoints.map((bp) => ({
|
|
523
|
-
x: bp.x - containerOffsetX,
|
|
524
|
-
y: bp.y - containerOffsetY
|
|
525
|
-
}));
|
|
526
|
-
section.bendPoints = relativeBendPoints.length > 0 ? relativeBendPoints : void 0;
|
|
527
|
-
if (isDebugEnabled()) {
|
|
528
|
-
console.log(`[BPMN] Fixed edge ${edge.id} with ${relativeBendPoints.length} bend points`);
|
|
249
|
+
}
|
|
250
|
+
if (node.boundaryEvents) {
|
|
251
|
+
for (const be of node.boundaryEvents) {
|
|
252
|
+
if (be.bpmn?.type) {
|
|
253
|
+
nodeTypeMap.set(be.id, be.bpmn.type);
|
|
529
254
|
}
|
|
530
255
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
return "left";
|
|
549
|
-
}
|
|
550
|
-
} else if (dx < 0) {
|
|
551
|
-
if (isSource) {
|
|
552
|
-
return "left";
|
|
553
|
-
} else {
|
|
554
|
-
return "right";
|
|
555
|
-
}
|
|
556
|
-
} else {
|
|
557
|
-
if (isSource) {
|
|
558
|
-
return dy > 0 ? "bottom" : "top";
|
|
559
|
-
} else {
|
|
560
|
-
return dy > 0 ? "top" : "bottom";
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
/**
|
|
565
|
-
* Get the connection point on a node boundary
|
|
566
|
-
*/
|
|
567
|
-
getConnectionPoint(bounds, side) {
|
|
568
|
-
const centerX = bounds.x + bounds.width / 2;
|
|
569
|
-
const centerY = bounds.y + bounds.height / 2;
|
|
570
|
-
switch (side) {
|
|
571
|
-
case "top":
|
|
572
|
-
return { x: centerX, y: bounds.y };
|
|
573
|
-
case "bottom":
|
|
574
|
-
return { x: centerX, y: bounds.y + bounds.height };
|
|
575
|
-
case "left":
|
|
576
|
-
return { x: bounds.x, y: centerY };
|
|
577
|
-
case "right":
|
|
578
|
-
return { x: bounds.x + bounds.width, y: centerY };
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
/**
|
|
582
|
-
* Calculate bend points that avoid obstacles while ensuring perpendicular connections
|
|
583
|
-
* Uses LOCAL routing - only considers obstacles actually blocking the direct path
|
|
584
|
-
*/
|
|
585
|
-
calculatePerpendicularAvoidingPath(originalStart, originalEnd, source, target, obstacles, isReturnEdge) {
|
|
586
|
-
const dx = originalEnd.x - originalStart.x;
|
|
587
|
-
const dy = originalEnd.y - originalStart.y;
|
|
588
|
-
const sourceSide = this.determineConnectionSide(originalStart, originalEnd, source, true);
|
|
589
|
-
const targetSide = this.determineConnectionSide(originalStart, originalEnd, target, false);
|
|
590
|
-
const startPoint = this.getConnectionPoint(source, sourceSide);
|
|
591
|
-
const endPoint = this.getConnectionPoint(target, targetSide);
|
|
592
|
-
const pathMinX = Math.min(startPoint.x, endPoint.x) - this.margin;
|
|
593
|
-
const pathMaxX = Math.max(startPoint.x, endPoint.x) + this.margin;
|
|
594
|
-
const pathMinY = Math.min(startPoint.y, endPoint.y) - this.margin;
|
|
595
|
-
const pathMaxY = Math.max(startPoint.y, endPoint.y) + this.margin;
|
|
596
|
-
const blockingObstacles = obstacles.filter((obs) => {
|
|
597
|
-
const obsRight = obs.x + obs.width;
|
|
598
|
-
const obsBottom = obs.y + obs.height;
|
|
599
|
-
const overlapX = obs.x < pathMaxX && obsRight > pathMinX;
|
|
600
|
-
const overlapY = obs.y < pathMaxY && obsBottom > pathMinY;
|
|
601
|
-
return overlapX && overlapY;
|
|
602
|
-
});
|
|
603
|
-
if (blockingObstacles.length === 0) {
|
|
604
|
-
const bendPoints2 = this.createPerpendicularPath(startPoint, endPoint, sourceSide, targetSide);
|
|
605
|
-
return { bendPoints: bendPoints2, startPoint, endPoint };
|
|
606
|
-
}
|
|
607
|
-
const targetIsRight = dx > 0;
|
|
608
|
-
const targetIsAbove = dy < 0;
|
|
609
|
-
const targetIsBelow = dy > 0;
|
|
610
|
-
let bendPoints;
|
|
611
|
-
if (targetIsRight) {
|
|
612
|
-
if (targetIsAbove) {
|
|
613
|
-
bendPoints = this.routeRightThenUp(startPoint, endPoint, blockingObstacles, source, target);
|
|
614
|
-
} else if (targetIsBelow) {
|
|
615
|
-
bendPoints = this.routeRightThenDown(startPoint, endPoint, blockingObstacles, source, target);
|
|
616
|
-
} else {
|
|
617
|
-
bendPoints = this.routeRightWithObstacleAvoidance(startPoint, endPoint, sourceSide, targetSide, blockingObstacles, source, target);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
if (sizedGraph) {
|
|
259
|
+
for (const child of sizedGraph.children ?? []) {
|
|
260
|
+
collectNodeTypes(child);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const buildMaps = (node) => {
|
|
264
|
+
nodeMap.set(node.id, node);
|
|
265
|
+
if (node.edges) {
|
|
266
|
+
for (const edge of node.edges) {
|
|
267
|
+
const source = edge.sources?.[0];
|
|
268
|
+
const target = edge.targets?.[0];
|
|
269
|
+
if (source && target) {
|
|
270
|
+
if (!edgeMap.has(source)) {
|
|
271
|
+
edgeMap.set(source, []);
|
|
618
272
|
}
|
|
619
|
-
|
|
620
|
-
if (
|
|
621
|
-
|
|
622
|
-
} else if (targetIsBelow) {
|
|
623
|
-
bendPoints = this.routeDownWithObstacleAvoidance(startPoint, endPoint, sourceSide, targetSide, blockingObstacles, source, target);
|
|
624
|
-
} else {
|
|
625
|
-
bendPoints = this.routeLeftWithObstacleAvoidance(startPoint, endPoint, sourceSide, targetSide, blockingObstacles, source, target);
|
|
273
|
+
edgeMap.get(source).push(target);
|
|
274
|
+
if (!reverseEdgeMap.has(target)) {
|
|
275
|
+
reverseEdgeMap.set(target, []);
|
|
626
276
|
}
|
|
627
|
-
|
|
628
|
-
return { bendPoints, startPoint, endPoint };
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Route right then up - for edges going both right and up
|
|
632
|
-
* Source exits from right side, target enters from left side
|
|
633
|
-
* Strategy: go horizontally right past obstacles, then vertically to target
|
|
634
|
-
*/
|
|
635
|
-
routeRightThenUp(start, end, obstacles, source, target) {
|
|
636
|
-
const bendPoints = [];
|
|
637
|
-
const blockingObstacles = obstacles.filter((obs) => {
|
|
638
|
-
const obsRight = obs.x + obs.width;
|
|
639
|
-
const obsBottom = obs.y + obs.height;
|
|
640
|
-
const pathMinY = Math.min(start.y, end.y);
|
|
641
|
-
const pathMaxY = Math.max(start.y, end.y);
|
|
642
|
-
return obs.x < end.x && obsRight > start.x - this.margin && obs.y < pathMaxY && obsBottom > pathMinY;
|
|
643
|
-
});
|
|
644
|
-
if (blockingObstacles.length === 0) {
|
|
645
|
-
bendPoints.push({ x: end.x, y: start.y });
|
|
646
|
-
return bendPoints;
|
|
647
|
-
}
|
|
648
|
-
let clearX = start.x;
|
|
649
|
-
for (const obs of blockingObstacles) {
|
|
650
|
-
clearX = Math.max(clearX, obs.x + obs.width + this.margin);
|
|
651
|
-
}
|
|
652
|
-
clearX = Math.max(clearX, end.x);
|
|
653
|
-
if (Math.abs(clearX - end.x) < 5) {
|
|
654
|
-
bendPoints.push({ x: end.x, y: start.y });
|
|
655
|
-
} else {
|
|
656
|
-
bendPoints.push({ x: clearX, y: start.y });
|
|
657
|
-
bendPoints.push({ x: clearX, y: end.y });
|
|
658
|
-
}
|
|
659
|
-
return bendPoints;
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Route right then down - for edges going both right and down
|
|
663
|
-
* Source exits from right side, target enters from left side
|
|
664
|
-
* Strategy: go horizontally right past obstacles, then vertically to target
|
|
665
|
-
*/
|
|
666
|
-
routeRightThenDown(start, end, obstacles, source, target) {
|
|
667
|
-
const bendPoints = [];
|
|
668
|
-
const blockingObstacles = obstacles.filter((obs) => {
|
|
669
|
-
const obsRight = obs.x + obs.width;
|
|
670
|
-
const obsBottom = obs.y + obs.height;
|
|
671
|
-
const pathMinY = Math.min(start.y, end.y);
|
|
672
|
-
const pathMaxY = Math.max(start.y, end.y);
|
|
673
|
-
return obs.x < end.x && obsRight > start.x - this.margin && obs.y < pathMaxY && obsBottom > pathMinY;
|
|
674
|
-
});
|
|
675
|
-
if (blockingObstacles.length === 0) {
|
|
676
|
-
bendPoints.push({ x: end.x, y: start.y });
|
|
677
|
-
return bendPoints;
|
|
678
|
-
}
|
|
679
|
-
let clearX = start.x;
|
|
680
|
-
for (const obs of blockingObstacles) {
|
|
681
|
-
clearX = Math.max(clearX, obs.x + obs.width + this.margin);
|
|
682
|
-
}
|
|
683
|
-
clearX = Math.max(clearX, end.x);
|
|
684
|
-
if (Math.abs(clearX - end.x) < 5) {
|
|
685
|
-
bendPoints.push({ x: end.x, y: start.y });
|
|
686
|
-
} else {
|
|
687
|
-
bendPoints.push({ x: clearX, y: start.y });
|
|
688
|
-
bendPoints.push({ x: clearX, y: end.y });
|
|
689
|
-
}
|
|
690
|
-
return bendPoints;
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
|
-
* Create a simple perpendicular path without obstacle avoidance
|
|
694
|
-
*/
|
|
695
|
-
createPerpendicularPath(start, end, sourceSide, targetSide) {
|
|
696
|
-
const bendPoints = [];
|
|
697
|
-
if (Math.abs(start.x - end.x) < 5 || Math.abs(start.y - end.y) < 5) {
|
|
698
|
-
return bendPoints;
|
|
699
|
-
}
|
|
700
|
-
const isVerticalExit = sourceSide === "top" || sourceSide === "bottom";
|
|
701
|
-
const isVerticalEntry = targetSide === "top" || targetSide === "bottom";
|
|
702
|
-
if (isVerticalExit && isVerticalEntry) {
|
|
703
|
-
const midY = (start.y + end.y) / 2;
|
|
704
|
-
bendPoints.push({ x: start.x, y: midY });
|
|
705
|
-
bendPoints.push({ x: end.x, y: midY });
|
|
706
|
-
} else if (!isVerticalExit && !isVerticalEntry) {
|
|
707
|
-
const midX = (start.x + end.x) / 2;
|
|
708
|
-
bendPoints.push({ x: midX, y: start.y });
|
|
709
|
-
bendPoints.push({ x: midX, y: end.y });
|
|
710
|
-
} else if (isVerticalExit && !isVerticalEntry) {
|
|
711
|
-
bendPoints.push({ x: start.x, y: end.y });
|
|
712
|
-
} else {
|
|
713
|
-
bendPoints.push({ x: end.x, y: start.y });
|
|
714
|
-
}
|
|
715
|
-
return bendPoints;
|
|
716
|
-
}
|
|
717
|
-
/**
|
|
718
|
-
* Route downward with obstacle avoidance, maintaining perpendicular connections
|
|
719
|
-
*/
|
|
720
|
-
routeDownWithObstacleAvoidance(start, end, sourceSide, targetSide, obstacles, source, target) {
|
|
721
|
-
const bendPoints = [];
|
|
722
|
-
let avoidX = Math.min(start.x, end.x);
|
|
723
|
-
for (const obs of obstacles) {
|
|
724
|
-
if (obs.y <= end.y && obs.y + obs.height >= start.y) {
|
|
725
|
-
if (obs.x <= start.x && obs.x + obs.width >= start.x) {
|
|
726
|
-
avoidX = Math.min(avoidX, obs.x - this.margin);
|
|
727
|
-
}
|
|
728
|
-
if (obs.x <= end.x && obs.x + obs.width >= end.x) {
|
|
729
|
-
avoidX = Math.min(avoidX, obs.x - this.margin);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
avoidX = Math.min(avoidX, source.x - this.margin);
|
|
734
|
-
avoidX = Math.min(avoidX, target.x - this.margin);
|
|
735
|
-
if (sourceSide === "bottom") {
|
|
736
|
-
const exitY = start.y + this.margin;
|
|
737
|
-
bendPoints.push({ x: start.x, y: exitY });
|
|
738
|
-
if (avoidX < start.x - 5) {
|
|
739
|
-
bendPoints.push({ x: avoidX, y: exitY });
|
|
740
|
-
bendPoints.push({ x: avoidX, y: end.y - this.margin });
|
|
741
|
-
bendPoints.push({ x: end.x, y: end.y - this.margin });
|
|
742
|
-
} else if (Math.abs(start.x - end.x) > 5) {
|
|
743
|
-
bendPoints.push({ x: end.x, y: exitY });
|
|
744
|
-
}
|
|
745
|
-
} else {
|
|
746
|
-
const midY = (start.y + end.y) / 2;
|
|
747
|
-
bendPoints.push({ x: avoidX, y: start.y });
|
|
748
|
-
bendPoints.push({ x: avoidX, y: midY });
|
|
749
|
-
bendPoints.push({ x: end.x, y: midY });
|
|
750
|
-
}
|
|
751
|
-
return bendPoints;
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* Route upward with obstacle avoidance, maintaining perpendicular connections
|
|
755
|
-
*/
|
|
756
|
-
routeUpWithObstacleAvoidance(start, end, sourceSide, targetSide, obstacles, source, target) {
|
|
757
|
-
const bendPoints = [];
|
|
758
|
-
let clearX = Math.max(source.x + source.width, target.x + target.width) + this.margin;
|
|
759
|
-
for (const obs of obstacles) {
|
|
760
|
-
clearX = Math.max(clearX, obs.x + obs.width + this.margin);
|
|
761
|
-
}
|
|
762
|
-
if (sourceSide === "right") {
|
|
763
|
-
bendPoints.push({ x: clearX, y: start.y });
|
|
764
|
-
bendPoints.push({ x: clearX, y: end.y });
|
|
765
|
-
} else if (sourceSide === "top") {
|
|
766
|
-
const exitY = start.y - this.margin;
|
|
767
|
-
bendPoints.push({ x: start.x, y: exitY });
|
|
768
|
-
bendPoints.push({ x: clearX, y: exitY });
|
|
769
|
-
bendPoints.push({ x: clearX, y: end.y });
|
|
770
|
-
} else {
|
|
771
|
-
bendPoints.push({ x: clearX, y: start.y });
|
|
772
|
-
bendPoints.push({ x: clearX, y: end.y });
|
|
773
|
-
}
|
|
774
|
-
return bendPoints;
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* Route rightward with obstacle avoidance, maintaining perpendicular connections
|
|
778
|
-
*/
|
|
779
|
-
routeRightWithObstacleAvoidance(start, end, sourceSide, targetSide, obstacles, source, target) {
|
|
780
|
-
const bendPoints = [];
|
|
781
|
-
const sourceRight = source.x + source.width;
|
|
782
|
-
const targetLeft = target.x;
|
|
783
|
-
let routeX = sourceRight + this.margin;
|
|
784
|
-
for (const obs of obstacles) {
|
|
785
|
-
const obsLeft = obs.x;
|
|
786
|
-
const obsRight = obs.x + obs.width;
|
|
787
|
-
if (obsRight > sourceRight && obsLeft < targetLeft) {
|
|
788
|
-
routeX = Math.max(routeX, obsRight + this.margin);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
routeX = Math.min(routeX, targetLeft - this.margin);
|
|
792
|
-
if (routeX <= sourceRight) {
|
|
793
|
-
let clearY = Math.max(source.y + source.height, target.y + target.height) + this.margin;
|
|
794
|
-
for (const obs of obstacles) {
|
|
795
|
-
clearY = Math.max(clearY, obs.y + obs.height + this.margin);
|
|
796
|
-
}
|
|
797
|
-
if (sourceSide === "right") {
|
|
798
|
-
bendPoints.push({ x: start.x + this.margin, y: start.y });
|
|
799
|
-
bendPoints.push({ x: start.x + this.margin, y: clearY });
|
|
800
|
-
bendPoints.push({ x: end.x - this.margin, y: clearY });
|
|
801
|
-
bendPoints.push({ x: end.x - this.margin, y: end.y });
|
|
802
|
-
} else {
|
|
803
|
-
bendPoints.push({ x: start.x, y: clearY });
|
|
804
|
-
bendPoints.push({ x: end.x, y: clearY });
|
|
805
|
-
}
|
|
806
|
-
} else {
|
|
807
|
-
bendPoints.push({ x: routeX, y: start.y });
|
|
808
|
-
bendPoints.push({ x: routeX, y: end.y });
|
|
809
|
-
}
|
|
810
|
-
return bendPoints;
|
|
811
|
-
}
|
|
812
|
-
/**
|
|
813
|
-
* Route leftward with obstacle avoidance, maintaining perpendicular connections
|
|
814
|
-
*/
|
|
815
|
-
routeLeftWithObstacleAvoidance(start, end, sourceSide, targetSide, obstacles, source, target) {
|
|
816
|
-
const bendPoints = [];
|
|
817
|
-
let clearX = Math.min(source.x, target.x) - this.margin;
|
|
818
|
-
for (const obs of obstacles) {
|
|
819
|
-
if (obs.x < source.x && obs.x + obs.width > target.x + target.width) {
|
|
820
|
-
clearX = Math.min(clearX, obs.x - this.margin);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
if (sourceSide === "left") {
|
|
824
|
-
bendPoints.push({ x: clearX, y: start.y });
|
|
825
|
-
bendPoints.push({ x: clearX, y: end.y });
|
|
826
|
-
} else {
|
|
827
|
-
const exitX = start.x - this.margin;
|
|
828
|
-
bendPoints.push({ x: exitX, y: start.y });
|
|
829
|
-
bendPoints.push({ x: exitX, y: end.y });
|
|
830
|
-
}
|
|
831
|
-
return bendPoints;
|
|
832
|
-
}
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
// src/layout/post-processing/boundary-event/collector.ts
|
|
838
|
-
function collectBoundaryEventInfo(graph) {
|
|
839
|
-
const info = /* @__PURE__ */ new Map();
|
|
840
|
-
const edgeMap = /* @__PURE__ */ new Map();
|
|
841
|
-
const collectEdges = (node) => {
|
|
842
|
-
if (node.edges) {
|
|
843
|
-
for (const edge of node.edges) {
|
|
844
|
-
const source = edge.sources?.[0];
|
|
845
|
-
const target = edge.targets?.[0];
|
|
846
|
-
if (!source || !target) continue;
|
|
847
|
-
if (!edgeMap.has(source)) {
|
|
848
|
-
edgeMap.set(source, []);
|
|
849
|
-
}
|
|
850
|
-
edgeMap.get(source).push(target);
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
if (node.children) {
|
|
854
|
-
for (const child of node.children) {
|
|
855
|
-
collectEdges(child);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
};
|
|
859
|
-
const collectBoundaryEvents = (node) => {
|
|
860
|
-
if (node.boundaryEvents) {
|
|
861
|
-
const totalBoundaries = node.boundaryEvents.length;
|
|
862
|
-
node.boundaryEvents.forEach((be, index) => {
|
|
863
|
-
const targets = edgeMap.get(be.id) || [];
|
|
864
|
-
info.set(be.id, {
|
|
865
|
-
attachedToRef: be.attachedToRef,
|
|
866
|
-
targets,
|
|
867
|
-
boundaryIndex: index,
|
|
868
|
-
totalBoundaries
|
|
869
|
-
});
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
if (node.children) {
|
|
873
|
-
for (const child of node.children) {
|
|
874
|
-
collectBoundaryEvents(child);
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
};
|
|
878
|
-
for (const child of graph.children ?? []) {
|
|
879
|
-
collectEdges(child);
|
|
880
|
-
collectBoundaryEvents(child);
|
|
881
|
-
}
|
|
882
|
-
return info;
|
|
883
|
-
}
|
|
884
|
-
var init_collector = __esm({
|
|
885
|
-
"src/layout/post-processing/boundary-event/collector.ts"() {
|
|
886
|
-
"use strict";
|
|
887
|
-
init_cjs_shims();
|
|
888
|
-
}
|
|
889
|
-
});
|
|
890
|
-
|
|
891
|
-
// src/layout/post-processing/boundary-event/mover.ts
|
|
892
|
-
function buildNodeAndEdgeMaps(graph, sizedGraph) {
|
|
893
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
894
|
-
const nodeTypeMap = /* @__PURE__ */ new Map();
|
|
895
|
-
const edgeMap = /* @__PURE__ */ new Map();
|
|
896
|
-
const reverseEdgeMap = /* @__PURE__ */ new Map();
|
|
897
|
-
const collectNodeTypes = (node) => {
|
|
898
|
-
if (node.bpmn?.type) {
|
|
899
|
-
nodeTypeMap.set(node.id, node.bpmn.type);
|
|
900
|
-
}
|
|
901
|
-
if (node.children) {
|
|
902
|
-
for (const child of node.children) {
|
|
903
|
-
collectNodeTypes(child);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
if (node.boundaryEvents) {
|
|
907
|
-
for (const be of node.boundaryEvents) {
|
|
908
|
-
if (be.bpmn?.type) {
|
|
909
|
-
nodeTypeMap.set(be.id, be.bpmn.type);
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
};
|
|
914
|
-
if (sizedGraph) {
|
|
915
|
-
for (const child of sizedGraph.children ?? []) {
|
|
916
|
-
collectNodeTypes(child);
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
const buildMaps = (node) => {
|
|
920
|
-
nodeMap.set(node.id, node);
|
|
921
|
-
if (node.edges) {
|
|
922
|
-
for (const edge of node.edges) {
|
|
923
|
-
const source = edge.sources?.[0];
|
|
924
|
-
const target = edge.targets?.[0];
|
|
925
|
-
if (source && target) {
|
|
926
|
-
if (!edgeMap.has(source)) {
|
|
927
|
-
edgeMap.set(source, []);
|
|
928
|
-
}
|
|
929
|
-
edgeMap.get(source).push(target);
|
|
930
|
-
if (!reverseEdgeMap.has(target)) {
|
|
931
|
-
reverseEdgeMap.set(target, []);
|
|
932
|
-
}
|
|
933
|
-
reverseEdgeMap.get(target).push(source);
|
|
277
|
+
reverseEdgeMap.get(target).push(source);
|
|
934
278
|
}
|
|
935
279
|
}
|
|
936
280
|
}
|
|
@@ -1354,6 +698,22 @@ function recalculateEdgesForMovedNodes(graph, movedNodes, boundaryEventInfo) {
|
|
|
1354
698
|
for (const [nodeId] of movedNodes) {
|
|
1355
699
|
obstacleIds.add(nodeId);
|
|
1356
700
|
}
|
|
701
|
+
const flowNodePatterns = [
|
|
702
|
+
/^task_/,
|
|
703
|
+
/^gateway_/,
|
|
704
|
+
/^start_/,
|
|
705
|
+
/^end_/,
|
|
706
|
+
/^subprocess_/,
|
|
707
|
+
/^call_/,
|
|
708
|
+
/^intermediate_/,
|
|
709
|
+
/^event_/,
|
|
710
|
+
/^catch_/
|
|
711
|
+
];
|
|
712
|
+
for (const [nodeId] of nodeMap) {
|
|
713
|
+
if (flowNodePatterns.some((pattern) => pattern.test(nodeId))) {
|
|
714
|
+
obstacleIds.add(nodeId);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
1357
717
|
const boundaryEventPositions = /* @__PURE__ */ new Map();
|
|
1358
718
|
for (const [beId, info] of boundaryEventInfo) {
|
|
1359
719
|
const attachedNode = nodeMap.get(info.attachedToRef);
|
|
@@ -1392,8 +752,7 @@ function recalculateEdgesForMovedNodes(graph, movedNodes, boundaryEventInfo) {
|
|
|
1392
752
|
sourceNode,
|
|
1393
753
|
targetNode,
|
|
1394
754
|
obstacleIds,
|
|
1395
|
-
nodeMap
|
|
1396
|
-
debug
|
|
755
|
+
nodeMap
|
|
1397
756
|
);
|
|
1398
757
|
}
|
|
1399
758
|
}
|
|
@@ -1505,9 +864,29 @@ function recalculateEdgeWithObstacleAvoidance(edge, source, target, obstacleIds,
|
|
|
1505
864
|
clearX = Math.max(clearX, obs.x + obs.width + 30);
|
|
1506
865
|
}
|
|
1507
866
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
867
|
+
let horizontalClearY = endY;
|
|
868
|
+
const horizSegMinX = Math.min(clearX, endX);
|
|
869
|
+
const horizSegMaxX = Math.max(clearX, endX);
|
|
870
|
+
const margin = 20;
|
|
871
|
+
for (const obs of obstacles) {
|
|
872
|
+
const obsRight = obs.x + obs.width;
|
|
873
|
+
const obsBottom = obs.y + obs.height;
|
|
874
|
+
if (obs.x < horizSegMaxX && obsRight > horizSegMinX) {
|
|
875
|
+
if (horizontalClearY >= obs.y - margin && horizontalClearY <= obsBottom + margin) {
|
|
876
|
+
horizontalClearY = Math.max(horizontalClearY, obsBottom + margin);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (horizontalClearY > endY) {
|
|
881
|
+
waypoints.push({ x: clearX, y: startY });
|
|
882
|
+
waypoints.push({ x: clearX, y: horizontalClearY });
|
|
883
|
+
waypoints.push({ x: endX, y: horizontalClearY });
|
|
884
|
+
waypoints.push({ x: endX, y: endY });
|
|
885
|
+
} else {
|
|
886
|
+
waypoints.push({ x: clearX, y: startY });
|
|
887
|
+
waypoints.push({ x: clearX, y: endY });
|
|
888
|
+
waypoints.push({ x: endX, y: endY });
|
|
889
|
+
}
|
|
1511
890
|
} else if (!isPrimarilyVertical && dx > 0) {
|
|
1512
891
|
const startX = sx + sw;
|
|
1513
892
|
const startY = sy + sh / 2;
|
|
@@ -1654,6 +1033,85 @@ var init_boundary_event = __esm({
|
|
|
1654
1033
|
}
|
|
1655
1034
|
});
|
|
1656
1035
|
|
|
1036
|
+
// src/layout/edge-routing/geometry-utils.ts
|
|
1037
|
+
function distance(p1, p2) {
|
|
1038
|
+
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
|
1039
|
+
}
|
|
1040
|
+
function segmentIntersectsRect(p1, p2, rect) {
|
|
1041
|
+
const margin = 5;
|
|
1042
|
+
const left = rect.x - margin;
|
|
1043
|
+
const right = rect.x + rect.width + margin;
|
|
1044
|
+
const top = rect.y - margin;
|
|
1045
|
+
const bottom = rect.y + rect.height + margin;
|
|
1046
|
+
if (p1.x < left && p2.x < left || p1.x > right && p2.x > right) return false;
|
|
1047
|
+
if (p1.y < top && p2.y < top || p1.y > bottom && p2.y > bottom) return false;
|
|
1048
|
+
if (Math.abs(p1.x - p2.x) < 1) {
|
|
1049
|
+
const x = p1.x;
|
|
1050
|
+
const minY = Math.min(p1.y, p2.y);
|
|
1051
|
+
const maxY = Math.max(p1.y, p2.y);
|
|
1052
|
+
return x >= left && x <= right && maxY >= top && minY <= bottom;
|
|
1053
|
+
}
|
|
1054
|
+
if (Math.abs(p1.y - p2.y) < 1) {
|
|
1055
|
+
const y = p1.y;
|
|
1056
|
+
const minX = Math.min(p1.x, p2.x);
|
|
1057
|
+
const maxX = Math.max(p1.x, p2.x);
|
|
1058
|
+
return y >= top && y <= bottom && maxX >= left && minX <= right;
|
|
1059
|
+
}
|
|
1060
|
+
return true;
|
|
1061
|
+
}
|
|
1062
|
+
function scoreRoute(start, bendPoints, end, obstacles) {
|
|
1063
|
+
let score = 0;
|
|
1064
|
+
const crossingPenalty = 1e3;
|
|
1065
|
+
const lengthWeight = 0.1;
|
|
1066
|
+
const path = [start, ...bendPoints, end];
|
|
1067
|
+
for (const obs of obstacles) {
|
|
1068
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
1069
|
+
const p1 = path[i];
|
|
1070
|
+
const p2 = path[i + 1];
|
|
1071
|
+
if (p1 && p2 && segmentIntersectsRect(p1, p2, obs)) {
|
|
1072
|
+
score += crossingPenalty;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
1077
|
+
const p1 = path[i];
|
|
1078
|
+
const p2 = path[i + 1];
|
|
1079
|
+
if (p1 && p2) {
|
|
1080
|
+
score += distance(p1, p2) * lengthWeight;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return score;
|
|
1084
|
+
}
|
|
1085
|
+
function findClearVerticalPath(x, startY, endY, obstacles) {
|
|
1086
|
+
const minY = Math.min(startY, endY);
|
|
1087
|
+
const maxY = Math.max(startY, endY);
|
|
1088
|
+
const margin = 10;
|
|
1089
|
+
for (const obs of obstacles) {
|
|
1090
|
+
const obsLeft = obs.x - margin;
|
|
1091
|
+
const obsRight = obs.x + obs.width + margin;
|
|
1092
|
+
const obsTop = obs.y;
|
|
1093
|
+
const obsBottom = obs.y + obs.height;
|
|
1094
|
+
if (x >= obsLeft && x <= obsRight) {
|
|
1095
|
+
if (obsBottom > minY && obsTop < maxY) {
|
|
1096
|
+
const spaceAbove = obsTop - minY;
|
|
1097
|
+
const spaceBelow = maxY - obsBottom;
|
|
1098
|
+
if (spaceBelow > spaceAbove && obsBottom + margin < maxY) {
|
|
1099
|
+
return obsBottom + margin;
|
|
1100
|
+
} else if (obsTop - margin > minY) {
|
|
1101
|
+
return obsTop - margin;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
var init_geometry_utils = __esm({
|
|
1109
|
+
"src/layout/edge-routing/geometry-utils.ts"() {
|
|
1110
|
+
"use strict";
|
|
1111
|
+
init_cjs_shims();
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1657
1115
|
// src/types/bpmn-constants.ts
|
|
1658
1116
|
var DEFAULT_SIZES, BPMN_ELEMENT_MAP, EVENT_DEFINITION_MAP, DEFAULT_ELK_OPTIONS, EVENT_TYPES, TASK_TYPES, GATEWAY_TYPES, SUBPROCESS_TYPES, GROUP_TYPE, ARTIFACT_TYPES_SET;
|
|
1659
1117
|
var init_bpmn_constants = __esm({
|
|
@@ -2309,9 +1767,9 @@ var init_lane_arranger = __esm({
|
|
|
2309
1767
|
laneHeaderWidth = 30;
|
|
2310
1768
|
lanePadding = 0;
|
|
2311
1769
|
// No extra padding - tight fit
|
|
2312
|
-
laneExtraWidth =
|
|
1770
|
+
laneExtraWidth = 130;
|
|
2313
1771
|
// Extra width for each lane
|
|
2314
|
-
laneExtraHeight =
|
|
1772
|
+
laneExtraHeight = 120;
|
|
2315
1773
|
// Extra height for each lane
|
|
2316
1774
|
/**
|
|
2317
1775
|
* Rearrange lanes within pools to stack vertically
|
|
@@ -2387,16 +1845,21 @@ var init_lane_arranger = __esm({
|
|
|
2387
1845
|
}
|
|
2388
1846
|
/**
|
|
2389
1847
|
* Recursively build lane structure with positioned nodes
|
|
1848
|
+
* Uses ConstraintSolver for vertical stacking
|
|
2390
1849
|
*/
|
|
2391
1850
|
buildLaneStructure(origChildren, layoutedNodes, nodeToLane, startY, maxRight) {
|
|
2392
1851
|
const lanes = [];
|
|
2393
|
-
let currentY = startY;
|
|
2394
1852
|
const origLanes = origChildren.filter((c) => c.bpmn?.type === "lane");
|
|
2395
1853
|
origLanes.sort((a, b) => {
|
|
2396
1854
|
const partA = a.layoutOptions?.["elk.partitioning.partition"];
|
|
2397
1855
|
const partB = b.layoutOptions?.["elk.partitioning.partition"];
|
|
2398
1856
|
return (partA !== void 0 ? Number(partA) : 0) - (partB !== void 0 ? Number(partB) : 0);
|
|
2399
1857
|
});
|
|
1858
|
+
if (origLanes.length === 0) {
|
|
1859
|
+
return { lanes: [], totalHeight: 0 };
|
|
1860
|
+
}
|
|
1861
|
+
const laneHeights = /* @__PURE__ */ new Map();
|
|
1862
|
+
const laneNodes = /* @__PURE__ */ new Map();
|
|
2400
1863
|
for (const origLane of origLanes) {
|
|
2401
1864
|
const hasNestedLanes = origLane.children?.some((c) => c.bpmn?.type === "lane");
|
|
2402
1865
|
if (hasNestedLanes) {
|
|
@@ -2408,22 +1871,8 @@ var init_lane_arranger = __esm({
|
|
|
2408
1871
|
0,
|
|
2409
1872
|
nestedWidth
|
|
2410
1873
|
);
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
}
|
|
2414
|
-
const laneNode = {
|
|
2415
|
-
id: origLane.id,
|
|
2416
|
-
x: this.laneHeaderWidth,
|
|
2417
|
-
y: currentY,
|
|
2418
|
-
width: maxRight,
|
|
2419
|
-
// Fill full width
|
|
2420
|
-
height: nested.totalHeight,
|
|
2421
|
-
// Tight fit
|
|
2422
|
-
children: nested.lanes,
|
|
2423
|
-
bpmn: origLane.bpmn
|
|
2424
|
-
};
|
|
2425
|
-
lanes.push(laneNode);
|
|
2426
|
-
currentY += laneNode.height;
|
|
1874
|
+
laneHeights.set(origLane.id, nested.totalHeight);
|
|
1875
|
+
laneNodes.set(origLane.id, nested.lanes);
|
|
2427
1876
|
} else {
|
|
2428
1877
|
const nodesInLane = [];
|
|
2429
1878
|
if (origLane.children) {
|
|
@@ -2440,23 +1889,39 @@ var init_lane_arranger = __esm({
|
|
|
2440
1889
|
const contentHeight = nodesInLane.length > 0 ? maxY - minY : 50;
|
|
2441
1890
|
const laneHeight = contentHeight + this.laneExtraHeight;
|
|
2442
1891
|
const yOffset = nodesInLane.length > 0 ? this.laneExtraHeight / 2 - minY : 0;
|
|
1892
|
+
const xOffset = this.laneExtraWidth / 2;
|
|
2443
1893
|
for (const node of nodesInLane) {
|
|
1894
|
+
node.x = (node.x ?? 0) + xOffset;
|
|
2444
1895
|
node.y = (node.y ?? 0) + yOffset;
|
|
2445
1896
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
x: this.laneHeaderWidth,
|
|
2449
|
-
y: currentY,
|
|
2450
|
-
width: maxRight,
|
|
2451
|
-
height: laneHeight,
|
|
2452
|
-
children: nodesInLane,
|
|
2453
|
-
bpmn: origLane.bpmn
|
|
2454
|
-
};
|
|
2455
|
-
lanes.push(laneNode);
|
|
2456
|
-
currentY += laneHeight;
|
|
1897
|
+
laneHeights.set(origLane.id, laneHeight);
|
|
1898
|
+
laneNodes.set(origLane.id, nodesInLane);
|
|
2457
1899
|
}
|
|
2458
1900
|
}
|
|
2459
|
-
|
|
1901
|
+
let currentY = startY;
|
|
1902
|
+
for (const origLane of origLanes) {
|
|
1903
|
+
const height = laneHeights.get(origLane.id) ?? 100;
|
|
1904
|
+
const children = laneNodes.get(origLane.id) ?? [];
|
|
1905
|
+
const hasNestedLanes = origLane.children?.some((c) => c.bpmn?.type === "lane");
|
|
1906
|
+
if (hasNestedLanes) {
|
|
1907
|
+
for (const nestedLane of children) {
|
|
1908
|
+
nestedLane.width = maxRight - this.laneHeaderWidth;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
const laneNode = {
|
|
1912
|
+
id: origLane.id,
|
|
1913
|
+
x: this.laneHeaderWidth,
|
|
1914
|
+
y: currentY,
|
|
1915
|
+
width: maxRight,
|
|
1916
|
+
height,
|
|
1917
|
+
children,
|
|
1918
|
+
bpmn: origLane.bpmn
|
|
1919
|
+
};
|
|
1920
|
+
lanes.push(laneNode);
|
|
1921
|
+
currentY += height;
|
|
1922
|
+
}
|
|
1923
|
+
const totalHeight = currentY - startY;
|
|
1924
|
+
return { lanes, totalHeight };
|
|
2460
1925
|
}
|
|
2461
1926
|
/**
|
|
2462
1927
|
* Rearrange nested lanes within a parent lane
|
|
@@ -2566,18 +2031,55 @@ var init_lane_arranger = __esm({
|
|
|
2566
2031
|
}
|
|
2567
2032
|
const waypoints = [];
|
|
2568
2033
|
waypoints.push({ x: startX, y: startY });
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2034
|
+
const obstaclesInPath = this.getObstaclesInPath(
|
|
2035
|
+
startX,
|
|
2036
|
+
startY,
|
|
2037
|
+
endX,
|
|
2038
|
+
endY,
|
|
2039
|
+
sourceId,
|
|
2040
|
+
targetId,
|
|
2041
|
+
nodePositions
|
|
2042
|
+
);
|
|
2043
|
+
if (Math.abs(startY - endY) > 10 || obstaclesInPath.length > 0) {
|
|
2044
|
+
if (obstaclesInPath.length > 0 && Math.abs(startY - endY) <= 10) {
|
|
2045
|
+
const routePoints = this.routeAroundObstacles(
|
|
2046
|
+
startX,
|
|
2047
|
+
startY,
|
|
2048
|
+
endX,
|
|
2049
|
+
endY,
|
|
2050
|
+
obstaclesInPath,
|
|
2051
|
+
nodePositions
|
|
2052
|
+
);
|
|
2053
|
+
for (const pt of routePoints) {
|
|
2054
|
+
waypoints.push(pt);
|
|
2055
|
+
}
|
|
2056
|
+
} else {
|
|
2057
|
+
const midX = this.findClearMidX(
|
|
2058
|
+
startX,
|
|
2059
|
+
endX,
|
|
2060
|
+
startY,
|
|
2061
|
+
endY,
|
|
2062
|
+
sourceId,
|
|
2063
|
+
targetId,
|
|
2064
|
+
nodePositions
|
|
2065
|
+
);
|
|
2066
|
+
if (midX !== null) {
|
|
2067
|
+
waypoints.push({ x: midX, y: startY });
|
|
2068
|
+
waypoints.push({ x: midX, y: endY });
|
|
2069
|
+
} else {
|
|
2070
|
+
const routePoints = this.routeAroundObstacles(
|
|
2071
|
+
startX,
|
|
2072
|
+
startY,
|
|
2073
|
+
endX,
|
|
2074
|
+
endY,
|
|
2075
|
+
obstaclesInPath,
|
|
2076
|
+
nodePositions
|
|
2077
|
+
);
|
|
2078
|
+
for (const pt of routePoints) {
|
|
2079
|
+
waypoints.push(pt);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2581
2083
|
}
|
|
2582
2084
|
waypoints.push({ x: endX, y: endY });
|
|
2583
2085
|
if (isDebugEnabled()) {
|
|
@@ -2593,12 +2095,88 @@ var init_lane_arranger = __esm({
|
|
|
2593
2095
|
}
|
|
2594
2096
|
}
|
|
2595
2097
|
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Get obstacles in the direct path from source to target.
|
|
2100
|
+
* This handles the case where source and target are at similar Y positions
|
|
2101
|
+
* but there are nodes in between.
|
|
2102
|
+
*/
|
|
2103
|
+
getObstaclesInPath(startX, startY, endX, endY, sourceId, targetId, nodePositions) {
|
|
2104
|
+
const minX = Math.min(startX, endX);
|
|
2105
|
+
const maxX = Math.max(startX, endX);
|
|
2106
|
+
const minY = Math.min(startY, endY);
|
|
2107
|
+
const maxY = Math.max(startY, endY);
|
|
2108
|
+
const obstacles = [];
|
|
2109
|
+
const flowNodePatterns = [
|
|
2110
|
+
/^task_/,
|
|
2111
|
+
/^gateway_/,
|
|
2112
|
+
/^start_/,
|
|
2113
|
+
/^end_/,
|
|
2114
|
+
/^subprocess_/,
|
|
2115
|
+
/^call_/,
|
|
2116
|
+
/^intermediate_/,
|
|
2117
|
+
/^event_/,
|
|
2118
|
+
/^catch_/
|
|
2119
|
+
];
|
|
2120
|
+
for (const [nodeId, pos] of nodePositions) {
|
|
2121
|
+
if (nodeId === sourceId || nodeId === targetId) continue;
|
|
2122
|
+
if (nodeId.startsWith("lane_")) continue;
|
|
2123
|
+
const isFlowNode = flowNodePatterns.some((pattern) => pattern.test(nodeId));
|
|
2124
|
+
if (!isFlowNode) continue;
|
|
2125
|
+
const nodeLeft = pos.x;
|
|
2126
|
+
const nodeRight = pos.x + pos.width;
|
|
2127
|
+
const nodeTop = pos.y;
|
|
2128
|
+
const nodeBottom = pos.y + pos.height;
|
|
2129
|
+
const xOverlap = nodeRight > minX && nodeLeft < maxX;
|
|
2130
|
+
if (xOverlap) {
|
|
2131
|
+
if (nodeTop <= maxY && nodeBottom >= minY) {
|
|
2132
|
+
if (isDebugEnabled()) {
|
|
2133
|
+
console.log(`[BPMN] getObstaclesInPath: found obstacle ${nodeId} at x=[${nodeLeft}, ${nodeRight}], y=[${nodeTop}, ${nodeBottom}]`);
|
|
2134
|
+
}
|
|
2135
|
+
obstacles.push({ id: nodeId, ...pos });
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
obstacles.sort((a, b) => a.x - b.x);
|
|
2140
|
+
return obstacles;
|
|
2141
|
+
}
|
|
2142
|
+
/**
|
|
2143
|
+
* Route around obstacles when source and target are at similar Y levels.
|
|
2144
|
+
* Decides whether to go above or below based on available space.
|
|
2145
|
+
*/
|
|
2146
|
+
routeAroundObstacles(startX, startY, endX, endY, obstacles, nodePositions) {
|
|
2147
|
+
const margin = 20;
|
|
2148
|
+
const points = [];
|
|
2149
|
+
let minObsY = Infinity;
|
|
2150
|
+
let maxObsY = -Infinity;
|
|
2151
|
+
let minObsX = Infinity;
|
|
2152
|
+
let maxObsX = -Infinity;
|
|
2153
|
+
for (const obs of obstacles) {
|
|
2154
|
+
minObsY = Math.min(minObsY, obs.y);
|
|
2155
|
+
maxObsY = Math.max(maxObsY, obs.y + obs.height);
|
|
2156
|
+
minObsX = Math.min(minObsX, obs.x);
|
|
2157
|
+
maxObsX = Math.max(maxObsX, obs.x + obs.width);
|
|
2158
|
+
}
|
|
2159
|
+
const spaceAbove = minObsY - margin;
|
|
2160
|
+
const spaceBelow = maxObsY + margin;
|
|
2161
|
+
const obsCenterY = (minObsY + maxObsY) / 2;
|
|
2162
|
+
const goAbove = startY < obsCenterY || spaceAbove > 0;
|
|
2163
|
+
const routeY = goAbove ? minObsY - margin : maxObsY + margin;
|
|
2164
|
+
if (isDebugEnabled()) {
|
|
2165
|
+
console.log(`[BPMN] routeAroundObstacles: goAbove=${goAbove}, routeY=${routeY}, minObsX=${minObsX}, maxObsX=${maxObsX}`);
|
|
2166
|
+
}
|
|
2167
|
+
points.push({ x: startX, y: routeY });
|
|
2168
|
+
points.push({ x: maxObsX + margin, y: routeY });
|
|
2169
|
+
points.push({ x: maxObsX + margin, y: endY });
|
|
2170
|
+
return points;
|
|
2171
|
+
}
|
|
2596
2172
|
/**
|
|
2597
2173
|
* Find a clear X position for vertical edge segment that avoids obstacles.
|
|
2598
2174
|
* Checks all three segments of the L-shaped path:
|
|
2599
2175
|
* 1. Horizontal from (startX, startY) to (midX, startY)
|
|
2600
2176
|
* 2. Vertical from (midX, startY) to (midX, endY)
|
|
2601
2177
|
* 3. Horizontal from (midX, endY) to (endX, endY)
|
|
2178
|
+
*
|
|
2179
|
+
* Returns null if no valid route can be found (caller should use routeAroundObstacles instead)
|
|
2602
2180
|
*/
|
|
2603
2181
|
findClearMidX(startX, endX, startY, endY, sourceId, targetId, nodePositions) {
|
|
2604
2182
|
const margin = 15;
|
|
@@ -2609,9 +2187,23 @@ var init_lane_arranger = __esm({
|
|
|
2609
2187
|
if (isDebugEnabled()) {
|
|
2610
2188
|
console.log(`[BPMN] findClearMidX: startX=${startX}, endX=${endX}, startY=${startY}, endY=${endY}`);
|
|
2611
2189
|
}
|
|
2190
|
+
const flowNodePatterns = [
|
|
2191
|
+
/^task_/,
|
|
2192
|
+
/^gateway_/,
|
|
2193
|
+
/^start_/,
|
|
2194
|
+
/^end_/,
|
|
2195
|
+
/^subprocess_/,
|
|
2196
|
+
/^call_/,
|
|
2197
|
+
/^intermediate_/,
|
|
2198
|
+
/^event_/,
|
|
2199
|
+
/^catch_/
|
|
2200
|
+
];
|
|
2612
2201
|
const allObstacles = [];
|
|
2613
2202
|
for (const [nodeId, pos] of nodePositions) {
|
|
2614
2203
|
if (nodeId === sourceId || nodeId === targetId) continue;
|
|
2204
|
+
if (nodeId.startsWith("lane_")) continue;
|
|
2205
|
+
const isFlowNode = flowNodePatterns.some((pattern) => pattern.test(nodeId));
|
|
2206
|
+
if (!isFlowNode) continue;
|
|
2615
2207
|
const nodeLeft = pos.x;
|
|
2616
2208
|
const nodeRight = pos.x + pos.width;
|
|
2617
2209
|
const nodeTop = pos.y;
|
|
@@ -2692,14 +2284,469 @@ var init_lane_arranger = __esm({
|
|
|
2692
2284
|
return rightMost;
|
|
2693
2285
|
}
|
|
2694
2286
|
if (isDebugEnabled()) {
|
|
2695
|
-
console.log(`[BPMN] findClearMidX: no valid route found,
|
|
2287
|
+
console.log(`[BPMN] findClearMidX: no valid route found, returning null`);
|
|
2288
|
+
}
|
|
2289
|
+
return null;
|
|
2290
|
+
}
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
});
|
|
2294
|
+
|
|
2295
|
+
// src/layout/constraint/constraint-solver.ts
|
|
2296
|
+
var kiwi, DEFAULT_OPTIONS, ConstraintSolver;
|
|
2297
|
+
var init_constraint_solver = __esm({
|
|
2298
|
+
"src/layout/constraint/constraint-solver.ts"() {
|
|
2299
|
+
"use strict";
|
|
2300
|
+
init_cjs_shims();
|
|
2301
|
+
kiwi = __toESM(require("kiwi.js"));
|
|
2302
|
+
DEFAULT_OPTIONS = {
|
|
2303
|
+
defaultStrength: "strong",
|
|
2304
|
+
debug: false
|
|
2305
|
+
};
|
|
2306
|
+
ConstraintSolver = class {
|
|
2307
|
+
solver = null;
|
|
2308
|
+
variables;
|
|
2309
|
+
pendingConstraints;
|
|
2310
|
+
kiwiConstraints;
|
|
2311
|
+
options;
|
|
2312
|
+
solved = false;
|
|
2313
|
+
constructor(options) {
|
|
2314
|
+
this.variables = /* @__PURE__ */ new Map();
|
|
2315
|
+
this.pendingConstraints = [];
|
|
2316
|
+
this.kiwiConstraints = [];
|
|
2317
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Add a node to the solver
|
|
2321
|
+
* Note: Nodes are stored but not added to solver until solve() is called
|
|
2322
|
+
*/
|
|
2323
|
+
addNode(id, initialX, initialY, width, height) {
|
|
2324
|
+
if (this.variables.has(id)) {
|
|
2325
|
+
if (this.options.debug) {
|
|
2326
|
+
console.log(`[Constraint] Node ${id} already exists, skipping`);
|
|
2327
|
+
}
|
|
2328
|
+
return;
|
|
2329
|
+
}
|
|
2330
|
+
const x = new kiwi.Variable(`${id}_x`);
|
|
2331
|
+
const y = new kiwi.Variable(`${id}_y`);
|
|
2332
|
+
this.variables.set(id, { x, y, width, height, initialX, initialY });
|
|
2333
|
+
if (this.options.debug) {
|
|
2334
|
+
console.log(`[Constraint] Added node ${id} at (${initialX}, ${initialY})`);
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Add a node from bounds
|
|
2339
|
+
*/
|
|
2340
|
+
addNodeFromBounds(id, bounds) {
|
|
2341
|
+
this.addNode(id, bounds.x, bounds.y, bounds.width, bounds.height);
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* Add multiple nodes from a map
|
|
2345
|
+
*/
|
|
2346
|
+
addNodesFromMap(nodes) {
|
|
2347
|
+
for (const [id, bounds] of nodes) {
|
|
2348
|
+
this.addNodeFromBounds(id, bounds);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
/**
|
|
2352
|
+
* Add a constraint
|
|
2353
|
+
* Note: Constraints are stored and applied when solve() is called
|
|
2354
|
+
*/
|
|
2355
|
+
addConstraint(constraint) {
|
|
2356
|
+
const nodeIds = this.getConstraintNodeIds(constraint);
|
|
2357
|
+
for (const id of nodeIds) {
|
|
2358
|
+
if (!this.variables.has(id)) {
|
|
2359
|
+
if (this.options.debug) {
|
|
2360
|
+
console.log(`[Constraint] Node ${id} not found for constraint`);
|
|
2361
|
+
}
|
|
2362
|
+
return false;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
if (constraint.type === "noOverlap") {
|
|
2366
|
+
if (this.options.debug) {
|
|
2367
|
+
console.log(`[Constraint] noOverlap constraint requires special handling`);
|
|
2368
|
+
}
|
|
2369
|
+
return false;
|
|
2370
|
+
}
|
|
2371
|
+
const strength = this.getStrength(constraint.strength);
|
|
2372
|
+
this.pendingConstraints.push({ constraint, strength });
|
|
2373
|
+
return true;
|
|
2374
|
+
}
|
|
2375
|
+
/**
|
|
2376
|
+
* Get node IDs referenced by a constraint
|
|
2377
|
+
*/
|
|
2378
|
+
getConstraintNodeIds(constraint) {
|
|
2379
|
+
switch (constraint.type) {
|
|
2380
|
+
case "alignX":
|
|
2381
|
+
case "alignY":
|
|
2382
|
+
return constraint.nodes;
|
|
2383
|
+
case "leftOf":
|
|
2384
|
+
case "rightOf":
|
|
2385
|
+
case "above":
|
|
2386
|
+
case "below":
|
|
2387
|
+
return [constraint.node, constraint.reference];
|
|
2388
|
+
case "fixedPosition":
|
|
2389
|
+
return [constraint.node];
|
|
2390
|
+
case "inContainer":
|
|
2391
|
+
return [constraint.node, constraint.container];
|
|
2392
|
+
case "minDistance":
|
|
2393
|
+
return [constraint.node1, constraint.node2];
|
|
2394
|
+
case "noOverlap":
|
|
2395
|
+
return constraint.nodes;
|
|
2396
|
+
default:
|
|
2397
|
+
return [];
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
/**
|
|
2401
|
+
* Add multiple constraints
|
|
2402
|
+
*/
|
|
2403
|
+
addConstraints(constraints) {
|
|
2404
|
+
let successCount = 0;
|
|
2405
|
+
for (const constraint of constraints) {
|
|
2406
|
+
if (this.addConstraint(constraint)) {
|
|
2407
|
+
successCount++;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return successCount;
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Build and solve the constraint system
|
|
2414
|
+
*/
|
|
2415
|
+
solve() {
|
|
2416
|
+
if (this.solved) {
|
|
2417
|
+
const result2 = /* @__PURE__ */ new Map();
|
|
2418
|
+
for (const [id, vars] of this.variables) {
|
|
2419
|
+
result2.set(id, {
|
|
2420
|
+
x: vars.x.value(),
|
|
2421
|
+
y: vars.y.value()
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
return result2;
|
|
2425
|
+
}
|
|
2426
|
+
this.solver = new kiwi.Solver();
|
|
2427
|
+
this.kiwiConstraints = [];
|
|
2428
|
+
for (const pending of this.pendingConstraints) {
|
|
2429
|
+
this.applyConstraint(pending.constraint, pending.strength);
|
|
2430
|
+
}
|
|
2431
|
+
for (const [_id, vars] of this.variables) {
|
|
2432
|
+
this.solver.addEditVariable(vars.x, kiwi.Strength.weak);
|
|
2433
|
+
this.solver.addEditVariable(vars.y, kiwi.Strength.weak);
|
|
2434
|
+
this.solver.suggestValue(vars.x, vars.initialX);
|
|
2435
|
+
this.solver.suggestValue(vars.y, vars.initialY);
|
|
2436
|
+
}
|
|
2437
|
+
try {
|
|
2438
|
+
this.solver.updateVariables();
|
|
2439
|
+
this.solved = true;
|
|
2440
|
+
} catch (error) {
|
|
2441
|
+
if (this.options.debug) {
|
|
2442
|
+
console.error(`[Constraint] Solver failed:`, error);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
const result = /* @__PURE__ */ new Map();
|
|
2446
|
+
for (const [id, vars] of this.variables) {
|
|
2447
|
+
result.set(id, {
|
|
2448
|
+
x: vars.x.value(),
|
|
2449
|
+
y: vars.y.value()
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
return result;
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
* Apply a constraint to the solver
|
|
2456
|
+
*/
|
|
2457
|
+
applyConstraint(constraint, strength) {
|
|
2458
|
+
try {
|
|
2459
|
+
switch (constraint.type) {
|
|
2460
|
+
case "alignX":
|
|
2461
|
+
return this.addAlignXConstraint(constraint, strength);
|
|
2462
|
+
case "alignY":
|
|
2463
|
+
return this.addAlignYConstraint(constraint, strength);
|
|
2464
|
+
case "leftOf":
|
|
2465
|
+
return this.addLeftOfConstraint(constraint, strength);
|
|
2466
|
+
case "rightOf":
|
|
2467
|
+
return this.addRightOfConstraint(constraint, strength);
|
|
2468
|
+
case "above":
|
|
2469
|
+
return this.addAboveConstraint(constraint, strength);
|
|
2470
|
+
case "below":
|
|
2471
|
+
return this.addBelowConstraint(constraint, strength);
|
|
2472
|
+
case "fixedPosition":
|
|
2473
|
+
return this.addFixedPositionConstraint(constraint, strength);
|
|
2474
|
+
case "inContainer":
|
|
2475
|
+
return this.addInContainerConstraint(constraint, strength);
|
|
2476
|
+
case "minDistance":
|
|
2477
|
+
return this.addMinDistanceConstraint(constraint, strength);
|
|
2478
|
+
default:
|
|
2479
|
+
return false;
|
|
2480
|
+
}
|
|
2481
|
+
} catch (error) {
|
|
2482
|
+
if (this.options.debug) {
|
|
2483
|
+
console.error(`[Constraint] Failed to apply constraint:`, error);
|
|
2484
|
+
}
|
|
2485
|
+
return false;
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
/**
|
|
2489
|
+
* Get full bounds including width and height
|
|
2490
|
+
*/
|
|
2491
|
+
solveWithBounds() {
|
|
2492
|
+
const positions = this.solve();
|
|
2493
|
+
const result = /* @__PURE__ */ new Map();
|
|
2494
|
+
for (const [id, pos] of positions) {
|
|
2495
|
+
const vars = this.variables.get(id);
|
|
2496
|
+
if (vars) {
|
|
2497
|
+
result.set(id, {
|
|
2498
|
+
x: pos.x,
|
|
2499
|
+
y: pos.y,
|
|
2500
|
+
width: vars.width,
|
|
2501
|
+
height: vars.height
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
return result;
|
|
2506
|
+
}
|
|
2507
|
+
/**
|
|
2508
|
+
* Clear all nodes and constraints
|
|
2509
|
+
*/
|
|
2510
|
+
clear() {
|
|
2511
|
+
this.solver = null;
|
|
2512
|
+
this.variables.clear();
|
|
2513
|
+
this.pendingConstraints = [];
|
|
2514
|
+
this.kiwiConstraints = [];
|
|
2515
|
+
this.solved = false;
|
|
2516
|
+
}
|
|
2517
|
+
// ============================================================================
|
|
2518
|
+
// Private: Constraint Implementations
|
|
2519
|
+
// ============================================================================
|
|
2520
|
+
addAlignXConstraint(constraint, strength) {
|
|
2521
|
+
const nodes = constraint.nodes.map((id) => this.variables.get(id)).filter((v) => v !== void 0);
|
|
2522
|
+
if (nodes.length < 2) return false;
|
|
2523
|
+
const referenceX = nodes[0].x;
|
|
2524
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
2525
|
+
const c = new kiwi.Constraint(
|
|
2526
|
+
new kiwi.Expression(nodes[i].x, [-1, referenceX]),
|
|
2527
|
+
kiwi.Operator.Eq,
|
|
2528
|
+
0,
|
|
2529
|
+
strength
|
|
2530
|
+
);
|
|
2531
|
+
this.solver.addConstraint(c);
|
|
2532
|
+
this.kiwiConstraints.push(c);
|
|
2533
|
+
}
|
|
2534
|
+
return true;
|
|
2535
|
+
}
|
|
2536
|
+
addAlignYConstraint(constraint, strength) {
|
|
2537
|
+
const nodes = constraint.nodes.map((id) => this.variables.get(id)).filter((v) => v !== void 0);
|
|
2538
|
+
if (nodes.length < 2) return false;
|
|
2539
|
+
const referenceY = nodes[0].y;
|
|
2540
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
2541
|
+
const c = new kiwi.Constraint(
|
|
2542
|
+
new kiwi.Expression(nodes[i].y, [-1, referenceY]),
|
|
2543
|
+
kiwi.Operator.Eq,
|
|
2544
|
+
0,
|
|
2545
|
+
strength
|
|
2546
|
+
);
|
|
2547
|
+
this.solver.addConstraint(c);
|
|
2548
|
+
this.kiwiConstraints.push(c);
|
|
2549
|
+
}
|
|
2550
|
+
return true;
|
|
2551
|
+
}
|
|
2552
|
+
addLeftOfConstraint(constraint, strength) {
|
|
2553
|
+
const nodeVars = this.variables.get(constraint.node);
|
|
2554
|
+
const refVars = this.variables.get(constraint.reference);
|
|
2555
|
+
if (!nodeVars || !refVars) return false;
|
|
2556
|
+
const minSeparation = nodeVars.width + constraint.minGap;
|
|
2557
|
+
const c = new kiwi.Constraint(
|
|
2558
|
+
new kiwi.Expression(refVars.x, [-1, nodeVars.x], -minSeparation),
|
|
2559
|
+
kiwi.Operator.Ge,
|
|
2560
|
+
0,
|
|
2561
|
+
strength
|
|
2562
|
+
);
|
|
2563
|
+
this.solver.addConstraint(c);
|
|
2564
|
+
this.kiwiConstraints.push(c);
|
|
2565
|
+
return true;
|
|
2566
|
+
}
|
|
2567
|
+
addRightOfConstraint(constraint, strength) {
|
|
2568
|
+
const nodeVars = this.variables.get(constraint.node);
|
|
2569
|
+
const refVars = this.variables.get(constraint.reference);
|
|
2570
|
+
if (!nodeVars || !refVars) return false;
|
|
2571
|
+
const minSeparation = refVars.width + constraint.minGap;
|
|
2572
|
+
const c = new kiwi.Constraint(
|
|
2573
|
+
new kiwi.Expression(nodeVars.x, [-1, refVars.x], -minSeparation),
|
|
2574
|
+
kiwi.Operator.Ge,
|
|
2575
|
+
0,
|
|
2576
|
+
strength
|
|
2577
|
+
);
|
|
2578
|
+
this.solver.addConstraint(c);
|
|
2579
|
+
this.kiwiConstraints.push(c);
|
|
2580
|
+
return true;
|
|
2581
|
+
}
|
|
2582
|
+
addAboveConstraint(constraint, strength) {
|
|
2583
|
+
const nodeVars = this.variables.get(constraint.node);
|
|
2584
|
+
const refVars = this.variables.get(constraint.reference);
|
|
2585
|
+
if (!nodeVars || !refVars) return false;
|
|
2586
|
+
const minSeparation = nodeVars.height + constraint.minGap;
|
|
2587
|
+
const c = new kiwi.Constraint(
|
|
2588
|
+
new kiwi.Expression(refVars.y, [-1, nodeVars.y], -minSeparation),
|
|
2589
|
+
kiwi.Operator.Ge,
|
|
2590
|
+
0,
|
|
2591
|
+
strength
|
|
2592
|
+
);
|
|
2593
|
+
this.solver.addConstraint(c);
|
|
2594
|
+
this.kiwiConstraints.push(c);
|
|
2595
|
+
return true;
|
|
2596
|
+
}
|
|
2597
|
+
addBelowConstraint(constraint, strength) {
|
|
2598
|
+
const nodeVars = this.variables.get(constraint.node);
|
|
2599
|
+
const refVars = this.variables.get(constraint.reference);
|
|
2600
|
+
if (!nodeVars || !refVars) return false;
|
|
2601
|
+
const minSeparation = refVars.height + constraint.minGap;
|
|
2602
|
+
const c = new kiwi.Constraint(
|
|
2603
|
+
new kiwi.Expression(nodeVars.y, [-1, refVars.y], -minSeparation),
|
|
2604
|
+
kiwi.Operator.Ge,
|
|
2605
|
+
0,
|
|
2606
|
+
strength
|
|
2607
|
+
);
|
|
2608
|
+
this.solver.addConstraint(c);
|
|
2609
|
+
this.kiwiConstraints.push(c);
|
|
2610
|
+
return true;
|
|
2611
|
+
}
|
|
2612
|
+
addFixedPositionConstraint(constraint, strength) {
|
|
2613
|
+
const nodeVars = this.variables.get(constraint.node);
|
|
2614
|
+
if (!nodeVars) return false;
|
|
2615
|
+
if (constraint.x !== void 0) {
|
|
2616
|
+
const c = new kiwi.Constraint(
|
|
2617
|
+
new kiwi.Expression(nodeVars.x, -constraint.x),
|
|
2618
|
+
kiwi.Operator.Eq,
|
|
2619
|
+
0,
|
|
2620
|
+
strength
|
|
2621
|
+
);
|
|
2622
|
+
this.solver.addConstraint(c);
|
|
2623
|
+
this.kiwiConstraints.push(c);
|
|
2624
|
+
}
|
|
2625
|
+
if (constraint.y !== void 0) {
|
|
2626
|
+
const c = new kiwi.Constraint(
|
|
2627
|
+
new kiwi.Expression(nodeVars.y, -constraint.y),
|
|
2628
|
+
kiwi.Operator.Eq,
|
|
2629
|
+
0,
|
|
2630
|
+
strength
|
|
2631
|
+
);
|
|
2632
|
+
this.solver.addConstraint(c);
|
|
2633
|
+
this.kiwiConstraints.push(c);
|
|
2634
|
+
}
|
|
2635
|
+
return true;
|
|
2636
|
+
}
|
|
2637
|
+
addInContainerConstraint(constraint, strength) {
|
|
2638
|
+
const nodeVars = this.variables.get(constraint.node);
|
|
2639
|
+
const containerVars = this.variables.get(constraint.container);
|
|
2640
|
+
if (!nodeVars || !containerVars) return false;
|
|
2641
|
+
const padding = constraint.padding;
|
|
2642
|
+
const leftC = new kiwi.Constraint(
|
|
2643
|
+
new kiwi.Expression(nodeVars.x, [-1, containerVars.x], -padding),
|
|
2644
|
+
kiwi.Operator.Ge,
|
|
2645
|
+
0,
|
|
2646
|
+
strength
|
|
2647
|
+
);
|
|
2648
|
+
this.solver.addConstraint(leftC);
|
|
2649
|
+
this.kiwiConstraints.push(leftC);
|
|
2650
|
+
const topC = new kiwi.Constraint(
|
|
2651
|
+
new kiwi.Expression(nodeVars.y, [-1, containerVars.y], -padding),
|
|
2652
|
+
kiwi.Operator.Ge,
|
|
2653
|
+
0,
|
|
2654
|
+
strength
|
|
2655
|
+
);
|
|
2656
|
+
this.solver.addConstraint(topC);
|
|
2657
|
+
this.kiwiConstraints.push(topC);
|
|
2658
|
+
const rightC = new kiwi.Constraint(
|
|
2659
|
+
new kiwi.Expression(
|
|
2660
|
+
containerVars.x,
|
|
2661
|
+
[-1, nodeVars.x],
|
|
2662
|
+
containerVars.width - padding - nodeVars.width
|
|
2663
|
+
),
|
|
2664
|
+
kiwi.Operator.Ge,
|
|
2665
|
+
0,
|
|
2666
|
+
strength
|
|
2667
|
+
);
|
|
2668
|
+
this.solver.addConstraint(rightC);
|
|
2669
|
+
this.kiwiConstraints.push(rightC);
|
|
2670
|
+
const bottomC = new kiwi.Constraint(
|
|
2671
|
+
new kiwi.Expression(
|
|
2672
|
+
containerVars.y,
|
|
2673
|
+
[-1, nodeVars.y],
|
|
2674
|
+
containerVars.height - padding - nodeVars.height
|
|
2675
|
+
),
|
|
2676
|
+
kiwi.Operator.Ge,
|
|
2677
|
+
0,
|
|
2678
|
+
strength
|
|
2679
|
+
);
|
|
2680
|
+
this.solver.addConstraint(bottomC);
|
|
2681
|
+
this.kiwiConstraints.push(bottomC);
|
|
2682
|
+
return true;
|
|
2683
|
+
}
|
|
2684
|
+
addMinDistanceConstraint(constraint, strength) {
|
|
2685
|
+
const node1Vars = this.variables.get(constraint.node1);
|
|
2686
|
+
const node2Vars = this.variables.get(constraint.node2);
|
|
2687
|
+
if (!node1Vars || !node2Vars) return false;
|
|
2688
|
+
if (constraint.axis === "x") {
|
|
2689
|
+
const size = node1Vars.width;
|
|
2690
|
+
const c = new kiwi.Constraint(
|
|
2691
|
+
new kiwi.Expression(
|
|
2692
|
+
node2Vars.x,
|
|
2693
|
+
[-1, node1Vars.x],
|
|
2694
|
+
-(size + constraint.minDistance)
|
|
2695
|
+
),
|
|
2696
|
+
kiwi.Operator.Ge,
|
|
2697
|
+
0,
|
|
2698
|
+
strength
|
|
2699
|
+
);
|
|
2700
|
+
this.solver.addConstraint(c);
|
|
2701
|
+
this.kiwiConstraints.push(c);
|
|
2702
|
+
} else {
|
|
2703
|
+
const size = node1Vars.height;
|
|
2704
|
+
const c = new kiwi.Constraint(
|
|
2705
|
+
new kiwi.Expression(
|
|
2706
|
+
node2Vars.y,
|
|
2707
|
+
[-1, node1Vars.y],
|
|
2708
|
+
-(size + constraint.minDistance)
|
|
2709
|
+
),
|
|
2710
|
+
kiwi.Operator.Ge,
|
|
2711
|
+
0,
|
|
2712
|
+
strength
|
|
2713
|
+
);
|
|
2714
|
+
this.solver.addConstraint(c);
|
|
2715
|
+
this.kiwiConstraints.push(c);
|
|
2716
|
+
}
|
|
2717
|
+
return true;
|
|
2718
|
+
}
|
|
2719
|
+
// ============================================================================
|
|
2720
|
+
// Private: Helpers
|
|
2721
|
+
// ============================================================================
|
|
2722
|
+
getStrength(strength) {
|
|
2723
|
+
const s = strength ?? this.options.defaultStrength ?? "strong";
|
|
2724
|
+
switch (s) {
|
|
2725
|
+
case "required":
|
|
2726
|
+
return kiwi.Strength.required;
|
|
2727
|
+
case "strong":
|
|
2728
|
+
return kiwi.Strength.strong;
|
|
2729
|
+
case "medium":
|
|
2730
|
+
return kiwi.Strength.medium;
|
|
2731
|
+
case "weak":
|
|
2732
|
+
return kiwi.Strength.weak;
|
|
2733
|
+
default:
|
|
2734
|
+
return kiwi.Strength.strong;
|
|
2696
2735
|
}
|
|
2697
|
-
return simpleMidX;
|
|
2698
2736
|
}
|
|
2699
2737
|
};
|
|
2700
2738
|
}
|
|
2701
2739
|
});
|
|
2702
2740
|
|
|
2741
|
+
// src/layout/constraint/index.ts
|
|
2742
|
+
var init_constraint = __esm({
|
|
2743
|
+
"src/layout/constraint/index.ts"() {
|
|
2744
|
+
"use strict";
|
|
2745
|
+
init_cjs_shims();
|
|
2746
|
+
init_constraint_solver();
|
|
2747
|
+
}
|
|
2748
|
+
});
|
|
2749
|
+
|
|
2703
2750
|
// src/layout/post-processing/pool-arranger.ts
|
|
2704
2751
|
var PoolArranger;
|
|
2705
2752
|
var init_pool_arranger = __esm({
|
|
@@ -2707,12 +2754,13 @@ var init_pool_arranger = __esm({
|
|
|
2707
2754
|
"use strict";
|
|
2708
2755
|
init_cjs_shims();
|
|
2709
2756
|
init_artifact_positioner();
|
|
2757
|
+
init_constraint();
|
|
2710
2758
|
PoolArranger = class {
|
|
2711
2759
|
poolHeaderWidth = 55;
|
|
2712
2760
|
poolPaddingX = 25;
|
|
2713
|
-
poolPaddingY =
|
|
2761
|
+
poolPaddingY = 40;
|
|
2714
2762
|
minPoolHeight = 100;
|
|
2715
|
-
poolExtraWidth =
|
|
2763
|
+
poolExtraWidth = 140;
|
|
2716
2764
|
poolExtraHeight = 80;
|
|
2717
2765
|
/**
|
|
2718
2766
|
* Rearrange pools within collaborations
|
|
@@ -2774,24 +2822,41 @@ var init_pool_arranger = __esm({
|
|
|
2774
2822
|
maxPoolWidth = Math.max(maxPoolWidth, (pool.width ?? 680) + this.poolExtraWidth);
|
|
2775
2823
|
}
|
|
2776
2824
|
}
|
|
2777
|
-
|
|
2778
|
-
const nodePositions = /* @__PURE__ */ new Map();
|
|
2825
|
+
const poolHeights = /* @__PURE__ */ new Map();
|
|
2779
2826
|
for (const pool of pools) {
|
|
2780
2827
|
const origPool = origPoolMap.get(pool.id);
|
|
2781
2828
|
const isBlackBox = origPool?.bpmn?.isBlackBox === true;
|
|
2782
2829
|
const hasLanes = origPool?.children?.some((c) => c.bpmn?.type === "lane");
|
|
2783
|
-
pool.
|
|
2784
|
-
pool.y = currentY;
|
|
2830
|
+
pool.width = maxPoolWidth;
|
|
2785
2831
|
if (isBlackBox) {
|
|
2786
|
-
pool.width = maxPoolWidth;
|
|
2787
2832
|
pool.height = 60;
|
|
2788
|
-
} else if (hasLanes) {
|
|
2789
|
-
pool.width = maxPoolWidth;
|
|
2790
|
-
} else {
|
|
2791
|
-
pool.width = maxPoolWidth;
|
|
2833
|
+
} else if (!hasLanes) {
|
|
2792
2834
|
pool.height = (pool.height ?? 200) + this.poolExtraHeight;
|
|
2793
|
-
this.offsetPoolChildren(pool, this.poolExtraHeight / 2);
|
|
2835
|
+
this.offsetPoolChildren(pool, this.poolExtraWidth / 2, this.poolExtraHeight / 2);
|
|
2794
2836
|
}
|
|
2837
|
+
poolHeights.set(pool.id, pool.height ?? 200);
|
|
2838
|
+
}
|
|
2839
|
+
const solver = new ConstraintSolver();
|
|
2840
|
+
for (const pool of pools) {
|
|
2841
|
+
const height = poolHeights.get(pool.id) ?? 200;
|
|
2842
|
+
solver.addNode(pool.id, 0, 0, maxPoolWidth, height);
|
|
2843
|
+
}
|
|
2844
|
+
for (let i = 1; i < pools.length; i++) {
|
|
2845
|
+
solver.addConstraint({
|
|
2846
|
+
type: "below",
|
|
2847
|
+
node: pools[i].id,
|
|
2848
|
+
reference: pools[i - 1].id,
|
|
2849
|
+
minGap: 0,
|
|
2850
|
+
strength: "required"
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
const positions = solver.solve();
|
|
2854
|
+
const nodePositions = /* @__PURE__ */ new Map();
|
|
2855
|
+
let totalHeight = 0;
|
|
2856
|
+
for (const pool of pools) {
|
|
2857
|
+
const pos = positions.get(pool.id);
|
|
2858
|
+
pool.x = 0;
|
|
2859
|
+
pool.y = pos?.y ?? totalHeight;
|
|
2795
2860
|
nodePositions.set(pool.id, {
|
|
2796
2861
|
x: pool.x,
|
|
2797
2862
|
y: pool.y,
|
|
@@ -2799,10 +2864,10 @@ var init_pool_arranger = __esm({
|
|
|
2799
2864
|
height: pool.height ?? 200
|
|
2800
2865
|
});
|
|
2801
2866
|
this.collectNodePositionsInPool(pool, pool.x, pool.y, nodePositions);
|
|
2802
|
-
|
|
2867
|
+
totalHeight = pool.y + (pool.height ?? 200);
|
|
2803
2868
|
}
|
|
2804
2869
|
collab.width = maxPoolWidth;
|
|
2805
|
-
collab.height =
|
|
2870
|
+
collab.height = totalHeight;
|
|
2806
2871
|
if (collab.edges && origCollab.edges) {
|
|
2807
2872
|
this.recalculateMessageFlows(collab.edges, nodePositions, pools, origCollab.edges);
|
|
2808
2873
|
}
|
|
@@ -2988,22 +3053,34 @@ var init_pool_arranger = __esm({
|
|
|
2988
3053
|
/**
|
|
2989
3054
|
* Offset all children within a pool
|
|
2990
3055
|
*/
|
|
2991
|
-
offsetPoolChildren(pool, offsetY) {
|
|
3056
|
+
offsetPoolChildren(pool, offsetX, offsetY) {
|
|
2992
3057
|
if (!pool.children) return;
|
|
2993
3058
|
for (const child of pool.children) {
|
|
3059
|
+
if (child.x !== void 0) {
|
|
3060
|
+
child.x += offsetX;
|
|
3061
|
+
}
|
|
2994
3062
|
if (child.y !== void 0) {
|
|
2995
3063
|
child.y += offsetY;
|
|
2996
3064
|
}
|
|
2997
|
-
this.offsetPoolChildren(child, 0);
|
|
3065
|
+
this.offsetPoolChildren(child, 0, 0);
|
|
2998
3066
|
}
|
|
2999
3067
|
if (pool.edges) {
|
|
3000
3068
|
for (const edge of pool.edges) {
|
|
3001
3069
|
if (edge.sections) {
|
|
3002
3070
|
for (const section of edge.sections) {
|
|
3003
|
-
if (section.startPoint)
|
|
3004
|
-
|
|
3071
|
+
if (section.startPoint) {
|
|
3072
|
+
section.startPoint.x += offsetX;
|
|
3073
|
+
section.startPoint.y += offsetY;
|
|
3074
|
+
}
|
|
3075
|
+
if (section.endPoint) {
|
|
3076
|
+
section.endPoint.x += offsetX;
|
|
3077
|
+
section.endPoint.y += offsetY;
|
|
3078
|
+
}
|
|
3005
3079
|
if (section.bendPoints) {
|
|
3006
|
-
for (const bp of section.bendPoints)
|
|
3080
|
+
for (const bp of section.bendPoints) {
|
|
3081
|
+
bp.x += offsetX;
|
|
3082
|
+
bp.y += offsetY;
|
|
3083
|
+
}
|
|
3007
3084
|
}
|
|
3008
3085
|
}
|
|
3009
3086
|
}
|
|
@@ -3039,6 +3116,13 @@ var init_pool_arranger = __esm({
|
|
|
3039
3116
|
}
|
|
3040
3117
|
}
|
|
3041
3118
|
}
|
|
3119
|
+
const blackboxPoolIds = /* @__PURE__ */ new Set();
|
|
3120
|
+
for (const pool of pools) {
|
|
3121
|
+
const isBlackbox = !pool.children || pool.children.length === 0 || (pool.height ?? 0) <= 80;
|
|
3122
|
+
if (isBlackbox) {
|
|
3123
|
+
blackboxPoolIds.add(pool.id);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3042
3126
|
for (const edge of edges) {
|
|
3043
3127
|
const sourceId = edge.sources?.[0];
|
|
3044
3128
|
const targetId = edge.targets?.[0];
|
|
@@ -3053,7 +3137,7 @@ var init_pool_arranger = __esm({
|
|
|
3053
3137
|
if (isSequenceFlow) {
|
|
3054
3138
|
this.createSequenceFlowWaypoints(sourcePos, targetPos, waypoints);
|
|
3055
3139
|
} else if (isMessageFlow) {
|
|
3056
|
-
this.createMessageFlowWaypoints(sourcePos, targetPos, waypoints);
|
|
3140
|
+
this.createMessageFlowWaypoints(sourcePos, targetPos, waypoints, nodePositions, targetId, sourceId, blackboxPoolIds);
|
|
3057
3141
|
} else if (isDataAssociation) {
|
|
3058
3142
|
edge._absoluteCoords = true;
|
|
3059
3143
|
edge.sections = [];
|
|
@@ -3136,28 +3220,203 @@ var init_pool_arranger = __esm({
|
|
|
3136
3220
|
}
|
|
3137
3221
|
/**
|
|
3138
3222
|
* Create waypoints for message flows
|
|
3223
|
+
* Routes message flows to avoid being too close to other nodes in the target pool
|
|
3139
3224
|
*/
|
|
3140
|
-
createMessageFlowWaypoints(sourcePos, targetPos, waypoints) {
|
|
3225
|
+
createMessageFlowWaypoints(sourcePos, targetPos, waypoints, nodePositions, targetId, sourceId, blackboxPoolIds) {
|
|
3141
3226
|
let startX, startY, endX, endY;
|
|
3142
|
-
|
|
3227
|
+
const goingDown = sourcePos.y + sourcePos.height < targetPos.y;
|
|
3228
|
+
const isTargetBlackbox = targetId ? blackboxPoolIds?.has(targetId) ?? false : false;
|
|
3229
|
+
const isSourceBlackbox = sourceId ? blackboxPoolIds?.has(sourceId) ?? false : false;
|
|
3230
|
+
if (goingDown) {
|
|
3143
3231
|
startX = sourcePos.x + sourcePos.width / 2;
|
|
3144
3232
|
startY = sourcePos.y + sourcePos.height;
|
|
3145
|
-
|
|
3233
|
+
if (isTargetBlackbox) {
|
|
3234
|
+
const targetLeft = targetPos.x;
|
|
3235
|
+
const targetRight = targetPos.x + targetPos.width;
|
|
3236
|
+
if (startX >= targetLeft + 20 && startX <= targetRight - 20) {
|
|
3237
|
+
endX = startX;
|
|
3238
|
+
} else {
|
|
3239
|
+
endX = targetPos.x + targetPos.width / 2;
|
|
3240
|
+
}
|
|
3241
|
+
} else {
|
|
3242
|
+
endX = targetPos.x + targetPos.width / 2;
|
|
3243
|
+
}
|
|
3146
3244
|
endY = targetPos.y;
|
|
3147
3245
|
} else {
|
|
3148
3246
|
startX = sourcePos.x + sourcePos.width / 2;
|
|
3149
3247
|
startY = sourcePos.y;
|
|
3150
|
-
|
|
3248
|
+
if (isTargetBlackbox) {
|
|
3249
|
+
const targetLeft = targetPos.x;
|
|
3250
|
+
const targetRight = targetPos.x + targetPos.width;
|
|
3251
|
+
if (startX >= targetLeft + 20 && startX <= targetRight - 20) {
|
|
3252
|
+
endX = startX;
|
|
3253
|
+
} else {
|
|
3254
|
+
endX = targetPos.x + targetPos.width / 2;
|
|
3255
|
+
}
|
|
3256
|
+
} else {
|
|
3257
|
+
endX = targetPos.x + targetPos.width / 2;
|
|
3258
|
+
}
|
|
3151
3259
|
endY = targetPos.y + targetPos.height;
|
|
3152
3260
|
}
|
|
3153
|
-
|
|
3261
|
+
if (isSourceBlackbox) {
|
|
3262
|
+
const sourceLeft = sourcePos.x;
|
|
3263
|
+
const sourceRight = sourcePos.x + sourcePos.width;
|
|
3264
|
+
if (endX >= sourceLeft + 20 && endX <= sourceRight - 20) {
|
|
3265
|
+
startX = endX;
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
const containerPatterns = [/^pool_/, /^participant_/, /^lane_/, /^process_/, /^collaboration_/];
|
|
3269
|
+
const minClearance = 25;
|
|
3270
|
+
const sourcePoolYMin = sourcePos.y - 50;
|
|
3271
|
+
const sourcePoolYMax = sourcePos.y + sourcePos.height + 50;
|
|
3272
|
+
const targetPoolYMin = targetPos.y - 50;
|
|
3273
|
+
const targetPoolYMax = targetPos.y + targetPos.height + 50;
|
|
3274
|
+
const fullVerticalMinY = Math.min(startY, endY);
|
|
3275
|
+
const fullVerticalMaxY = Math.max(startY, endY);
|
|
3276
|
+
const nodesBlockingDirectPath = [];
|
|
3277
|
+
for (const [nodeId, bounds] of nodePositions) {
|
|
3278
|
+
if (nodeId === targetId || nodeId === sourceId) continue;
|
|
3279
|
+
const isContainer = containerPatterns.some((pattern) => pattern.test(nodeId));
|
|
3280
|
+
if (isContainer) continue;
|
|
3281
|
+
const nodeLeft = bounds.x;
|
|
3282
|
+
const nodeRight = bounds.x + bounds.width;
|
|
3283
|
+
const nodeTop = bounds.y;
|
|
3284
|
+
const nodeBottom = bounds.y + bounds.height;
|
|
3285
|
+
const inSourcePool = nodeTop >= sourcePoolYMin && nodeBottom <= sourcePoolYMax;
|
|
3286
|
+
const inTargetPool = nodeTop >= targetPoolYMin && nodeBottom <= targetPoolYMax;
|
|
3287
|
+
if (inSourcePool || inTargetPool) continue;
|
|
3288
|
+
const overlapsVerticalLine = startX >= nodeLeft - minClearance && startX <= nodeRight + minClearance;
|
|
3289
|
+
const nodeInVerticalRange = nodeBottom > fullVerticalMinY && nodeTop < fullVerticalMaxY;
|
|
3290
|
+
if (overlapsVerticalLine && nodeInVerticalRange) {
|
|
3291
|
+
nodesBlockingDirectPath.push({ id: nodeId, bounds });
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
let finalRouteX = startX;
|
|
3295
|
+
if (nodesBlockingDirectPath.length > 0) {
|
|
3296
|
+
const allObstacles = [];
|
|
3297
|
+
for (const { bounds } of nodesBlockingDirectPath) {
|
|
3298
|
+
allObstacles.push({
|
|
3299
|
+
left: bounds.x - minClearance,
|
|
3300
|
+
right: bounds.x + bounds.width + minClearance
|
|
3301
|
+
});
|
|
3302
|
+
}
|
|
3303
|
+
for (const [nodeId, bounds] of nodePositions) {
|
|
3304
|
+
if (nodeId === targetId || nodeId === sourceId) continue;
|
|
3305
|
+
const isContainer = containerPatterns.some((pattern) => pattern.test(nodeId));
|
|
3306
|
+
if (isContainer) continue;
|
|
3307
|
+
const nodeTop = bounds.y;
|
|
3308
|
+
const nodeBottom = bounds.y + bounds.height;
|
|
3309
|
+
const inSourcePool = nodeTop >= sourcePoolYMin && nodeBottom <= sourcePoolYMax;
|
|
3310
|
+
const inTargetPool = nodeTop >= targetPoolYMin && nodeBottom <= targetPoolYMax;
|
|
3311
|
+
if (inSourcePool || inTargetPool) continue;
|
|
3312
|
+
const nodeInVerticalRange = nodeBottom > fullVerticalMinY && nodeTop < fullVerticalMaxY;
|
|
3313
|
+
if (nodeInVerticalRange) {
|
|
3314
|
+
allObstacles.push({
|
|
3315
|
+
left: bounds.x - minClearance,
|
|
3316
|
+
right: bounds.x + bounds.width + minClearance
|
|
3317
|
+
});
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
allObstacles.sort((a, b) => a.left - b.left);
|
|
3321
|
+
const mergedObstacles = [];
|
|
3322
|
+
for (const obs of allObstacles) {
|
|
3323
|
+
if (mergedObstacles.length === 0) {
|
|
3324
|
+
mergedObstacles.push({ ...obs });
|
|
3325
|
+
} else {
|
|
3326
|
+
const last = mergedObstacles[mergedObstacles.length - 1];
|
|
3327
|
+
if (obs.left <= last.right) {
|
|
3328
|
+
last.right = Math.max(last.right, obs.right);
|
|
3329
|
+
} else {
|
|
3330
|
+
mergedObstacles.push({ ...obs });
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
let bestRouteX = startX;
|
|
3335
|
+
let bestShift = Infinity;
|
|
3336
|
+
if (mergedObstacles.length > 0) {
|
|
3337
|
+
const leftMost = mergedObstacles[0].left;
|
|
3338
|
+
if (leftMost > 20) {
|
|
3339
|
+
const shiftNeeded = Math.abs(startX - leftMost);
|
|
3340
|
+
if (shiftNeeded < bestShift) {
|
|
3341
|
+
bestShift = shiftNeeded;
|
|
3342
|
+
bestRouteX = leftMost;
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
if (mergedObstacles.length > 0) {
|
|
3347
|
+
const rightMost = mergedObstacles[mergedObstacles.length - 1].right;
|
|
3348
|
+
const shiftNeeded = Math.abs(startX - rightMost);
|
|
3349
|
+
if (shiftNeeded < bestShift) {
|
|
3350
|
+
bestShift = shiftNeeded;
|
|
3351
|
+
bestRouteX = rightMost;
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
for (let i = 0; i < mergedObstacles.length - 1; i++) {
|
|
3355
|
+
const gapLeft = mergedObstacles[i].right;
|
|
3356
|
+
const gapRight = mergedObstacles[i + 1].left;
|
|
3357
|
+
const gapWidth = gapRight - gapLeft;
|
|
3358
|
+
if (gapWidth >= 10) {
|
|
3359
|
+
const gapCenter = (gapLeft + gapRight) / 2;
|
|
3360
|
+
const shiftNeeded = Math.abs(startX - gapCenter);
|
|
3361
|
+
if (shiftNeeded < bestShift) {
|
|
3362
|
+
bestShift = shiftNeeded;
|
|
3363
|
+
bestRouteX = gapCenter;
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
finalRouteX = bestRouteX;
|
|
3368
|
+
}
|
|
3154
3369
|
const horizontalDist = Math.abs(startX - endX);
|
|
3155
|
-
|
|
3156
|
-
|
|
3370
|
+
waypoints.push({ x: startX, y: startY });
|
|
3371
|
+
const needsObstacleAvoidance = Math.abs(finalRouteX - startX) > 5 && horizontalDist <= 5;
|
|
3372
|
+
if (needsObstacleAvoidance) {
|
|
3373
|
+
waypoints.push({ x: finalRouteX, y: startY });
|
|
3374
|
+
waypoints.push({ x: finalRouteX, y: endY });
|
|
3375
|
+
if (Math.abs(finalRouteX - endX) > 5) {
|
|
3376
|
+
waypoints.push({ x: endX, y: endY });
|
|
3377
|
+
}
|
|
3378
|
+
} else if (horizontalDist > 5) {
|
|
3379
|
+
let routeY = horizontalDist > 200 ? endY - 20 : (startY + endY) / 2;
|
|
3380
|
+
if (targetId) {
|
|
3381
|
+
const minX = Math.min(startX, endX);
|
|
3382
|
+
const maxX = Math.max(startX, endX);
|
|
3383
|
+
for (const [nodeId, bounds] of nodePositions) {
|
|
3384
|
+
if (nodeId === targetId || nodeId === sourceId) continue;
|
|
3385
|
+
const nodeLeft = bounds.x;
|
|
3386
|
+
const nodeRight = bounds.x + bounds.width;
|
|
3387
|
+
const nodeTop = bounds.y;
|
|
3388
|
+
const nodeBottom = bounds.y + bounds.height;
|
|
3389
|
+
const overlapsHorizontally = nodeRight > minX && nodeLeft < maxX;
|
|
3390
|
+
if (overlapsHorizontally) {
|
|
3391
|
+
if (goingDown) {
|
|
3392
|
+
if (routeY >= nodeTop && routeY <= nodeBottom) {
|
|
3393
|
+
routeY = Math.min(routeY, nodeTop - minClearance);
|
|
3394
|
+
} else if (nodeTop > startY && nodeTop < endY) {
|
|
3395
|
+
routeY = Math.min(routeY, nodeTop - minClearance);
|
|
3396
|
+
}
|
|
3397
|
+
} else {
|
|
3398
|
+
if (routeY >= nodeTop && routeY <= nodeBottom) {
|
|
3399
|
+
routeY = Math.min(routeY, nodeTop - minClearance);
|
|
3400
|
+
} else if (nodeTop < startY && nodeBottom > endY) {
|
|
3401
|
+
routeY = Math.min(routeY, nodeTop - minClearance);
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
if (goingDown) {
|
|
3407
|
+
routeY = Math.max(routeY, startY + 10);
|
|
3408
|
+
routeY = Math.min(routeY, endY - 10);
|
|
3409
|
+
} else {
|
|
3410
|
+
routeY = Math.max(routeY, endY + 10);
|
|
3411
|
+
routeY = Math.min(routeY, startY - 10);
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3157
3414
|
waypoints.push({ x: startX, y: routeY });
|
|
3158
3415
|
waypoints.push({ x: endX, y: routeY });
|
|
3416
|
+
waypoints.push({ x: endX, y: endY });
|
|
3417
|
+
} else {
|
|
3418
|
+
waypoints.push({ x: endX, y: endY });
|
|
3159
3419
|
}
|
|
3160
|
-
waypoints.push({ x: endX, y: endY });
|
|
3161
3420
|
}
|
|
3162
3421
|
/**
|
|
3163
3422
|
* Find the task associated with an artifact
|
|
@@ -3190,286 +3449,263 @@ var init_pool_arranger = __esm({
|
|
|
3190
3449
|
}
|
|
3191
3450
|
});
|
|
3192
3451
|
|
|
3193
|
-
// src/layout/post-processing/
|
|
3194
|
-
var
|
|
3195
|
-
var
|
|
3196
|
-
"src/layout/post-processing/
|
|
3452
|
+
// src/layout/post-processing/compactor.ts
|
|
3453
|
+
var DEFAULT_OPTIONS2, Compactor;
|
|
3454
|
+
var init_compactor = __esm({
|
|
3455
|
+
"src/layout/post-processing/compactor.ts"() {
|
|
3197
3456
|
"use strict";
|
|
3198
3457
|
init_cjs_shims();
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3458
|
+
DEFAULT_OPTIONS2 = {
|
|
3459
|
+
minHorizontalGap: 60,
|
|
3460
|
+
minVerticalGap: 40,
|
|
3461
|
+
compactHorizontal: true,
|
|
3462
|
+
compactVertical: true,
|
|
3463
|
+
considerDependencies: true
|
|
3464
|
+
};
|
|
3465
|
+
Compactor = class {
|
|
3466
|
+
options;
|
|
3467
|
+
constructor(options) {
|
|
3468
|
+
this.options = { ...DEFAULT_OPTIONS2, ...options };
|
|
3469
|
+
}
|
|
3202
3470
|
/**
|
|
3203
|
-
*
|
|
3204
|
-
* @param layoutedGraph - The ELK layouted graph (contains edge coordinates)
|
|
3205
|
-
* @param originalGraph - The original BPMN graph (contains bpmn metadata)
|
|
3471
|
+
* Compact the graph layout
|
|
3206
3472
|
*/
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
this.
|
|
3210
|
-
if (
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
this.processEdges(layoutedGraph, originalGraph, gateways, 0, 0);
|
|
3218
|
-
}
|
|
3219
|
-
/**
|
|
3220
|
-
* Collect all gateway nodes and calculate their diamond corners
|
|
3221
|
-
* Uses originalGraph for BPMN type info and layoutedGraph for positions
|
|
3222
|
-
*/
|
|
3223
|
-
collectGateways(layoutedNode, originalNode, gateways, offsetX, offsetY) {
|
|
3224
|
-
const bpmn = originalNode.bpmn;
|
|
3225
|
-
if (this.isGateway(bpmn?.type)) {
|
|
3226
|
-
const x = offsetX + (layoutedNode.x ?? 0);
|
|
3227
|
-
const y = offsetY + (layoutedNode.y ?? 0);
|
|
3228
|
-
const width = layoutedNode.width ?? 50;
|
|
3229
|
-
const height = layoutedNode.height ?? 50;
|
|
3230
|
-
gateways.set(layoutedNode.id, {
|
|
3231
|
-
id: layoutedNode.id,
|
|
3232
|
-
bounds: { x, y, width, height },
|
|
3233
|
-
corners: {
|
|
3234
|
-
left: { x, y: y + height / 2 },
|
|
3235
|
-
top: { x: x + width / 2, y },
|
|
3236
|
-
right: { x: x + width, y: y + height / 2 },
|
|
3237
|
-
bottom: { x: x + width / 2, y: y + height }
|
|
3238
|
-
}
|
|
3239
|
-
});
|
|
3240
|
-
}
|
|
3241
|
-
if (layoutedNode.children && originalNode.children) {
|
|
3242
|
-
const isContainer = this.isContainer(bpmn?.type);
|
|
3243
|
-
const newOffsetX = isContainer ? offsetX + (layoutedNode.x ?? 0) : offsetX;
|
|
3244
|
-
const newOffsetY = isContainer ? offsetY + (layoutedNode.y ?? 0) : offsetY;
|
|
3245
|
-
const originalChildMap = /* @__PURE__ */ new Map();
|
|
3246
|
-
for (const child of originalNode.children) {
|
|
3247
|
-
const childNode = child;
|
|
3248
|
-
if (childNode.id) {
|
|
3249
|
-
originalChildMap.set(childNode.id, childNode);
|
|
3250
|
-
}
|
|
3473
|
+
compact(graph) {
|
|
3474
|
+
if (!graph.children || graph.children.length === 0) return;
|
|
3475
|
+
const nodeBounds = this.collectNodeBounds(graph);
|
|
3476
|
+
if (nodeBounds.length === 0) return;
|
|
3477
|
+
const edges = this.collectEdges(graph);
|
|
3478
|
+
if (this.options.considerDependencies && edges.length > 0) {
|
|
3479
|
+
this.compactWithDependencies(graph, nodeBounds, edges);
|
|
3480
|
+
} else {
|
|
3481
|
+
if (this.options.compactHorizontal) {
|
|
3482
|
+
this.compactHorizontalSimple(nodeBounds);
|
|
3251
3483
|
}
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3484
|
+
if (this.options.compactVertical) {
|
|
3485
|
+
this.compactVerticalSimple(nodeBounds);
|
|
3486
|
+
}
|
|
3487
|
+
this.applyBounds(graph, nodeBounds);
|
|
3488
|
+
}
|
|
3489
|
+
for (const child of graph.children) {
|
|
3490
|
+
if (child.children && child.children.length > 0) {
|
|
3491
|
+
this.compact(child);
|
|
3257
3492
|
}
|
|
3258
3493
|
}
|
|
3259
3494
|
}
|
|
3260
3495
|
/**
|
|
3261
|
-
*
|
|
3496
|
+
* Collect node bounds from graph
|
|
3262
3497
|
*/
|
|
3263
|
-
|
|
3264
|
-
const
|
|
3265
|
-
if (
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3498
|
+
collectNodeBounds(graph) {
|
|
3499
|
+
const bounds = [];
|
|
3500
|
+
if (!graph.children) return bounds;
|
|
3501
|
+
for (const child of graph.children) {
|
|
3502
|
+
const node = child;
|
|
3503
|
+
if (node.x !== void 0 && node.y !== void 0) {
|
|
3504
|
+
bounds.push({
|
|
3505
|
+
id: node.id,
|
|
3506
|
+
x: node.x,
|
|
3507
|
+
y: node.y,
|
|
3508
|
+
width: node.width ?? 0,
|
|
3509
|
+
height: node.height ?? 0
|
|
3510
|
+
});
|
|
3270
3511
|
}
|
|
3271
3512
|
}
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3513
|
+
return bounds;
|
|
3514
|
+
}
|
|
3515
|
+
/**
|
|
3516
|
+
* Collect edges from graph
|
|
3517
|
+
*/
|
|
3518
|
+
collectEdges(graph) {
|
|
3519
|
+
const edges = [];
|
|
3520
|
+
const collectFromNode = (node) => {
|
|
3521
|
+
if (node.edges) {
|
|
3522
|
+
for (const edge of node.edges) {
|
|
3523
|
+
const elkEdge = edge;
|
|
3524
|
+
if (elkEdge.sources && elkEdge.targets) {
|
|
3525
|
+
for (const source of elkEdge.sources) {
|
|
3526
|
+
for (const target of elkEdge.targets) {
|
|
3527
|
+
edges.push({ source, target });
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3281
3531
|
}
|
|
3282
3532
|
}
|
|
3283
|
-
|
|
3284
|
-
const
|
|
3285
|
-
|
|
3286
|
-
this.processEdges(layoutedChild, originalChild, gateways, newOffsetX, newOffsetY);
|
|
3533
|
+
if (node.children) {
|
|
3534
|
+
for (const child of node.children) {
|
|
3535
|
+
collectFromNode(child);
|
|
3287
3536
|
}
|
|
3288
3537
|
}
|
|
3289
|
-
}
|
|
3538
|
+
};
|
|
3539
|
+
collectFromNode(graph);
|
|
3540
|
+
return edges;
|
|
3290
3541
|
}
|
|
3291
3542
|
/**
|
|
3292
|
-
*
|
|
3543
|
+
* Simple horizontal compaction - move nodes left while maintaining minimum gap
|
|
3293
3544
|
*/
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
const
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3545
|
+
compactHorizontalSimple(nodes) {
|
|
3546
|
+
if (nodes.length === 0) return;
|
|
3547
|
+
nodes.sort((a, b) => a.x - b.x);
|
|
3548
|
+
const minX = nodes[0].x;
|
|
3549
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
3550
|
+
const prev = nodes[i - 1];
|
|
3551
|
+
const curr = nodes[i];
|
|
3552
|
+
if (this.hasVerticalOverlap(prev, curr)) {
|
|
3553
|
+
const minAllowedX = prev.x + prev.width + this.options.minHorizontalGap;
|
|
3554
|
+
if (curr.x > minAllowedX) {
|
|
3555
|
+
curr.x = minAllowedX;
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3307
3558
|
}
|
|
3308
|
-
const
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3559
|
+
const newMinX = Math.min(...nodes.map((n) => n.x));
|
|
3560
|
+
const offsetX = minX - newMinX;
|
|
3561
|
+
if (offsetX > 0) {
|
|
3562
|
+
for (const node of nodes) {
|
|
3563
|
+
node.x += offsetX;
|
|
3313
3564
|
}
|
|
3314
|
-
this.adjustEndPoint(edge, section, targetGateway, offsetX, offsetY);
|
|
3315
3565
|
}
|
|
3316
3566
|
}
|
|
3317
3567
|
/**
|
|
3318
|
-
*
|
|
3568
|
+
* Simple vertical compaction - move nodes up while maintaining minimum gap
|
|
3319
3569
|
*/
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
const
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3570
|
+
compactVerticalSimple(nodes) {
|
|
3571
|
+
if (nodes.length === 0) return;
|
|
3572
|
+
nodes.sort((a, b) => a.y - b.y);
|
|
3573
|
+
const minY = nodes[0].y;
|
|
3574
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
3575
|
+
const prev = nodes[i - 1];
|
|
3576
|
+
const curr = nodes[i];
|
|
3577
|
+
if (this.hasHorizontalOverlap(prev, curr)) {
|
|
3578
|
+
const minAllowedY = prev.y + prev.height + this.options.minVerticalGap;
|
|
3579
|
+
if (curr.y > minAllowedY) {
|
|
3580
|
+
curr.y = minAllowedY;
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
const newMinY = Math.min(...nodes.map((n) => n.y));
|
|
3585
|
+
const offsetY = minY - newMinY;
|
|
3586
|
+
if (offsetY > 0) {
|
|
3587
|
+
for (const node of nodes) {
|
|
3588
|
+
node.y += offsetY;
|
|
3338
3589
|
}
|
|
3339
3590
|
}
|
|
3340
3591
|
}
|
|
3341
3592
|
/**
|
|
3342
|
-
*
|
|
3593
|
+
* Compact with dependency consideration using topological sort
|
|
3343
3594
|
*/
|
|
3344
|
-
|
|
3345
|
-
const
|
|
3346
|
-
const
|
|
3347
|
-
|
|
3348
|
-
if (section.bendPoints && section.bendPoints.length > 0) {
|
|
3349
|
-
const lastBend = section.bendPoints[section.bendPoints.length - 1];
|
|
3350
|
-
prevPoint = { x: offsetX + lastBend.x, y: offsetY + lastBend.y };
|
|
3351
|
-
} else {
|
|
3352
|
-
prevPoint = { x: offsetX + section.startPoint.x, y: offsetY + section.startPoint.y };
|
|
3353
|
-
}
|
|
3354
|
-
const dx = endX - prevPoint.x;
|
|
3355
|
-
const dy = endY - prevPoint.y;
|
|
3356
|
-
let targetCorner;
|
|
3357
|
-
if (Math.abs(dx) > Math.abs(dy)) {
|
|
3358
|
-
targetCorner = dx > 0 ? gateway.corners.left : gateway.corners.right;
|
|
3359
|
-
} else {
|
|
3360
|
-
targetCorner = dy > 0 ? gateway.corners.top : gateway.corners.bottom;
|
|
3595
|
+
compactWithDependencies(graph, nodeBounds, edges) {
|
|
3596
|
+
const boundsMap = /* @__PURE__ */ new Map();
|
|
3597
|
+
for (const node of nodeBounds) {
|
|
3598
|
+
boundsMap.set(node.id, node);
|
|
3361
3599
|
}
|
|
3362
|
-
const
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
const
|
|
3375
|
-
if (
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3600
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
3601
|
+
for (const edge of edges) {
|
|
3602
|
+
if (!dependencies.has(edge.target)) {
|
|
3603
|
+
dependencies.set(edge.target, /* @__PURE__ */ new Set());
|
|
3604
|
+
}
|
|
3605
|
+
dependencies.get(edge.target).add(edge.source);
|
|
3606
|
+
}
|
|
3607
|
+
const sorted = this.topologicalSort([...boundsMap.keys()], dependencies);
|
|
3608
|
+
if (this.options.compactHorizontal) {
|
|
3609
|
+
for (const nodeId of sorted) {
|
|
3610
|
+
const node = boundsMap.get(nodeId);
|
|
3611
|
+
if (!node) continue;
|
|
3612
|
+
const deps = dependencies.get(nodeId);
|
|
3613
|
+
if (!deps || deps.size === 0) continue;
|
|
3614
|
+
let maxRight = 0;
|
|
3615
|
+
for (const depId of deps) {
|
|
3616
|
+
const dep = boundsMap.get(depId);
|
|
3617
|
+
if (dep) {
|
|
3618
|
+
maxRight = Math.max(maxRight, dep.x + dep.width);
|
|
3619
|
+
}
|
|
3379
3620
|
}
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
}
|
|
3384
|
-
} else {
|
|
3385
|
-
const intersectionPoint = this.findDiamondIntersection(
|
|
3386
|
-
prevPoint,
|
|
3387
|
-
{ x: endX, y: endY },
|
|
3388
|
-
gateway
|
|
3389
|
-
);
|
|
3390
|
-
if (intersectionPoint) {
|
|
3391
|
-
section.endPoint = {
|
|
3392
|
-
x: intersectionPoint.x - offsetX,
|
|
3393
|
-
y: intersectionPoint.y - offsetY
|
|
3394
|
-
};
|
|
3395
|
-
if (isDebugEnabled()) {
|
|
3396
|
-
console.log(`[BPMN] GatewayEdgeAdjuster: Adjusted edge ${edge.id} endpoint to diamond intersection (${intersectionPoint.x},${intersectionPoint.y})`);
|
|
3621
|
+
const targetX = maxRight + this.options.minHorizontalGap;
|
|
3622
|
+
if (node.x > targetX) {
|
|
3623
|
+
node.x = targetX;
|
|
3397
3624
|
}
|
|
3398
3625
|
}
|
|
3399
3626
|
}
|
|
3627
|
+
this.applyBounds(graph, nodeBounds);
|
|
3400
3628
|
}
|
|
3401
3629
|
/**
|
|
3402
|
-
*
|
|
3630
|
+
* Apply bounds back to graph nodes
|
|
3403
3631
|
*/
|
|
3404
|
-
|
|
3405
|
-
const
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
minDist = dist;
|
|
3417
|
-
closest = corners[i];
|
|
3418
|
-
}
|
|
3419
|
-
}
|
|
3420
|
-
return closest;
|
|
3421
|
-
}
|
|
3422
|
-
/**
|
|
3423
|
-
* Find intersection point between a line segment and the diamond edges
|
|
3424
|
-
*/
|
|
3425
|
-
findDiamondIntersection(from, to, gateway) {
|
|
3426
|
-
const { corners } = gateway;
|
|
3427
|
-
const edges = [
|
|
3428
|
-
[corners.left, corners.top],
|
|
3429
|
-
// top-left edge
|
|
3430
|
-
[corners.top, corners.right],
|
|
3431
|
-
// top-right edge
|
|
3432
|
-
[corners.right, corners.bottom],
|
|
3433
|
-
// bottom-right edge
|
|
3434
|
-
[corners.bottom, corners.left]
|
|
3435
|
-
// bottom-left edge
|
|
3436
|
-
];
|
|
3437
|
-
for (const [p1, p2] of edges) {
|
|
3438
|
-
const intersection = lineIntersection(from, to, p1, p2);
|
|
3439
|
-
if (intersection) {
|
|
3440
|
-
return intersection;
|
|
3632
|
+
applyBounds(graph, bounds) {
|
|
3633
|
+
const boundsMap = /* @__PURE__ */ new Map();
|
|
3634
|
+
for (const b of bounds) {
|
|
3635
|
+
boundsMap.set(b.id, b);
|
|
3636
|
+
}
|
|
3637
|
+
if (!graph.children) return;
|
|
3638
|
+
for (const child of graph.children) {
|
|
3639
|
+
const node = child;
|
|
3640
|
+
const b = boundsMap.get(node.id);
|
|
3641
|
+
if (b) {
|
|
3642
|
+
node.x = b.x;
|
|
3643
|
+
node.y = b.y;
|
|
3441
3644
|
}
|
|
3442
3645
|
}
|
|
3443
|
-
return null;
|
|
3444
3646
|
}
|
|
3445
3647
|
/**
|
|
3446
|
-
* Check if
|
|
3648
|
+
* Check if two nodes have vertical overlap (same horizontal band)
|
|
3447
3649
|
*/
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
"eventBasedGateway",
|
|
3455
|
-
"complexGateway"
|
|
3456
|
-
].includes(type);
|
|
3650
|
+
hasVerticalOverlap(a, b) {
|
|
3651
|
+
const aTop = a.y;
|
|
3652
|
+
const aBottom = a.y + a.height;
|
|
3653
|
+
const bTop = b.y;
|
|
3654
|
+
const bBottom = b.y + b.height;
|
|
3655
|
+
return aTop < bBottom && bTop < aBottom;
|
|
3457
3656
|
}
|
|
3458
3657
|
/**
|
|
3459
|
-
* Check if
|
|
3658
|
+
* Check if two nodes have horizontal overlap (same vertical band)
|
|
3460
3659
|
*/
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3660
|
+
hasHorizontalOverlap(a, b) {
|
|
3661
|
+
const aLeft = a.x;
|
|
3662
|
+
const aRight = a.x + a.width;
|
|
3663
|
+
const bLeft = b.x;
|
|
3664
|
+
const bRight = b.x + b.width;
|
|
3665
|
+
return aLeft < bRight && bLeft < aRight;
|
|
3666
|
+
}
|
|
3667
|
+
/**
|
|
3668
|
+
* Topological sort using Kahn's algorithm
|
|
3669
|
+
*/
|
|
3670
|
+
topologicalSort(nodes, dependencies) {
|
|
3671
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
3672
|
+
const adjacencyList = /* @__PURE__ */ new Map();
|
|
3673
|
+
for (const node of nodes) {
|
|
3674
|
+
inDegree.set(node, 0);
|
|
3675
|
+
adjacencyList.set(node, []);
|
|
3676
|
+
}
|
|
3677
|
+
for (const [target, sources] of dependencies) {
|
|
3678
|
+
for (const source of sources) {
|
|
3679
|
+
if (adjacencyList.has(source)) {
|
|
3680
|
+
adjacencyList.get(source).push(target);
|
|
3681
|
+
inDegree.set(target, (inDegree.get(target) ?? 0) + 1);
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
const queue = [];
|
|
3686
|
+
for (const node of nodes) {
|
|
3687
|
+
if ((inDegree.get(node) ?? 0) === 0) {
|
|
3688
|
+
queue.push(node);
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
const sorted = [];
|
|
3692
|
+
while (queue.length > 0) {
|
|
3693
|
+
const node = queue.shift();
|
|
3694
|
+
sorted.push(node);
|
|
3695
|
+
for (const dependent of adjacencyList.get(node) ?? []) {
|
|
3696
|
+
const newDegree = (inDegree.get(dependent) ?? 0) - 1;
|
|
3697
|
+
inDegree.set(dependent, newDegree);
|
|
3698
|
+
if (newDegree === 0) {
|
|
3699
|
+
queue.push(dependent);
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
for (const node of nodes) {
|
|
3704
|
+
if (!sorted.includes(node)) {
|
|
3705
|
+
sorted.push(node);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
return sorted;
|
|
3473
3709
|
}
|
|
3474
3710
|
};
|
|
3475
3711
|
}
|
|
@@ -3489,18 +3725,50 @@ var init_default_options = __esm({
|
|
|
3489
3725
|
"use strict";
|
|
3490
3726
|
init_cjs_shims();
|
|
3491
3727
|
DEFAULT_ELK_OPTIONS2 = {
|
|
3728
|
+
// === Basic Configuration ===
|
|
3492
3729
|
"elk.algorithm": "layered",
|
|
3493
3730
|
"elk.direction": "RIGHT",
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
"elk.
|
|
3497
|
-
|
|
3498
|
-
"elk.layered.spacing.edgeNodeBetweenLayers": 30,
|
|
3499
|
-
"elk.layered.spacing.edgeEdgeBetweenLayers": 20,
|
|
3500
|
-
"elk.hierarchyHandling": "INCLUDE_CHILDREN",
|
|
3731
|
+
// === Layer Assignment ===
|
|
3732
|
+
// NETWORK_SIMPLEX provides better layer distribution than LONGEST_PATH
|
|
3733
|
+
"elk.layered.layering.strategy": "NETWORK_SIMPLEX",
|
|
3734
|
+
// === Crossing Minimization ===
|
|
3501
3735
|
"elk.layered.crossingMinimization.strategy": "LAYER_SWEEP",
|
|
3736
|
+
// TWO_SIDED greedy switch improves crossing reduction
|
|
3737
|
+
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
3738
|
+
// === Node Placement ===
|
|
3739
|
+
// BRANDES_KOEPF provides better vertical alignment than NETWORK_SIMPLEX
|
|
3502
3740
|
"elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
|
|
3503
|
-
|
|
3741
|
+
// Use BALANCED alignment for symmetric layouts
|
|
3742
|
+
"elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
|
|
3743
|
+
// === Edge Routing ===
|
|
3744
|
+
"elk.edgeRouting": "ORTHOGONAL",
|
|
3745
|
+
// Distribute self-loops evenly
|
|
3746
|
+
"elk.layered.edgeRouting.selfLoopDistribution": "EQUALLY",
|
|
3747
|
+
// === Edge Labels ===
|
|
3748
|
+
// Place edge labels at the center of the edge
|
|
3749
|
+
"elk.edgeLabels.placement": "CENTER",
|
|
3750
|
+
// Ensure labels don't overlap with nodes or other labels
|
|
3751
|
+
"elk.spacing.labelLabel": 5,
|
|
3752
|
+
"elk.spacing.labelNode": 10,
|
|
3753
|
+
"elk.spacing.edgeLabel": 10,
|
|
3754
|
+
// === Spacing (increased for better readability) ===
|
|
3755
|
+
"elk.spacing.nodeNode": 60,
|
|
3756
|
+
"elk.spacing.edgeNode": 40,
|
|
3757
|
+
"elk.spacing.edgeEdge": 25,
|
|
3758
|
+
"elk.layered.spacing.nodeNodeBetweenLayers": 100,
|
|
3759
|
+
"elk.layered.spacing.edgeNodeBetweenLayers": 40,
|
|
3760
|
+
"elk.layered.spacing.edgeEdgeBetweenLayers": 25,
|
|
3761
|
+
// === Hierarchy Handling ===
|
|
3762
|
+
"elk.hierarchyHandling": "INCLUDE_CHILDREN",
|
|
3763
|
+
// === Model Order ===
|
|
3764
|
+
// Consider both node and edge order from the input model
|
|
3765
|
+
"elk.layered.considerModelOrder.strategy": "NODES_AND_EDGES",
|
|
3766
|
+
// === Compaction ===
|
|
3767
|
+
// Compact connected components together
|
|
3768
|
+
"elk.layered.compaction.connectedComponents": true,
|
|
3769
|
+
// === Alignment ===
|
|
3770
|
+
// Center alignment for cleaner appearance
|
|
3771
|
+
"elk.alignment": "CENTER"
|
|
3504
3772
|
};
|
|
3505
3773
|
}
|
|
3506
3774
|
});
|
|
@@ -3677,10 +3945,10 @@ var init_elk_graph_preparer = __esm({
|
|
|
3677
3945
|
/**
|
|
3678
3946
|
* Prepare children for ELK layout
|
|
3679
3947
|
*/
|
|
3680
|
-
prepareChildrenForElk(
|
|
3681
|
-
if (!
|
|
3948
|
+
prepareChildrenForElk(nodes, boundaryEventTargetIds = /* @__PURE__ */ new Set(), mainFlowNodes = /* @__PURE__ */ new Set()) {
|
|
3949
|
+
if (!nodes) return [];
|
|
3682
3950
|
const result = [];
|
|
3683
|
-
for (const child of
|
|
3951
|
+
for (const child of nodes) {
|
|
3684
3952
|
const node = child;
|
|
3685
3953
|
result.push(this.prepareNodeForElk(node, boundaryEventTargetIds, mainFlowNodes));
|
|
3686
3954
|
if (node.boundaryEvents && node.boundaryEvents.length > 0) {
|
|
@@ -3712,7 +3980,7 @@ var init_elk_graph_preparer = __esm({
|
|
|
3712
3980
|
"elk.layered.layering.layerConstraint": "LAST"
|
|
3713
3981
|
};
|
|
3714
3982
|
}
|
|
3715
|
-
if (mainFlowNodes.has(node.id)) {
|
|
3983
|
+
if (mainFlowNodes.has(node.id) && !boundaryEventTargetIds.has(node.id)) {
|
|
3716
3984
|
layoutOptions = {
|
|
3717
3985
|
...layoutOptions,
|
|
3718
3986
|
"elk.priority": "10"
|
|
@@ -3721,7 +3989,7 @@ var init_elk_graph_preparer = __esm({
|
|
|
3721
3989
|
if (boundaryEventTargetIds.has(node.id)) {
|
|
3722
3990
|
layoutOptions = {
|
|
3723
3991
|
...layoutOptions,
|
|
3724
|
-
"elk.priority": "
|
|
3992
|
+
"elk.priority": "1"
|
|
3725
3993
|
};
|
|
3726
3994
|
}
|
|
3727
3995
|
const isExpandedSubprocess = node.bpmn?.isExpanded === true && (node.bpmn?.type === "subProcess" || node.bpmn?.type === "transaction" || node.bpmn?.type === "adHocSubProcess" || node.bpmn?.type === "eventSubProcess");
|
|
@@ -4047,553 +4315,6 @@ var init_result_merger = __esm({
|
|
|
4047
4315
|
}
|
|
4048
4316
|
});
|
|
4049
4317
|
|
|
4050
|
-
// src/layout/normalization/main-flow-normalizer.ts
|
|
4051
|
-
var TARGET_MAIN_FLOW_Y, GATEWAY_OFFSET_Y, MainFlowNormalizer;
|
|
4052
|
-
var init_main_flow_normalizer = __esm({
|
|
4053
|
-
"src/layout/normalization/main-flow-normalizer.ts"() {
|
|
4054
|
-
"use strict";
|
|
4055
|
-
init_cjs_shims();
|
|
4056
|
-
init_debug();
|
|
4057
|
-
TARGET_MAIN_FLOW_Y = 12;
|
|
4058
|
-
GATEWAY_OFFSET_Y = 150;
|
|
4059
|
-
MainFlowNormalizer = class {
|
|
4060
|
-
/**
|
|
4061
|
-
* Normalize main flow position to keep it at the top of the diagram.
|
|
4062
|
-
*
|
|
4063
|
-
* Strategy:
|
|
4064
|
-
* 1. Identify "upstream main flow" nodes (before converging gateway)
|
|
4065
|
-
* 2. Shift these nodes up to target Y
|
|
4066
|
-
* 3. Position converging gateway below the main flow
|
|
4067
|
-
* 4. Position downstream nodes (after gateway) relative to gateway
|
|
4068
|
-
*/
|
|
4069
|
-
normalize(graph, mainFlowNodes, boundaryEventTargetIds, originalGraph) {
|
|
4070
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
4071
|
-
const nodesInsideContainers = /* @__PURE__ */ new Set();
|
|
4072
|
-
const buildNodeMap2 = (node, parentIsContainer = false) => {
|
|
4073
|
-
nodeMap.set(node.id, node);
|
|
4074
|
-
if (parentIsContainer) {
|
|
4075
|
-
nodesInsideContainers.add(node.id);
|
|
4076
|
-
}
|
|
4077
|
-
if (node.children) {
|
|
4078
|
-
const nodeType = this.getNodeTypeFromOriginal(node.id, originalGraph);
|
|
4079
|
-
const isContainer = nodeType === "subProcess" || nodeType === "transaction" || nodeType === "adHocSubProcess" || nodeType === "eventSubProcess";
|
|
4080
|
-
for (const child of node.children) {
|
|
4081
|
-
buildNodeMap2(child, isContainer || parentIsContainer);
|
|
4082
|
-
}
|
|
4083
|
-
}
|
|
4084
|
-
};
|
|
4085
|
-
buildNodeMap2(graph);
|
|
4086
|
-
const convergingGatewayIds = /* @__PURE__ */ new Set();
|
|
4087
|
-
const upstreamMainFlow = [];
|
|
4088
|
-
const downstreamMainFlow = [];
|
|
4089
|
-
for (const nodeId of mainFlowNodes) {
|
|
4090
|
-
if (this.isConvergingGatewayWithBoundaryInputs(nodeId, graph, boundaryEventTargetIds)) {
|
|
4091
|
-
const nodeType = this.getNodeTypeFromOriginal(nodeId, originalGraph);
|
|
4092
|
-
const isEndEvent = nodeType === "endEvent";
|
|
4093
|
-
if (!isEndEvent) {
|
|
4094
|
-
convergingGatewayIds.add(nodeId);
|
|
4095
|
-
}
|
|
4096
|
-
}
|
|
4097
|
-
}
|
|
4098
|
-
const edgeMap = /* @__PURE__ */ new Map();
|
|
4099
|
-
const collectEdges = (node) => {
|
|
4100
|
-
if (node.edges) {
|
|
4101
|
-
for (const edge of node.edges) {
|
|
4102
|
-
const source = edge.sources?.[0];
|
|
4103
|
-
const target = edge.targets?.[0];
|
|
4104
|
-
if (source && target) {
|
|
4105
|
-
if (!edgeMap.has(source)) edgeMap.set(source, []);
|
|
4106
|
-
edgeMap.get(source).push(target);
|
|
4107
|
-
}
|
|
4108
|
-
}
|
|
4109
|
-
}
|
|
4110
|
-
if (node.children) {
|
|
4111
|
-
for (const child of node.children) {
|
|
4112
|
-
collectEdges(child);
|
|
4113
|
-
}
|
|
4114
|
-
}
|
|
4115
|
-
};
|
|
4116
|
-
collectEdges(graph);
|
|
4117
|
-
const downstreamNodeIds = /* @__PURE__ */ new Set();
|
|
4118
|
-
const markDownstream = (nodeId) => {
|
|
4119
|
-
if (downstreamNodeIds.has(nodeId)) return;
|
|
4120
|
-
downstreamNodeIds.add(nodeId);
|
|
4121
|
-
const targets = edgeMap.get(nodeId) || [];
|
|
4122
|
-
for (const targetId of targets) {
|
|
4123
|
-
if (mainFlowNodes.has(targetId)) {
|
|
4124
|
-
markDownstream(targetId);
|
|
4125
|
-
}
|
|
4126
|
-
}
|
|
4127
|
-
};
|
|
4128
|
-
for (const gatewayId of convergingGatewayIds) {
|
|
4129
|
-
markDownstream(gatewayId);
|
|
4130
|
-
}
|
|
4131
|
-
for (const nodeId of mainFlowNodes) {
|
|
4132
|
-
if (nodesInsideContainers.has(nodeId)) continue;
|
|
4133
|
-
const node = nodeMap.get(nodeId);
|
|
4134
|
-
if (!node || node.y === void 0) continue;
|
|
4135
|
-
if (downstreamNodeIds.has(nodeId)) {
|
|
4136
|
-
downstreamMainFlow.push(node);
|
|
4137
|
-
} else {
|
|
4138
|
-
upstreamMainFlow.push(node);
|
|
4139
|
-
}
|
|
4140
|
-
}
|
|
4141
|
-
const endEventNodes = [];
|
|
4142
|
-
const otherUpstreamNodes = [];
|
|
4143
|
-
for (const node of upstreamMainFlow) {
|
|
4144
|
-
const nodeType = this.getNodeTypeFromOriginal(node.id, originalGraph);
|
|
4145
|
-
if (nodeType === "endEvent") {
|
|
4146
|
-
endEventNodes.push(node);
|
|
4147
|
-
} else {
|
|
4148
|
-
otherUpstreamNodes.push(node);
|
|
4149
|
-
}
|
|
4150
|
-
}
|
|
4151
|
-
let currentMinY = Infinity;
|
|
4152
|
-
for (const node of otherUpstreamNodes) {
|
|
4153
|
-
if (node.y !== void 0) {
|
|
4154
|
-
currentMinY = Math.min(currentMinY, node.y);
|
|
4155
|
-
}
|
|
4156
|
-
}
|
|
4157
|
-
if (currentMinY === Infinity || currentMinY <= TARGET_MAIN_FLOW_Y) {
|
|
4158
|
-
return;
|
|
4159
|
-
}
|
|
4160
|
-
const offsetY = currentMinY - TARGET_MAIN_FLOW_Y;
|
|
4161
|
-
if (isDebugEnabled()) {
|
|
4162
|
-
console.log(`[BPMN] Normalizing main flow: currentMinY=${currentMinY}, offsetY=${offsetY}`);
|
|
4163
|
-
console.log(`[BPMN] Upstream nodes: ${upstreamMainFlow.map((n) => n.id).join(", ")}`);
|
|
4164
|
-
console.log(`[BPMN] Downstream nodes: ${downstreamMainFlow.map((n) => n.id).join(", ")}`);
|
|
4165
|
-
}
|
|
4166
|
-
for (const node of otherUpstreamNodes) {
|
|
4167
|
-
if (node.y !== void 0) {
|
|
4168
|
-
node.y -= offsetY;
|
|
4169
|
-
if (isDebugEnabled()) {
|
|
4170
|
-
console.log(`[BPMN] Shifted upstream ${node.id} to y=${node.y}`);
|
|
4171
|
-
}
|
|
4172
|
-
}
|
|
4173
|
-
}
|
|
4174
|
-
for (const endNode of endEventNodes) {
|
|
4175
|
-
const predecessorId = this.findPredecessorOnMainFlow(endNode.id, graph, mainFlowNodes);
|
|
4176
|
-
if (predecessorId) {
|
|
4177
|
-
const predecessor = nodeMap.get(predecessorId);
|
|
4178
|
-
if (predecessor && predecessor.y !== void 0) {
|
|
4179
|
-
const predecessorCenterY = predecessor.y + (predecessor.height ?? 80) / 2;
|
|
4180
|
-
const endNodeCenterY = (endNode.height ?? 36) / 2;
|
|
4181
|
-
endNode.y = predecessorCenterY - endNodeCenterY;
|
|
4182
|
-
if (isDebugEnabled()) {
|
|
4183
|
-
console.log(`[BPMN] Aligned endEvent ${endNode.id} with predecessor ${predecessorId}: y=${endNode.y}`);
|
|
4184
|
-
}
|
|
4185
|
-
}
|
|
4186
|
-
} else {
|
|
4187
|
-
if (endNode.y !== void 0) {
|
|
4188
|
-
endNode.y -= offsetY;
|
|
4189
|
-
if (isDebugEnabled()) {
|
|
4190
|
-
console.log(`[BPMN] Shifted upstream ${endNode.id} to y=${endNode.y} (no predecessor found)`);
|
|
4191
|
-
}
|
|
4192
|
-
}
|
|
4193
|
-
}
|
|
4194
|
-
}
|
|
4195
|
-
const endEventsByX = /* @__PURE__ */ new Map();
|
|
4196
|
-
for (const endNode of endEventNodes) {
|
|
4197
|
-
const x = endNode.x ?? 0;
|
|
4198
|
-
if (!endEventsByX.has(x)) {
|
|
4199
|
-
endEventsByX.set(x, []);
|
|
4200
|
-
}
|
|
4201
|
-
endEventsByX.get(x).push(endNode);
|
|
4202
|
-
}
|
|
4203
|
-
const adjustedEndEvents = /* @__PURE__ */ new Map();
|
|
4204
|
-
const MIN_SPACING = 50;
|
|
4205
|
-
for (const [x, nodesAtX] of endEventsByX) {
|
|
4206
|
-
if (nodesAtX.length <= 1) continue;
|
|
4207
|
-
nodesAtX.sort((a, b) => (a.y ?? 0) - (b.y ?? 0));
|
|
4208
|
-
for (let i = 1; i < nodesAtX.length; i++) {
|
|
4209
|
-
const prevNode = nodesAtX[i - 1];
|
|
4210
|
-
const currNode = nodesAtX[i];
|
|
4211
|
-
const prevBottom = (prevNode.y ?? 0) + (prevNode.height ?? 36);
|
|
4212
|
-
const currTop = currNode.y ?? 0;
|
|
4213
|
-
if (currTop < prevBottom + MIN_SPACING) {
|
|
4214
|
-
const newY = prevBottom + MIN_SPACING;
|
|
4215
|
-
currNode.y = newY;
|
|
4216
|
-
adjustedEndEvents.set(currNode.id, newY);
|
|
4217
|
-
if (isDebugEnabled()) {
|
|
4218
|
-
console.log(`[BPMN] Adjusted overlapping endEvent ${currNode.id} at x=${x}: y=${currNode.y}`);
|
|
4219
|
-
}
|
|
4220
|
-
}
|
|
4221
|
-
}
|
|
4222
|
-
}
|
|
4223
|
-
const edgesWithAdjustedEndpoint = /* @__PURE__ */ new Set();
|
|
4224
|
-
if (adjustedEndEvents.size > 0) {
|
|
4225
|
-
this.updateEdgesForAdjustedEndEvents(graph, adjustedEndEvents, nodeMap, edgesWithAdjustedEndpoint);
|
|
4226
|
-
}
|
|
4227
|
-
const mainFlowBottom = Math.max(...upstreamMainFlow.map((n) => (n.y ?? 0) + (n.height ?? 80)));
|
|
4228
|
-
const targetGatewayY = mainFlowBottom + GATEWAY_OFFSET_Y;
|
|
4229
|
-
let currentGatewayY = Infinity;
|
|
4230
|
-
for (const gatewayId of convergingGatewayIds) {
|
|
4231
|
-
const gateway = nodeMap.get(gatewayId);
|
|
4232
|
-
if (gateway && gateway.y !== void 0) {
|
|
4233
|
-
currentGatewayY = Math.min(currentGatewayY, gateway.y);
|
|
4234
|
-
}
|
|
4235
|
-
}
|
|
4236
|
-
if (currentGatewayY !== Infinity) {
|
|
4237
|
-
const downstreamOffsetY = currentGatewayY - targetGatewayY;
|
|
4238
|
-
for (const node of downstreamMainFlow) {
|
|
4239
|
-
if (node.y !== void 0) {
|
|
4240
|
-
node.y -= downstreamOffsetY;
|
|
4241
|
-
if (isDebugEnabled()) {
|
|
4242
|
-
console.log(`[BPMN] Shifted downstream ${node.id} to y=${node.y}`);
|
|
4243
|
-
}
|
|
4244
|
-
}
|
|
4245
|
-
}
|
|
4246
|
-
}
|
|
4247
|
-
for (const [nodeId, node] of nodeMap) {
|
|
4248
|
-
if (node.y !== void 0 && nodeId.startsWith("boundary_")) {
|
|
4249
|
-
const attachedNodeId = this.findBoundaryAttachedNode(nodeId, graph);
|
|
4250
|
-
if (attachedNodeId && mainFlowNodes.has(attachedNodeId)) {
|
|
4251
|
-
const attachedNode = nodeMap.get(attachedNodeId);
|
|
4252
|
-
if (attachedNode && attachedNode.y !== void 0) {
|
|
4253
|
-
const attachedBottom = attachedNode.y + (attachedNode.height ?? 80);
|
|
4254
|
-
node.y = attachedBottom - (node.height ?? 36) / 2;
|
|
4255
|
-
}
|
|
4256
|
-
}
|
|
4257
|
-
}
|
|
4258
|
-
}
|
|
4259
|
-
this.updateEdgesAfterNormalization(graph, [...upstreamMainFlow, ...downstreamMainFlow], offsetY, edgesWithAdjustedEndpoint);
|
|
4260
|
-
}
|
|
4261
|
-
/**
|
|
4262
|
-
* Find the predecessor node of a given node on the main flow
|
|
4263
|
-
*/
|
|
4264
|
-
findPredecessorOnMainFlow(nodeId, graph, mainFlowNodes) {
|
|
4265
|
-
const findIncoming = (node) => {
|
|
4266
|
-
if (node.edges) {
|
|
4267
|
-
for (const edge of node.edges) {
|
|
4268
|
-
const target = edge.targets?.[0];
|
|
4269
|
-
const source = edge.sources?.[0];
|
|
4270
|
-
if (target === nodeId && source && mainFlowNodes.has(source)) {
|
|
4271
|
-
if (!source.includes("boundary")) {
|
|
4272
|
-
return source;
|
|
4273
|
-
}
|
|
4274
|
-
}
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
if (node.children) {
|
|
4278
|
-
for (const child of node.children) {
|
|
4279
|
-
const result = findIncoming(child);
|
|
4280
|
-
if (result) return result;
|
|
4281
|
-
}
|
|
4282
|
-
}
|
|
4283
|
-
return void 0;
|
|
4284
|
-
};
|
|
4285
|
-
return findIncoming(graph);
|
|
4286
|
-
}
|
|
4287
|
-
/**
|
|
4288
|
-
* Get the BPMN type of a node by searching the original graph (with BPMN info preserved)
|
|
4289
|
-
*/
|
|
4290
|
-
getNodeTypeFromOriginal(nodeId, graph) {
|
|
4291
|
-
const findType = (children) => {
|
|
4292
|
-
if (!children) return void 0;
|
|
4293
|
-
for (const child of children) {
|
|
4294
|
-
const node = child;
|
|
4295
|
-
if (node.id === nodeId) {
|
|
4296
|
-
return node.bpmn?.type;
|
|
4297
|
-
}
|
|
4298
|
-
if (node.children) {
|
|
4299
|
-
const result = findType(node.children);
|
|
4300
|
-
if (result) return result;
|
|
4301
|
-
}
|
|
4302
|
-
}
|
|
4303
|
-
return void 0;
|
|
4304
|
-
};
|
|
4305
|
-
return findType(graph.children);
|
|
4306
|
-
}
|
|
4307
|
-
/**
|
|
4308
|
-
* Check if a node is a converging gateway that receives edges from both
|
|
4309
|
-
* main flow and boundary event branches (should be positioned below main flow)
|
|
4310
|
-
*/
|
|
4311
|
-
isConvergingGatewayWithBoundaryInputs(nodeId, graph, boundaryEventTargetIds) {
|
|
4312
|
-
const incomingSources = [];
|
|
4313
|
-
const collectIncoming = (node) => {
|
|
4314
|
-
if (node.edges) {
|
|
4315
|
-
for (const edge of node.edges) {
|
|
4316
|
-
const target = edge.targets?.[0];
|
|
4317
|
-
const source = edge.sources?.[0];
|
|
4318
|
-
if (target === nodeId && source) {
|
|
4319
|
-
incomingSources.push(source);
|
|
4320
|
-
}
|
|
4321
|
-
}
|
|
4322
|
-
}
|
|
4323
|
-
if (node.children) {
|
|
4324
|
-
for (const child of node.children) {
|
|
4325
|
-
collectIncoming(child);
|
|
4326
|
-
}
|
|
4327
|
-
}
|
|
4328
|
-
};
|
|
4329
|
-
collectIncoming(graph);
|
|
4330
|
-
if (incomingSources.length <= 1) return false;
|
|
4331
|
-
let hasMainFlowInput = false;
|
|
4332
|
-
let hasBoundaryInput = false;
|
|
4333
|
-
for (const sourceId of incomingSources) {
|
|
4334
|
-
if (boundaryEventTargetIds.has(sourceId) || this.isDownstreamOfBoundaryTarget(sourceId, graph, boundaryEventTargetIds)) {
|
|
4335
|
-
hasBoundaryInput = true;
|
|
4336
|
-
} else if (!sourceId.startsWith("boundary_")) {
|
|
4337
|
-
hasMainFlowInput = true;
|
|
4338
|
-
}
|
|
4339
|
-
}
|
|
4340
|
-
return hasMainFlowInput && hasBoundaryInput;
|
|
4341
|
-
}
|
|
4342
|
-
/**
|
|
4343
|
-
* Check if a node is downstream of a boundary event target
|
|
4344
|
-
*/
|
|
4345
|
-
isDownstreamOfBoundaryTarget(nodeId, graph, boundaryEventTargetIds, visited = /* @__PURE__ */ new Set()) {
|
|
4346
|
-
if (visited.has(nodeId)) return false;
|
|
4347
|
-
visited.add(nodeId);
|
|
4348
|
-
if (boundaryEventTargetIds.has(nodeId)) return true;
|
|
4349
|
-
const findIncoming = (node) => {
|
|
4350
|
-
const sources2 = [];
|
|
4351
|
-
if (node.edges) {
|
|
4352
|
-
for (const edge of node.edges) {
|
|
4353
|
-
if (edge.targets?.[0] === nodeId) {
|
|
4354
|
-
sources2.push(edge.sources?.[0] ?? "");
|
|
4355
|
-
}
|
|
4356
|
-
}
|
|
4357
|
-
}
|
|
4358
|
-
if (node.children) {
|
|
4359
|
-
for (const child of node.children) {
|
|
4360
|
-
sources2.push(...findIncoming(child));
|
|
4361
|
-
}
|
|
4362
|
-
}
|
|
4363
|
-
return sources2.filter((s) => s);
|
|
4364
|
-
};
|
|
4365
|
-
const sources = findIncoming(graph);
|
|
4366
|
-
for (const sourceId of sources) {
|
|
4367
|
-
if (this.isDownstreamOfBoundaryTarget(sourceId, graph, boundaryEventTargetIds, visited)) {
|
|
4368
|
-
return true;
|
|
4369
|
-
}
|
|
4370
|
-
}
|
|
4371
|
-
return false;
|
|
4372
|
-
}
|
|
4373
|
-
/**
|
|
4374
|
-
* Find the node that a boundary event is attached to
|
|
4375
|
-
*/
|
|
4376
|
-
findBoundaryAttachedNode(boundaryEventId, graph) {
|
|
4377
|
-
const parts = boundaryEventId.replace("boundary_", "").split("_");
|
|
4378
|
-
if (parts.length >= 2) {
|
|
4379
|
-
const suffix = parts.slice(1).join("_");
|
|
4380
|
-
const possibleParents = ["call_activity_" + suffix, "task_" + suffix, suffix];
|
|
4381
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
4382
|
-
const buildMap = (node) => {
|
|
4383
|
-
nodeMap.set(node.id, node);
|
|
4384
|
-
if (node.children) {
|
|
4385
|
-
for (const child of node.children) {
|
|
4386
|
-
buildMap(child);
|
|
4387
|
-
}
|
|
4388
|
-
}
|
|
4389
|
-
};
|
|
4390
|
-
buildMap(graph);
|
|
4391
|
-
for (const parentId of possibleParents) {
|
|
4392
|
-
if (nodeMap.has(parentId)) {
|
|
4393
|
-
return parentId;
|
|
4394
|
-
}
|
|
4395
|
-
}
|
|
4396
|
-
}
|
|
4397
|
-
return null;
|
|
4398
|
-
}
|
|
4399
|
-
/**
|
|
4400
|
-
* Update edges after normalization to adjust waypoints
|
|
4401
|
-
*/
|
|
4402
|
-
/**
|
|
4403
|
-
* Update edges that connect to endEvents that were adjusted for overlap
|
|
4404
|
-
*/
|
|
4405
|
-
updateEdgesForAdjustedEndEvents(graph, adjustedEndEvents, nodeMap, edgesWithAdjustedEndpoint) {
|
|
4406
|
-
const updateEdges = (node) => {
|
|
4407
|
-
if (node.edges) {
|
|
4408
|
-
for (const edge of node.edges) {
|
|
4409
|
-
const targetId = edge.targets?.[0];
|
|
4410
|
-
if (targetId && adjustedEndEvents.has(targetId)) {
|
|
4411
|
-
const targetNode = nodeMap.get(targetId);
|
|
4412
|
-
if (!targetNode) continue;
|
|
4413
|
-
const newTargetY = (targetNode.y ?? 0) + (targetNode.height ?? 36) / 2;
|
|
4414
|
-
if (edge.sections) {
|
|
4415
|
-
for (const section of edge.sections) {
|
|
4416
|
-
if (section.bendPoints && section.bendPoints.length > 0 && section.endPoint) {
|
|
4417
|
-
const lastBend = section.bendPoints[section.bendPoints.length - 1];
|
|
4418
|
-
const oldEndY = section.endPoint.y;
|
|
4419
|
-
if (Math.abs(lastBend.y - oldEndY) < 1) {
|
|
4420
|
-
lastBend.y = newTargetY;
|
|
4421
|
-
section.endPoint.y = newTargetY;
|
|
4422
|
-
edgesWithAdjustedEndpoint.add(edge.id);
|
|
4423
|
-
if (isDebugEnabled()) {
|
|
4424
|
-
console.log(`[BPMN] Updated edge ${edge.id} last bendPoint and endpoint to y=${newTargetY}`);
|
|
4425
|
-
}
|
|
4426
|
-
} else {
|
|
4427
|
-
section.endPoint.y = newTargetY;
|
|
4428
|
-
edgesWithAdjustedEndpoint.add(edge.id);
|
|
4429
|
-
if (isDebugEnabled()) {
|
|
4430
|
-
console.log(`[BPMN] Updated edge ${edge.id} endpoint to y=${newTargetY}`);
|
|
4431
|
-
}
|
|
4432
|
-
}
|
|
4433
|
-
} else if (section.endPoint) {
|
|
4434
|
-
section.endPoint.y = newTargetY;
|
|
4435
|
-
edgesWithAdjustedEndpoint.add(edge.id);
|
|
4436
|
-
if (isDebugEnabled()) {
|
|
4437
|
-
console.log(`[BPMN] Updated edge ${edge.id} endpoint to y=${newTargetY}`);
|
|
4438
|
-
}
|
|
4439
|
-
}
|
|
4440
|
-
}
|
|
4441
|
-
}
|
|
4442
|
-
}
|
|
4443
|
-
}
|
|
4444
|
-
}
|
|
4445
|
-
if (node.children) {
|
|
4446
|
-
for (const child of node.children) {
|
|
4447
|
-
updateEdges(child);
|
|
4448
|
-
}
|
|
4449
|
-
}
|
|
4450
|
-
};
|
|
4451
|
-
updateEdges(graph);
|
|
4452
|
-
}
|
|
4453
|
-
updateEdgesAfterNormalization(graph, movedNodes, offsetY, edgesWithAdjustedEndpoint = /* @__PURE__ */ new Set()) {
|
|
4454
|
-
const movedNodeIds = new Set(movedNodes.map((n) => n.id));
|
|
4455
|
-
const updateEdges = (node) => {
|
|
4456
|
-
if (node.edges) {
|
|
4457
|
-
for (const edge of node.edges) {
|
|
4458
|
-
const sourceId = edge.sources?.[0];
|
|
4459
|
-
const targetId = edge.targets?.[0];
|
|
4460
|
-
const sourceMoved = sourceId && movedNodeIds.has(sourceId);
|
|
4461
|
-
const targetMoved = targetId && movedNodeIds.has(targetId);
|
|
4462
|
-
const endpointAlreadyAdjusted = edgesWithAdjustedEndpoint.has(edge.id);
|
|
4463
|
-
if (sourceMoved && targetMoved && edge.sections) {
|
|
4464
|
-
for (const section of edge.sections) {
|
|
4465
|
-
if (section.startPoint) {
|
|
4466
|
-
section.startPoint.y -= offsetY;
|
|
4467
|
-
}
|
|
4468
|
-
if (section.endPoint && !endpointAlreadyAdjusted) {
|
|
4469
|
-
section.endPoint.y -= offsetY;
|
|
4470
|
-
}
|
|
4471
|
-
if (section.bendPoints) {
|
|
4472
|
-
const bendCount = section.bendPoints.length;
|
|
4473
|
-
for (let i = 0; i < bendCount; i++) {
|
|
4474
|
-
if (endpointAlreadyAdjusted && i === bendCount - 1) continue;
|
|
4475
|
-
section.bendPoints[i].y -= offsetY;
|
|
4476
|
-
}
|
|
4477
|
-
}
|
|
4478
|
-
}
|
|
4479
|
-
} else if (sourceMoved && !targetMoved && edge.sections) {
|
|
4480
|
-
for (const section of edge.sections) {
|
|
4481
|
-
if (section.startPoint) {
|
|
4482
|
-
section.startPoint.y -= offsetY;
|
|
4483
|
-
}
|
|
4484
|
-
}
|
|
4485
|
-
} else if (!sourceMoved && targetMoved && edge.sections && !endpointAlreadyAdjusted) {
|
|
4486
|
-
for (const section of edge.sections) {
|
|
4487
|
-
if (section.endPoint) {
|
|
4488
|
-
section.endPoint.y -= offsetY;
|
|
4489
|
-
}
|
|
4490
|
-
}
|
|
4491
|
-
}
|
|
4492
|
-
}
|
|
4493
|
-
}
|
|
4494
|
-
if (node.children) {
|
|
4495
|
-
for (const child of node.children) {
|
|
4496
|
-
updateEdges(child);
|
|
4497
|
-
}
|
|
4498
|
-
}
|
|
4499
|
-
};
|
|
4500
|
-
updateEdges(graph);
|
|
4501
|
-
}
|
|
4502
|
-
};
|
|
4503
|
-
}
|
|
4504
|
-
});
|
|
4505
|
-
|
|
4506
|
-
// src/layout/normalization/gateway-propagator.ts
|
|
4507
|
-
var GatewayPropagator;
|
|
4508
|
-
var init_gateway_propagator = __esm({
|
|
4509
|
-
"src/layout/normalization/gateway-propagator.ts"() {
|
|
4510
|
-
"use strict";
|
|
4511
|
-
init_cjs_shims();
|
|
4512
|
-
init_debug();
|
|
4513
|
-
GatewayPropagator = class {
|
|
4514
|
-
/**
|
|
4515
|
-
* Propagate gateway movement to downstream nodes (nodes after the gateway in the flow)
|
|
4516
|
-
*/
|
|
4517
|
-
propagate(graph, gatewayMoves, mainFlowNodes) {
|
|
4518
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
4519
|
-
const edgeMap = /* @__PURE__ */ new Map();
|
|
4520
|
-
const buildMaps = (node) => {
|
|
4521
|
-
nodeMap.set(node.id, node);
|
|
4522
|
-
if (node.edges) {
|
|
4523
|
-
for (const edge of node.edges) {
|
|
4524
|
-
const source = edge.sources?.[0];
|
|
4525
|
-
const target = edge.targets?.[0];
|
|
4526
|
-
if (source && target) {
|
|
4527
|
-
if (!edgeMap.has(source)) edgeMap.set(source, []);
|
|
4528
|
-
edgeMap.get(source).push(target);
|
|
4529
|
-
}
|
|
4530
|
-
}
|
|
4531
|
-
}
|
|
4532
|
-
if (node.children) {
|
|
4533
|
-
for (const child of node.children) {
|
|
4534
|
-
buildMaps(child);
|
|
4535
|
-
}
|
|
4536
|
-
}
|
|
4537
|
-
};
|
|
4538
|
-
buildMaps(graph);
|
|
4539
|
-
for (const [gatewayId, gatewayMove] of gatewayMoves) {
|
|
4540
|
-
if (gatewayMove.newX === void 0) continue;
|
|
4541
|
-
const gateway = nodeMap.get(gatewayId);
|
|
4542
|
-
if (!gateway) continue;
|
|
4543
|
-
const gatewayRight = gatewayMove.newX + (gateway.width ?? 50);
|
|
4544
|
-
const downstreamTargets = edgeMap.get(gatewayId) || [];
|
|
4545
|
-
for (const targetId of downstreamTargets) {
|
|
4546
|
-
if (!mainFlowNodes.has(targetId)) continue;
|
|
4547
|
-
const targetNode = nodeMap.get(targetId);
|
|
4548
|
-
if (!targetNode) continue;
|
|
4549
|
-
const newX = gatewayRight + 50;
|
|
4550
|
-
const currentX = targetNode.x ?? 0;
|
|
4551
|
-
if (newX > currentX) {
|
|
4552
|
-
targetNode.x = newX;
|
|
4553
|
-
gatewayMoves.set(targetId, {
|
|
4554
|
-
newY: targetNode.y ?? 0,
|
|
4555
|
-
offset: 0,
|
|
4556
|
-
newX
|
|
4557
|
-
});
|
|
4558
|
-
if (isDebugEnabled()) {
|
|
4559
|
-
console.log(`[BPMN] Propagating gateway movement to ${targetId}: x ${currentX} -> ${newX}`);
|
|
4560
|
-
}
|
|
4561
|
-
this.propagateDownstreamX(targetId, newX, targetNode.width ?? 100, nodeMap, edgeMap, mainFlowNodes, gatewayMoves);
|
|
4562
|
-
}
|
|
4563
|
-
}
|
|
4564
|
-
}
|
|
4565
|
-
}
|
|
4566
|
-
/**
|
|
4567
|
-
* Recursively propagate X movement to downstream main flow nodes
|
|
4568
|
-
*/
|
|
4569
|
-
propagateDownstreamX(sourceId, sourceX, sourceWidth, nodeMap, edgeMap, mainFlowNodes, moves) {
|
|
4570
|
-
const targets = edgeMap.get(sourceId) || [];
|
|
4571
|
-
const sourceRight = sourceX + sourceWidth;
|
|
4572
|
-
for (const targetId of targets) {
|
|
4573
|
-
if (!mainFlowNodes.has(targetId)) continue;
|
|
4574
|
-
if (moves.has(targetId)) continue;
|
|
4575
|
-
const targetNode = nodeMap.get(targetId);
|
|
4576
|
-
if (!targetNode) continue;
|
|
4577
|
-
const newX = sourceRight + 50;
|
|
4578
|
-
const currentX = targetNode.x ?? 0;
|
|
4579
|
-
if (newX > currentX) {
|
|
4580
|
-
targetNode.x = newX;
|
|
4581
|
-
moves.set(targetId, {
|
|
4582
|
-
newY: targetNode.y ?? 0,
|
|
4583
|
-
offset: 0,
|
|
4584
|
-
newX
|
|
4585
|
-
});
|
|
4586
|
-
if (isDebugEnabled()) {
|
|
4587
|
-
console.log(`[BPMN] Propagating X to ${targetId}: x ${currentX} -> ${newX}`);
|
|
4588
|
-
}
|
|
4589
|
-
this.propagateDownstreamX(targetId, newX, targetNode.width ?? 100, nodeMap, edgeMap, mainFlowNodes, moves);
|
|
4590
|
-
}
|
|
4591
|
-
}
|
|
4592
|
-
}
|
|
4593
|
-
};
|
|
4594
|
-
}
|
|
4595
|
-
});
|
|
4596
|
-
|
|
4597
4318
|
// src/layout/elk-layouter.ts
|
|
4598
4319
|
var import_elkjs, ElkLayouter;
|
|
4599
4320
|
var init_elk_layouter = __esm({
|
|
@@ -4602,48 +4323,41 @@ var init_elk_layouter = __esm({
|
|
|
4602
4323
|
init_cjs_shims();
|
|
4603
4324
|
import_elkjs = __toESM(require("elkjs"));
|
|
4604
4325
|
init_size_calculator();
|
|
4605
|
-
init_edge_fixer();
|
|
4606
4326
|
init_boundary_event();
|
|
4607
4327
|
init_artifact_positioner();
|
|
4608
4328
|
init_group_positioner();
|
|
4609
4329
|
init_lane_arranger();
|
|
4610
4330
|
init_pool_arranger();
|
|
4611
|
-
|
|
4331
|
+
init_compactor();
|
|
4612
4332
|
init_elk_graph_preparer();
|
|
4613
4333
|
init_result_merger();
|
|
4614
|
-
init_main_flow_normalizer();
|
|
4615
|
-
init_gateway_propagator();
|
|
4616
4334
|
init_debug();
|
|
4617
4335
|
ElkLayouter = class {
|
|
4618
4336
|
elk;
|
|
4619
4337
|
userOptions;
|
|
4338
|
+
enableCompaction;
|
|
4620
4339
|
sizeCalculator;
|
|
4621
|
-
edgeFixer;
|
|
4622
4340
|
boundaryEventHandler;
|
|
4623
4341
|
artifactPositioner;
|
|
4624
4342
|
groupPositioner;
|
|
4625
4343
|
laneArranger;
|
|
4626
4344
|
poolArranger;
|
|
4627
|
-
|
|
4345
|
+
compactor;
|
|
4628
4346
|
graphPreparer;
|
|
4629
4347
|
resultMerger;
|
|
4630
|
-
mainFlowNormalizer;
|
|
4631
|
-
gatewayPropagator;
|
|
4632
4348
|
constructor(options) {
|
|
4633
4349
|
this.elk = new import_elkjs.default();
|
|
4634
4350
|
this.userOptions = options?.elkOptions ?? {};
|
|
4351
|
+
this.enableCompaction = options?.enableCompaction ?? false;
|
|
4635
4352
|
this.sizeCalculator = new SizeCalculator();
|
|
4636
|
-
this.edgeFixer = new EdgeFixer();
|
|
4637
4353
|
this.boundaryEventHandler = new BoundaryEventHandler();
|
|
4638
4354
|
this.artifactPositioner = new ArtifactPositioner();
|
|
4639
4355
|
this.groupPositioner = new GroupPositioner();
|
|
4640
4356
|
this.laneArranger = new LaneArranger();
|
|
4641
4357
|
this.poolArranger = new PoolArranger();
|
|
4642
|
-
this.
|
|
4358
|
+
this.compactor = new Compactor();
|
|
4643
4359
|
this.graphPreparer = new ElkGraphPreparer();
|
|
4644
4360
|
this.resultMerger = new ResultMerger();
|
|
4645
|
-
this.mainFlowNormalizer = new MainFlowNormalizer();
|
|
4646
|
-
this.gatewayPropagator = new GatewayPropagator();
|
|
4647
4361
|
}
|
|
4648
4362
|
/**
|
|
4649
4363
|
* Run ELK layout on the graph
|
|
@@ -4657,24 +4371,14 @@ var init_elk_layouter = __esm({
|
|
|
4657
4371
|
const groupInfo = this.groupPositioner.collectInfo(sizedGraph);
|
|
4658
4372
|
const elkGraph = this.graphPreparer.prepare(sizedGraph, this.userOptions, boundaryEventTargetIds);
|
|
4659
4373
|
const layoutedElkGraph = await this.elk.layout(elkGraph);
|
|
4660
|
-
const
|
|
4661
|
-
|
|
4662
|
-
|
|
4374
|
+
const movedNodes = this.boundaryEventHandler.identifyNodesToMove(
|
|
4375
|
+
layoutedElkGraph,
|
|
4376
|
+
boundaryEventInfo,
|
|
4377
|
+
sizedGraph,
|
|
4378
|
+
isDebugEnabled()
|
|
4379
|
+
);
|
|
4663
4380
|
if (movedNodes.size > 0) {
|
|
4664
4381
|
this.boundaryEventHandler.applyNodeMoves(layoutedElkGraph, movedNodes);
|
|
4665
|
-
const gatewayMoves = this.boundaryEventHandler.repositionConvergingGateways(
|
|
4666
|
-
layoutedElkGraph,
|
|
4667
|
-
movedNodes,
|
|
4668
|
-
boundaryEventInfo,
|
|
4669
|
-
isDebugEnabled()
|
|
4670
|
-
);
|
|
4671
|
-
if (gatewayMoves.size > 0) {
|
|
4672
|
-
this.boundaryEventHandler.applyNodeMoves(layoutedElkGraph, gatewayMoves);
|
|
4673
|
-
this.gatewayPropagator.propagate(layoutedElkGraph, gatewayMoves, mainFlowNodes);
|
|
4674
|
-
}
|
|
4675
|
-
for (const [id, move] of gatewayMoves) {
|
|
4676
|
-
movedNodes.set(id, move);
|
|
4677
|
-
}
|
|
4678
4382
|
this.boundaryEventHandler.recalculateEdgesForMovedNodes(layoutedElkGraph, movedNodes, boundaryEventInfo);
|
|
4679
4383
|
}
|
|
4680
4384
|
this.artifactPositioner.reposition(layoutedElkGraph, artifactInfo);
|
|
@@ -4682,7 +4386,9 @@ var init_elk_layouter = __esm({
|
|
|
4682
4386
|
this.poolArranger.rearrange(layoutedElkGraph, sizedGraph);
|
|
4683
4387
|
this.groupPositioner.reposition(layoutedElkGraph, groupInfo, sizedGraph);
|
|
4684
4388
|
this.artifactPositioner.recalculateWithObstacleAvoidance(layoutedElkGraph, artifactInfo);
|
|
4685
|
-
this.
|
|
4389
|
+
if (this.enableCompaction) {
|
|
4390
|
+
this.compactor.compact(layoutedElkGraph);
|
|
4391
|
+
}
|
|
4686
4392
|
this.updateContainerBounds(layoutedElkGraph);
|
|
4687
4393
|
return this.resultMerger.merge(sizedGraph, layoutedElkGraph);
|
|
4688
4394
|
}
|
|
@@ -4731,6 +4437,158 @@ var init_elk_layouter = __esm({
|
|
|
4731
4437
|
}
|
|
4732
4438
|
});
|
|
4733
4439
|
|
|
4440
|
+
// src/layout/tree/tree-layouter.ts
|
|
4441
|
+
var init_tree_layouter = __esm({
|
|
4442
|
+
"src/layout/tree/tree-layouter.ts"() {
|
|
4443
|
+
"use strict";
|
|
4444
|
+
init_cjs_shims();
|
|
4445
|
+
}
|
|
4446
|
+
});
|
|
4447
|
+
|
|
4448
|
+
// src/layout/tree/index.ts
|
|
4449
|
+
var init_tree = __esm({
|
|
4450
|
+
"src/layout/tree/index.ts"() {
|
|
4451
|
+
"use strict";
|
|
4452
|
+
init_cjs_shims();
|
|
4453
|
+
init_tree_layouter();
|
|
4454
|
+
}
|
|
4455
|
+
});
|
|
4456
|
+
|
|
4457
|
+
// src/layout/edge-routing/pathfinding-router.ts
|
|
4458
|
+
var import_pathfinding;
|
|
4459
|
+
var init_pathfinding_router = __esm({
|
|
4460
|
+
"src/layout/edge-routing/pathfinding-router.ts"() {
|
|
4461
|
+
"use strict";
|
|
4462
|
+
init_cjs_shims();
|
|
4463
|
+
import_pathfinding = __toESM(require("pathfinding"));
|
|
4464
|
+
}
|
|
4465
|
+
});
|
|
4466
|
+
|
|
4467
|
+
// src/layout/edge-routing/edge-fixer.ts
|
|
4468
|
+
var init_edge_fixer = __esm({
|
|
4469
|
+
"src/layout/edge-routing/edge-fixer.ts"() {
|
|
4470
|
+
"use strict";
|
|
4471
|
+
init_cjs_shims();
|
|
4472
|
+
init_geometry_utils();
|
|
4473
|
+
init_pathfinding_router();
|
|
4474
|
+
init_debug();
|
|
4475
|
+
}
|
|
4476
|
+
});
|
|
4477
|
+
|
|
4478
|
+
// src/layout/edge-routing/gateway-endpoint-adjuster.ts
|
|
4479
|
+
function adjustGatewayEndpoint(endpoint, adjacentPoint, gatewayBounds, isSource) {
|
|
4480
|
+
const gatewayCenterX = gatewayBounds.x + gatewayBounds.width / 2;
|
|
4481
|
+
const gatewayCenterY = gatewayBounds.y + gatewayBounds.height / 2;
|
|
4482
|
+
const tolerance = 1;
|
|
4483
|
+
if (isDebugEnabled()) {
|
|
4484
|
+
console.log(`[BPMN] adjustGatewayEndpoint: isSource=${isSource}`);
|
|
4485
|
+
console.log(` endpoint: (${endpoint.x}, ${endpoint.y})`);
|
|
4486
|
+
console.log(` gatewayBounds: x=${gatewayBounds.x}, y=${gatewayBounds.y}, w=${gatewayBounds.width}, h=${gatewayBounds.height}`);
|
|
4487
|
+
console.log(` gatewayCenter: (${gatewayCenterX}, ${gatewayCenterY})`);
|
|
4488
|
+
console.log(` right edge x: ${gatewayBounds.x + gatewayBounds.width}`);
|
|
4489
|
+
}
|
|
4490
|
+
const leftCorner = { x: gatewayBounds.x, y: gatewayCenterY };
|
|
4491
|
+
const rightCorner = { x: gatewayBounds.x + gatewayBounds.width, y: gatewayCenterY };
|
|
4492
|
+
const topCorner = { x: gatewayCenterX, y: gatewayBounds.y };
|
|
4493
|
+
const bottomCorner = { x: gatewayCenterX, y: gatewayBounds.y + gatewayBounds.height };
|
|
4494
|
+
if (Math.abs(endpoint.x - gatewayBounds.x) < tolerance && Math.abs(endpoint.y - gatewayCenterY) < tolerance) {
|
|
4495
|
+
if (isDebugEnabled()) console.log(` -> Already at LEFT corner, no adjustment`);
|
|
4496
|
+
return endpoint;
|
|
4497
|
+
}
|
|
4498
|
+
if (Math.abs(endpoint.x - (gatewayBounds.x + gatewayBounds.width)) < tolerance && Math.abs(endpoint.y - gatewayCenterY) < tolerance) {
|
|
4499
|
+
if (isDebugEnabled()) console.log(` -> Already at RIGHT corner, no adjustment`);
|
|
4500
|
+
return endpoint;
|
|
4501
|
+
}
|
|
4502
|
+
if (Math.abs(endpoint.y - gatewayBounds.y) < tolerance && Math.abs(endpoint.x - gatewayCenterX) < tolerance) {
|
|
4503
|
+
if (isDebugEnabled()) console.log(` -> Already at TOP corner, no adjustment`);
|
|
4504
|
+
return endpoint;
|
|
4505
|
+
}
|
|
4506
|
+
if (Math.abs(endpoint.y - (gatewayBounds.y + gatewayBounds.height)) < tolerance && Math.abs(endpoint.x - gatewayCenterX) < tolerance) {
|
|
4507
|
+
if (isDebugEnabled()) console.log(` -> Already at BOTTOM corner, no adjustment`);
|
|
4508
|
+
return endpoint;
|
|
4509
|
+
}
|
|
4510
|
+
if (isDebugEnabled()) {
|
|
4511
|
+
console.log(` -> NOT at corner, will adjust`);
|
|
4512
|
+
}
|
|
4513
|
+
const result = calculateDiamondIntersection(
|
|
4514
|
+
endpoint,
|
|
4515
|
+
gatewayBounds,
|
|
4516
|
+
gatewayCenterX,
|
|
4517
|
+
gatewayCenterY,
|
|
4518
|
+
isSource,
|
|
4519
|
+
adjacentPoint
|
|
4520
|
+
);
|
|
4521
|
+
if (isDebugEnabled()) {
|
|
4522
|
+
console.log(` -> Adjusted to: (${result.x}, ${result.y})`);
|
|
4523
|
+
}
|
|
4524
|
+
return result;
|
|
4525
|
+
}
|
|
4526
|
+
function calculateDiamondIntersection(endpoint, gatewayBounds, gatewayCenterX, gatewayCenterY, isSource, adjacentPoint) {
|
|
4527
|
+
const tolerance = 1;
|
|
4528
|
+
const leftCorner = { x: gatewayBounds.x, y: gatewayCenterY };
|
|
4529
|
+
const rightCorner = { x: gatewayBounds.x + gatewayBounds.width, y: gatewayCenterY };
|
|
4530
|
+
const topCorner = { x: gatewayCenterX, y: gatewayBounds.y };
|
|
4531
|
+
const bottomCorner = { x: gatewayCenterX, y: gatewayBounds.y + gatewayBounds.height };
|
|
4532
|
+
const isOnLeftEdge = Math.abs(endpoint.x - gatewayBounds.x) < tolerance;
|
|
4533
|
+
const isOnRightEdge = Math.abs(endpoint.x - (gatewayBounds.x + gatewayBounds.width)) < tolerance;
|
|
4534
|
+
const isOnTopEdge = Math.abs(endpoint.y - gatewayBounds.y) < tolerance;
|
|
4535
|
+
const isOnBottomEdge = Math.abs(endpoint.y - (gatewayBounds.y + gatewayBounds.height)) < tolerance;
|
|
4536
|
+
const halfWidth = gatewayBounds.width / 2;
|
|
4537
|
+
const halfHeight = gatewayBounds.height / 2;
|
|
4538
|
+
if (isOnLeftEdge || isOnRightEdge) {
|
|
4539
|
+
const yDistFromCenter = Math.abs(endpoint.y - gatewayCenterY);
|
|
4540
|
+
if (yDistFromCenter >= halfHeight) {
|
|
4541
|
+
return isOnLeftEdge ? leftCorner : rightCorner;
|
|
4542
|
+
}
|
|
4543
|
+
const xOffsetFromCenter = halfWidth * (1 - yDistFromCenter / halfHeight);
|
|
4544
|
+
const intersectX = isOnLeftEdge ? gatewayCenterX - xOffsetFromCenter : gatewayCenterX + xOffsetFromCenter;
|
|
4545
|
+
return { x: intersectX, y: endpoint.y };
|
|
4546
|
+
}
|
|
4547
|
+
if (isOnTopEdge || isOnBottomEdge) {
|
|
4548
|
+
const xDistFromCenter = Math.abs(endpoint.x - gatewayCenterX);
|
|
4549
|
+
if (xDistFromCenter >= halfWidth) {
|
|
4550
|
+
return isOnTopEdge ? topCorner : bottomCorner;
|
|
4551
|
+
}
|
|
4552
|
+
const yOffsetFromCenter = halfHeight * (1 - xDistFromCenter / halfWidth);
|
|
4553
|
+
const intersectY = isOnTopEdge ? gatewayCenterY - yOffsetFromCenter : gatewayCenterY + yOffsetFromCenter;
|
|
4554
|
+
return { x: endpoint.x, y: intersectY };
|
|
4555
|
+
}
|
|
4556
|
+
const dx = isSource ? adjacentPoint.x - endpoint.x : endpoint.x - adjacentPoint.x;
|
|
4557
|
+
const dy = isSource ? adjacentPoint.y - endpoint.y : endpoint.y - adjacentPoint.y;
|
|
4558
|
+
if (Math.abs(dx) > Math.abs(dy)) {
|
|
4559
|
+
if (isSource) {
|
|
4560
|
+
return dx > 0 ? rightCorner : leftCorner;
|
|
4561
|
+
} else {
|
|
4562
|
+
return dx > 0 ? leftCorner : rightCorner;
|
|
4563
|
+
}
|
|
4564
|
+
} else {
|
|
4565
|
+
if (isSource) {
|
|
4566
|
+
return dy > 0 ? bottomCorner : topCorner;
|
|
4567
|
+
} else {
|
|
4568
|
+
return dy > 0 ? topCorner : bottomCorner;
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
var init_gateway_endpoint_adjuster = __esm({
|
|
4573
|
+
"src/layout/edge-routing/gateway-endpoint-adjuster.ts"() {
|
|
4574
|
+
"use strict";
|
|
4575
|
+
init_cjs_shims();
|
|
4576
|
+
init_debug();
|
|
4577
|
+
}
|
|
4578
|
+
});
|
|
4579
|
+
|
|
4580
|
+
// src/layout/edge-routing/index.ts
|
|
4581
|
+
var init_edge_routing = __esm({
|
|
4582
|
+
"src/layout/edge-routing/index.ts"() {
|
|
4583
|
+
"use strict";
|
|
4584
|
+
init_cjs_shims();
|
|
4585
|
+
init_edge_fixer();
|
|
4586
|
+
init_geometry_utils();
|
|
4587
|
+
init_gateway_endpoint_adjuster();
|
|
4588
|
+
init_pathfinding_router();
|
|
4589
|
+
}
|
|
4590
|
+
});
|
|
4591
|
+
|
|
4734
4592
|
// src/layout/index.ts
|
|
4735
4593
|
var init_layout = __esm({
|
|
4736
4594
|
"src/layout/index.ts"() {
|
|
@@ -4739,6 +4597,9 @@ var init_layout = __esm({
|
|
|
4739
4597
|
init_elk_layouter();
|
|
4740
4598
|
init_default_options();
|
|
4741
4599
|
init_size_calculator();
|
|
4600
|
+
init_tree();
|
|
4601
|
+
init_constraint();
|
|
4602
|
+
init_edge_routing();
|
|
4742
4603
|
}
|
|
4743
4604
|
});
|
|
4744
4605
|
|
|
@@ -4971,119 +4832,6 @@ var init_lane_resolver = __esm({
|
|
|
4971
4832
|
}
|
|
4972
4833
|
});
|
|
4973
4834
|
|
|
4974
|
-
// src/layout/edge-routing/gateway-endpoint-adjuster.ts
|
|
4975
|
-
function adjustGatewayEndpoint(endpoint, adjacentPoint, gatewayBounds, isSource) {
|
|
4976
|
-
const gatewayCenterX = gatewayBounds.x + gatewayBounds.width / 2;
|
|
4977
|
-
const gatewayCenterY = gatewayBounds.y + gatewayBounds.height / 2;
|
|
4978
|
-
const tolerance = 1;
|
|
4979
|
-
if (isDebugEnabled()) {
|
|
4980
|
-
console.log(`[BPMN] adjustGatewayEndpoint: isSource=${isSource}`);
|
|
4981
|
-
console.log(` endpoint: (${endpoint.x}, ${endpoint.y})`);
|
|
4982
|
-
console.log(` gatewayBounds: x=${gatewayBounds.x}, y=${gatewayBounds.y}, w=${gatewayBounds.width}, h=${gatewayBounds.height}`);
|
|
4983
|
-
console.log(` gatewayCenter: (${gatewayCenterX}, ${gatewayCenterY})`);
|
|
4984
|
-
console.log(` right edge x: ${gatewayBounds.x + gatewayBounds.width}`);
|
|
4985
|
-
}
|
|
4986
|
-
const leftCorner = { x: gatewayBounds.x, y: gatewayCenterY };
|
|
4987
|
-
const rightCorner = { x: gatewayBounds.x + gatewayBounds.width, y: gatewayCenterY };
|
|
4988
|
-
const topCorner = { x: gatewayCenterX, y: gatewayBounds.y };
|
|
4989
|
-
const bottomCorner = { x: gatewayCenterX, y: gatewayBounds.y + gatewayBounds.height };
|
|
4990
|
-
if (Math.abs(endpoint.x - gatewayBounds.x) < tolerance && Math.abs(endpoint.y - gatewayCenterY) < tolerance) {
|
|
4991
|
-
if (isDebugEnabled()) console.log(` -> Already at LEFT corner, no adjustment`);
|
|
4992
|
-
return endpoint;
|
|
4993
|
-
}
|
|
4994
|
-
if (Math.abs(endpoint.x - (gatewayBounds.x + gatewayBounds.width)) < tolerance && Math.abs(endpoint.y - gatewayCenterY) < tolerance) {
|
|
4995
|
-
if (isDebugEnabled()) console.log(` -> Already at RIGHT corner, no adjustment`);
|
|
4996
|
-
return endpoint;
|
|
4997
|
-
}
|
|
4998
|
-
if (Math.abs(endpoint.y - gatewayBounds.y) < tolerance && Math.abs(endpoint.x - gatewayCenterX) < tolerance) {
|
|
4999
|
-
if (isDebugEnabled()) console.log(` -> Already at TOP corner, no adjustment`);
|
|
5000
|
-
return endpoint;
|
|
5001
|
-
}
|
|
5002
|
-
if (Math.abs(endpoint.y - (gatewayBounds.y + gatewayBounds.height)) < tolerance && Math.abs(endpoint.x - gatewayCenterX) < tolerance) {
|
|
5003
|
-
if (isDebugEnabled()) console.log(` -> Already at BOTTOM corner, no adjustment`);
|
|
5004
|
-
return endpoint;
|
|
5005
|
-
}
|
|
5006
|
-
if (isDebugEnabled()) {
|
|
5007
|
-
console.log(` -> NOT at corner, will adjust`);
|
|
5008
|
-
}
|
|
5009
|
-
const result = calculateDiamondIntersection(
|
|
5010
|
-
endpoint,
|
|
5011
|
-
gatewayBounds,
|
|
5012
|
-
gatewayCenterX,
|
|
5013
|
-
gatewayCenterY,
|
|
5014
|
-
isSource,
|
|
5015
|
-
adjacentPoint
|
|
5016
|
-
);
|
|
5017
|
-
if (isDebugEnabled()) {
|
|
5018
|
-
console.log(` -> Adjusted to: (${result.x}, ${result.y})`);
|
|
5019
|
-
}
|
|
5020
|
-
return result;
|
|
5021
|
-
}
|
|
5022
|
-
function calculateDiamondIntersection(endpoint, gatewayBounds, gatewayCenterX, gatewayCenterY, isSource, adjacentPoint) {
|
|
5023
|
-
const tolerance = 1;
|
|
5024
|
-
const leftCorner = { x: gatewayBounds.x, y: gatewayCenterY };
|
|
5025
|
-
const rightCorner = { x: gatewayBounds.x + gatewayBounds.width, y: gatewayCenterY };
|
|
5026
|
-
const topCorner = { x: gatewayCenterX, y: gatewayBounds.y };
|
|
5027
|
-
const bottomCorner = { x: gatewayCenterX, y: gatewayBounds.y + gatewayBounds.height };
|
|
5028
|
-
const isOnLeftEdge = Math.abs(endpoint.x - gatewayBounds.x) < tolerance;
|
|
5029
|
-
const isOnRightEdge = Math.abs(endpoint.x - (gatewayBounds.x + gatewayBounds.width)) < tolerance;
|
|
5030
|
-
const isOnTopEdge = Math.abs(endpoint.y - gatewayBounds.y) < tolerance;
|
|
5031
|
-
const isOnBottomEdge = Math.abs(endpoint.y - (gatewayBounds.y + gatewayBounds.height)) < tolerance;
|
|
5032
|
-
const halfWidth = gatewayBounds.width / 2;
|
|
5033
|
-
const halfHeight = gatewayBounds.height / 2;
|
|
5034
|
-
if (isOnLeftEdge || isOnRightEdge) {
|
|
5035
|
-
const yDistFromCenter = Math.abs(endpoint.y - gatewayCenterY);
|
|
5036
|
-
if (yDistFromCenter >= halfHeight) {
|
|
5037
|
-
return isOnLeftEdge ? leftCorner : rightCorner;
|
|
5038
|
-
}
|
|
5039
|
-
const xOffsetFromCenter = halfWidth * (1 - yDistFromCenter / halfHeight);
|
|
5040
|
-
const intersectX = isOnLeftEdge ? gatewayCenterX - xOffsetFromCenter : gatewayCenterX + xOffsetFromCenter;
|
|
5041
|
-
return { x: intersectX, y: endpoint.y };
|
|
5042
|
-
}
|
|
5043
|
-
if (isOnTopEdge || isOnBottomEdge) {
|
|
5044
|
-
const xDistFromCenter = Math.abs(endpoint.x - gatewayCenterX);
|
|
5045
|
-
if (xDistFromCenter >= halfWidth) {
|
|
5046
|
-
return isOnTopEdge ? topCorner : bottomCorner;
|
|
5047
|
-
}
|
|
5048
|
-
const yOffsetFromCenter = halfHeight * (1 - xDistFromCenter / halfWidth);
|
|
5049
|
-
const intersectY = isOnTopEdge ? gatewayCenterY - yOffsetFromCenter : gatewayCenterY + yOffsetFromCenter;
|
|
5050
|
-
return { x: endpoint.x, y: intersectY };
|
|
5051
|
-
}
|
|
5052
|
-
const dx = isSource ? adjacentPoint.x - endpoint.x : endpoint.x - adjacentPoint.x;
|
|
5053
|
-
const dy = isSource ? adjacentPoint.y - endpoint.y : endpoint.y - adjacentPoint.y;
|
|
5054
|
-
if (Math.abs(dx) > Math.abs(dy)) {
|
|
5055
|
-
if (isSource) {
|
|
5056
|
-
return dx > 0 ? rightCorner : leftCorner;
|
|
5057
|
-
} else {
|
|
5058
|
-
return dx > 0 ? leftCorner : rightCorner;
|
|
5059
|
-
}
|
|
5060
|
-
} else {
|
|
5061
|
-
if (isSource) {
|
|
5062
|
-
return dy > 0 ? bottomCorner : topCorner;
|
|
5063
|
-
} else {
|
|
5064
|
-
return dy > 0 ? topCorner : bottomCorner;
|
|
5065
|
-
}
|
|
5066
|
-
}
|
|
5067
|
-
}
|
|
5068
|
-
var init_gateway_endpoint_adjuster = __esm({
|
|
5069
|
-
"src/layout/edge-routing/gateway-endpoint-adjuster.ts"() {
|
|
5070
|
-
"use strict";
|
|
5071
|
-
init_cjs_shims();
|
|
5072
|
-
init_debug();
|
|
5073
|
-
}
|
|
5074
|
-
});
|
|
5075
|
-
|
|
5076
|
-
// src/layout/edge-routing/index.ts
|
|
5077
|
-
var init_edge_routing = __esm({
|
|
5078
|
-
"src/layout/edge-routing/index.ts"() {
|
|
5079
|
-
"use strict";
|
|
5080
|
-
init_cjs_shims();
|
|
5081
|
-
init_edge_fixer();
|
|
5082
|
-
init_geometry_utils();
|
|
5083
|
-
init_gateway_endpoint_adjuster();
|
|
5084
|
-
}
|
|
5085
|
-
});
|
|
5086
|
-
|
|
5087
4835
|
// src/transform/diagram-builder.ts
|
|
5088
4836
|
var DiagramBuilder;
|
|
5089
4837
|
var init_diagram_builder = __esm({
|
|
@@ -5101,10 +4849,13 @@ var init_diagram_builder = __esm({
|
|
|
5101
4849
|
nodeOffsets = /* @__PURE__ */ new Map();
|
|
5102
4850
|
// Map to track node BPMN metadata for gateway detection
|
|
5103
4851
|
nodeBpmn = /* @__PURE__ */ new Map();
|
|
4852
|
+
// List of already placed edge labels for collision detection
|
|
4853
|
+
placedEdgeLabels = [];
|
|
5104
4854
|
/**
|
|
5105
4855
|
* Build the diagram model from a layouted graph
|
|
5106
4856
|
*/
|
|
5107
4857
|
build(graph, definitions) {
|
|
4858
|
+
this.placedEdgeLabels = [];
|
|
5108
4859
|
this.boundaryEventPositions.clear();
|
|
5109
4860
|
this.nodePositions.clear();
|
|
5110
4861
|
this.nodeOffsets.clear();
|
|
@@ -5112,7 +4863,10 @@ var init_diagram_builder = __esm({
|
|
|
5112
4863
|
const shapes = [];
|
|
5113
4864
|
const edges = [];
|
|
5114
4865
|
const mainElement = definitions.rootElements[0];
|
|
5115
|
-
|
|
4866
|
+
if (!mainElement) {
|
|
4867
|
+
throw new Error("Cannot create BPMN diagram: definitions.rootElements is empty. The graph must contain at least one process or collaboration.");
|
|
4868
|
+
}
|
|
4869
|
+
const planeElement = mainElement.type === "collaboration" ? mainElement.id : mainElement.id;
|
|
5116
4870
|
for (const child of graph.children) {
|
|
5117
4871
|
this.collectShapesAndEdges(child, shapes, edges);
|
|
5118
4872
|
}
|
|
@@ -5145,11 +4899,16 @@ var init_diagram_builder = __esm({
|
|
|
5145
4899
|
const absoluteY = offsetY + node.y;
|
|
5146
4900
|
const nodeWidth = node.width ?? 100;
|
|
5147
4901
|
const nodeHeight = node.height ?? 80;
|
|
4902
|
+
let effectiveHeight = nodeHeight;
|
|
4903
|
+
if (this.isEventType(node.bpmn?.type) && node.labels && node.labels.length > 0) {
|
|
4904
|
+
const labelHeight = node.labels[0]?.height ?? 14;
|
|
4905
|
+
effectiveHeight = nodeHeight + 4 + labelHeight;
|
|
4906
|
+
}
|
|
5148
4907
|
this.nodePositions.set(node.id, {
|
|
5149
4908
|
x: absoluteX,
|
|
5150
4909
|
y: absoluteY,
|
|
5151
4910
|
width: nodeWidth,
|
|
5152
|
-
height:
|
|
4911
|
+
height: effectiveHeight
|
|
5153
4912
|
});
|
|
5154
4913
|
if (node.bpmn) {
|
|
5155
4914
|
this.storeNodeBpmn(node.id, { type: node.bpmn.type });
|
|
@@ -5286,47 +5045,44 @@ var init_diagram_builder = __esm({
|
|
|
5286
5045
|
if (node.bpmn?.type === "participant" || node.bpmn?.type === "lane") {
|
|
5287
5046
|
shape.isHorizontal = true;
|
|
5288
5047
|
}
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
}
|
|
5328
|
-
};
|
|
5329
|
-
}
|
|
5048
|
+
const nodeWidth = node.width ?? 36;
|
|
5049
|
+
const nodeHeight = node.height ?? 36;
|
|
5050
|
+
const label = node.labels?.[0];
|
|
5051
|
+
const labelText = node.bpmn?.name ?? label?.text ?? "";
|
|
5052
|
+
if (this.isEventType(node.bpmn?.type) && labelText) {
|
|
5053
|
+
const labelWidth = label?.width ?? 100;
|
|
5054
|
+
const labelHeight = label?.height ?? 14;
|
|
5055
|
+
shape.label = {
|
|
5056
|
+
bounds: {
|
|
5057
|
+
x: absoluteX + (nodeWidth - labelWidth) / 2,
|
|
5058
|
+
y: absoluteY + nodeHeight + 4,
|
|
5059
|
+
// 4px gap below the circle
|
|
5060
|
+
width: labelWidth,
|
|
5061
|
+
height: labelHeight
|
|
5062
|
+
}
|
|
5063
|
+
};
|
|
5064
|
+
} else if (this.isGatewayType(node.bpmn?.type) && labelText) {
|
|
5065
|
+
const labelWidth = label?.width ?? 100;
|
|
5066
|
+
const estimatedLines = this.estimateLabelLines(labelText, labelWidth);
|
|
5067
|
+
const labelHeight = estimatedLines * 14;
|
|
5068
|
+
shape.label = {
|
|
5069
|
+
bounds: {
|
|
5070
|
+
x: absoluteX + (nodeWidth - labelWidth) / 2,
|
|
5071
|
+
y: absoluteY - labelHeight - 4,
|
|
5072
|
+
// 4px gap above the diamond
|
|
5073
|
+
width: labelWidth,
|
|
5074
|
+
height: labelHeight
|
|
5075
|
+
}
|
|
5076
|
+
};
|
|
5077
|
+
} else if (label?.x !== void 0 && label?.y !== void 0) {
|
|
5078
|
+
shape.label = {
|
|
5079
|
+
bounds: {
|
|
5080
|
+
x: absoluteX + label.x,
|
|
5081
|
+
y: absoluteY + label.y,
|
|
5082
|
+
width: label?.width ?? 100,
|
|
5083
|
+
height: label?.height ?? 20
|
|
5084
|
+
}
|
|
5085
|
+
};
|
|
5330
5086
|
}
|
|
5331
5087
|
return shape;
|
|
5332
5088
|
}
|
|
@@ -5420,6 +5176,13 @@ var init_diagram_builder = __esm({
|
|
|
5420
5176
|
}
|
|
5421
5177
|
}
|
|
5422
5178
|
this.ensureOrthogonalWaypoints(waypoints);
|
|
5179
|
+
this.ensurePerpendicularEndpoints(
|
|
5180
|
+
waypoints,
|
|
5181
|
+
sourceId,
|
|
5182
|
+
targetId,
|
|
5183
|
+
sourceIsGateway,
|
|
5184
|
+
targetIsGateway
|
|
5185
|
+
);
|
|
5423
5186
|
const edgeModel = {
|
|
5424
5187
|
id: `${edge.id}_di`,
|
|
5425
5188
|
bpmnElement: edge.id,
|
|
@@ -5429,7 +5192,19 @@ var init_diagram_builder = __esm({
|
|
|
5429
5192
|
const label = edge.labels[0];
|
|
5430
5193
|
const labelWidth = label?.width ?? 50;
|
|
5431
5194
|
const labelHeight = label?.height ?? 14;
|
|
5432
|
-
const labelPos = this.
|
|
5195
|
+
const labelPos = this.calculateSmartLabelPosition(
|
|
5196
|
+
waypoints,
|
|
5197
|
+
labelWidth,
|
|
5198
|
+
labelHeight,
|
|
5199
|
+
sourceId,
|
|
5200
|
+
targetId
|
|
5201
|
+
);
|
|
5202
|
+
this.placedEdgeLabels.push({
|
|
5203
|
+
x: labelPos.x,
|
|
5204
|
+
y: labelPos.y,
|
|
5205
|
+
width: labelWidth,
|
|
5206
|
+
height: labelHeight
|
|
5207
|
+
});
|
|
5433
5208
|
edgeModel.label = {
|
|
5434
5209
|
bounds: {
|
|
5435
5210
|
x: labelPos.x,
|
|
@@ -5442,49 +5217,137 @@ var init_diagram_builder = __esm({
|
|
|
5442
5217
|
return edgeModel;
|
|
5443
5218
|
}
|
|
5444
5219
|
/**
|
|
5445
|
-
* Calculate label position
|
|
5446
|
-
*
|
|
5220
|
+
* Calculate smart label position on the edge
|
|
5221
|
+
* Strategy:
|
|
5222
|
+
* 1. Find the longest segment of the edge (best visibility)
|
|
5223
|
+
* 2. Place label at the midpoint of that segment
|
|
5224
|
+
* 3. Offset based on segment direction, avoiding overlap with source/target nodes
|
|
5447
5225
|
*/
|
|
5448
|
-
|
|
5226
|
+
calculateSmartLabelPosition(waypoints, labelWidth, labelHeight, sourceId, targetId) {
|
|
5449
5227
|
if (waypoints.length < 2) {
|
|
5450
5228
|
return { x: 0, y: 0 };
|
|
5451
5229
|
}
|
|
5452
|
-
const
|
|
5453
|
-
const
|
|
5454
|
-
let
|
|
5230
|
+
const sourcePos = sourceId ? this.nodePositions.get(sourceId) : void 0;
|
|
5231
|
+
const targetPos = targetId ? this.nodePositions.get(targetId) : void 0;
|
|
5232
|
+
let bestSegmentIndex = -1;
|
|
5233
|
+
let bestSegmentLength = 0;
|
|
5455
5234
|
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
5456
5235
|
const wpCurrent = waypoints[i];
|
|
5457
5236
|
const wpNext = waypoints[i + 1];
|
|
5458
5237
|
if (!wpCurrent || !wpNext) continue;
|
|
5459
|
-
const
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5238
|
+
const segmentLength2 = distance(wpCurrent, wpNext);
|
|
5239
|
+
if (segmentLength2 < 30) continue;
|
|
5240
|
+
const midX2 = (wpCurrent.x + wpNext.x) / 2;
|
|
5241
|
+
const midY2 = (wpCurrent.y + wpNext.y) / 2;
|
|
5242
|
+
const tooCloseToSource = sourcePos && this.isPointNearNode(midX2, midY2, sourcePos, 20);
|
|
5243
|
+
const tooCloseToTarget = targetPos && this.isPointNearNode(midX2, midY2, targetPos, 20);
|
|
5244
|
+
if (!tooCloseToSource && !tooCloseToTarget && segmentLength2 > bestSegmentLength) {
|
|
5245
|
+
bestSegmentLength = segmentLength2;
|
|
5246
|
+
bestSegmentIndex = i;
|
|
5247
|
+
}
|
|
5248
|
+
}
|
|
5249
|
+
if (bestSegmentIndex < 0) {
|
|
5250
|
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
5251
|
+
const wpCurrent = waypoints[i];
|
|
5252
|
+
const wpNext = waypoints[i + 1];
|
|
5253
|
+
if (!wpCurrent || !wpNext) continue;
|
|
5254
|
+
const segmentLength2 = distance(wpCurrent, wpNext);
|
|
5255
|
+
if (segmentLength2 > bestSegmentLength) {
|
|
5256
|
+
bestSegmentLength = segmentLength2;
|
|
5257
|
+
bestSegmentIndex = i;
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
}
|
|
5261
|
+
if (bestSegmentIndex < 0) {
|
|
5262
|
+
bestSegmentIndex = 0;
|
|
5263
|
+
}
|
|
5264
|
+
const wpStart = waypoints[bestSegmentIndex];
|
|
5265
|
+
const wpEnd = waypoints[bestSegmentIndex + 1];
|
|
5266
|
+
if (!wpStart || !wpEnd) {
|
|
5267
|
+
return { x: 0, y: 0 };
|
|
5268
|
+
}
|
|
5269
|
+
const midX = (wpStart.x + wpEnd.x) / 2;
|
|
5270
|
+
const midY = (wpStart.y + wpEnd.y) / 2;
|
|
5271
|
+
const dx = wpEnd.x - wpStart.x;
|
|
5272
|
+
const dy = wpEnd.y - wpStart.y;
|
|
5273
|
+
const isHorizontal = Math.abs(dx) > Math.abs(dy);
|
|
5274
|
+
const segmentLength = Math.sqrt(dx * dx + dy * dy);
|
|
5275
|
+
const offset = 5;
|
|
5276
|
+
if (isHorizontal) {
|
|
5277
|
+
let labelX = midX - labelWidth / 2;
|
|
5278
|
+
let labelY = midY - labelHeight - offset;
|
|
5279
|
+
const labelBounds = { x: labelX, y: labelY, width: labelWidth, height: labelHeight };
|
|
5280
|
+
if (this.labelOverlapsAnyNode(labelBounds)) {
|
|
5281
|
+
labelY = midY + offset;
|
|
5282
|
+
}
|
|
5283
|
+
return { x: labelX, y: labelY };
|
|
5284
|
+
} else {
|
|
5285
|
+
const positions = segmentLength > 80 ? [0.35, 0.5, 0.65, 0.2, 0.8, 0.15, 0.85] : [0.5];
|
|
5286
|
+
for (const ratio of positions) {
|
|
5287
|
+
const testY = wpStart.y + (wpEnd.y - wpStart.y) * ratio - labelHeight / 2;
|
|
5288
|
+
const boundsRight = { x: midX + offset, y: testY, width: labelWidth, height: labelHeight };
|
|
5289
|
+
if (!this.labelOverlapsAnyNode(boundsRight) && !this.labelOverlapsAnyEdgeLabel(boundsRight)) {
|
|
5290
|
+
return { x: midX + offset, y: testY };
|
|
5291
|
+
}
|
|
5292
|
+
const boundsLeft = { x: midX - labelWidth - offset, y: testY, width: labelWidth, height: labelHeight };
|
|
5293
|
+
if (!this.labelOverlapsAnyNode(boundsLeft) && !this.labelOverlapsAnyEdgeLabel(boundsLeft)) {
|
|
5294
|
+
return { x: midX - labelWidth - offset, y: testY };
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
let labelX = midX + offset;
|
|
5298
|
+
let labelY = midY - labelHeight / 2;
|
|
5299
|
+
for (let yOffset = 0; yOffset <= 100; yOffset += 20) {
|
|
5300
|
+
for (const side of [1, -1]) {
|
|
5301
|
+
for (const yDir of [0, 1, -1]) {
|
|
5302
|
+
const testX = side === 1 ? midX + offset : midX - labelWidth - offset;
|
|
5303
|
+
const testY = labelY + yDir * yOffset;
|
|
5304
|
+
const testBounds = { x: testX, y: testY, width: labelWidth, height: labelHeight };
|
|
5305
|
+
if (!this.labelOverlapsAnyEdgeLabel(testBounds)) {
|
|
5306
|
+
if (!this.labelOverlapsAnyNode(testBounds)) {
|
|
5307
|
+
return { x: testX, y: testY };
|
|
5308
|
+
}
|
|
5309
|
+
labelX = testX;
|
|
5310
|
+
labelY = testY;
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5479
5313
|
}
|
|
5480
5314
|
}
|
|
5481
|
-
|
|
5315
|
+
return { x: labelX, y: labelY };
|
|
5482
5316
|
}
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5317
|
+
}
|
|
5318
|
+
/**
|
|
5319
|
+
* Check if a point is near a node (within padding distance)
|
|
5320
|
+
*/
|
|
5321
|
+
isPointNearNode(x, y, nodePos, padding) {
|
|
5322
|
+
return x >= nodePos.x - padding && x <= nodePos.x + nodePos.width + padding && y >= nodePos.y - padding && y <= nodePos.y + nodePos.height + padding;
|
|
5323
|
+
}
|
|
5324
|
+
/**
|
|
5325
|
+
* Check if a label bounds overlaps with any node
|
|
5326
|
+
*/
|
|
5327
|
+
labelOverlapsAnyNode(labelBounds) {
|
|
5328
|
+
for (const nodePos of this.nodePositions.values()) {
|
|
5329
|
+
if (this.boundsOverlap(labelBounds, nodePos)) {
|
|
5330
|
+
return true;
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
return false;
|
|
5334
|
+
}
|
|
5335
|
+
/**
|
|
5336
|
+
* Check if a label bounds overlaps with any already placed edge label
|
|
5337
|
+
*/
|
|
5338
|
+
labelOverlapsAnyEdgeLabel(labelBounds) {
|
|
5339
|
+
for (const placedLabel of this.placedEdgeLabels) {
|
|
5340
|
+
if (this.boundsOverlap(labelBounds, placedLabel)) {
|
|
5341
|
+
return true;
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5344
|
+
return false;
|
|
5345
|
+
}
|
|
5346
|
+
/**
|
|
5347
|
+
* Check if two rectangles overlap
|
|
5348
|
+
*/
|
|
5349
|
+
boundsOverlap(a, b) {
|
|
5350
|
+
return !(a.x + a.width < b.x || b.x + b.width < a.x || a.y + a.height < b.y || b.y + b.height < a.y);
|
|
5488
5351
|
}
|
|
5489
5352
|
/**
|
|
5490
5353
|
* Ensure all waypoint segments are orthogonal (horizontal or vertical)
|
|
@@ -5516,6 +5379,135 @@ var init_diagram_builder = __esm({
|
|
|
5516
5379
|
}
|
|
5517
5380
|
}
|
|
5518
5381
|
}
|
|
5382
|
+
/**
|
|
5383
|
+
* Detect which side of a node a point is connected to.
|
|
5384
|
+
* Returns 'top', 'bottom', 'left', 'right', or 'unknown'.
|
|
5385
|
+
*
|
|
5386
|
+
* First tries exact match with tolerance, then falls back to closest edge detection.
|
|
5387
|
+
*/
|
|
5388
|
+
detectConnectionSide(point, nodeBounds, _isGateway = false) {
|
|
5389
|
+
const exactTolerance = 3;
|
|
5390
|
+
const maxTolerance = 15;
|
|
5391
|
+
const nodeTop = nodeBounds.y;
|
|
5392
|
+
const nodeBottom = nodeBounds.y + nodeBounds.height;
|
|
5393
|
+
const nodeLeft = nodeBounds.x;
|
|
5394
|
+
const nodeRight = nodeBounds.x + nodeBounds.width;
|
|
5395
|
+
if (Math.abs(point.y - nodeTop) <= exactTolerance) return "top";
|
|
5396
|
+
if (Math.abs(point.y - nodeBottom) <= exactTolerance) return "bottom";
|
|
5397
|
+
if (Math.abs(point.x - nodeLeft) <= exactTolerance) return "left";
|
|
5398
|
+
if (Math.abs(point.x - nodeRight) <= exactTolerance) return "right";
|
|
5399
|
+
const distToTop = Math.abs(point.y - nodeTop);
|
|
5400
|
+
const distToBottom = Math.abs(point.y - nodeBottom);
|
|
5401
|
+
const distToLeft = Math.abs(point.x - nodeLeft);
|
|
5402
|
+
const distToRight = Math.abs(point.x - nodeRight);
|
|
5403
|
+
const minDist = Math.min(distToTop, distToBottom, distToLeft, distToRight);
|
|
5404
|
+
if (minDist > maxTolerance) return "unknown";
|
|
5405
|
+
if (minDist === distToTop) return "top";
|
|
5406
|
+
if (minDist === distToBottom) return "bottom";
|
|
5407
|
+
if (minDist === distToLeft) return "left";
|
|
5408
|
+
return "right";
|
|
5409
|
+
}
|
|
5410
|
+
/**
|
|
5411
|
+
* Detect connection side for gateway based on edge direction.
|
|
5412
|
+
* When point is on a diagonal edge of the diamond, use the adjacent point to determine direction.
|
|
5413
|
+
*/
|
|
5414
|
+
detectGatewayConnectionSide(point, adjacentPoint, nodeBounds, isSource) {
|
|
5415
|
+
const centerX = nodeBounds.x + nodeBounds.width / 2;
|
|
5416
|
+
const centerY = nodeBounds.y + nodeBounds.height / 2;
|
|
5417
|
+
const nodeTop = nodeBounds.y;
|
|
5418
|
+
const nodeBottom = nodeBounds.y + nodeBounds.height;
|
|
5419
|
+
const nodeLeft = nodeBounds.x;
|
|
5420
|
+
const nodeRight = nodeBounds.x + nodeBounds.width;
|
|
5421
|
+
const distToTopCorner = Math.abs(point.x - centerX) + Math.abs(point.y - nodeTop);
|
|
5422
|
+
const distToBottomCorner = Math.abs(point.x - centerX) + Math.abs(point.y - nodeBottom);
|
|
5423
|
+
const distToLeftCorner = Math.abs(point.x - nodeLeft) + Math.abs(point.y - centerY);
|
|
5424
|
+
const distToRightCorner = Math.abs(point.x - nodeRight) + Math.abs(point.y - centerY);
|
|
5425
|
+
const minDist = Math.min(distToTopCorner, distToBottomCorner, distToLeftCorner, distToRightCorner);
|
|
5426
|
+
const tolerance = 5;
|
|
5427
|
+
if (distToTopCorner <= minDist + tolerance && distToTopCorner < distToBottomCorner - tolerance && distToTopCorner < distToLeftCorner - tolerance && distToTopCorner < distToRightCorner - tolerance) {
|
|
5428
|
+
return "top";
|
|
5429
|
+
}
|
|
5430
|
+
if (distToBottomCorner <= minDist + tolerance && distToBottomCorner < distToTopCorner - tolerance && distToBottomCorner < distToLeftCorner - tolerance && distToBottomCorner < distToRightCorner - tolerance) {
|
|
5431
|
+
return "bottom";
|
|
5432
|
+
}
|
|
5433
|
+
if (distToLeftCorner <= minDist + tolerance && distToLeftCorner < distToTopCorner - tolerance && distToLeftCorner < distToBottomCorner - tolerance && distToLeftCorner < distToRightCorner - tolerance) {
|
|
5434
|
+
return "left";
|
|
5435
|
+
}
|
|
5436
|
+
if (distToRightCorner <= minDist + tolerance && distToRightCorner < distToTopCorner - tolerance && distToRightCorner < distToBottomCorner - tolerance && distToRightCorner < distToLeftCorner - tolerance) {
|
|
5437
|
+
return "right";
|
|
5438
|
+
}
|
|
5439
|
+
const dx = isSource ? adjacentPoint.x - point.x : point.x - adjacentPoint.x;
|
|
5440
|
+
const dy = isSource ? adjacentPoint.y - point.y : point.y - adjacentPoint.y;
|
|
5441
|
+
if (Math.abs(dx) > Math.abs(dy)) {
|
|
5442
|
+
return dx > 0 ? "right" : "left";
|
|
5443
|
+
} else {
|
|
5444
|
+
return dy > 0 ? "bottom" : "top";
|
|
5445
|
+
}
|
|
5446
|
+
}
|
|
5447
|
+
/**
|
|
5448
|
+
* Ensure edge endpoints connect perpendicular to node borders.
|
|
5449
|
+
* - Connection to top/bottom: last segment must be vertical (same x)
|
|
5450
|
+
* - Connection to left/right: last segment must be horizontal (same y)
|
|
5451
|
+
*
|
|
5452
|
+
* When inserting bend points, we also update the previous waypoint to maintain
|
|
5453
|
+
* orthogonality (no diagonal lines).
|
|
5454
|
+
*/
|
|
5455
|
+
ensurePerpendicularEndpoints(waypoints, sourceId, targetId, sourceIsGateway = false, targetIsGateway = false) {
|
|
5456
|
+
if (waypoints.length < 2) return;
|
|
5457
|
+
const tolerance = 2;
|
|
5458
|
+
const minBendOffset = 15;
|
|
5459
|
+
if (targetId) {
|
|
5460
|
+
const targetPos = this.nodePositions.get(targetId);
|
|
5461
|
+
if (targetPos) {
|
|
5462
|
+
const lastIdx = waypoints.length - 1;
|
|
5463
|
+
const endPoint = waypoints[lastIdx];
|
|
5464
|
+
const prevPoint = waypoints[lastIdx - 1];
|
|
5465
|
+
if (endPoint && prevPoint) {
|
|
5466
|
+
const side = targetIsGateway ? this.detectGatewayConnectionSide(endPoint, prevPoint, targetPos, false) : this.detectConnectionSide(endPoint, targetPos, false);
|
|
5467
|
+
if (side === "top" || side === "bottom") {
|
|
5468
|
+
if (Math.abs(endPoint.x - prevPoint.x) > tolerance) {
|
|
5469
|
+
const bendY = side === "top" ? endPoint.y - minBendOffset : endPoint.y + minBendOffset;
|
|
5470
|
+
const bendPoint = { x: endPoint.x, y: bendY };
|
|
5471
|
+
waypoints.splice(lastIdx, 0, bendPoint);
|
|
5472
|
+
prevPoint.y = bendY;
|
|
5473
|
+
}
|
|
5474
|
+
} else if (side === "left" || side === "right") {
|
|
5475
|
+
if (Math.abs(endPoint.y - prevPoint.y) > tolerance) {
|
|
5476
|
+
const bendX = side === "left" ? endPoint.x - minBendOffset : endPoint.x + minBendOffset;
|
|
5477
|
+
const bendPoint = { x: bendX, y: endPoint.y };
|
|
5478
|
+
waypoints.splice(lastIdx, 0, bendPoint);
|
|
5479
|
+
prevPoint.x = bendX;
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
}
|
|
5485
|
+
if (sourceId) {
|
|
5486
|
+
const sourcePos = this.nodePositions.get(sourceId) ?? this.boundaryEventPositions.get(sourceId);
|
|
5487
|
+
if (sourcePos) {
|
|
5488
|
+
const startPoint = waypoints[0];
|
|
5489
|
+
const nextPoint = waypoints[1];
|
|
5490
|
+
if (startPoint && nextPoint) {
|
|
5491
|
+
const side = sourceIsGateway ? this.detectGatewayConnectionSide(startPoint, nextPoint, sourcePos, true) : this.detectConnectionSide(startPoint, sourcePos, false);
|
|
5492
|
+
if (side === "top" || side === "bottom") {
|
|
5493
|
+
if (Math.abs(startPoint.x - nextPoint.x) > tolerance) {
|
|
5494
|
+
const bendY = side === "top" ? startPoint.y - minBendOffset : startPoint.y + minBendOffset;
|
|
5495
|
+
const bendPoint = { x: startPoint.x, y: bendY };
|
|
5496
|
+
waypoints.splice(1, 0, bendPoint);
|
|
5497
|
+
nextPoint.y = bendY;
|
|
5498
|
+
}
|
|
5499
|
+
} else if (side === "left" || side === "right") {
|
|
5500
|
+
if (Math.abs(startPoint.y - nextPoint.y) > tolerance) {
|
|
5501
|
+
const bendX = side === "left" ? startPoint.x - minBendOffset : startPoint.x + minBendOffset;
|
|
5502
|
+
const bendPoint = { x: bendX, y: startPoint.y };
|
|
5503
|
+
waypoints.splice(1, 0, bendPoint);
|
|
5504
|
+
nextPoint.x = bendX;
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
}
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5519
5511
|
};
|
|
5520
5512
|
}
|
|
5521
5513
|
});
|
|
@@ -5570,7 +5562,10 @@ var init_model_builder = __esm({
|
|
|
5570
5562
|
rootElements: []
|
|
5571
5563
|
};
|
|
5572
5564
|
for (const child of graph.children) {
|
|
5573
|
-
const bpmnType = child.bpmn
|
|
5565
|
+
const bpmnType = child.bpmn?.type;
|
|
5566
|
+
if (!bpmnType) {
|
|
5567
|
+
throw new Error(`Invalid graph child: missing bpmn.type property for node ${child.id}`);
|
|
5568
|
+
}
|
|
5574
5569
|
if (bpmnType === "collaboration") {
|
|
5575
5570
|
definitions.rootElements.push(this.buildCollaboration(child));
|
|
5576
5571
|
const collab = child;
|
|
@@ -5584,8 +5579,13 @@ var init_model_builder = __esm({
|
|
|
5584
5579
|
}
|
|
5585
5580
|
} else if (bpmnType === "process") {
|
|
5586
5581
|
definitions.rootElements.push(this.buildProcess(child));
|
|
5582
|
+
} else {
|
|
5583
|
+
throw new Error(`Invalid top-level element type: "${bpmnType}". Only "process" or "collaboration" are allowed at the top level.`);
|
|
5587
5584
|
}
|
|
5588
5585
|
}
|
|
5586
|
+
if (definitions.rootElements.length === 0) {
|
|
5587
|
+
throw new Error("Cannot create BPMN definitions: no valid process or collaboration found in the graph.");
|
|
5588
|
+
}
|
|
5589
5589
|
return definitions;
|
|
5590
5590
|
}
|
|
5591
5591
|
/**
|
|
@@ -6453,7 +6453,10 @@ var init_converter = __esm({
|
|
|
6453
6453
|
modelBuilder;
|
|
6454
6454
|
xmlGenerator;
|
|
6455
6455
|
constructor(options) {
|
|
6456
|
-
this.layouter = new ElkLayouter({
|
|
6456
|
+
this.layouter = new ElkLayouter({
|
|
6457
|
+
elkOptions: options?.elkOptions,
|
|
6458
|
+
enableCompaction: options?.enableCompaction
|
|
6459
|
+
});
|
|
6457
6460
|
this.modelBuilder = new ModelBuilder();
|
|
6458
6461
|
this.xmlGenerator = new BpmnXmlGenerator();
|
|
6459
6462
|
}
|