@crazyhappyone/auto-graph 0.1.3 → 0.1.4

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.
@@ -1292,6 +1292,9 @@ function applyLayoutConstraints(input) {
1292
1292
  const nodeById = new Map(input.nodes.map((node) => [node.id, node]));
1293
1293
  applyFixedPositionLocks(input.nodes, boxes, locks, diagnostics);
1294
1294
  applyExactPositions(input.constraints, boxes, locks, diagnostics, nodeById);
1295
+ if (input.distributeContainedChildren) {
1296
+ yieldFixedPositionLocks(input, boxes, locks);
1297
+ }
1295
1298
  applyContainment(input.constraints, boxes, locks, diagnostics, false);
1296
1299
  applyRelative(input.constraints, boxes, locks, diagnostics);
1297
1300
  applyAlign(input.constraints, boxes, locks, diagnostics);
@@ -1305,6 +1308,13 @@ function applyLayoutConstraints(input) {
1305
1308
  );
1306
1309
  applyContainment(input.constraints, boxes, locks, diagnostics, true);
1307
1310
  applyDistributeContained(input, boxes, locks, diagnostics);
1311
+ if (input.distributeContainedChildren) {
1312
+ const diagBefore = diagnostics.length;
1313
+ applyContainment(input.constraints, boxes, locks, diagnostics, true);
1314
+ applyDistributeContained(input, boxes, locks, diagnostics);
1315
+ dedupReplayDiagnostics(diagnostics, diagBefore);
1316
+ }
1317
+ removeResolvedConstraintDiagnostics(input.constraints, boxes, diagnostics);
1308
1318
  reportOverlaps(boxes, diagnostics, containmentOverlapKeys(input.constraints));
1309
1319
  reportIntraContainerOverflow(input, boxes, diagnostics);
1310
1320
  return { boxes, locks, diagnostics };
@@ -1350,6 +1360,62 @@ function applyFixedPositionLocks(nodes, boxes, locks, diagnostics) {
1350
1360
  locks.set(node.id, { nodeId: node.id, source: "fixed-position" });
1351
1361
  }
1352
1362
  }
