@tscircuit/schematic-trace-solver 0.0.162 → 0.0.164

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -825,7 +825,7 @@ type CandidateLabel = {
825
825
  height: number;
826
826
  };
827
827
  type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
828
- type CandidatePhase = "rotate" | "trace-anchor" | "outward-trace-anchor" | "shift" | "lateral-shift";
828
+ type CandidatePhase = "rotate" | "trace-anchor" | "connected-rail-shift" | "outward-trace-anchor" | "shift" | "lateral-shift";
829
829
  type EvaluatedCandidate = CandidateLabel & {
830
830
  status: CandidateStatus$2;
831
831
  selected: boolean;
@@ -902,6 +902,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
902
902
  private getNetLabelHeight;
903
903
  private getCandidateStatus;
904
904
  private isAcceptableTraceAnchorChipCollision;
905
+ private staysOutsideConnectedPinRow;
905
906
  private getBoundsStatus;
906
907
  private isTraceTooCloseToLabel;
907
908
  private getLabelTraceClearanceBounds;
package/dist/index.js CHANGED
@@ -1507,13 +1507,17 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1507
1507
  excludeRectsForSegment: (segIndex2) => {
1508
1508
  const lastSegIndex2 = path.length - 2;
1509
1509
  const excludedRects = /* @__PURE__ */ new Set();
1510
- if (segIndex2 === 0 || segIndex2 === lastSegIndex2) {
1510
+ const segmentStart = path[segIndex2];
1511
+ const segmentEnd = path[segIndex2 + 1];
1512
+ const oppositeTerminalPin = segIndex2 === 0 ? PB : segIndex2 === lastSegIndex2 ? PA : null;
1513
+ if (oppositeTerminalPin) {
1511
1514
  for (const textObstacle of this.textObstacles) {
1512
- excludedRects.add(textObstacle);
1515
+ const textBounds = getTextBoxBounds(textObstacle.textBox);
1516
+ if (textObstacle.textBox.chipId !== oppositeTerminalPin.chipId || !segmentIntersectsRect(segmentStart, segmentEnd, textBounds)) {
1517
+ excludedRects.add(textObstacle);
1518
+ }
1513
1519
  }
1514
1520
  }
1515
- const segmentStart = path[segIndex2];
1516
- const segmentEnd = path[segIndex2 + 1];
1517
1521
  const endpointEpsilon = 1e-9;
1518
1522
  const segmentTouchesPin = (pin) => [segmentStart, segmentEnd].some(
1519
1523
  (point) => Math.abs(point.x - pin.x) <= endpointEpsilon && Math.abs(point.y - pin.y) <= endpointEpsilon
@@ -8831,6 +8835,7 @@ function getPaddedBounds(bounds, padding) {
8831
8835
 
8832
8836
  // lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
8833
8837
  var LABEL_TRACE_CLEARANCE2 = 0.1;
8838
+ var CONNECTED_PIN_ROW_OVERLAP_TOLERANCE = 0.1;
8834
8839
  var AvailableNetOrientationSolver = class extends BaseSolver {
8835
8840
  inputProblem;
8836
8841
  traces;
@@ -9852,7 +9857,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9852
9857
  label
9853
9858
  });
9854
9859
  if (boundsStatus !== "valid") {
9855
- if (phase !== "trace-anchor" || boundsStatus !== "chip-collision" || !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds)) {
9860
+ if (phase !== "trace-anchor" && phase !== "connected-rail-shift" || boundsStatus !== "chip-collision" || !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds) || phase === "connected-rail-shift" && !this.staysOutsideConnectedPinRow({ candidate, label, bounds })) {
9856
9861
  return boundsStatus;
9857
9862
  }
9858
9863
  const nonChipBoundsStatus = this.getBoundsStatus({
@@ -9906,6 +9911,15 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9906
9911
  return anchorPoint.y < chipBounds.minY - EPS12 || anchorPoint.y > chipBounds.maxY + EPS12;
9907
9912
  });
9908
9913
  }
9914
+ staysOutsideConnectedPinRow(params) {
9915
+ const { candidate, label, bounds } = params;
9916
+ const pin = this.pinMap[label.pinIds[0]];
9917
+ if (!pin || !isYOrientation(candidate.orientation)) return false;
9918
+ if (candidate.anchorPoint.x < pin.x) {
9919
+ return bounds.maxX <= pin.x + CONNECTED_PIN_ROW_OVERLAP_TOLERANCE + EPS12;
9920
+ }
9921
+ return bounds.minX >= pin.x - CONNECTED_PIN_ROW_OVERLAP_TOLERANCE - EPS12;
9922
+ }
9909
9923
  getBoundsStatus(candidateBoundsCheck) {
9910
9924
  const { bounds, labelIndex, label, ignoreChipCollisions } = candidateBoundsCheck;
9911
9925
  if (!ignoreChipCollisions) {
@@ -10060,8 +10074,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10060
10074
  orientation,
10061
10075
  direction,
10062
10076
  baseAnchor: connectorSource,
10063
- maxSearchDistance: this.maxSearchDistance,
10077
+ maxSearchDistance: this.getSearchDistanceLimit(label, orientation),
10064
10078
  outwardDistance: 0,
10079
+ phase: "connected-rail-shift",
10065
10080
  stopOnTraceCollision: false,
10066
10081
  connectorSource,
10067
10082
  startDistance: 0
@@ -51,6 +51,7 @@ import { visualizeAvailableNetOrientationSolver } from "./visualize"
51
51
  import { AvailableNetOrientationObstacleIndex } from "./AvailableNetOrientationObstacleIndex"
52
52
 
53
53
  const LABEL_TRACE_CLEARANCE = 0.1
54
+ const CONNECTED_PIN_ROW_OVERLAP_TOLERANCE = 0.1
54
55
 
55
56
  export class AvailableNetOrientationSolver extends BaseSolver {
56
57
  inputProblem: InputProblem
@@ -1443,9 +1444,11 @@ export class AvailableNetOrientationSolver extends BaseSolver {
1443
1444
  })
1444
1445
  if (boundsStatus !== "valid") {
1445
1446
  if (
1446
- phase !== "trace-anchor" ||
1447
+ (phase !== "trace-anchor" && phase !== "connected-rail-shift") ||
1447
1448
  boundsStatus !== "chip-collision" ||
1448
- !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds)
1449
+ !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds) ||
1450
+ (phase === "connected-rail-shift" &&
1451
+ !this.staysOutsideConnectedPinRow({ candidate, label, bounds }))
1449
1452
  ) {
1450
1453
  return boundsStatus
1451
1454
  }
@@ -1525,6 +1528,20 @@ export class AvailableNetOrientationSolver extends BaseSolver {
1525
1528
  })
1526
1529
  }
1527
1530
 
1531
+ private staysOutsideConnectedPinRow(params: {
1532
+ candidate: CandidateLabel
1533
+ label: NetLabelPlacement
1534
+ bounds: Bounds
1535
+ }) {
1536
+ const { candidate, label, bounds } = params
1537
+ const pin = this.pinMap[label.pinIds[0]!]
1538
+ if (!pin || !isYOrientation(candidate.orientation)) return false
1539
+ if (candidate.anchorPoint.x < pin.x) {
1540
+ return bounds.maxX <= pin.x + CONNECTED_PIN_ROW_OVERLAP_TOLERANCE + EPS
1541
+ }
1542
+ return bounds.minX >= pin.x - CONNECTED_PIN_ROW_OVERLAP_TOLERANCE - EPS
1543
+ }
1544
+
1528
1545
  private getBoundsStatus(candidateBoundsCheck: {
1529
1546
  bounds: Bounds
1530
1547
  labelIndex: number
@@ -1756,8 +1773,9 @@ export class AvailableNetOrientationSolver extends BaseSolver {
1756
1773
  orientation,
1757
1774
  direction,
1758
1775
  baseAnchor: connectorSource,
1759
- maxSearchDistance: this.maxSearchDistance,
1776
+ maxSearchDistance: this.getSearchDistanceLimit(label, orientation),
1760
1777
  outwardDistance: 0,
1778
+ phase: "connected-rail-shift",
1761
1779
  stopOnTraceCollision: false,
1762
1780
  connectorSource,
1763
1781
  startDistance: 0,
@@ -39,6 +39,7 @@ export type CandidateStatus =
39
39
  export type CandidatePhase =
40
40
  | "rotate"
41
41
  | "trace-anchor"
42
+ | "connected-rail-shift"
42
43
  | "outward-trace-anchor"
43
44
  | "shift"
44
45
  | "lateral-shift"
@@ -325,15 +325,26 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver {
325
325
  excludeRectsForSegment: (segIndex) => {
326
326
  const lastSegIndex = path.length - 2
327
327
  const excludedRects = new Set<ObstacleRect>()
328
+ const segmentStart = path[segIndex]!
329
+ const segmentEnd = path[segIndex + 1]!
328
330
 
329
- if (segIndex === 0 || segIndex === lastSegIndex) {
331
+ const oppositeTerminalPin =
332
+ segIndex === 0 ? PB : segIndex === lastSegIndex ? PA : null
333
+ if (oppositeTerminalPin) {
330
334
  for (const textObstacle of this.textObstacles) {
331
- excludedRects.add(textObstacle)
335
+ // Preserve the existing terminal-segment clearance exception, but
336
+ // do not let a segment that wraps around the opposite endpoint cut
337
+ // through that component's actual text.
338
+ const textBounds = getTextBoxBounds(textObstacle.textBox)
339
+ if (
340
+ textObstacle.textBox.chipId !== oppositeTerminalPin.chipId ||
341
+ !segmentIntersectsRect(segmentStart, segmentEnd, textBounds)
342
+ ) {
343
+ excludedRects.add(textObstacle)
344
+ }
332
345
  }
333
346
  }
334
347
 
335
- const segmentStart = path[segIndex]!
336
- const segmentEnd = path[segIndex + 1]!
337
348
  const endpointEpsilon = 1e-9
338
349
  const segmentTouchesPin = (pin: MspConnectionPair["pins"][number]) =>
339
350
  [segmentStart, segmentEnd].some(
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://github.com/tscircuit/schematic-trace-solver.git"
6
6
  },
7
7
  "main": "dist/index.js",
8
- "version": "0.0.162",
8
+ "version": "0.0.164",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",