@tscircuit/schematic-trace-solver 0.0.116 → 0.0.118

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
@@ -744,6 +744,8 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
744
744
  currentLabelIndex: number | null;
745
745
  currentLabel: NetLabelPlacement | null;
746
746
  currentCandidateResults: EvaluatedCandidate[];
747
+ blockedStandaloneLabelIndex: number | null;
748
+ blockedStandaloneLabelTargetAnchorX: number | null;
747
749
  private traceMap;
748
750
  private chipObstacleSpatialIndex;
749
751
  private maxSearchDistance;
@@ -782,6 +784,8 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
782
784
  private findValidCandidateInShiftColumn;
783
785
  private evaluateCandidate;
784
786
  private getCandidateConnectorTrace;
787
+ private isStandaloneSinglePinNetLabel;
788
+ private labelIntersectsDifferentNetTrace;
785
789
  private getSearchStartAnchor;
786
790
  private getWickOffsetAnchor;
787
791
  private getSideOffsetAnchor;
package/dist/index.js CHANGED
@@ -7848,6 +7848,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7848
7848
  currentLabelIndex = null;
7849
7849
  currentLabel = null;
7850
7850
  currentCandidateResults = [];
7851
+ blockedStandaloneLabelIndex = null;
7852
+ blockedStandaloneLabelTargetAnchorX = null;
7851
7853
  traceMap;
7852
7854
  chipObstacleSpatialIndex;
7853
7855
  maxSearchDistance;
@@ -7895,6 +7897,52 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7895
7897
  indices.push(i);
7896
7898
  }
7897
7899
  }
7900
+ const blockedStandaloneLabelIndex = indices.find((index) => {
7901
+ const label = this.outputNetLabelPlacements[index];
7902
+ const orientations = this.getAvailableOrientations(label);
7903
+ return this.isPortOnlyLabel(label) && this.isStandaloneSinglePinNetLabel(label) && orientations.length === 1 && isYOrientation(orientations[0]) && this.labelIntersectsDifferentNetTrace(label);
7904
+ });
7905
+ if (blockedStandaloneLabelIndex === void 0) return indices;
7906
+ this.blockedStandaloneLabelIndex = blockedStandaloneLabelIndex;
7907
+ this.blockedStandaloneLabelTargetAnchorX = null;
7908
+ const blockedLabel = this.outputNetLabelPlacements[blockedStandaloneLabelIndex];
7909
+ const requiredOrientation = this.getAvailableOrientations(blockedLabel)[0];
7910
+ const chipSide = this.getChipSideForPoint(blockedLabel.anchorPoint);
7911
+ const affectedSlots = [];
7912
+ const labelsToOrder = [];
7913
+ for (let slot = 0; slot < indices.length; slot++) {
7914
+ const index = indices[slot];
7915
+ const label = this.outputNetLabelPlacements[index];
7916
+ const orientations = this.getAvailableOrientations(label);
7917
+ if (this.getChipSideForPoint(label.anchorPoint) === chipSide && orientations.length === 1 && orientations[0] === requiredOrientation) {
7918
+ affectedSlots.push(slot);
7919
+ labelsToOrder.push(index);
7920
+ }
7921
+ }
7922
+ labelsToOrder.sort((a, b) => {
7923
+ const aY = this.outputNetLabelPlacements[a].anchorPoint.y;
7924
+ const bY = this.outputNetLabelPlacements[b].anchorPoint.y;
7925
+ return requiredOrientation === "y+" ? bY - aY : aY - bY;
7926
+ });
7927
+ const blockedLabelWidth = this.getNetLabelWidth(blockedLabel);
7928
+ let columnLabelIndex;
7929
+ if (blockedLabelWidth !== void 0) {
7930
+ columnLabelIndex = labelsToOrder.find((index) => {
7931
+ if (index === blockedStandaloneLabelIndex) return false;
7932
+ const label = this.outputNetLabelPlacements[index];
7933
+ return this.getNetLabelWidth(label) === blockedLabelWidth;
7934
+ });
7935
+ }
7936
+ if (columnLabelIndex !== void 0) {
7937
+ const columnLabel = this.outputNetLabelPlacements[columnLabelIndex];
7938
+ this.blockedStandaloneLabelTargetAnchorX = this.getSearchStartAnchor(
7939
+ columnLabel,
7940
+ requiredOrientation
7941
+ ).x;
7942
+ }
7943
+ for (let i = 0; i < affectedSlots.length; i++) {
7944
+ indices[affectedSlots[i]] = labelsToOrder[i];
7945
+ }
7898
7946
  return indices;
7899
7947
  }
7900
7948
  shouldProcessLabel(label) {
@@ -8213,6 +8261,28 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8213
8261
  candidate.orientation
8214
8262
  );
8215
8263
  }
