@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/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
- hardClearCandidate.points,
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
- candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
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
- candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
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((geometry) => geometry.obstacleBox),
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
- return candidates;
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
- return candidates;
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 labelOffset2(segment) {
7132
- const offset = 10;
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
  });