@tscircuit/schematic-trace-solver 0.0.145 → 0.0.147

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.
Files changed (20) hide show
  1. package/dist/index.d.ts +9 -2
  2. package/dist/index.js +482 -86
  3. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +38 -30
  4. package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +30 -8
  5. package/lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts +530 -0
  6. package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +6 -21
  7. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +7 -23
  8. package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts +7 -14
  9. package/lib/types/InputProblem.ts +8 -0
  10. package/lib/utils/getNetLabelWidthForConnection.ts +63 -0
  11. package/package.json +1 -1
  12. package/tests/assets/inline-net-label-anchored-label-clearance.json +56 -0
  13. package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +80 -0
  14. package/tests/repros/repro-ti-power-output-section.input.ts +341 -0
  15. package/tests/repros/repro-ti-power-output-section.test.ts +13 -0
  16. package/tests/solvers/InlineNetLabelSolver/__snapshots__/fallback-net-label-width.snap.svg +69 -0
  17. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-anchored-label-clearance.snap.svg +54 -0
  18. package/tests/solvers/InlineNetLabelSolver/fallback-net-label-width.test.ts +152 -0
  19. package/tests/solvers/InlineNetLabelSolver/inline-net-label-anchored-label-clearance.test.ts +28 -0
  20. package/tests/solvers/InlineNetLabelSolver/push-anchored-net-labels-away.test.ts +227 -0
package/dist/index.js CHANGED
@@ -678,6 +678,50 @@ import "graphics-debug";
678
678
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
679
679
  import { calculateElbow as calculateElbow2 } from "calculate-elbow";
680
680
 
681
+ // lib/utils/getNetLabelWidthForConnection.ts
682
+ var getConfiguredWidth = (connections, includeFallbackNetLabelWidth) => {
683
+ const explicitWidth = connections.find(
684
+ (connection) => connection?.netLabelWidth !== void 0
685
+ )?.netLabelWidth;
686
+ if (explicitWidth !== void 0) return explicitWidth;
687
+ if (!includeFallbackNetLabelWidth) return void 0;
688
+ return connections.find(
689
+ (connection) => connection?.fallbackNetLabelWidth !== void 0
690
+ )?.fallbackNetLabelWidth;
691
+ };
692
+ var getNetLabelWidthForConnection = ({
693
+ inputProblem,
694
+ netId,
695
+ pinIds,
696
+ includeFallbackNetLabelWidth = true
697
+ }) => {
698
+ if (netId) {
699
+ const widthByNetId = getConfiguredWidth(
700
+ [
701
+ inputProblem.netConnections.find(
702
+ (connection) => connection.netId === netId
703
+ ),
704
+ inputProblem.directConnections.find(
705
+ (connection) => connection.netId === netId
706
+ )
707
+ ],
708
+ includeFallbackNetLabelWidth
709
+ );
710
+ if (widthByNetId !== void 0) return widthByNetId;
711
+ }
712
+ return getConfiguredWidth(
713
+ [
714
+ inputProblem.directConnections.find(
715
+ (connection) => connection.pinIds.some((pinId) => pinIds.includes(pinId))
716
+ ),
717
+ inputProblem.netConnections.find(
718
+ (connection) => connection.pinIds.some((pinId) => pinIds.includes(pinId))
719
+ )
720
+ ],
721
+ includeFallbackNetLabelWidth
722
+ );
723
+ };
724
+
681
725
  // lib/utils/textBoxBounds.ts
