@crazyhappyone/auto-graph 0.1.3 → 0.2.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/index.js CHANGED
@@ -96,6 +96,9 @@ 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
+ if (input.distributeContainedChildren) {
100
+ yieldFixedPositionLocks(input, boxes, locks);
101
+ }
99
102
  applyContainment(input.constraints, boxes, locks, diagnostics, false);
100
103
  applyRelative(input.constraints, boxes, locks, diagnostics);
101
104
  applyAlign(input.constraints, boxes, locks, diagnostics);
@@ -109,6 +112,13 @@ function applyLayoutConstraints(input) {
109
112
  );
110
113
  applyContainment(input.constraints, boxes, locks, diagnostics, true);
111
114
  applyDistributeContained(input, boxes, locks, diagnostics);
115
+ if (input.distributeContainedChildren) {
116
+ const diagBefore = diagnostics.length;
117
+ applyContainment(input.constraints, boxes, locks, diagnostics, true);
118
+ applyDistributeContained(input, boxes, locks, diagnostics);
119
+ dedupReplayDiagnostics(diagnostics, diagBefore);
120
+ }
121
+ removeResolvedConstraintDiagnostics(input.constraints, boxes, diagnostics);
112
122
  reportOverlaps(boxes, diagnostics, containmentOverlapKeys(input.constraints));
113
123
  reportIntraContainerOverflow(input, boxes, diagnostics);
114
124
  return { boxes, locks, diagnostics };
@@ -154,6 +164,62 @@ function applyFixedPositionLocks(nodes, boxes, locks, diagnostics) {
154
164
  locks.set(node.id, { nodeId: node.id, source: "fixed-position" });
155
165
  }
156
166
  }
