@crazyhappyone/auto-graph 0.1.1 → 0.1.3
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/README.md +6 -1
- package/README.zh-CN.md +6 -1
- package/dist/cli/index.cjs +293 -21
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +293 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +293 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +293 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -678,7 +678,7 @@ interface InitialLayoutResult {
|
|
|
678
678
|
|
|
679
679
|
declare function runDagreInitialLayout(input: DagreLayoutInput): InitialLayoutResult;
|
|
680
680
|
|
|
681
|
-
type RouteKind = "orthogonal" | "straight";
|
|
681
|
+
type RouteKind = "orthogonal" | "straight" | "obstacle-avoiding";
|
|
682
682
|
interface RouteEdgeInput {
|
|
683
683
|
kind?: RouteKind;
|
|
684
684
|
direction: DiagramDirection;
|
|
@@ -688,6 +688,8 @@ interface RouteEdgeInput {
|
|
|
688
688
|
targetAnchor?: AnchorName;
|
|
689
689
|
obstacles?: readonly Box[];
|
|
690
690
|
hardObstacles?: readonly Box[];
|
|
691
|
+
/** Maximum greedy rerouting iterations (default 5). */
|
|
692
|
+
maxRoutingAttempts?: number;
|
|
691
693
|
}
|
|
692
694
|
interface RouteEdgeResult {
|
|
693
695
|
points: Point[];
|
|
@@ -710,6 +712,8 @@ declare function stringifyCanonical(value: unknown, precision?: number): string;
|
|
|
710
712
|
interface SolveDiagramOptions {
|
|
711
713
|
routeKind?: RouteKind;
|
|
712
714
|
obstacleMargin?: number | Insets;
|
|
715
|
+
/** Extra horizontal/vertical clearance reserved around nodes for edge corridors. */
|
|
716
|
+
routingGutter?: number;
|
|
713
717
|
overlapSpacing?: number;
|
|
714
718
|
minLaneGutter?: number;
|
|
715
719
|
prefitLabelSize?: boolean;
|
|
@@ -727,6 +731,12 @@ interface SolveDiagramOptions {
|
|
|
727
731
|
textMeasurer?: TextMeasurer;
|
|
728
732
|
/** When true, promote deliverability-breaking diagnostics to errors. */
|
|
729
733
|
strict?: boolean;
|
|
734
|
+
/** Maximum greedy rerouting iterations per edge (default 5). */
|
|
735
|
+
maxRoutingAttempts?: number;
|
|
736
|
+
/** Edge label placement mode: "beside" offsets away from the edge, "on-path" (default) places at the midpoint. */
|
|
737
|
+
labelPlacement?: "beside" | "on-path";
|
|
738
|
+
/** Pixels to offset edge labels from the edge path when labelPlacement is "beside". */
|
|
739
|
+
labelOffset?: number;
|
|
730
740
|
}
|
|
731
741
|
interface PortShiftingOptions {
|
|
732
742
|
enabled?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -678,7 +678,7 @@ interface InitialLayoutResult {
|
|
|
678
678
|
|
|
679
679
|
declare function runDagreInitialLayout(input: DagreLayoutInput): InitialLayoutResult;
|
|
680
680
|
|
|
681
|
-
type RouteKind = "orthogonal" | "straight";
|
|
681
|
+
type RouteKind = "orthogonal" | "straight" | "obstacle-avoiding";
|
|
682
682
|
interface RouteEdgeInput {
|
|
683
683
|
kind?: RouteKind;
|
|
684
684
|
direction: DiagramDirection;
|
|
@@ -688,6 +688,8 @@ interface RouteEdgeInput {
|
|
|
688
688
|
targetAnchor?: AnchorName;
|
|
689
689
|
obstacles?: readonly Box[];
|
|
690
690
|
hardObstacles?: readonly Box[];
|
|
691
|
+
/** Maximum greedy rerouting iterations (default 5). */
|
|
692
|
+
maxRoutingAttempts?: number;
|
|
691
693
|
}
|
|
692
694
|
interface RouteEdgeResult {
|
|
693
695
|
points: Point[];
|
|
@@ -710,6 +712,8 @@ declare function stringifyCanonical(value: unknown, precision?: number): string;
|
|
|
710
712
|
interface SolveDiagramOptions {
|
|
711
713
|
routeKind?: RouteKind;
|
|
712
714
|
obstacleMargin?: number | Insets;
|
|
715
|
+
/** Extra horizontal/vertical clearance reserved around nodes for edge corridors. */
|
|
716
|
+
routingGutter?: number;
|
|
713
717
|
overlapSpacing?: number;
|
|
714
718
|
minLaneGutter?: number;
|
|
715
719
|
prefitLabelSize?: boolean;
|
|
@@ -727,6 +731,12 @@ interface SolveDiagramOptions {
|
|
|
727
731
|
textMeasurer?: TextMeasurer;
|
|
728
732
|
/** When true, promote deliverability-breaking diagnostics to errors. */
|
|
729
733
|
strict?: boolean;
|
|
734
|
+
/** Maximum greedy rerouting iterations per edge (default 5). */
|
|
735
|
+
maxRoutingAttempts?: number;
|
|
736
|
+
/** Edge label placement mode: "beside" offsets away from the edge, "on-path" (default) places at the midpoint. */
|
|
737
|
+
labelPlacement?: "beside" | "on-path";
|
|
738
|
+
/** Pixels to offset edge labels from the edge path when labelPlacement is "beside". */
|
|
739
|
+
labelOffset?: number;
|
|
730
740
|
}
|
|
731
741
|
interface PortShiftingOptions {
|
|
732
742
|
enabled?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -2068,7 +2068,7 @@ function point(value) {
|
|
|
2068
2068
|
return { x: value.x, y: value.y };
|
|
2069
2069
|
}
|
|
2070
2070
|
var directionSchema = z.enum(["TB", "LR", "BT", "RL"]);
|
|
2071
|
-
var routeKindSchema = z.enum(["orthogonal", "straight"]);
|
|
2071
|
+
var routeKindSchema = z.enum(["orthogonal", "straight", "obstacle-avoiding"]);
|
|
2072
2072
|
var outputFormatSchema = z.enum(["svg", "excalidraw"]);
|
|
2073
2073
|
var edgeStrokeStyleSchema = z.enum(["solid", "dashed"]);
|
|
2074
2074
|
var edgeArrowheadSchema = z.enum(["triangle", "hollowTriangle"]);
|
|
@@ -3859,6 +3859,7 @@ function routeEdge(input) {
|
|
|
3859
3859
|
const diagnostics = [];
|
|
3860
3860
|
const softObstacles = input.obstacles ?? [];
|
|
3861
3861
|
const hardObstacles = input.hardObstacles ?? [];
|
|
3862
|
+
const maxAttempts = input.maxRoutingAttempts ?? 5;
|
|
3862
3863
|
const defaultAnchors = defaultAnchorsForGeometry(
|
|
3863
3864
|
input.source.box,
|
|
3864
3865
|
input.target.box,
|
|
@@ -3954,6 +3955,51 @@ function routeEdge(input) {
|
|
|
3954
3955
|
)
|
|
3955
3956
|
);
|
|
3956
3957
|
if (hardClearCandidate !== void 0) {
|
|
3958
|
+
let bestPoints2 = hardClearCandidate.points;
|
|
3959
|
+
if (input.kind === "obstacle-avoiding") {
|
|
3960
|
+
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
3961
|
+
for (const candidate of candidateRoutes) {
|
|
3962
|
+
if (routeCrossesBoxes(candidate.points, hardObstacles) || routeIntersectsEndpointInteriors(
|
|
3963
|
+
candidate.points,
|
|
3964
|
+
candidate.endpointObstacles
|
|
3965
|
+
)) {
|
|
3966
|
+
continue;
|
|
3967
|
+
}
|
|
3968
|
+
const rerouted2 = greedyRerouteAroundObstacles(
|
|
3969
|
+
candidate.points,
|
|
3970
|
+
allObstacles,
|
|
3971
|
+
maxAttempts
|
|
3972
|
+
);
|
|
3973
|
+
if (!routeCrossesBoxes(rerouted2, allObstacles) && !routeIntersectsEndpointInteriors(
|
|
3974
|
+
rerouted2,
|
|
3975
|
+
candidate.endpointObstacles
|
|
3976
|
+
)) {
|
|
3977
|
+
return {
|
|
3978
|
+
points: finalizeRoute(
|
|
3979
|
+
rerouted2,
|
|
3980
|
+
softObstacles,
|
|
3981
|
+
hardObstacles,
|
|
3982
|
+
diagnostics
|
|
3983
|
+
),
|
|
3984
|
+
diagnostics
|
|
3985
|
+
};
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
const rerouted = greedyRerouteAroundObstacles(
|
|
3989
|
+
bestPoints2,
|
|
3990
|
+
allObstacles,
|
|
3991
|
+
Math.min(maxAttempts, 3)
|
|
3992
|
+
);
|
|
3993
|
+
const reroutedAvoidsEndpointInteriors = !routeIntersectsEndpointInteriors(
|
|
3994
|
+
rerouted,
|
|
3995
|
+
hardClearCandidate.endpointObstacles
|
|
3996
|
+
);
|
|
3997
|
+
if (reroutedAvoidsEndpointInteriors) {
|
|
3998
|
+
if (routeCrossesBoxes(rerouted, hardObstacles) && !routeCrossesBoxes(bestPoints2, hardObstacles)) ; else {
|
|
3999
|
+
bestPoints2 = rerouted;
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
3957
4003
|
diagnostics.push({
|
|
3958
4004
|
severity: "warning",
|
|
3959
4005
|
code: "routing.obstacle.unavoidable",
|
|
@@ -3961,7 +4007,7 @@ function routeEdge(input) {
|
|
|
3961
4007
|
});
|
|
3962
4008
|
return {
|
|
3963
4009
|
points: finalizeRoute(
|
|
3964
|
-
|
|
4010
|
+
bestPoints2,
|
|
3965
4011
|
softObstacles,
|
|
3966
4012
|
hardObstacles,
|
|
3967
4013
|
diagnostics
|
|
@@ -3970,6 +4016,33 @@ function routeEdge(input) {
|
|
|
3970
4016
|
};
|
|
3971
4017
|
}
|
|
3972
4018
|
if (hardObstacles.length > 0) {
|
|
4019
|
+
let bestPoints2 = candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors);
|
|
4020
|
+
if (input.kind === "obstacle-avoiding") {
|
|
4021
|
+
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
4022
|
+
for (const candidate of candidateRoutes) {
|
|
4023
|
+
const rerouted = greedyRerouteAroundObstacles(
|
|
4024
|
+
candidate.points,
|
|
4025
|
+
allObstacles,
|
|
4026
|
+
maxAttempts
|
|
4027
|
+
);
|
|
4028
|
+
if (!routeCrossesBoxes(rerouted, allObstacles)) {
|
|
4029
|
+
return {
|
|
4030
|
+
points: finalizeRoute(
|
|
4031
|
+
rerouted,
|
|
4032
|
+
softObstacles,
|
|
4033
|
+
hardObstacles,
|
|
4034
|
+
diagnostics
|
|
4035
|
+
),
|
|
4036
|
+
diagnostics
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
bestPoints2 = greedyRerouteAroundObstacles(
|
|
4041
|
+
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
|
|
4042
|
+
allObstacles,
|
|
4043
|
+
maxAttempts
|
|
4044
|
+
);
|
|
4045
|
+
}
|
|
3973
4046
|
diagnostics.push({
|
|
3974
4047
|
severity: "error",
|
|
3975
4048
|
code: "routing.evidence.crossing_forbidden",
|
|
@@ -3977,7 +4050,7 @@ function routeEdge(input) {
|
|
|
3977
4050
|
});
|
|
3978
4051
|
return {
|
|
3979
4052
|
points: finalizeRoute(
|
|
3980
|
-
|
|
4053
|
+
bestPoints2,
|
|
3981
4054
|
softObstacles,
|
|
3982
4055
|
hardObstacles,
|
|
3983
4056
|
diagnostics
|
|
@@ -3985,6 +4058,33 @@ function routeEdge(input) {
|
|
|
3985
4058
|
diagnostics
|
|
3986
4059
|
};
|
|
3987
4060
|
}
|
|
4061
|
+
let bestPoints = candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors);
|
|
4062
|
+
if (input.kind === "obstacle-avoiding") {
|
|
4063
|
+
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
4064
|
+
for (const candidate of candidateRoutes) {
|
|
4065
|
+
const rerouted = greedyRerouteAroundObstacles(
|
|
4066
|
+
candidate.points,
|
|
4067
|
+
allObstacles,
|
|
4068
|
+
maxAttempts
|
|
4069
|
+
);
|
|
4070
|
+
if (!routeCrossesBoxes(rerouted, allObstacles)) {
|
|
4071
|
+
return {
|
|
4072
|
+
points: finalizeRoute(
|
|
4073
|
+
rerouted,
|
|
4074
|
+
softObstacles,
|
|
4075
|
+
hardObstacles,
|
|
4076
|
+
diagnostics
|
|
4077
|
+
),
|
|
4078
|
+
diagnostics
|
|
4079
|
+
};
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
bestPoints = greedyRerouteAroundObstacles(
|
|
4083
|
+
candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
|
|
4084
|
+
allObstacles,
|
|
4085
|
+
maxAttempts
|
|
4086
|
+
);
|
|
4087
|
+
}
|
|
3988
4088
|
diagnostics.push({
|
|
3989
4089
|
severity: "warning",
|
|
3990
4090
|
code: "routing.obstacle.unavoidable",
|
|
@@ -3992,7 +4092,7 @@ function routeEdge(input) {
|
|
|
3992
4092
|
});
|
|
3993
4093
|
return {
|
|
3994
4094
|
points: finalizeRoute(
|
|
3995
|
-
|
|
4095
|
+
bestPoints,
|
|
3996
4096
|
softObstacles,
|
|
3997
4097
|
hardObstacles,
|
|
3998
4098
|
diagnostics
|
|
@@ -4150,6 +4250,70 @@ function insetBox(box, margin) {
|
|
|
4150
4250
|
height: box.height - margin * 2
|
|
4151
4251
|
};
|
|
4152
4252
|
}
|
|
4253
|
+
function greedyRerouteAroundObstacles(points, obstacles, maxIterations) {
|
|
4254
|
+
let current = [...points];
|
|
4255
|
+
for (let iter = 0; iter < maxIterations; iter++) {
|
|
4256
|
+
const improved = pushRouteAwayFromObstacles(current, obstacles);
|
|
4257
|
+
if (improved === null) {
|
|
4258
|
+
break;
|
|
4259
|
+
}
|
|
4260
|
+
current = improved;
|
|
4261
|
+
if (!routeCrossesBoxes(current, obstacles)) {
|
|
4262
|
+
break;
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
return current;
|
|
4266
|
+
}
|
|
4267
|
+
function pushRouteAwayFromObstacles(points, obstacles) {
|
|
4268
|
+
const result = [];
|
|
4269
|
+
let improved = false;
|
|
4270
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
4271
|
+
const a = points[i];
|
|
4272
|
+
const b = points[i + 1];
|
|
4273
|
+
if (a === void 0 || b === void 0) {
|
|
4274
|
+
result.push(a ?? b ?? { x: 0, y: 0 });
|
|
4275
|
+
continue;
|
|
4276
|
+
}
|
|
4277
|
+
result.push(a);
|
|
4278
|
+
const intersectors = obstacles.filter(
|
|
4279
|
+
(obs) => segmentIntersectsBox(a, b, obs)
|
|
4280
|
+
);
|
|
4281
|
+
if (intersectors.length === 0) {
|
|
4282
|
+
continue;
|
|
4283
|
+
}
|
|
4284
|
+
const mx = (a.x + b.x) / 2;
|
|
4285
|
+
const my = (a.y + b.y) / 2;
|
|
4286
|
+
const isHorizontal = a.y === b.y;
|
|
4287
|
+
const margin = 12;
|
|
4288
|
+
let bestWaypoint = null;
|
|
4289
|
+
let bestDist = Infinity;
|
|
4290
|
+
for (const obs of intersectors) {
|
|
4291
|
+
const candidates = isHorizontal ? [
|
|
4292
|
+
{ x: mx, y: obs.y - margin },
|
|
4293
|
+
{ x: mx, y: obs.y + obs.height + margin }
|
|
4294
|
+
] : [
|
|
4295
|
+
{ x: obs.x - margin, y: my },
|
|
4296
|
+
{ x: obs.x + obs.width + margin, y: my }
|
|
4297
|
+
];
|
|
4298
|
+
for (const wp of candidates) {
|
|
4299
|
+
const dist = Math.hypot(wp.x - mx, wp.y - my);
|
|
4300
|
+
if (dist < bestDist) {
|
|
4301
|
+
bestDist = dist;
|
|
4302
|
+
bestWaypoint = wp;
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
if (bestWaypoint !== null) {
|
|
4307
|
+
result.push(bestWaypoint);
|
|
4308
|
+
improved = true;
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
const last = points[points.length - 1];
|
|
4312
|
+
if (last !== void 0) {
|
|
4313
|
+
result.push(last);
|
|
4314
|
+
}
|
|
4315
|
+
return improved ? result : null;
|
|
4316
|
+
}
|
|
4153
4317
|
function fallbackRoute(input, defaultAnchors) {
|
|
4154
4318
|
return [
|
|
4155
4319
|
getEdgePort(
|
|
@@ -4736,7 +4900,9 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4736
4900
|
styledEdges,
|
|
4737
4901
|
nodeGeometryById,
|
|
4738
4902
|
coordinatedNodes,
|
|
4739
|
-
[...nodeGeometryById.values()].map(
|
|
4903
|
+
[...nodeGeometryById.values()].map(
|
|
4904
|
+
(geometry) => options.routingGutter === void 0 ? geometry.obstacleBox : expandBox(geometry.obstacleBox, options.routingGutter)
|
|
4905
|
+
),
|
|
4740
4906
|
[...softObstacles, ...titleBarObstacles],
|
|
4741
4907
|
routingTextObstacles,
|
|
4742
4908
|
hardObstacles,
|
|
@@ -4751,7 +4917,9 @@ function solveDiagram(diagram, options = {}) {
|
|
|
4751
4917
|
...baseTextAnnotations.map((annotation) => annotation.box),
|
|
4752
4918
|
...frameTextAnnotation.map((annotation) => annotation.box)
|
|
4753
4919
|
],
|
|
4754
|
-
options.textMeasurer
|
|
4920
|
+
options.textMeasurer,
|
|
4921
|
+
options.labelPlacement,
|
|
4922
|
+
options.labelOffset
|
|
4755
4923
|
);
|
|
4756
4924
|
const textAnnotations = [
|
|
4757
4925
|
...baseTextAnnotations,
|
|
@@ -6692,7 +6860,8 @@ function coordinateBaseTextAnnotations(input) {
|
|
|
6692
6860
|
}
|
|
6693
6861
|
return annotations;
|
|
6694
6862
|
}
|
|
6695
|
-
function coordinateEdgeTextAnnotations(edges, obstacleBoxes, textMeasurer) {
|
|
6863
|
+
function coordinateEdgeTextAnnotations(edges, obstacleBoxes, textMeasurer, labelPlacement, labelOffset3) {
|
|
6864
|
+
const labelBaseOffset = labelPlacement === "beside" ? labelOffset3 ?? 16 : 10;
|
|
6696
6865
|
const measurer = textMeasurer ?? createDefaultTextMeasurer();
|
|
6697
6866
|
const annotations = [];
|
|
6698
6867
|
const placedLabelBoxes = [];
|
|
@@ -6719,7 +6888,8 @@ function coordinateEdgeTextAnnotations(edges, obstacleBoxes, textMeasurer) {
|
|
|
6719
6888
|
layout2,
|
|
6720
6889
|
edges,
|
|
6721
6890
|
obstacleBoxes,
|
|
6722
|
-
placedLabelBoxes
|
|
6891
|
+
placedLabelBoxes,
|
|
6892
|
+
labelBaseOffset
|
|
6723
6893
|
);
|
|
6724
6894
|
placedLabelBoxes.push({
|
|
6725
6895
|
x: center.x - layout2.box.width / 2,
|
|
@@ -7003,8 +7173,8 @@ function fallbackLabelLayout(text) {
|
|
|
7003
7173
|
diagnostics: []
|
|
7004
7174
|
};
|
|
7005
7175
|
}
|
|
7006
|
-
function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes) {
|
|
7007
|
-
const placement = labelPlacementOnPolyline2(edge.points);
|
|
7176
|
+
function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes, baseOffset = 10) {
|
|
7177
|
+
const placement = labelPlacementOnPolyline2(edge.points, baseOffset);
|
|
7008
7178
|
if (placement === void 0) {
|
|
7009
7179
|
return { x: 0, y: 0 };
|
|
7010
7180
|
}
|
|
@@ -7059,9 +7229,7 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
|
|
|
7059
7229
|
{ x: placement.x, y: placement.y + offset }
|
|
7060
7230
|
);
|
|
7061
7231
|
}
|
|
7062
|
-
|
|
7063
|
-
}
|
|
7064
|
-
if (segment.start.x === segment.end.x) {
|
|
7232
|
+
} else if (segment.start.x === segment.end.x) {
|
|
7065
7233
|
const needed = layout2.box.width / 2 + EDGE_LABEL_CLEARANCE;
|
|
7066
7234
|
const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
|
|
7067
7235
|
for (let step = 1; step <= maxSteps; step += 1) {
|
|
@@ -7071,14 +7239,90 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
|
|
|
7071
7239
|
{ x: placement.x - offset, y: placement.y }
|
|
7072
7240
|
);
|
|
7073
7241
|
}
|
|
7074
|
-
|
|
7242
|
+
} else {
|
|
7243
|
+
const dx = segment.end.x - segment.start.x;
|
|
7244
|
+
const dy = segment.end.y - segment.start.y;
|
|
7245
|
+
const segLen = Math.hypot(dx, dy);
|
|
7246
|
+
if (segLen > 0) {
|
|
7247
|
+
const nx = -dy / segLen;
|
|
7248
|
+
const ny = dx / segLen;
|
|
7249
|
+
const needed = (Math.abs(nx) * layout2.box.width + Math.abs(ny) * layout2.box.height) / 2 + EDGE_LABEL_CLEARANCE;
|
|
7250
|
+
const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
|
|
7251
|
+
for (let step = 1; step <= maxSteps; step += 1) {
|
|
7252
|
+
const offset = EDGE_LABEL_CLEARANCE * step;
|
|
7253
|
+
candidates.push(
|
|
7254
|
+
{ x: placement.x + nx * offset, y: placement.y + ny * offset },
|
|
7255
|
+
{ x: placement.x - nx * offset, y: placement.y - ny * offset }
|
|
7256
|
+
);
|
|
7257
|
+
}
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
const totalLen = points.reduce((sum, p, idx) => {
|
|
7261
|
+
if (idx === 0) return 0;
|
|
7262
|
+
const prev = points[idx - 1];
|
|
7263
|
+
return sum + Math.hypot((p?.x ?? 0) - (prev?.x ?? 0), (p?.y ?? 0) - (prev?.y ?? 0));
|
|
7264
|
+
}, 0);
|
|
7265
|
+
if (totalLen > 200) {
|
|
7266
|
+
for (const ratio of [0.25, 0.75]) {
|
|
7267
|
+
const qp = labelPlacementAtRatio(points, ratio, totalLen);
|
|
7268
|
+
if (qp !== void 0) {
|
|
7269
|
+
candidates.push(qp);
|
|
7270
|
+
const qTargetDist = totalLen * ratio;
|
|
7271
|
+
let qTravelled = 0;
|
|
7272
|
+
let seg;
|
|
7273
|
+
for (let si = 1; si < points.length; si++) {
|
|
7274
|
+
const sp = points[si - 1];
|
|
7275
|
+
const sc = points[si];
|
|
7276
|
+
if (sp === void 0 || sc === void 0) continue;
|
|
7277
|
+
const sl = Math.hypot(sc.x - sp.x, sc.y - sp.y);
|
|
7278
|
+
if (sl <= 0) continue;
|
|
7279
|
+
if (qTravelled + sl >= qTargetDist) {
|
|
7280
|
+
seg = { start: sp, end: sc, length: sl };
|
|
7281
|
+
break;
|
|
7282
|
+
}
|
|
7283
|
+
qTravelled += sl;
|
|
7284
|
+
}
|
|
7285
|
+
if (seg !== void 0) {
|
|
7286
|
+
const segLen = Math.hypot(
|
|
7287
|
+
seg.end.x - seg.start.x,
|
|
7288
|
+
seg.end.y - seg.start.y
|
|
7289
|
+
);
|
|
7290
|
+
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;
|
|
7291
|
+
const qpMaxSteps = Math.max(
|
|
7292
|
+
12,
|
|
7293
|
+
Math.ceil(qpNeeded / EDGE_LABEL_CLEARANCE)
|
|
7294
|
+
);
|
|
7295
|
+
for (let step = 1; step <= qpMaxSteps; step += 1) {
|
|
7296
|
+
const offset = EDGE_LABEL_CLEARANCE * step;
|
|
7297
|
+
if (seg.start.y === seg.end.y) {
|
|
7298
|
+
candidates.push(
|
|
7299
|
+
{ x: qp.x, y: qp.y - offset },
|
|
7300
|
+
{ x: qp.x, y: qp.y + offset }
|
|
7301
|
+
);
|
|
7302
|
+
} else if (seg.start.x === seg.end.x) {
|
|
7303
|
+
candidates.push(
|
|
7304
|
+
{ x: qp.x - offset, y: qp.y },
|
|
7305
|
+
{ x: qp.x + offset, y: qp.y }
|
|
7306
|
+
);
|
|
7307
|
+
} else {
|
|
7308
|
+
const nx = -(seg.end.y - seg.start.y) / segLen;
|
|
7309
|
+
const ny = (seg.end.x - seg.start.x) / segLen;
|
|
7310
|
+
candidates.push(
|
|
7311
|
+
{ x: qp.x + nx * offset, y: qp.y + ny * offset },
|
|
7312
|
+
{ x: qp.x - nx * offset, y: qp.y - ny * offset }
|
|
7313
|
+
);
|
|
7314
|
+
}
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7075
7319
|
}
|
|
7076
7320
|
return candidates;
|
|
7077
7321
|
}
|
|
7078
|
-
function labelPlacementOnPolyline2(points) {
|
|
7079
|
-
return labelSegmentOnPolyline(points)?.placement;
|
|
7322
|
+
function labelPlacementOnPolyline2(points, baseOffset = 10) {
|
|
7323
|
+
return labelSegmentOnPolyline(points, baseOffset)?.placement;
|
|
7080
7324
|
}
|
|
7081
|
-
function labelSegmentOnPolyline(points) {
|
|
7325
|
+
function labelSegmentOnPolyline(points, baseOffset = 10) {
|
|
7082
7326
|
const segments = nonZeroSegments2(points);
|
|
7083
7327
|
const totalLength = segments.reduce(
|
|
7084
7328
|
(sum, segment) => sum + segment.length,
|
|
@@ -7093,7 +7337,7 @@ function labelSegmentOnPolyline(points) {
|
|
|
7093
7337
|
const ratio = remaining / segment.length;
|
|
7094
7338
|
const x = segment.start.x + (segment.end.x - segment.start.x) * ratio;
|
|
7095
7339
|
const y = segment.start.y + (segment.end.y - segment.start.y) * ratio;
|
|
7096
|
-
const offset2 = labelOffset2(segment);
|
|
7340
|
+
const offset2 = labelOffset2(segment, baseOffset);
|
|
7097
7341
|
return {
|
|
7098
7342
|
start: segment.start,
|
|
7099
7343
|
end: segment.end,
|
|
@@ -7128,8 +7372,36 @@ function nonZeroSegments2(points) {
|
|
|
7128
7372
|
}
|
|
7129
7373
|
return segments;
|
|
7130
7374
|
}
|
|
7131
|
-
function
|
|
7132
|
-
|
|
7375
|
+
function labelPlacementAtRatio(points, ratio, totalLength) {
|
|
7376
|
+
if (points.length < 2 || ratio < 0 || ratio > 1) {
|
|
7377
|
+
return void 0;
|
|
7378
|
+
}
|
|
7379
|
+
const targetDist = totalLength * ratio;
|
|
7380
|
+
let travelled = 0;
|
|
7381
|
+
for (let idx = 1; idx < points.length; idx++) {
|
|
7382
|
+
const prev = points[idx - 1];
|
|
7383
|
+
const curr = points[idx];
|
|
7384
|
+
if (prev === void 0 || curr === void 0) {
|
|
7385
|
+
continue;
|
|
7386
|
+
}
|
|
7387
|
+
const segLen = Math.hypot(curr.x - prev.x, curr.y - prev.y);
|
|
7388
|
+
if (segLen <= 0) {
|
|
7389
|
+
continue;
|
|
7390
|
+
}
|
|
7391
|
+
if (travelled + segLen >= targetDist) {
|
|
7392
|
+
const t = (targetDist - travelled) / segLen;
|
|
7393
|
+
const offset = labelOffset2({ start: prev, end: curr, length: segLen });
|
|
7394
|
+
return {
|
|
7395
|
+
x: prev.x + (curr.x - prev.x) * t + offset.x,
|
|
7396
|
+
y: prev.y + (curr.y - prev.y) * t + offset.y
|
|
7397
|
+
};
|
|
7398
|
+
}
|
|
7399
|
+
travelled += segLen;
|
|
7400
|
+
}
|
|
7401
|
+
return void 0;
|
|
7402
|
+
}
|
|
7403
|
+
function labelOffset2(segment, baseOffset = 10) {
|
|
7404
|
+
const offset = baseOffset;
|
|
7133
7405
|
const dx = segment.end.x - segment.start.x;
|
|
7134
7406
|
const dy = segment.end.y - segment.start.y;
|
|
7135
7407
|
return {
|
|
@@ -7242,7 +7514,7 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
7242
7514
|
return { diagnostics };
|
|
7243
7515
|
}
|
|
7244
7516
|
const solved = solveDiagram(normalized.diagram, {
|
|
7245
|
-
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
|
|
7517
|
+
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : normalized.diagram.metadata?.routeKind === "obstacle-avoiding" ? "obstacle-avoiding" : "orthogonal",
|
|
7246
7518
|
...solvePortShiftingOption(normalized.diagram.metadata?.portShifting),
|
|
7247
7519
|
...options.textMeasurer === void 0 ? {} : { textMeasurer: options.textMeasurer }
|
|
7248
7520
|
});
|