682
726
  function getTextBoxBounds(textBox, padding = {}) {
683
727
  return {
@@ -1281,7 +1325,12 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1281
1325
  const netId = this.connectionPair?.userNetId;
1282
1326
  if (!netId) return {};
1283
1327
  const orientations = this.inputProblem.availableNetLabelOrientations[netId] ?? ["x+", "x-", "y+", "y-"];
1284
- const netLabelWidth = this.getNetLabelWidthForConnectionPair(netId);
1328
+ const netLabelWidth = getNetLabelWidthForConnection({
1329
+ inputProblem: this.inputProblem,
1330
+ netId,
1331
+ pinIds: this.pins.map((pin) => pin.pinId),
1332
+ includeFallbackNetLabelWidth: false
1333
+ });
1285
1334
  const netLabelHeight = this.getNetLabelHeightForConnectionPair(netId);
1286
1335
  const padding = {
1287
1336
  minX: 0,
@@ -1315,24 +1364,6 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1315
1364
  }
1316
1365
  return padding;
1317
1366
  }
1318
- getNetLabelWidthForConnectionPair(netId) {
1319
- const ncWidth = this.inputProblem.netConnections.find(
1320
- (nc) => nc.netId === netId
1321
- )?.netLabelWidth;
1322
- if (ncWidth !== void 0) return ncWidth;
1323
- const dcWidthByNetId = this.inputProblem.directConnections.find(
1324
- (dc) => dc.netId === netId
1325
- )?.netLabelWidth;
1326
- if (dcWidthByNetId !== void 0) return dcWidthByNetId;
1327
- const pinIds = this.pins.map((p) => p.pinId);
1328
- const dcWidthByPinId = this.inputProblem.directConnections.find(
1329
- (dc) => dc.pinIds.some((pid) => pinIds.includes(pid))
1330
- )?.netLabelWidth;
1331
- if (dcWidthByPinId !== void 0) return dcWidthByPinId;
1332
- return this.inputProblem.netConnections.find(
1333
- (nc) => nc.pinIds.some((pid) => pinIds.includes(pid))
1334
- )?.netLabelWidth;
1335
- }
1336
1367
  getNetLabelHeightForConnectionPair(netId) {
1337
1368
  const ncHeight = this.inputProblem.netConnections.find(
1338
1369
  (nc) => nc.netId === netId
@@ -3217,27 +3248,15 @@ var NetLabelPlacementSolver = class extends BaseSolver {
3217
3248
  return groups;
3218
3249
  }
3219
3250
  getNetLabelWidthForGroup(group) {
3220
- if (group.netId) {
3221
- const ncWidth = this.inputProblem.netConnections.find(
3222
- (nc) => nc.netId === group.netId
3223
- )?.netLabelWidth;
3224
- if (ncWidth !== void 0) return ncWidth;
3225
- const dcWidthByNetId = this.inputProblem.directConnections.find(
3226
- (dc) => dc.netId === group.netId
3227
- )?.netLabelWidth;
3228
- if (dcWidthByNetId !== void 0) return dcWidthByNetId;
3229
- }
3230
3251
  const pinIds = group.overlappingTraces?.pins.map((p) => p.pinId) ?? [];
3231
3252
  if (group.portOnlyPinId) {
3232
3253
  pinIds.push(group.portOnlyPinId);
3233
3254
  }
3234
- const dcWidthByPinId = this.inputProblem.directConnections.find(
3235
- (dc) => dc.pinIds.some((pid) => pinIds.includes(pid))
3236
- )?.netLabelWidth;
3237
- if (dcWidthByPinId !== void 0) return dcWidthByPinId;
3238
- return this.inputProblem.netConnections.find(
3239
- (nc) => nc.pinIds.some((pid) => pinIds.includes(pid))
3240
- )?.netLabelWidth;
3255
+ return getNetLabelWidthForConnection({
3256
+ inputProblem: this.inputProblem,
3257
+ netId: group.netId,
3258
+ pinIds
3259
+ });
3241
3260
  }
3242
3261
  getNetLabelHeightForGroup(group) {
3243
3262
  if (group.netId) {
@@ -8527,13 +8546,21 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8527
8546
  const bY = this.outputNetLabelPlacements[b].anchorPoint.y;
8528
8547
  return requiredOrientation === "y+" ? bY - aY : aY - bY;
8529
8548
  });
8530
- const blockedLabelWidth = this.getNetLabelWidth(blockedLabel);
8549
+ const blockedLabelWidth = getNetLabelWidthForConnection({
8550
+ inputProblem: this.inputProblem,
8551
+ netId: blockedLabel.netId,
8552
+ pinIds: blockedLabel.pinIds
8553
+ });
8531
8554
  let columnLabelIndex;
8532
8555
  if (blockedLabelWidth !== void 0) {
8533
8556
  columnLabelIndex = labelsToOrder.find((index) => {
8534
8557
  if (index === blockedStandaloneLabelIndex) return false;
8535
8558
  const label = this.outputNetLabelPlacements[index];
8536
- return this.getNetLabelWidth(label) === blockedLabelWidth;
8559
+ return getNetLabelWidthForConnection({
8560
+ inputProblem: this.inputProblem,
8561
+ netId: label.netId,
8562
+ pinIds: label.pinIds
8563
+ }) === blockedLabelWidth;
8537
8564
  });
8538
8565
  }
8539
8566
  if (columnLabelIndex !== void 0) {
@@ -8754,7 +8781,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8754
8781
  if (!chip) return null;
8755
8782
  const { width, height } = getDimsForOrientation({
8756
8783
  orientation,
8757
- netLabelWidth: this.getNetLabelWidth(label),
8784
+ netLabelWidth: getNetLabelWidthForConnection({
8785
+ inputProblem: this.inputProblem,
8786
+ netId: label.netId,
8787
+ pinIds: label.pinIds
8788
+ }),
8758
8789
  netLabelHeight: this.getNetLabelHeight(label)
8759
8790
  });
8760
8791
  const nextPinToRight = this.outputNetLabelPlacements.map((otherLabel, index) => ({
@@ -9188,7 +9219,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9188
9219
  labelIntersectsDifferentNetTrace(label) {
9189
9220
  const { width, height } = getDimsForOrientation({
9190
9221
  orientation: label.orientation,
9191
- netLabelWidth: this.getNetLabelWidth(label),
9222
+ netLabelWidth: getNetLabelWidthForConnection({
9223
+ inputProblem: this.inputProblem,
9224
+ netId: label.netId,
9225
+ pinIds: label.pinIds
9226
+ }),
9192
9227
  netLabelHeight: this.getNetLabelHeight(label)
9193
9228
  });
9194
9229
  const center = getCenterFromAnchor(
@@ -9206,7 +9241,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9206
9241
  const anchorPoint = this.getWickOffsetAnchor(label.anchorPoint, orientation);
9207
9242
  const { width, height } = getDimsForOrientation({
9208
9243
  orientation,
9209
- netLabelWidth: this.getNetLabelWidth(label),
9244
+ netLabelWidth: getNetLabelWidthForConnection({
9245
+ inputProblem: this.inputProblem,
9246
+ netId: label.netId,
9247
+ pinIds: label.pinIds
9248
+ }),
9210
9249
  netLabelHeight: this.getNetLabelHeight(label)
9211
9250
  });
9212
9251
  const searchStartAnchor = this.getSideOffsetAnchor({
@@ -9261,7 +9300,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9261
9300
  getSearchDistanceLimit(label, orientation) {
9262
9301
  const { width, height } = getDimsForOrientation({
9263
9302
  orientation,
9264
- netLabelWidth: this.getNetLabelWidth(label),
9303
+ netLabelWidth: getNetLabelWidthForConnection({
9304
+ inputProblem: this.inputProblem,
9305
+ netId: label.netId,
9306
+ pinIds: label.pinIds
9307
+ }),
9265
9308
  netLabelHeight: this.getNetLabelHeight(label)
9266
9309
  });
9267
9310
  const labelLength = orientation === "y+" || orientation === "y-" ? height : width;
@@ -9312,7 +9355,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9312
9355
  createCandidate(label, anchorPoint, orientation, connectorSource) {
9313
9356
  const { width, height } = getDimsForOrientation({
9314
9357
  orientation,
9315
- netLabelWidth: this.getNetLabelWidth(label),
9358
+ netLabelWidth: getNetLabelWidthForConnection({
9359
+ inputProblem: this.inputProblem,
9360
+ netId: label.netId,
9361
+ pinIds: label.pinIds
9362
+ }),
9316
9363
  netLabelHeight: this.getNetLabelHeight(label)
9317
9364
  });
9318
9365
  return {
@@ -9324,25 +9371,6 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9324
9371
  center: getCenterFromAnchor(anchorPoint, orientation, width, height)
9325
9372
  };
9326
9373
  }
9327
- getNetLabelWidth(label) {
9328
- if (label.netId) {
9329
- const ncWidth = this.inputProblem.netConnections.find(
9330
- (connection) => connection.netId === label.netId
9331
- )?.netLabelWidth;
9332
- if (ncWidth !== void 0) return ncWidth;
9333
- const dcWidthByNetId = this.inputProblem.directConnections.find(
9334
- (dc) => dc.netId === label.netId
9335
- )?.netLabelWidth;
9336
- if (dcWidthByNetId !== void 0) return dcWidthByNetId;
9337
- }
9338
- const dcWidthByPinId = this.inputProblem.directConnections.find(
9339
- (dc) => dc.pinIds.some((pid) => label.pinIds.includes(pid))
9340
- )?.netLabelWidth;
9341
- if (dcWidthByPinId !== void 0) return dcWidthByPinId;
9342
- return this.inputProblem.netConnections.find(
9343
- (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
9344
- )?.netLabelWidth;
9345
- }
9346
9374
  getNetLabelHeight(label) {
9347
9375
  if (label.netId) {
9348
9376
  const ncHeight = this.inputProblem.netConnections.find(
@@ -10541,18 +10569,12 @@ var getChipBounds = (inputProblem) => {
10541
10569
  };
10542
10570
  };
10543
10571
  var getNetLabelWidth = (inputProblem, label) => {
10544
- const ncWidthByNetId = inputProblem.netConnections.find(
10545
- (connection) => connection.netId === label.netId
10546
- )?.netLabelWidth;
10547
- if (ncWidthByNetId !== void 0) return ncWidthByNetId;
10548
- const dcWidth = inputProblem.directConnections.find(
10549
- (dc) => dc.pinIds.some((pid) => label.pinIds.includes(pid))
10550
- )?.netLabelWidth;
10551
- if (dcWidth !== void 0) return dcWidth;
10552
- const ncWidthByPinId = inputProblem.netConnections.find(
10553
- (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
10554
- )?.netLabelWidth;
10555
- if (ncWidthByPinId !== void 0) return ncWidthByPinId;
10572
+ const configuredWidth = getNetLabelWidthForConnection({
10573
+ inputProblem,
10574
+ netId: label.netId,
10575
+ pinIds: label.pinIds
10576
+ });
10577
+ if (configuredWidth !== void 0) return configuredWidth;
10556
10578
  if (label.orientation === "y+" || label.orientation === "y-") {
10557
10579
  return label.height;
10558
10580
  }
@@ -10958,10 +10980,10 @@ function isPointOnPath(point, path) {
10958
10980
  }
10959
10981
  function buildMergedObstacleLabel(seedLabel, allLabels) {
10960
10982
  const TOUCH_MARGIN = 0.01;
10961
- const getBounds3 = (l) => getRectBounds(l.center, l.width, l.height);
10983
+ const getBounds4 = (l) => getRectBounds(l.center, l.width, l.height);
10962
10984
  const touches = (a, b) => {
10963
- const ba = getBounds3(a);
10964
- const bb = getBounds3(b);
10985
+ const ba = getBounds4(a);
10986
+ const bb = getBounds4(b);
10965
10987
  return ba.minX <= bb.maxX + TOUCH_MARGIN && ba.maxX >= bb.minX - TOUCH_MARGIN && ba.minY <= bb.maxY + TOUCH_MARGIN && ba.maxY >= bb.minY - TOUCH_MARGIN;
10966
10988
  };
10967
10989
  const group = /* @__PURE__ */ new Set([seedLabel]);
@@ -10978,7 +11000,7 @@ function buildMergedObstacleLabel(seedLabel, allLabels) {
10978
11000
  }
10979
11001
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
10980
11002
  for (const l of group) {
10981
- const b = getBounds3(l);
11003
+ const b = getBounds4(l);
10982
11004
  if (b.minX < minX) minX = b.minX;
10983
11005
  if (b.minY < minY) minY = b.minY;
10984
11006
  if (b.maxX > maxX) maxX = b.maxX;
@@ -12789,6 +12811,365 @@ var alignPortOnlyInlineNetLabelStubs = ({
12789
12811
  return alignedPlacements;
12790
12812
  };
12791
12813
 
12814
+ // lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts
12815
+ var LABEL_CLEARANCE2 = 0.05;
12816
+ var POINT_EPSILON = 1e-6;
12817
+ var CONTIGUOUS_LABEL_GAP = 0.01;
12818
+ var MAX_OUTWARD_DISTANCE = 5;
12819
+ var getBounds3 = (placement) => {
12820
+ const renderedWidth = placement.axis === "y" ? placement.height : placement.width;
12821
+ const renderedHeight = placement.axis === "y" ? placement.width : placement.height;
12822
+ return {
12823
+ minX: placement.center.x - renderedWidth / 2,
12824
+ maxX: placement.center.x + renderedWidth / 2,
12825
+ minY: placement.center.y - renderedHeight / 2,
12826
+ maxY: placement.center.y + renderedHeight / 2
12827
+ };
12828
+ };
12829
+ var pointsEqual3 = (a, b) => Math.abs(a.x - b.x) <= POINT_EPSILON && Math.abs(a.y - b.y) <= POINT_EPSILON;
12830
+ var pathIntersectsBounds = (path, bounds) => {
12831
+ for (let index = 0; index < path.length - 1; index++) {
12832
+ const start = path[index];
12833
+ const end = path[index + 1];
12834
+ const segmentBounds = {
12835
+ minX: Math.min(start.x, end.x),
12836
+ maxX: Math.max(start.x, end.x),
12837
+ minY: Math.min(start.y, end.y),
12838
+ maxY: Math.max(start.y, end.y)
12839
+ };
12840
+ if (boundsOverlap(segmentBounds, bounds)) return true;
12841
+ }
12842
+ return false;
12843
+ };
12844
+ var isPointOnPath2 = (point, path) => {
12845
+ for (let index = 0; index < path.length - 1; index++) {
12846
+ const start = path[index];
12847
+ const end = path[index + 1];
12848
+ const minX = Math.min(start.x, end.x) - POINT_EPSILON;
12849
+ const maxX = Math.max(start.x, end.x) + POINT_EPSILON;
12850
+ const minY = Math.min(start.y, end.y) - POINT_EPSILON;
12851
+ const maxY = Math.max(start.y, end.y) + POINT_EPSILON;
12852
+ const isHorizontal4 = Math.abs(start.y - end.y) <= POINT_EPSILON;
12853
+ const isVertical5 = Math.abs(start.x - end.x) <= POINT_EPSILON;
12854
+ if ((isHorizontal4 && Math.abs(point.y - start.y) <= POINT_EPSILON || isVertical5 && Math.abs(point.x - start.x) <= POINT_EPSILON) && point.x >= minX && point.x <= maxX && point.y >= minY && point.y <= maxY) {
12855
+ return true;
12856
+ }
12857
+ }
12858
+ return false;
12859
+ };
12860
+ var getRequiredOutwardDistance = (label, inlineBounds) => {
12861
+ const labelBounds = getBounds3(label);
12862
+ if (label.orientation === "x-" || label.orientation === "x+") {
12863
+ const nearby2 = inlineBounds.filter(
12864
+ (bounds) => labelBounds.minY < bounds.maxY && labelBounds.maxY > bounds.minY
12865
+ );
12866
+ if (nearby2.length === 0) return 0;
12867
+ if (label.orientation === "x-") {
12868
+ const targetMaxX = Math.min(...nearby2.map((bounds) => bounds.minX));
12869
+ return Math.max(0, labelBounds.maxX - targetMaxX + LABEL_CLEARANCE2);
12870
+ }
12871
+ const targetMinX = Math.max(...nearby2.map((bounds) => bounds.maxX));
12872
+ return Math.max(0, targetMinX - labelBounds.minX + LABEL_CLEARANCE2);
12873
+ }
12874
+ const nearby = inlineBounds.filter(
12875
+ (bounds) => labelBounds.minX < bounds.maxX && labelBounds.maxX > bounds.minX
12876
+ );
12877
+ if (nearby.length === 0) return 0;
12878
+ if (label.orientation === "y-") {
12879
+ const targetMaxY = Math.min(...nearby.map((bounds) => bounds.minY));
12880
+ return Math.max(0, labelBounds.maxY - targetMaxY + LABEL_CLEARANCE2);
12881
+ }
12882
+ const targetMinY = Math.max(...nearby.map((bounds) => bounds.maxY));
12883
+ return Math.max(0, targetMinY - labelBounds.minY + LABEL_CLEARANCE2);
12884
+ };
12885
+ var moveLabel = (label, orientation, distance5) => {
12886
+ const direction = dir(orientation);
12887
+ return {
12888
+ ...label,
12889
+ anchorPoint: {
12890
+ x: label.anchorPoint.x + direction.x * distance5,
12891
+ y: label.anchorPoint.y + direction.y * distance5
12892
+ },
12893
+ center: {
12894
+ x: label.center.x + direction.x * distance5,
12895
+ y: label.center.y + direction.y * distance5
12896
+ }
12897
+ };
12898
+ };
12899
+ var getDistanceToShoveBoundsPast = (obstacleBounds, movingBounds, orientation) => {
12900
+ switch (orientation) {
12901
+ case "x-":
12902
+ return obstacleBounds.maxX - movingBounds.minX + LABEL_CLEARANCE2;
12903
+ case "x+":
12904
+ return movingBounds.maxX - obstacleBounds.minX + LABEL_CLEARANCE2;
12905
+ case "y-":
12906
+ return obstacleBounds.maxY - movingBounds.minY + LABEL_CLEARANCE2;
12907
+ case "y+":
12908
+ return movingBounds.maxY - obstacleBounds.minY + LABEL_CLEARANCE2;
12909
+ }
12910
+ };
12911
+ var boundsGapOnPerpendicularAxis = (a, b, orientation) => {
12912
+ if (orientation === "x-" || orientation === "x+") {
12913
+ return Math.max(0, a.minY - b.maxY, b.minY - a.maxY);
12914
+ }
12915
+ return Math.max(0, a.minX - b.maxX, b.minX - a.maxX);
12916
+ };
12917
+ var sharesOwnerChip = (label, ownerChipIds, chipIdByPinId) => label.pinIds.some((pinId) => ownerChipIds.has(chipIdByPinId.get(pinId) ?? ""));
12918
+ var isGeneratedLabelConnector = (trace) => trace.mspPairId.startsWith("available-net-orientation-") || trace.mspPairId.startsWith("inline-net-label-clearance-");
12919
+ var findConnectorTraceIndex = (label, traces) => traces.findIndex((trace) => {
12920
+ if (trace.globalConnNetId !== label.globalConnNetId) return false;
12921
+ if (!isGeneratedLabelConnector(trace)) return false;
12922
+ const first = trace.tracePath[0];
12923
+ const last = trace.tracePath.at(-1);
12924
+ return Boolean(
12925
+ first && pointsEqual3(first, label.anchorPoint) || last && pointsEqual3(last, label.anchorPoint)
12926
+ );
12927
+ });
12928
+ var canAddConnectorAtAnchor = (label, traces, pinMap) => {
12929
+ if (label.pinIds.some((pinId) => {
12930
+ const pin = pinMap[pinId];
12931
+ return pin && pointsEqual3(pin, label.anchorPoint);
12932
+ })) {
12933
+ return true;
12934
+ }
12935
+ return traces.some(
12936
+ (trace) => trace.globalConnNetId === label.globalConnNetId && isPointOnPath2(label.anchorPoint, trace.tracePath)
12937
+ );
12938
+ };
12939
+ var moveConnectorEndpoint = (trace, oldAnchor, newAnchor) => {
12940
+ const tracePath = trace.tracePath.map((point) => ({ ...point }));
12941
+ if (pointsEqual3(tracePath[0], oldAnchor)) tracePath[0] = newAnchor;
12942
+ if (pointsEqual3(tracePath.at(-1), oldAnchor)) {
12943
+ tracePath[tracePath.length - 1] = newAnchor;
12944
+ }
12945
+ return { ...trace, tracePath };
12946
+ };
12947
+ var createConnectorTrace = ({
12948
+ label,
12949
+ labelIndex,
12950
+ newAnchor,
12951
+ pinMap
12952
+ }) => {
12953
+ const mspPairId = `inline-net-label-clearance-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
12954
+ return {
12955
+ mspPairId,
12956
+ dcConnNetId: label.dcConnNetId ?? label.globalConnNetId,
12957
+ globalConnNetId: label.globalConnNetId,
12958
+ userNetId: label.netId,
12959
+ pins: getTracePins(label, pinMap),
12960
+ tracePath: [label.anchorPoint, newAnchor],
12961
+ mspConnectionPairIds: [mspPairId],
12962
+ pinIds: label.pinIds
12963
+ };
12964
+ };
12965
+ var getContiguousLabelGroup = ({
12966
+ triggerIndex,
12967
+ labels,
12968
+ chipIdByPinId
12969
+ }) => {
12970
+ const trigger = labels[triggerIndex];
12971
+ const ownerChipIds = new Set(
12972
+ trigger.pinIds.flatMap((pinId) => {
12973
+ const chipId = chipIdByPinId.get(pinId);
12974
+ return chipId ? [chipId] : [];
12975
+ })
12976
+ );
12977
+ const candidates = labels.map((label, labelIndex) => ({ label, labelIndex })).filter(
12978
+ ({ label }) => label.orientation === trigger.orientation && label.mspConnectionPairIds.length === 0 && sharesOwnerChip(label, ownerChipIds, chipIdByPinId)
12979
+ );
12980
+ const group = /* @__PURE__ */ new Set([triggerIndex]);
12981
+ let changed = true;
12982
+ while (changed) {
12983
+ changed = false;
12984
+ for (const { label, labelIndex } of candidates) {
12985
+ if (group.has(labelIndex)) continue;
12986
+ if ([...group].some(
12987
+ (memberIndex) => boundsGapOnPerpendicularAxis(
12988
+ getBounds3(labels[memberIndex]),
12989
+ getBounds3(label),
12990
+ trigger.orientation
12991
+ ) <= CONTIGUOUS_LABEL_GAP
12992
+ )) {
12993
+ group.add(labelIndex);
12994
+ changed = true;
12995
+ }
12996
+ }
12997
+ }
12998
+ return { group, ownerChipIds };
12999
+ };
13000
+ var pushAnchoredNetLabelsAwayFromInlineLabels = ({
13001
+ inputProblem,
13002
+ traces,
13003
+ netLabelPlacements,
13004
+ inlineNetLabelPlacements
13005
+ }) => {
13006
+ const outputTraces = traces.map((trace) => ({
13007
+ ...trace,
13008
+ tracePath: trace.tracePath.map((point) => ({ ...point }))
13009
+ }));
13010
+ const outputLabels = netLabelPlacements.map((label) => ({ ...label }));
13011
+ const inlineBounds = inlineNetLabelPlacements.map(getBounds3);
13012
+ const pinMap = getPinMap(inputProblem);
13013
+ const chipIdByPinId = /* @__PURE__ */ new Map();
13014
+ for (const chip of inputProblem.chips) {
13015
+ for (const pin of chip.pins) chipIdByPinId.set(pin.pinId, chip.chipId);
13016
+ }
13017
+ const movedLabelIndices = /* @__PURE__ */ new Set();
13018
+ for (let triggerIndex = 0; triggerIndex < outputLabels.length; triggerIndex++) {
13019
+ const trigger = outputLabels[triggerIndex];
13020
+ const distance5 = getRequiredOutwardDistance(trigger, inlineBounds);
13021
+ if (distance5 <= POINT_EPSILON || distance5 > MAX_OUTWARD_DISTANCE) continue;
13022
+ const { group, ownerChipIds } = getContiguousLabelGroup({
13023
+ triggerIndex,
13024
+ labels: outputLabels,
13025
+ chipIdByPinId
13026
+ });
13027
+ const distances = new Map(
13028
+ [...group].map((labelIndex) => [labelIndex, distance5])
13029
+ );
13030
+ let failed = false;
13031
+ for (let iteration = 0; iteration < outputLabels.length; iteration++) {
13032
+ let adjustedObstacle = false;
13033
+ for (const [movingIndex, movingDistance] of distances) {
13034
+ const movingBounds = getBounds3(
13035
+ moveLabel(
13036
+ outputLabels[movingIndex],
13037
+ trigger.orientation,
13038
+ movingDistance
13039
+ )
13040
+ );
13041
+ for (let obstacleIndex = 0; obstacleIndex < outputLabels.length; obstacleIndex++) {
13042
+ if (group.has(obstacleIndex)) continue;
13043
+ const obstacle = outputLabels[obstacleIndex];
13044
+ const existingObstacleDistance = distances.get(obstacleIndex) ?? 0;
13045
+ const obstacleBounds = getBounds3(
13046
+ moveLabel(obstacle, trigger.orientation, existingObstacleDistance)
13047
+ );
13048
+ if (!boundsOverlap(movingBounds, obstacleBounds)) continue;
13049
+ if (!sharesOwnerChip(obstacle, ownerChipIds, chipIdByPinId) || findConnectorTraceIndex(obstacle, outputTraces) === -1 && !canAddConnectorAtAnchor(obstacle, outputTraces, pinMap)) {
13050
+ failed = true;
13051
+ break;
13052
+ }
13053
+ const shoveDistance = getDistanceToShoveBoundsPast(
13054
+ getBounds3(obstacle),
13055
+ movingBounds,
13056
+ trigger.orientation
13057
+ );
13058
+ if (shoveDistance > MAX_OUTWARD_DISTANCE || shoveDistance <= existingObstacleDistance + POINT_EPSILON) {
13059
+ failed = true;
13060
+ break;
13061
+ }
13062
+ distances.set(obstacleIndex, shoveDistance);
13063
+ adjustedObstacle = true;
13064
+ }
13065
+ if (failed) break;
13066
+ }
13067
+ if (failed || !adjustedObstacle) break;
13068
+ }
13069
+ if (failed) continue;
13070
+ const proposals = /* @__PURE__ */ new Map();
13071
+ for (const [labelIndex, labelDistance] of distances) {
13072
+ proposals.set(
13073
+ labelIndex,
13074
+ moveLabel(
13075
+ outputLabels[labelIndex],
13076
+ trigger.orientation,
13077
+ labelDistance
13078
+ )
13079
+ );
13080
+ }
13081
+ const finalLabelAt = (labelIndex) => proposals.get(labelIndex) ?? outputLabels[labelIndex];
13082
+ for (const [labelIndex, movedLabel] of proposals) {
13083
+ const movedBounds = getBounds3(movedLabel);
13084
+ if (inlineBounds.some((bounds) => boundsOverlap(movedBounds, bounds))) {
13085
+ failed = true;
13086
+ break;
13087
+ }
13088
+ if (inputProblem.chips.some(
13089
+ (chip) => boundsOverlap(movedBounds, {
13090
+ minX: chip.center.x - chip.width / 2,
13091
+ maxX: chip.center.x + chip.width / 2,
13092
+ minY: chip.center.y - chip.height / 2,
13093
+ maxY: chip.center.y + chip.height / 2
13094
+ })
13095
+ ) || (inputProblem.textBoxes ?? []).some(
13096
+ (textBox) => boundsOverlap(movedBounds, getTextBoxBounds(textBox))
13097
+ )) {
13098
+ failed = true;
13099
+ break;
13100
+ }
13101
+ if (outputLabels.some(
13102
+ (_, otherIndex) => otherIndex !== labelIndex && boundsOverlap(movedBounds, getBounds3(finalLabelAt(otherIndex)))
13103
+ )) {
13104
+ failed = true;
13105
+ break;
13106
+ }
13107
+ if (outputTraces.some(
13108
+ (trace) => trace.globalConnNetId !== movedLabel.globalConnNetId && pathIntersectsBounds(trace.tracePath, movedBounds)
13109
+ )) {
13110
+ failed = true;
13111
+ break;
13112
+ }
13113
+ }
13114
+ if (failed) continue;
13115
+ const connectorUpdates = [];
13116
+ for (const [labelIndex, movedLabel] of proposals) {
13117
+ const label = outputLabels[labelIndex];
13118
+ const connectorIndex = findConnectorTraceIndex(label, outputTraces);
13119
+ if (connectorIndex === -1 && !canAddConnectorAtAnchor(label, outputTraces, pinMap)) {
13120
+ failed = true;
13121
+ break;
13122
+ }
13123
+ const connector = connectorIndex === -1 ? createConnectorTrace({
13124
+ label,
13125
+ labelIndex,
13126
+ newAnchor: movedLabel.anchorPoint,
13127
+ pinMap
13128
+ }) : moveConnectorEndpoint(
13129
+ outputTraces[connectorIndex],
13130
+ label.anchorPoint,
13131
+ movedLabel.anchorPoint
13132
+ );
13133
+ const connectorObstructed = inlineBounds.some(
13134
+ (bounds) => pathIntersectsBounds(connector.tracePath, bounds)
13135
+ ) || inputProblem.chips.some(
13136
+ (chip) => pathIntersectsBounds(connector.tracePath, {
13137
+ minX: chip.center.x - chip.width / 2,
13138
+ maxX: chip.center.x + chip.width / 2,
13139
+ minY: chip.center.y - chip.height / 2,
13140
+ maxY: chip.center.y + chip.height / 2
13141
+ })
13142
+ ) || (inputProblem.textBoxes ?? []).some(
13143
+ (textBox) => pathIntersectsBounds(connector.tracePath, getTextBoxBounds(textBox))
13144
+ ) || outputLabels.some(
13145
+ (_, otherIndex) => otherIndex !== labelIndex && pathIntersectsBounds(
13146
+ connector.tracePath,
13147
+ getBounds3(finalLabelAt(otherIndex))
13148
+ )
13149
+ );
13150
+ if (connectorObstructed) {
13151
+ failed = true;
13152
+ break;
13153
+ }
13154
+ connectorUpdates.push({ labelIndex, connectorIndex, trace: connector });
13155
+ }
13156
+ if (failed) continue;
13157
+ for (const [labelIndex, movedLabel] of proposals) {
13158
+ outputLabels[labelIndex] = movedLabel;
13159
+ movedLabelIndices.add(labelIndex);
13160
+ }
13161
+ for (const update of connectorUpdates) {
13162
+ if (update.connectorIndex === -1) outputTraces.push(update.trace);
13163
+ else outputTraces[update.connectorIndex] = update.trace;
13164
+ }
13165
+ }
13166
+ return {
13167
+ traces: outputTraces,
13168
+ netLabelPlacements: outputLabels,
13169
+ movedLabelCount: movedLabelIndices.size
13170
+ };
13171
+ };
13172
+
12792
13173
  // lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
12793
13174
  var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
12794
13175
  var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
@@ -12803,6 +13184,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
12803
13184
  queuedConnections;
12804
13185
  tracesByPinPairKey;
12805
13186
  hasAlignedPortOnlyStubs = false;
13187
+ postProcessedOutput;
12806
13188
  constructor(input) {
12807
13189
  super();
12808
13190
  this.inputProblem = input.inputProblem;
@@ -12863,6 +13245,10 @@ var InlineNetLabelSolver = class extends BaseSolver {
12863
13245
  this.hasAlignedPortOnlyStubs = true;
12864
13246
  return;
12865
13247
  }
13248
+ if (!this.postProcessedOutput) {
13249
+ this.postProcessedOutput = this.buildPostProcessedOutput();
13250
+ return;
13251
+ }
12866
13252
  this.solved = true;
12867
13253
  this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length;
12868
13254
  }
@@ -13109,19 +13495,29 @@ var InlineNetLabelSolver = class extends BaseSolver {
13109
13495
  (trace) => !(supersededNetLabelKeys.has(trace.globalConnNetId) && trace.mspPairId.startsWith("available-net-orientation-"))
13110
13496
  );
13111
13497
  }
13112
- getOutput() {
13498
+ buildPostProcessedOutput() {
13113
13499
  const superseded = this.getSupersededNetLabelKeys();
13500
+ const retainedNetLabelPlacements = this.inputNetLabelPlacements.filter(
13501
+ (placement) => !superseded.has(placement.globalConnNetId)
13502
+ );
13503
+ const outputTraces = this.getOutputTraces(superseded);
13504
+ const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
13505
+ inputProblem: this.inputProblem,
13506
+ traces: outputTraces,
13507
+ netLabelPlacements: retainedNetLabelPlacements,
13508
+ inlineNetLabelPlacements: this.inlineNetLabelPlacements
13509
+ });
13510
+ this.stats.pushedAnchoredNetLabelCount = pushed.movedLabelCount;
13114
13511
  return {
13115
- // AvailableNetOrientationSolver may have routed an elbow from a port to
13116
- // the anchored label that this inline placement supersedes. Keep the
13117
- // actual net trace, but discard that now-orphaned label connector.
13118
- traces: this.getOutputTraces(superseded),
13119
- netLabelPlacements: this.inputNetLabelPlacements.filter(
13120
- (placement) => !superseded.has(placement.globalConnNetId)
13121
- ),
13512
+ traces: pushed.traces,
13513
+ netLabelPlacements: pushed.netLabelPlacements,
13122
13514
  inlineNetLabelPlacements: this.inlineNetLabelPlacements
13123
13515
  };
13124
13516
  }
13517
+ getOutput() {
13518
+ if (this.postProcessedOutput) return this.postProcessedOutput;
13519
+ return this.buildPostProcessedOutput();
13520
+ }
13125
13521
  visualize() {
13126
13522
  const graphics = visualizeInputProblem(this.inputProblem);
13127
13523
  graphics.lines ??= [];