1363
+ function dedupReplayDiagnostics(diagnostics, keepUpTo) {
1364
+ const seen = /* @__PURE__ */ new Set();
1365
+ for (let i = 0; i < keepUpTo && i < diagnostics.length; i += 1) {
1366
+ const d = diagnostics[i];
1367
+ if (d === void 0) continue;
1368
+ seen.add(diagnosticFingerprint(d));
1369
+ }
1370
+ for (let i = diagnostics.length - 1; i >= keepUpTo; i -= 1) {
1371
+ const d = diagnostics[i];
1372
+ if (d === void 0) continue;
1373
+ const fp = diagnosticFingerprint(d);
1374
+ if (seen.has(fp)) {
1375
+ diagnostics.splice(i, 1);
1376
+ } else {
1377
+ seen.add(fp);
1378
+ }
1379
+ }
1380
+ }
1381
+ function diagnosticFingerprint(d) {
1382
+ const nodeId = typeof d.detail?.nodeId === "string" ? d.detail.nodeId : "";
1383
+ const containerId = typeof d.detail?.containerId === "string" ? d.detail.containerId : "";
1384
+ return `${d.code}|${nodeId}|${containerId}`;
1385
+ }
1386
+ function yieldFixedPositionLocks(input, boxes, locks) {
1387
+ for (const c of input.constraints) {
1388
+ if (c.kind !== "containment") continue;
1389
+ const container = boxes.get(c.containerId);
1390
+ if (container === void 0) continue;
1391
+ const content = contentBox(container, c.padding);
1392
+ const mainAxis = input.direction === "LR" || input.direction === "RL" ? "width" : "height";
1393
+ const crossAxis = mainAxis === "width" ? "height" : "width";
1394
+ let eligible = 0;
1395
+ for (const childId of c.childIds) {
1396
+ const box = boxes.get(childId);
1397
+ if (box === void 0) continue;
1398
+ const lock = locks.get(childId);
1399
+ if (lock?.source === "exact-position") continue;
1400
+ const fits = box[mainAxis] <= content[mainAxis] && box[crossAxis] <= content[crossAxis];
1401
+ if (fits) {
1402
+ eligible += 1;
1403
+ }
1404
+ }
1405
+ if (eligible < 2) continue;
1406
+ for (const childId of c.childIds) {
1407
+ const lock = locks.get(childId);
1408
+ if (lock?.source === "fixed-position") {
1409
+ const box = boxes.get(childId);
1410
+ if (box === void 0) continue;
1411
+ const fits = box[mainAxis] <= content[mainAxis] && box[crossAxis] <= content[crossAxis];
1412
+ if (fits) {
1413
+ locks.delete(childId);
1414
+ }
1415
+ }
1416
+ }
1417
+ }
1418
+ }
1353
1419
  function applyExactPositions(constraints, boxes, locks, diagnostics, nodeById) {
1354
1420
  for (const constraint of constraints) {
1355
1421
  if (constraint.kind !== "exact-position") {
@@ -1422,7 +1488,7 @@ function applyContainment(constraints, boxes, locks, diagnostics, reportOverflow
1422
1488
  code: "constraints.locked-target-not-moved",
1423
1489
  message: `Locked child ${childId} was not moved into containment.`,
1424
1490
  path: ["constraints", constraint.id ?? constraint.containerId],
1425
- detail: { nodeId: childId }
1491
+ detail: { nodeId: childId, containerId: constraint.containerId }
1426
1492
  });
1427
1493
  if (!isInside(child, content)) {
1428
1494
  diagnostics.push({
@@ -1574,6 +1640,60 @@ function repairOverlaps(input, boxes, locks, diagnostics, siblingPairs) {
1574
1640
  }
1575
1641
  reportOverlaps(boxes, diagnostics, ignoredPairs);
1576
1642
  }
1643
+ function removeResolvedConstraintDiagnostics(constraints, boxes, diagnostics) {
1644
+ for (let i = diagnostics.length - 1; i >= 0; i -= 1) {
1645
+ const d = diagnostics[i];
1646
+ if (d === void 0) continue;
1647
+ if (d.code === "constraints.overlap.unresolved") {
1648
+ const aId = d.detail?.firstId;
1649
+ const bId = d.detail?.secondId;
1650
+ if (typeof aId !== "string" || typeof bId !== "string") continue;
1651
+ const a = boxes.get(aId);
1652
+ const b = boxes.get(bId);
1653
+ if (a !== void 0 && b !== void 0 && !intersectsAabb(a, b)) {
1654
+ diagnostics.splice(i, 1);
1655
+ }
1656
+ continue;
1657
+ }
1658
+ if (d.code === "constraints.containment.impossible" || d.code === "constraints.locked-target-not-moved" && typeof d.message === "string" && d.message.includes("not moved into containment")) {
1659
+ const nodeId = d.detail?.nodeId;
1660
+ if (typeof nodeId !== "string") continue;
1661
+ const child = boxes.get(nodeId);
1662
+ if (child === void 0) continue;
1663
+ const diagContainerId = typeof d.detail?.containerId === "string" ? d.detail.containerId : void 0;
1664
+ let resolved = false;
1665
+ for (const c of constraints) {
1666
+ if (c.kind !== "containment") continue;
1667
+ if (!c.childIds.includes(nodeId)) continue;
1668
+ if (diagContainerId !== void 0 && c.containerId !== diagContainerId) {
1669
+ continue;
1670
+ }
1671
+ const container = boxes.get(c.containerId);
1672
+ if (container === void 0) continue;
1673
+ const content = contentBox(container, c.padding);
1674
+ if (isInside(child, content)) {
1675
+ diagnostics.splice(i, 1);
1676
+ resolved = true;
1677
+ }
1678
+ break;
1679
+ }
1680
+ if (!resolved && diagContainerId !== void 0) {
1681
+ for (const c of constraints) {
1682
+ if (c.kind !== "containment") continue;
1683
+ if (c.containerId !== diagContainerId) continue;
1684
+ if (!c.childIds.includes(nodeId)) continue;
1685
+ const container = boxes.get(c.containerId);
1686
+ if (container === void 0) continue;
1687
+ const content = contentBox(container, c.padding);
1688
+ if (isInside(child, content)) {
1689
+ diagnostics.splice(i, 1);
1690
+ }
1691
+ break;
1692
+ }
1693
+ }
1694
+ }
1695
+ }
1696
+ }
1577
1697
  function reportOverlaps(boxes, diagnostics, ignoredPairs = /* @__PURE__ */ new Set()) {
1578
1698
  const ids = [...boxes.keys()].sort();
1579
1699
  const reported = new Set(
@@ -1881,6 +2001,12 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
1881
2001
  continue;
1882
2002
  }
1883
2003
  if (locks.has(childId)) {
2004
+ const lock = locks.get(childId);
2005
+ if (lock?.source === "fixed-position") {
2006
+ unlocked.push({ id: childId, box });
2007
+ locks.delete(childId);
2008
+ continue;
2009
+ }
1884
2010
  diagnostics.push({
1885
2011
  severity: "warning",
1886
2012
  code: "constraints.locked-target-not-moved",
@@ -1939,6 +2065,7 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
1939
2065
  });
1940
2066
  }
1941
2067
  boxes.set(child.id, clamped);
2068
+ locks.delete(child.id);
1942
2069
  pos = clamped[axis] + clamped[mainSize] + minGap;
1943
2070
  }
1944
2071
  diagnostics.push({
@@ -3388,7 +3515,7 @@ function routeEdge(input) {
3388
3515
  const rerouted = greedyRerouteAroundObstacles(
3389
3516
  bestPoints2,
3390
3517
  allObstacles,
3391
- Math.min(maxAttempts, 3)
3518
+ maxAttempts
3392
3519
  );
3393
3520
  const reroutedAvoidsEndpointInteriors = !routeIntersectsEndpointInteriors(
3394
3521
  rerouted,
@@ -4150,9 +4277,7 @@ function solveDiagram(diagram, options = {}) {
4150
4277
  options?.overlapSpacing ?? 40,
4151
4278
  Math.max(0, options?.minLaneGutter ?? 0)
4152
4279
  );
4153
- if (swimlaneContracts.layouts.size > 0) {
4154
- removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
4155
- }
4280
+ removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
4156
4281
  diagnostics.push(...swimlaneContracts.diagnostics);
4157
4282
  const coordinatedNodes = coordinateNodes(
4158
4283
  styledNodes,
@@ -6095,7 +6220,8 @@ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacle
6095
6220
  ...softObstacles,
6096
6221
  ...routeTextObstacles
6097
6222
  ],
6098
- hardObstacles
6223
+ hardObstacles,
6224
+ ...options.maxRoutingAttempts === void 0 ? {} : { maxRoutingAttempts: options.maxRoutingAttempts }
6099
6225
  });
6100
6226
  diagnostics.push(
6101
6227
  ...route.diagnostics.map((diagnostic) => ({
@@ -6578,7 +6704,8 @@ function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes,
6578
6704
  for (const candidate of edgeLabelAnchorCandidates(
6579
6705
  edge.points,
6580
6706
  placement,
6581
- layout2
6707
+ layout2,
6708
+ baseOffset
6582
6709
  )) {
6583
6710
  const labelBox = {
6584
6711
  x: candidate.x - layout2.box.width / 2,
@@ -6610,8 +6737,8 @@ function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes,
6610
6737
  }
6611
6738
  return placement;
6612
6739
  }
6613
- function edgeLabelAnchorCandidates(points, placement, layout2) {
6614
- const segment = labelSegmentOnPolyline(points);
6740
+ function edgeLabelAnchorCandidates(points, placement, layout2, baseOffset = 10) {
6741
+ const segment = labelSegmentOnPolyline(points, baseOffset);
6615
6742
  if (segment === void 0) {
6616
6743
  return [placement];
6617
6744
  }
@@ -6661,7 +6788,7 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
6661
6788
  }, 0);
6662
6789
  if (totalLen > 200) {
6663
6790
  for (const ratio of [0.25, 0.75]) {
6664
- const qp = labelPlacementAtRatio(points, ratio, totalLen);
6791
+ const qp = labelPlacementAtRatio(points, ratio, totalLen, baseOffset);
6665
6792
  if (qp !== void 0) {
6666
6793
  candidates.push(qp);
6667
6794
  const qTargetDist = totalLen * ratio;
@@ -6747,7 +6874,7 @@ function labelSegmentOnPolyline(points, baseOffset = 10) {
6747
6874
  if (last === void 0) {
6748
6875
  return void 0;
6749
6876
  }
6750
- const offset = labelOffset2(last);
6877
+ const offset = labelOffset2(last, baseOffset);
6751
6878
  return {
6752
6879
  start: last.start,
6753
6880
  end: last.end,
@@ -6769,7 +6896,7 @@ function nonZeroSegments2(points) {
6769
6896
  }
6770
6897
  return segments;
6771
6898
  }
6772
- function labelPlacementAtRatio(points, ratio, totalLength) {
6899
+ function labelPlacementAtRatio(points, ratio, totalLength, baseOffset = 10) {
6773
6900
  if (points.length < 2 || ratio < 0 || ratio > 1) {
6774
6901
  return void 0;
6775
6902
  }
@@ -6787,7 +6914,10 @@ function labelPlacementAtRatio(points, ratio, totalLength) {
6787
6914
  }
6788
6915
  if (travelled + segLen >= targetDist) {
6789
6916
  const t = (targetDist - travelled) / segLen;
6790
- const offset = labelOffset2({ start: prev, end: curr, length: segLen });
6917
+ const offset = labelOffset2(
6918
+ { start: prev, end: curr, length: segLen },
6919
+ baseOffset
6920
+ );
6791
6921
  return {
6792
6922
  x: prev.x + (curr.x - prev.x) * t + offset.x,
6793
6923
  y: prev.y + (curr.y - prev.y) * t + offset.y