8264
+ isStandaloneSinglePinNetLabel(label) {
8265
+ return this.inputProblem.netConnections.some(
8266
+ (connection) => connection.netId === label.netId && connection.pinIds.length === 1 && connection.pinIds[0] === label.pinIds[0]
8267
+ );
8268
+ }
8269
+ labelIntersectsDifferentNetTrace(label) {
8270
+ const { width, height } = getDimsForOrientation({
8271
+ orientation: label.orientation,
8272
+ netLabelWidth: this.getNetLabelWidth(label),
8273
+ netLabelHeight: this.getNetLabelHeight(label)
8274
+ });
8275
+ const center = getCenterFromAnchor(
8276
+ label.anchorPoint,
8277
+ label.orientation,
8278
+ width,
8279
+ height
8280
+ );
8281
+ const bounds = getRectBounds(center, width, height);
8282
+ return Object.values(this.traceMap).some(
8283
+ (trace) => trace.globalConnNetId !== label.globalConnNetId && tracePathIntersectsBounds(trace.tracePath, bounds)
8284
+ );
8285
+ }
8216
8286
  getSearchStartAnchor(label, orientation) {
8217
8287
  const anchorPoint = this.getWickOffsetAnchor(label.anchorPoint, orientation);
8218
8288
  const { width, height } = getDimsForOrientation({
@@ -8220,13 +8290,17 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8220
8290
  netLabelWidth: this.getNetLabelWidth(label),
8221
8291
  netLabelHeight: this.getNetLabelHeight(label)
8222
8292
  });
8223
- return this.getSideOffsetAnchor({
8293
+ const searchStartAnchor = this.getSideOffsetAnchor({
8224
8294
  anchorPoint,
8225
8295
  labelAnchorPoint: label.anchorPoint,
8226
8296
  orientation,
8227
8297
  width,
8228
8298
  height
8229
8299
  });
8300
+ if (this.blockedStandaloneLabelIndex !== null && label === this.outputNetLabelPlacements[this.blockedStandaloneLabelIndex] && isYOrientation(orientation) && this.blockedStandaloneLabelTargetAnchorX !== null) {
8301
+ searchStartAnchor.x = this.blockedStandaloneLabelTargetAnchorX;
8302
+ }
8303
+ return searchStartAnchor;
8230
8304
  }
8231
8305
  getWickOffsetAnchor(anchorPoint, orientation) {
8232
8306
  const direction = dir(orientation);
@@ -8486,6 +8560,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8486
8560
  if (i === labelIndex) continue;
8487
8561
  const label = this.outputNetLabelPlacements[i];
8488
8562
  const otherBounds = getRectBounds(label.center, label.width, label.height);
8563
+ if (i === this.blockedStandaloneLabelIndex && isYOrientation(label.orientation)) {
8564
+ otherBounds.minX -= LABEL_SEARCH_STEP;
8565
+ otherBounds.maxX += LABEL_SEARCH_STEP;
8566
+ }
8489
8567
  if (rectsOverlap2(bounds, otherBounds)) return true;
8490
8568
  }
8491
8569
  return false;
@@ -51,6 +51,8 @@ export class AvailableNetOrientationSolver extends BaseSolver {
51
51
  currentLabel: NetLabelPlacement | null = null
52
52
  currentCandidateResults: EvaluatedCandidate[] = []
53
53
 
54
+ blockedStandaloneLabelIndex: number | null = null
55
+ blockedStandaloneLabelTargetAnchorX: number | null = null
54
56
  private traceMap: Record<string, SolvedTracePath>
55
57
  private chipObstacleSpatialIndex: ChipObstacleSpatialIndex
56
58
  private maxSearchDistance: number
@@ -110,6 +112,71 @@ export class AvailableNetOrientationSolver extends BaseSolver {
110
112
  }
111
113
  }
112
114
 
115
+ const blockedStandaloneLabelIndex = indices.find((index) => {
116
+ const label = this.outputNetLabelPlacements[index]!
117
+ const orientations = this.getAvailableOrientations(label)
118
+ return (
119
+ this.isPortOnlyLabel(label) &&
120
+ this.isStandaloneSinglePinNetLabel(label) &&
121
+ orientations.length === 1 &&
122
+ isYOrientation(orientations[0]!) &&
123
+ this.labelIntersectsDifferentNetTrace(label)
124
+ )
125
+ })
126
+ if (blockedStandaloneLabelIndex === undefined) return indices
127
+ this.blockedStandaloneLabelIndex = blockedStandaloneLabelIndex
128
+ this.blockedStandaloneLabelTargetAnchorX = null
129
+
130
+ const blockedLabel =
131
+ this.outputNetLabelPlacements[blockedStandaloneLabelIndex]!
132
+ const requiredOrientation = this.getAvailableOrientations(blockedLabel)[0]!
133
+ const chipSide = this.getChipSideForPoint(blockedLabel.anchorPoint)
134
+ const affectedSlots: number[] = []
135
+ const labelsToOrder: number[] = []
136
+
137
+ for (let slot = 0; slot < indices.length; slot++) {
138
+ const index = indices[slot]!
139
+ const label = this.outputNetLabelPlacements[index]!
140
+ const orientations = this.getAvailableOrientations(label)
141
+ if (
142
+ this.getChipSideForPoint(label.anchorPoint) === chipSide &&
143
+ orientations.length === 1 &&
144
+ orientations[0] === requiredOrientation
145
+ ) {
146
+ affectedSlots.push(slot)
147
+ labelsToOrder.push(index)
148
+ }
149
+ }
150
+
151
+ // Place vertical labels on the same chip side from the outward end inward.
152
+ // The standalone label is corrected first, then a lower neighbor yields
153
+ // space during its own placement, keeping the standalone connector direct.
154
+ labelsToOrder.sort((a, b) => {
155
+ const aY = this.outputNetLabelPlacements[a]!.anchorPoint.y
156
+ const bY = this.outputNetLabelPlacements[b]!.anchorPoint.y
157
+ return requiredOrientation === "y+" ? bY - aY : aY - bY
158
+ })
159
+ const blockedLabelWidth = this.getNetLabelWidth(blockedLabel)
160
+ let columnLabelIndex: number | undefined
161
+ if (blockedLabelWidth !== undefined) {
162
+ columnLabelIndex = labelsToOrder.find((index) => {
163
+ if (index === blockedStandaloneLabelIndex) return false
164
+ const label = this.outputNetLabelPlacements[index]!
165
+ return this.getNetLabelWidth(label) === blockedLabelWidth
166
+ })
167
+ }
168
+ if (columnLabelIndex !== undefined) {
169
+ const columnLabel = this.outputNetLabelPlacements[columnLabelIndex]!
170
+ // Reuse the established same-width column instead of a nearby search step.
171
+ this.blockedStandaloneLabelTargetAnchorX = this.getSearchStartAnchor(
172
+ columnLabel,
173
+ requiredOrientation,
174
+ ).x
175
+ }
176
+ for (let i = 0; i < affectedSlots.length; i++) {
177
+ indices[affectedSlots[i]!] = labelsToOrder[i]!
178
+ }
179
+
113
180
  return indices
114
181
  }
115
182
 
@@ -551,6 +618,35 @@ export class AvailableNetOrientationSolver extends BaseSolver {
551
618
  )
552
619
  }
