@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.js
CHANGED
|
@@ -96,11 +96,21 @@ function applyLayoutConstraints(input) {
|
|
|
96
96
|
const nodeById = new Map(input.nodes.map((node) => [node.id, node]));
|
|
97
97
|
applyFixedPositionLocks(input.nodes, boxes, locks, diagnostics);
|
|
98
98
|
applyExactPositions(input.constraints, boxes, locks, diagnostics, nodeById);
|
|
99
|
-
applyContainment(input.constraints, boxes, locks, diagnostics);
|
|
99
|
+
applyContainment(input.constraints, boxes, locks, diagnostics, false);
|
|
100
100
|
applyRelative(input.constraints, boxes, locks, diagnostics);
|
|
101
101
|
applyAlign(input.constraints, boxes, locks, diagnostics);
|
|
102
102
|
applyDistribute(input.constraints, boxes, locks, diagnostics);
|
|
103
|
-
repairOverlaps(
|
|
103
|
+
repairOverlaps(
|
|
104
|
+
input,
|
|
105
|
+
boxes,
|
|
106
|
+
locks,
|
|
107
|
+
diagnostics,
|
|
108
|
+
siblingOverlapKeys(input.constraints)
|
|
109
|
+
);
|
|
110
|
+
applyContainment(input.constraints, boxes, locks, diagnostics, true);
|
|
111
|
+
applyDistributeContained(input, boxes, locks, diagnostics);
|
|
112
|
+
reportOverlaps(boxes, diagnostics, containmentOverlapKeys(input.constraints));
|
|
113
|
+
reportIntraContainerOverflow(input, boxes, diagnostics);
|
|
104
114
|
return { boxes, locks, diagnostics };
|
|
105
115
|
}
|
|
106
116
|
function cloneValidBoxes(input, diagnostics) {
|
|
@@ -188,7 +198,7 @@ function applyExactPositions(constraints, boxes, locks, diagnostics, nodeById) {
|
|
|
188
198
|
locks.set(targetId, { nodeId: targetId, source: "exact-position" });
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
|
-
function applyContainment(constraints, boxes, locks, diagnostics) {
|
|
201
|
+
function applyContainment(constraints, boxes, locks, diagnostics, reportOverflow) {
|
|
192
202
|
for (const constraint of constraints) {
|
|
193
203
|
if (constraint.kind !== "containment") {
|
|
194
204
|
continue;
|
|
@@ -210,21 +220,23 @@ function applyContainment(constraints, boxes, locks, diagnostics) {
|
|
|
210
220
|
continue;
|
|
211
221
|
}
|
|
212
222
|
if (locks.has(childId)) {
|
|
213
|
-
|
|
214
|
-
severity: "warning",
|
|
215
|
-
code: "constraints.locked-target-not-moved",
|
|
216
|
-
message: `Locked child ${childId} was not moved into containment.`,
|
|
217
|
-
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
218
|
-
detail: { nodeId: childId }
|
|
219
|
-
});
|
|
220
|
-
if (!isInside(child, content)) {
|
|
223
|
+
if (!reportOverflow) {
|
|
221
224
|
diagnostics.push({
|
|
222
|
-
severity: "
|
|
223
|
-
code: "constraints.
|
|
224
|
-
message: `Locked child ${childId}
|
|
225
|
+
severity: "warning",
|
|
226
|
+
code: "constraints.locked-target-not-moved",
|
|
227
|
+
message: `Locked child ${childId} was not moved into containment.`,
|
|
225
228
|
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
226
|
-
detail: { nodeId: childId
|
|
229
|
+
detail: { nodeId: childId }
|
|
227
230
|
});
|
|
231
|
+
if (!isInside(child, content)) {
|
|
232
|
+
diagnostics.push({
|
|
233
|
+
severity: "error",
|
|
234
|
+
code: "constraints.containment.impossible",
|
|
235
|
+
message: `Locked child ${childId} cannot fit inside ${constraint.containerId}.`,
|
|
236
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
237
|
+
detail: { nodeId: childId, containerId: constraint.containerId }
|
|
238
|
+
});
|
|
239
|
+
}
|
|
228
240
|
}
|
|
229
241
|
continue;
|
|
230
242
|
}
|
|
@@ -239,6 +251,15 @@ function applyContainment(constraints, boxes, locks, diagnostics) {
|
|
|
239
251
|
continue;
|
|
240
252
|
}
|
|
241
253
|
boxes.set(childId, next);
|
|
254
|
+
if (reportOverflow) {
|
|
255
|
+
diagnostics.push({
|
|
256
|
+
severity: "warning",
|
|
257
|
+
code: "containment_overflow",
|
|
258
|
+
message: `Child ${childId} was clamped back inside ${constraint.containerId} after constraint solving.`,
|
|
259
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
260
|
+
detail: { nodeId: childId, containerId: constraint.containerId }
|
|
261
|
+
});
|
|
262
|
+
}
|
|
242
263
|
}
|
|
243
264
|
}
|
|
244
265
|
}
|
|
@@ -314,10 +335,11 @@ function applyDistribute(constraints, boxes, locks, diagnostics) {
|
|
|
314
335
|
}
|
|
315
336
|
}
|
|
316
337
|
}
|
|
317
|
-
function repairOverlaps(input, boxes, locks, diagnostics) {
|
|
338
|
+
function repairOverlaps(input, boxes, locks, diagnostics, siblingPairs) {
|
|
318
339
|
const spacing = input.overlapSpacing ?? 40;
|
|
319
340
|
const axis = input.direction === "LR" || input.direction === "RL" ? "x" : "y";
|
|
320
341
|
const secondaryAxis = axis === "x" ? "y" : "x";
|
|
342
|
+
const ignoredPairs = containmentOverlapKeys(input.constraints);
|
|
321
343
|
const ids = [...boxes.keys()].sort();
|
|
322
344
|
for (let pass = 0; pass < 2; pass += 1) {
|
|
323
345
|
for (const firstId of ids) {
|
|
@@ -325,6 +347,9 @@ function repairOverlaps(input, boxes, locks, diagnostics) {
|
|
|
325
347
|
if (firstId >= secondId) {
|
|
326
348
|
continue;
|
|
327
349
|
}
|
|
350
|
+
if (ignoredPairs.has(overlapKey(firstId, secondId))) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
328
353
|
const first = boxes.get(firstId);
|
|
329
354
|
const second = boxes.get(secondId);
|
|
330
355
|
if (first === void 0 || second === void 0 || !intersectsAabb(first, second)) {
|
|
@@ -339,16 +364,40 @@ function repairOverlaps(input, boxes, locks, diagnostics) {
|
|
|
339
364
|
const moving = movingId === firstId ? first : second;
|
|
340
365
|
const fixed = movingId === firstId ? second : first;
|
|
341
366
|
const repairAxis = firstLocked === secondLocked && pass === 0 ? secondaryAxis : axis;
|
|
342
|
-
const
|
|
367
|
+
const pairKey = overlapKey(firstId, secondId);
|
|
368
|
+
const effectiveSpacing = siblingPairs.has(pairKey) ? Math.max(spacing, input.minSiblingGap ?? 0) : spacing;
|
|
369
|
+
const moved = movePastOverlap(
|
|
370
|
+
moving,
|
|
371
|
+
fixed,
|
|
372
|
+
repairAxis,
|
|
373
|
+
effectiveSpacing
|
|
374
|
+
);
|
|
343
375
|
boxes.set(movingId, moved);
|
|
344
376
|
}
|
|
345
377
|
}
|
|
346
378
|
}
|
|
379
|
+
reportOverlaps(boxes, diagnostics, ignoredPairs);
|
|
380
|
+
}
|
|
381
|
+
function reportOverlaps(boxes, diagnostics, ignoredPairs = /* @__PURE__ */ new Set()) {
|
|
382
|
+
const ids = [...boxes.keys()].sort();
|
|
383
|
+
const reported = new Set(
|
|
384
|
+
diagnostics.filter(
|
|
385
|
+
(diagnostic) => diagnostic.code === "constraints.overlap.unresolved"
|
|
386
|
+
).map((diagnostic) => {
|
|
387
|
+
const firstId = diagnostic.detail?.firstId;
|
|
388
|
+
const secondId = diagnostic.detail?.secondId;
|
|
389
|
+
return typeof firstId === "string" && typeof secondId === "string" ? overlapKey(firstId, secondId) : void 0;
|
|
390
|
+
}).filter((key) => key !== void 0)
|
|
391
|
+
);
|
|
347
392
|
for (const firstId of ids) {
|
|
348
393
|
for (const secondId of ids) {
|
|
349
394
|
if (firstId >= secondId) {
|
|
350
395
|
continue;
|
|
351
396
|
}
|
|
397
|
+
const key = overlapKey(firstId, secondId);
|
|
398
|
+
if (reported.has(key) || ignoredPairs.has(key)) {
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
352
401
|
const first = boxes.get(firstId);
|
|
353
402
|
const second = boxes.get(secondId);
|
|
354
403
|
if (first !== void 0 && second !== void 0 && intersectsAabb(first, second)) {
|
|
@@ -359,9 +408,136 @@ function repairOverlaps(input, boxes, locks, diagnostics) {
|
|
|
359
408
|
path: ["boxes"],
|
|
360
409
|
detail: { firstId, secondId }
|
|
361
410
|
});
|
|
411
|
+
reported.add(key);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function reportIntraContainerOverflow(input, boxes, diagnostics) {
|
|
417
|
+
if (input.minSiblingGap === void 0) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const minGap = input.minSiblingGap;
|
|
421
|
+
const axis = input.direction === "LR" || input.direction === "RL" ? "x" : "y";
|
|
422
|
+
for (const constraint of input.constraints) {
|
|
423
|
+
if (constraint.kind !== "containment") {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
const container = boxes.get(constraint.containerId);
|
|
427
|
+
if (container === void 0) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
const children = [];
|
|
431
|
+
for (const childId of constraint.childIds) {
|
|
432
|
+
const child = boxes.get(childId);
|
|
433
|
+
if (child !== void 0) {
|
|
434
|
+
children.push(child);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (children.length < 2) {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
const sorted = [...children].sort((a, b) => a[axis] - b[axis]);
|
|
441
|
+
const mainDim = axis === "x" ? "width" : "height";
|
|
442
|
+
let overlapPairs = 0;
|
|
443
|
+
for (let i = 0; i < sorted.length; i += 1) {
|
|
444
|
+
const first = sorted[i];
|
|
445
|
+
if (first === void 0) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
for (let j = i + 1; j < sorted.length; j += 1) {
|
|
449
|
+
const second = sorted[j];
|
|
450
|
+
if (second === void 0) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (second[axis] >= first[axis] + first[mainDim]) {
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
if (intersectsAabb(first, second)) {
|
|
457
|
+
overlapPairs += 1;
|
|
458
|
+
}
|
|
362
459
|
}
|
|
363
460
|
}
|
|
461
|
+
if (overlapPairs > 0) {
|
|
462
|
+
diagnostics.push({
|
|
463
|
+
severity: "warning",
|
|
464
|
+
code: "intra_container_overflow",
|
|
465
|
+
message: `${overlapPairs} sibling pair(s) overlap inside ${constraint.containerId}.`,
|
|
466
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
467
|
+
detail: {
|
|
468
|
+
containerId: constraint.containerId,
|
|
469
|
+
overlapPairs,
|
|
470
|
+
minGap
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
const pad = constraint.padding ?? { top: 0, right: 0, bottom: 0, left: 0 };
|
|
475
|
+
const contentMain = mainDim === "width" ? Math.max(0, container.width - pad.left - pad.right) : Math.max(0, container.height - pad.top - pad.bottom);
|
|
476
|
+
let childStart = Infinity;
|
|
477
|
+
let childEnd = -Infinity;
|
|
478
|
+
for (const child of sorted) {
|
|
479
|
+
const start = child[axis];
|
|
480
|
+
const end = start + child[mainDim];
|
|
481
|
+
if (start < childStart) childStart = start;
|
|
482
|
+
if (end > childEnd) childEnd = end;
|
|
483
|
+
}
|
|
484
|
+
if (sorted.length === 0) {
|
|
485
|
+
childStart = 0;
|
|
486
|
+
childEnd = 0;
|
|
487
|
+
}
|
|
488
|
+
const actualExtent = childEnd - childStart;
|
|
489
|
+
if (actualExtent > contentMain) {
|
|
490
|
+
diagnostics.push({
|
|
491
|
+
severity: "error",
|
|
492
|
+
code: "intra_container_overflow_total",
|
|
493
|
+
message: `Container ${constraint.containerId} cannot fit ${sorted.length} siblings along ${axis} (extent ${actualExtent}, available ${contentMain}).`,
|
|
494
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
495
|
+
detail: {
|
|
496
|
+
containerId: constraint.containerId,
|
|
497
|
+
axis,
|
|
498
|
+
needed: actualExtent,
|
|
499
|
+
available: contentMain,
|
|
500
|
+
siblingCount: sorted.length,
|
|
501
|
+
minGap
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function overlapKey(firstId, secondId) {
|
|
508
|
+
return firstId < secondId ? `${firstId}\0${secondId}` : `${secondId}\0${firstId}`;
|
|
509
|
+
}
|
|
510
|
+
function containmentOverlapKeys(constraints) {
|
|
511
|
+
const keys = /* @__PURE__ */ new Set();
|
|
512
|
+
for (const constraint of constraints) {
|
|
513
|
+
if (constraint.kind !== "containment") {
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
for (const childId of constraint.childIds) {
|
|
517
|
+
keys.add(overlapKey(constraint.containerId, childId));
|
|
518
|
+
}
|
|
364
519
|
}
|
|
520
|
+
return keys;
|
|
521
|
+
}
|
|
522
|
+
function siblingOverlapKeys(constraints) {
|
|
523
|
+
const keys = /* @__PURE__ */ new Set();
|
|
524
|
+
for (const constraint of constraints) {
|
|
525
|
+
if (constraint.kind !== "containment") {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
const { childIds } = constraint;
|
|
529
|
+
for (let i = 0; i < childIds.length; i += 1) {
|
|
530
|
+
for (let j = i + 1; j < childIds.length; j += 1) {
|
|
531
|
+
const a = childIds[i];
|
|
532
|
+
const b = childIds[j];
|
|
533
|
+
if (a === void 0 || b === void 0) {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
keys.add(overlapKey(a, b));
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return keys;
|
|
365
541
|
}
|
|
366
542
|
function setUnlockedBox(id, next, boxes, locks, diagnostics, constraint) {
|
|
367
543
|
const current = boxes.get(id);
|
|
@@ -479,10 +655,125 @@ function contentBox(container, padding) {
|
|
|
479
655
|
return {
|
|
480
656
|
x: container.x + margin.left,
|
|
481
657
|
y: container.y + margin.top,
|
|
482
|
-
width: container.width - margin.left - margin.right,
|
|
483
|
-
height: container.height - margin.top - margin.bottom
|
|
658
|
+
width: Math.max(0, container.width - margin.left - margin.right),
|
|
659
|
+
height: Math.max(0, container.height - margin.top - margin.bottom)
|
|
484
660
|
};
|
|
485
661
|
}
|
|
662
|
+
function applyDistributeContained(input, boxes, locks, diagnostics) {
|
|
663
|
+
if (!input.distributeContainedChildren) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
const axis = input.direction === "LR" || input.direction === "RL" ? "x" : "y";
|
|
667
|
+
const crossAxis = axis === "x" ? "y" : "x";
|
|
668
|
+
const mainSize = axis === "x" ? "width" : "height";
|
|
669
|
+
const crossSize = axis === "x" ? "height" : "width";
|
|
670
|
+
const minGap = input.minSiblingGap ?? 8;
|
|
671
|
+
for (const constraint of input.constraints) {
|
|
672
|
+
if (constraint.kind !== "containment") {
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
const container = boxes.get(constraint.containerId);
|
|
676
|
+
if (container === void 0) {
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
const content = contentBox(container, constraint.padding);
|
|
680
|
+
const unlocked = [];
|
|
681
|
+
const reserved = [];
|
|
682
|
+
for (const childId of constraint.childIds) {
|
|
683
|
+
const box = boxes.get(childId);
|
|
684
|
+
if (box === void 0) {
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (locks.has(childId)) {
|
|
688
|
+
diagnostics.push({
|
|
689
|
+
severity: "warning",
|
|
690
|
+
code: "constraints.locked-target-not-moved",
|
|
691
|
+
message: `Locked child ${childId} skipped during containment distribution.`,
|
|
692
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
693
|
+
detail: { nodeId: childId }
|
|
694
|
+
});
|
|
695
|
+
reserved.push(intervalForBox(box, axis, mainSize));
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
unlocked.push({ id: childId, box });
|
|
699
|
+
}
|
|
700
|
+
if (unlocked.length < 2) {
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
const oversized = unlocked.filter(
|
|
704
|
+
(child) => child.box[mainSize] > content[mainSize] || child.box[crossSize] > content[crossSize]
|
|
705
|
+
);
|
|
706
|
+
if (oversized.length > 0) {
|
|
707
|
+
diagnostics.push({
|
|
708
|
+
severity: "warning",
|
|
709
|
+
code: "constraints.containment.impossible",
|
|
710
|
+
message: `Skipped ${oversized.length} oversized child(ren) during distribution in ${constraint.containerId}.`,
|
|
711
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
712
|
+
detail: {
|
|
713
|
+
containerId: constraint.containerId,
|
|
714
|
+
oversized: oversized.map((c) => c.id)
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
for (const child of oversized) {
|
|
719
|
+
reserved.push(intervalForBox(child.box, axis, mainSize));
|
|
720
|
+
}
|
|
721
|
+
reserved.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
722
|
+
const distributable = unlocked.filter(
|
|
723
|
+
(child) => child.box[mainSize] <= content[mainSize] && child.box[crossSize] <= content[crossSize]
|
|
724
|
+
);
|
|
725
|
+
if (distributable.length < 2) {
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
let pos = content[axis];
|
|
729
|
+
for (const child of distributable) {
|
|
730
|
+
pos = advancePastReserved(pos, child.box[mainSize], reserved, minGap);
|
|
731
|
+
const crossPos = content[crossAxis] + Math.max(0, (content[crossSize] - child.box[crossSize]) / 2);
|
|
732
|
+
const next = { ...child.box };
|
|
733
|
+
next[axis] = pos;
|
|
734
|
+
next[crossAxis] = crossPos;
|
|
735
|
+
const clamped = moveInside(next, content);
|
|
736
|
+
if (clamped[axis] !== next[axis]) {
|
|
737
|
+
diagnostics.push({
|
|
738
|
+
severity: "warning",
|
|
739
|
+
code: "intra_container_distributed_clamped",
|
|
740
|
+
message: `Distribution gap clamped for ${child.id} in ${constraint.containerId}.`,
|
|
741
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
742
|
+
detail: { nodeId: child.id, containerId: constraint.containerId }
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
boxes.set(child.id, clamped);
|
|
746
|
+
pos = clamped[axis] + clamped[mainSize] + minGap;
|
|
747
|
+
}
|
|
748
|
+
diagnostics.push({
|
|
749
|
+
severity: "info",
|
|
750
|
+
code: "intra_container_distributed",
|
|
751
|
+
message: `Distributed ${distributable.length} children in ${constraint.containerId} along ${axis}.`,
|
|
752
|
+
path: ["constraints", constraint.id ?? constraint.containerId],
|
|
753
|
+
detail: {
|
|
754
|
+
containerId: constraint.containerId,
|
|
755
|
+
count: distributable.length,
|
|
756
|
+
axis
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function intervalForBox(box, axis, mainSize) {
|
|
762
|
+
return { start: box[axis], end: box[axis] + box[mainSize] };
|
|
763
|
+
}
|
|
764
|
+
function advancePastReserved(pos, size, reserved, minGap) {
|
|
765
|
+
let next = pos;
|
|
766
|
+
for (const interval of reserved) {
|
|
767
|
+
if (next + size + minGap <= interval.start) {
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
if (next >= interval.end + minGap) {
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
next = interval.end + minGap;
|
|
774
|
+
}
|
|
775
|
+
return next;
|
|
776
|
+
}
|
|
486
777
|
function moveInside(child, container) {
|
|
487
778
|
return {
|
|
488
779
|
...child,
|
|
@@ -1188,7 +1479,11 @@ var DEFAULT_GROUP_PADDING = {
|
|
|
1188
1479
|
left: 16
|
|
1189
1480
|
};
|
|
1190
1481
|
var DEFAULT_LABEL_MAX_WIDTH = 160;
|
|
1191
|
-
var DEFAULT_FONT = {
|
|
1482
|
+
var DEFAULT_FONT = {
|
|
1483
|
+
fontFamily: "Arial",
|
|
1484
|
+
fontSize: 14,
|
|
1485
|
+
lineHeight: 18
|
|
1486
|
+
};
|
|
1192
1487
|
var DEFAULT_MATRIX_CELL_SIZE = { width: 120, height: 36 };
|
|
1193
1488
|
var DEFAULT_TABLE_CELL_SIZE = { width: 128, height: 34 };
|
|
1194
1489
|
var DEFAULT_PANEL_ITEM_HEIGHT = 28;
|
|
@@ -1364,7 +1659,9 @@ function endpoint(value, nodeIdOverride) {
|
|
|
1364
1659
|
function style(value) {
|
|
1365
1660
|
return {
|
|
1366
1661
|
...value.fill === void 0 ? {} : { fill: value.fill },
|
|
1367
|
-
...value.stroke === void 0 ? {} : { stroke: value.stroke }
|
|
1662
|
+
...value.stroke === void 0 ? {} : { stroke: value.stroke },
|
|
1663
|
+
...value.fontFamily === void 0 ? {} : { fontFamily: value.fontFamily },
|
|
1664
|
+
...value.fontSize === void 0 ? {} : { fontSize: value.fontSize }
|
|
1368
1665
|
};
|
|
1369
1666
|
}
|
|
1370
1667
|
function compartments(value) {
|
|
@@ -1381,6 +1678,10 @@ function normalizeFrame(frame) {
|
|
|
1381
1678
|
...frame.context === void 0 ? {} : { context: frame.context },
|
|
1382
1679
|
...frame.name === void 0 ? {} : { name: frame.name },
|
|
1383
1680
|
titleTab: frame.titleTab,
|
|
1681
|
+
...frame.headerHeight === void 0 ? {} : { headerHeight: frame.headerHeight },
|
|
1682
|
+
...frame.padding === void 0 ? {} : { padding: frame.padding },
|
|
1683
|
+
...frame.labelPosition === void 0 ? {} : { labelPosition: frame.labelPosition },
|
|
1684
|
+
...frame.direction === void 0 ? {} : { direction: frame.direction },
|
|
1384
1685
|
...frame.style === void 0 ? {} : { style: style(frame.style) }
|
|
1385
1686
|
};
|
|
1386
1687
|
}
|
|
@@ -1524,6 +1825,9 @@ function normalizeGroups(dsl, measurer) {
|
|
|
1524
1825
|
nodeIds: [...group?.nodes ?? []],
|
|
1525
1826
|
groupIds: [...group?.groups ?? []],
|
|
1526
1827
|
padding: group?.padding ?? { ...DEFAULT_GROUP_PADDING },
|
|
1828
|
+
...group?.headerHeight === void 0 ? {} : { headerHeight: group.headerHeight },
|
|
1829
|
+
...group?.labelPosition === void 0 ? {} : { labelPosition: group.labelPosition },
|
|
1830
|
+
...group?.direction === void 0 ? {} : { direction: group.direction },
|
|
1527
1831
|
...labelLayout === void 0 ? {} : { labelLayout }
|
|
1528
1832
|
};
|
|
1529
1833
|
});
|
|
@@ -1793,6 +2097,12 @@ var insetsSchema = z.object({
|
|
|
1793
2097
|
bottom: finiteNumberSchema,
|
|
1794
2098
|
left: finiteNumberSchema
|
|
1795
2099
|
});
|
|
2100
|
+
var nonNegativeInsetsSchema = z.object({
|
|
2101
|
+
top: nonNegativeNumberSchema,
|
|
2102
|
+
right: nonNegativeNumberSchema,
|
|
2103
|
+
bottom: nonNegativeNumberSchema,
|
|
2104
|
+
left: nonNegativeNumberSchema
|
|
2105
|
+
});
|
|
1796
2106
|
var labelSchema = z.union([
|
|
1797
2107
|
z.string(),
|
|
1798
2108
|
z.object({
|
|
@@ -1802,7 +2112,9 @@ var labelSchema = z.union([
|
|
|
1802
2112
|
]);
|
|
1803
2113
|
var styleSchema = z.object({
|
|
1804
2114
|
fill: z.string().optional(),
|
|
1805
|
-
stroke: z.string().optional()
|
|
2115
|
+
stroke: z.string().optional(),
|
|
2116
|
+
fontFamily: z.string().optional(),
|
|
2117
|
+
fontSize: finiteNumberSchema.optional()
|
|
1806
2118
|
});
|
|
1807
2119
|
var blockCellSchema = z.union([
|
|
1808
2120
|
z.string(),
|
|
@@ -1873,7 +2185,10 @@ var groupSchema = z.object({
|
|
|
1873
2185
|
label: labelSchema.optional(),
|
|
1874
2186
|
nodes: z.array(z.string()).optional(),
|
|
1875
2187
|
groups: z.array(z.string()).optional(),
|
|
1876
|
-
padding: insetsSchema.optional()
|
|
2188
|
+
padding: insetsSchema.optional(),
|
|
2189
|
+
headerHeight: nonNegativeNumberSchema.optional(),
|
|
2190
|
+
labelPosition: z.enum(["top", "inside", "outside"]).optional(),
|
|
2191
|
+
direction: z.enum(["horizontal", "vertical"]).optional()
|
|
1877
2192
|
});
|
|
1878
2193
|
var swimlaneSchema = z.object({
|
|
1879
2194
|
label: labelSchema.optional(),
|
|
@@ -2085,6 +2400,10 @@ var diagramDslSchema = z.object({
|
|
|
2085
2400
|
context: z.string().optional(),
|
|
2086
2401
|
name: z.string().optional(),
|
|
2087
2402
|
titleTab: z.string(),
|
|
2403
|
+
headerHeight: nonNegativeNumberSchema.optional(),
|
|
2404
|
+
padding: z.union([nonNegativeNumberSchema, nonNegativeInsetsSchema]).optional(),
|
|
2405
|
+
labelPosition: z.enum(["top", "inside", "outside"]).optional(),
|
|
2406
|
+
direction: z.enum(["horizontal", "vertical"]).optional(),
|
|
2088
2407
|
style: styleSchema.optional()
|
|
2089
2408
|
}).optional(),
|
|
2090
2409
|
output: z.object({
|
|
@@ -3219,7 +3538,7 @@ function renderSolvedTextAnnotation(annotation, className, options) {
|
|
|
3219
3538
|
`data-text-surface="${escapeAttribute(annotation.surfaceKind)}"`,
|
|
3220
3539
|
`data-owner-id="${escapeAttribute(annotation.ownerId)}"`,
|
|
3221
3540
|
`data-text-backend="${escapeAttribute(annotation.textBackend ?? "deterministic")}"`,
|
|
3222
|
-
`font-family="${
|
|
3541
|
+
`font-family="${escapeAttribute(annotation.fontFamily)}"`,
|
|
3223
3542
|
`font-size="${formatNumber(annotation.fontSize)}"`,
|
|
3224
3543
|
`fill="#111827"`
|
|
3225
3544
|
];
|
|
@@ -3547,7 +3866,12 @@ function routeEdge(input) {
|
|
|
3547
3866
|
input.source.center,
|
|
3548
3867
|
input.targetAnchor ?? defaultAnchors.targetAnchor
|
|
3549
3868
|
);
|
|
3550
|
-
const points =
|
|
3869
|
+
const points = finalizeRoute(
|
|
3870
|
+
[source, target],
|
|
3871
|
+
softObstacles,
|
|
3872
|
+
hardObstacles,
|
|
3873
|
+
diagnostics
|
|
3874
|
+
);
|
|
3551
3875
|
if (routeCrossesBoxes(points, hardObstacles)) {
|
|
3552
3876
|
diagnostics.push({
|
|
3553
3877
|
severity: "error",
|
|
@@ -3603,7 +3927,15 @@ function routeEdge(input) {
|
|
|
3603
3927
|
candidate.points,
|
|
3604
3928
|
candidate.endpointObstacles
|
|
3605
3929
|
)) {
|
|
3606
|
-
return {
|
|
3930
|
+
return {
|
|
3931
|
+
points: finalizeRoute(
|
|
3932
|
+
candidate.points,
|
|
3933
|
+
softObstacles,
|
|
3934
|
+
hardObstacles,
|
|
3935
|
+
diagnostics
|
|
3936
|
+
),
|
|
3937
|
+
diagnostics
|
|
3938
|
+
};
|
|
3607
3939
|
}
|
|
3608
3940
|
}
|
|
3609
3941
|
const hardClearCandidate = candidateRoutes.find(
|
|
@@ -3619,7 +3951,12 @@ function routeEdge(input) {
|
|
|
3619
3951
|
message: "No bounded orthogonal route candidate avoided all soft obstacles."
|
|
3620
3952
|
});
|
|
3621
3953
|
return {
|
|
3622
|
-
points:
|
|
3954
|
+
points: finalizeRoute(
|
|
3955
|
+
hardClearCandidate.points,
|
|
3956
|
+
softObstacles,
|
|
3957
|
+
hardObstacles,
|
|
3958
|
+
diagnostics
|
|
3959
|
+
),
|
|
3623
3960
|
diagnostics
|
|
3624
3961
|
};
|
|
3625
3962
|
}
|
|
@@ -3630,8 +3967,11 @@ function routeEdge(input) {
|
|
|
3630
3967
|
message: "No bounded orthogonal route candidate avoided hard evidence block obstacles."
|
|
3631
3968
|
});
|
|
3632
3969
|
return {
|
|
3633
|
-
points:
|
|
3634
|
-
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors)
|
|
3970
|
+
points: finalizeRoute(
|
|
3971
|
+
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
|
|
3972
|
+
softObstacles,
|
|
3973
|
+
hardObstacles,
|
|
3974
|
+
diagnostics
|
|
3635
3975
|
),
|
|
3636
3976
|
diagnostics
|
|
3637
3977
|
};
|
|
@@ -3642,12 +3982,133 @@ function routeEdge(input) {
|
|
|
3642
3982
|
message: "No bounded orthogonal route candidate avoided all obstacles."
|
|
3643
3983
|
});
|
|
3644
3984
|
return {
|
|
3645
|
-
points:
|
|
3646
|
-
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors)
|
|
3985
|
+
points: finalizeRoute(
|
|
3986
|
+
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
|
|
3987
|
+
softObstacles,
|
|
3988
|
+
hardObstacles,
|
|
3989
|
+
diagnostics
|
|
3647
3990
|
),
|
|
3648
3991
|
diagnostics
|
|
3649
3992
|
};
|
|
3650
3993
|
}
|
|
3994
|
+
function finalizeRoute(points, softObstacles, hardObstacles, diagnostics) {
|
|
3995
|
+
const simplified = simplifyRoute(points);
|
|
3996
|
+
const crossesHardObstacles = routeCrossesBoxes(simplified, hardObstacles);
|
|
3997
|
+
const crossesSoftObstacles = routeCrossesBoxes(simplified, softObstacles);
|
|
3998
|
+
if (simplified.length < 3 && (crossesHardObstacles || crossesSoftObstacles)) {
|
|
3999
|
+
diagnostics.push({
|
|
4000
|
+
severity: crossesHardObstacles ? "error" : "warning",
|
|
4001
|
+
code: "route_obstacle_fallback",
|
|
4002
|
+
message: "Obstacle-aware routing fell back to fewer than three route points.",
|
|
4003
|
+
detail: { pointCount: simplified.length }
|
|
4004
|
+
});
|
|
4005
|
+
return expandFallbackRoute(simplified, [
|
|
4006
|
+
...softObstacles,
|
|
4007
|
+
...hardObstacles
|
|
4008
|
+
]);
|
|
4009
|
+
}
|
|
4010
|
+
return simplified;
|
|
4011
|
+
}
|
|
4012
|
+
function expandFallbackRoute(points, obstacles) {
|
|
4013
|
+
if (points.length !== 2) {
|
|
4014
|
+
return points.map((point2) => ({ ...point2 }));
|
|
4015
|
+
}
|
|
4016
|
+
const [source, target] = points;
|
|
4017
|
+
if (source === void 0 || target === void 0) {
|
|
4018
|
+
return points.map((point2) => ({ ...point2 }));
|
|
4019
|
+
}
|
|
4020
|
+
if (source.y === target.y) {
|
|
4021
|
+
const detourY = horizontalDetourLane(source, target, obstacles);
|
|
4022
|
+
return [
|
|
4023
|
+
{ ...source },
|
|
4024
|
+
{ x: source.x, y: detourY },
|
|
4025
|
+
{ x: target.x, y: detourY },
|
|
4026
|
+
{ ...target }
|
|
4027
|
+
];
|
|
4028
|
+
}
|
|
4029
|
+
if (source.x === target.x) {
|
|
4030
|
+
const detourX = verticalDetourLane(source, target, obstacles);
|
|
4031
|
+
return [
|
|
4032
|
+
{ ...source },
|
|
4033
|
+
{ x: detourX, y: source.y },
|
|
4034
|
+
{ x: detourX, y: target.y },
|
|
4035
|
+
{ ...target }
|
|
4036
|
+
];
|
|
4037
|
+
}
|
|
4038
|
+
const hv = diagonalDetourHV(source, target, obstacles);
|
|
4039
|
+
const vh = diagonalDetourVH(source, target, obstacles);
|
|
4040
|
+
const viable = [hv, vh].filter((c) => !routeCrossesBoxes(c, obstacles));
|
|
4041
|
+
if (viable.length > 0) {
|
|
4042
|
+
const directLen = Math.hypot(target.x - source.x, target.y - source.y);
|
|
4043
|
+
let best = viable[0];
|
|
4044
|
+
for (let i = 1; i < viable.length; i += 1) {
|
|
4045
|
+
const cand = viable[i];
|
|
4046
|
+
if (cand !== void 0 && pathLength(cand) - directLen < pathLength(best) - directLen) {
|
|
4047
|
+
best = cand;
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
return best;
|
|
4051
|
+
}
|
|
4052
|
+
return [
|
|
4053
|
+
{ ...source },
|
|
4054
|
+
{ x: (source.x + target.x) / 2, y: source.y },
|
|
4055
|
+
{ x: (source.x + target.x) / 2, y: target.y },
|
|
4056
|
+
{ ...target }
|
|
4057
|
+
];
|
|
4058
|
+
}
|
|
4059
|
+
function horizontalDetourLane(source, target, obstacles) {
|
|
4060
|
+
const crossing = obstacles.filter(
|
|
4061
|
+
(obstacle) => segmentIntersectsBox(source, target, obstacle)
|
|
4062
|
+
);
|
|
4063
|
+
if (crossing.length === 0) {
|
|
4064
|
+
return source.y + (source.x <= target.x ? 1 : -1) * 24;
|
|
4065
|
+
}
|
|
4066
|
+
const margin = 24;
|
|
4067
|
+
const above = Math.min(...crossing.map((obstacle) => obstacle.y)) - margin;
|
|
4068
|
+
const below = Math.max(...crossing.map((obstacle) => obstacle.y + obstacle.height)) + margin;
|
|
4069
|
+
return Math.abs(above - source.y) <= Math.abs(below - source.y) ? above : below;
|
|
4070
|
+
}
|
|
4071
|
+
function verticalDetourLane(source, target, obstacles) {
|
|
4072
|
+
const crossing = obstacles.filter(
|
|
4073
|
+
(obstacle) => segmentIntersectsBox(source, target, obstacle)
|
|
4074
|
+
);
|
|
4075
|
+
if (crossing.length === 0) {
|
|
4076
|
+
return source.x + (source.y <= target.y ? 1 : -1) * 24;
|
|
4077
|
+
}
|
|
4078
|
+
const margin = 24;
|
|
4079
|
+
const left = Math.min(...crossing.map((obstacle) => obstacle.x)) - margin;
|
|
4080
|
+
const right = Math.max(...crossing.map((obstacle) => obstacle.x + obstacle.width)) + margin;
|
|
4081
|
+
return Math.abs(left - source.x) <= Math.abs(right - source.x) ? left : right;
|
|
4082
|
+
}
|
|
4083
|
+
function diagonalDetourHV(source, target, obstacles) {
|
|
4084
|
+
const detourY = horizontalDetourLane(source, target, obstacles);
|
|
4085
|
+
return [
|
|
4086
|
+
{ ...source },
|
|
4087
|
+
{ x: source.x, y: detourY },
|
|
4088
|
+
{ x: target.x, y: detourY },
|
|
4089
|
+
{ ...target }
|
|
4090
|
+
];
|
|
4091
|
+
}
|
|
4092
|
+
function diagonalDetourVH(source, target, obstacles) {
|
|
4093
|
+
const detourX = verticalDetourLane(source, target, obstacles);
|
|
4094
|
+
return [
|
|
4095
|
+
{ ...source },
|
|
4096
|
+
{ x: detourX, y: source.y },
|
|
4097
|
+
{ x: detourX, y: target.y },
|
|
4098
|
+
{ ...target }
|
|
4099
|
+
];
|
|
4100
|
+
}
|
|
4101
|
+
function pathLength(points) {
|
|
4102
|
+
let len = 0;
|
|
4103
|
+
for (let i = 1; i < points.length; i += 1) {
|
|
4104
|
+
const a = points[i - 1];
|
|
4105
|
+
const b = points[i];
|
|
4106
|
+
if (a !== void 0 && b !== void 0) {
|
|
4107
|
+
len += Math.hypot(b.x - a.x, b.y - a.y);
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
return len;
|
|
4111
|
+
}
|
|
3651
4112
|
function endpointObstaclesForAutoAnchors(input) {
|
|
3652
4113
|
const boxes = [];
|
|
3653
4114
|
if (input.sourceAnchor === void 0 && hasDistinctAnchors(input.source)) {
|
|
@@ -4019,6 +4480,15 @@ var DEFAULT_PANEL_WIDTH = 320;
|
|
|
4019
4480
|
var DEFAULT_PANEL_ITEM_HEIGHT2 = 28;
|
|
4020
4481
|
var DEFAULT_EVIDENCE_BLOCK_GAP = 24;
|
|
4021
4482
|
var EDGE_LABEL_CLEARANCE = 8;
|
|
4483
|
+
var DEFAULT_CJK_FONT_FAMILY = "YaHei,SimSun,sans-serif";
|
|
4484
|
+
var DEFAULT_MIN_CJK_FONT_SIZE = 14;
|
|
4485
|
+
function prefitLabelFont(node, _options) {
|
|
4486
|
+
const cjk = labelCjkTypography(node.label?.metadata);
|
|
4487
|
+
const fontFamily = cjk.fontFamily ?? DEFAULT_FONT.fontFamily;
|
|
4488
|
+
const fontSize = cjk.fontSize ?? DEFAULT_FONT.fontSize;
|
|
4489
|
+
const lineHeight = fontSize !== DEFAULT_FONT.fontSize ? Math.max(DEFAULT_FONT.lineHeight ?? 18, fontSize * 1.2) : DEFAULT_FONT.lineHeight ?? 18;
|
|
4490
|
+
return { fontFamily, fontSize, lineHeight };
|
|
4491
|
+
}
|
|
4022
4492
|
var EVIDENCE_TEXT_FONT = {
|
|
4023
4493
|
fontFamily: "Arial, sans-serif",
|
|
4024
4494
|
fontSize: 10,
|
|
@@ -4026,43 +4496,85 @@ var EVIDENCE_TEXT_FONT = {
|
|
|
4026
4496
|
};
|
|
4027
4497
|
function solveDiagram(diagram, options = {}) {
|
|
4028
4498
|
const diagnostics = [...diagram.diagnostics];
|
|
4029
|
-
const nodes =
|
|
4030
|
-
|
|
4031
|
-
|
|
4499
|
+
const nodes = stableUniqueById(
|
|
4500
|
+
diagram.nodes,
|
|
4501
|
+
diagnostics,
|
|
4502
|
+
"nodes",
|
|
4503
|
+
"duplicate_node_id"
|
|
4504
|
+
);
|
|
4505
|
+
const edges = stableUniqueById(
|
|
4506
|
+
diagram.edges,
|
|
4507
|
+
diagnostics,
|
|
4508
|
+
"edges",
|
|
4509
|
+
"duplicate_edge_id"
|
|
4510
|
+
);
|
|
4511
|
+
const groups = stableUniqueById(
|
|
4512
|
+
diagram.groups,
|
|
4513
|
+
diagnostics,
|
|
4514
|
+
"groups",
|
|
4515
|
+
"duplicate_group_id"
|
|
4516
|
+
);
|
|
4517
|
+
const cjkTypography = createCjkTypographyOptions(options);
|
|
4518
|
+
const cjkStyledNodes = nodes.map(
|
|
4519
|
+
(node) => enhanceNodeCjkTypography(node, cjkTypography, diagnostics)
|
|
4520
|
+
);
|
|
4521
|
+
const styledNodes = options.prefitLabelSize === true ? cjkStyledNodes.map(
|
|
4522
|
+
(node) => prefitNodeLabelSize(node, options, diagnostics)
|
|
4523
|
+
) : cjkStyledNodes;
|
|
4524
|
+
const styledEdges = edges.map(
|
|
4525
|
+
(edge) => enhanceEdgeCjkTypography(edge, cjkTypography, diagnostics)
|
|
4526
|
+
);
|
|
4527
|
+
const styledGroups = groups.map(
|
|
4528
|
+
(group) => enhanceGroupCjkTypography(group, cjkTypography, diagnostics)
|
|
4529
|
+
);
|
|
4530
|
+
const styledSwimlanes = (diagram.swimlanes ?? []).map(
|
|
4531
|
+
(swimlane) => enhanceSwimlaneCjkTypography(swimlane, cjkTypography, diagnostics)
|
|
4532
|
+
);
|
|
4032
4533
|
const constraints = stableByConstraintId(diagram.constraints);
|
|
4033
4534
|
const layout2 = runDagreInitialLayout({
|
|
4034
4535
|
direction: diagram.direction,
|
|
4035
|
-
nodes:
|
|
4036
|
-
edges:
|
|
4536
|
+
nodes: styledNodes.map((node) => ({ id: node.id, size: node.size })),
|
|
4537
|
+
edges: styledEdges.map((edge) => ({
|
|
4037
4538
|
id: edge.id,
|
|
4038
4539
|
sourceId: edge.source.nodeId,
|
|
4039
4540
|
targetId: edge.target.nodeId
|
|
4040
4541
|
}))
|
|
4041
4542
|
});
|
|
4042
4543
|
diagnostics.push(...layout2.diagnostics);
|
|
4544
|
+
const initialNodeBoxes = wrapVerticalStackIfNeeded(
|
|
4545
|
+
layout2.boxes,
|
|
4546
|
+
styledNodes,
|
|
4547
|
+
styledEdges,
|
|
4548
|
+
diagram.direction,
|
|
4549
|
+
options,
|
|
4550
|
+
diagnostics
|
|
4551
|
+
);
|
|
4043
4552
|
const constrained = applyLayoutConstraints({
|
|
4044
4553
|
direction: diagram.direction,
|
|
4045
4554
|
overlapSpacing: options?.overlapSpacing ?? 40,
|
|
4046
|
-
|
|
4047
|
-
|
|
4555
|
+
...options.minSiblingGap === void 0 ? {} : { minSiblingGap: options.minSiblingGap },
|
|
4556
|
+
...options.distributeContainedChildren === void 0 ? {} : { distributeContainedChildren: options.distributeContainedChildren },
|
|
4557
|
+
boxes: initialNodeBoxes,
|
|
4558
|
+
nodes: styledNodes,
|
|
4048
4559
|
constraints
|
|
4049
4560
|
});
|
|
4050
4561
|
diagnostics.push(...constrained.diagnostics);
|
|
4051
4562
|
const swimlaneContracts = applySwimlaneLayoutContracts(
|
|
4052
|
-
|
|
4563
|
+
styledSwimlanes,
|
|
4053
4564
|
constraints,
|
|
4054
|
-
|
|
4565
|
+
styledEdges,
|
|
4055
4566
|
isTopToBottomReadingDirection(diagram.metadata?.primaryReadingDirection),
|
|
4056
4567
|
constrained.boxes,
|
|
4057
4568
|
constrained.locks,
|
|
4058
|
-
options?.overlapSpacing ?? 40
|
|
4569
|
+
options?.overlapSpacing ?? 40,
|
|
4570
|
+
Math.max(0, options?.minLaneGutter ?? 0)
|
|
4059
4571
|
);
|
|
4060
4572
|
if (swimlaneContracts.layouts.size > 0) {
|
|
4061
4573
|
removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
|
|
4062
4574
|
}
|
|
4063
4575
|
diagnostics.push(...swimlaneContracts.diagnostics);
|
|
4064
4576
|
const coordinatedNodes = coordinateNodes(
|
|
4065
|
-
|
|
4577
|
+
styledNodes,
|
|
4066
4578
|
constrained.boxes,
|
|
4067
4579
|
options,
|
|
4068
4580
|
diagnostics
|
|
@@ -4078,13 +4590,13 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4078
4590
|
])
|
|
4079
4591
|
);
|
|
4080
4592
|
const coordinatedGroups = coordinateGroups(
|
|
4081
|
-
|
|
4593
|
+
styledGroups,
|
|
4082
4594
|
constrained.boxes,
|
|
4083
4595
|
options,
|
|
4084
4596
|
diagnostics
|
|
4085
4597
|
);
|
|
4086
4598
|
const coordinatedSwimlanes = coordinateSwimlanes(
|
|
4087
|
-
|
|
4599
|
+
styledSwimlanes,
|
|
4088
4600
|
constrained.boxes,
|
|
4089
4601
|
swimlaneContracts.layouts
|
|
4090
4602
|
);
|
|
@@ -4184,7 +4696,7 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4184
4696
|
...frameTextAnnotation.filter(isPreRouteTextObstacle)
|
|
4185
4697
|
];
|
|
4186
4698
|
const coordinatedEdges = coordinateEdges(
|
|
4187
|
-
|
|
4699
|
+
styledEdges,
|
|
4188
4700
|
nodeGeometryById,
|
|
4189
4701
|
coordinatedNodes,
|
|
4190
4702
|
[...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
|
|
@@ -4200,6 +4712,11 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4200
4712
|
);
|
|
4201
4713
|
const edgeTextAnnotations = coordinateEdgeTextAnnotations(
|
|
4202
4714
|
coordinatedEdges,
|
|
4715
|
+
[
|
|
4716
|
+
...coordinatedNodes.map((node) => node.box),
|
|
4717
|
+
...baseTextAnnotations.map((annotation) => annotation.box),
|
|
4718
|
+
...frameTextAnnotation.map((annotation) => annotation.box)
|
|
4719
|
+
],
|
|
4203
4720
|
options.textMeasurer
|
|
4204
4721
|
);
|
|
4205
4722
|
const textAnnotations = [
|
|
@@ -4217,6 +4734,12 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4217
4734
|
...edgePointBounds,
|
|
4218
4735
|
...edgeTextAnnotations.map((annotation) => annotation.box)
|
|
4219
4736
|
];
|
|
4737
|
+
diagnostics.push(
|
|
4738
|
+
...reportPageOverflow(
|
|
4739
|
+
frame === void 0 ? unionBoxes(boundsBase) : unionBoxes([...boundsBase, frame.box, frame.titleBox]),
|
|
4740
|
+
options.pageBounds
|
|
4741
|
+
)
|
|
4742
|
+
);
|
|
4220
4743
|
return {
|
|
4221
4744
|
id: diagram.id,
|
|
4222
4745
|
...diagram.title === void 0 ? {} : { title: diagram.title },
|
|
@@ -4235,7 +4758,303 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4235
4758
|
...diagram.metadata === void 0 ? {} : { metadata: diagram.metadata }
|
|
4236
4759
|
};
|
|
4237
4760
|
}
|
|
4238
|
-
function
|
|
4761
|
+
function solveDiagramSafe(diagram, options = {}) {
|
|
4762
|
+
return solveDiagram(diagram, { ...options, prefitLabelSize: true });
|
|
4763
|
+
}
|
|
4764
|
+
function prefitNodeLabelSize(node, options, diagnostics) {
|
|
4765
|
+
if (node.label === void 0) {
|
|
4766
|
+
return node;
|
|
4767
|
+
}
|
|
4768
|
+
const measurer = options.textMeasurer ?? createDefaultTextMeasurer();
|
|
4769
|
+
const layout2 = fitLabel(
|
|
4770
|
+
node.label.text,
|
|
4771
|
+
{
|
|
4772
|
+
font: prefitLabelFont(node),
|
|
4773
|
+
padding: DEFAULT_NODE_PADDING,
|
|
4774
|
+
minSize: DEFAULT_NODE_MIN_SIZE,
|
|
4775
|
+
maxWidth: node.label.maxWidth ?? Math.max(node.size.width, DEFAULT_LABEL_MAX_WIDTH)
|
|
4776
|
+
},
|
|
4777
|
+
measurer
|
|
4778
|
+
);
|
|
4779
|
+
const width = Math.max(node.size.width, layout2.fittedSize.width);
|
|
4780
|
+
const height = Math.max(node.size.height, layout2.fittedSize.height);
|
|
4781
|
+
const resized = width !== node.size.width || height !== node.size.height;
|
|
4782
|
+
if (resized) {
|
|
4783
|
+
diagnostics.push({
|
|
4784
|
+
severity: "info",
|
|
4785
|
+
code: "prefit_label_resized",
|
|
4786
|
+
message: `Node ${node.id} size expanded to fit its label.`,
|
|
4787
|
+
path: ["nodes", node.id],
|
|
4788
|
+
detail: {
|
|
4789
|
+
nodeId: node.id,
|
|
4790
|
+
from: { width: node.size.width, height: node.size.height },
|
|
4791
|
+
to: { width, height }
|
|
4792
|
+
}
|
|
4793
|
+
});
|
|
4794
|
+
}
|
|
4795
|
+
const centeredLayout = expandLabelLayoutToNode(layout2, { width, height });
|
|
4796
|
+
return { ...node, size: { width, height }, labelLayout: centeredLayout };
|
|
4797
|
+
}
|
|
4798
|
+
function expandLabelLayoutToNode(layout2, nodeSize) {
|
|
4799
|
+
if (layout2.box.width >= nodeSize.width && layout2.box.height >= nodeSize.height) {
|
|
4800
|
+
return layout2;
|
|
4801
|
+
}
|
|
4802
|
+
const offsetX = Math.max(0, (nodeSize.width - layout2.box.width) / 2);
|
|
4803
|
+
const offsetY = Math.max(0, (nodeSize.height - layout2.box.height) / 2);
|
|
4804
|
+
if (offsetX === 0 && offsetY === 0) {
|
|
4805
|
+
return layout2;
|
|
4806
|
+
}
|
|
4807
|
+
return {
|
|
4808
|
+
...layout2,
|
|
4809
|
+
box: {
|
|
4810
|
+
x: layout2.box.x + offsetX,
|
|
4811
|
+
y: layout2.box.y + offsetY,
|
|
4812
|
+
width: layout2.box.width,
|
|
4813
|
+
height: layout2.box.height
|
|
4814
|
+
},
|
|
4815
|
+
contentBox: {
|
|
4816
|
+
x: layout2.contentBox.x + offsetX,
|
|
4817
|
+
y: layout2.contentBox.y + offsetY,
|
|
4818
|
+
width: layout2.contentBox.width,
|
|
4819
|
+
height: layout2.contentBox.height
|
|
4820
|
+
},
|
|
4821
|
+
lines: layout2.lines.map((line) => ({
|
|
4822
|
+
...line,
|
|
4823
|
+
box: {
|
|
4824
|
+
x: line.box.x + offsetX,
|
|
4825
|
+
y: line.box.y + offsetY,
|
|
4826
|
+
width: line.box.width,
|
|
4827
|
+
height: line.box.height
|
|
4828
|
+
}
|
|
4829
|
+
}))
|
|
4830
|
+
};
|
|
4831
|
+
}
|
|
4832
|
+
function reportPageOverflow(contentBounds, pageBounds) {
|
|
4833
|
+
if (pageBounds === void 0) {
|
|
4834
|
+
return [];
|
|
4835
|
+
}
|
|
4836
|
+
const overflowRight = Math.max(
|
|
4837
|
+
0,
|
|
4838
|
+
contentBounds.x + contentBounds.width - pageBounds.width
|
|
4839
|
+
);
|
|
4840
|
+
const overflowBottom = Math.max(
|
|
4841
|
+
0,
|
|
4842
|
+
contentBounds.y + contentBounds.height - pageBounds.height
|
|
4843
|
+
);
|
|
4844
|
+
const overflowLeft = Math.max(0, -contentBounds.x);
|
|
4845
|
+
const overflowTop = Math.max(0, -contentBounds.y);
|
|
4846
|
+
if (overflowRight === 0 && overflowBottom === 0 && overflowLeft === 0 && overflowTop === 0) {
|
|
4847
|
+
return [];
|
|
4848
|
+
}
|
|
4849
|
+
return [
|
|
4850
|
+
{
|
|
4851
|
+
severity: "warning",
|
|
4852
|
+
code: "page_overflow",
|
|
4853
|
+
message: `Content ${contentBounds.width}x${contentBounds.height} exceeds page ${pageBounds.width}x${pageBounds.height}.`,
|
|
4854
|
+
path: ["bounds"],
|
|
4855
|
+
detail: {
|
|
4856
|
+
page: { width: pageBounds.width, height: pageBounds.height },
|
|
4857
|
+
content: {
|
|
4858
|
+
width: contentBounds.width,
|
|
4859
|
+
height: contentBounds.height
|
|
4860
|
+
},
|
|
4861
|
+
overflow: {
|
|
4862
|
+
right: overflowRight,
|
|
4863
|
+
bottom: overflowBottom,
|
|
4864
|
+
left: overflowLeft,
|
|
4865
|
+
top: overflowTop
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
];
|
|
4870
|
+
}
|
|
4871
|
+
function createCjkTypographyOptions(options) {
|
|
4872
|
+
const fontFamily = options.cjkFontFamily === false ? void 0 : options.cjkFontFamily ?? DEFAULT_CJK_FONT_FAMILY;
|
|
4873
|
+
const minFontSize = options.minCjkFontSize === false ? void 0 : options.minCjkFontSize ?? DEFAULT_MIN_CJK_FONT_SIZE;
|
|
4874
|
+
return {
|
|
4875
|
+
...fontFamily === void 0 ? {} : { fontFamily },
|
|
4876
|
+
...minFontSize === void 0 ? {} : { minFontSize }
|
|
4877
|
+
};
|
|
4878
|
+
}
|
|
4879
|
+
function enhanceNodeCjkTypography(node, options, diagnostics) {
|
|
4880
|
+
const nodeWithStyle = enhanceStyledLabelOwner(
|
|
4881
|
+
node,
|
|
4882
|
+
["nodes", node.id],
|
|
4883
|
+
options,
|
|
4884
|
+
diagnostics
|
|
4885
|
+
);
|
|
4886
|
+
const ports = nodeWithStyle.ports === void 0 ? void 0 : nodeWithStyle.ports.map(
|
|
4887
|
+
(port) => enhanceStyledLabelOwner(
|
|
4888
|
+
port,
|
|
4889
|
+
["nodes", node.id, "ports", port.id],
|
|
4890
|
+
options,
|
|
4891
|
+
diagnostics
|
|
4892
|
+
)
|
|
4893
|
+
);
|
|
4894
|
+
return ports === void 0 ? nodeWithStyle : { ...nodeWithStyle, ports };
|
|
4895
|
+
}
|
|
4896
|
+
function enhanceEdgeCjkTypography(edge, options, diagnostics) {
|
|
4897
|
+
return enhanceStyledLabelOwner(
|
|
4898
|
+
edge,
|
|
4899
|
+
["edges", edge.id],
|
|
4900
|
+
options,
|
|
4901
|
+
diagnostics
|
|
4902
|
+
);
|
|
4903
|
+
}
|
|
4904
|
+
function enhanceGroupCjkTypography(group, options, diagnostics) {
|
|
4905
|
+
return enhanceStyledLabelOwner(
|
|
4906
|
+
group,
|
|
4907
|
+
["groups", group.id],
|
|
4908
|
+
options,
|
|
4909
|
+
diagnostics
|
|
4910
|
+
);
|
|
4911
|
+
}
|
|
4912
|
+
function enhanceSwimlaneCjkTypography(swimlane, options, diagnostics) {
|
|
4913
|
+
const root = enhanceStyledLabelOwner(
|
|
4914
|
+
swimlane,
|
|
4915
|
+
["swimlanes", swimlane.id],
|
|
4916
|
+
options,
|
|
4917
|
+
diagnostics
|
|
4918
|
+
);
|
|
4919
|
+
const lanes = root.lanes.map(
|
|
4920
|
+
(lane) => enhanceSwimlaneLaneCjkTypography(swimlane.id, lane, options, diagnostics)
|
|
4921
|
+
);
|
|
4922
|
+
return { ...root, lanes };
|
|
4923
|
+
}
|
|
4924
|
+
function enhanceSwimlaneLaneCjkTypography(swimlaneId, lane, options, diagnostics) {
|
|
4925
|
+
return enhanceStyledLabelOwner(
|
|
4926
|
+
lane,
|
|
4927
|
+
["swimlanes", swimlaneId, "lanes", lane.id],
|
|
4928
|
+
options,
|
|
4929
|
+
diagnostics
|
|
4930
|
+
);
|
|
4931
|
+
}
|
|
4932
|
+
function enhanceStyledLabelOwner(owner, path, options, diagnostics) {
|
|
4933
|
+
const text = owner.label?.text;
|
|
4934
|
+
if (text === void 0 || !containsCjk(text)) {
|
|
4935
|
+
return owner;
|
|
4936
|
+
}
|
|
4937
|
+
const typography = cjkTypographyForOwner(owner, options);
|
|
4938
|
+
if (typography.fontFamily === void 0 && typography.fontSize === void 0) {
|
|
4939
|
+
return owner;
|
|
4940
|
+
}
|
|
4941
|
+
const label = owner.label;
|
|
4942
|
+
if (label === void 0) {
|
|
4943
|
+
return owner;
|
|
4944
|
+
}
|
|
4945
|
+
const nextLabel = {
|
|
4946
|
+
...label,
|
|
4947
|
+
metadata: {
|
|
4948
|
+
...metadataObject(label.metadata),
|
|
4949
|
+
cjkTypography: typography
|
|
4950
|
+
}
|
|
4951
|
+
};
|
|
4952
|
+
const nextOwner = { ...owner, label: nextLabel };
|
|
4953
|
+
const maybeStyled = nextOwner;
|
|
4954
|
+
const nextStyle = enhanceCjkStyle(maybeStyled.style, typography);
|
|
4955
|
+
reportCjkTypographyDiagnostics(
|
|
4956
|
+
path,
|
|
4957
|
+
typography,
|
|
4958
|
+
maybeStyled.style,
|
|
4959
|
+
diagnostics
|
|
4960
|
+
);
|
|
4961
|
+
return nextStyle === maybeStyled.style ? nextOwner : { ...nextOwner, style: nextStyle };
|
|
4962
|
+
}
|
|
4963
|
+
function cjkTypographyForOwner(owner, options) {
|
|
4964
|
+
const metadataTypography = labelCjkTypography(owner.label?.metadata);
|
|
4965
|
+
const fontFamily = metadataTypography.fontFamily ?? owner.style?.fontFamily ?? options.fontFamily;
|
|
4966
|
+
const fontSize = boostedCjkFontSize(
|
|
4967
|
+
metadataTypography.fontSize ?? owner.style?.fontSize,
|
|
4968
|
+
options.minFontSize
|
|
4969
|
+
);
|
|
4970
|
+
return {
|
|
4971
|
+
...fontFamily === void 0 ? {} : { fontFamily },
|
|
4972
|
+
...fontSize === void 0 ? {} : { fontSize }
|
|
4973
|
+
};
|
|
4974
|
+
}
|
|
4975
|
+
function labelCjkTypography(metadata) {
|
|
4976
|
+
const metadataRecord = metadataObject(metadata);
|
|
4977
|
+
if (metadataRecord === void 0) {
|
|
4978
|
+
return {};
|
|
4979
|
+
}
|
|
4980
|
+
const value = metadataRecord.cjkTypography;
|
|
4981
|
+
if (value === void 0 || value === null || typeof value !== "object") {
|
|
4982
|
+
return {};
|
|
4983
|
+
}
|
|
4984
|
+
const typography = value;
|
|
4985
|
+
const fontFamily = typeof typography.fontFamily === "string" ? typography.fontFamily : void 0;
|
|
4986
|
+
const fontSize = typeof typography.fontSize === "number" && Number.isFinite(typography.fontSize) && typography.fontSize > 0 ? typography.fontSize : void 0;
|
|
4987
|
+
return {
|
|
4988
|
+
...fontFamily === void 0 ? {} : { fontFamily },
|
|
4989
|
+
...fontSize === void 0 ? {} : { fontSize }
|
|
4990
|
+
};
|
|
4991
|
+
}
|
|
4992
|
+
function metadataObject(metadata) {
|
|
4993
|
+
if (metadata === void 0 || metadata === null || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
4994
|
+
return void 0;
|
|
4995
|
+
}
|
|
4996
|
+
return metadata;
|
|
4997
|
+
}
|
|
4998
|
+
function typographyForLabel(label) {
|
|
4999
|
+
return labelCjkTypography(label?.metadata);
|
|
5000
|
+
}
|
|
5001
|
+
function typographyTextStyle(label, base) {
|
|
5002
|
+
const typography = typographyForLabel(label);
|
|
5003
|
+
return {
|
|
5004
|
+
...base,
|
|
5005
|
+
...typography.fontFamily === void 0 ? {} : { fontFamily: typography.fontFamily },
|
|
5006
|
+
...typography.fontSize === void 0 ? {} : {
|
|
5007
|
+
fontSize: typography.fontSize,
|
|
5008
|
+
lineHeight: Math.max(base.lineHeight ?? 0, typography.fontSize * 1.2)
|
|
5009
|
+
}
|
|
5010
|
+
};
|
|
5011
|
+
}
|
|
5012
|
+
function boostedCjkFontSize(current, minFontSize) {
|
|
5013
|
+
if (minFontSize === void 0) {
|
|
5014
|
+
return current;
|
|
5015
|
+
}
|
|
5016
|
+
if (current === void 0 || current < minFontSize) {
|
|
5017
|
+
return minFontSize;
|
|
5018
|
+
}
|
|
5019
|
+
return current;
|
|
5020
|
+
}
|
|
5021
|
+
function enhanceCjkStyle(style2, typography) {
|
|
5022
|
+
let next = style2;
|
|
5023
|
+
if (typography.fontFamily !== void 0 && next?.fontFamily === void 0) {
|
|
5024
|
+
next = { ...next, fontFamily: typography.fontFamily };
|
|
5025
|
+
}
|
|
5026
|
+
if (typography.fontSize !== void 0 && (next?.fontSize === void 0 || next.fontSize < typography.fontSize)) {
|
|
5027
|
+
next = { ...next, fontSize: typography.fontSize };
|
|
5028
|
+
}
|
|
5029
|
+
return next;
|
|
5030
|
+
}
|
|
5031
|
+
function reportCjkTypographyDiagnostics(path, typography, previousStyle, diagnostics) {
|
|
5032
|
+
if (typography.fontFamily !== void 0 && previousStyle?.fontFamily === void 0) {
|
|
5033
|
+
diagnostics.push({
|
|
5034
|
+
severity: "info",
|
|
5035
|
+
code: "cjk_font_family_applied",
|
|
5036
|
+
message: `Applied CJK font family ${typography.fontFamily}.`,
|
|
5037
|
+
path: [...path, "label", "metadata", "cjkTypography", "fontFamily"],
|
|
5038
|
+
detail: { fontFamily: typography.fontFamily }
|
|
5039
|
+
});
|
|
5040
|
+
}
|
|
5041
|
+
if (typography.fontSize !== void 0 && (previousStyle?.fontSize === void 0 || previousStyle.fontSize < typography.fontSize)) {
|
|
5042
|
+
diagnostics.push({
|
|
5043
|
+
severity: "info",
|
|
5044
|
+
code: "cjk_font_size_boosted",
|
|
5045
|
+
message: `Raised CJK font size to ${typography.fontSize}.`,
|
|
5046
|
+
path: [...path, "label", "metadata", "cjkTypography", "fontSize"],
|
|
5047
|
+
detail: {
|
|
5048
|
+
minFontSize: typography.fontSize,
|
|
5049
|
+
...previousStyle?.fontSize === void 0 ? {} : { previousFontSize: previousStyle.fontSize }
|
|
5050
|
+
}
|
|
5051
|
+
});
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
function containsCjk(value) {
|
|
5055
|
+
return /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u.test(value);
|
|
5056
|
+
}
|
|
5057
|
+
function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottomFlow, nodeBoxes, locks, overlapSpacing, laneGutter) {
|
|
4239
5058
|
const layouts = /* @__PURE__ */ new Map();
|
|
4240
5059
|
const diagnostics = [];
|
|
4241
5060
|
const movedChildIds = /* @__PURE__ */ new Set();
|
|
@@ -4253,7 +5072,8 @@ function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottom
|
|
|
4253
5072
|
nodeBoxes,
|
|
4254
5073
|
locks,
|
|
4255
5074
|
diagnostics,
|
|
4256
|
-
movedChildIds
|
|
5075
|
+
movedChildIds,
|
|
5076
|
+
laneGutter
|
|
4257
5077
|
);
|
|
4258
5078
|
if (layout2 !== void 0) {
|
|
4259
5079
|
layouts.set(swimlane.id, layout2);
|
|
@@ -4268,10 +5088,123 @@ function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottom
|
|
|
4268
5088
|
movedChildIds
|
|
4269
5089
|
)
|
|
4270
5090
|
);
|
|
5091
|
+
if (laneGutter > 0) {
|
|
5092
|
+
diagnostics.push({
|
|
5093
|
+
severity: "info",
|
|
5094
|
+
code: "lane_gutter_applied",
|
|
5095
|
+
message: `Applied ${laneGutter}px gutter between ${layouts.size} contract swimlane lane(s).`,
|
|
5096
|
+
path: ["swimlanes"],
|
|
5097
|
+
detail: { laneGutter, swimlaneCount: layouts.size }
|
|
5098
|
+
});
|
|
5099
|
+
}
|
|
4271
5100
|
}
|
|
4272
5101
|
return { layouts, diagnostics, movedChildIds };
|
|
4273
5102
|
}
|
|
4274
|
-
function
|
|
5103
|
+
function wrapVerticalStackIfNeeded(boxes, nodes, edges, direction, options, diagnostics) {
|
|
5104
|
+
const wrapped = new Map([...boxes].map(([id, box]) => [id, { ...box }]));
|
|
5105
|
+
const maxStackDepth = options.maxStackDepth;
|
|
5106
|
+
if (maxStackDepth === void 0 || maxStackDepth <= 0 || nodes.length <= maxStackDepth) {
|
|
5107
|
+
reportVerticalRunaway(
|
|
5108
|
+
wrapped,
|
|
5109
|
+
nodes,
|
|
5110
|
+
edges,
|
|
5111
|
+
direction,
|
|
5112
|
+
options,
|
|
5113
|
+
diagnostics
|
|
5114
|
+
);
|
|
5115
|
+
return wrapped;
|
|
5116
|
+
}
|
|
5117
|
+
if (edges.length > 0 || !isVerticalRunaway(wrapped, nodes, direction, options)) {
|
|
5118
|
+
reportVerticalRunaway(
|
|
5119
|
+
wrapped,
|
|
5120
|
+
nodes,
|
|
5121
|
+
edges,
|
|
5122
|
+
direction,
|
|
5123
|
+
options,
|
|
5124
|
+
diagnostics
|
|
5125
|
+
);
|
|
5126
|
+
return wrapped;
|
|
5127
|
+
}
|
|
5128
|
+
const ordered = nodes.map((node) => ({ node, box: wrapped.get(node.id) })).filter(
|
|
5129
|
+
(item) => item.box !== void 0
|
|
5130
|
+
).sort((a, b) => {
|
|
5131
|
+
const delta = a.box.y - b.box.y;
|
|
5132
|
+
return delta === 0 ? a.node.id.localeCompare(b.node.id) : delta;
|
|
5133
|
+
});
|
|
5134
|
+
const columns = Math.ceil(ordered.length / maxStackDepth);
|
|
5135
|
+
const horizontalGap = options.overlapSpacing ?? 40;
|
|
5136
|
+
const verticalGap = Math.max(24, horizontalGap / 2);
|
|
5137
|
+
const columnWidths = Array.from(
|
|
5138
|
+
{ length: columns },
|
|
5139
|
+
(_, column) => Math.max(
|
|
5140
|
+
0,
|
|
5141
|
+
...ordered.slice(column * maxStackDepth, (column + 1) * maxStackDepth).map((item) => item.box.width)
|
|
5142
|
+
)
|
|
5143
|
+
);
|
|
5144
|
+
const startX = Math.min(...ordered.map((item) => item.box.x));
|
|
5145
|
+
const startY = Math.min(...ordered.map((item) => item.box.y));
|
|
5146
|
+
let columnX = startX;
|
|
5147
|
+
for (let column = 0; column < columns; column += 1) {
|
|
5148
|
+
let y = startY;
|
|
5149
|
+
const items = ordered.slice(
|
|
5150
|
+
column * maxStackDepth,
|
|
5151
|
+
(column + 1) * maxStackDepth
|
|
5152
|
+
);
|
|
5153
|
+
for (const item of items) {
|
|
5154
|
+
wrapped.set(item.node.id, { ...item.box, x: columnX, y });
|
|
5155
|
+
y += item.box.height + verticalGap;
|
|
5156
|
+
}
|
|
5157
|
+
columnX += (columnWidths[column] ?? 0) + horizontalGap;
|
|
5158
|
+
}
|
|
5159
|
+
diagnostics.push({
|
|
5160
|
+
severity: "warning",
|
|
5161
|
+
code: "vertical_runaway",
|
|
5162
|
+
message: `Single-column layout exceeded maxStackDepth ${maxStackDepth}; wrapped into ${columns} columns.`,
|
|
5163
|
+
path: ["nodes"],
|
|
5164
|
+
detail: { nodeCount: ordered.length, maxStackDepth, columns }
|
|
5165
|
+
});
|
|
5166
|
+
return wrapped;
|
|
5167
|
+
}
|
|
5168
|
+
function reportVerticalRunaway(boxes, nodes, edges, direction, options, diagnostics) {
|
|
5169
|
+
if (!isVerticalRunaway(boxes, nodes, direction, options)) {
|
|
5170
|
+
return;
|
|
5171
|
+
}
|
|
5172
|
+
diagnostics.push({
|
|
5173
|
+
severity: "warning",
|
|
5174
|
+
code: "vertical_runaway",
|
|
5175
|
+
message: "Layout produced a tall vertical stack beyond the preferred aspect ratio.",
|
|
5176
|
+
path: ["nodes"],
|
|
5177
|
+
detail: {
|
|
5178
|
+
nodeCount: nodes.length,
|
|
5179
|
+
edgeCount: edges.length,
|
|
5180
|
+
...options.preferredAspectRatio === void 0 ? {} : { preferredAspectRatio: options.preferredAspectRatio },
|
|
5181
|
+
...options.maxStackDepth === void 0 ? {} : { maxStackDepth: options.maxStackDepth }
|
|
5182
|
+
}
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5185
|
+
function isVerticalRunaway(boxes, nodes, direction, options) {
|
|
5186
|
+
if (options.maxStackDepth === void 0 && options.preferredAspectRatio === void 0) {
|
|
5187
|
+
return false;
|
|
5188
|
+
}
|
|
5189
|
+
if (nodes.length < 2 || direction !== "LR" && direction !== "RL") {
|
|
5190
|
+
return false;
|
|
5191
|
+
}
|
|
5192
|
+
const nodeBoxes = nodes.map((node) => boxes.get(node.id)).filter((box) => box !== void 0);
|
|
5193
|
+
if (nodeBoxes.length < 2) {
|
|
5194
|
+
return false;
|
|
5195
|
+
}
|
|
5196
|
+
const bounds = unionBoxes(nodeBoxes);
|
|
5197
|
+
const aspectRatio = bounds.width <= 0 ? Number.POSITIVE_INFINITY : bounds.height / bounds.width;
|
|
5198
|
+
const preferred = options.preferredAspectRatio ?? 3;
|
|
5199
|
+
if (aspectRatio < preferred) {
|
|
5200
|
+
return false;
|
|
5201
|
+
}
|
|
5202
|
+
const xCenters = nodeBoxes.map((box) => box.x + box.width / 2);
|
|
5203
|
+
const xSpread = Math.max(...xCenters) - Math.min(...xCenters);
|
|
5204
|
+
const maxWidth = Math.max(...nodeBoxes.map((box) => box.width));
|
|
5205
|
+
return xSpread <= Math.max(maxWidth, options.overlapSpacing ?? 40);
|
|
5206
|
+
}
|
|
5207
|
+
function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, locks, diagnostics, movedChildIds, laneGutter) {
|
|
4275
5208
|
const headerHeight = swimlane.headerHeight ?? 28;
|
|
4276
5209
|
const padding = swimlane.padding ?? 16;
|
|
4277
5210
|
const laneBounds = swimlane.lanes.map((lane) => {
|
|
@@ -4295,7 +5228,8 @@ function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes
|
|
|
4295
5228
|
padding,
|
|
4296
5229
|
locks,
|
|
4297
5230
|
diagnostics,
|
|
4298
|
-
movedChildIds
|
|
5231
|
+
movedChildIds,
|
|
5232
|
+
laneGutter
|
|
4299
5233
|
);
|
|
4300
5234
|
}
|
|
4301
5235
|
return applyHorizontalSwimlaneContract(
|
|
@@ -4306,10 +5240,11 @@ function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes
|
|
|
4306
5240
|
padding,
|
|
4307
5241
|
locks,
|
|
4308
5242
|
diagnostics,
|
|
4309
|
-
movedChildIds
|
|
5243
|
+
movedChildIds,
|
|
5244
|
+
laneGutter
|
|
4310
5245
|
);
|
|
4311
5246
|
}
|
|
4312
|
-
function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds) {
|
|
5247
|
+
function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds, laneGutter) {
|
|
4313
5248
|
const populatedBounds = laneBounds.filter(
|
|
4314
5249
|
(box) => box !== void 0
|
|
4315
5250
|
);
|
|
@@ -4328,6 +5263,7 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
4328
5263
|
const rankSpacing = Math.max(96, maxRankStackHeight + padding);
|
|
4329
5264
|
const contentHeight = maxRank === 0 ? maxChildHeight : maxRankStackHeight + maxRank * rankSpacing;
|
|
4330
5265
|
const slotWidth = Math.max(...populatedBounds.map((box) => box.width)) + padding * 2;
|
|
5266
|
+
const laneStep = slotWidth + laneGutter;
|
|
4331
5267
|
const laneContentTop = top + headerHeight + padding;
|
|
4332
5268
|
for (let index = 0; index < swimlane.lanes.length; index += 1) {
|
|
4333
5269
|
const lane = swimlane.lanes[index];
|
|
@@ -4336,7 +5272,7 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
4336
5272
|
continue;
|
|
4337
5273
|
}
|
|
4338
5274
|
const target = {
|
|
4339
|
-
x: left +
|
|
5275
|
+
x: left + laneStep * index + padding,
|
|
4340
5276
|
y: laneContentTop
|
|
4341
5277
|
};
|
|
4342
5278
|
if (maxRank === 0) {
|
|
@@ -4372,11 +5308,12 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
4372
5308
|
box: {
|
|
4373
5309
|
x: left,
|
|
4374
5310
|
y: top,
|
|
4375
|
-
width:
|
|
5311
|
+
width: laneStep * (swimlane.lanes.length - 1) + slotWidth,
|
|
4376
5312
|
height: contentHeight + padding * 2 + headerHeight
|
|
4377
5313
|
},
|
|
4378
5314
|
slotWidth,
|
|
4379
|
-
slotHeight: contentHeight + padding * 2 + headerHeight
|
|
5315
|
+
slotHeight: contentHeight + padding * 2 + headerHeight,
|
|
5316
|
+
laneStep
|
|
4380
5317
|
};
|
|
4381
5318
|
}
|
|
4382
5319
|
function isTopToBottomReadingDirection(value) {
|
|
@@ -4521,7 +5458,7 @@ function rankStacks(childIds, nodeBoxes, flowRanks) {
|
|
|
4521
5458
|
}
|
|
4522
5459
|
return stacks;
|
|
4523
5460
|
}
|
|
4524
|
-
function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds) {
|
|
5461
|
+
function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds, laneGutter) {
|
|
4525
5462
|
const populatedBounds = laneBounds.filter(
|
|
4526
5463
|
(box) => box !== void 0
|
|
4527
5464
|
);
|
|
@@ -4529,6 +5466,7 @@ function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, header
|
|
|
4529
5466
|
const left = Math.min(...populatedBounds.map((box) => box.x));
|
|
4530
5467
|
const slotWidth = Math.max(...populatedBounds.map((box) => box.width)) + headerHeight + padding * 2;
|
|
4531
5468
|
const slotHeight = Math.max(...populatedBounds.map((box) => box.height)) + padding * 2;
|
|
5469
|
+
const laneStep = slotHeight + laneGutter;
|
|
4532
5470
|
for (let index = 0; index < swimlane.lanes.length; index += 1) {
|
|
4533
5471
|
const lane = swimlane.lanes[index];
|
|
4534
5472
|
const bounds = laneBounds[index];
|
|
@@ -4537,7 +5475,7 @@ function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, header
|
|
|
4537
5475
|
}
|
|
4538
5476
|
const target = {
|
|
4539
5477
|
x: left + headerHeight + padding,
|
|
4540
|
-
y: top +
|
|
5478
|
+
y: top + laneStep * index + padding
|
|
4541
5479
|
};
|
|
4542
5480
|
moveLaneChildren(
|
|
4543
5481
|
lane.children,
|
|
@@ -4556,10 +5494,11 @@ function applyHorizontalSwimlaneContract(swimlane, nodeBoxes, laneBounds, header
|
|
|
4556
5494
|
x: left,
|
|
4557
5495
|
y: top,
|
|
4558
5496
|
width: slotWidth,
|
|
4559
|
-
height:
|
|
5497
|
+
height: laneStep * (swimlane.lanes.length - 1) + slotHeight
|
|
4560
5498
|
},
|
|
4561
5499
|
slotWidth,
|
|
4562
|
-
slotHeight
|
|
5500
|
+
slotHeight,
|
|
5501
|
+
laneStep
|
|
4563
5502
|
};
|
|
4564
5503
|
}
|
|
4565
5504
|
function moveLaneChildren(childIds, nodeBoxes, locks, diagnostics, movedChildIds, offset) {
|
|
@@ -4888,7 +5827,10 @@ function coordinatePorts(node, nodeBox, portShifting) {
|
|
|
4888
5827
|
}
|
|
4889
5828
|
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
4890
5829
|
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
4891
|
-
const
|
|
5830
|
+
const requestedSpacing = portShifting?.spacing ?? 24;
|
|
5831
|
+
const maxOffset = side === "left" || side === "right" ? nodeBox.height / 2 : nodeBox.width / 2;
|
|
5832
|
+
const availableSpan = 2 * maxOffset;
|
|
5833
|
+
const spacing = shiftingEnabled && count > 1 ? Math.min(requestedSpacing, availableSpan / (count - 1)) : requestedSpacing;
|
|
4892
5834
|
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
4893
5835
|
switch (side) {
|
|
4894
5836
|
case "left":
|
|
@@ -4943,13 +5885,13 @@ function coordinateSwimlanes(swimlanes, nodeBoxes, layouts) {
|
|
|
4943
5885
|
if (layout2 === "contract" && contractLayout !== void 0) {
|
|
4944
5886
|
const lanes2 = swimlane.lanes.map((lane, index) => {
|
|
4945
5887
|
const box = swimlane.orientation === "vertical" ? {
|
|
4946
|
-
x: contractLayout.box.x + contractLayout.
|
|
5888
|
+
x: contractLayout.box.x + contractLayout.laneStep * index,
|
|
4947
5889
|
y: contractLayout.box.y,
|
|
4948
5890
|
width: contractLayout.slotWidth,
|
|
4949
5891
|
height: contractLayout.box.height
|
|
4950
5892
|
} : {
|
|
4951
5893
|
x: contractLayout.box.x,
|
|
4952
|
-
y: contractLayout.box.y + contractLayout.
|
|
5894
|
+
y: contractLayout.box.y + contractLayout.laneStep * index,
|
|
4953
5895
|
width: contractLayout.box.width,
|
|
4954
5896
|
height: contractLayout.slotHeight
|
|
4955
5897
|
};
|
|
@@ -5050,17 +5992,19 @@ function coordinateSwimlanes(swimlanes, nodeBoxes, layouts) {
|
|
|
5050
5992
|
});
|
|
5051
5993
|
}
|
|
5052
5994
|
function coordinateFrame(frame, contentBounds) {
|
|
5053
|
-
const padding =
|
|
5054
|
-
const titleHeight = 28;
|
|
5995
|
+
const padding = framePadding(frame.padding);
|
|
5996
|
+
const titleHeight = frame.headerHeight ?? 28;
|
|
5055
5997
|
const titleWidth = Math.max(180, frame.titleTab.length * 7);
|
|
5056
5998
|
const box = {
|
|
5057
|
-
x: contentBounds.x - padding,
|
|
5058
|
-
y: contentBounds.y - padding - titleHeight,
|
|
5059
|
-
width: contentBounds.width + padding
|
|
5060
|
-
height: contentBounds.height + padding
|
|
5999
|
+
x: contentBounds.x - padding.left,
|
|
6000
|
+
y: contentBounds.y - padding.top - titleHeight,
|
|
6001
|
+
width: contentBounds.width + padding.left + padding.right,
|
|
6002
|
+
height: contentBounds.height + padding.top + padding.bottom + titleHeight
|
|
5061
6003
|
};
|
|
5062
6004
|
return {
|
|
5063
6005
|
...frame,
|
|
6006
|
+
headerHeight: titleHeight,
|
|
6007
|
+
padding: frame.padding ?? 32,
|
|
5064
6008
|
box,
|
|
5065
6009
|
titleBox: {
|
|
5066
6010
|
x: box.x,
|
|
@@ -5070,6 +6014,9 @@ function coordinateFrame(frame, contentBounds) {
|
|
|
5070
6014
|
}
|
|
5071
6015
|
};
|
|
5072
6016
|
}
|
|
6017
|
+
function framePadding(value) {
|
|
6018
|
+
return normalizeInsets(value ?? 32);
|
|
6019
|
+
}
|
|
5073
6020
|
function expand(box, padding, titleSize) {
|
|
5074
6021
|
return {
|
|
5075
6022
|
x: box.x - padding,
|
|
@@ -5165,10 +6112,16 @@ function edgeBounds(edges) {
|
|
|
5165
6112
|
if (edge.points.length === 0) {
|
|
5166
6113
|
return [];
|
|
5167
6114
|
}
|
|
5168
|
-
const
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
6115
|
+
const extraPoints = [];
|
|
6116
|
+
if (edge.points.length >= 2) {
|
|
6117
|
+
const arrowhead = computeArrowhead(edge.points);
|
|
6118
|
+
extraPoints.push(arrowhead.tip, arrowhead.left, arrowhead.right);
|
|
6119
|
+
}
|
|
6120
|
+
const allPoints = [...edge.points, ...extraPoints];
|
|
6121
|
+
const minX = Math.min(...allPoints.map((point2) => point2.x));
|
|
6122
|
+
const minY = Math.min(...allPoints.map((point2) => point2.y));
|
|
6123
|
+
const maxX = Math.max(...allPoints.map((point2) => point2.x));
|
|
6124
|
+
const maxY = Math.max(...allPoints.map((point2) => point2.y));
|
|
5172
6125
|
return [
|
|
5173
6126
|
{
|
|
5174
6127
|
x: minX,
|
|
@@ -5565,6 +6518,7 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5565
6518
|
ownerId: node.id,
|
|
5566
6519
|
surfaceKind: "node-label",
|
|
5567
6520
|
layout: layout2,
|
|
6521
|
+
typography: typographyForLabel(node.label),
|
|
5568
6522
|
anchor: node.box
|
|
5569
6523
|
})
|
|
5570
6524
|
);
|
|
@@ -5580,6 +6534,7 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5580
6534
|
ownerId: group.id,
|
|
5581
6535
|
surfaceKind: "group-label",
|
|
5582
6536
|
layout: layout2,
|
|
6537
|
+
typography: typographyForLabel(group.label),
|
|
5583
6538
|
anchor: group.box
|
|
5584
6539
|
})
|
|
5585
6540
|
);
|
|
@@ -5592,7 +6547,11 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5592
6547
|
const layout2 = fitLabel(
|
|
5593
6548
|
port.label.text,
|
|
5594
6549
|
{
|
|
5595
|
-
font:
|
|
6550
|
+
font: typographyTextStyle(port.label, {
|
|
6551
|
+
fontFamily: "Arial",
|
|
6552
|
+
fontSize: 10,
|
|
6553
|
+
lineHeight: 12
|
|
6554
|
+
}),
|
|
5596
6555
|
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
5597
6556
|
minSize: { width: 0, height: 0 },
|
|
5598
6557
|
maxWidth: 160
|
|
@@ -5604,6 +6563,7 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5604
6563
|
ownerId: `${node.id}.${port.id}`,
|
|
5605
6564
|
surfaceKind: "port-label",
|
|
5606
6565
|
layout: layout2,
|
|
6566
|
+
typography: typographyForLabel(port.label),
|
|
5607
6567
|
anchor: portLabelBox(port)
|
|
5608
6568
|
})
|
|
5609
6569
|
);
|
|
@@ -5654,7 +6614,11 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5654
6614
|
const layout2 = fitLabel(
|
|
5655
6615
|
lane.label.text,
|
|
5656
6616
|
{
|
|
5657
|
-
font:
|
|
6617
|
+
font: typographyTextStyle(lane.label, {
|
|
6618
|
+
fontFamily: "Arial",
|
|
6619
|
+
fontSize: 12,
|
|
6620
|
+
lineHeight: 14
|
|
6621
|
+
}),
|
|
5658
6622
|
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
5659
6623
|
minSize: { width: 0, height: 0 },
|
|
5660
6624
|
maxWidth: swimlane.orientation === "horizontal" ? labelBox.height : labelBox.width
|
|
@@ -5666,6 +6630,7 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5666
6630
|
ownerId: `${swimlane.id}.${lane.id}`,
|
|
5667
6631
|
surfaceKind: "swimlane-label",
|
|
5668
6632
|
layout: layout2,
|
|
6633
|
+
typography: typographyForLabel(lane.label),
|
|
5669
6634
|
anchor: labelBox
|
|
5670
6635
|
})
|
|
5671
6636
|
);
|
|
@@ -5673,9 +6638,10 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
5673
6638
|
}
|
|
5674
6639
|
return annotations;
|
|
5675
6640
|
}
|
|
5676
|
-
function coordinateEdgeTextAnnotations(edges, textMeasurer) {
|
|
6641
|
+
function coordinateEdgeTextAnnotations(edges, obstacleBoxes, textMeasurer) {
|
|
5677
6642
|
const measurer = textMeasurer ?? createDefaultTextMeasurer();
|
|
5678
6643
|
const annotations = [];
|
|
6644
|
+
const placedLabelBoxes = [];
|
|
5679
6645
|
for (const edge of edges) {
|
|
5680
6646
|
if (edge.label?.text === void 0) {
|
|
5681
6647
|
continue;
|
|
@@ -5683,19 +6649,37 @@ function coordinateEdgeTextAnnotations(edges, textMeasurer) {
|
|
|
5683
6649
|
const layout2 = fitLabel(
|
|
5684
6650
|
edge.label.text,
|
|
5685
6651
|
{
|
|
5686
|
-
font:
|
|
6652
|
+
font: typographyTextStyle(edge.label, {
|
|
6653
|
+
fontFamily: "Arial",
|
|
6654
|
+
fontSize: 12,
|
|
6655
|
+
lineHeight: 14
|
|
6656
|
+
}),
|
|
5687
6657
|
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
5688
6658
|
minSize: { width: 0, height: 0 },
|
|
5689
6659
|
maxWidth: 200
|
|
5690
6660
|
},
|
|
5691
6661
|
measurer
|
|
5692
6662
|
);
|
|
6663
|
+
const center = edgeLabelAnchor(
|
|
6664
|
+
edge,
|
|
6665
|
+
layout2,
|
|
6666
|
+
edges,
|
|
6667
|
+
obstacleBoxes,
|
|
6668
|
+
placedLabelBoxes
|
|
6669
|
+
);
|
|
6670
|
+
placedLabelBoxes.push({
|
|
6671
|
+
x: center.x - layout2.box.width / 2,
|
|
6672
|
+
y: center.y - layout2.box.height / 2,
|
|
6673
|
+
width: layout2.box.width,
|
|
6674
|
+
height: layout2.box.height
|
|
6675
|
+
});
|
|
5693
6676
|
annotations.push(
|
|
5694
6677
|
buildCenteredTextAnnotation({
|
|
5695
6678
|
ownerId: edge.id,
|
|
5696
6679
|
surfaceKind: "edge-label",
|
|
5697
6680
|
layout: layout2,
|
|
5698
|
-
|
|
6681
|
+
typography: typographyForLabel(edge.label),
|
|
6682
|
+
center
|
|
5699
6683
|
})
|
|
5700
6684
|
);
|
|
5701
6685
|
}
|
|
@@ -5734,7 +6718,8 @@ function buildTextAnnotation(input) {
|
|
|
5734
6718
|
anchor: input.anchor,
|
|
5735
6719
|
paddings: input.layout.padding,
|
|
5736
6720
|
lines: input.layout.lines,
|
|
5737
|
-
|
|
6721
|
+
fontFamily: input.typography?.fontFamily ?? normalizeOutputFontFamily(input.layout.font),
|
|
6722
|
+
fontSize: input.typography?.fontSize ?? input.layout.font.fontSize,
|
|
5738
6723
|
textBackend: input.layout.textBackend
|
|
5739
6724
|
};
|
|
5740
6725
|
}
|
|
@@ -5744,6 +6729,7 @@ function buildAnchorCenteredTextAnnotation(input) {
|
|
|
5744
6729
|
surfaceKind: input.surfaceKind,
|
|
5745
6730
|
...input.surfaceIndex === void 0 ? {} : { surfaceIndex: input.surfaceIndex },
|
|
5746
6731
|
layout: input.layout,
|
|
6732
|
+
...input.typography === void 0 ? {} : { typography: input.typography },
|
|
5747
6733
|
center: {
|
|
5748
6734
|
x: input.anchor.x + input.anchor.width / 2,
|
|
5749
6735
|
y: input.anchor.y + input.anchor.height / 2
|
|
@@ -5766,10 +6752,14 @@ function buildCenteredTextAnnotation(input) {
|
|
|
5766
6752
|
anchor: input.anchor ?? input.center,
|
|
5767
6753
|
paddings: input.layout.padding,
|
|
5768
6754
|
lines: input.layout.lines,
|
|
5769
|
-
|
|
6755
|
+
fontFamily: input.typography?.fontFamily ?? normalizeOutputFontFamily(input.layout.font),
|
|
6756
|
+
fontSize: input.typography?.fontSize ?? input.layout.font.fontSize,
|
|
5770
6757
|
textBackend: input.layout.textBackend
|
|
5771
6758
|
};
|
|
5772
6759
|
}
|
|
6760
|
+
function normalizeOutputFontFamily(font) {
|
|
6761
|
+
return font.fontFamily === "Arial" ? "Arial, sans-serif" : font.fontFamily;
|
|
6762
|
+
}
|
|
5773
6763
|
function reportTextAnnotationCollisions(annotations) {
|
|
5774
6764
|
const diagnostics = [];
|
|
5775
6765
|
const relevantAnnotations = annotations.filter(
|
|
@@ -5959,12 +6949,16 @@ function fallbackLabelLayout(text) {
|
|
|
5959
6949
|
diagnostics: []
|
|
5960
6950
|
};
|
|
5961
6951
|
}
|
|
5962
|
-
function edgeLabelAnchor(edge, layout2, edges) {
|
|
6952
|
+
function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes) {
|
|
5963
6953
|
const placement = labelPlacementOnPolyline2(edge.points);
|
|
5964
6954
|
if (placement === void 0) {
|
|
5965
6955
|
return { x: 0, y: 0 };
|
|
5966
6956
|
}
|
|
5967
|
-
for (const candidate of edgeLabelAnchorCandidates(
|
|
6957
|
+
for (const candidate of edgeLabelAnchorCandidates(
|
|
6958
|
+
edge.points,
|
|
6959
|
+
placement,
|
|
6960
|
+
layout2
|
|
6961
|
+
)) {
|
|
5968
6962
|
const labelBox = {
|
|
5969
6963
|
x: candidate.x - layout2.box.width / 2,
|
|
5970
6964
|
y: candidate.y - layout2.box.height / 2,
|
|
@@ -5977,36 +6971,55 @@ function edgeLabelAnchor(edge, layout2, edges) {
|
|
|
5977
6971
|
const crossesOtherRoute = edges.some(
|
|
5978
6972
|
(other) => other.id !== edge.id && routeIntersectsTextBox(other.points, labelBox)
|
|
5979
6973
|
);
|
|
5980
|
-
if (
|
|
6974
|
+
if (crossesOtherRoute) {
|
|
6975
|
+
continue;
|
|
6976
|
+
}
|
|
6977
|
+
const overlapsNode = obstacleBoxes.some(
|
|
6978
|
+
(box) => intersectsAabb(labelBox, box)
|
|
6979
|
+
);
|
|
6980
|
+
if (overlapsNode) {
|
|
6981
|
+
continue;
|
|
6982
|
+
}
|
|
6983
|
+
const overlapsPlacedLabel = placedLabelBoxes.some(
|
|
6984
|
+
(box) => intersectsAabb(labelBox, box)
|
|
6985
|
+
);
|
|
6986
|
+
if (!overlapsPlacedLabel) {
|
|
5981
6987
|
return candidate;
|
|
5982
6988
|
}
|
|
5983
6989
|
}
|
|
5984
6990
|
return placement;
|
|
5985
6991
|
}
|
|
5986
|
-
function edgeLabelAnchorCandidates(points, placement) {
|
|
6992
|
+
function edgeLabelAnchorCandidates(points, placement, layout2) {
|
|
5987
6993
|
const segment = labelSegmentOnPolyline(points);
|
|
5988
6994
|
if (segment === void 0) {
|
|
5989
6995
|
return [placement];
|
|
5990
6996
|
}
|
|
6997
|
+
const candidates = [placement];
|
|
5991
6998
|
if (segment.start.y === segment.end.y) {
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
6999
|
+
const needed = layout2.box.height / 2 + EDGE_LABEL_CLEARANCE;
|
|
7000
|
+
const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
|
|
7001
|
+
for (let step = 1; step <= maxSteps; step += 1) {
|
|
7002
|
+
const offset = EDGE_LABEL_CLEARANCE * step;
|
|
7003
|
+
candidates.push(
|
|
7004
|
+
{ x: placement.x, y: placement.y - offset },
|
|
7005
|
+
{ x: placement.x, y: placement.y + offset }
|
|
7006
|
+
);
|
|
7007
|
+
}
|
|
7008
|
+
return candidates;
|
|
5999
7009
|
}
|
|
6000
7010
|
if (segment.start.x === segment.end.x) {
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
7011
|
+
const needed = layout2.box.width / 2 + EDGE_LABEL_CLEARANCE;
|
|
7012
|
+
const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
|
|
7013
|
+
for (let step = 1; step <= maxSteps; step += 1) {
|
|
7014
|
+
const offset = EDGE_LABEL_CLEARANCE * step;
|
|
7015
|
+
candidates.push(
|
|
7016
|
+
{ x: placement.x + offset, y: placement.y },
|
|
7017
|
+
{ x: placement.x - offset, y: placement.y }
|
|
7018
|
+
);
|
|
7019
|
+
}
|
|
7020
|
+
return candidates;
|
|
6008
7021
|
}
|
|
6009
|
-
return
|
|
7022
|
+
return candidates;
|
|
6010
7023
|
}
|
|
6011
7024
|
function labelPlacementOnPolyline2(points) {
|
|
6012
7025
|
return labelSegmentOnPolyline(points)?.placement;
|
|
@@ -6097,8 +7110,26 @@ function portGeometry(nodeGeometry, port) {
|
|
|
6097
7110
|
obstacleBox: port.box
|
|
6098
7111
|
};
|
|
6099
7112
|
}
|
|
6100
|
-
function
|
|
6101
|
-
|
|
7113
|
+
function stableUniqueById(items, diagnostics, pathRoot, code) {
|
|
7114
|
+
const firstById = /* @__PURE__ */ new Map();
|
|
7115
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
7116
|
+
const item = items[index];
|
|
7117
|
+
if (item === void 0) {
|
|
7118
|
+
continue;
|
|
7119
|
+
}
|
|
7120
|
+
if (firstById.has(item.id)) {
|
|
7121
|
+
diagnostics.push({
|
|
7122
|
+
severity: "error",
|
|
7123
|
+
code,
|
|
7124
|
+
message: `Duplicate ${pathRoot.slice(0, -1)} id ${item.id} was ignored; first occurrence was kept.`,
|
|
7125
|
+
path: [pathRoot, index, "id"],
|
|
7126
|
+
detail: { id: item.id, duplicateIndex: index }
|
|
7127
|
+
});
|
|
7128
|
+
continue;
|
|
7129
|
+
}
|
|
7130
|
+
firstById.set(item.id, item);
|
|
7131
|
+
}
|
|
7132
|
+
return [...firstById.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
6102
7133
|
}
|
|
6103
7134
|
function stableByConstraintId(items) {
|
|
6104
7135
|
return [...items].sort(
|
|
@@ -6347,6 +7378,6 @@ function isPointLikeRecord(value) {
|
|
|
6347
7378
|
return isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number";
|
|
6348
7379
|
}
|
|
6349
7380
|
|
|
6350
|
-
export { DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DeterministicTextMeasurer, LabelFitter, PretextTextMeasurer, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
|
|
7381
|
+
export { DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DeterministicTextMeasurer, LabelFitter, PretextTextMeasurer, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, solveDiagramSafe, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
|
|
6351
7382
|
//# sourceMappingURL=index.js.map
|
|
6352
7383
|
//# sourceMappingURL=index.js.map
|