@crazyhappyone/auto-graph 0.0.6 → 0.1.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/cli/index.cjs +4011 -2983
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +4011 -2983
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +1131 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -2
- package/dist/index.d.ts +39 -2
- package/dist/index.js +1131 -100
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -99,11 +99,21 @@ function applyLayoutConstraints(input) {
|
|
|
99
99
|
const nodeById = new Map(input.nodes.map((node) => [node.id, node]));
|
|
100
100
|
applyFixedPositionLocks(input.nodes, boxes, locks, diagnostics);
|
|
101
101
|
applyExactPositions(input.constraints, boxes, locks, diagnostics, nodeById);
|
|
102
|
-
applyContainment(input.constraints, boxes, locks, diagnostics);
|
|
102
|
+
applyContainment(input.constraints, boxes, locks, diagnostics, false);
|
|
103
103
|
applyRelative(input.constraints, boxes, locks, diagnostics);
|
|
104
104
|
applyAlign(input.constraints, boxes, locks, diagnostics);
|
|
105
105
|
applyDistribute(input.constraints, boxes, locks, diagnostics);
|
|
106
|
-
repairOverlaps(
|
|
106
|
+
repairOverlaps(
|
|
107
|
+
input,
|
|
108
|
+
boxes,
|
|
109
|
+
locks,
|
|
110
|
+
diagnostics,
|
|
111
|
+
siblingOverlapKeys(input.constraints)
|
|
112
|
+
);
|
|
113
|
+
applyContainment(input.constraints, boxes, locks, diagnostics, true);
|
|
114
|
+
applyDistributeContained(input, boxes, locks, diagnostics);
|
|
115
|
+
reportOverlaps(boxes, diagnostics, containmentOverlapKeys(input.constraints));
|
|
116
|
+
reportIntraContainerOverflow(input, boxes, diagnostics);
|
|
107
117
|
return { boxes, locks, diagnostics };
|
|
108
118
|
}
|
|
109
119
|
function cloneValidBoxes(input, diagnostics) {
|
|
@@ -191,7 +201,7 @@ function applyExactPositions(constraints, boxes, locks, diagnostics, nodeById) {
|
|
|
191
201
|
locks.set(targetId, { nodeId: targetId, source: "exact-position" });
|
|
192
202
|
}
|
|
193
203
|
}
|
|
194
|
-
function applyContainment(constraints, boxes, locks, diagnostics) {
|
|
204
|
+
function applyContainment(constraints, boxes, locks, diagnostics, reportOverflow) {
|
|
195
205
|
for (const constraint of constraints) {
|
|
196
206
|
if (constraint.kind !== "containment") {
|
|
197
207
|
continue;
|
|
@@ -213,21 +223,23 @@ function applyContainment(constraints, boxes, locks, diagnostics) {
|
|
|
213
223
|
continue;
|
|
214
224
|
}
|
|
215
225
|
if (locks.has(childId)) {
|
|
216
|
-
|
|
217
|
-
severity: "warning",
|
|
218
|
-
code: "constraints.locked-target-not-moved",
|
|
219
|
-
message: `Locked child ${childId} was not moved into containment.`,
|
|
220
|
-
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
221
|
-
detail: { nodeId: childId }
|
|
222
|
-
});
|
|
223
|
-
if (!isInside(child, content)) {
|
|
226
|
+
if (!reportOverflow) {
|
|
224
227
|
diagnostics.push({
|
|
225
|
-
severity: "
|
|
226
|
-
code: "constraints.
|
|
227
|
-
message: `Locked child ${childId}
|
|
228
|
+
severity: "warning",
|
|
229
|
+
code: "constraints.locked-target-not-moved",
|
|
230
|
+
message: `Locked child ${childId} was not moved into containment.`,
|
|
228
231
|
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
229
|
-
detail: { nodeId: childId
|
|
232
|
+
detail: { nodeId: childId }
|
|
230
233
|
});
|
|
234
|
+
if (!isInside(child, content)) {
|
|
235
|
+
diagnostics.push({
|
|
236
|
+
severity: "error",
|
|
237
|
+
code: "constraints.containment.impossible",
|
|
238
|
+
message: `Locked child ${childId} cannot fit inside ${constraint.containerId}.`,
|
|
239
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
240
|
+
detail: { nodeId: childId, containerId: constraint.containerId }
|
|
241
|
+
});
|
|
242
|
+
}
|
|
231
243
|
}
|
|
232
244
|
continue;
|
|
233
245
|
}
|
|
@@ -242,6 +254,15 @@ function applyContainment(constraints, boxes, locks, diagnostics) {
|
|
|
242
254
|
continue;
|
|
243
255
|
}
|
|
244
256
|
boxes.set(childId, next);
|
|
257
|
+
if (reportOverflow) {
|
|
258
|
+
diagnostics.push({
|
|
259
|
+
severity: "warning",
|
|
260
|
+
code: "containment_overflow",
|
|
261
|
+
message: `Child ${childId} was clamped back inside ${constraint.containerId} after constraint solving.`,
|
|
262
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
263
|
+
detail: { nodeId: childId, containerId: constraint.containerId }
|
|
264
|
+
});
|
|
265
|
+
}
|
|
245
266
|
}
|
|
246
267
|
}
|
|
247
268
|
}
|
|
@@ -317,10 +338,11 @@ function applyDistribute(constraints, boxes, locks, diagnostics) {
|
|
|
317
338
|
}
|
|
318
339
|
}
|
|
319
340
|
}
|
|
320
|
-
function repairOverlaps(input, boxes, locks, diagnostics) {
|
|
341
|
+
function repairOverlaps(input, boxes, locks, diagnostics, siblingPairs) {
|
|
321
342
|
const spacing = input.overlapSpacing ?? 40;
|
|
322
343
|
const axis = input.direction === "LR" || input.direction === "RL" ? "x" : "y";
|
|
323
344
|
const secondaryAxis = axis === "x" ? "y" : "x";
|
|
345
|
+
const ignoredPairs = containmentOverlapKeys(input.constraints);
|
|
324
346
|
const ids = [...boxes.keys()].sort();
|
|
325
347
|
for (let pass = 0; pass < 2; pass += 1) {
|
|
326
348
|
for (const firstId of ids) {
|
|
@@ -328,6 +350,9 @@ function repairOverlaps(input, boxes, locks, diagnostics) {
|
|
|
328
350
|
if (firstId >= secondId) {
|
|
329
351
|
continue;
|
|
330
352
|
}
|
|
353
|
+
if (ignoredPairs.has(overlapKey(firstId, secondId))) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
331
356
|
const first = boxes.get(firstId);
|
|
332
357
|
const second = boxes.get(secondId);
|
|
333
358
|
if (first === void 0 || second === void 0 || !intersectsAabb(first, second)) {
|
|
@@ -342,16 +367,40 @@ function repairOverlaps(input, boxes, locks, diagnostics) {
|
|
|
342
367
|
const moving = movingId === firstId ? first : second;
|
|
343
368
|
const fixed = movingId === firstId ? second : first;
|
|
344
369
|
const repairAxis = firstLocked === secondLocked && pass === 0 ? secondaryAxis : axis;
|
|
345
|
-
const
|
|
370
|
+
const pairKey = overlapKey(firstId, secondId);
|
|
371
|
+
const effectiveSpacing = siblingPairs.has(pairKey) ? Math.max(spacing, input.minSiblingGap ?? 0) : spacing;
|
|
372
|
+
const moved = movePastOverlap(
|
|
373
|
+
moving,
|
|
374
|
+
fixed,
|
|
375
|
+
repairAxis,
|
|
376
|
+
effectiveSpacing
|
|
377
|
+
);
|
|
346
378
|
boxes.set(movingId, moved);
|
|
347
379
|
}
|
|
348
380
|
}
|
|
349
381
|
}
|
|
382
|
+
reportOverlaps(boxes, diagnostics, ignoredPairs);
|
|
383
|
+
}
|
|
384
|
+
function reportOverlaps(boxes, diagnostics, ignoredPairs = /* @__PURE__ */ new Set()) {
|
|
385
|
+
const ids = [...boxes.keys()].sort();
|
|
386
|
+
const reported = new Set(
|
|
387
|
+
diagnostics.filter(
|
|
388
|
+
(diagnostic) => diagnostic.code === "constraints.overlap.unresolved"
|
|
389
|
+
).map((diagnostic) => {
|
|
390
|
+
const firstId = diagnostic.detail?.firstId;
|
|
391
|
+
const secondId = diagnostic.detail?.secondId;
|
|
392
|
+
return typeof firstId === "string" && typeof secondId === "string" ? overlapKey(firstId, secondId) : void 0;
|
|
393
|
+
}).filter((key) => key !== void 0)
|
|
394
|
+
);
|
|
350
395
|
for (const firstId of ids) {
|
|
351
396
|
for (const secondId of ids) {
|
|
352
397
|
if (firstId >= secondId) {
|
|
353
398
|
continue;
|
|
354
399
|
}
|
|
400
|
+
const key = overlapKey(firstId, secondId);
|
|
401
|
+
if (reported.has(key) || ignoredPairs.has(key)) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
355
404
|
const first = boxes.get(firstId);
|
|
356
405
|
const second = boxes.get(secondId);
|
|
357
406
|
if (first !== void 0 && second !== void 0 && intersectsAabb(first, second)) {
|
|
@@ -362,9 +411,136 @@ function repairOverlaps(input, boxes, locks, diagnostics) {
|
|
|
362
411
|
path: ["boxes"],
|
|
363
412
|
detail: { firstId, secondId }
|
|
364
413
|
});
|
|
414
|
+
reported.add(key);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function reportIntraContainerOverflow(input, boxes, diagnostics) {
|
|
420
|
+
if (input.minSiblingGap === void 0) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const minGap = input.minSiblingGap;
|
|
424
|
+
const axis = input.direction === "LR" || input.direction === "RL" ? "x" : "y";
|
|
425
|
+
for (const constraint of input.constraints) {
|
|
426
|
+
if (constraint.kind !== "containment") {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
const container = boxes.get(constraint.containerId);
|
|
430
|
+
if (container === void 0) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
const children = [];
|
|
434
|
+
for (const childId of constraint.childIds) {
|
|
435
|
+
const child = boxes.get(childId);
|
|
436
|
+
if (child !== void 0) {
|
|
437
|
+
children.push(child);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (children.length < 2) {
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
const sorted = [...children].sort((a, b) => a[axis] - b[axis]);
|
|
444
|
+
const mainDim = axis === "x" ? "width" : "height";
|
|
445
|
+
let overlapPairs = 0;
|
|
446
|
+
for (let i = 0; i < sorted.length; i += 1) {
|
|
447
|
+
const first = sorted[i];
|
|
448
|
+
if (first === void 0) {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
for (let j = i + 1; j < sorted.length; j += 1) {
|
|
452
|
+
const second = sorted[j];
|
|
453
|
+
if (second === void 0) {
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (second[axis] >= first[axis] + first[mainDim]) {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
if (intersectsAabb(first, second)) {
|
|
460
|
+
overlapPairs += 1;
|
|
461
|
+
}
|
|
365
462
|
}
|
|
366
463
|
}
|
|
464
|
+
if (overlapPairs > 0) {
|
|
465
|
+
diagnostics.push({
|
|
466
|
+
severity: "warning",
|
|
467
|
+
code: "intra_container_overflow",
|
|
468
|
+
message: `${overlapPairs} sibling pair(s) overlap inside ${constraint.containerId}.`,
|
|
469
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
470
|
+
detail: {
|
|
471
|
+
containerId: constraint.containerId,
|
|
472
|
+
overlapPairs,
|
|
473
|
+
minGap
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
const pad = constraint.padding ?? { top: 0, right: 0, bottom: 0, left: 0 };
|
|
478
|
+
const contentMain = mainDim === "width" ? Math.max(0, container.width - pad.left - pad.right) : Math.max(0, container.height - pad.top - pad.bottom);
|
|
479
|
+
let childStart = Infinity;
|
|
480
|
+
let childEnd = -Infinity;
|
|
481
|
+
for (const child of sorted) {
|
|
482
|
+
const start = child[axis];
|
|
483
|
+
const end = start + child[mainDim];
|
|
484
|
+
if (start < childStart) childStart = start;
|
|
485
|
+
if (end > childEnd) childEnd = end;
|
|
486
|
+
}
|
|
487
|
+
if (sorted.length === 0) {
|
|
488
|
+
childStart = 0;
|
|
489
|
+
childEnd = 0;
|
|
490
|
+
}
|
|
491
|
+
const actualExtent = childEnd - childStart;
|
|
492
|
+
if (actualExtent > contentMain) {
|
|
493
|
+
diagnostics.push({
|
|
494
|
+
severity: "error",
|
|
495
|
+
code: "intra_container_overflow_total",
|
|
496
|
+
message: `Container ${constraint.containerId} cannot fit ${sorted.length} siblings along ${axis} (extent ${actualExtent}, available ${contentMain}).`,
|
|
497
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
498
|
+
detail: {
|
|
499
|
+
containerId: constraint.containerId,
|
|
500
|
+
axis,
|
|
501
|
+
needed: actualExtent,
|
|
502
|
+
available: contentMain,
|
|
503
|
+
siblingCount: sorted.length,
|
|
504
|
+
minGap
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function overlapKey(firstId, secondId) {
|
|
511
|
+
return firstId < secondId ? `${firstId}\0${secondId}` : `${secondId}\0${firstId}`;
|
|
512
|
+
}
|
|
513
|
+
function containmentOverlapKeys(constraints) {
|
|
514
|
+
const keys = /* @__PURE__ */ new Set();
|
|
515
|
+
for (const constraint of constraints) {
|
|
516
|
+
if (constraint.kind !== "containment") {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
for (const childId of constraint.childIds) {
|
|
520
|
+
keys.add(overlapKey(constraint.containerId, childId));
|
|
521
|
+
}
|
|
367
522
|
}
|
|
523
|
+
return keys;
|
|
524
|
+
}
|
|
525
|
+
function siblingOverlapKeys(constraints) {
|
|
526
|
+
const keys = /* @__PURE__ */ new Set();
|
|
527
|
+
for (const constraint of constraints) {
|
|
528
|
+
if (constraint.kind !== "containment") {
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
const { childIds } = constraint;
|
|
532
|
+
for (let i = 0; i < childIds.length; i += 1) {
|
|
533
|
+
for (let j = i + 1; j < childIds.length; j += 1) {
|
|
534
|
+
const a = childIds[i];
|
|
535
|
+
const b = childIds[j];
|
|
536
|
+
if (a === void 0 || b === void 0) {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
keys.add(overlapKey(a, b));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return keys;
|
|
368
544
|
}
|
|
369
545
|
function setUnlockedBox(id, next, boxes, locks, diagnostics, constraint) {
|
|
370
546
|
const current = boxes.get(id);
|
|
@@ -482,10 +658,125 @@ function contentBox(container, padding) {
|
|
|
482
658
|
return {
|
|
483
659
|
x: container.x + margin.left,
|
|
484
660
|
y: container.y + margin.top,
|
|
485
|
-
width: container.width - margin.left - margin.right,
|
|
486
|
-
height: container.height - margin.top - margin.bottom
|
|
661
|
+
width: Math.max(0, container.width - margin.left - margin.right),
|
|
662
|
+
height: Math.max(0, container.height - margin.top - margin.bottom)
|
|
487
663
|
};
|
|
488
664
|
}
|
|
665
|
+
function applyDistributeContained(input, boxes, locks, diagnostics) {
|
|
666
|
+
if (!input.distributeContainedChildren) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const axis = input.direction === "LR" || input.direction === "RL" ? "x" : "y";
|
|
670
|
+
const crossAxis = axis === "x" ? "y" : "x";
|
|
671
|
+
const mainSize = axis === "x" ? "width" : "height";
|
|
672
|
+
const crossSize = axis === "x" ? "height" : "width";
|
|
673
|
+
const minGap = input.minSiblingGap ?? 8;
|
|
674
|
+
for (const constraint of input.constraints) {
|
|
675
|
+
if (constraint.kind !== "containment") {
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
const container = boxes.get(constraint.containerId);
|
|
679
|
+
if (container === void 0) {
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
const content = contentBox(container, constraint.padding);
|
|
683
|
+
const unlocked = [];
|
|
684
|
+
const reserved = [];
|
|
685
|
+
for (const childId of constraint.childIds) {
|
|
686
|
+
const box = boxes.get(childId);
|
|
687
|
+
if (box === void 0) {
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
if (locks.has(childId)) {
|
|
691
|
+
diagnostics.push({
|
|
692
|
+
severity: "warning",
|
|
693
|
+
code: "constraints.locked-target-not-moved",
|
|
694
|
+
message: `Locked child ${childId} skipped during containment distribution.`,
|
|
695
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
696
|
+
detail: { nodeId: childId }
|
|
697
|
+
});
|
|
698
|
+
reserved.push(intervalForBox(box, axis, mainSize));
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
unlocked.push({ id: childId, box });
|
|
702
|
+
}
|
|
703
|
+
if (unlocked.length < 2) {
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
const oversized = unlocked.filter(
|
|
707
|
+
(child) => child.box[mainSize] > content[mainSize] || child.box[crossSize] > content[crossSize]
|
|
708
|
+
);
|
|
709
|
+
if (oversized.length > 0) {
|
|
710
|
+
diagnostics.push({
|
|
711
|
+
severity: "warning",
|
|
712
|
+
code: "constraints.containment.impossible",
|
|
713
|
+
message: `Skipped ${oversized.length} oversized child(ren) during distribution in ${constraint.containerId}.`,
|
|
714
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
715
|
+
detail: {
|
|
716
|
+
containerId: constraint.containerId,
|
|
717
|
+
oversized: oversized.map((c) => c.id)
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
for (const child of oversized) {
|
|
722
|
+
reserved.push(intervalForBox(child.box, axis, mainSize));
|
|
723
|
+
}
|
|
724
|
+
reserved.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
725
|
+
const distributable = unlocked.filter(
|
|
726
|
+
(child) => child.box[mainSize] <= content[mainSize] && child.box[crossSize] <= content[crossSize]
|
|
727
|
+
);
|
|
728
|
+
if (distributable.length < 2) {
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
let pos = content[axis];
|
|
732
|
+
for (const child of distributable) {
|
|
733
|
+
pos = advancePastReserved(pos, child.box[mainSize], reserved, minGap);
|
|
734
|
+
const crossPos = content[crossAxis] + Math.max(0, (content[crossSize] - child.box[crossSize]) / 2);
|
|
735
|
+
const next = { ...child.box };
|
|
736
|
+
next[axis] = pos;
|
|
737
|
+
next[crossAxis] = crossPos;
|
|
738
|
+
const clamped = moveInside(next, content);
|
|
739
|
+
if (clamped[axis] !== next[axis]) {
|
|
740
|
+
diagnostics.push({
|
|
741
|
+
severity: "warning",
|
|
742
|
+
code: "intra_container_distributed_clamped",
|
|
743
|
+
message: `Distribution gap clamped for ${child.id} in ${constraint.containerId}.`,
|
|
744
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
745
|
+
detail: { nodeId: child.id, containerId: constraint.containerId }
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
boxes.set(child.id, clamped);
|
|
749
|
+
pos = clamped[axis] + clamped[mainSize] + minGap;
|
|
750
|
+
}
|
|
751
|
+
diagnostics.push({
|
|
752
|
+
severity: "info",
|
|
753
|
+
code: "intra_container_distributed",
|
|
754
|
+
message: `Distributed ${distributable.length} children in ${constraint.containerId} along ${axis}.`,
|
|
755
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
756
|
+
detail: {
|
|
757
|
+
containerId: constraint.containerId,
|
|
758
|
+
count: distributable.length,
|
|
759
|
+
axis
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
function intervalForBox(box, axis, mainSize) {
|
|
765
|
+
return { start: box[axis], end: box[axis] + box[mainSize] };
|
|
766
|
+
}
|
|
767
|
+
function advancePastReserved(pos, size, reserved, minGap) {
|
|
768
|
+
let next = pos;
|
|
769
|
+
for (const interval of reserved) {
|
|
770
|
+
if (next + size + minGap <= interval.start) {
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
if (next >= interval.end + minGap) {
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
next = interval.end + minGap;
|
|
777
|
+
}
|
|
778
|
+
return next;
|
|
779
|
+
}
|
|
489
780
|
function moveInside(child, container) {
|
|
490
781
|
return {
|
|
491
782
|
...child,
|
|
@@ -1191,7 +1482,11 @@ var DEFAULT_GROUP_PADDING = {
|
|
|
1191
1482
|
left: 16
|
|
1192
1483
|
};
|
|
1193
1484
|
var DEFAULT_LABEL_MAX_WIDTH = 160;
|
|
1194
|
-
var DEFAULT_FONT = {
|
|
1485
|
+
var DEFAULT_FONT = {
|
|
1486
|
+
fontFamily: "Arial",
|
|
1487
|
+
fontSize: 14,
|
|
1488
|
+
lineHeight: 18
|
|
1489
|
+
};
|
|
1195
1490
|
var DEFAULT_MATRIX_CELL_SIZE = { width: 120, height: 36 };
|
|
1196
1491
|
var DEFAULT_TABLE_CELL_SIZE = { width: 128, height: 34 };
|
|
1197
1492
|
var DEFAULT_PANEL_ITEM_HEIGHT = 28;
|
|
@@ -1367,7 +1662,9 @@ function endpoint(value, nodeIdOverride) {
|
|
|
1367
1662
|
function style(value) {
|
|
1368
1663
|
return {
|
|
1369
1664
|
...value.fill === void 0 ? {} : { fill: value.fill },
|
|
1370
|
-
...value.stroke === void 0 ? {} : { stroke: value.stroke }
|
|
1665
|
+
...value.stroke === void 0 ? {} : { stroke: value.stroke },
|
|
1666
|
+
...value.fontFamily === void 0 ? {} : { fontFamily: value.fontFamily },
|
|
1667
|
+
...value.fontSize === void 0 ? {} : { fontSize: value.fontSize }
|
|
1371
1668
|
};
|
|
1372
1669
|
}
|
|
1373
1670
|
function compartments(value) {
|
|
@@ -1384,6 +1681,10 @@ function normalizeFrame(frame) {
|
|
|
1384
1681
|
...frame.context === void 0 ? {} : { context: frame.context },
|
|
1385
1682
|
...frame.name === void 0 ? {} : { name: frame.name },
|
|
1386
1683
|
titleTab: frame.titleTab,
|
|
1684
|
+
...frame.headerHeight === void 0 ? {} : { headerHeight: frame.headerHeight },
|
|
1685
|
+
...frame.padding === void 0 ? {} : { padding: frame.padding },
|
|
1686
|
+
...frame.labelPosition === void 0 ? {} : { labelPosition: frame.labelPosition },
|
|
1687
|
+
...frame.direction === void 0 ? {} : { direction: frame.direction },
|
|
1387
1688
|
...frame.style === void 0 ? {} : { style: style(frame.style) }
|
|
1388
1689
|
};
|
|
1389
1690
|
}
|
|
@@ -1527,6 +1828,9 @@ function normalizeGroups(dsl, measurer) {
|
|
|
1527
1828
|
nodeIds: [...group?.nodes ?? []],
|
|
1528
1829
|
groupIds: [...group?.groups ?? []],
|
|
1529
1830
|
padding: group?.padding ?? { ...DEFAULT_GROUP_PADDING },
|
|
1831
|
+
...group?.headerHeight === void 0 ? {} : { headerHeight: group.headerHeight },
|
|
1832
|
+
...group?.labelPosition === void 0 ? {} : { labelPosition: group.labelPosition },
|
|
1833
|
+
...group?.direction === void 0 ? {} : { direction: group.direction },
|
|
1530
1834
|
...labelLayout === void 0 ? {} : { labelLayout }
|
|
1531
1835
|
};
|
|
1532
1836
|
});
|
|
@@ -1796,6 +2100,12 @@ var insetsSchema = zod.z.object({
|
|
|
1796
2100
|
bottom: finiteNumberSchema,
|
|
1797
2101
|
left: finiteNumberSchema
|
|
1798
2102
|
});
|
|
2103
|
+
var nonNegativeInsetsSchema = zod.z.object({
|
|
2104
|
+
top: nonNegativeNumberSchema,
|
|
2105
|
+
right: nonNegativeNumberSchema,
|
|
2106
|
+
bottom: nonNegativeNumberSchema,
|
|
2107
|
+
left: nonNegativeNumberSchema
|
|
2108
|
+
});
|
|
1799
2109
|
var labelSchema = zod.z.union([
|
|
1800
2110
|
zod.z.string(),
|
|
1801
2111
|
zod.z.object({
|
|
@@ -1805,7 +2115,9 @@ var labelSchema = zod.z.union([
|
|
|
1805
2115
|
]);
|
|
1806
2116
|
var styleSchema = zod.z.object({
|
|
1807
2117
|
fill: zod.z.string().optional(),
|
|
1808
|
-
stroke: zod.z.string().optional()
|
|
2118
|
+
stroke: zod.z.string().optional(),
|
|
2119
|
+
fontFamily: zod.z.string().optional(),
|
|
2120
|
+
fontSize: finiteNumberSchema.optional()
|
|
1809
2121
|
});
|
|
1810
2122
|
var blockCellSchema = zod.z.union([
|
|
1811
2123
|
zod.z.string(),
|
|
@@ -1876,7 +2188,10 @@ var groupSchema = zod.z.object({
|
|
|
1876
2188
|
label: labelSchema.optional(),
|
|
1877
2189
|
nodes: zod.z.array(zod.z.string()).optional(),
|
|
1878
2190
|
groups: zod.z.array(zod.z.string()).optional(),
|
|
1879
|
-
padding: insetsSchema.optional()
|
|
2191
|
+
padding: insetsSchema.optional(),
|
|
2192
|
+
headerHeight: nonNegativeNumberSchema.optional(),
|
|
2193
|
+
labelPosition: zod.z.enum(["top", "inside", "outside"]).optional(),
|
|
2194
|
+
direction: zod.z.enum(["horizontal", "vertical"]).optional()
|
|
1880
2195
|
});
|
|
1881
2196
|
var swimlaneSchema = zod.z.object({
|
|
1882
2197
|
label: labelSchema.optional(),
|
|
@@ -2088,6 +2403,10 @@ var diagramDslSchema = zod.z.object({
|
|
|
2088
2403
|
context: zod.z.string().optional(),
|
|
2089
2404
|
name: zod.z.string().optional(),
|
|
2090
2405
|
titleTab: zod.z.string(),
|
|
2406
|
+
headerHeight: nonNegativeNumberSchema.optional(),
|
|
2407
|
+
padding: zod.z.union([nonNegativeNumberSchema, nonNegativeInsetsSchema]).optional(),
|
|
2408
|
+
labelPosition: zod.z.enum(["top", "inside", "outside"]).optional(),
|
|
2409
|
+
direction: zod.z.enum(["horizontal", "vertical"]).optional(),
|
|
2091
2410
|
style: styleSchema.optional()
|
|
2092
2411
|
}).optional(),
|
|
2093
2412
|
output: zod.z.object({
|
|
@@ -3222,7 +3541,7 @@ function renderSolvedTextAnnotation(annotation, className, options) {
|
|
|
3222
3541
|
`data-text-surface="${escapeAttribute(annotation.surfaceKind)}"`,
|
|
3223
3542
|
`data-owner-id="${escapeAttribute(annotation.ownerId)}"`,
|
|
3224
3543
|
`data-text-backend="${escapeAttribute(annotation.textBackend ?? "deterministic")}"`,
|
|
3225
|
-
`font-family="${
|
|
3544
|
+
`font-family="${escapeAttribute(annotation.fontFamily)}"`,
|
|
3226
3545
|
`font-size="${formatNumber(annotation.fontSize)}"`,
|
|
3227
3546
|
`fill="#111827"`
|
|
3228
3547
|
];
|
|
@@ -3550,7 +3869,12 @@ function routeEdge(input) {
|
|
|
3550
3869
|
input.source.center,
|
|
3551
3870
|
input.targetAnchor ?? defaultAnchors.targetAnchor
|
|
3552
3871
|
);
|
|
3553
|
-
const points =
|
|
3872
|
+
const points = finalizeRoute(
|
|
3873
|
+
[source, target],
|
|
3874
|
+
softObstacles,
|
|
3875
|
+
hardObstacles,
|
|
3876
|
+
diagnostics
|
|
3877
|
+
);
|
|
3554
3878
|
if (routeCrossesBoxes(points, hardObstacles)) {
|
|
3555
3879
|
diagnostics.push({
|
|
3556
3880
|
severity: "error",
|
|
@@ -3606,7 +3930,15 @@ function routeEdge(input) {
|
|
|
3606
3930
|
candidate.points,
|
|
3607
3931
|
candidate.endpointObstacles
|
|
3608
3932
|
)) {
|
|
3609
|
-
return {
|
|
3933
|
+
return {
|
|
3934
|
+
points: finalizeRoute(
|
|
3935
|
+
candidate.points,
|
|
3936
|
+
softObstacles,
|
|
3937
|
+
hardObstacles,
|
|
3938
|
+
diagnostics
|
|
3939
|
+
),
|
|
3940
|
+
diagnostics
|
|
3941
|
+
};
|
|
3610
3942
|
}
|
|
3611
3943
|
}
|
|
3612
3944
|
const hardClearCandidate = candidateRoutes.find(
|
|
@@ -3622,7 +3954,12 @@ function routeEdge(input) {
|
|
|
3622
3954
|
message: "No bounded orthogonal route candidate avoided all soft obstacles."
|
|
3623
3955
|
});
|
|
3624
3956
|
return {
|
|
3625
|
-
points:
|
|
3957
|
+
points: finalizeRoute(
|
|
3958
|
+
hardClearCandidate.points,
|
|
3959
|
+
softObstacles,
|
|
3960
|
+
hardObstacles,
|
|
3961
|
+
diagnostics
|
|
3962
|
+
),
|
|
3626
3963
|
diagnostics
|
|
3627
3964
|
};
|
|
3628
3965
|
}
|
|
@@ -3633,8 +3970,11 @@ function routeEdge(input) {
|
|
|
3633
3970
|
message: "No bounded orthogonal route candidate avoided hard evidence block obstacles."
|
|
3634
3971
|
});
|
|
3635
3972
|
return {
|
|
3636
|
-
points:
|
|
3637
|
-
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors)
|
|
3973
|
+
points: finalizeRoute(
|
|
3974
|
+
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
|
|
3975
|
+
softObstacles,
|
|
3976
|
+
hardObstacles,
|
|
3977
|
+
diagnostics
|
|
3638
3978
|
),
|
|
3639
3979
|
diagnostics
|
|
3640
3980
|
};
|
|
@@ -3645,12 +3985,133 @@ function routeEdge(input) {
|
|
|
3645
3985
|
message: "No bounded orthogonal route candidate avoided all obstacles."
|
|
3646
3986
|
});
|
|
3647
3987
|
return {
|
|
3648
|
-
points:
|
|
3649
|
-
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors)
|
|
3988
|
+
points: finalizeRoute(
|
|
3989
|
+
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
|
|
3990
|
+
softObstacles,
|
|
3991
|
+
hardObstacles,
|
|
3992
|
+
diagnostics
|
|
3650
3993
|
),
|
|
3651
3994
|
diagnostics
|
|
3652
3995
|
};
|
|
3653
3996
|
}
|
|
3997
|
+
function finalizeRoute(points, softObstacles, hardObstacles, diagnostics) {
|
|
3998
|
+
const simplified = simplifyRoute(points);
|
|
3999
|
+
const crossesHardObstacles = routeCrossesBoxes(simplified, hardObstacles);
|
|
4000
|
+
const crossesSoftObstacles = routeCrossesBoxes(simplified, softObstacles);
|
|
4001
|
+
if (simplified.length < 3 && (crossesHardObstacles || crossesSoftObstacles)) {
|
|
4002
|
+
diagnostics.push({
|
|
4003
|
+
severity: crossesHardObstacles ? "error" : "warning",
|
|
4004
|
+
code: "route_obstacle_fallback",
|
|
4005
|
+
message: "Obstacle-aware routing fell back to fewer than three route points.",
|
|
4006
|
+
detail: { pointCount: simplified.length }
|
|
4007
|
+
});
|
|
4008
|
+
return expandFallbackRoute(simplified, [
|
|
4009
|
+
...softObstacles,
|
|
4010
|
+
...hardObstacles
|
|
4011
|
+
]);
|
|
4012
|
+
}
|
|
4013
|
+
return simplified;
|
|
4014
|
+
}
|
|
4015
|
+
function expandFallbackRoute(points, obstacles) {
|
|
4016
|
+
if (points.length !== 2) {
|
|
4017
|
+
return points.map((point2) => ({ ...point2 }));
|
|
4018
|
+
}
|
|
4019
|
+
const [source, target] = points;
|
|
4020
|
+
if (source === void 0 || target === void 0) {
|
|
4021
|
+
return points.map((point2) => ({ ...point2 }));
|
|
4022
|
+
}
|
|
4023
|
+
if (source.y === target.y) {
|
|
4024
|
+
const detourY = horizontalDetourLane(source, target, obstacles);
|
|
4025
|
+
return [
|
|
4026
|
+
{ ...source },
|
|
4027
|
+
{ x: source.x, y: detourY },
|
|
4028
|
+
{ x: target.x, y: detourY },
|
|
4029
|
+
{ ...target }
|
|
4030
|
+
];
|
|
4031
|
+
}
|
|
4032
|
+
if (source.x === target.x) {
|
|
4033
|
+
const detourX = verticalDetourLane(source, target, obstacles);
|
|
4034
|
+
return [
|
|
4035
|
+
{ ...source },
|
|
4036
|
+
{ x: detourX, y: source.y },
|
|
4037
|
+
{ x: detourX, y: target.y },
|
|
4038
|
+
{ ...target }
|
|
4039
|
+
];
|
|
4040
|
+
}
|
|
4041
|
+
const hv = diagonalDetourHV(source, target, obstacles);
|
|
4042
|
+
const vh = diagonalDetourVH(source, target, obstacles);
|
|
4043
|
+
const viable = [hv, vh].filter((c) => !routeCrossesBoxes(c, obstacles));
|
|
4044
|
+
if (viable.length > 0) {
|
|
4045
|
+
const directLen = Math.hypot(target.x - source.x, target.y - source.y);
|
|
4046
|
+
let best = viable[0];
|
|
4047
|
+
for (let i = 1; i < viable.length; i += 1) {
|
|
4048
|
+
const cand = viable[i];
|
|
4049
|
+
if (cand !== void 0 && pathLength(cand) - directLen < pathLength(best) - directLen) {
|
|
4050
|
+
best = cand;
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
return best;
|
|
4054
|
+
}
|
|
4055
|
+
return [
|
|
4056
|
+
{ ...source },
|
|
4057
|
+
{ x: (source.x + target.x) / 2, y: source.y },
|
|
4058
|
+
{ x: (source.x + target.x) / 2, y: target.y },
|
|
4059
|
+
{ ...target }
|
|
4060
|
+
];
|
|
4061
|
+
}
|
|
4062
|
+
function horizontalDetourLane(source, target, obstacles) {
|
|
4063
|
+
const crossing = obstacles.filter(
|
|
4064
|
+
(obstacle) => segmentIntersectsBox(source, target, obstacle)
|
|
4065
|
+
);
|
|
4066
|
+
if (crossing.length === 0) {
|
|
4067
|
+
return source.y + (source.x <= target.x ? 1 : -1) * 24;
|
|
4068
|
+
}
|
|
4069
|
+
const margin = 24;
|
|
4070
|
+
const above = Math.min(...crossing.map((obstacle) => obstacle.y)) - margin;
|
|
4071
|
+
const below = Math.max(...crossing.map((obstacle) => obstacle.y + obstacle.height)) + margin;
|
|
4072
|
+
return Math.abs(above - source.y) <= Math.abs(below - source.y) ? above : below;
|
|
4073
|
+
}
|
|
4074
|
+
function verticalDetourLane(source, target, obstacles) {
|
|
4075
|
+
const crossing = obstacles.filter(
|
|
4076
|
+
(obstacle) => segmentIntersectsBox(source, target, obstacle)
|
|
4077
|
+
);
|
|
4078
|
+
if (crossing.length === 0) {
|
|
4079
|
+
return source.x + (source.y <= target.y ? 1 : -1) * 24;
|
|
4080
|
+
}
|
|
4081
|
+
const margin = 24;
|
|
4082
|
+
const left = Math.min(...crossing.map((obstacle) => obstacle.x)) - margin;
|
|
4083
|
+
const right = Math.max(...crossing.map((obstacle) => obstacle.x + obstacle.width)) + margin;
|
|
4084
|
+
return Math.abs(left - source.x) <= Math.abs(right - source.x) ? left : right;
|
|
4085
|
+
}
|
|
4086
|
+
function diagonalDetourHV(source, target, obstacles) {
|
|
4087
|
+
const detourY = horizontalDetourLane(source, target, obstacles);
|
|
4088
|
+
return [
|
|
4089
|
+
{ ...source },
|
|
4090
|
+
{ x: source.x, y: detourY },
|
|
4091
|
+
{ x: target.x, y: detourY },
|
|
4092
|
+
{ ...target }
|
|
4093
|
+
];
|
|
4094
|
+
}
|
|
4095
|
+
function diagonalDetourVH(source, target, obstacles) {
|
|
4096
|
+
const detourX = verticalDetourLane(source, target, obstacles);
|
|
4097
|
+
return [
|
|
4098
|
+
{ ...source },
|
|
4099
|
+
{ x: detourX, y: source.y },
|
|
4100
|
+
{ x: detourX, y: target.y },
|
|
4101
|
+
{ ...target }
|
|
4102
|
+
];
|
|
4103
|
+
}
|
|
4104
|
+
function pathLength(points) {
|
|
4105
|
+
let len = 0;
|
|
4106
|
+
for (let i = 1; i < points.length; i += 1) {
|
|
4107
|
+
const a = points[i - 1];
|
|
4108
|
+
const b = points[i];
|
|
4109
|
+
if (a !== void 0 && b !== void 0) {
|
|
4110
|
+
len += Math.hypot(b.x - a.x, b.y - a.y);
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
return len;
|
|
4114
|
+
}
|
|
3654
4115
|
function endpointObstaclesForAutoAnchors(input) {
|
|
3655
4116
|
const boxes = [];
|
|
3656
4117
|
if (input.sourceAnchor === void 0 && hasDistinctAnchors(input.source)) {
|
|
@@ -4022,6 +4483,15 @@ var DEFAULT_PANEL_WIDTH = 320;
|
|
|
4022
4483
|
var DEFAULT_PANEL_ITEM_HEIGHT2 = 28;
|
|
4023
4484
|
var DEFAULT_EVIDENCE_BLOCK_GAP = 24;
|
|
4024
4485
|
var EDGE_LABEL_CLEARANCE = 8;
|
|
4486
|
+
var DEFAULT_CJK_FONT_FAMILY = "YaHei,SimSun,sans-serif";
|
|
4487
|
+
var DEFAULT_MIN_CJK_FONT_SIZE = 14;
|
|
4488
|
+
function prefitLabelFont(node, _options) {
|
|
4489
|
+
const cjk = labelCjkTypography(node.label?.metadata);
|
|
4490
|
+
const fontFamily = cjk.fontFamily ?? DEFAULT_FONT.fontFamily;
|
|
4491
|
+
const fontSize = cjk.fontSize ?? DEFAULT_FONT.fontSize;
|
|
4492
|
+
const lineHeight = fontSize !== DEFAULT_FONT.fontSize ? Math.max(DEFAULT_FONT.lineHeight ?? 18, fontSize * 1.2) : DEFAULT_FONT.lineHeight ?? 18;
|
|
4493
|
+
return { fontFamily, fontSize, lineHeight };
|
|
4494
|
+
}
|
|
4025
4495
|
var EVIDENCE_TEXT_FONT = {
|
|
4026
4496
|
fontFamily: "Arial, sans-serif",
|
|
4027
4497
|
fontSize: 10,
|
|
@@ -4029,43 +4499,85 @@ var EVIDENCE_TEXT_FONT = {
|
|
|
4029
4499
|
};
|
|
4030
4500
|
function solveDiagram(diagram, options = {}) {
|
|
4031
4501
|
const diagnostics = [...diagram.diagnostics];
|
|
4032
|
-
const nodes =
|
|
4033
|
-
|
|
4034
|
-
|
|
4502
|
+
const nodes = stableUniqueById(
|
|
4503
|
+
diagram.nodes,
|
|
4504
|
+
diagnostics,
|
|
4505
|
+
"nodes",
|
|
4506
|
+
"duplicate_node_id"
|
|
4507
|
+
);
|
|
4508
|
+
const edges = stableUniqueById(
|
|
4509
|
+
diagram.edges,
|
|
4510
|
+
diagnostics,
|
|
4511
|
+
"edges",
|
|
4512
|
+
"duplicate_edge_id"
|
|
4513
|
+
);
|
|
4514
|
+
const groups = stableUniqueById(
|
|
4515
|
+
diagram.groups,
|
|
4516
|
+
diagnostics,
|
|
4517
|
+
"groups",
|
|
4518
|
+
"duplicate_group_id"
|
|
4519
|
+
);
|
|
4520
|
+
const cjkTypography = createCjkTypographyOptions(options);
|
|
4521
|
+
const cjkStyledNodes = nodes.map(
|
|
4522
|
+
(node) => enhanceNodeCjkTypography(node, cjkTypography, diagnostics)
|
|
4523
|
+
);
|
|
4524
|
+
const styledNodes = options.prefitLabelSize === true ? cjkStyledNodes.map(
|
|
4525
|
+
(node) => prefitNodeLabelSize(node, options, diagnostics)
|
|
4526
|
+
) : cjkStyledNodes;
|
|
4527
|
+
const styledEdges = edges.map(
|
|
4528
|
+
(edge) => enhanceEdgeCjkTypography(edge, cjkTypography, diagnostics)
|
|
4529
|
+
);
|
|
4530
|
+
const styledGroups = groups.map(
|
|
4531
|
+
(group) => enhanceGroupCjkTypography(group, cjkTypography, diagnostics)
|
|
4532
|
+
);
|
|
4533
|
+
const styledSwimlanes = (diagram.swimlanes ?? []).map(
|
|
4534
|
+
(swimlane) => enhanceSwimlaneCjkTypography(swimlane, cjkTypography, diagnostics)
|
|
4535
|
+
);
|
|
4035
4536
|
const constraints = stableByConstraintId(diagram.constraints);
|
|
4036
4537
|
const layout2 = runDagreInitialLayout({
|
|
4037
4538
|
direction: diagram.direction,
|
|
4038
|
-
nodes:
|
|
4039
|
-
edges:
|
|
4539
|
+
nodes: styledNodes.map((node) => ({ id: node.id, size: node.size })),
|
|
4540
|
+
edges: styledEdges.map((edge) => ({
|
|
4040
4541
|
id: edge.id,
|
|
4041
4542
|
sourceId: edge.source.nodeId,
|
|
4042
4543
|
targetId: edge.target.nodeId
|
|
4043
4544
|
}))
|
|
4044
4545
|
});
|
|
4045
4546
|
diagnostics.push(...layout2.diagnostics);
|
|
4547
|
+
const initialNodeBoxes = wrapVerticalStackIfNeeded(
|
|
4548
|
+
layout2.boxes,
|
|
4549
|
+
styledNodes,
|
|
4550
|
+
styledEdges,
|
|
4551
|
+
diagram.direction,
|
|
4552
|
+
options,
|
|
4553
|
+
diagnostics
|
|
4554
|
+
);
|
|
4046
4555
|
const constrained = applyLayoutConstraints({
|
|
4047
4556
|
direction: diagram.direction,
|
|
4048
4557
|
overlapSpacing: options?.overlapSpacing ?? 40,
|
|
4049
|
-
|
|
4050
|
-
|
|
4558
|
+
...options.minSiblingGap === void 0 ? {} : { minSiblingGap: options.minSiblingGap },
|
|
4559
|
+
...options.distributeContainedChildren === void 0 ? {} : { distributeContainedChildren: options.distributeContainedChildren },
|
|
4560
|
+
boxes: initialNodeBoxes,
|
|
4561
|
+
nodes: styledNodes,
|
|
4051
4562
|
constraints
|
|
4052
4563
|
});
|
|
4053
4564
|
diagnostics.push(...constrained.diagnostics);
|
|
4054
4565
|
const swimlaneContracts = applySwimlaneLayoutContracts(
|
|
4055
|
-
|
|
4566
|
+
styledSwimlanes,
|
|
4056
4567
|
constraints,
|
|
4057
|
-
|
|
4568
|
+
styledEdges,
|
|
4058
4569
|
isTopToBottomReadingDirection(diagram.metadata?.primaryReadingDirection),
|
|
4059
4570
|
constrained.boxes,
|
|
4060
4571
|
constrained.locks,
|
|
4061
|
-
options?.overlapSpacing ?? 40
|
|
4572
|
+
options?.overlapSpacing ?? 40,
|
|
4573
|
+
Math.max(0, options?.minLaneGutter ?? 0)
|
|
4062
4574
|
);
|
|
4063
4575
|
if (swimlaneContracts.layouts.size > 0) {
|
|
4064
4576
|
removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
|
|
4065
4577
|
}
|
|
4066
4578
|
diagnostics.push(...swimlaneContracts.diagnostics);
|
|
4067
4579
|
const coordinatedNodes = coordinateNodes(
|
|
4068
|
-
|
|
4580
|
+
styledNodes,
|
|
4069
4581
|
constrained.boxes,
|
|
4070
4582
|
options,
|
|
4071
4583
|
diagnostics
|
|
@@ -4081,13 +4593,13 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4081
4593
|
])
|
|
4082
4594
|
);
|
|
4083
4595
|
const coordinatedGroups = coordinateGroups(
|
|
4084
|
-
|
|
4596
|
+
styledGroups,
|
|
4085
4597
|
constrained.boxes,
|
|
4086
4598
|
options,
|
|
4087
4599
|
diagnostics
|
|
4088
4600
|
);
|
|
4089
4601
|
const coordinatedSwimlanes = coordinateSwimlanes(
|
|
4090
|
-
|
|
4602
|
+
styledSwimlanes,
|
|
4091
4603
|
constrained.boxes,
|
|
4092
4604
|
swimlaneContracts.layouts
|
|
4093
4605
|
);
|
|
@@ -4187,7 +4699,7 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4187
4699
|
...frameTextAnnotation.filter(isPreRouteTextObstacle)
|
|
4188
4700
|
];
|
|
4189
4701
|
const coordinatedEdges = coordinateEdges(
|
|
4190
|
-
|
|
4702
|
+
styledEdges,
|
|
4191
4703
|
nodeGeometryById,
|
|
4192
4704
|
coordinatedNodes,
|
|
4193
4705
|
[...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
|
|
@@ -4203,6 +4715,11 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4203
4715
|
);
|
|
4204
4716
|
const edgeTextAnnotations = coordinateEdgeTextAnnotations(
|
|
4205
4717
|
coordinatedEdges,
|
|
4718
|
+
[
|
|
4719
|
+
...coordinatedNodes.map((node) => node.box),
|
|
4720
|
+
...baseTextAnnotations.map((annotation) => annotation.box),
|
|
4721
|
+
...frameTextAnnotation.map((annotation) => annotation.box)
|
|
4722
|
+
],
|
|
4206
4723
|
options.textMeasurer
|
|
4207
4724
|
);
|
|
4208
4725
|
const textAnnotations = [
|
|
@@ -4220,6 +4737,12 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4220
4737
|
...edgePointBounds,
|
|
4221
4738
|
...edgeTextAnnotations.map((annotation) => annotation.box)
|
|
4222
4739
|
];
|
|
4740
|
+
diagnostics.push(
|
|
4741
|
+
...reportPageOverflow(
|
|
4742
|
+
frame === void 0 ? unionBoxes(boundsBase) : unionBoxes([...boundsBase, frame.box, frame.titleBox]),
|
|
4743
|
+
options.pageBounds
|
|
4744
|
+
)
|
|
4745
|
+
);
|
|
4223
4746
|
return {
|
|
4224
4747
|
id: diagram.id,
|
|
4225
4748
|
...diagram.title === void 0 ? {} : { title: diagram.title },
|
|
@@ -4238,7 +4761,303 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4238
4761
|
...diagram.metadata === void 0 ? {} : { metadata: diagram.metadata }
|
|
4239
4762
|
};
|
|
4240
4763
|
}
|
|
4241
|
-
function
|
|
4764
|
+
function solveDiagramSafe(diagram, options = {}) {
|
|
4765
|
+
return solveDiagram(diagram, { ...options, prefitLabelSize: true });
|
|
4766
|
+
}
|
|
4767
|
+
function prefitNodeLabelSize(node, options, diagnostics) {
|
|
4768
|
+
if (node.label === void 0) {
|
|
4769
|
+
return node;
|
|
4770
|
+
}
|
|
4771
|
+
const measurer = options.textMeasurer ?? createDefaultTextMeasurer();
|
|
4772
|
+
const layout2 = fitLabel(
|
|
4773
|
+
node.label.text,
|
|
4774
|
+
{
|
|
4775
|
+
font: prefitLabelFont(node),
|
|
4776
|
+
padding: DEFAULT_NODE_PADDING,
|
|
4777
|
+
minSize: DEFAULT_NODE_MIN_SIZE,
|
|
4778
|
+
maxWidth: node.label.maxWidth ?? Math.max(node.size.width, DEFAULT_LABEL_MAX_WIDTH)
|
|
4779
|
+
},
|
|
4780
|
+
measurer
|
|
4781
|
+
);
|
|
4782
|
+
const width = Math.max(node.size.width, layout2.fittedSize.width);
|
|
4783
|
+
const height = Math.max(node.size.height, layout2.fittedSize.height);
|
|
4784
|
+
const resized = width !== node.size.width || height !== node.size.height;
|
|
4785
|
+
if (resized) {
|
|
4786
|
+
diagnostics.push({
|
|
4787
|
+
severity: "info",
|
|
4788
|
+
code: "prefit_label_resized",
|
|
4789
|
+
message: `Node ${node.id} size expanded to fit its label.`,
|
|
4790
|
+
path: ["nodes", node.id],
|
|
4791
|
+
detail: {
|
|
4792
|
+
nodeId: node.id,
|
|
4793
|
+
from: { width: node.size.width, height: node.size.height },
|
|
4794
|
+
to: { width, height }
|
|
4795
|
+
}
|
|
4796
|
+
});
|
|
4797
|
+
}
|
|
4798
|
+
const centeredLayout = expandLabelLayoutToNode(layout2, { width, height });
|
|
4799
|
+
return { ...node, size: { width, height }, labelLayout: centeredLayout };
|
|
4800
|
+
}
|
|
4801
|
+
function expandLabelLayoutToNode(layout2, nodeSize) {
|
|
4802
|
+
if (layout2.box.width >= nodeSize.width && layout2.box.height >= nodeSize.height) {
|
|
4803
|
+
return layout2;
|
|
4804
|
+
}
|
|
4805
|
+
const offsetX = Math.max(0, (nodeSize.width - layout2.box.width) / 2);
|
|
4806
|
+
const offsetY = Math.max(0, (nodeSize.height - layout2.box.height) / 2);
|
|
4807
|
+
if (offsetX === 0 && offsetY === 0) {
|
|
4808
|
+
return layout2;
|
|
4809
|
+
}
|
|
4810
|
+
return {
|
|
4811
|
+
...layout2,
|
|
4812
|
+
box: {
|
|
4813
|
+
x: layout2.box.x + offsetX,
|
|
4814
|
+
y: layout2.box.y + offsetY,
|
|
4815
|
+
width: layout2.box.width,
|
|
4816
|
+
height: layout2.box.height
|
|
4817
|
+
},
|
|
4818
|
+
contentBox: {
|
|
4819
|
+
x: layout2.contentBox.x + offsetX,
|
|
4820
|
+
y: layout2.contentBox.y + offsetY,
|
|
4821
|
+
width: layout2.contentBox.width,
|
|
4822
|
+
height: layout2.contentBox.height
|
|
4823
|
+
},
|
|
4824
|
+
lines: layout2.lines.map((line) => ({
|
|
4825
|
+
...line,
|
|
4826
|
+
box: {
|
|
4827
|
+
x: line.box.x + offsetX,
|
|
4828
|
+
y: line.box.y + offsetY,
|
|
4829
|
+
width: line.box.width,
|
|
4830
|
+
height: line.box.height
|
|
4831
|
+
}
|
|
4832
|
+
}))
|
|
4833
|
+
};
|
|
4834
|
+
}
|
|
4835
|
+
function reportPageOverflow(contentBounds, pageBounds) {
|
|
4836
|
+
if (pageBounds === void 0) {
|
|
4837
|
+
return [];
|
|
4838
|
+
}
|
|
4839
|
+
const overflowRight = Math.max(
|
|
4840
|
+
0,
|
|
4841
|
+
contentBounds.x + contentBounds.width - pageBounds.width
|
|
4842
|
+
);
|
|
4843
|
+
const overflowBottom = Math.max(
|
|
4844
|
+
0,
|
|
4845
|
+
contentBounds.y + contentBounds.height - pageBounds.height
|
|
4846
|
+
);
|
|
4847
|
+
const overflowLeft = Math.max(0, -contentBounds.x);
|
|
4848
|
+
const overflowTop = Math.max(0, -contentBounds.y);
|
|
4849
|
+
if (overflowRight === 0 && overflowBottom === 0 && overflowLeft === 0 && overflowTop === 0) {
|
|
4850
|
+
return [];
|
|
4851
|
+
}
|
|
4852
|
+
return [
|
|
4853
|
+
{
|
|
4854
|
+
severity: "warning",
|
|
4855
|
+
code: "page_overflow",
|
|
4856
|
+
message: `Content ${contentBounds.width}x${contentBounds.height} exceeds page ${pageBounds.width}x${pageBounds.height}.`,
|
|
4857
|
+
path: ["bounds"],
|
|
4858
|
+
detail: {
|
|
4859
|
+
page: { width: pageBounds.width, height: pageBounds.height },
|
|
4860
|
+
content: {
|
|
4861
|
+
width: contentBounds.width,
|
|
4862
|
+
height: contentBounds.height
|
|
4863
|
+
},
|
|
4864
|
+
overflow: {
|
|
4865
|
+
right: overflowRight,
|
|
4866
|
+
bottom: overflowBottom,
|
|
4867
|
+
left: overflowLeft,
|
|
4868
|
+
top: overflowTop
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
];
|
|
4873
|
+
}
|
|
4874
|
+
function createCjkTypographyOptions(options) {
|
|
4875
|
+
const fontFamily = options.cjkFontFamily === false ? void 0 : options.cjkFontFamily ?? DEFAULT_CJK_FONT_FAMILY;
|
|
4876
|
+
const minFontSize = options.minCjkFontSize === false ? void 0 : options.minCjkFontSize ?? DEFAULT_MIN_CJK_FONT_SIZE;
|
|
4877
|
+
return {
|
|
4878
|
+
...fontFamily === void 0 ? {} : { fontFamily },
|
|
4879
|
+
...minFontSize === void 0 ? {} : { minFontSize }
|
|
4880
|
+
};
|
|
4881
|
+
}
|
|
4882
|
+
function enhanceNodeCjkTypography(node, options, diagnostics) {
|
|
4883
|
+
const nodeWithStyle = enhanceStyledLabelOwner(
|
|
4884
|
+
node,
|
|
4885
|
+
["nodes", node.id],
|
|
4886
|
+
options,
|
|
4887
|
+
diagnostics
|
|
4888
|
+
);
|
|
4889
|
+
const ports = nodeWithStyle.ports === void 0 ? void 0 : nodeWithStyle.ports.map(
|
|
4890
|
+
(port) => enhanceStyledLabelOwner(
|
|
4891
|
+
port,
|
|
4892
|
+
["nodes", node.id, "ports", port.id],
|
|
4893
|
+
options,
|
|
4894
|
+
diagnostics
|
|
4895
|
+
)
|
|
4896
|
+
);
|
|
4897
|
+
return ports === void 0 ? nodeWithStyle : { ...nodeWithStyle, ports };
|
|
4898
|
+
}
|
|
4899
|
+
function enhanceEdgeCjkTypography(edge, options, diagnostics) {
|
|
4900
|
+
return enhanceStyledLabelOwner(
|
|
4901
|
+
edge,
|
|
4902
|
+
["edges", edge.id],
|
|
4903
|
+
options,
|
|
4904
|
+
diagnostics
|
|
4905
|
+
);
|
|
4906
|
+
}
|
|
4907
|
+
function enhanceGroupCjkTypography(group, options, diagnostics) {
|
|
4908
|
+
return enhanceStyledLabelOwner(
|
|
4909
|
+
group,
|
|
4910
|
+
["groups", group.id],
|
|
4911
|
+
options,
|
|
4912
|
+
diagnostics
|
|
4913
|
+
);
|
|
4914
|
+
}
|
|
4915
|
+
function enhanceSwimlaneCjkTypography(swimlane, options, diagnostics) {
|
|
4916
|
+
const root = enhanceStyledLabelOwner(
|
|
4917
|
+
swimlane,
|
|
4918
|
+
["swimlanes", swimlane.id],
|
|
4919
|
+
options,
|
|
4920
|
+
diagnostics
|
|
4921
|
+
);
|
|
4922
|
+
const lanes = root.lanes.map(
|
|
4923
|
+
(lane) => enhanceSwimlaneLaneCjkTypography(swimlane.id, lane, options, diagnostics)
|
|
4924
|
+
);
|
|
4925
|
+
return { ...root, lanes };
|
|
4926
|
+
}
|
|
4927
|
+
function enhanceSwimlaneLaneCjkTypography(swimlaneId, lane, options, diagnostics) {
|
|
4928
|
+
return enhanceStyledLabelOwner(
|
|
4929
|
+
lane,
|
|
4930
|
+
["swimlanes", swimlaneId, "lanes", lane.id],
|
|
4931
|
+
options,
|
|
4932
|
+
diagnostics
|
|
4933
|
+
);
|
|
4934
|
+
}
|
|
4935
|
+
function enhanceStyledLabelOwner(owner, path, options, diagnostics) {
|
|
4936
|
+
const text = owner.label?.text;
|
|
4937
|
+
if (text === void 0 || !containsCjk(text)) {
|
|
4938
|
+
return owner;
|
|
4939
|
+
}
|
|
4940
|
+
const typography = cjkTypographyForOwner(owner, options);
|
|
4941
|
+
if (typography.fontFamily === void 0 && typography.fontSize === void 0) {
|
|
4942
|
+
return owner;
|
|
4943
|
+
}
|
|
4944
|
+
const label = owner.label;
|
|
4945
|
+
if (label === void 0) {
|
|
4946
|
+
return owner;
|
|
4947
|
+
}
|
|
4948
|
+
const nextLabel = {
|
|
4949
|
+
...label,
|
|
4950
|
+
metadata: {
|
|
4951
|
+
...metadataObject(label.metadata),
|
|
4952
|
+
cjkTypography: typography
|
|
4953
|
+
}
|
|
4954
|
+
};
|
|
4955
|
+
const nextOwner = { ...owner, label: nextLabel };
|
|
4956
|
+
const maybeStyled = nextOwner;
|
|
4957
|
+
const nextStyle = enhanceCjkStyle(maybeStyled.style, typography);
|
|
4958
|
+
reportCjkTypographyDiagnostics(
|
|
4959
|
+
path,
|
|
4960
|
+
typography,
|
|
4961
|
+
maybeStyled.style,
|
|
4962
|
+
diagnostics
|
|
4963
|
+
);
|
|
4964
|
+
return nextStyle === maybeStyled.style ? nextOwner : { ...nextOwner, style: nextStyle };
|
|
4965
|
+
}
|
|
4966
|
+
function cjkTypographyForOwner(owner, options) {
|
|
4967
|
+
const metadataTypography = labelCjkTypography(owner.label?.metadata);
|
|
4968
|
+
const fontFamily = metadataTypography.fontFamily ?? owner.style?.fontFamily ?? options.fontFamily;
|
|
4969
|
+
const fontSize = boostedCjkFontSize(
|
|
4970
|
+
metadataTypography.fontSize ?? owner.style?.fontSize,
|
|
4971
|
+
options.minFontSize
|
|
4972
|
+
);
|
|
4973
|
+
return {
|
|
4974
|
+
...fontFamily === void 0 ? {} : { fontFamily },
|
|
4975
|
+
...fontSize === void 0 ? {} : { fontSize }
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
function labelCjkTypography(metadata) {
|
|
4979
|
+
const metadataRecord = metadataObject(metadata);
|
|
4980
|
+
if (metadataRecord === void 0) {
|
|
4981
|
+
return {};
|
|
4982
|
+
}
|
|
4983
|
+
const value = metadataRecord.cjkTypography;
|
|
4984
|
+
if (value === void 0 || value === null || typeof value !== "object") {
|
|
4985
|
+
return {};
|
|
4986
|
+
}
|
|
4987
|
+
const typography = value;
|
|
4988
|
+
const fontFamily = typeof typography.fontFamily === "string" ? typography.fontFamily : void 0;
|
|
4989
|
+
const fontSize = typeof typography.fontSize === "number" && Number.isFinite(typography.fontSize) && typography.fontSize > 0 ? typography.fontSize : void 0;
|
|
4990
|
+
return {
|
|
4991
|
+
...fontFamily === void 0 ? {} : { fontFamily },
|
|
4992
|
+
...fontSize === void 0 ? {} : { fontSize }
|
|
4993
|
+
};
|
|
4994
|
+
}
|
|
4995
|
+
function metadataObject(metadata) {
|
|
4996
|
+
if (metadata === void 0 || metadata === null || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
4997
|
+
return void 0;
|
|
4998
|
+
}
|
|
4999
|
+
return metadata;
|
|
5000
|
+
}
|
|
5001
|
+
function typographyForLabel(label) {
|
|
5002
|
+
return labelCjkTypography(label?.metadata);
|
|
5003
|
+
}
|
|
5004
|
+
function typographyTextStyle(label, base) {
|
|
5005
|
+
const typography = typographyForLabel(label);
|
|
5006
|
+
return {
|
|
5007
|
+
...base,
|
|
5008
|
+
...typography.fontFamily === void 0 ? {} : { fontFamily: typography.fontFamily },
|
|
5009
|
+
...typography.fontSize === void 0 ? {} : {
|
|
5010
|
+
fontSize: typography.fontSize,
|
|
5011
|
+
lineHeight: Math.max(base.lineHeight ?? 0, typography.fontSize * 1.2)
|
|
5012
|
+
}
|
|
5013
|
+
};
|
|
5014
|
+
}
|
|
5015
|
+
function boostedCjkFontSize(current, minFontSize) {
|
|
5016
|
+
if (minFontSize === void 0) {
|
|
5017
|
+
return current;
|
|
5018
|
+
}
|
|
5019
|
+
if (current === void 0 || current < minFontSize) {
|
|
5020
|
+
return minFontSize;
|
|
5021
|
+
}
|
|
5022
|
+
return current;
|
|
5023
|
+
}
|
|
5024
|
+
function enhanceCjkStyle(style2, typography) {
|
|
5025
|
+
let next = style2;
|
|
5026
|
+
if (typography.fontFamily !== void 0 && next?.fontFamily === void 0) {
|
|
5027
|
+
next = { ...next, fontFamily: typography.fontFamily };
|
|
5028
|
+
}
|
|
5029
|
+
if (typography.fontSize !== void 0 && (next?.fontSize === void 0 || next.fontSize < typography.fontSize)) {
|
|
5030
|
+
next = { ...next, fontSize: typography.fontSize };
|
|
5031
|
+
}
|
|
5032
|
+
return next;
|
|
5033
|
+
}
|
|
5034
|
+
function reportCjkTypographyDiagnostics(path, typography, previousStyle, diagnostics) {
|
|
5035
|
+
if (typography.fontFamily !== void 0 && previousStyle?.fontFamily === void 0) {
|
|
5036
|
+
diagnostics.push({
|
|
5037
|
+
severity: "info",
|
|
5038
|
+
code: "cjk_font_family_applied",
|
|
5039
|
+
message: `Applied CJK font family ${typography.fontFamily}.`,
|
|
5040
|
+
path: [...path, "label", "metadata", "cjkTypography", "fontFamily"],
|
|
5041
|
+
detail: { fontFamily: typography.fontFamily }
|
|
5042
|
+
});
|
|
5043
|
+
}
|
|
5044
|
+
if (typography.fontSize !== void 0 && (previousStyle?.fontSize === void 0 || previousStyle.fontSize < typography.fontSize)) {
|
|
5045
|
+
diagnostics.push({
|
|
5046
|
+
severity: "info",
|
|
5047
|
+
code: "cjk_font_size_boosted",
|
|
5048
|
+
message: `Raised CJK font size to ${typography.fontSize}.`,
|
|
5049
|
+
path: [...path, "label", "metadata", "cjkTypography", "fontSize"],
|
|
5050
|
+
detail: {
|
|
5051
|
+
minFontSize: typography.fontSize,
|
|
5052
|
+
...previousStyle?.fontSize === void 0 ? {} : { previousFontSize: previousStyle.fontSize }
|
|
5053
|
+
}
|
|
5054
|
+
});
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
function containsCjk(value) {
|
|
5058
|
+
return /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u.test(value);
|
|
5059
|
+
}
|
|
5060
|
+
function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottomFlow, nodeBoxes, locks, overlapSpacing, laneGutter) {
|
|
4242
5061
|
const layouts = /* @__PURE__ */ new Map();
|
|
4243
5062
|
const diagnostics = [];
|
|
4244
5063
|
const movedChildIds = /* @__PURE__ */ new Set();
|
|
@@ -4256,7 +5075,8 @@ function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottom
|
|
|
4256
5075
|
nodeBoxes,
|
|
4257
5076
|
locks,
|
|
4258
5077
|
diagnostics,
|
|
4259
|
-
movedChildIds
|
|
5078
|
+
movedChildIds,
|
|
5079
|
+
laneGutter
|
|
4260
5080
|
);
|
|
4261
5081
|
if (layout2 !== void 0) {
|
|
4262
5082
|
layouts.set(swimlane.id, layout2);
|
|
@@ -4271,10 +5091,123 @@ function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottom
|
|
|
4271
5091
|
movedChildIds
|
|
4272
5092
|
)
|
|
4273
5093
|
);
|
|
5094
|
+
if (laneGutter > 0) {
|
|
5095
|
+
diagnostics.push({
|
|
5096
|
+
severity: "info",
|
|
5097
|
+
code: "lane_gutter_applied",
|
|
5098
|
+
message: `Applied ${laneGutter}px gutter between ${layouts.size} contract swimlane lane(s).`,
|
|
5099
|
+
path: ["swimlanes"],
|
|
5100
|
+
detail: { laneGutter, swimlaneCount: layouts.size }
|
|
5101
|
+
});
|
|
5102
|
+
}
|
|
4274
5103
|
}
|
|
4275
5104
|
return { layouts, diagnostics, movedChildIds };
|
|
4276
5105
|
}
|
|
4277
|
-
function
|
|
5106
|
+
function wrapVerticalStackIfNeeded(boxes, nodes, edges, direction, options, diagnostics) {
|
|
5107
|
+
const wrapped = new Map([...boxes].map(([id, box]) => [id, { ...box }]));
|
|
5108
|
+
const maxStackDepth = options.maxStackDepth;
|
|
5109
|
+
if (maxStackDepth === void 0 || maxStackDepth <= 0 || nodes.length <= maxStackDepth) {
|
|
5110
|
+
reportVerticalRunaway(
|
|
5111
|
+
wrapped,
|
|
5112
|
+
nodes,
|
|
5113
|
+
edges,
|
|
5114
|
+
direction,
|
|
5115
|
+
options,
|
|
5116
|
+
diagnostics
|
|
5117
|
+
);
|
|
5118
|
+
return wrapped;
|
|
5119
|
+
}
|
|
5120
|
+
if (edges.length > 0 || !isVerticalRunaway(wrapped, nodes, direction, options)) {
|
|
5121
|
+
reportVerticalRunaway(
|
|
5122
|
+
wrapped,
|
|
5123
|
+
nodes,
|
|
5124
|
+
edges,
|
|
5125
|
+
direction,
|
|
5126
|
+
options,
|
|
5127
|
+
diagnostics
|
|
5128
|
+
);
|
|
5129
|
+
return wrapped;
|
|
5130
|
+
}
|
|
5131
|
+
const ordered = nodes.map((node) => ({ node, box: wrapped.get(node.id) })).filter(
|
|
5132
|
+
(item) => item.box !== void 0
|
|
5133
|
+
).sort((a, b) => {
|
|
5134
|
+
const delta = a.box.y - b.box.y;
|
|
5135
|
+
return delta === 0 ? a.node.id.localeCompare(b.node.id) : delta;
|
|
5136
|
+
});
|
|
5137
|
+
const columns = Math.ceil(ordered.length / maxStackDepth);
|
|
5138
|
+
const horizontalGap = options.overlapSpacing ?? 40;
|
|
5139
|
+
const verticalGap = Math.max(24, horizontalGap / 2);
|
|
5140
|
+
const columnWidths = Array.from(
|
|
5141
|
+
{ length: columns },
|
|
5142
|
+
(_, column) => Math.max(
|
|
5143
|
+
0,
|
|
5144
|
+
...ordered.slice(column * maxStackDepth, (column + 1) * maxStackDepth).map((item) => item.box.width)
|
|
5145
|
+
)
|
|
5146
|
+
);
|
|
5147
|
+
const startX = Math.min(...ordered.map((item) => item.box.x));
|
|
5148
|
+
const startY = Math.min(...ordered.map((item) => item.box.y));
|
|
5149
|
+
let columnX = startX;
|
|
5150
|
+
for (let column = 0; column < columns; column += 1) {
|
|
5151
|
+
let y = startY;
|
|
5152
|
+
const items = ordered.slice(
|
|
5153
|
+
column * maxStackDepth,
|
|
5154
|
+
(column + 1) * maxStackDepth
|
|
5155
|
+
);
|
|
5156
|
+
for (const item of items) {
|
|
5157
|
+
wrapped.set(item.node.id, { ...item.box, x: columnX, y });
|
|
5158
|
+
y += item.box.height + verticalGap;
|
|
5159
|
+
}
|
|
5160
|
+
columnX += (columnWidths[column] ?? 0) + horizontalGap;
|
|
5161
|
+
}
|
|
5162
|
+
diagnostics.push({
|
|
5163
|
+
severity: "warning",
|
|
5164
|
+
code: "vertical_runaway",
|
|
5165
|
+
message: `Single-column layout exceeded maxStackDepth ${maxStackDepth}; wrapped into ${columns} columns.`,
|
|
5166
|
+
path: ["nodes"],
|
|
5167
|
+
detail: { nodeCount: ordered.length, maxStackDepth, columns }
|
|
5168
|
+
});
|
|
5169
|
+
return wrapped;
|
|
5170
|
+
}
|
|
5171
|
+
function reportVerticalRunaway(boxes, nodes, edges, direction, options, diagnostics) {
|
|
5172
|
+
if (!isVerticalRunaway(boxes, nodes, direction, options)) {
|
|
5173
|
+
return;
|
|
5174
|
+
}
|
|
5175
|
+
diagnostics.push({
|
|
5176
|
+
severity: "warning",
|
|
5177
|
+
code: "vertical_runaway",
|
|
5178
|
+
message: "Layout produced a tall vertical stack beyond the preferred aspect ratio.",
|
|
5179
|
+
path: ["nodes"],
|
|
5180
|
+
detail: {
|
|
5181
|
+
nodeCount: nodes.length,
|
|
5182
|
+
edgeCount: edges.length,
|
|
5183
|
+
...options.preferredAspectRatio === void 0 ? {} : { preferredAspectRatio: options.preferredAspectRatio },
|
|
5184
|
+
...options.maxStackDepth === void 0 ? {} : { maxStackDepth: options.maxStackDepth }
|
|
5185
|
+
}
|
|
5186
|
+
});
|
|
5187
|
+
}
|
|
5188
|
+
function isVerticalRunaway(boxes, nodes, direction, options) {
|
|
5189
|
+
if (options.maxStackDepth === void 0 && options.preferredAspectRatio === void 0) {
|
|
5190
|
+
return false;
|
|
5191
|
+
}
|
|
5192
|
+
if (nodes.length < 2 || direction !== "LR" && direction !== "RL") {
|
|
5193
|
+
return false;
|
|
5194
|
+
}
|
|
5195
|
+
const nodeBoxes = nodes.map((node) => boxes.get(node.id)).filter((box) => box !== void 0);
|
|
5196
|
+
if (nodeBoxes.length < 2) {
|
|
5197
|
+
return false;
|
|
5198
|
+
}
|
|
5199
|
+
const bounds = unionBoxes(nodeBoxes);
|
|
5200
|
+
const aspectRatio = bounds.width <= 0 ? Number.POSITIVE_INFINITY : bounds.height / bounds.width;
|
|
5201
|
+
const preferred = options.preferredAspectRatio ?? 3;
|
|
5202
|
+
if (aspectRatio < preferred) {
|
|
5203
|
+
return false;
|
|
5204
|
+
}
|
|
5205
|
+
const xCenters = nodeBoxes.map((box) => box.x + box.width / 2);
|
|
5206
|
+
const xSpread = Math.max(...xCenters) - Math.min(...xCenters);
|
|
5207
|
+
const maxWidth = Math.max(...nodeBoxes.map((box) => box.width));
|
|
5208
|
+
return xSpread <= Math.max(maxWidth, options.overlapSpacing ?? 40);
|
|
5209
|
+
}
|
|
5210
|
+
function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, locks, diagnostics, movedChildIds, laneGutter) {
|
|
4278
5211
|
const headerHeight = swimlane.headerHeight ?? 28;
|
|
4279
5212
|
const padding = swimlane.padding ?? 16;
|
|
4280
5213
|
const laneBounds = swimlane.lanes.map((lane) => {
|
|
@@ -4298,7 +5231,8 @@ function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes
|
|
|
4298
5231
|
padding,
|
|
4299
5232
|
locks,
|
|
4300
5233
|
diagnostics,
|
|
4301
|
-
movedChildIds
|
|
5234
|
+
movedChildIds,
|
|
5235
|
+
laneGutter
|
|
4302
5236
|
);
|
|
4303
5237
|
}
|
|
4304
5238
|
return applyHorizontalSwimlaneContract(
|
|
@@ -4309,10 +5243,11 @@ function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes
|
|
|
4309
5243
|
padding,
|
|
4310
5244
|
locks,
|
|
4311
5245
|
diagnostics,
|
|
4312
|
-
movedChildIds
|
|
5246
|
+
movedChildIds,
|
|
5247
|
+
laneGutter
|
|
4313
5248
|
);
|
|
4314
5249
|
}
|
|
4315
|
-
function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds) {
|
|
5250
|
+
function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds, laneGutter) {
|
|
4316
5251
|
const populatedBounds = laneBounds.filter(
|
|
4317
5252
|
(box) => box !== void 0
|
|
4318
5253
|
);
|
|
@@ -4331,6 +5266,7 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
4331
5266
|
const rankSpacing = Math.max(96, maxRankStackHeight + padding);
|
|
4332
5267
|
const contentHeight = maxRank === 0 ? maxChildHeight : maxRankStackHeight + maxRank * rankSpacing;
|
|
4333
5268
|
const slotWidth = Math.max(...populatedBounds.map((box) => box.width)) + padding * 2;
|
|
5269
|
+
const laneStep = slotWidth + laneGutter;
|
|
4334
5270
|
const laneContentTop = top + headerHeight + padding;
|
|
4335
5271
|
for (let index = 0; index < swimlane.lanes.length; index += 1) {
|
|
4336
5272
|
const lane = swimlane.lanes[index];
|
|
@@ -4339,7 +5275,7 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
4339
5275
|
continue;
|
|
4340
5276
|
}
|
|
4341
5277
|
const target = {
|
|
4342
|
-
x: left +
|
|
5278
|
+
x: left + laneStep * index + padding,
|
|
4343
5279
|
y: laneContentTop
|
|
4344
5280
|
};
|
|
4345
5281
|
if (maxRank === 0) {
|
|
@@ -4375,11 +5311,12 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
4375
5311
|
box: {
|
|
4376
5312
|
x: left,
|
|
4377
5313
|
y: top,
|
|
4378
|
-
width:
|
|
5314
|
+
width: laneStep * (swimlane.lanes.length - 1) + slotWidth,
|
|
4379
5315
|
height: contentHeight + padding * 2 + headerHeight
|
|
4380
5316
|
},
|
|
4381
5317
|
slotWidth,
|
|
4382
|
-
slotHeight: contentHeight + padding * 2 + headerHeight
|
|
5318
|
+
slotHeight: contentHeight + padding * 2 + headerHeight,
|
|
5319
|
+
laneStep
|
|
4383
5320
|
};
|
|
4384
5321
|
}
|
|
4385
5322
|
function isTopToBottomReadingDirection(value) {
|
|
@@ -4524,7 +5461,7 @@ function rankStacks(childIds, nodeBoxes, flowRanks) {
|
|
|
4524
5461
|
}
|
|
4525
5462
|
return stacks;
|
|
4526
5463
|
}
|
|
4527
|
-
function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds) {
|
|
5464
|
+
function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds, laneGutter) {
|
|
4528
5465
|
const populatedBounds = laneBounds.filter(
|
|
4529
5466
|
(box) => box !== void 0
|
|
4530
5467
|
);
|
|
@@ -4532,6 +5469,7 @@ function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, header
|
|
|
4532
5469
|
const left = Math.min(...populatedBounds.map((box) => box.x));
|
|
4533
5470
|
const slotWidth = Math.max(...populatedBounds.map((box) => box.width)) + headerHeight + padding * 2;
|
|
4534
5471
|
const slotHeight = Math.max(...populatedBounds.map((box) => box.height)) + padding * 2;
|
|
5472
|
+
const laneStep = slotHeight + laneGutter;
|
|
4535
5473
|
for (let index = 0; index < swimlane.lanes.length; index += 1) {
|
|
4536
5474
|
const lane = swimlane.lanes[index];
|
|
4537
5475
|
const bounds = laneBounds[index];
|
|
@@ -4540,7 +5478,7 @@ function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, header
|
|
|
4540
5478
|
}
|
|
4541
5479
|
const target = {
|
|
4542
5480
|
x: left + headerHeight + padding,
|
|
4543
|
-
y: top +
|
|
5481
|
+
y: top + laneStep * index + padding
|
|
4544
5482
|
};
|
|
4545
5483
|
moveLaneChildren(
|
|
4546
5484
|
lane.children,
|
|
@@ -4559,10 +5497,11 @@ function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, header
|
|
|
4559
5497
|
x: left,
|
|
4560
5498
|
y: top,
|
|
4561
5499
|
width: slotWidth,
|
|
4562
|
-
height:
|
|
5500
|
+
height: laneStep * (swimlane.lanes.length - 1) + slotHeight
|
|
4563
5501
|
},
|
|
4564
5502
|
slotWidth,
|
|
4565
|
-
slotHeight
|
|
5503
|
+
slotHeight,
|
|
5504
|
+
laneStep
|
|
4566
5505
|
};
|
|
4567
5506
|
}
|
|
4568
5507
|
function moveLaneChildren(childIds, nodeBoxes, locks, diagnostics, movedChildIds, offset) {
|
|
@@ -4891,7 +5830,10 @@ function coordinatePorts(node, nodeBox, portShifting) {
|
|
|
4891
5830
|
}
|
|
4892
5831
|
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
4893
5832
|
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
4894
|
-
const
|
|
5833
|
+
const requestedSpacing = portShifting?.spacing ?? 24;
|
|
5834
|
+
const maxOffset = side === "left" || side === "right" ? nodeBox.height / 2 : nodeBox.width / 2;
|
|
5835
|
+
const availableSpan = 2 * maxOffset;
|
|
5836
|
+
const spacing = shiftingEnabled && count > 1 ? Math.min(requestedSpacing, availableSpan / (count - 1)) : requestedSpacing;
|
|
4895
5837
|
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
4896
5838
|
switch (side) {
|
|
4897
5839
|
case "left":
|
|
@@ -4946,13 +5888,13 @@ function coordinateSwimlanes(swimlanes, nodeBoxes, layouts) {
|
|
|
4946
5888
|
if (layout2 === "contract" && contractLayout !== void 0) {
|
|
4947
5889
|
const lanes2 = swimlane.lanes.map((lane, index) => {
|
|
4948
5890
|
const box = swimlane.orientation === "vertical" ? {
|
|
4949
|
-
x: contractLayout.box.x + contractLayout.
|
|
5891
|
+
x: contractLayout.box.x + contractLayout.laneStep * index,
|
|
4950
5892
|
y: contractLayout.box.y,
|
|
4951
5893
|
width: contractLayout.slotWidth,
|
|
4952
5894
|
height: contractLayout.box.height
|
|
4953
5895
|
} : {
|
|
4954
5896
|
x: contractLayout.box.x,
|
|
4955
|
-
y: contractLayout.box.y + contractLayout.
|
|
5897
|
+
y: contractLayout.box.y + contractLayout.laneStep * index,
|
|
4956
5898
|
width: contractLayout.box.width,
|
|
4957
5899
|
height: contractLayout.slotHeight
|
|
4958
5900
|
};
|
|
@@ -5053,17 +5995,19 @@ function coordinateSwimlanes(swimlanes, nodeBoxes, layouts) {
|
|
|
5053
5995
|
});
|
|
5054
5996
|
}
|
|
5055
5997
|
function coordinateFrame(frame, contentBounds) {
|
|
5056
|
-
const padding =
|
|
5057
|
-
const titleHeight = 28;
|
|
5998
|
+
const padding = framePadding(frame.padding);
|
|
5999
|
+
const titleHeight = frame.headerHeight ?? 28;
|
|
5058
6000
|
const titleWidth = Math.max(180, frame.titleTab.length * 7);
|
|
5059
6001
|
const box = {
|
|
5060
|
-
x: contentBounds.x - padding,
|
|
5061
|
-
y: contentBounds.y - padding - titleHeight,
|
|
5062
|
-
width: contentBounds.width + padding
|
|
5063
|
-
height: contentBounds.height + padding
|
|
6002
|
+
x: contentBounds.x - padding.left,
|
|
6003
|
+
y: contentBounds.y - padding.top - titleHeight,
|
|
6004
|
+
width: contentBounds.width + padding.left + padding.right,
|
|
6005
|
+
height: contentBounds.height + padding.top + padding.bottom + titleHeight
|
|
5064
6006
|
};
|
|
5065
6007
|
return {
|
|
5066
6008
|
...frame,
|
|
6009
|
+
headerHeight: titleHeight,
|
|
6010
|
+
padding: frame.padding ?? 32,
|
|
5067
6011
|
box,
|
|
5068
6012
|
titleBox: {
|
|
5069
6013
|
x: box.x,
|
|
@@ -5073,6 +6017,9 @@ function coordinateFrame(frame, contentBounds) {
|
|
|
5073
6017
|
}
|
|
5074
6018
|
};
|
|
5075
6019
|
}
|
|
6020
|
+
function framePadding(value) {
|
|
6021
|
+
return normalizeInsets(value ?? 32);
|
|
6022
|
+
}
|
|
5076
6023
|
function expand(box, padding, titleSize) {
|
|
5077
6024
|
return {
|
|
5078
6025
|
x: box.x - padding,
|
|
@@ -5168,10 +6115,16 @@ function edgeBounds(edges) {
|
|
|
5168
6115
|
if (edge.points.length === 0) {
|
|
5169
6116
|
return [];
|
|
5170
6117
|
}
|
|
5171
|
-
const
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
6118
|
+
const extraPoints = [];
|
|
6119
|
+
if (edge.points.length >= 2) {
|
|
6120
|
+
const arrowhead = computeArrowhead(edge.points);
|
|
6121
|
+
extraPoints.push(arrowhead.tip, arrowhead.left, arrowhead.right);
|
|
6122
|
+
}
|
|
6123
|
+
const allPoints = [...edge.points, ...extraPoints];
|
|
6124
|
+
const minX = Math.min(...allPoints.map((point2) => point2.x));
|
|
6125
|
+
const minY = Math.min(...allPoints.map((point2) => point2.y));
|
|
6126
|
+
const maxX = Math.max(...allPoints.map((point2) => point2.x));
|
|
6127
|
+
const maxY = Math.max(...allPoints.map((point2) => point2.y));
|
|
5175
6128
|
return [
|
|
5176
6129
|
{
|
|
5177
6130
|
x: minX,
|
|
@@ -5568,6 +6521,7 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5568
6521
|
ownerId: node.id,
|
|
5569
6522
|
surfaceKind: "node-label",
|
|
5570
6523
|
layout: layout2,
|
|
6524
|
+
typography: typographyForLabel(node.label),
|
|
5571
6525
|
anchor: node.box
|
|
5572
6526
|
})
|
|
5573
6527
|
);
|
|
@@ -5583,6 +6537,7 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5583
6537
|
ownerId: group.id,
|
|
5584
6538
|
surfaceKind: "group-label",
|
|
5585
6539
|
layout: layout2,
|
|
6540
|
+
typography: typographyForLabel(group.label),
|
|
5586
6541
|
anchor: group.box
|
|
5587
6542
|
})
|
|
5588
6543
|
);
|
|
@@ -5595,7 +6550,11 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5595
6550
|
const layout2 = fitLabel(
|
|
5596
6551
|
port.label.text,
|
|
5597
6552
|
{
|
|
5598
|
-
font:
|
|
6553
|
+
font: typographyTextStyle(port.label, {
|
|
6554
|
+
fontFamily: "Arial",
|
|
6555
|
+
fontSize: 10,
|
|
6556
|
+
lineHeight: 12
|
|
6557
|
+
}),
|
|
5599
6558
|
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
5600
6559
|
minSize: { width: 0, height: 0 },
|
|
5601
6560
|
maxWidth: 160
|
|
@@ -5607,6 +6566,7 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5607
6566
|
ownerId: `${node.id}.${port.id}`,
|
|
5608
6567
|
surfaceKind: "port-label",
|
|
5609
6568
|
layout: layout2,
|
|
6569
|
+
typography: typographyForLabel(port.label),
|
|
5610
6570
|
anchor: portLabelBox(port)
|
|
5611
6571
|
})
|
|
5612
6572
|
);
|
|
@@ -5657,7 +6617,11 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5657
6617
|
const layout2 = fitLabel(
|
|
5658
6618
|
lane.label.text,
|
|
5659
6619
|
{
|
|
5660
|
-
font:
|
|
6620
|
+
font: typographyTextStyle(lane.label, {
|
|
6621
|
+
fontFamily: "Arial",
|
|
6622
|
+
fontSize: 12,
|
|
6623
|
+
lineHeight: 14
|
|
6624
|
+
}),
|
|
5661
6625
|
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
5662
6626
|
minSize: { width: 0, height: 0 },
|
|
5663
6627
|
maxWidth: swimlane.orientation === "horizontal" ? labelBox.height : labelBox.width
|
|
@@ -5669,6 +6633,7 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5669
6633
|
ownerId: `${swimlane.id}.${lane.id}`,
|
|
5670
6634
|
surfaceKind: "swimlane-label",
|
|
5671
6635
|
layout: layout2,
|
|
6636
|
+
typography: typographyForLabel(lane.label),
|
|
5672
6637
|
anchor: labelBox
|
|
5673
6638
|
})
|
|
5674
6639
|
);
|
|
@@ -5676,9 +6641,10 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5676
6641
|
}
|
|
5677
6642
|
return annotations;
|
|
5678
6643
|
}
|
|
5679
|
-
function coordinateEdgeTextAnnotations(edges, textMeasurer) {
|
|
6644
|
+
function coordinateEdgeTextAnnotations(edges, obstacleBoxes, textMeasurer) {
|
|
5680
6645
|
const measurer = textMeasurer ?? createDefaultTextMeasurer();
|
|
5681
6646
|
const annotations = [];
|
|
6647
|
+
const placedLabelBoxes = [];
|
|
5682
6648
|
for (const edge of edges) {
|
|
5683
6649
|
if (edge.label?.text === void 0) {
|
|
5684
6650
|
continue;
|
|
@@ -5686,19 +6652,37 @@ function coordinateEdgeTextAnnotations(edges, textMeasurer) {
|
|
|
5686
6652
|
const layout2 = fitLabel(
|
|
5687
6653
|
edge.label.text,
|
|
5688
6654
|
{
|
|
5689
|
-
font:
|
|
6655
|
+
font: typographyTextStyle(edge.label, {
|
|
6656
|
+
fontFamily: "Arial",
|
|
6657
|
+
fontSize: 12,
|
|
6658
|
+
lineHeight: 14
|
|
6659
|
+
}),
|
|
5690
6660
|
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
5691
6661
|
minSize: { width: 0, height: 0 },
|
|
5692
6662
|
maxWidth: 200
|
|
5693
6663
|
},
|
|
5694
6664
|
measurer
|
|
5695
6665
|
);
|
|
6666
|
+
const center = edgeLabelAnchor(
|
|
6667
|
+
edge,
|
|
6668
|
+
layout2,
|
|
6669
|
+
edges,
|
|
6670
|
+
obstacleBoxes,
|
|
6671
|
+
placedLabelBoxes
|
|
6672
|
+
);
|
|
6673
|
+
placedLabelBoxes.push({
|
|
6674
|
+
x: center.x - layout2.box.width / 2,
|
|
6675
|
+
y: center.y - layout2.box.height / 2,
|
|
6676
|
+
width: layout2.box.width,
|
|
6677
|
+
height: layout2.box.height
|
|
6678
|
+
});
|
|
5696
6679
|
annotations.push(
|
|
5697
6680
|
buildCenteredTextAnnotation({
|
|
5698
6681
|
ownerId: edge.id,
|
|
5699
6682
|
surfaceKind: "edge-label",
|
|
5700
6683
|
layout: layout2,
|
|
5701
|
-
|
|
6684
|
+
typography: typographyForLabel(edge.label),
|
|
6685
|
+
center
|
|
5702
6686
|
})
|
|
5703
6687
|
);
|
|
5704
6688
|
}
|
|
@@ -5737,7 +6721,8 @@ function buildTextAnnotation(input) {
|
|
|
5737
6721
|
anchor: input.anchor,
|
|
5738
6722
|
paddings: input.layout.padding,
|
|
5739
6723
|
lines: input.layout.lines,
|
|
5740
|
-
|
|
6724
|
+
fontFamily: input.typography?.fontFamily ?? normalizeOutputFontFamily(input.layout.font),
|
|
6725
|
+
fontSize: input.typography?.fontSize ?? input.layout.font.fontSize,
|
|
5741
6726
|
textBackend: input.layout.textBackend
|
|
5742
6727
|
};
|
|
5743
6728
|
}
|
|
@@ -5747,6 +6732,7 @@ function buildAnchorCenteredTextAnnotation(input) {
|
|
|
5747
6732
|
surfaceKind: input.surfaceKind,
|
|
5748
6733
|
...input.surfaceIndex === void 0 ? {} : { surfaceIndex: input.surfaceIndex },
|
|
5749
6734
|
layout: input.layout,
|
|
6735
|
+
...input.typography === void 0 ? {} : { typography: input.typography },
|
|
5750
6736
|
center: {
|
|
5751
6737
|
x: input.anchor.x + input.anchor.width / 2,
|
|
5752
6738
|
y: input.anchor.y + input.anchor.height / 2
|
|
@@ -5769,10 +6755,14 @@ function buildCenteredTextAnnotation(input) {
|
|
|
5769
6755
|
anchor: input.anchor ?? input.center,
|
|
5770
6756
|
paddings: input.layout.padding,
|
|
5771
6757
|
lines: input.layout.lines,
|
|
5772
|
-
|
|
6758
|
+
fontFamily: input.typography?.fontFamily ?? normalizeOutputFontFamily(input.layout.font),
|
|
6759
|
+
fontSize: input.typography?.fontSize ?? input.layout.font.fontSize,
|
|
5773
6760
|
textBackend: input.layout.textBackend
|
|
5774
6761
|
};
|
|
5775
6762
|
}
|
|
6763
|
+
function normalizeOutputFontFamily(font) {
|
|
6764
|
+
return font.fontFamily === "Arial" ? "Arial, sans-serif" : font.fontFamily;
|
|
6765
|
+
}
|
|
5776
6766
|
function reportTextAnnotationCollisions(annotations) {
|
|
5777
6767
|
const diagnostics = [];
|
|
5778
6768
|
const relevantAnnotations = annotations.filter(
|
|
@@ -5962,12 +6952,16 @@ function fallbackLabelLayout(text) {
|
|
|
5962
6952
|
diagnostics: []
|
|
5963
6953
|
};
|
|
5964
6954
|
}
|
|
5965
|
-
function edgeLabelAnchor(edge, layout2, edges) {
|
|
6955
|
+
function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes) {
|
|
5966
6956
|
const placement = labelPlacementOnPolyline2(edge.points);
|
|
5967
6957
|
if (placement === void 0) {
|
|
5968
6958
|
return { x: 0, y: 0 };
|
|
5969
6959
|
}
|
|
5970
|
-
for (const candidate of edgeLabelAnchorCandidates(
|
|
6960
|
+
for (const candidate of edgeLabelAnchorCandidates(
|
|
6961
|
+
edge.points,
|
|
6962
|
+
placement,
|
|
6963
|
+
layout2
|
|
6964
|
+
)) {
|
|
5971
6965
|
const labelBox = {
|
|
5972
6966
|
x: candidate.x - layout2.box.width / 2,
|
|
5973
6967
|
y: candidate.y - layout2.box.height / 2,
|
|
@@ -5980,36 +6974,55 @@ function edgeLabelAnchor(edge, layout2, edges) {
|
|
|
5980
6974
|
const crossesOtherRoute = edges.some(
|
|
5981
6975
|
(other) => other.id !== edge.id && routeIntersectsTextBox(other.points, labelBox)
|
|
5982
6976
|
);
|
|
5983
|
-
if (
|
|
6977
|
+
if (crossesOtherRoute) {
|
|
6978
|
+
continue;
|
|
6979
|
+
}
|
|
6980
|
+
const overlapsNode = obstacleBoxes.some(
|
|
6981
|
+
(box) => intersectsAabb(labelBox, box)
|
|
6982
|
+
);
|
|
6983
|
+
if (overlapsNode) {
|
|
6984
|
+
continue;
|
|
6985
|
+
}
|
|
6986
|
+
const overlapsPlacedLabel = placedLabelBoxes.some(
|
|
6987
|
+
(box) => intersectsAabb(labelBox, box)
|
|
6988
|
+
);
|
|
6989
|
+
if (!overlapsPlacedLabel) {
|
|
5984
6990
|
return candidate;
|
|
5985
6991
|
}
|
|
5986
6992
|
}
|
|
5987
6993
|
return placement;
|
|
5988
6994
|
}
|
|
5989
|
-
function edgeLabelAnchorCandidates(points, placement) {
|
|
6995
|
+
function edgeLabelAnchorCandidates(points, placement, layout2) {
|
|
5990
6996
|
const segment = labelSegmentOnPolyline(points);
|
|
5991
6997
|
if (segment === void 0) {
|
|
5992
6998
|
return [placement];
|
|
5993
6999
|
}
|
|
7000
|
+
const candidates = [placement];
|
|
5994
7001
|
if (segment.start.y === segment.end.y) {
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
7002
|
+
const needed = layout2.box.height / 2 + EDGE_LABEL_CLEARANCE;
|
|
7003
|
+
const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
|
|
7004
|
+
for (let step = 1; step <= maxSteps; step += 1) {
|
|
7005
|
+
const offset = EDGE_LABEL_CLEARANCE * step;
|
|
7006
|
+
candidates.push(
|
|
7007
|
+
{ x: placement.x, y: placement.y - offset },
|
|
7008
|
+
{ x: placement.x, y: placement.y + offset }
|
|
7009
|
+
);
|
|
7010
|
+
}
|
|
7011
|
+
return candidates;
|
|
6002
7012
|
}
|
|
6003
7013
|
if (segment.start.x === segment.end.x) {
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
7014
|
+
const needed = layout2.box.width / 2 + EDGE_LABEL_CLEARANCE;
|
|
7015
|
+
const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
|
|
7016
|
+
for (let step = 1; step <= maxSteps; step += 1) {
|
|
7017
|
+
const offset = EDGE_LABEL_CLEARANCE * step;
|
|
7018
|
+
candidates.push(
|
|
7019
|
+
{ x: placement.x + offset, y: placement.y },
|
|
7020
|
+
{ x: placement.x - offset, y: placement.y }
|
|
7021
|
+
);
|
|
7022
|
+
}
|
|
7023
|
+
return candidates;
|
|
6011
7024
|
}
|
|
6012
|
-
return
|
|
7025
|
+
return candidates;
|
|
6013
7026
|
}
|
|
6014
7027
|
function labelPlacementOnPolyline2(points) {
|
|
6015
7028
|
return labelSegmentOnPolyline(points)?.placement;
|
|
@@ -6100,8 +7113,26 @@ function portGeometry(nodeGeometry, port) {
|
|
|
6100
7113
|
obstacleBox: port.box
|
|
6101
7114
|
};
|
|
6102
7115
|
}
|
|
6103
|
-
function
|
|
6104
|
-
|
|
7116
|
+
function stableUniqueById(items, diagnostics, pathRoot, code) {
|
|
7117
|
+
const firstById = /* @__PURE__ */ new Map();
|
|
7118
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
7119
|
+
const item = items[index];
|
|
7120
|
+
if (item === void 0) {
|
|
7121
|
+
continue;
|
|
7122
|
+
}
|
|
7123
|
+
if (firstById.has(item.id)) {
|
|
7124
|
+
diagnostics.push({
|
|
7125
|
+
severity: "error",
|
|
7126
|
+
code,
|
|
7127
|
+
message: `Duplicate ${pathRoot.slice(0, -1)} id ${item.id} was ignored; first occurrence was kept.`,
|
|
7128
|
+
path: [pathRoot, index, "id"],
|
|
7129
|
+
detail: { id: item.id, duplicateIndex: index }
|
|
7130
|
+
});
|
|
7131
|
+
continue;
|
|
7132
|
+
}
|
|
7133
|
+
firstById.set(item.id, item);
|
|
7134
|
+
}
|
|
7135
|
+
return [...firstById.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
6105
7136
|
}
|
|
6106
7137
|
function stableByConstraintId(items) {
|
|
6107
7138
|
return [...items].sort(
|
|
@@ -6383,6 +7414,7 @@ exports.routeEdge = routeEdge;
|
|
|
6383
7414
|
exports.runDagreInitialLayout = runDagreInitialLayout;
|
|
6384
7415
|
exports.simplifyRoute = simplifyRoute;
|
|
6385
7416
|
exports.solveDiagram = solveDiagram;
|
|
7417
|
+
exports.solveDiagramSafe = solveDiagramSafe;
|
|
6386
7418
|
exports.sortDslDiagnostics = sortDslDiagnostics;
|
|
6387
7419
|
exports.stringifyCanonical = stringifyCanonical;
|
|
6388
7420
|
exports.toCanvasFont = toCanvasFont;
|