553
620
 
621
+ private isStandaloneSinglePinNetLabel(label: NetLabelPlacement) {
622
+ return this.inputProblem.netConnections.some(
623
+ (connection) =>
624
+ connection.netId === label.netId &&
625
+ connection.pinIds.length === 1 &&
626
+ connection.pinIds[0] === label.pinIds[0],
627
+ )
628
+ }
629
+
630
+ private labelIntersectsDifferentNetTrace(label: NetLabelPlacement) {
631
+ const { width, height } = getDimsForOrientation({
632
+ orientation: label.orientation,
633
+ netLabelWidth: this.getNetLabelWidth(label),
634
+ netLabelHeight: this.getNetLabelHeight(label),
635
+ })
636
+ const center = getCenterFromAnchor(
637
+ label.anchorPoint,
638
+ label.orientation,
639
+ width,
640
+ height,
641
+ )
642
+ const bounds = getRectBounds(center, width, height)
643
+ return Object.values(this.traceMap).some(
644
+ (trace) =>
645
+ trace.globalConnNetId !== label.globalConnNetId &&
646
+ tracePathIntersectsBounds(trace.tracePath, bounds),
647
+ )
648
+ }
649
+
554
650
  private getSearchStartAnchor(
555
651
  label: NetLabelPlacement,
556
652
  orientation: FacingDirection,
@@ -562,13 +658,23 @@ export class AvailableNetOrientationSolver extends BaseSolver {
562
658
  netLabelHeight: this.getNetLabelHeight(label),
563
659
  })
564
660
 