167
+ function dedupReplayDiagnostics(diagnostics, keepUpTo) {
168
+ const seen = /* @__PURE__ */ new Set();
169
+ for (let i = 0; i < keepUpTo && i < diagnostics.length; i += 1) {
170
+ const d = diagnostics[i];
171
+ if (d === void 0) continue;
172
+ seen.add(diagnosticFingerprint(d));
173
+ }
174
+ for (let i = diagnostics.length - 1; i >= keepUpTo; i -= 1) {
175
+ const d = diagnostics[i];
176
+ if (d === void 0) continue;
177
+ const fp = diagnosticFingerprint(d);
178
+ if (seen.has(fp)) {
179
+ diagnostics.splice(i, 1);
180
+ } else {
181
+ seen.add(fp);
182
+ }
183
+ }
184
+ }
185
+ function diagnosticFingerprint(d) {
186
+ const nodeId = typeof d.detail?.nodeId === "string" ? d.detail.nodeId : "";
187
+ const containerId = typeof d.detail?.containerId === "string" ? d.detail.containerId : "";
188
+ return `${d.code}|${nodeId}|${containerId}`;
189
+ }
190
+ function yieldFixedPositionLocks(input, boxes, locks) {
191
+ for (const c of input.constraints) {
192
+ if (c.kind !== "containment") continue;
193
+ const container = boxes.get(c.containerId);
194
+ if (container === void 0) continue;
195
+ const content = contentBox(container, c.padding);
196
+ const mainAxis = input.direction === "LR" || input.direction === "RL" ? "width" : "height";
197
+ const crossAxis = mainAxis === "width" ? "height" : "width";
198
+ let eligible = 0;
199
+ for (const childId of c.childIds) {
200
+ const box = boxes.get(childId);
201
+ if (box === void 0) continue;
202
+ const lock = locks.get(childId);
203
+ if (lock?.source === "exact-position") continue;
204
+ const fits = box[mainAxis] <= content[mainAxis] && box[crossAxis] <= content[crossAxis];
205
+ if (fits) {
206
+ eligible += 1;
207
+ }
208
+ }
209
+ if (eligible < 2) continue;
210
+ for (const childId of c.childIds) {
211
+ const lock = locks.get(childId);
212
+ if (lock?.source === "fixed-position") {
213
+ const box = boxes.get(childId);
214
+ if (box === void 0) continue;
215
+ const fits = box[mainAxis] <= content[mainAxis] && box[crossAxis] <= content[crossAxis];
216
+ if (fits) {
217
+ locks.delete(childId);
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
157
223
  function applyExactPositions(constraints, boxes, locks, diagnostics, nodeById) {
158
224
  for (const constraint of constraints) {
159
225
  if (constraint.kind !== "exact-position") {
@@ -226,7 +292,7 @@ function applyContainment(constraints, boxes, locks, diagnostics, reportOverflow
226
292
  code: "constraints.locked-target-not-moved",
227
293
  message: `Locked child ${childId} was not moved into containment.`,
228
294
  path: ["constraints", constraint.id ?? constraint.containerId],
229
- detail: { nodeId: childId }
295
+ detail: { nodeId: childId, containerId: constraint.containerId }
230
296
  });
231
297
  if (!isInside(child, content)) {
232
298
  diagnostics.push({
@@ -378,6 +444,60 @@ function repairOverlaps(input, boxes, locks, diagnostics, siblingPairs) {
378
444
  }
379
445
  reportOverlaps(boxes, diagnostics, ignoredPairs);
380
446
  }
447
+ function removeResolvedConstraintDiagnostics(constraints, boxes, diagnostics) {
448
+ for (let i = diagnostics.length - 1; i >= 0; i -= 1) {
449
+ const d = diagnostics[i];
450
+ if (d === void 0) continue;
451
+ if (d.code === "constraints.overlap.unresolved") {
452
+ const aId = d.detail?.firstId;
453
+ const bId = d.detail?.secondId;
454
+ if (typeof aId !== "string" || typeof bId !== "string") continue;
455
+ const a = boxes.get(aId);
456
+ const b = boxes.get(bId);
457
+ if (a !== void 0 && b !== void 0 && !intersectsAabb(a, b)) {
458
+ diagnostics.splice(i, 1);
459
+ }
460
+ continue;
461
+ }
462
+ if (d.code === "constraints.containment.impossible" || d.code === "constraints.locked-target-not-moved" && typeof d.message === "string" && d.message.includes("not moved into containment")) {
463
+ const nodeId = d.detail?.nodeId;
464
+ if (typeof nodeId !== "string") continue;
465
+ const child = boxes.get(nodeId);
466
+ if (child === void 0) continue;
467
+ const diagContainerId = typeof d.detail?.containerId === "string" ? d.detail.containerId : void 0;
468
+ let resolved = false;
469
+ for (const c of constraints) {
470
+ if (c.kind !== "containment") continue;
471
+ if (!c.childIds.includes(nodeId)) continue;
472
+ if (diagContainerId !== void 0 && c.containerId !== diagContainerId) {
473
+ continue;
474
+ }
475
+ const container = boxes.get(c.containerId);
476
+ if (container === void 0) continue;
477
+ const content = contentBox(container, c.padding);
478
+ if (isInside(child, content)) {
479
+ diagnostics.splice(i, 1);
480
+ resolved = true;
481
+ }
482
+ break;
483
+ }
484
+ if (!resolved && diagContainerId !== void 0) {
485
+ for (const c of constraints) {
486
+ if (c.kind !== "containment") continue;
487
+ if (c.containerId !== diagContainerId) continue;
488
+ if (!c.childIds.includes(nodeId)) continue;
489
+ const container = boxes.get(c.containerId);
490
+ if (container === void 0) continue;
491
+ const content = contentBox(container, c.padding);
492
+ if (isInside(child, content)) {
493
+ diagnostics.splice(i, 1);
494
+ }
495
+ break;
496
+ }
497
+ }
498
+ }
499
+ }
500
+ }
381
501
  function reportOverlaps(boxes, diagnostics, ignoredPairs = /* @__PURE__ */ new Set()) {
382
502
  const ids = [...boxes.keys()].sort();
383
503
  const reported = new Set(
@@ -685,6 +805,11 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
685
805
  continue;
686
806
  }
687
807
  if (locks.has(childId)) {
808
+ const lock = locks.get(childId);
809
+ if (lock?.source === "fixed-position") {
810
+ unlocked.push({ id: childId, box });
811
+ continue;
812
+ }
688
813
  diagnostics.push({
689
814
  severity: "warning",
690
815
  code: "constraints.locked-target-not-moved",
@@ -743,6 +868,7 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
743
868
  });
744
869
  }
745
870
  boxes.set(child.id, clamped);
871
+ locks.delete(child.id);
746
872
  pos = clamped[axis] + clamped[mainSize] + minGap;
747
873
  }
748
874
  diagnostics.push({
@@ -3854,6 +3980,213 @@ function isValidDimension(value) {
3854
3980
  return Number.isFinite(value) && value >= 0;
3855
3981
  }
3856
3982
 
3983
+ // src/routing/astar.ts
3984
+ function findObstacleFreePath(source, target, obstacles, options = {}) {
3985
+ const margin = options.margin ?? 0;
3986
+ const turnPenalty = options.turnPenalty ?? 50;
3987
+ const segmentPenalty = options.segmentPenalty ?? 1;
3988
+ const endpointObstacles = options.endpointObstacles ?? [];
3989
+ const maxNodes = options.maxNodes ?? 4e3;
3990
+ const xs = collectXs(source, target, obstacles, margin);
3991
+ const ys = collectYs(source, target, obstacles, margin);
3992
+ if (xs.length * ys.length > maxNodes) {
3993
+ return null;
3994
+ }
3995
+ const { nodes, nodeIndex } = buildGraph(xs, ys);
3996
+ connectHorizontalEdges(nodes, ys, obstacles, endpointObstacles, margin);
3997
+ connectVerticalEdges(nodes, xs, obstacles, endpointObstacles, margin);
3998
+ const path = aStarSearch(
3999
+ nodes,
4000
+ nodeIndex,
4001
+ source,
4002
+ target,
4003
+ turnPenalty,
4004
+ segmentPenalty
4005
+ );
4006
+ if (path === null) return null;
4007
+ return simplifyRoute(path);
4008
+ }
4009
+ function collectXs(source, target, obstacles, margin) {
4010
+ const set = /* @__PURE__ */ new Set();
4011
+ set.add(source.x);
4012
+ set.add(target.x);
4013
+ for (const obs of obstacles) {
4014
+ set.add(obs.x - margin - 2);
4015
+ set.add(obs.x + obs.width + margin + 2);
4016
+ }
4017
+ return [...set].sort((a, b) => a - b);
4018
+ }
4019
+ function collectYs(source, target, obstacles, margin) {
4020
+ const set = /* @__PURE__ */ new Set();
4021
+ set.add(source.y);
4022
+ set.add(target.y);
4023
+ for (const obs of obstacles) {
4024
+ set.add(obs.y - margin - 2);
4025
+ set.add(obs.y + obs.height + margin + 2);
4026
+ }
4027
+ return [...set].sort((a, b) => a - b);
4028
+ }
4029
+ function buildGraph(xs, ys) {
4030
+ const nodes = [];
4031
+ const nodeIndex = /* @__PURE__ */ new Map();
4032
+ for (let xi = 0; xi < xs.length; xi++) {
4033
+ for (let yi = 0; yi < ys.length; yi++) {
4034
+ const x = xs[xi];
4035
+ const y = ys[yi];
4036
+ const id = nodes.length;
4037
+ nodes.push({ x, y, id, neighbors: /* @__PURE__ */ new Map() });
4038
+ nodeIndex.set(`${x},${y}`, id);
4039
+ }
4040
+ }
4041
+ return { nodes, nodeIndex };
4042
+ }
4043
+ function connectHorizontalEdges(nodes, ys, obstacles, endpointObstacles, margin) {
4044
+ for (const y of ys) {
4045
+ const row = nodes.filter((n) => n.y === y).sort((a, b) => a.x - b.x);
4046
+ for (let i = 0; i < row.length - 1; i++) {
4047
+ const a = row[i];
4048
+ const b = row[i + 1];
4049
+ const dx = b.x - a.x;
4050
+ if (dx <= 0) continue;
4051
+ if (segmentCrossesAny(a, b, obstacles, endpointObstacles, margin)) {
4052
+ continue;
4053
+ }
4054
+ a.neighbors.set(b.id, dx);
4055
+ b.neighbors.set(a.id, dx);
4056
+ }
4057
+ }
4058
+ }
4059
+ function connectVerticalEdges(nodes, xs, obstacles, endpointObstacles, margin) {
4060
+ for (const x of xs) {
4061
+ const col = nodes.filter((n) => n.x === x).sort((a, b) => a.y - b.y);
4062
+ for (let i = 0; i < col.length - 1; i++) {
4063
+ const a = col[i];
4064
+ const b = col[i + 1];
4065
+ const dy = b.y - a.y;
4066
+ if (dy <= 0) continue;
4067
+ if (segmentCrossesAny(a, b, obstacles, endpointObstacles, margin)) {
4068
+ continue;
4069
+ }
4070
+ a.neighbors.set(b.id, dy);
4071
+ b.neighbors.set(a.id, dy);
4072
+ }
4073
+ }
4074
+ }
4075
+ function segmentCrossesAny(a, b, obstacles, endpointObstacles, margin) {
4076
+ for (const obs of obstacles) {
4077
+ if (segmentCrossesBoxStrict(a, b, obs, margin)) return true;
4078
+ }
4079
+ for (const ep of endpointObstacles) {
4080
+ if (segmentCrossesBoxStrict(a, b, ep, margin)) return true;
4081
+ }
4082
+ return false;
4083
+ }
4084
+ function segmentCrossesBoxStrict(start, end, box, margin) {
4085
+ const left = box.x - margin;
4086
+ const right = box.x + box.width + margin;
4087
+ const top = box.y - margin;
4088
+ const bottom = box.y + box.height + margin;
4089
+ if (pointInsideStrict(start, left, right, top, bottom)) return true;
4090
+ if (pointInsideStrict(end, left, right, top, bottom)) return true;
4091
+ if (start.x === end.x) {
4092
+ return start.x >= left && start.x <= right && rangesOverlap(start.y, end.y, top, bottom);
4093
+ }
4094
+ if (start.y === end.y) {
4095
+ return start.y >= top && start.y <= bottom && rangesOverlap(start.x, end.x, left, right);
4096
+ }
4097
+ return segmentEdgeIntersect(start, end, left, top, right, top) || segmentEdgeIntersect(start, end, right, top, right, bottom) || segmentEdgeIntersect(start, end, right, bottom, left, bottom) || segmentEdgeIntersect(start, end, left, bottom, left, top);
4098
+ }
4099
+ function pointInsideStrict(p, left, right, top, bottom) {
4100
+ return p.x > left && p.x < right && p.y > top && p.y < bottom;
4101
+ }
4102
+ function rangesOverlap(a, b, min, max) {
4103
+ const low = Math.min(a, b);
4104
+ const high = Math.max(a, b);
4105
+ return high > min && low < max;
4106
+ }
4107
+ function segmentEdgeIntersect(start, end, x1, y1, x2, y2) {
4108
+ const denominator = (end.x - start.x) * (y2 - y1) - (end.y - start.y) * (x2 - x1);
4109
+ if (denominator === 0) return false;
4110
+ const t = ((start.x - x1) * (y2 - y1) - (start.y - y1) * (x2 - x1)) / denominator;
4111
+ const u = ((start.x - x1) * (end.y - start.y) - (start.y - y1) * (end.x - start.x)) / denominator;
4112
+ return t > 0 && t < 1 && u > 0 && u < 1;
4113
+ }
4114
+ function aStarSearch(nodes, nodeIndex, source, target, turnPenalty, segmentPenalty) {
4115
+ const startId = nodeIndex.get(`${source.x},${source.y}`);
4116
+ const goalId = nodeIndex.get(`${target.x},${target.y}`);
4117
+ if (startId === void 0 || goalId === void 0) return null;
4118
+ const gScore = /* @__PURE__ */ new Map();
4119
+ gScore.set(startId, 0);
4120
+ const cameFrom = /* @__PURE__ */ new Map();
4121
+ const cameFromDir = /* @__PURE__ */ new Map();
4122
+ const openSet = [];
4123
+ openSet.push({
4124
+ id: startId,
4125
+ f: manhattan(source, target)
4126
+ });
4127
+ while (openSet.length > 0) {
4128
+ let bestIdx = 0;
4129
+ for (let i = 1; i < openSet.length; i++) {
4130
+ if (openSet[i].f < openSet[bestIdx].f) {
4131
+ bestIdx = i;
4132
+ }
4133
+ }
4134
+ const current = openSet.splice(bestIdx, 1)[0];
4135
+ if (current.id === goalId) {
4136
+ return reconstructPath(nodes, cameFrom, goalId);
4137
+ }
4138
+ const node = nodes[current.id];
4139
+ const currentG = gScore.get(current.id);
4140
+ const prevDir = cameFromDir.get(current.id);
4141
+ for (const [neighborId, edgeCost] of node.neighbors) {
4142
+ const neighbor = nodes[neighborId];
4143
+ const tentativeG = currentG + edgeCost * segmentPenalty;
4144
+ const newDir = neighbor.y === node.y ? "h" : "v";
4145
+ const turnCost = prevDir !== void 0 && prevDir !== newDir ? turnPenalty : 0;
4146
+ const totalG = tentativeG + turnCost;
4147
+ const existingG = gScore.get(neighborId);
4148
+ if (existingG === void 0 || totalG < existingG) {
4149
+ gScore.set(neighborId, totalG);
4150
+ cameFrom.set(neighborId, current.id);
4151
+ cameFromDir.set(neighborId, newDir);
4152
+ const f = totalG + manhattan(neighbor, target);
4153
+ openSet.push({ id: neighborId, f });
4154
+ }
4155
+ }
4156
+ }
4157
+ return null;
4158
+ }
4159
+ function manhattan(a, b) {
4160
+ return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
4161
+ }
4162
+ function reconstructPath(nodes, cameFrom, goalId) {
4163
+ const path = [];
4164
+ let current = goalId;
4165
+ while (current !== void 0) {
4166
+ const node = nodes[current];
4167
+ path.unshift({ x: node.x, y: node.y });
4168
+ current = cameFrom.get(current);
4169
+ }
4170
+ return path;
4171
+ }
4172
+ function simplifyRoute(points) {
4173
+ if (points.length <= 2) return [...points];
4174
+ const result = [points[0]];
4175
+ for (let i = 1; i < points.length - 1; i++) {
4176
+ const prev = result[result.length - 1];
4177
+ const curr = points[i];
4178
+ const next = points[i + 1];
4179
+ if (!areCollinear(prev, curr, next)) {
4180
+ result.push(curr);
4181
+ }
4182
+ }
4183
+ result.push(points[points.length - 1]);
4184
+ return result;
4185
+ }
4186
+ function areCollinear(a, b, c) {
4187
+ return a.x === b.x && b.x === c.x || a.y === b.y && b.y === c.y;
4188
+ }
4189
+
3857
4190
  // src/routing/routes.ts
3858
4191
  function routeEdge(input) {
3859
4192
  const diagnostics = [];
@@ -3899,6 +4232,43 @@ function routeEdge(input) {
3899
4232
  }
3900
4233
  return { points, diagnostics };
3901
4234
  }
4235
+ if ((input.kind ?? "orthogonal") === "obstacle-avoiding") {
4236
+ const endpointObstacles = endpointObstaclesForAutoAnchors(input);
4237
+ for (const { sourceAnchor, targetAnchor } of routeAnchorPairs(
4238
+ input,
4239
+ defaultAnchors
4240
+ )) {
4241
+ const source = getEdgePort(
4242
+ input.source,
4243
+ input.target.center,
4244
+ sourceAnchor
4245
+ );
4246
+ const target = getEdgePort(
4247
+ input.target,
4248
+ input.source.center,
4249
+ targetAnchor
4250
+ );
4251
+ const path = findObstacleFreePath(
4252
+ source,
4253
+ target,
4254
+ [...softObstacles, ...hardObstacles],
4255
+ {
4256
+ endpointObstacles
4257
+ }
4258
+ );
4259
+ if (path !== null && path.length >= 2) {
4260
+ const finalized = finalizeRoute(
4261
+ path,
4262
+ softObstacles,
4263
+ hardObstacles,
4264
+ diagnostics
4265
+ );
4266
+ if (!routeIntersectsObstacles(finalized, softObstacles) && !routeIntersectsObstacles(finalized, hardObstacles)) {
4267
+ return { points: finalized, diagnostics };
4268
+ }
4269
+ }
4270
+ }
4271
+ }
3902
4272
  const routeLaneObstacles = [...softObstacles, ...hardObstacles];
3903
4273
  const anchorPairs = routeAnchorPairs(input, defaultAnchors);
3904
4274
  const candidateRoutes = anchorPairs.flatMap(
@@ -3988,7 +4358,7 @@ function routeEdge(input) {
3988
4358
  const rerouted = greedyRerouteAroundObstacles(
3989
4359
  bestPoints2,
3990
4360
  allObstacles,
3991
- Math.min(maxAttempts, 3)
4361
+ maxAttempts
3992
4362
  );
3993
4363
  const reroutedAvoidsEndpointInteriors = !routeIntersectsEndpointInteriors(
3994
4364
  rerouted,
@@ -4101,7 +4471,7 @@ function routeEdge(input) {
4101
4471
  };
4102
4472
  }
4103
4473
  function finalizeRoute(points, softObstacles, hardObstacles, diagnostics) {
4104
- const simplified = simplifyRoute(points);
4474
+ const simplified = simplifyRoute2(points);
4105
4475
  if (simplified.length >= 3) {
4106
4476
  return simplified;
4107
4477
  }
@@ -4389,7 +4759,7 @@ function squaredDistance2(a, b) {
4389
4759
  const dy = a.y - b.y;
4390
4760
  return dx * dx + dy * dy;
4391
4761
  }
4392
- function simplifyRoute(points) {
4762
+ function simplifyRoute2(points) {
4393
4763
  const withoutDuplicates = [];
4394
4764
  for (const point2 of points) {
4395
4765
  const previous = withoutDuplicates.at(-1);
@@ -4401,7 +4771,7 @@ function simplifyRoute(points) {
4401
4771
  for (const point2 of withoutDuplicates) {
4402
4772
  const previous = simplified.at(-1);
4403
4773
  const beforePrevious = simplified.at(-2);
4404
- if (previous !== void 0 && beforePrevious !== void 0 && areCollinear(beforePrevious, previous, point2)) {
4774
+ if (previous !== void 0 && beforePrevious !== void 0 && areCollinear2(beforePrevious, previous, point2)) {
4405
4775
  simplified[simplified.length - 1] = { ...point2 };
4406
4776
  } else {
4407
4777
  simplified.push({ ...point2 });
@@ -4616,17 +4986,17 @@ function segmentIntersectsBox(start, end, box) {
4616
4986
  return true;
4617
4987
  }
4618
4988
  if (start.x === end.x) {
4619
- return start.x > left && start.x < right && rangesOverlap(start.y, end.y, top, bottom);
4989
+ return start.x > left && start.x < right && rangesOverlap2(start.y, end.y, top, bottom);
4620
4990
  }
4621
4991
  if (start.y === end.y) {
4622
- return start.y > top && start.y < bottom && rangesOverlap(start.x, end.x, left, right);
4992
+ return start.y > top && start.y < bottom && rangesOverlap2(start.x, end.x, left, right);
4623
4993
  }
4624
4994
  return segmentIntersectsBoxEdge(start, end, left, top, right, top) || segmentIntersectsBoxEdge(start, end, right, top, right, bottom) || segmentIntersectsBoxEdge(start, end, right, bottom, left, bottom) || segmentIntersectsBoxEdge(start, end, left, bottom, left, top);
4625
4995
  }
4626
4996
  function pointInsideBox(point2, box) {
4627
4997
  return point2.x > box.x && point2.x < box.x + box.width && point2.y > box.y && point2.y < box.y + box.height;
4628
4998
  }
4629
- function rangesOverlap(a, b, min, max) {
4999
+ function rangesOverlap2(a, b, min, max) {
4630
5000
  const low = Math.min(a, b);
4631
5001
  const high = Math.max(a, b);
4632
5002
  return high > min && low < max;
@@ -4650,7 +5020,7 @@ function segmentBox(a, b) {
4650
5020
  height: Math.max(1, Math.abs(a.y - b.y))
4651
5021
  };
4652
5022
  }
4653
- function areCollinear(a, b, c) {
5023
+ function areCollinear2(a, b, c) {
4654
5024
  return a.x === b.x && b.x === c.x || a.y === b.y && b.y === c.y;
4655
5025
  }
4656
5026
 
@@ -4730,6 +5100,7 @@ function solveDiagram(diagram, options = {}) {
4730
5100
  options,
4731
5101
  diagnostics
4732
5102
  );
5103
+ expandNodeBoxesForPorts(styledNodes, initialNodeBoxes, options, diagnostics);
4733
5104
  const constrained = applyLayoutConstraints({
4734
5105
  direction: diagram.direction,
4735
5106
  overlapSpacing: options?.overlapSpacing ?? 40,
@@ -4750,9 +5121,7 @@ function solveDiagram(diagram, options = {}) {
4750
5121
  options?.overlapSpacing ?? 40,
4751
5122
  Math.max(0, options?.minLaneGutter ?? 0)
4752
5123
  );
4753
- if (swimlaneContracts.layouts.size > 0) {
4754
- removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
4755
- }
5124
+ removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
4756
5125
  diagnostics.push(...swimlaneContracts.diagnostics);
4757
5126
  const coordinatedNodes = coordinateNodes(
4758
5127
  styledNodes,
@@ -4795,6 +5164,11 @@ function solveDiagram(diagram, options = {}) {
4795
5164
  swimlanes: coordinatedSwimlanes,
4796
5165
  ...options.textMeasurer === void 0 ? {} : { textMeasurer: options.textMeasurer }
4797
5166
  });
5167
+ const edgeLabelEstimates = estimateEdgeLabelAnnotations(
5168
+ styledEdges,
5169
+ nodeGeometryById,
5170
+ options.textMeasurer
5171
+ );
4798
5172
  const layoutBoxes = [
4799
5173
  ...coordinatedNodes.map((node) => node.box),
4800
5174
  ...coordinatedNodes.flatMap(
@@ -4875,7 +5249,10 @@ function solveDiagram(diagram, options = {}) {
4875
5249
  const frameTextAnnotation = frame === void 0 ? [] : [coordinateFrameTextAnnotation(frame, options.textMeasurer)];
4876
5250
  const routingTextObstacles = [
4877
5251
  ...baseTextAnnotations.filter(isPreRouteTextObstacle),
4878
- ...frameTextAnnotation.filter(isPreRouteTextObstacle)
5252
+ ...frameTextAnnotation.filter(isPreRouteTextObstacle),
5253
+ // Dry-run edge-label estimates so edges route around
5254
+ // each other's label areas (Issue #41).
5255
+ ...edgeLabelEstimates
4879
5256
  ];
4880
5257
  const margin = options.obstacleMargin ?? 0;
4881
5258
  const softObstacles = [
@@ -4908,7 +5285,8 @@ function solveDiagram(diagram, options = {}) {
4908
5285
  hardObstacles,
4909
5286
  diagram.direction,
4910
5287
  options,
4911
- diagnostics
5288
+ diagnostics,
5289
+ coordinatedGroups
4912
5290
  );
4913
5291
  const edgeTextAnnotations = coordinateEdgeTextAnnotations(
4914
5292
  coordinatedEdges,
@@ -5024,22 +5402,7 @@ function expandLabelLayoutToNode(layout2, nodeSize) {
5024
5402
  y: layout2.box.y + offsetY,
5025
5403
  width: layout2.box.width,
5026
5404
  height: layout2.box.height
5027
- },
5028
- contentBox: {
5029
- x: layout2.contentBox.x + offsetX,
5030
- y: layout2.contentBox.y + offsetY,
5031
- width: layout2.contentBox.width,
5032
- height: layout2.contentBox.height
5033
- },
5034
- lines: layout2.lines.map((line) => ({
5035
- ...line,
5036
- box: {
5037
- x: line.box.x + offsetX,
5038
- y: line.box.y + offsetY,
5039
- width: line.box.width,
5040
- height: line.box.height
5041
- }
5042
- }))
5405
+ }
5043
5406
  };
5044
5407
  }
5045
5408
  function reportPageOverflow(contentBounds, pageBounds) {
@@ -5986,6 +6349,7 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
5986
6349
  });
5987
6350
  continue;
5988
6351
  }
6352
+ const ports = node.ports === void 0 ? void 0 : coordinatePorts(node, box, options.portShifting);
5989
6353
  const geometry = computeShapeGeometry({
5990
6354
  shape: node.shape,
5991
6355
  box,
@@ -5995,7 +6359,7 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
5995
6359
  id: node.id,
5996
6360
  ...node.label === void 0 ? {} : { label: node.label },
5997
6361
  ...node.style === void 0 ? {} : { style: node.style },
5998
- ...node.ports === void 0 ? {} : { ports: coordinatePorts(node, box, options.portShifting) },
6362
+ ...ports === void 0 ? {} : { ports },
5999
6363
  ...node.compartments === void 0 ? {} : { compartments: node.compartments },
6000
6364
  ...node.labelLayout === void 0 ? {} : { labelLayout: node.labelLayout },
6001
6365
  shape: node.shape,
@@ -6007,6 +6371,78 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
6007
6371
  }
6008
6372
  return coordinated;
6009
6373
  }
6374
+ var PORT_BOX_SIZE = 10;
6375
+ var MIN_PORT_EDGE_GAP = 12;
6376
+ function expandNodeBoxesForPorts(nodes, boxes, options, diagnostics) {
6377
+ const shiftingEnabled = options.portShifting?.enabled ?? true;
6378
+ if (!shiftingEnabled) return;
6379
+ const requestedSpacing = options.portShifting?.spacing ?? 24;
6380
+ const minSpacing = Math.max(
6381
+ requestedSpacing,
6382
+ PORT_BOX_SIZE + MIN_PORT_EDGE_GAP
6383
+ );
6384
+ for (const node of nodes) {
6385
+ if (node.ports === void 0 || node.ports.length === 0) continue;
6386
+ const box = boxes.get(node.id);
6387
+ if (box === void 0) continue;
6388
+ let heightExpansion = 0;
6389
+ let widthExpansion = 0;
6390
+ const portsBySide = /* @__PURE__ */ new Map();
6391
+ for (const port of node.ports) {
6392
+ const list = portsBySide.get(port.side) ?? [];
6393
+ list.push(port);
6394
+ portsBySide.set(port.side, list);
6395
+ }
6396
+ for (const [side, ports] of portsBySide) {
6397
+ const count = (ports ?? []).length;
6398
+ if (count <= 1) continue;
6399
+ const isVertical = side === "left" || side === "right";
6400
+ const availableSpan = isVertical ? box.height : box.width;
6401
+ const requiredSpan = (count - 1) * minSpacing + PORT_BOX_SIZE;
6402
+ if (requiredSpan > availableSpan) {
6403
+ const expansion = requiredSpan - availableSpan;
6404
+ if (isVertical) {
6405
+ heightExpansion = Math.max(heightExpansion, expansion);
6406
+ } else {
6407
+ widthExpansion = Math.max(widthExpansion, expansion);
6408
+ }
6409
+ diagnostics.push({
6410
+ severity: "info",
6411
+ code: "port_capacity_overflow",
6412
+ message: `Expanded node ${node.id} ${isVertical ? "height" : "width"} by ${Math.ceil(expansion)} px to fit ${count} port(s) on ${side} side.`,
6413
+ path: ["nodes", node.id, "ports"],
6414
+ detail: {
6415
+ nodeId: node.id,
6416
+ side,
6417
+ portCount: count,
6418
+ expansion: Math.ceil(expansion)
6419
+ }
6420
+ });
6421
+ }
6422
+ }
6423
+ if (heightExpansion > 0) {
6424
+ box.y -= heightExpansion / 2;
6425
+ box.height += heightExpansion;
6426
+ }
6427
+ if (widthExpansion > 0) {
6428
+ box.x -= widthExpansion / 2;
6429
+ box.width += widthExpansion;
6430
+ }
6431
+ if ((heightExpansion > 0 || widthExpansion > 0) && node.labelLayout !== void 0) {
6432
+ const layout2 = node.labelLayout;
6433
+ const newOffsetX = Math.max(0, (box.width - layout2.box.width) / 2);
6434
+ const newOffsetY = Math.max(0, (box.height - layout2.box.height) / 2);
6435
+ node.labelLayout = {
6436
+ ...layout2,
6437
+ box: {
6438
+ ...layout2.box,
6439
+ x: newOffsetX,
6440
+ y: newOffsetY
6441
+ }
6442
+ };
6443
+ }
6444
+ }
6445
+ }
6010
6446
  function coordinatePorts(node, nodeBox, portShifting) {
6011
6447
  const portsBySide = /* @__PURE__ */ new Map();
6012
6448
  for (const port of node.ports ?? []) {
@@ -6043,7 +6479,11 @@ function portAnchor(nodeBox, side, index, count, portShifting) {
6043
6479
  const requestedSpacing = portShifting?.spacing ?? 24;
6044
6480
  const maxOffset = side === "left" || side === "right" ? nodeBox.height / 2 : nodeBox.width / 2;
6045
6481
  const availableSpan = 2 * maxOffset;
6046
- const spacing = shiftingEnabled && count > 1 ? Math.min(requestedSpacing, availableSpan / (count - 1)) : requestedSpacing;
6482
+ const minSpacing = PORT_BOX_SIZE + MIN_PORT_EDGE_GAP;
6483
+ const spacing = shiftingEnabled && count > 1 ? Math.max(
6484
+ Math.min(requestedSpacing, availableSpan / (count - 1)),
6485
+ minSpacing
6486
+ ) : requestedSpacing;
6047
6487
  const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
6048
6488
  switch (side) {
6049
6489
  case "left":
@@ -6069,7 +6509,7 @@ function portAnchor(nodeBox, side, index, count, portShifting) {
6069
6509
  }
6070
6510
  }
6071
6511
  function portBox(anchor) {
6072
- const size = 10;
6512
+ const size = PORT_BOX_SIZE;
6073
6513
  return {
6074
6514
  x: anchor.x - size / 2,
6075
6515
  y: anchor.y - size / 2,
@@ -6658,7 +7098,7 @@ function evidenceOverlapDiagnostic(block, conflict) {
6658
7098
  }
6659
7099
  };
6660
7100
  }
6661
- function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacles, textObstacles, hardObstacles, direction, options, diagnostics) {
7101
+ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacles, textObstacles, hardObstacles, direction, options, diagnostics, groups) {
6662
7102
  const coordinated = [];
6663
7103
  const coordinatedNodeById = new Map(
6664
7104
  coordinatedNodes.map((node) => [node.id, node])
@@ -6682,8 +7122,7 @@ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacle
6682
7122
  }
6683
7123
  const sourcePort = coordinatedNodeById.get(edge.source.nodeId)?.ports?.find((port) => port.id === edge.source.portId);
6684
7124
  const targetPort = coordinatedNodeById.get(edge.target.nodeId)?.ports?.find((port) => port.id === edge.target.portId);
6685
- const connectedTextOwners = edgeConnectedTextOwnerIds(edge);
6686
- const routeTextObstacles = textObstacles.filter((annotation) => !connectedTextOwners.has(annotation.ownerId)).map((annotation) => annotation.box);
7125
+ const routeTextObstacles = textObstacles.filter((annotation) => !isEdgeConnectedTextAnnotation(edge, annotation)).map((annotation) => annotation.box);
6687
7126
  const route = routeEdge({
6688
7127
  kind: options.routeKind ?? "orthogonal",
6689
7128
  direction,
@@ -6696,9 +7135,11 @@ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacle
6696
7135
  (obstacle) => obstacle !== source.obstacleBox && obstacle !== target.obstacleBox
6697
7136
  ),
6698
7137
  ...softObstacles,
7138
+ ...groupObstaclesForEdge(edge, groups, options.obstacleMargin ?? 0),
6699
7139
  ...routeTextObstacles
6700
7140
  ],
6701
- hardObstacles
7141
+ hardObstacles,
7142
+ ...options.maxRoutingAttempts === void 0 ? {} : { maxRoutingAttempts: options.maxRoutingAttempts }
6702
7143
  });
6703
7144
  diagnostics.push(
6704
7145
  ...route.diagnostics.map((diagnostic) => ({
@@ -6713,15 +7154,52 @@ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacle
6713
7154
  }
6714
7155
  return coordinated;
6715
7156
  }
6716
- function edgeConnectedTextOwnerIds(edge) {
6717
- const owners = /* @__PURE__ */ new Set();
6718
- if (edge.source.portId !== void 0) {
6719
- owners.add(`${edge.source.nodeId}.${edge.source.portId}`);
7157
+ function isEdgeConnectedTextAnnotation(edge, annotation) {
7158
+ switch (annotation.surfaceKind) {
7159
+ case "edge-label":
7160
+ return annotation.ownerId === edge.id;
7161
+ case "node-label":
7162
+ case "compartment-row":
7163
+ return annotation.ownerId === edge.source.nodeId || annotation.ownerId === edge.target.nodeId;
7164
+ case "port-label":
7165
+ return edge.source.portId !== void 0 && annotation.ownerId === `${edge.source.nodeId}.${edge.source.portId}` || edge.target.portId !== void 0 && annotation.ownerId === `${edge.target.nodeId}.${edge.target.portId}`;
7166
+ case "group-label":
7167
+ case "swimlane-label":
7168
+ case "frame-title":
7169
+ return false;
6720
7170
  }
6721
- if (edge.target.portId !== void 0) {
6722
- owners.add(`${edge.target.nodeId}.${edge.target.portId}`);
7171
+ }
7172
+ function ancestorGroupIds(groups, nodeId) {
7173
+ const direct = /* @__PURE__ */ new Set();
7174
+ for (const group of groups) {
7175
+ if (group.nodeIds.includes(nodeId)) {
7176
+ direct.add(group.id);
7177
+ }
7178
+ }
7179
+ let previousSize = -1;
7180
+ const ancestors = new Set(direct);
7181
+ while (ancestors.size !== previousSize) {
7182
+ previousSize = ancestors.size;
7183
+ for (const group of groups) {
7184
+ for (const candidate of ancestors) {
7185
+ if (group.groupIds.includes(candidate)) {
7186
+ ancestors.add(group.id);
7187
+ break;
7188
+ }
7189
+ }
7190
+ }
6723
7191
  }
6724
- return owners;
7192
+ return ancestors;
7193
+ }
7194
+ function groupObstaclesForEdge(edge, groups, margin) {
7195
+ const sourceAncestors = ancestorGroupIds(groups, edge.source.nodeId);
7196
+ const targetAncestors = ancestorGroupIds(groups, edge.target.nodeId);
7197
+ return groups.filter((group) => {
7198
+ if (sourceAncestors.has(group.id) || targetAncestors.has(group.id)) {
7199
+ return false;
7200
+ }
7201
+ return true;
7202
+ }).map((group) => margin === 0 ? group.box : expandBox(group.box, margin));
6725
7203
  }
6726
7204
  function coordinateBaseTextAnnotations(input) {
6727
7205
  const measurer = input.textMeasurer ?? createDefaultTextMeasurer();
@@ -6909,6 +7387,55 @@ function coordinateEdgeTextAnnotations(edges, obstacleBoxes, textMeasurer, label
6909
7387
  }
6910
7388
  return annotations;
6911
7389
  }
7390
+ function estimateEdgeLabelAnnotations(edges, nodes, textMeasurer) {
7391
+ const measurer = textMeasurer ?? createDefaultTextMeasurer();
7392
+ const annotations = [];
7393
+ for (const edge of edges) {
7394
+ if (edge.label?.text === void 0) {
7395
+ continue;
7396
+ }
7397
+ const sourceGeom = nodes.get(edge.source.nodeId);
7398
+ const targetGeom = nodes.get(edge.target.nodeId);
7399
+ if (sourceGeom === void 0 || targetGeom === void 0) {
7400
+ continue;
7401
+ }
7402
+ const layout2 = fitLabel(
7403
+ edge.label.text,
7404
+ {
7405
+ font: typographyTextStyle(edge.label, {
7406
+ fontFamily: "Arial",
7407
+ fontSize: 12,
7408
+ lineHeight: 14
7409
+ }),
7410
+ padding: { top: 0, right: 0, bottom: 0, left: 0 },
7411
+ minSize: { width: 0, height: 0 },
7412
+ maxWidth: 200
7413
+ },
7414
+ measurer
7415
+ );
7416
+ const cx = (sourceGeom.center.x + targetGeom.center.x) / 2;
7417
+ const cy = (sourceGeom.center.y + targetGeom.center.y) / 2;
7418
+ const box = {
7419
+ x: cx - layout2.box.width / 2,
7420
+ y: cy - layout2.box.height / 2,
7421
+ width: layout2.box.width,
7422
+ height: layout2.box.height
7423
+ };
7424
+ annotations.push({
7425
+ text: layout2.text,
7426
+ ownerId: edge.id,
7427
+ surfaceKind: "edge-label",
7428
+ box,
7429
+ anchor: { x: cx, y: cy },
7430
+ paddings: layout2.padding,
7431
+ lines: layout2.lines,
7432
+ fontFamily: normalizeOutputFontFamily(layout2.font),
7433
+ fontSize: layout2.font.fontSize,
7434
+ textBackend: layout2.textBackend
7435
+ });
7436
+ }
7437
+ return annotations;
7438
+ }
6912
7439
  function coordinateFrameTextAnnotation(frame, textMeasurer) {
6913
7440
  const layout2 = fitLabel(
6914
7441
  frame.titleTab,
@@ -7029,9 +7556,8 @@ function reportRouteTextClearance(edges, annotations) {
7029
7556
  const diagnostics = [];
7030
7557
  const relevantAnnotations = annotations.filter(isRouteClearanceText);
7031
7558
  for (const edge of edges) {
7032
- const connectedTextOwners = edgeConnectedTextOwnerIds(edge);
7033
7559
  for (const annotation of relevantAnnotations) {
7034
- if (annotation.ownerId === edge.id || connectedTextOwners.has(annotation.ownerId)) {
7560
+ if (isEdgeConnectedTextAnnotation(edge, annotation)) {
7035
7561
  continue;
7036
7562
  }
7037
7563
  if (!routeIntersectsTextBox(edge.points, annotation.box)) {
@@ -7055,9 +7581,6 @@ function reportRouteTextClearance(edges, annotations) {
7055
7581
  return diagnostics;
7056
7582
  }
7057
7583
  function isPreRouteTextObstacle(annotation) {
7058
- if (annotation.surfaceKind === "edge-label") {
7059
- return false;
7060
- }
7061
7584
  return isRouteClearanceText(annotation);
7062
7585
  }
7063
7586
  function isRouteClearanceText(annotation) {
@@ -7068,8 +7591,9 @@ function isRouteClearanceText(annotation) {
7068
7591
  case "frame-title":
7069
7592
  return true;
7070
7593
  case "node-label":
7071
- case "group-label":
7072
7594
  case "compartment-row":
7595
+ return true;
7596
+ case "group-label":
7073
7597
  return textExtendsOutsideAnchor(annotation);
7074
7598
  }
7075
7599
  }
@@ -7102,17 +7626,17 @@ function segmentIntersectsBox2(start, end, box) {
7102
7626
  return true;
7103
7627
  }
7104
7628
  if (start.x === end.x) {
7105
- return start.x > left && start.x < right && rangesOverlap2(start.y, end.y, top, bottom);
7629
+ return start.x > left && start.x < right && rangesOverlap3(start.y, end.y, top, bottom);
7106
7630
  }
7107
7631
  if (start.y === end.y) {
7108
- return start.y > top && start.y < bottom && rangesOverlap2(start.x, end.x, left, right);
7632
+ return start.y > top && start.y < bottom && rangesOverlap3(start.x, end.x, left, right);
7109
7633
  }
7110
7634
  return segmentIntersectsBoxEdge2(start, end, left, top, right, top) || segmentIntersectsBoxEdge2(start, end, right, top, right, bottom) || segmentIntersectsBoxEdge2(start, end, right, bottom, left, bottom) || segmentIntersectsBoxEdge2(start, end, left, bottom, left, top);
7111
7635
  }
7112
7636
  function pointInsideBox2(point2, box) {
7113
7637
  return point2.x > box.x && point2.x < box.x + box.width && point2.y > box.y && point2.y < box.y + box.height;
7114
7638
  }
7115
- function rangesOverlap2(a, b, min, max) {
7639
+ function rangesOverlap3(a, b, min, max) {
7116
7640
  const low = Math.min(a, b);
7117
7641
  const high = Math.max(a, b);
7118
7642
  return high > min && low < max;
@@ -7181,7 +7705,8 @@ function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes,
7181
7705
  for (const candidate of edgeLabelAnchorCandidates(
7182
7706
  edge.points,
7183
7707
  placement,
7184
- layout2
7708
+ layout2,
7709
+ baseOffset
7185
7710
  )) {
7186
7711
  const labelBox = {
7187
7712
  x: candidate.x - layout2.box.width / 2,
@@ -7213,8 +7738,8 @@ function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes,
7213
7738
  }
7214
7739
  return placement;
7215
7740
  }
7216
- function edgeLabelAnchorCandidates(points, placement, layout2) {
7217
- const segment = labelSegmentOnPolyline(points);
7741
+ function edgeLabelAnchorCandidates(points, placement, layout2, baseOffset = 10) {
7742
+ const segment = labelSegmentOnPolyline(points, baseOffset);
7218
7743
  if (segment === void 0) {
7219
7744
  return [placement];
7220
7745
  }
@@ -7264,7 +7789,7 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
7264
7789
  }, 0);
7265
7790
  if (totalLen > 200) {
7266
7791
  for (const ratio of [0.25, 0.75]) {
7267
- const qp = labelPlacementAtRatio(points, ratio, totalLen);
7792
+ const qp = labelPlacementAtRatio(points, ratio, totalLen, baseOffset);
7268
7793
  if (qp !== void 0) {
7269
7794
  candidates.push(qp);
7270
7795
  const qTargetDist = totalLen * ratio;
@@ -7350,7 +7875,7 @@ function labelSegmentOnPolyline(points, baseOffset = 10) {
7350
7875
  if (last === void 0) {
7351
7876
  return void 0;
7352
7877
  }
7353
- const offset = labelOffset2(last);
7878
+ const offset = labelOffset2(last, baseOffset);
7354
7879
  return {
7355
7880
  start: last.start,
7356
7881
  end: last.end,
@@ -7372,7 +7897,7 @@ function nonZeroSegments2(points) {
7372
7897
  }
7373
7898
  return segments;
7374
7899
  }
7375
- function labelPlacementAtRatio(points, ratio, totalLength) {
7900
+ function labelPlacementAtRatio(points, ratio, totalLength, baseOffset = 10) {
7376
7901
  if (points.length < 2 || ratio < 0 || ratio > 1) {
7377
7902
  return void 0;
7378
7903
  }
@@ -7390,7 +7915,10 @@ function labelPlacementAtRatio(points, ratio, totalLength) {
7390
7915
  }
7391
7916
  if (travelled + segLen >= targetDist) {
7392
7917
  const t = (targetDist - travelled) / segLen;
7393
- const offset = labelOffset2({ start: prev, end: curr, length: segLen });
7918
+ const offset = labelOffset2(
7919
+ { start: prev, end: curr, length: segLen },
7920
+ baseOffset
7921
+ );
7394
7922
  return {
7395
7923
  x: prev.x + (curr.x - prev.x) * t + offset.x,
7396
7924
  y: prev.y + (curr.y - prev.y) * t + offset.y
@@ -7704,6 +8232,6 @@ function isPointLikeRecord(value) {
7704
8232
  return isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number";
7705
8233
  }
7706
8234
 
7707
- export { DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DELIVERABILITY_DIAGNOSTIC_CODES, 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 };
8235
+ export { DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DELIVERABILITY_DIAGNOSTIC_CODES, 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, simplifyRoute2 as simplifyRoute, solveDiagram, solveDiagramSafe, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
7708
8236
  //# sourceMappingURL=index.js.map
7709
8237
  //# sourceMappingURL=index.js.map