@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/index.cjs CHANGED
@@ -2071,7 +2071,7 @@ function point(value) {
2071
2071
  return { x: value.x, y: value.y };
2072
2072
  }
2073
2073
  var directionSchema = zod.z.enum(["TB", "LR", "BT", "RL"]);
2074
- var routeKindSchema = zod.z.enum(["orthogonal", "straight"]);
2074
+ var routeKindSchema = zod.z.enum(["orthogonal", "straight", "obstacle-avoiding"]);
2075
2075
  var outputFormatSchema = zod.z.enum(["svg", "excalidraw"]);
2076
2076
  var edgeStrokeStyleSchema = zod.z.enum(["solid", "dashed"]);
2077
2077
  var edgeArrowheadSchema = zod.z.enum(["triangle", "hollowTriangle"]);
@@ -3750,6 +3750,15 @@ function indent(value) {
3750
3750
  function indentLines(values) {
3751
3751
  return values.map(indent);
3752
3752
  }
3753
+
3754
+ // src/ir/diagnostics.ts
3755
+ var DELIVERABILITY_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
3756
+ "constraints.locked-target-not-moved",
3757
+ "routing.evidence.crossing_forbidden",
3758
+ "routing.obstacle.unavoidable",
3759
+ "route_obstacle_fallback",
3760
+ "routing.text-clearance.unresolved"
3761
+ ]);
3753
3762
  var DEFAULT_OPTIONS = {
3754
3763
  nodesep: 80,
3755
3764
  ranksep: 100,
@@ -3948,6 +3957,51 @@ function routeEdge(input) {
3948
3957
  )
3949
3958
  );