565
- return this.getSideOffsetAnchor({
661
+ const searchStartAnchor = this.getSideOffsetAnchor({
566
662
  anchorPoint,
567
663
  labelAnchorPoint: label.anchorPoint,
568
664
  orientation,
569
665
  width,
570
666
  height,
571
667
  })
668
+ if (
669
+ this.blockedStandaloneLabelIndex !== null &&
670
+ label ===
671
+ this.outputNetLabelPlacements[this.blockedStandaloneLabelIndex] &&
672
+ isYOrientation(orientation) &&
673
+ this.blockedStandaloneLabelTargetAnchorX !== null
674
+ ) {
675
+ searchStartAnchor.x = this.blockedStandaloneLabelTargetAnchorX
676
+ }
677
+ return searchStartAnchor
572
678
  }
573
679
 
574
680
  private getWickOffsetAnchor(
@@ -932,6 +1038,14 @@ export class AvailableNetOrientationSolver extends BaseSolver {
932
1038
  if (i === labelIndex) continue
933
1039
  const label = this.outputNetLabelPlacements[i]!
934
1040
  const otherBounds = getRectBounds(label.center, label.width, label.height)
1041
+ if (
1042
+ i === this.blockedStandaloneLabelIndex &&
1043
+ isYOrientation(label.orientation)
1044
+ ) {
1045
+ // Keep later labels one search step away from the blocked label.
1046
+ otherBounds.minX -= LABEL_SEARCH_STEP
1047
+ otherBounds.maxX += LABEL_SEARCH_STEP
1048
+ }
935
1049
  if (rectsOverlap(bounds, otherBounds)) return true
936
1050
  }
937
1051
  return false
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.116",
4
+ "version": "0.0.118",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -0,0 +1,4 @@
1
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
2
+ import inputProblem from "../../tests/bug-reports/bug-report-20260731T052229Z/bug-report-20260731T052229Z.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />
@@ -0,0 +1,93 @@
1
+ <svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="-1.9,2.550000000000001 -1.9,2.350000000000001" data-type="line" data-label="" points="219.27392739273924,85.28052805280515 219.27392739273924,103.76237623762367" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,2.550000000000001 -1.9,2.1500000000000012" data-type="line" data-label="" points="219.27392739273924,85.28052805280515 219.27392739273924,122.24422442244213" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,2.350000000000001 -1.9,2.1500000000000012" data-type="line" data-label="" points="219.27392739273924,103.76237623762367 219.27392739273924,122.24422442244213" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,0.7500000000000013 -1.9,0.5500000000000014" data-type="line" data-label="" points="219.27392739273924,251.6171617161715 219.27392739273924,270.09900990099" fill="none" stroke="hsl(44, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,1.950000000000001 -1.9,1.750000000000001" data-type="line" data-label="" points="219.27392739273924,140.72607260726062 219.27392739273924,159.2079207920791" fill="none" stroke="hsl(125, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,1.950000000000001 -1.9,1.5500000000000012" data-type="line" data-label="" points="219.27392739273924,140.72607260726062 219.27392739273924,177.68976897689757" fill="none" stroke="hsl(125, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,1.950000000000001 -1.9,1.3500000000000012" data-type="line" data-label="" points="219.27392739273924,140.72607260726062 219.27392739273924,196.17161716171606" fill="none" stroke="hsl(125, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,1.750000000000001 -1.9,1.5500000000000012" data-type="line" data-label="" points="219.27392739273924,159.2079207920791 219.27392739273924,177.68976897689757" fill="none" stroke="hsl(125, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,1.750000000000001 -1.9,1.3500000000000012" data-type="line" data-label="" points="219.27392739273924,159.2079207920791 219.27392739273924,196.17161716171606" fill="none" stroke="hsl(125, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,1.5500000000000012 -1.9,1.3500000000000012" data-type="line" data-label="" points="219.27392739273924,177.68976897689757 219.27392739273924,196.17161716171606" fill="none" stroke="hsl(125, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.9,2.350000000000001 -2.0999999999999996,2.350000000000001 -2.0999999999999996,2.550000000000001 -1.9,2.550000000000001" data-type="line" data-label="" points="219.27392739273924,103.76237623762367 200.79207920792078,103.76237623762367 200.79207920792078,85.28052805280515 219.27392739273924,85.28052805280515" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.9,2.1500000000000012 -2.0999999999999996,2.1500000000000012 -2.0999999999999996,2.350000000000001 -1.9,2.350000000000001" data-type="line" data-label="" points="219.27392739273924,122.24422442244213 200.79207920792078,122.24422442244213 200.79207920792078,103.76237623762367 219.27392739273924,103.76237623762367" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.9,0.7500000000000013 -2.0999999999999996,0.7500000000000013 -2.0999999999999996,0.5500000000000014 -1.9,0.5500000000000014" data-type="line" data-label="" points="219.27392739273924,251.6171617161715 200.79207920792078,251.6171617161715 200.79207920792078,270.09900990099 219.27392739273924,270.09900990099" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.9,1.750000000000001 -2.0999999999999996,1.750000000000001 -2.0999999999999996,1.950000000000001 -1.9,1.950000000000001" data-type="line" data-label="" points="219.27392739273924,159.2079207920791 200.79207920792078,159.2079207920791 200.79207920792078,140.72607260726062 219.27392739273924,140.72607260726062" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.9,1.5500000000000012 -2.0999999999999996,1.5500000000000012 -2.0999999999999996,1.750000000000001 -1.9,1.750000000000001" data-type="line" data-label="" points="219.27392739273924,177.68976897689757 200.79207920792078,177.68976897689757 200.79207920792078,159.2079207920791 219.27392739273924,159.2079207920791" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.9,1.3500000000000012 -2.0999999999999996,1.3500000000000012 -2.0999999999999996,1.5500000000000012 -1.9,1.5500000000000012" data-type="line" data-label="" points="219.27392739273924,196.17161716171606 200.79207920792078,196.17161716171606 200.79207920792078,177.68976897689757 219.27392739273924,177.68976897689757" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-2.0999999999999996,2.350000000000001 -2.4499999999999993,2.350000000000001 -2.4499999999999993,2.351000000000001" data-type="line" data-label="" points="200.79207920792078,103.76237623762367 168.44884488448847,103.76237623762367 168.44884488448847,103.66996699669957" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-2.0999999999999996,1.750000000000001 -2.4499999999999993,1.750000000000001 -2.4499999999999993,1.751000000000001" data-type="line" data-label="" points="200.79207920792078,159.2079207920791 168.44884488448847,159.2079207920791 168.44884488448847,159.115511551155" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.9,1.1500000000000012 -2.4499999999999993,1.1500000000000012 -2.4499999999999993,1.1510000000000011" data-type="line" data-label="" points="219.27392739273924,214.65346534653452 168.44884488448847,214.65346534653452 168.44884488448847,214.56105610561045" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-2.0999999999999996,0.7500000000000013 -3.1599999999999993,0.7500000000000013 -3.1599999999999993,0.8010000000000014" data-type="line" data-label="" points="200.79207920792078,251.6171617161715 102.83828382838288,251.6171617161715 102.83828382838288,246.90429042904276" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="219.2739273927392" y="62.1782178217822" width="351.15511551155123" height="517.4917491749175" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.01082142857142857"/></g><g><rect data-type="rect" data-label="CC2745R10E0WRHARQ1" data-x="-0.41999999999999993" data-y="-2.9299999999999997" x="256.2376237623762" y="583.3663366336633" width="199.60396039603967" height="16.633663366336577" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.01082142857142857"/></g><g><rect data-type="rect" data-label="U1" data-x="-1.38" data-y="2.915" x="250.69306930693068" y="39.99999999999997" width="33.2673267326733" height="23.10231023102307" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.01082142857142857"/></g><g><rect data-type="rect" data-label="netId: VDDR
2
+ globalConnNetId: connectivity_net0" data-x="-2.4499999999999993" data-y="2.561000000000001" x="140.72607260726076" y="64.85808580858078" width="55.44554455445541" height="38.811881188118804" fill="#ef444466" stroke="#ef4444" stroke-width="0.01082142857142857"/></g><g><rect data-type="rect" data-label="netId: VDDIO
3
+ globalConnNetId: connectivity_net1" data-x="-3.1599999999999993" data-y="1.0110000000000015" x="69.57095709570964" y="208.09240924092393" width="66.53465346534648" height="38.811881188118804" fill="#ef444466" stroke="#ef4444" stroke-width="0.01082142857142857"/></g><g><rect data-type="rect" data-label="netId: VDDS
4
+ globalConnNetId: connectivity_net2" data-x="-2.4499999999999993" data-y="1.961000000000001" x="140.72607260726076" y="120.3036303630362" width="55.44554455445541" height="38.81188118811883" fill="#ef444466" stroke="#ef4444" stroke-width="0.01082142857142857"/></g><g><rect data-type="rect" data-label="netId: VDDD
5
+ globalConnNetId: connectivity_net3" data-x="-2.4499999999999993" data-y="1.361000000000001" x="140.72607260726076" y="175.74917491749164" width="55.44554455445541" height="38.811881188118804" fill="#ef444466" stroke="#ef4444" stroke-width="0.01082142857142857"/></g><g><circle data-type="point" data-label="schematic_port_0
6
+ x-" data-x="-1.9" data-y="2.550000000000001" cx="219.27392739273924" cy="85.28052805280515" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_1
7
+ x-" data-x="-1.9" data-y="2.350000000000001" cx="219.27392739273924" cy="103.76237623762367" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_2
8
+ x+" data-x="1.9" data-y="1.7500000000000004" cx="570.4290429042904" cy="159.20792079207916" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_3
9
+ x+" data-x="1.9" data-y="1.5500000000000003" cx="570.4290429042904" cy="177.68976897689765" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_4
10
+ x+" data-x="1.9" data-y="1.3500000000000005" cx="570.4290429042904" cy="196.1716171617161" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_5
11
+ x+" data-x="1.9" data-y="1.1500000000000004" cx="570.4290429042904" cy="214.65346534653463" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_6
12
+ x+" data-x="1.9" data-y="0.9500000000000002" cx="570.4290429042904" cy="233.13531353135312" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_7
13
+ x+" data-x="1.9" data-y="0.75" cx="570.4290429042904" cy="251.6171617161716" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_8
14
+ x-" data-x="-1.9" data-y="0.7500000000000013" cx="219.27392739273924" cy="251.6171617161715" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_9
15
+ x+" data-x="1.9" data-y="0.5499999999999998" cx="570.4290429042904" cy="270.0990099009901" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_10
16
+ x+" data-x="1.9" data-y="0.34999999999999964" cx="570.4290429042904" cy="288.5808580858086" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_11
17
+ x+" data-x="1.9" data-y="0.14999999999999947" cx="570.4290429042904" cy="307.06270627062713" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_12
18
+ x+" data-x="1.9" data-y="-0.05000000000000071" cx="570.4290429042904" cy="325.5445544554456" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_13
19
+ x+" data-x="1.9" data-y="-0.2500000000000009" cx="570.4290429042904" cy="344.0264026402641" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_14
20
+ x+" data-x="1.9" data-y="-0.45000000000000107" cx="570.4290429042904" cy="362.5082508250826" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_15
21
+ x+" data-x="1.9" data-y="-0.6500000000000012" cx="570.4290429042904" cy="380.9900990099011" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_16
22
+ x-" data-x="-1.9" data-y="0.5500000000000014" cx="219.27392739273924" cy="270.09900990099" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_17
23
+ x-" data-x="-1.9" data-y="1.950000000000001" cx="219.27392739273924" cy="140.72607260726062" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_18
24
+ x-" data-x="-1.9" data-y="0.15000000000000124" cx="219.27392739273924" cy="307.06270627062696" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_19
25
+ x-" data-x="-1.9" data-y="-0.049999999999998934" cx="219.27392739273924" cy="325.54455445544545" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_20
26
+ x-" data-x="-1.9" data-y="-0.2499999999999991" cx="219.27392739273924" cy="344.02640264026394" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_21
27
+ x-" data-x="-1.9" data-y="-0.4499999999999993" cx="219.27392739273924" cy="362.5082508250824" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_22
28
+ x-" data-x="-1.9" data-y="-0.6499999999999995" cx="219.27392739273924" cy="380.990099009901" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_23
29
+ x-" data-x="-1.9" data-y="-0.8499999999999996" cx="219.27392739273924" cy="399.47194719471946" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_24
30
+ x+" data-x="1.9" data-y="2.150000000000001" cx="570.4290429042904" cy="122.24422442244216" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_25
31
+ x-" data-x="-1.9" data-y="-1.65" cx="219.27392739273924" cy="473.3993399339934" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_26
32
+ x-" data-x="-1.9" data-y="-2.2500000000000004" cx="219.27392739273924" cy="528.8448844884489" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_27
33
+ x-" data-x="-1.9" data-y="1.1500000000000012" cx="219.27392739273924" cy="214.65346534653452" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_28
34
+ x-" data-x="-1.9" data-y="1.750000000000001" cx="219.27392739273924" cy="159.2079207920791" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_29
35
+ x-" data-x="-1.9" data-y="-2.650000000000001" cx="219.27392739273924" cy="565.8085808580859" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_30
36
+ x-" data-x="-1.9" data-y="1.5500000000000012" cx="219.27392739273924" cy="177.68976897689757" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_31
37
+ x-" data-x="-1.9" data-y="-1.0499999999999998" cx="219.27392739273924" cy="417.95379537953795" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_32
38
+ x-" data-x="-1.9" data-y="-1.25" cx="219.27392739273924" cy="436.43564356435644" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_33
39
+ x-" data-x="-1.9" data-y="2.1500000000000012" cx="219.27392739273924" cy="122.24422442244213" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_34
40
+ x+" data-x="1.9" data-y="-1.0500000000000012" cx="570.4290429042904" cy="417.95379537953806" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_35
41
+ x+" data-x="1.9" data-y="-1.6500000000000012" cx="570.4290429042904" cy="473.39933993399353" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_36
42
+ x+" data-x="1.9" data-y="-2.050000000000001" cx="570.4290429042904" cy="510.36303630363045" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_37
43
+ x-" data-x="-1.9" data-y="1.3500000000000012" cx="219.27392739273924" cy="196.17161716171606" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_38
44
+ x+" data-x="1.9" data-y="2.550000000000001" cx="570.4290429042904" cy="85.28052805280515" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_39
45
+ x+" data-x="1.9" data-y="-2.450000000000001" cx="570.4290429042904" cy="547.3267326732674" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_40
46
+ x+" data-x="1.9" data-y="-2.6500000000000012" cx="570.4290429042904" cy="565.808580858086" r="3" fill="hsl(184, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
47
+ orientation: y+" data-x="-2.4499999999999993" data-y="2.351000000000001" cx="168.44884488448847" cy="103.66996699669957" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
48
+ orientation: y+" data-x="-3.1599999999999993" data-y="0.8010000000000014" cx="102.83828382838288" cy="246.90429042904276" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
49
+ orientation: y+" data-x="-2.4499999999999993" data-y="1.751000000000001" cx="168.44884488448847" cy="159.115511551155" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
50
+ orientation: y+" data-x="-2.4499999999999993" data-y="1.1510000000000011" cx="168.44884488448847" cy="214.56105610561045" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
51
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
52
+ const svg = e.currentTarget;
53
+ const rect = svg.getBoundingClientRect();
54
+ const x = e.clientX - rect.left;
55
+ const y = e.clientY - rect.top;
56
+ const crosshair = svg.getElementById('crosshair');
57
+ const h = svg.getElementById('crosshair-h');
58
+ const v = svg.getElementById('crosshair-v');
59
+ const coords = svg.getElementById('coordinates');
60
+
61
+ crosshair.style.display = 'block';
62
+ h.setAttribute('x1', '0');
63
+ h.setAttribute('x2', '640');
64
+ h.setAttribute('y1', y);
65
+ h.setAttribute('y2', y);
66
+ v.setAttribute('x1', x);
67
+ v.setAttribute('x2', x);
68
+ v.setAttribute('y1', '0');
69
+ v.setAttribute('y2', '640');
70
+
71
+ // Calculate real coordinates using inverse transformation
72
+ const matrix = {"a":92.40924092409242,"c":0,"e":394.8514851485148,"b":0,"d":-92.40924092409242,"f":320.9240924092409};
73
+ // Manually invert and apply the affine transform
74
+ // Since we only use translate and scale, we can directly compute:
75
+ // x' = (x - tx) / sx
76
+ // y' = (y - ty) / sy
77
+ const sx = matrix.a;
78
+ const sy = matrix.d;
79
+ const tx = matrix.e;
80
+ const ty = matrix.f;
81
+ const realPoint = {
82
+ x: (x - tx) / sx,
83
+ y: (y - ty) / sy // Flip y back since we used negative scale
84
+ }
85
+
86
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
87
+ coords.setAttribute('x', (x + 5).toString());
88
+ coords.setAttribute('y', (y - 5).toString());
89
+ });
90
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
91
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
92
+ });
93
+ ]]></script></svg>
@@ -0,0 +1,299 @@
1
+ {
2
+ "chips": [
3
+ {
4
+ "chipId": "schematic_component_0",
5
+ "center": {
6
+ "x": 0,
7
+ "y": 0
8
+ },
9
+ "width": 3.8,
10
+ "height": 5.6,
11
+ "pins": [
12
+ {
13
+ "pinId": "schematic_port_0",
14
+ "x": -1.9,
15
+ "y": 2.550000000000001
16
+ },
17
+ {
18
+ "pinId": "schematic_port_1",
19
+ "x": -1.9,
20
+ "y": 2.350000000000001
21
+ },
22
+ {
23
+ "pinId": "schematic_port_2",
24
+ "x": 1.9,
25
+ "y": 1.7500000000000004
26
+ },
27
+ {
28
+ "pinId": "schematic_port_3",
29
+ "x": 1.9,
30
+ "y": 1.5500000000000003
31
+ },
32
+ {
33
+ "pinId": "schematic_port_4",
34
+ "x": 1.9,
35
+ "y": 1.3500000000000005
36
+ },
37
+ {
38
+ "pinId": "schematic_port_5",
39
+ "x": 1.9,
40
+ "y": 1.1500000000000004
41
+ },
42
+ {
43
+ "pinId": "schematic_port_6",
44
+ "x": 1.9,
45
+ "y": 0.9500000000000002
46
+ },
47
+ {
48
+ "pinId": "schematic_port_7",
49
+ "x": 1.9,
50
+ "y": 0.75
51
+ },
52
+ {
53
+ "pinId": "schematic_port_8",
54
+ "x": -1.9,
55
+ "y": 0.7500000000000013
56
+ },
57
+ {
58
+ "pinId": "schematic_port_9",
59
+ "x": 1.9,
60
+ "y": 0.5499999999999998
61
+ },
62
+ {
63
+ "pinId": "schematic_port_10",
64
+ "x": 1.9,
65
+ "y": 0.34999999999999964
66
+ },
67
+ {
68
+ "pinId": "schematic_port_11",
69
+ "x": 1.9,
70
+ "y": 0.14999999999999947
71
+ },
72
+ {
73
+ "pinId": "schematic_port_12",
74
+ "x": 1.9,
75
+ "y": -0.05000000000000071
76
+ },
77
+ {
78
+ "pinId": "schematic_port_13",
79
+ "x": 1.9,
80
+ "y": -0.2500000000000009
81
+ },
82
+ {
83
+ "pinId": "schematic_port_14",
84
+ "x": 1.9,
85
+ "y": -0.45000000000000107
86
+ },
87
+ {
88
+ "pinId": "schematic_port_15",
89
+ "x": 1.9,
90
+ "y": -0.6500000000000012
91
+ },
92
+ {
93
+ "pinId": "schematic_port_16",
94
+ "x": -1.9,
95
+ "y": 0.5500000000000014
96
+ },
97
+ {
98
+ "pinId": "schematic_port_17",
99
+ "x": -1.9,
100
+ "y": 1.950000000000001
101
+ },
102
+ {
103
+ "pinId": "schematic_port_18",
104
+ "x": -1.9,
105
+ "y": 0.15000000000000124
106
+ },
107
+ {
108
+ "pinId": "schematic_port_19",
109
+ "x": -1.9,
110
+ "y": -0.049999999999998934
111
+ },
112
+ {
113
+ "pinId": "schematic_port_20",
114
+ "x": -1.9,
115
+ "y": -0.2499999999999991
116
+ },
117
+ {
118
+ "pinId": "schematic_port_21",
119
+ "x": -1.9,
120
+ "y": -0.4499999999999993
121
+ },
122
+ {
123
+ "pinId": "schematic_port_22",
124
+ "x": -1.9,
125
+ "y": -0.6499999999999995
126
+ },
127
+ {
128
+ "pinId": "schematic_port_23",
129
+ "x": -1.9,
130
+ "y": -0.8499999999999996
131
+ },
132
+ {
133
+ "pinId": "schematic_port_24",
134
+ "x": 1.9,
135
+ "y": 2.150000000000001
136
+ },
137
+ {
138
+ "pinId": "schematic_port_25",
139
+ "x": -1.9,
140
+ "y": -1.65
141
+ },
142
+ {
143
+ "pinId": "schematic_port_26",
144
+ "x": -1.9,
145
+ "y": -2.2500000000000004
146
+ },
147
+ {
148
+ "pinId": "schematic_port_27",
149
+ "x": -1.9,
150
+ "y": 1.1500000000000012
151
+ },
152
+ {
153
+ "pinId": "schematic_port_28",
154
+ "x": -1.9,
155
+ "y": 1.750000000000001
156
+ },
157
+ {
158
+ "pinId": "schematic_port_29",
159
+ "x": -1.9,
160
+ "y": -2.650000000000001
161
+ },
162
+ {
163
+ "pinId": "schematic_port_30",
164
+ "x": -1.9,
165
+ "y": 1.5500000000000012
166
+ },
167
+ {
168
+ "pinId": "schematic_port_31",
169
+ "x": -1.9,
170
+ "y": -1.0499999999999998
171
+ },
172
+ {
173
+ "pinId": "schematic_port_32",
174
+ "x": -1.9,
175
+ "y": -1.25
176
+ },
177
+ {
178
+ "pinId": "schematic_port_33",
179
+ "x": -1.9,
180
+ "y": 2.1500000000000012
181
+ },
182
+ {
183
+ "pinId": "schematic_port_34",
184
+ "x": 1.9,
185
+ "y": -1.0500000000000012
186
+ },
187
+ {
188
+ "pinId": "schematic_port_35",
189
+ "x": 1.9,
190
+ "y": -1.6500000000000012
191
+ },
192
+ {
193
+ "pinId": "schematic_port_36",
194
+ "x": 1.9,
195
+ "y": -2.050000000000001
196
+ },
197
+ {
198
+ "pinId": "schematic_port_37",
199
+ "x": -1.9,
200
+ "y": 1.3500000000000012
201
+ },
202
+ {
203
+ "pinId": "schematic_port_38",
204
+ "x": 1.9,
205
+ "y": 2.550000000000001
206
+ },
207
+ {
208
+ "pinId": "schematic_port_39",
209
+ "x": 1.9,
210
+ "y": -2.450000000000001
211
+ },
212
+ {
213
+ "pinId": "schematic_port_40",
214
+ "x": 1.9,
215
+ "y": -2.6500000000000012
216
+ }
217
+ ]
218
+ }
219
+ ],
220
+ "directConnections": [],
221
+ "netConnections": [
222
+ {
223
+ "netId": "VDDR",
224
+ "netLabelWidth": 0.42,
225
+ "netLabelHeight": 0.6,
226
+ "pinIds": [
227
+ "schematic_port_0",
228
+ "schematic_port_1",
229
+ "schematic_port_33"
230
+ ]
231
+ },
232
+ {
233
+ "netId": "VDDIO",
234
+ "netLabelWidth": 0.42,
235
+ "netLabelHeight": 0.72,
236
+ "pinIds": [
237
+ "schematic_port_8",
238
+ "schematic_port_16"
239
+ ]
240
+ },
241
+ {
242
+ "netId": "VDDS",
243
+ "netLabelWidth": 0.42,
244
+ "netLabelHeight": 0.6,
245
+ "pinIds": [
246
+ "schematic_port_17",
247
+ "schematic_port_28",
248
+ "schematic_port_30",
249
+ "schematic_port_37"
250
+ ]
251
+ },
252
+ {
253
+ "netId": "VDDD",
254
+ "netLabelWidth": 0.42,
255
+ "netLabelHeight": 0.6,
256
+ "pinIds": [
257
+ "schematic_port_27"
258
+ ]
259
+ }
260
+ ],
261
+ "textBoxes": [
262
+ {
263
+ "chipId": "schematic_component_0",
264
+ "center": {
265
+ "x": -0.41999999999999993,
266
+ "y": -2.9299999999999997
267
+ },
268
+ "width": 2.16,
269
+ "height": 0.17999999999999972,
270
+ "text": "CC2745R10E0WRHARQ1"
271
+ },
272
+ {
273
+ "chipId": "schematic_component_0",
274
+ "center": {
275
+ "x": -1.38,
276
+ "y": 2.915
277
+ },
278
+ "width": 0.3600000000000001,
279
+ "height": 0.24999999999999956,
280
+ "text": "U1"
281
+ }
282
+ ],
283
+ "availableNetLabelOrientations": {
284
+ "VDDR": [
285
+ "y+"
286
+ ],
287
+ "VDDS": [
288
+ "y+"
289
+ ],
290
+ "VDDD": [
291
+ "y+"
292
+ ],
293
+ "VDDIO": [
294
+ "y+"
295
+ ]
296
+ },
297
+ "maxMspPairDistance": 2.4,
298
+ "_hideRatsNet": false
299
+ }
@@ -0,0 +1,61 @@
1
+ import { expect, test } from "bun:test"
2
+ import { LABEL_SEARCH_STEP } from "lib/solvers/AvailableNetOrientationSolver/constants"
3
+ import { segmentIntersectsRect } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
4
+ import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
5
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
6
+ import inputProblem from "./bug-report-20260731T052229Z.json"
7
+ import "tests/fixtures/matcher"
8
+
9
+ test("bug-report-20260731T052229Z", () => {
10
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
11
+
12
+ solver.solve()
13
+
14
+ const output = solver.sameNetJunctionAlignmentSolver!.getOutput()
15
+ const vdddLabel = output.netLabelPlacements.find(
16
+ (placement) => placement.netId === "VDDD",
17
+ )!
18
+ const vdddBounds = getRectBounds(
19
+ vdddLabel.center,
20
+ vdddLabel.width,
21
+ vdddLabel.height,
22
+ )
23
+ const intersectingDifferentNetTraceIds = output.traces
24
+ .filter((trace) => trace.globalConnNetId !== vdddLabel.globalConnNetId)
25
+ .filter((trace) =>
26
+ trace.tracePath.some(
27
+ (point, index) =>
28
+ index < trace.tracePath.length - 1 &&
29
+ segmentIntersectsRect(point, trace.tracePath[index + 1]!, vdddBounds),
30
+ ),
31
+ )
32
+ .map((trace) => trace.mspPairId)
33
+ const vdddConnector = output.traces.find(
34
+ (trace) =>
35
+ trace.userNetId === "VDDD" &&
36
+ trace.mspPairId.startsWith("available-net-orientation-"),
37
+ )!
38
+ const alignedLabelXs = ["VDDR", "VDDS", "VDDD"].map(
39
+ (netId) =>
40
+ output.netLabelPlacements.find((placement) => placement.netId === netId)!
41
+ .anchorPoint.x,
42
+ )
43
+ const vddioLabel = output.netLabelPlacements.find(
44
+ (placement) => placement.netId === "VDDIO",
45
+ )!
46
+ const vddioBounds = getRectBounds(
47
+ vddioLabel.center,
48
+ vddioLabel.width,
49
+ vddioLabel.height,
50
+ )
51
+
52
+ expect(vdddLabel.orientation).toBe("y+")
53
+ expect(new Set(alignedLabelXs)).toHaveLength(1)
54
+ expect(vdddBounds.minX - vddioBounds.maxX).toBeGreaterThanOrEqual(
55
+ LABEL_SEARCH_STEP,
56
+ )
57
+ expect(intersectingDifferentNetTraceIds).toEqual([])
58
+ expect(vdddConnector.tracePath).toHaveLength(3)
59
+ expect(vdddConnector.tracePath[0]!.y).toBe(vdddConnector.tracePath[1]!.y)
60
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
61
+ })