@crazyhappyone/auto-graph 0.1.0 → 0.1.2
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 +352 -31
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +352 -31
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +353 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +353 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3144,6 +3144,15 @@ function fitDslLabel(label, measurer) {
|
|
|
3144
3144
|
function point(value) {
|
|
3145
3145
|
return { x: value.x, y: value.y };
|
|
3146
3146
|
}
|
|
3147
|
+
|
|
3148
|
+
// src/ir/diagnostics.ts
|
|
3149
|
+
var DELIVERABILITY_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
|
|
3150
|
+
"constraints.locked-target-not-moved",
|
|
3151
|
+
"routing.evidence.crossing_forbidden",
|
|
3152
|
+
"routing.obstacle.unavoidable",
|
|
3153
|
+
"route_obstacle_fallback",
|
|
3154
|
+
"routing.text-clearance.unresolved"
|
|
3155
|
+
]);
|
|
3147
3156
|
var DEFAULT_OPTIONS = {
|
|
3148
3157
|
nodesep: 80,
|
|
3149
3158
|
ranksep: 100,
|
|
@@ -3342,6 +3351,51 @@ function routeEdge(input) {
|
|
|
3342
3351
|
)
|
|
3343
3352
|
);
|
|
3344
3353
|
if (hardClearCandidate !== void 0) {
|
|
3354
|
+
let bestPoints2 = hardClearCandidate.points;
|
|
3355
|
+
if (input.kind === "obstacle-avoiding") {
|
|
3356
|
+
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
3357
|
+
for (const candidate of candidateRoutes) {
|
|
3358
|
+
if (routeCrossesBoxes(candidate.points, hardObstacles) || routeIntersectsEndpointInteriors(
|
|
3359
|
+
candidate.points,
|
|
3360
|
+
candidate.endpointObstacles
|
|
3361
|
+
)) {
|
|
3362
|
+
continue;
|
|
3363
|
+
}
|
|
3364
|
+
const rerouted2 = greedyRerouteAroundObstacles(
|
|
3365
|
+
candidate.points,
|
|
3366
|
+
allObstacles,
|
|
3367
|
+
3
|
|
3368
|
+
);
|
|
3369
|
+
if (!routeCrossesBoxes(rerouted2, allObstacles) && !routeIntersectsEndpointInteriors(
|
|
3370
|
+
rerouted2,
|
|
3371
|
+
candidate.endpointObstacles
|
|
3372
|
+
)) {
|
|
3373
|
+
return {
|
|
3374
|
+
points: finalizeRoute(
|
|
3375
|
+
rerouted2,
|
|
3376
|
+
softObstacles,
|
|
3377
|
+
hardObstacles,
|
|
3378
|
+
diagnostics
|
|
3379
|
+
),
|
|
3380
|
+
diagnostics
|
|
3381
|
+
};
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
const rerouted = greedyRerouteAroundObstacles(
|
|
3385
|
+
bestPoints2,
|
|
3386
|
+
allObstacles,
|
|
3387
|
+
3
|
|
3388
|
+
);
|
|
3389
|
+
const reroutedAvoidsEndpointInteriors = !routeIntersectsEndpointInteriors(
|
|
3390
|
+
rerouted,
|
|
3391
|
+
hardClearCandidate.endpointObstacles
|
|
3392
|
+
);
|
|
3393
|
+
if (reroutedAvoidsEndpointInteriors) {
|
|
3394
|
+
if (routeCrossesBoxes(rerouted, hardObstacles) && !routeCrossesBoxes(bestPoints2, hardObstacles)) ; else {
|
|
3395
|
+
bestPoints2 = rerouted;
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3345
3399
|
diagnostics.push({
|
|
3346
3400
|
severity: "warning",
|
|
3347
3401
|
code: "routing.obstacle.unavoidable",
|
|
@@ -3349,7 +3403,7 @@ function routeEdge(input) {
|
|
|
3349
3403
|
});
|
|
3350
3404
|
return {
|
|
3351
3405
|
points: finalizeRoute(
|
|
3352
|
-
|
|
3406
|
+
bestPoints2,
|
|
3353
3407
|
softObstacles,
|
|
3354
3408
|
hardObstacles,
|
|
3355
3409
|
diagnostics
|
|
@@ -3358,6 +3412,33 @@ function routeEdge(input) {
|
|
|
3358
3412
|
};
|
|
3359
3413
|
}
|
|
3360
3414
|
if (hardObstacles.length > 0) {
|
|
3415
|
+
let bestPoints2 = candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors);
|
|
3416
|
+
if (input.kind === "obstacle-avoiding") {
|
|
3417
|
+
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
3418
|
+
for (const candidate of candidateRoutes) {
|
|
3419
|
+
const rerouted = greedyRerouteAroundObstacles(
|
|
3420
|
+
candidate.points,
|
|
3421
|
+
allObstacles,
|
|
3422
|
+
5
|
|
3423
|
+
);
|
|
3424
|
+
if (!routeCrossesBoxes(rerouted, allObstacles)) {
|
|
3425
|
+
return {
|
|
3426
|
+
points: finalizeRoute(
|
|
3427
|
+
rerouted,
|
|
3428
|
+
softObstacles,
|
|
3429
|
+
hardObstacles,
|
|
3430
|
+
diagnostics
|
|
3431
|
+
),
|
|
3432
|
+
diagnostics
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
bestPoints2 = greedyRerouteAroundObstacles(
|
|
3437
|
+
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
|
|
3438
|
+
allObstacles,
|
|
3439
|
+
5
|
|
3440
|
+
);
|
|
3441
|
+
}
|
|
3361
3442
|
diagnostics.push({
|
|
3362
3443
|
severity: "error",
|
|
3363
3444
|
code: "routing.evidence.crossing_forbidden",
|
|
@@ -3365,7 +3446,7 @@ function routeEdge(input) {
|
|
|
3365
3446
|
});
|
|
3366
3447
|
return {
|
|
3367
3448
|
points: finalizeRoute(
|
|
3368
|
-
|
|
3449
|
+
bestPoints2,
|
|
3369
3450
|
softObstacles,
|
|
3370
3451
|
hardObstacles,
|
|
3371
3452
|
diagnostics
|
|
@@ -3373,6 +3454,33 @@ function routeEdge(input) {
|
|
|
3373
3454
|
diagnostics
|
|
3374
3455
|
};
|
|
3375
3456
|
}
|
|
3457
|
+
let bestPoints = candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors);
|
|
3458
|
+
if (input.kind === "obstacle-avoiding") {
|
|
3459
|
+
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
3460
|
+
for (const candidate of candidateRoutes) {
|
|
3461
|
+
const rerouted = greedyRerouteAroundObstacles(
|
|
3462
|
+
candidate.points,
|
|
3463
|
+
allObstacles,
|
|
3464
|
+
5
|
|
3465
|
+
);
|
|
3466
|
+
if (!routeCrossesBoxes(rerouted, allObstacles)) {
|
|
3467
|
+
return {
|
|
3468
|
+
points: finalizeRoute(
|
|
3469
|
+
rerouted,
|
|
3470
|
+
softObstacles,
|
|
3471
|
+
hardObstacles,
|
|
3472
|
+
diagnostics
|
|
3473
|
+
),
|
|
3474
|
+
diagnostics
|
|
3475
|
+
};
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
bestPoints = greedyRerouteAroundObstacles(
|
|
3479
|
+
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
|
|
3480
|
+
allObstacles,
|
|
3481
|
+
5
|
|
3482
|
+
);
|
|
3483
|
+
}
|
|
3376
3484
|
diagnostics.push({
|
|
3377
3485
|
severity: "warning",
|
|
3378
3486
|
code: "routing.obstacle.unavoidable",
|
|
@@ -3380,7 +3488,7 @@ function routeEdge(input) {
|
|
|
3380
3488
|
});
|
|
3381
3489
|
return {
|
|
3382
3490
|
points: finalizeRoute(
|
|
3383
|
-
|
|
3491
|
+
bestPoints,
|
|
3384
3492
|
softObstacles,
|
|
3385
3493
|
hardObstacles,
|
|
3386
3494
|
diagnostics
|
|
@@ -3390,21 +3498,29 @@ function routeEdge(input) {
|
|
|
3390
3498
|
}
|
|
3391
3499
|
function finalizeRoute(points, softObstacles, hardObstacles, diagnostics) {
|
|
3392
3500
|
const simplified = simplifyRoute(points);
|
|
3501
|
+
if (simplified.length >= 3) {
|
|
3502
|
+
return simplified;
|
|
3503
|
+
}
|
|
3393
3504
|
const crossesHardObstacles = routeCrossesBoxes(simplified, hardObstacles);
|
|
3394
3505
|
const crossesSoftObstacles = routeCrossesBoxes(simplified, softObstacles);
|
|
3395
|
-
if (
|
|
3506
|
+
if (!crossesHardObstacles && !crossesSoftObstacles) {
|
|
3507
|
+
return simplified;
|
|
3508
|
+
}
|
|
3509
|
+
const expanded = expandFallbackRoute(simplified, [
|
|
3510
|
+
...softObstacles,
|
|
3511
|
+
...hardObstacles
|
|
3512
|
+
]);
|
|
3513
|
+
const expandedCrossesHard = routeCrossesBoxes(expanded, hardObstacles);
|
|
3514
|
+
const expandedCrossesSoft = routeCrossesBoxes(expanded, softObstacles);
|
|
3515
|
+
if (expandedCrossesHard || expandedCrossesSoft) {
|
|
3396
3516
|
diagnostics.push({
|
|
3397
|
-
severity:
|
|
3517
|
+
severity: expandedCrossesHard ? "error" : "warning",
|
|
3398
3518
|
code: "route_obstacle_fallback",
|
|
3399
3519
|
message: "Obstacle-aware routing fell back to fewer than three route points.",
|
|
3400
3520
|
detail: { pointCount: simplified.length }
|
|
3401
3521
|
});
|
|
3402
|
-
return expandFallbackRoute(simplified, [
|
|
3403
|
-
...softObstacles,
|
|
3404
|
-
...hardObstacles
|
|
3405
|
-
]);
|
|
3406
3522
|
}
|
|
3407
|
-
return
|
|
3523
|
+
return expanded;
|
|
3408
3524
|
}
|
|
3409
3525
|
function expandFallbackRoute(points, obstacles) {
|
|
3410
3526
|
if (points.length !== 2) {
|
|
@@ -3435,12 +3551,12 @@ function expandFallbackRoute(points, obstacles) {
|
|
|
3435
3551
|
const hv = diagonalDetourHV(source, target, obstacles);
|
|
3436
3552
|
const vh = diagonalDetourVH(source, target, obstacles);
|
|
3437
3553
|
const viable = [hv, vh].filter((c) => !routeCrossesBoxes(c, obstacles));
|
|
3438
|
-
|
|
3554
|
+
const [firstViable, ...remainingViable] = viable;
|
|
3555
|
+
if (firstViable !== void 0) {
|
|
3439
3556
|
const directLen = Math.hypot(target.x - source.x, target.y - source.y);
|
|
3440
|
-
let best =
|
|
3441
|
-
for (
|
|
3442
|
-
|
|
3443
|
-
if (cand !== void 0 && pathLength(cand) - directLen < pathLength(best) - directLen) {
|
|
3557
|
+
let best = firstViable;
|
|
3558
|
+
for (const cand of remainingViable) {
|
|
3559
|
+
if (pathLength(cand) - directLen < pathLength(best) - directLen) {
|
|
3444
3560
|
best = cand;
|
|
3445
3561
|
}
|
|
3446
3562
|
}
|
|
@@ -3530,6 +3646,70 @@ function insetBox(box, margin) {
|
|
|
3530
3646
|
height: box.height - margin * 2
|
|
3531
3647
|
};
|
|
3532
3648
|
}
|
|
3649
|
+
function greedyRerouteAroundObstacles(points, obstacles, maxIterations) {
|
|
3650
|
+
let current = [...points];
|
|
3651
|
+
for (let iter = 0; iter < maxIterations; iter++) {
|
|
3652
|
+
const improved = pushRouteAwayFromObstacles(current, obstacles);
|
|
3653
|
+
if (improved === null) {
|
|
3654
|
+
break;
|
|
3655
|
+
}
|
|
3656
|
+
current = improved;
|
|
3657
|
+
if (!routeCrossesBoxes(current, obstacles)) {
|
|
3658
|
+
break;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
return current;
|
|
3662
|
+
}
|
|
3663
|
+
function pushRouteAwayFromObstacles(points, obstacles) {
|
|
3664
|
+
const result = [];
|
|
3665
|
+
let improved = false;
|
|
3666
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
3667
|
+
const a = points[i];
|
|
3668
|
+
const b = points[i + 1];
|
|
3669
|
+
if (a === void 0 || b === void 0) {
|
|
3670
|
+
result.push(a ?? b ?? { x: 0, y: 0 });
|
|
3671
|
+
continue;
|
|
3672
|
+
}
|
|
3673
|
+
result.push(a);
|
|
3674
|
+
const intersectors = obstacles.filter(
|
|
3675
|
+
(obs) => segmentIntersectsBox(a, b, obs)
|
|
3676
|
+
);
|
|
3677
|
+
if (intersectors.length === 0) {
|
|
3678
|
+
continue;
|
|
3679
|
+
}
|
|
3680
|
+
const mx = (a.x + b.x) / 2;
|
|
3681
|
+
const my = (a.y + b.y) / 2;
|
|
3682
|
+
const isHorizontal = a.y === b.y;
|
|
3683
|
+
const margin = 12;
|
|
3684
|
+
let bestWaypoint = null;
|
|
3685
|
+
let bestDist = Infinity;
|
|
3686
|
+
for (const obs of intersectors) {
|
|
3687
|
+
const candidates = isHorizontal ? [
|
|
3688
|
+
{ x: mx, y: obs.y - margin },
|
|
3689
|
+
{ x: mx, y: obs.y + obs.height + margin }
|
|
3690
|
+
] : [
|
|
3691
|
+
{ x: obs.x - margin, y: my },
|
|
3692
|
+
{ x: obs.x + obs.width + margin, y: my }
|
|
3693
|
+
];
|
|
3694
|
+
for (const wp of candidates) {
|
|
3695
|
+
const dist = Math.hypot(wp.x - mx, wp.y - my);
|
|
3696
|
+
if (dist < bestDist) {
|
|
3697
|
+
bestDist = dist;
|
|
3698
|
+
bestWaypoint = wp;
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
if (bestWaypoint !== null) {
|
|
3703
|
+
result.push(bestWaypoint);
|
|
3704
|
+
improved = true;
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
const last = points[points.length - 1];
|
|
3708
|
+
if (last !== void 0) {
|
|
3709
|
+
result.push(last);
|
|
3710
|
+
}
|
|
3711
|
+
return improved ? result : null;
|
|
3712
|
+
}
|
|
3533
3713
|
function fallbackRoute(input, defaultAnchors) {
|
|
3534
3714
|
return [
|
|
3535
3715
|
getEdgePort(
|
|
@@ -4026,6 +4206,7 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4026
4206
|
];
|
|
4027
4207
|
const initialContentBounds = layoutBoxes.length === 0 ? { x: 0, y: 0, width: 0} : unionBoxes(layoutBoxes);
|
|
4028
4208
|
placeEvidenceBlocks(
|
|
4209
|
+
options.obstacleMargin ?? 0,
|
|
4029
4210
|
[
|
|
4030
4211
|
...coordinatedMatrices,
|
|
4031
4212
|
...coordinatedTables,
|
|
@@ -4092,17 +4273,35 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4092
4273
|
...baseTextAnnotations.filter(isPreRouteTextObstacle),
|
|
4093
4274
|
...frameTextAnnotation.filter(isPreRouteTextObstacle)
|
|
4094
4275
|
];
|
|
4276
|
+
const margin = options.obstacleMargin ?? 0;
|
|
4277
|
+
const softObstacles = [
|
|
4278
|
+
...coordinatedTables.map((table) => expandBox(table.box, margin)),
|
|
4279
|
+
...coordinatedEvidencePanels.map((panel) => expandBox(panel.box, margin))
|
|
4280
|
+
];
|
|
4281
|
+
const hardObstacles = coordinatedMatrices.map(
|
|
4282
|
+
(matrix) => expandBox(matrix.box, margin)
|
|
4283
|
+
);
|
|
4284
|
+
const titleBarObstacles = [];
|
|
4285
|
+
if (frame !== void 0) {
|
|
4286
|
+
titleBarObstacles.push(expandBox(frame.titleBox, margin));
|
|
4287
|
+
}
|
|
4288
|
+
for (const swimlane of coordinatedSwimlanes) {
|
|
4289
|
+
for (const lane of swimlane.lanes) {
|
|
4290
|
+
if (lane.headerBox !== void 0 && lane.headerBox.width > 0 && lane.headerBox.height > 0) {
|
|
4291
|
+
titleBarObstacles.push(expandBox(lane.headerBox, margin));
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4095
4295
|
const coordinatedEdges = coordinateEdges(
|
|
4096
4296
|
styledEdges,
|
|
4097
4297
|
nodeGeometryById,
|
|
4098
4298
|
coordinatedNodes,
|
|
4099
|
-
[...nodeGeometryById.values()].map(
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
],
|
|
4299
|
+
[...nodeGeometryById.values()].map(
|
|
4300
|
+
(geometry) => options.routingGutter === void 0 ? geometry.obstacleBox : expandBox(geometry.obstacleBox, options.routingGutter)
|
|
4301
|
+
),
|
|
4302
|
+
[...softObstacles, ...titleBarObstacles],
|
|
4104
4303
|
routingTextObstacles,
|
|
4105
|
-
|
|
4304
|
+
hardObstacles,
|
|
4106
4305
|
diagram.direction,
|
|
4107
4306
|
options,
|
|
4108
4307
|
diagnostics
|
|
@@ -4137,6 +4336,16 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4137
4336
|
options.pageBounds
|
|
4138
4337
|
)
|
|
4139
4338
|
);
|
|
4339
|
+
let degraded = false;
|
|
4340
|
+
const resultDiagnostics = diagnostics.map((diagnostic) => {
|
|
4341
|
+
if (DELIVERABILITY_DIAGNOSTIC_CODES.has(diagnostic.code)) {
|
|
4342
|
+
degraded = true;
|
|
4343
|
+
if (options.strict) {
|
|
4344
|
+
return { ...diagnostic, severity: "error" };
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
return diagnostic;
|
|
4348
|
+
});
|
|
4140
4349
|
return {
|
|
4141
4350
|
id: diagram.id,
|
|
4142
4351
|
...diagram.title === void 0 ? {} : { title: diagram.title },
|
|
@@ -4148,7 +4357,8 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4148
4357
|
...coordinatedMatrices.length === 0 ? {} : { matrices: coordinatedMatrices },
|
|
4149
4358
|
...coordinatedTables.length === 0 ? {} : { tables: coordinatedTables },
|
|
4150
4359
|
...coordinatedEvidencePanels.length === 0 ? {} : { evidencePanels: coordinatedEvidencePanels },
|
|
4151
|
-
diagnostics,
|
|
4360
|
+
diagnostics: resultDiagnostics,
|
|
4361
|
+
degraded,
|
|
4152
4362
|
bounds: frame === void 0 ? unionBoxes(boundsBase) : unionBoxes([...boundsBase, frame.box, frame.titleBox]),
|
|
4153
4363
|
...frame === void 0 ? {} : { frame },
|
|
4154
4364
|
...textAnnotations.length === 0 ? {} : { textAnnotations },
|
|
@@ -5534,16 +5744,25 @@ function blockBox(block, defaultSize) {
|
|
|
5534
5744
|
height: block.size?.height ?? defaultSize.height
|
|
5535
5745
|
};
|
|
5536
5746
|
}
|
|
5537
|
-
function placeEvidenceBlocks(blocks, contentBounds) {
|
|
5747
|
+
function placeEvidenceBlocks(obstacleMargin, blocks, contentBounds) {
|
|
5748
|
+
const margin = normalizeInsets(obstacleMargin);
|
|
5749
|
+
const horizontalGap = Math.max(
|
|
5750
|
+
DEFAULT_EVIDENCE_BLOCK_GAP,
|
|
5751
|
+
margin.right + margin.left
|
|
5752
|
+
);
|
|
5753
|
+
const verticalGap = Math.max(
|
|
5754
|
+
DEFAULT_EVIDENCE_BLOCK_GAP,
|
|
5755
|
+
margin.bottom + margin.top
|
|
5756
|
+
);
|
|
5538
5757
|
let nextY = contentBounds.y;
|
|
5539
|
-
const x = contentBounds.x + contentBounds.width +
|
|
5758
|
+
const x = contentBounds.x + contentBounds.width + horizontalGap;
|
|
5540
5759
|
for (const block of blocks) {
|
|
5541
5760
|
if (block.position !== void 0) {
|
|
5542
5761
|
continue;
|
|
5543
5762
|
}
|
|
5544
5763
|
block.box.x = x;
|
|
5545
5764
|
block.box.y = nextY;
|
|
5546
|
-
nextY += block.box.height +
|
|
5765
|
+
nextY += block.box.height + verticalGap;
|
|
5547
5766
|
}
|
|
5548
5767
|
}
|
|
5549
5768
|
function columnXOffsets(table, box) {
|
|
@@ -6399,9 +6618,7 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
|
|
|
6399
6618
|
{ x: placement.x, y: placement.y + offset }
|
|
6400
6619
|
);
|
|
6401
6620
|
}
|
|
6402
|
-
|
|
6403
|
-
}
|
|
6404
|
-
if (segment.start.x === segment.end.x) {
|
|
6621
|
+
} else if (segment.start.x === segment.end.x) {
|
|
6405
6622
|
const needed = layout2.box.width / 2 + EDGE_LABEL_CLEARANCE;
|
|
6406
6623
|
const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
|
|
6407
6624
|
for (let step = 1; step <= maxSteps; step += 1) {
|
|
@@ -6411,7 +6628,83 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
|
|
|
6411
6628
|
{ x: placement.x - offset, y: placement.y }
|
|
6412
6629
|
);
|
|
6413
6630
|
}
|
|
6414
|
-
|
|
6631
|
+
} else {
|
|
6632
|
+
const dx = segment.end.x - segment.start.x;
|
|
6633
|
+
const dy = segment.end.y - segment.start.y;
|
|
6634
|
+
const segLen = Math.hypot(dx, dy);
|
|
6635
|
+
if (segLen > 0) {
|
|
6636
|
+
const nx = -dy / segLen;
|
|
6637
|
+
const ny = dx / segLen;
|
|
6638
|
+
const needed = (Math.abs(nx) * layout2.box.width + Math.abs(ny) * layout2.box.height) / 2 + EDGE_LABEL_CLEARANCE;
|
|
6639
|
+
const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
|
|
6640
|
+
for (let step = 1; step <= maxSteps; step += 1) {
|
|
6641
|
+
const offset = EDGE_LABEL_CLEARANCE * step;
|
|
6642
|
+
candidates.push(
|
|
6643
|
+
{ x: placement.x + nx * offset, y: placement.y + ny * offset },
|
|
6644
|
+
{ x: placement.x - nx * offset, y: placement.y - ny * offset }
|
|
6645
|
+
);
|
|
6646
|
+
}
|
|
6647
|
+
}
|
|
6648
|
+
}
|
|
6649
|
+
const totalLen = points.reduce((sum, p, idx) => {
|
|
6650
|
+
if (idx === 0) return 0;
|
|
6651
|
+
const prev = points[idx - 1];
|
|
6652
|
+
return sum + Math.hypot((p?.x ?? 0) - (prev?.x ?? 0), (p?.y ?? 0) - (prev?.y ?? 0));
|
|
6653
|
+
}, 0);
|
|
6654
|
+
if (totalLen > 200) {
|
|
6655
|
+
for (const ratio of [0.25, 0.75]) {
|
|
6656
|
+
const qp = labelPlacementAtRatio(points, ratio, totalLen);
|
|
6657
|
+
if (qp !== void 0) {
|
|
6658
|
+
candidates.push(qp);
|
|
6659
|
+
const qTargetDist = totalLen * ratio;
|
|
6660
|
+
let qTravelled = 0;
|
|
6661
|
+
let seg;
|
|
6662
|
+
for (let si = 1; si < points.length; si++) {
|
|
6663
|
+
const sp = points[si - 1];
|
|
6664
|
+
const sc = points[si];
|
|
6665
|
+
if (sp === void 0 || sc === void 0) continue;
|
|
6666
|
+
const sl = Math.hypot(sc.x - sp.x, sc.y - sp.y);
|
|
6667
|
+
if (sl <= 0) continue;
|
|
6668
|
+
if (qTravelled + sl >= qTargetDist) {
|
|
6669
|
+
seg = { start: sp, end: sc, length: sl };
|
|
6670
|
+
break;
|
|
6671
|
+
}
|
|
6672
|
+
qTravelled += sl;
|
|
6673
|
+
}
|
|
6674
|
+
if (seg !== void 0) {
|
|
6675
|
+
const segLen = Math.hypot(
|
|
6676
|
+
seg.end.x - seg.start.x,
|
|
6677
|
+
seg.end.y - seg.start.y
|
|
6678
|
+
);
|
|
6679
|
+
const qpNeeded = seg.start.y === seg.end.y ? layout2.box.height / 2 + EDGE_LABEL_CLEARANCE : seg.start.x === seg.end.x ? layout2.box.width / 2 + EDGE_LABEL_CLEARANCE : (Math.abs(seg.start.y - seg.end.y) * layout2.box.width + Math.abs(seg.end.x - seg.start.x) * layout2.box.height) / (2 * segLen) + EDGE_LABEL_CLEARANCE;
|
|
6680
|
+
const qpMaxSteps = Math.max(
|
|
6681
|
+
12,
|
|
6682
|
+
Math.ceil(qpNeeded / EDGE_LABEL_CLEARANCE)
|
|
6683
|
+
);
|
|
6684
|
+
for (let step = 1; step <= qpMaxSteps; step += 1) {
|
|
6685
|
+
const offset = EDGE_LABEL_CLEARANCE * step;
|
|
6686
|
+
if (seg.start.y === seg.end.y) {
|
|
6687
|
+
candidates.push(
|
|
6688
|
+
{ x: qp.x, y: qp.y - offset },
|
|
6689
|
+
{ x: qp.x, y: qp.y + offset }
|
|
6690
|
+
);
|
|
6691
|
+
} else if (seg.start.x === seg.end.x) {
|
|
6692
|
+
candidates.push(
|
|
6693
|
+
{ x: qp.x - offset, y: qp.y },
|
|
6694
|
+
{ x: qp.x + offset, y: qp.y }
|
|
6695
|
+
);
|
|
6696
|
+
} else {
|
|
6697
|
+
const nx = -(seg.end.y - seg.start.y) / segLen;
|
|
6698
|
+
const ny = (seg.end.x - seg.start.x) / segLen;
|
|
6699
|
+
candidates.push(
|
|
6700
|
+
{ x: qp.x + nx * offset, y: qp.y + ny * offset },
|
|
6701
|
+
{ x: qp.x - nx * offset, y: qp.y - ny * offset }
|
|
6702
|
+
);
|
|
6703
|
+
}
|
|
6704
|
+
}
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6707
|
+
}
|
|
6415
6708
|
}
|
|
6416
6709
|
return candidates;
|
|
6417
6710
|
}
|
|
@@ -6468,6 +6761,34 @@ function nonZeroSegments2(points) {
|
|
|
6468
6761
|
}
|
|
6469
6762
|
return segments;
|
|
6470
6763
|
}
|
|
6764
|
+
function labelPlacementAtRatio(points, ratio, totalLength) {
|
|
6765
|
+
if (points.length < 2 || ratio < 0 || ratio > 1) {
|
|
6766
|
+
return void 0;
|
|
6767
|
+
}
|
|
6768
|
+
const targetDist = totalLength * ratio;
|
|
6769
|
+
let travelled = 0;
|
|
6770
|
+
for (let idx = 1; idx < points.length; idx++) {
|
|
6771
|
+
const prev = points[idx - 1];
|
|
6772
|
+
const curr = points[idx];
|
|
6773
|
+
if (prev === void 0 || curr === void 0) {
|
|
6774
|
+
continue;
|
|
6775
|
+
}
|
|
6776
|
+
const segLen = Math.hypot(curr.x - prev.x, curr.y - prev.y);
|
|
6777
|
+
if (segLen <= 0) {
|
|
6778
|
+
continue;
|
|
6779
|
+
}
|
|
6780
|
+
if (travelled + segLen >= targetDist) {
|
|
6781
|
+
const t = (targetDist - travelled) / segLen;
|
|
6782
|
+
const offset = labelOffset2({ start: prev, end: curr, length: segLen });
|
|
6783
|
+
return {
|
|
6784
|
+
x: prev.x + (curr.x - prev.x) * t + offset.x,
|
|
6785
|
+
y: prev.y + (curr.y - prev.y) * t + offset.y
|
|
6786
|
+
};
|
|
6787
|
+
}
|
|
6788
|
+
travelled += segLen;
|
|
6789
|
+
}
|
|
6790
|
+
return void 0;
|
|
6791
|
+
}
|
|
6471
6792
|
function labelOffset2(segment) {
|
|
6472
6793
|
const offset = 10;
|
|
6473
6794
|
const dx = segment.end.x - segment.start.x;
|
|
@@ -6581,7 +6902,7 @@ function isValidEdgeId(value) {
|
|
|
6581
6902
|
return value.length > 0 && EDGE_ID_PATTERN.test(value);
|
|
6582
6903
|
}
|
|
6583
6904
|
var directionSchema = z.enum(["TB", "LR", "BT", "RL"]);
|
|
6584
|
-
var routeKindSchema = z.enum(["orthogonal", "straight"]);
|
|
6905
|
+
var routeKindSchema = z.enum(["orthogonal", "straight", "obstacle-avoiding"]);
|
|
6585
6906
|
var outputFormatSchema = z.enum(["svg", "excalidraw"]);
|
|
6586
6907
|
var edgeStrokeStyleSchema = z.enum(["solid", "dashed"]);
|
|
6587
6908
|
var edgeArrowheadSchema = z.enum(["triangle", "hollowTriangle"]);
|
|
@@ -7180,7 +7501,7 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
7180
7501
|
return { diagnostics };
|
|
7181
7502
|
}
|
|
7182
7503
|
const solved = solveDiagram(normalized.diagram, {
|
|
7183
|
-
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
|
|
7504
|
+
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : normalized.diagram.metadata?.routeKind === "obstacle-avoiding" ? "obstacle-avoiding" : "orthogonal",
|
|
7184
7505
|
...solvePortShiftingOption(normalized.diagram.metadata?.portShifting),
|
|
7185
7506
|
...options.textMeasurer === void 0 ? {} : { textMeasurer: options.textMeasurer }
|
|
7186
7507
|
});
|