3950
3959
  if (hardClearCandidate !== void 0) {
3960
+ let bestPoints2 = hardClearCandidate.points;
3961
+ if (input.kind === "obstacle-avoiding") {
3962
+ const allObstacles = [...softObstacles, ...hardObstacles];
3963
+ for (const candidate of candidateRoutes) {
3964
+ if (routeCrossesBoxes(candidate.points, hardObstacles) || routeIntersectsEndpointInteriors(
3965
+ candidate.points,
3966
+ candidate.endpointObstacles
3967
+ )) {
3968
+ continue;
3969
+ }
3970
+ const rerouted2 = greedyRerouteAroundObstacles(
3971
+ candidate.points,
3972
+ allObstacles,
3973
+ 3
3974
+ );
3975
+ if (!routeCrossesBoxes(rerouted2, allObstacles) && !routeIntersectsEndpointInteriors(
3976
+ rerouted2,
3977
+ candidate.endpointObstacles
3978
+ )) {
3979
+ return {
3980
+ points: finalizeRoute(
3981
+ rerouted2,
3982
+ softObstacles,
3983
+ hardObstacles,
3984
+ diagnostics
3985
+ ),
3986
+ diagnostics
3987
+ };
3988
+ }
3989
+ }
3990
+ const rerouted = greedyRerouteAroundObstacles(
3991
+ bestPoints2,
3992
+ allObstacles,
3993
+ 3
3994
+ );
3995
+ const reroutedAvoidsEndpointInteriors = !routeIntersectsEndpointInteriors(
3996
+ rerouted,
3997
+ hardClearCandidate.endpointObstacles
3998
+ );
3999
+ if (reroutedAvoidsEndpointInteriors) {
4000
+ if (routeCrossesBoxes(rerouted, hardObstacles) && !routeCrossesBoxes(bestPoints2, hardObstacles)) ; else {
4001
+ bestPoints2 = rerouted;
4002
+ }
4003
+ }
4004
+ }
3951
4005
  diagnostics.push({
3952
4006
  severity: "warning",
3953
4007
  code: "routing.obstacle.unavoidable",
@@ -3955,7 +4009,7 @@ function routeEdge(input) {
3955
4009
  });
3956
4010
  return {
3957
4011
  points: finalizeRoute(
3958
- hardClearCandidate.points,
4012
+ bestPoints2,
3959
4013
  softObstacles,
3960
4014
  hardObstacles,
3961
4015
  diagnostics
@@ -3964,6 +4018,33 @@ function routeEdge(input) {
3964
4018
  };
3965
4019
  }
3966
4020
  if (hardObstacles.length > 0) {
4021
+ let bestPoints2 = candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors);
4022
+ if (input.kind === "obstacle-avoiding") {
4023
+ const allObstacles = [...softObstacles, ...hardObstacles];
4024
+ for (const candidate of candidateRoutes) {
4025
+ const rerouted = greedyRerouteAroundObstacles(
4026
+ candidate.points,
4027
+ allObstacles,
4028
+ 5
4029
+ );
4030
+ if (!routeCrossesBoxes(rerouted, allObstacles)) {
4031
+ return {
4032
+ points: finalizeRoute(
4033
+ rerouted,
4034
+ softObstacles,
4035
+ hardObstacles,
4036
+ diagnostics
4037
+ ),
4038
+ diagnostics
4039
+ };
4040
+ }
4041
+ }
4042
+ bestPoints2 = greedyRerouteAroundObstacles(
4043
+ candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
4044
+ allObstacles,
4045
+ 5
4046
+ );
4047
+ }
3967
4048
  diagnostics.push({
3968
4049
  severity: "error",
3969
4050
  code: "routing.evidence.crossing_forbidden",
@@ -3971,7 +4052,7 @@ function routeEdge(input) {
3971
4052
  });
3972
4053
  return {
3973
4054
  points: finalizeRoute(
3974
- candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
4055
+ bestPoints2,
3975
4056
  softObstacles,
3976
4057
  hardObstacles,
3977
4058
  diagnostics
@@ -3979,6 +4060,33 @@ function routeEdge(input) {
3979
4060
  diagnostics
3980
4061
  };
3981
4062
  }
4063
+ let bestPoints = candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors);
4064
+ if (input.kind === "obstacle-avoiding") {
4065
+ const allObstacles = [...softObstacles, ...hardObstacles];
4066
+ for (const candidate of candidateRoutes) {
4067
+ const rerouted = greedyRerouteAroundObstacles(
4068
+ candidate.points,
4069
+ allObstacles,
4070
+ 5
4071
+ );
4072
+ if (!routeCrossesBoxes(rerouted, allObstacles)) {
4073
+ return {
4074
+ points: finalizeRoute(
4075
+ rerouted,
4076
+ softObstacles,
4077
+ hardObstacles,
4078
+ diagnostics
4079
+ ),
4080
+ diagnostics
4081
+ };
4082
+ }
4083
+ }
4084
+ bestPoints = greedyRerouteAroundObstacles(
4085
+ candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
4086
+ allObstacles,
4087
+ 5
4088
+ );
4089
+ }
3982
4090
  diagnostics.push({
3983
4091
  severity: "warning",
3984
4092
  code: "routing.obstacle.unavoidable",
@@ -3986,7 +4094,7 @@ function routeEdge(input) {
3986
4094
  });
3987
4095
  return {
3988
4096
  points: finalizeRoute(
3989
- candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
4097
+ bestPoints,
3990
4098
  softObstacles,
3991
4099
  hardObstacles,
3992
4100
  diagnostics
@@ -3996,21 +4104,29 @@ function routeEdge(input) {
3996
4104
  }
3997
4105
  function finalizeRoute(points, softObstacles, hardObstacles, diagnostics) {
3998
4106
  const simplified = simplifyRoute(points);
4107
+ if (simplified.length >= 3) {
4108
+ return simplified;
4109
+ }
3999
4110
  const crossesHardObstacles = routeCrossesBoxes(simplified, hardObstacles);
4000
4111
  const crossesSoftObstacles = routeCrossesBoxes(simplified, softObstacles);
4001
- if (simplified.length < 3 && (crossesHardObstacles || crossesSoftObstacles)) {
4112
+ if (!crossesHardObstacles && !crossesSoftObstacles) {
4113
+ return simplified;
4114
+ }
4115
+ const expanded = expandFallbackRoute(simplified, [
4116
+ ...softObstacles,
4117
+ ...hardObstacles
4118
+ ]);
4119
+ const expandedCrossesHard = routeCrossesBoxes(expanded, hardObstacles);
4120
+ const expandedCrossesSoft = routeCrossesBoxes(expanded, softObstacles);
4121
+ if (expandedCrossesHard || expandedCrossesSoft) {
4002
4122
  diagnostics.push({
4003
- severity: crossesHardObstacles ? "error" : "warning",
4123
+ severity: expandedCrossesHard ? "error" : "warning",
4004
4124
  code: "route_obstacle_fallback",
4005
4125
  message: "Obstacle-aware routing fell back to fewer than three route points.",
4006
4126
  detail: { pointCount: simplified.length }
4007
4127
  });
4008
- return expandFallbackRoute(simplified, [
4009
- ...softObstacles,
4010
- ...hardObstacles
4011
- ]);
4012
4128
  }
4013
- return simplified;
4129
+ return expanded;
4014
4130
  }
4015
4131
  function expandFallbackRoute(points, obstacles) {
4016
4132
  if (points.length !== 2) {
@@ -4041,12 +4157,12 @@ function expandFallbackRoute(points, obstacles) {
4041
4157
  const hv = diagonalDetourHV(source, target, obstacles);
4042
4158
  const vh = diagonalDetourVH(source, target, obstacles);
4043
4159
  const viable = [hv, vh].filter((c) => !routeCrossesBoxes(c, obstacles));
4044
- if (viable.length > 0) {
4160
+ const [firstViable, ...remainingViable] = viable;
4161
+ if (firstViable !== void 0) {
4045
4162
  const directLen = Math.hypot(target.x - source.x, target.y - source.y);
4046
- let best = viable[0];
4047
- for (let i = 1; i < viable.length; i += 1) {
4048
- const cand = viable[i];
4049
- if (cand !== void 0 && pathLength(cand) - directLen < pathLength(best) - directLen) {
4163
+ let best = firstViable;
4164
+ for (const cand of remainingViable) {
4165
+ if (pathLength(cand) - directLen < pathLength(best) - directLen) {
4050
4166
  best = cand;
4051
4167
  }
4052
4168
  }
@@ -4136,6 +4252,70 @@ function insetBox(box, margin) {
4136
4252
  height: box.height - margin * 2
4137
4253
  };
4138
4254
  }
4255
+ function greedyRerouteAroundObstacles(points, obstacles, maxIterations) {
4256
+ let current = [...points];
4257
+ for (let iter = 0; iter < maxIterations; iter++) {
4258
+ const improved = pushRouteAwayFromObstacles(current, obstacles);
4259
+ if (improved === null) {
4260
+ break;
4261
+ }
4262
+ current = improved;
4263
+ if (!routeCrossesBoxes(current, obstacles)) {
4264
+ break;
4265
+ }
4266
+ }
4267
+ return current;
4268
+ }
4269
+ function pushRouteAwayFromObstacles(points, obstacles) {
4270
+ const result = [];
4271
+ let improved = false;
4272
+ for (let i = 0; i < points.length - 1; i++) {
4273
+ const a = points[i];
4274
+ const b = points[i + 1];
4275
+ if (a === void 0 || b === void 0) {
4276
+ result.push(a ?? b ?? { x: 0, y: 0 });
4277
+ continue;
4278
+ }
4279
+ result.push(a);
4280
+ const intersectors = obstacles.filter(
4281
+ (obs) => segmentIntersectsBox(a, b, obs)
4282
+ );
4283
+ if (intersectors.length === 0) {
4284
+ continue;
4285
+ }
4286
+ const mx = (a.x + b.x) / 2;
4287
+ const my = (a.y + b.y) / 2;
4288
+ const isHorizontal = a.y === b.y;
4289
+ const margin = 12;
4290
+ let bestWaypoint = null;
4291
+ let bestDist = Infinity;
4292
+ for (const obs of intersectors) {
4293
+ const candidates = isHorizontal ? [
4294
+ { x: mx, y: obs.y - margin },
4295
+ { x: mx, y: obs.y + obs.height + margin }
4296
+ ] : [
4297
+ { x: obs.x - margin, y: my },
4298
+ { x: obs.x + obs.width + margin, y: my }
4299
+ ];
4300
+ for (const wp of candidates) {
4301
+ const dist = Math.hypot(wp.x - mx, wp.y - my);
4302
+ if (dist < bestDist) {
4303
+ bestDist = dist;
4304
+ bestWaypoint = wp;
4305
+ }
4306
+ }
4307
+ }
4308
+ if (bestWaypoint !== null) {
4309
+ result.push(bestWaypoint);
4310
+ improved = true;
4311
+ }
4312
+ }
4313
+ const last = points[points.length - 1];
4314
+ if (last !== void 0) {
4315
+ result.push(last);
4316
+ }
4317
+ return improved ? result : null;
4318
+ }
4139
4319
  function fallbackRoute(input, defaultAnchors) {
4140
4320
  return [
4141
4321
  getEdgePort(
@@ -4632,6 +4812,7 @@ function solveDiagram(diagram, options = {}) {
4632
4812
  ];
4633
4813
  const initialContentBounds = layoutBoxes.length === 0 ? { x: 0, y: 0, width: 0} : unionBoxes(layoutBoxes);
4634
4814
  placeEvidenceBlocks(
4815
+ options.obstacleMargin ?? 0,
4635
4816
  [
4636
4817
  ...coordinatedMatrices,
4637
4818
  ...coordinatedTables,
@@ -4698,17 +4879,35 @@ function solveDiagram(diagram, options = {}) {
4698
4879
  ...baseTextAnnotations.filter(isPreRouteTextObstacle),
4699
4880
  ...frameTextAnnotation.filter(isPreRouteTextObstacle)
4700
4881
  ];
4882
+ const margin = options.obstacleMargin ?? 0;
4883
+ const softObstacles = [
4884
+ ...coordinatedTables.map((table) => expandBox(table.box, margin)),
4885
+ ...coordinatedEvidencePanels.map((panel) => expandBox(panel.box, margin))
4886
+ ];
4887
+ const hardObstacles = coordinatedMatrices.map(
4888
+ (matrix) => expandBox(matrix.box, margin)
4889
+ );
4890
+ const titleBarObstacles = [];
4891
+ if (frame !== void 0) {
4892
+ titleBarObstacles.push(expandBox(frame.titleBox, margin));
4893
+ }
4894
+ for (const swimlane of coordinatedSwimlanes) {
4895
+ for (const lane of swimlane.lanes) {
4896
+ if (lane.headerBox !== void 0 && lane.headerBox.width > 0 && lane.headerBox.height > 0) {
4897
+ titleBarObstacles.push(expandBox(lane.headerBox, margin));
4898
+ }
4899
+ }
4900
+ }
4701
4901
  const coordinatedEdges = coordinateEdges(
4702
4902
  styledEdges,
4703
4903
  nodeGeometryById,
4704
4904
  coordinatedNodes,
4705
- [...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
4706
- [
4707
- ...coordinatedTables.map((table) => table.box),
4708
- ...coordinatedEvidencePanels.map((panel) => panel.box)
4709
- ],
4905
+ [...nodeGeometryById.values()].map(
4906
+ (geometry) => options.routingGutter === void 0 ? geometry.obstacleBox : expandBox(geometry.obstacleBox, options.routingGutter)
4907
+ ),
4908
+ [...softObstacles, ...titleBarObstacles],
4710
4909
  routingTextObstacles,
4711
- coordinatedMatrices.map((matrix) => matrix.box),
4910
+ hardObstacles,
4712
4911
  diagram.direction,
4713
4912
  options,
4714
4913
  diagnostics
@@ -4743,6 +4942,16 @@ function solveDiagram(diagram, options = {}) {
4743
4942
  options.pageBounds
4744
4943
  )
4745
4944
  );
4945
+ let degraded = false;
4946
+ const resultDiagnostics = diagnostics.map((diagnostic) => {
4947
+ if (DELIVERABILITY_DIAGNOSTIC_CODES.has(diagnostic.code)) {
4948
+ degraded = true;
4949
+ if (options.strict) {
4950
+ return { ...diagnostic, severity: "error" };
4951
+ }
4952
+ }
4953
+ return diagnostic;
4954
+ });
4746
4955
  return {
4747
4956
  id: diagram.id,
4748
4957
  ...diagram.title === void 0 ? {} : { title: diagram.title },
@@ -4754,7 +4963,8 @@ function solveDiagram(diagram, options = {}) {
4754
4963
  ...coordinatedMatrices.length === 0 ? {} : { matrices: coordinatedMatrices },
4755
4964
  ...coordinatedTables.length === 0 ? {} : { tables: coordinatedTables },
4756
4965
  ...coordinatedEvidencePanels.length === 0 ? {} : { evidencePanels: coordinatedEvidencePanels },
4757
- diagnostics,
4966
+ diagnostics: resultDiagnostics,
4967
+ degraded,
4758
4968
  bounds: frame === void 0 ? unionBoxes(boundsBase) : unionBoxes([...boundsBase, frame.box, frame.titleBox]),
4759
4969
  ...frame === void 0 ? {} : { frame },
4760
4970
  ...textAnnotations.length === 0 ? {} : { textAnnotations },
@@ -6143,16 +6353,25 @@ function blockBox(block, defaultSize) {
6143
6353
  height: block.size?.height ?? defaultSize.height
6144
6354
  };
6145
6355
  }
6146
- function placeEvidenceBlocks(blocks, contentBounds) {
6356
+ function placeEvidenceBlocks(obstacleMargin, blocks, contentBounds) {
6357
+ const margin = normalizeInsets(obstacleMargin);
6358
+ const horizontalGap = Math.max(
6359
+ DEFAULT_EVIDENCE_BLOCK_GAP,
6360
+ margin.right + margin.left
6361
+ );
6362
+ const verticalGap = Math.max(
6363
+ DEFAULT_EVIDENCE_BLOCK_GAP,
6364
+ margin.bottom + margin.top
6365
+ );
6147
6366
  let nextY = contentBounds.y;
6148
- const x = contentBounds.x + contentBounds.width + DEFAULT_EVIDENCE_BLOCK_GAP;
6367
+ const x = contentBounds.x + contentBounds.width + horizontalGap;
6149
6368
  for (const block of blocks) {
6150
6369
  if (block.position !== void 0) {
6151
6370
  continue;
6152
6371
  }
6153
6372
  block.box.x = x;
6154
6373
  block.box.y = nextY;
6155
- nextY += block.box.height + DEFAULT_EVIDENCE_BLOCK_GAP;
6374
+ nextY += block.box.height + verticalGap;
6156
6375
  }
6157
6376
  }
6158
6377
  function columnXOffsets(table, box) {
@@ -7008,9 +7227,7 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
7008
7227
  { x: placement.x, y: placement.y + offset }
7009
7228
  );
7010
7229
  }
7011
- return candidates;
7012
- }
7013
- if (segment.start.x === segment.end.x) {
7230
+ } else if (segment.start.x === segment.end.x) {
7014
7231
  const needed = layout2.box.width / 2 + EDGE_LABEL_CLEARANCE;
7015
7232
  const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
7016
7233
  for (let step = 1; step <= maxSteps; step += 1) {
@@ -7020,7 +7237,83 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
7020
7237
  { x: placement.x - offset, y: placement.y }
7021
7238
  );
7022
7239
  }
7023
- return candidates;
7240
+ } else {
7241
+ const dx = segment.end.x - segment.start.x;
7242
+ const dy = segment.end.y - segment.start.y;
7243
+ const segLen = Math.hypot(dx, dy);
7244
+ if (segLen > 0) {
7245
+ const nx = -dy / segLen;
7246
+ const ny = dx / segLen;
7247
+ const needed = (Math.abs(nx) * layout2.box.width + Math.abs(ny) * layout2.box.height) / 2 + EDGE_LABEL_CLEARANCE;
7248
+ const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
7249
+ for (let step = 1; step <= maxSteps; step += 1) {
7250
+ const offset = EDGE_LABEL_CLEARANCE * step;
7251
+ candidates.push(
7252
+ { x: placement.x + nx * offset, y: placement.y + ny * offset },
7253
+ { x: placement.x - nx * offset, y: placement.y - ny * offset }
7254
+ );
7255
+ }
7256
+ }
7257
+ }
7258
+ const totalLen = points.reduce((sum, p, idx) => {
7259
+ if (idx === 0) return 0;
7260
+ const prev = points[idx - 1];
7261
+ return sum + Math.hypot((p?.x ?? 0) - (prev?.x ?? 0), (p?.y ?? 0) - (prev?.y ?? 0));
7262
+ }, 0);
7263
+ if (totalLen > 200) {
7264
+ for (const ratio of [0.25, 0.75]) {
7265
+ const qp = labelPlacementAtRatio(points, ratio, totalLen);
7266
+ if (qp !== void 0) {
7267
+ candidates.push(qp);
7268
+ const qTargetDist = totalLen * ratio;
7269
+ let qTravelled = 0;
7270
+ let seg;
7271
+ for (let si = 1; si < points.length; si++) {
7272
+ const sp = points[si - 1];
7273
+ const sc = points[si];
7274
+ if (sp === void 0 || sc === void 0) continue;
7275
+ const sl = Math.hypot(sc.x - sp.x, sc.y - sp.y);
7276
+ if (sl <= 0) continue;
7277
+ if (qTravelled + sl >= qTargetDist) {
7278
+ seg = { start: sp, end: sc, length: sl };
7279
+ break;
7280
+ }
7281
+ qTravelled += sl;
7282
+ }
7283
+ if (seg !== void 0) {
7284
+ const segLen = Math.hypot(
7285
+ seg.end.x - seg.start.x,
7286
+ seg.end.y - seg.start.y
7287
+ );
7288
+ 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;
7289
+ const qpMaxSteps = Math.max(
7290
+ 12,
7291
+ Math.ceil(qpNeeded / EDGE_LABEL_CLEARANCE)
7292
+ );
7293
+ for (let step = 1; step <= qpMaxSteps; step += 1) {
7294
+ const offset = EDGE_LABEL_CLEARANCE * step;
7295
+ if (seg.start.y === seg.end.y) {
7296
+ candidates.push(
7297
+ { x: qp.x, y: qp.y - offset },
7298
+ { x: qp.x, y: qp.y + offset }
7299
+ );
7300
+ } else if (seg.start.x === seg.end.x) {
7301
+ candidates.push(
7302
+ { x: qp.x - offset, y: qp.y },
7303
+ { x: qp.x + offset, y: qp.y }
7304
+ );
7305
+ } else {
7306
+ const nx = -(seg.end.y - seg.start.y) / segLen;
7307
+ const ny = (seg.end.x - seg.start.x) / segLen;
7308
+ candidates.push(
7309
+ { x: qp.x + nx * offset, y: qp.y + ny * offset },
7310
+ { x: qp.x - nx * offset, y: qp.y - ny * offset }
7311
+ );
7312
+ }
7313
+ }
7314
+ }
7315
+ }
7316
+ }
7024
7317
  }
7025
7318
  return candidates;
7026
7319
  }
@@ -7077,6 +7370,34 @@ function nonZeroSegments2(points) {
7077
7370
  }
7078
7371
  return segments;
7079
7372
  }
7373
+ function labelPlacementAtRatio(points, ratio, totalLength) {
7374
+ if (points.length < 2 || ratio < 0 || ratio > 1) {
7375
+ return void 0;
7376
+ }
7377
+ const targetDist = totalLength * ratio;
7378
+ let travelled = 0;
7379
+ for (let idx = 1; idx < points.length; idx++) {
7380
+ const prev = points[idx - 1];
7381
+ const curr = points[idx];
7382
+ if (prev === void 0 || curr === void 0) {
7383
+ continue;
7384
+ }
7385
+ const segLen = Math.hypot(curr.x - prev.x, curr.y - prev.y);
7386
+ if (segLen <= 0) {
7387
+ continue;
7388
+ }
7389
+ if (travelled + segLen >= targetDist) {
7390
+ const t = (targetDist - travelled) / segLen;
7391
+ const offset = labelOffset2({ start: prev, end: curr, length: segLen });
7392
+ return {
7393
+ x: prev.x + (curr.x - prev.x) * t + offset.x,
7394
+ y: prev.y + (curr.y - prev.y) * t + offset.y
7395
+ };
7396
+ }
7397
+ travelled += segLen;
7398
+ }
7399
+ return void 0;
7400
+ }
7080
7401
  function labelOffset2(segment) {
7081
7402
  const offset = 10;
7082
7403
  const dx = segment.end.x - segment.start.x;
@@ -7191,7 +7512,7 @@ function renderDiagramDsl(source, options = {}) {
7191
7512
  return { diagnostics };
7192
7513
  }
7193
7514
  const solved = solveDiagram(normalized.diagram, {
7194
- routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
7515
+ routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : normalized.diagram.metadata?.routeKind === "obstacle-avoiding" ? "obstacle-avoiding" : "orthogonal",
7195
7516
  ...solvePortShiftingOption(normalized.diagram.metadata?.portShifting),
7196
7517
  ...options.textMeasurer === void 0 ? {} : { textMeasurer: options.textMeasurer }
7197
7518
  });
@@ -7383,6 +7704,7 @@ function isPointLikeRecord(value) {
7383
7704
 
7384
7705
  exports.DEFAULT_CANONICAL_PRECISION = DEFAULT_CANONICAL_PRECISION;
7385
7706
  exports.DEFAULT_DSL_MAX_BYTES = DEFAULT_DSL_MAX_BYTES;
7707
+ exports.DELIVERABILITY_DIAGNOSTIC_CODES = DELIVERABILITY_DIAGNOSTIC_CODES;
7386
7708
  exports.DeterministicTextMeasurer = DeterministicTextMeasurer;
7387
7709
  exports.LabelFitter = LabelFitter;
7388
7710
  exports.PretextTextMeasurer = PretextTextMeasurer;