@tscircuit/schematic-trace-solver 0.0.136 → 0.0.140

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 (25) hide show
  1. package/.github/workflows/bun-pver-release.yml +7 -3
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +333 -45
  4. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationObstacleIndex.ts +182 -0
  5. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +67 -25
  6. package/lib/solvers/AvailableNetOrientationSolver/constants.ts +2 -0
  7. package/lib/solvers/Example28Solver/labelMovement.ts +71 -0
  8. package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +5 -2
  9. package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +93 -14
  10. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +12 -4
  11. package/package.json +5 -1
  12. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +4 -4
  13. package/tests/bug-reports/bug-report-20260721T221026Z/bug-report-20260721T221026Z.test.ts +11 -0
  14. package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +3 -3
  15. package/tests/bug-reports/bug-report-20260815T073240Z/bug-report-20260815T073240Z.test.ts +39 -0
  16. package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +237 -0
  17. package/tests/bug-reports/bug-report-20260819T091818Z/bug-report-20260819T091818Z.json +1052 -0
  18. package/tests/bug-reports/bug-report-20260819T091818Z/bug-report-20260819T091818Z.test.ts +33 -0
  19. package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +4 -4
  20. package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +124 -0
  21. package/tests/repros/assets/repro-board-648-esp12f-section.input.json +537 -0
  22. package/tests/repros/repro-board-589-regulator-section.test.ts +23 -1
  23. package/tests/repros/repro-board-648-esp12f-section.test.ts +13 -0
  24. package/tests/repros/repro161-power-section-autolayout.test.ts +7 -0
  25. package/tests/solvers/AvailableNetOrientationSolver/AvailableNetOrientationObstacleIndex.test.ts +122 -0
@@ -7,6 +7,10 @@ on:
7
7
  paths:
8
8
  - 'lib/**'
9
9
  workflow_dispatch:
10
+ permissions:
11
+ contents: write
12
+ id-token: write
13
+ pull-requests: write
10
14
  jobs:
11
15
  publish:
12
16
  runs-on: ubuntu-latest
@@ -18,14 +22,14 @@ jobs:
18
22
  uses: oven-sh/setup-bun@v2
19
23
  with:
20
24
  bun-version: 1.3.1
21
- - uses: actions/setup-node@v3
25
+ - uses: actions/setup-node@v6
22
26
  with:
23
- node-version: 20
27
+ node-version: 24
24
28
  registry-url: https://registry.npmjs.org/
29
+ package-manager-cache: false
25
30
  - run: npm install -g pver
26
31
  - run: bun install --frozen-lockfile
27
32
  - run: bun run build
28
33
  - run: pver release
29
34
  env:
30
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
31
35
  GITHUB_TOKEN: ${{ secrets.TSCIRCUIT_BOT_GITHUB_TOKEN }}
package/dist/index.d.ts CHANGED
@@ -787,6 +787,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
787
787
  private chipObstacleSpatialIndex;
788
788
  private maxSearchDistance;
789
789
  private pinMap;
790
+ private obstacleIndex;
790
791
  constructor(params: AvailableNetOrientationSolverParams);
791
792
  getConstructorParams(): ConstructorParameters<typeof AvailableNetOrientationSolver>[0];
792
793
  _step(): void;
@@ -824,6 +825,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
824
825
  private findValidLateralShiftedCandidate;
825
826
  private findValidCandidateInShiftColumn;
826
827
  private evaluateCandidate;
828
+ private recordCandidateResult;
827
829
  private getCandidateConnectorTrace;
828
830
  private isStandaloneSinglePinNetLabel;
829
831
  private labelIntersectsDifferentNetTrace;
@@ -1117,6 +1119,7 @@ interface SameNetJunctionAlignmentSolverInput {
1117
1119
  declare class SameNetJunctionAlignmentSolver extends BaseSolver {
1118
1120
  private input;
1119
1121
  outputTraces: SolvedTracePath[];
1122
+ outputNetLabelPlacements: NetLabelPlacement[];
1120
1123
  constructor(input: SameNetJunctionAlignmentSolverInput);
1121
1124
  _step(): void;
1122
1125
  getOutput(): {
package/dist/index.js CHANGED
@@ -7159,6 +7159,65 @@ var moveAttachedLabelsToReroutedTrace = ({
7159
7159
  }
7160
7160
  };
7161
7161
  });
7162
+ var moveNetLabelConnectorsToReroutedTraces = ({
7163
+ originalTraces,
7164
+ reroutedTraces,
7165
+ netLabelPlacements
7166
+ }) => {
7167
+ const traces = [...reroutedTraces];
7168
+ const reroutedTraceMap = new Map(
7169
+ reroutedTraces.map((trace) => [trace.mspPairId, trace])
7170
+ );
7171
+ const labels = netLabelPlacements.map((label) => {
7172
+ const originalHostTrace = originalTraces.find((trace) => {
7173
+ if (!label.mspConnectionPairIds.includes(trace.mspPairId)) return false;
7174
+ const reroutedTrace = reroutedTraceMap.get(trace.mspPairId);
7175
+ if (!reroutedTrace) return false;
7176
+ return reroutedTrace.tracePath !== trace.tracePath;
7177
+ });
7178
+ if (!originalHostTrace) return label;
7179
+ const reroutedHostTrace = reroutedTraceMap.get(originalHostTrace.mspPairId);
7180
+ const connectorIndex = originalTraces.findIndex((trace) => {
7181
+ if (trace.mspPairId === originalHostTrace.mspPairId) return false;
7182
+ if (trace.globalConnNetId !== label.globalConnNetId) return false;
7183
+ if (!tracePathContainsPoint(trace.tracePath, label.anchorPoint)) {
7184
+ return false;
7185
+ }
7186
+ return label.pinIds.every((pinId) => trace.pinIds.includes(pinId));
7187
+ });
7188
+ if (connectorIndex < 0) return label;
7189
+ const connector = originalTraces[connectorIndex];
7190
+ const originalJunction = connector.tracePath.find(
7191
+ (point) => tracePathContainsPoint(originalHostTrace.tracePath, point)
7192
+ );
7193
+ if (!originalJunction) return label;
7194
+ const reroutedJunction = getMovedAnchorPointForReroute(
7195
+ originalJunction,
7196
+ originalHostTrace.tracePath,
7197
+ reroutedHostTrace.tracePath
7198
+ );
7199
+ if (!reroutedJunction) return label;
7200
+ const delta = {
7201
+ x: reroutedJunction.x - originalJunction.x,
7202
+ y: reroutedJunction.y - originalJunction.y
7203
+ };
7204
+ const movedConnector = {
7205
+ ...connector,
7206
+ tracePath: connector.tracePath.map((point) => ({
7207
+ x: point.x + delta.x,
7208
+ y: point.y + delta.y
7209
+ }))
7210
+ };
7211
+ traces[connectorIndex] = movedConnector;
7212
+ return moveAttachedLabelsToReroutedTrace({
7213
+ trace: connector,
7214
+ originalTracePath: connector.tracePath,
7215
+ reroutedTracePath: movedConnector.tracePath,
7216
+ netLabelPlacements: [label]
7217
+ })[0];
7218
+ });
7219
+ return { traces, netLabelPlacements: labels };
7220
+ };
7162
7221
 
7163
7222
  // lib/solvers/Example28Solver/doesPathRunAlongChipBoundary.ts
7164
7223
  var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
@@ -7951,6 +8010,7 @@ var createPortOnlyLabelConnectorTrace = ({
7951
8010
 
7952
8011
  // lib/solvers/AvailableNetOrientationSolver/constants.ts
7953
8012
  var LABEL_SEARCH_STEP = 0.05;
8013
+ var MAX_RECORDED_CANDIDATES = 2e3;
7954
8014
  var WICK_CLEARANCE = 1e-3;
7955
8015
  var EPS12 = 1e-9;
7956
8016
  var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS12;
@@ -8007,14 +8067,6 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
8007
8067
  }
8008
8068
  return false;
8009
8069
  };
8010
- var tracePathCrossesAnyBounds = (tracePath, bounds) => {
8011
- for (let i = 0; i < tracePath.length - 1; i++) {
8012
- if (segmentCrossesBoundsInterior(tracePath[i], tracePath[i + 1], bounds)) {
8013
- return true;
8014
- }
8015
- }
8016
- return false;
8017
- };
8018
8070
  var tracePathIntersectsBounds = (tracePath, bounds) => {
8019
8071
  for (let i = 0; i < tracePath.length - 1; i++) {
8020
8072
  if (segmentIntersectsRect2(tracePath[i], tracePath[i + 1], bounds)) {
@@ -8184,6 +8236,145 @@ var ensureGraphicsArrays = (graphics) => {
8184
8236
  if (!graphics.texts) graphics.texts = [];
8185
8237
  };
8186
8238
 
8239
+ // lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationObstacleIndex.ts
8240
+ import Flatbush2 from "flatbush";
8241
+ var AvailableNetOrientationObstacleIndex = class {
8242
+ chipObstacleSpatialIndex;
8243
+ labelIndex = null;
8244
+ traceSegmentIndex = null;
8245
+ traceSegments = [];
8246
+ constructor(params) {
8247
+ this.chipObstacleSpatialIndex = params.chipObstacleSpatialIndex;
8248
+ this.rebuild(params);
8249
+ }
8250
+ rebuild(params) {
8251
+ this.labelIndex = this.buildLabelIndex(params.netLabelPlacements);
8252
+ this.traceSegments = this.getTraceSegments(params.traces);
8253
+ this.traceSegmentIndex = this.buildTraceSegmentIndex(this.traceSegments);
8254
+ }
8255
+ getLabelIndicesInBounds(bounds) {
8256
+ if (!this.labelIndex) return [];
8257
+ return this.labelIndex.search(
8258
+ bounds.minX,
8259
+ bounds.minY,
8260
+ bounds.maxX,
8261
+ bounds.maxY
8262
+ );
8263
+ }
8264
+ getTraceSegmentsInBounds(bounds) {
8265
+ if (!this.traceSegmentIndex) return [];
8266
+ const searchBounds = getPaddedBounds(bounds, EPS12);
8267
+ return this.traceSegmentIndex.search(
8268
+ searchBounds.minX,
8269
+ searchBounds.minY,
8270
+ searchBounds.maxX,
8271
+ searchBounds.maxY
8272
+ ).map((segmentIndex) => this.traceSegments[segmentIndex]);
8273
+ }
8274
+ getLabelIndicesNearTracePath(tracePath) {
8275
+ const labelIndices = /* @__PURE__ */ new Set();
8276
+ for (let pointIndex = 0; pointIndex < tracePath.length - 1; pointIndex++) {
8277
+ const segmentBounds = getPaddedBounds(
8278
+ getSegmentBounds(tracePath[pointIndex], tracePath[pointIndex + 1]),
8279
+ EPS12
8280
+ );
8281
+ for (const labelIndex of this.getLabelIndicesInBounds(segmentBounds)) {
8282
+ labelIndices.add(labelIndex);
8283
+ }
8284
+ }
8285
+ return [...labelIndices].sort((a, b) => a - b);
8286
+ }
8287
+ doesTracePathCrossChip(tracePath) {
8288
+ for (let pointIndex = 0; pointIndex < tracePath.length - 1; pointIndex++) {
8289
+ const start = tracePath[pointIndex];
8290
+ const end = tracePath[pointIndex + 1];
8291
+ const nearbyChips = this.chipObstacleSpatialIndex.getChipsInBounds(
8292
+ getPaddedBounds(getSegmentBounds(start, end), EPS12)
8293
+ );
8294
+ for (const chip of nearbyChips) {
8295
+ if (segmentCrossesBoundsInterior(start, end, chip.bounds)) return true;
8296
+ }
8297
+ }
8298
+ return false;
8299
+ }
8300
+ doesTraceCrossBoundsInterior(bounds) {
8301
+ for (const segment of this.getTraceSegmentsInBounds(bounds)) {
8302
+ if (segmentCrossesBoundsInterior(segment.start, segment.end, bounds)) {
8303
+ return true;
8304
+ }
8305
+ }
8306
+ return false;
8307
+ }
8308
+ doesTraceIntersectBounds(params) {
8309
+ for (const segment of this.getTraceSegmentsInBounds(params.bounds)) {
8310
+ if (segment.trace.globalConnNetId === params.excludedGlobalConnNetId) {
8311
+ continue;
8312
+ }
8313
+ if (segmentIntersectsRect2(segment.start, segment.end, params.bounds)) {
8314
+ return true;
8315
+ }
8316
+ }
8317
+ return false;
8318
+ }
8319
+ buildLabelIndex(netLabelPlacements) {
8320
+ if (netLabelPlacements.length === 0) return null;
8321
+ const labelIndex = new Flatbush2(netLabelPlacements.length);
8322
+ for (const label of netLabelPlacements) {
8323
+ labelIndex.add(
8324
+ label.center.x - label.width / 2,
8325
+ label.center.y - label.height / 2,
8326
+ label.center.x + label.width / 2,
8327
+ label.center.y + label.height / 2
8328
+ );
8329
+ }
8330
+ labelIndex.finish();
8331
+ return labelIndex;
8332
+ }
8333
+ getTraceSegments(traces) {
8334
+ const traceSegments = [];
8335
+ for (const trace of traces) {
8336
+ for (let pointIndex = 0; pointIndex < trace.tracePath.length - 1; pointIndex++) {
8337
+ traceSegments.push({
8338
+ trace,
8339
+ start: trace.tracePath[pointIndex],
8340
+ end: trace.tracePath[pointIndex + 1]
8341
+ });
8342
+ }
8343
+ }
8344
+ return traceSegments;
8345
+ }
8346
+ buildTraceSegmentIndex(traceSegments) {
8347
+ if (traceSegments.length === 0) return null;
8348
+ const traceSegmentIndex = new Flatbush2(traceSegments.length);
8349
+ for (const segment of traceSegments) {
8350
+ traceSegmentIndex.add(
8351
+ Math.min(segment.start.x, segment.end.x),
8352
+ Math.min(segment.start.y, segment.end.y),
8353
+ Math.max(segment.start.x, segment.end.x),
8354
+ Math.max(segment.start.y, segment.end.y)
8355
+ );
8356
+ }
8357
+ traceSegmentIndex.finish();
8358
+ return traceSegmentIndex;
8359
+ }
8360
+ };
8361
+ function getSegmentBounds(start, end) {
8362
+ return {
8363
+ minX: Math.min(start.x, end.x),
8364
+ minY: Math.min(start.y, end.y),
8365
+ maxX: Math.max(start.x, end.x),
8366
+ maxY: Math.max(start.y, end.y)
8367
+ };
8368
+ }
8369
+ function getPaddedBounds(bounds, padding) {
8370
+ return {
8371
+ minX: bounds.minX - padding,
8372
+ minY: bounds.minY - padding,
8373
+ maxX: bounds.maxX + padding,
8374
+ maxY: bounds.maxY + padding
8375
+ };
8376
+ }
8377
+
8187
8378
  // lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
8188
8379
  var LABEL_TRACE_CLEARANCE2 = 0.1;
8189
8380
  var AvailableNetOrientationSolver = class extends BaseSolver {
@@ -8202,6 +8393,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8202
8393
  chipObstacleSpatialIndex;
8203
8394
  maxSearchDistance;
8204
8395
  pinMap;
8396
+ obstacleIndex;
8205
8397
  constructor(params) {
8206
8398
  super();
8207
8399
  this.inputProblem = params.inputProblem;
@@ -8214,6 +8406,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8214
8406
  this.pinMap = getPinMap(params.inputProblem);
8215
8407
  this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
8216
8408
  this.maxSearchDistance = getMaxSearchDistance(params.inputProblem);
8409
+ this.obstacleIndex = new AvailableNetOrientationObstacleIndex({
8410
+ chipObstacleSpatialIndex: this.chipObstacleSpatialIndex,
8411
+ netLabelPlacements: this.outputNetLabelPlacements,
8412
+ traces: this.traces
8413
+ });
8217
8414
  this.crowdedPortOnlyLabelIndices = this.getCrowdedPortOnlyLabelIndices();
8218
8415
  this.queuedLabelIndices = this.getProcessableLabelIndices();
8219
8416
  this.setCurrentLabel(this.queuedLabelIndices[0] ?? null);
@@ -8395,6 +8592,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8395
8592
  ...toNetLabelPlacementPatch(candidate)
8396
8593
  };
8397
8594
  this.addConnectorTrace(label, candidate, labelIndex);
8595
+ this.obstacleIndex.rebuild({
8596
+ netLabelPlacements: this.outputNetLabelPlacements,
8597
+ traces: this.traces
8598
+ });
8398
8599
  }
8399
8600
  addConnectorTrace(label, candidate, labelIndex) {
8400
8601
  if (candidate.phase === "trace-anchor") return;
@@ -8557,7 +8758,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8557
8758
  anchorY - label.anchorPoint.y,
8558
8759
  anchorX - label.anchorPoint.x
8559
8760
  );
8560
- this.currentCandidateResults.push(result);
8761
+ this.recordCandidateResult(result);
8561
8762
  if (result.status !== "valid") continue;
8562
8763
  result.selected = true;
8563
8764
  return result;
@@ -8592,7 +8793,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8592
8793
  labelIndex,
8593
8794
  "rotate"
8594
8795
  );
8595
- this.currentCandidateResults.push(result);
8796
+ this.recordCandidateResult(result);
8596
8797
  if (result.status === "valid") {
8597
8798
  result.selected = true;
8598
8799
  return result;
@@ -8617,7 +8818,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8617
8818
  labelIndex,
8618
8819
  "trace-anchor"
8619
8820
  );
8620
- this.currentCandidateResults.push(result);
8821
+ this.recordCandidateResult(result);
8621
8822
  if (result.status === "valid") {
8622
8823
  result.selected = true;
8623
8824
  return result;
@@ -8654,7 +8855,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8654
8855
  labelIndex,
8655
8856
  "outward-trace-anchor"
8656
8857
  );
8657
- this.currentCandidateResults.push(preservedColumnResult);
8858
+ this.recordCandidateResult(preservedColumnResult);
8658
8859
  if (preservedColumnResult.status === "valid") {
8659
8860
  preservedColumnResult.selected = true;
8660
8861
  return preservedColumnResult;
@@ -8682,7 +8883,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8682
8883
  "outward-trace-anchor",
8683
8884
  distance5
8684
8885
  );
8685
- this.currentCandidateResults.push(result);
8886
+ this.recordCandidateResult(result);
8686
8887
  if (result.status === "valid") {
8687
8888
  result.selected = true;
8688
8889
  return result;
@@ -8807,7 +9008,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8807
9008
  distance5,
8808
9009
  outwardDistance
8809
9010
  );
8810
- this.currentCandidateResults.push(result);
9011
+ this.recordCandidateResult(result);
8811
9012
  if (result.status === "valid") {
8812
9013
  result.selected = true;
8813
9014
  return result;
@@ -8831,6 +9032,18 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8831
9032
  })
8832
9033
  };
8833
9034
  }
9035
+ recordCandidateResult(candidate) {
9036
+ this.stats.candidateEvaluations = (this.stats.candidateEvaluations ?? 0) + 1;
9037
+ if (this.currentCandidateResults.length < MAX_RECORDED_CANDIDATES) {
9038
+ this.currentCandidateResults.push(candidate);
9039
+ } else if (candidate.status === "valid") {
9040
+ this.currentCandidateResults[MAX_RECORDED_CANDIDATES - 1] = candidate;
9041
+ }
9042
+ this.stats.maxRecordedCandidates = Math.max(
9043
+ this.stats.maxRecordedCandidates ?? 0,
9044
+ this.currentCandidateResults.length
9045
+ );
9046
+ }
8834
9047
  getCandidateConnectorTrace(label, candidate, labelIndex) {
8835
9048
  if (this.crowdedPortOnlyLabelIndices.has(labelIndex) && this.getChipSideForPoint(label.anchorPoint) === "top" && isYOrientation(candidate.orientation)) {
8836
9049
  return simplifyOrthogonalPath([
@@ -9070,12 +9283,12 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9070
9283
  },
9071
9284
  labelIndex
9072
9285
  );
9073
- for (const chip of this.chipObstacleSpatialIndex.chips) {
9074
- if (tracePathCrossesAnyBounds(connectorTrace, chip.bounds)) {
9075
- return "chip-collision";
9076
- }
9286
+ if (this.obstacleIndex.doesTracePathCrossChip(connectorTrace)) {
9287
+ return "chip-collision";
9077
9288
  }
9078
- for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
9289
+ for (const i of this.obstacleIndex.getLabelIndicesNearTracePath(
9290
+ connectorTrace
9291
+ )) {
9079
9292
  if (i === labelIndex) continue;
9080
9293
  if (this.shouldIgnorePendingCrowdedLabel(labelIndex, i)) continue;
9081
9294
  const otherLabel = this.outputNetLabelPlacements[i];
@@ -9117,7 +9330,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9117
9330
  if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
9118
9331
  return "text-collision";
9119
9332
  }
9120
- if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
9333
+ if (this.obstacleIndex.doesTraceCrossBoundsInterior(bounds)) {
9121
9334
  return "trace-collision";
9122
9335
  }
9123
9336
  if (this.isTraceTooCloseToLabel(bounds, label)) {
@@ -9131,13 +9344,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9131
9344
  isTraceTooCloseToLabel(bounds, label) {
9132
9345
  if (!this.shouldCheckTraceClearanceForLabel(label)) return false;
9133
9346
  const clearanceBounds = this.getLabelTraceClearanceBounds(bounds);
9134
- for (const trace of Object.values(this.traceMap)) {
9135
- if (trace.globalConnNetId === label.globalConnNetId) continue;
9136
- if (tracePathIntersectsBounds(trace.tracePath, clearanceBounds)) {
9137
- return true;
9138
- }
9139
- }
9140
- return false;
9347
+ return this.obstacleIndex.doesTraceIntersectBounds({
9348
+ bounds: clearanceBounds,
9349
+ excludedGlobalConnNetId: label.globalConnNetId
9350
+ });
9141
9351
  }
9142
9352
  getLabelTraceClearanceBounds(bounds) {
9143
9353
  return {
@@ -9220,7 +9430,14 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9220
9430
  return this.crowdedPortOnlyLabelIndices.has(labelIndex) && this.crowdedPortOnlyLabelIndices.has(otherLabelIndex) && this.queuedLabelIndices.includes(otherLabelIndex);
9221
9431
  }
9222
9432
  intersectsAnyOtherNetLabel(bounds, labelIndex) {
9223
- for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
9433
+ const searchBounds = {
9434
+ minX: bounds.minX - LABEL_SEARCH_STEP,
9435
+ minY: bounds.minY - LABEL_SEARCH_STEP,
9436
+ maxX: bounds.maxX + LABEL_SEARCH_STEP,
9437
+ maxY: bounds.maxY + LABEL_SEARCH_STEP
9438
+ };
9439
+ const nearbyLabelIndices = this.obstacleIndex.getLabelIndicesInBounds(searchBounds).sort((a, b) => a - b);
9440
+ for (const i of nearbyLabelIndices) {
9224
9441
  if (i === labelIndex) continue;
9225
9442
  if (this.shouldIgnorePendingCrowdedLabel(labelIndex, i)) continue;
9226
9443
  const label = this.outputNetLabelPlacements[i];
@@ -9234,7 +9451,14 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9234
9451
  return false;
9235
9452
  }
9236
9453
  sharesChipBoundary(bounds) {
9237
- for (const chip of this.chipObstacleSpatialIndex.chips) {
9454
+ const boundarySearchBounds = {
9455
+ minX: bounds.minX - WICK_CLEARANCE - EPS12,
9456
+ minY: bounds.minY - WICK_CLEARANCE - EPS12,
9457
+ maxX: bounds.maxX + WICK_CLEARANCE + EPS12,
9458
+ maxY: bounds.maxY + WICK_CLEARANCE + EPS12
9459
+ };
9460
+ const nearbyChips = this.chipObstacleSpatialIndex.getChipsInBounds(boundarySearchBounds);
9461
+ for (const chip of nearbyChips) {
9238
9462
  const chipBounds = chip.bounds;
9239
9463
  const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS12 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS12;
9240
9464
  const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS12 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS12;
@@ -11749,6 +11973,35 @@ var railIsOnFacingSide = ({
11749
11973
  if (pin._facingDirection === "y+") return railY > pin.y;
11750
11974
  return false;
11751
11975
  };
11976
+ var getAttachedLabelIndexes = (trace, netLabelPlacements) => netLabelPlacements.flatMap((label, index) => {
11977
+ const labelOwnsTrace = label.mspConnectionPairIds.includes(trace.mspPairId) || label.mspConnectionPairIds.length === 0 && label.globalConnNetId === trace.globalConnNetId;
11978
+ return labelOwnsTrace && tracePathContainsPoint(trace.tracePath, label.anchorPoint) ? [index] : [];
11979
+ });
11980
+ var moveAttachedLabels = ({
11981
+ trace,
11982
+ reroutedTracePath,
11983
+ netLabelPlacements,
11984
+ attachedLabelIndexes
11985
+ }) => {
11986
+ const attachedLabels = attachedLabelIndexes.map(
11987
+ (index) => netLabelPlacements[index]
11988
+ );
11989
+ const movedAttachedLabels = moveAttachedLabelsToReroutedTrace({
11990
+ trace,
11991
+ originalTracePath: trace.tracePath,
11992
+ reroutedTracePath,
11993
+ netLabelPlacements: attachedLabels
11994
+ });
11995
+ const movedLabelByIndex = new Map(
11996
+ attachedLabelIndexes.map((labelIndex, index) => [
11997
+ labelIndex,
11998
+ movedAttachedLabels[index]
11999
+ ])
12000
+ );
12001
+ return netLabelPlacements.map(
12002
+ (label, index) => movedLabelByIndex.get(index) ?? label
12003
+ );
12004
+ };
11752
12005
  var getAlignedBranchPath = ({
11753
12006
  donorTrace,
11754
12007
  branchTrace
@@ -11789,7 +12042,8 @@ var candidateIsClear = ({
11789
12042
  originalTrace,
11790
12043
  traces,
11791
12044
  inputProblem,
11792
- netLabelPlacements
12045
+ candidateNetLabelPlacements,
12046
+ attachedLabelIndexes
11793
12047
  }) => {
11794
12048
  const obstacles = getObstacleRects(inputProblem);
11795
12049
  if (isPathCollidingWithObstacles(candidateTrace.tracePath, obstacles)) {
@@ -11801,14 +12055,15 @@ var candidateIsClear = ({
11801
12055
  if (doesPathCoincideWithTraces(candidateTrace.tracePath, otherNetTraces)) {
11802
12056
  return false;
11803
12057
  }
11804
- const candidateTraces = traces.map((trace) => {
11805
- if (trace.mspPairId === originalTrace.mspPairId) return candidateTrace;
11806
- return trace;
11807
- });
11808
- if (!preservesLabelAnchors(netLabelPlacements, traces, candidateTraces)) {
12058
+ if (!attachedLabelIndexes.every(
12059
+ (index) => tracePathContainsPoint(
12060
+ candidateTrace.tracePath,
12061
+ candidateNetLabelPlacements[index].anchorPoint
12062
+ )
12063
+ )) {
11809
12064
  return false;
11810
12065
  }
11811
- const otherNetLabelPlacements = netLabelPlacements.filter(
12066
+ const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
11812
12067
  (label) => label.globalConnNetId !== candidateTrace.globalConnNetId
11813
12068
  );
11814
12069
  if (pathIntersectsAnyNetLabel({
@@ -11817,7 +12072,7 @@ var candidateIsClear = ({
11817
12072
  })) {
11818
12073
  return false;
11819
12074
  }
11820
- const sameNetLabelPlacements = netLabelPlacements.filter(
12075
+ const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
11821
12076
  (label) => label.globalConnNetId === candidateTrace.globalConnNetId
11822
12077
  );
11823
12078
  if (!pathIntersectsAnyNetLabel({
@@ -11843,9 +12098,14 @@ var alignSameNetJunctions = ({
11843
12098
  netLabelPlacements
11844
12099
  }) => {
11845
12100
  let outputTraces = [...traces];
12101
+ let outputNetLabelPlacements = [...netLabelPlacements];
11846
12102
  const alignedBranchTraceIds = /* @__PURE__ */ new Set();
11847
12103
  let alignedJunctionCount = 0;
11848
- for (const donorTraceId of traces.map((trace) => trace.mspPairId)) {
12104
+ const donorTraceIds = traces.map((trace) => trace.mspPairId);
12105
+ const pendingDonorTraceIds = new Set(donorTraceIds);
12106
+ for (let donorIndex = 0; donorIndex < donorTraceIds.length; donorIndex++) {
12107
+ const donorTraceId = donorTraceIds[donorIndex];
12108
+ pendingDonorTraceIds.delete(donorTraceId);
11849
12109
  const donorTrace = outputTraces.find(
11850
12110
  (trace) => trace.mspPairId === donorTraceId
11851
12111
  );
@@ -11866,12 +12126,23 @@ var alignSameNetJunctions = ({
11866
12126
  if (!removesVisibleSegment && !shortensVisibleTrace) {
11867
12127
  continue;
11868
12128
  }
12129
+ const attachedLabelIndexes = getAttachedLabelIndexes(
12130
+ branchTrace,
12131
+ outputNetLabelPlacements
12132
+ );
12133
+ const candidateNetLabelPlacements = moveAttachedLabels({
12134
+ trace: branchTrace,
12135
+ reroutedTracePath: candidatePath,
12136
+ netLabelPlacements: outputNetLabelPlacements,
12137
+ attachedLabelIndexes
12138
+ });
11869
12139
  if (!candidateIsClear({
11870
12140
  candidateTrace,
11871
12141
  originalTrace: branchTrace,
11872
12142
  traces: outputTraces,
11873
12143
  inputProblem,
11874
- netLabelPlacements
12144
+ candidateNetLabelPlacements,
12145
+ attachedLabelIndexes
11875
12146
  })) {
11876
12147
  continue;
11877
12148
  }
@@ -11879,32 +12150,44 @@ var alignSameNetJunctions = ({
11879
12150
  if (trace.mspPairId === branchTrace.mspPairId) return candidateTrace;
11880
12151
  return trace;
11881
12152
  });
12153
+ outputNetLabelPlacements = candidateNetLabelPlacements;
11882
12154
  alignedBranchTraceIds.add(branchTrace.mspPairId);
11883
12155
  alignedJunctionCount++;
12156
+ if (!pendingDonorTraceIds.has(branchTrace.mspPairId)) {
12157
+ donorTraceIds.push(branchTrace.mspPairId);
12158
+ pendingDonorTraceIds.add(branchTrace.mspPairId);
12159
+ }
11884
12160
  }
11885
12161
  }
11886
- return { traces: outputTraces, alignedJunctionCount };
12162
+ return {
12163
+ traces: outputTraces,
12164
+ netLabelPlacements: outputNetLabelPlacements,
12165
+ alignedJunctionCount
12166
+ };
11887
12167
  };
11888
12168
 
11889
12169
  // lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts
11890
12170
  var SameNetJunctionAlignmentSolver = class extends BaseSolver {
11891
12171
  input;
11892
12172
  outputTraces;
12173
+ outputNetLabelPlacements;
11893
12174
  constructor(input) {
11894
12175
  super();
11895
12176
  this.input = input;
11896
12177
  this.outputTraces = input.traces;
12178
+ this.outputNetLabelPlacements = input.netLabelPlacements;
11897
12179
  }
11898
12180
  _step() {
11899
12181
  const result = alignSameNetJunctions(this.input);
11900
12182
  this.outputTraces = result.traces;
12183
+ this.outputNetLabelPlacements = result.netLabelPlacements;
11901
12184
  this.stats.alignedJunctionCount = result.alignedJunctionCount;
11902
12185
  this.solved = true;
11903
12186
  }
11904
12187
  getOutput() {
11905
12188
  return {
11906
12189
  traces: this.outputTraces,
11907
- netLabelPlacements: this.input.netLabelPlacements
12190
+ netLabelPlacements: this.outputNetLabelPlacements
11908
12191
  };
11909
12192
  }
11910
12193
  visualize() {
@@ -11918,7 +12201,7 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
11918
12201
  strokeColor: "purple"
11919
12202
  });
11920
12203
  }
11921
- for (const label of this.input.netLabelPlacements) {
12204
+ for (const label of this.outputNetLabelPlacements) {
11922
12205
  const labelRect = {
11923
12206
  center: label.center,
11924
12207
  width: label.width,
@@ -12904,11 +13187,16 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
12904
13187
  (instance) => {
12905
13188
  const previousOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
12906
13189
  const alignmentOutput = instance.traceCleanupSolver2.getOutput();
13190
+ const connectorMovement = moveNetLabelConnectorsToReroutedTraces({
13191
+ originalTraces: previousOutput.traces,
13192
+ reroutedTraces: alignmentOutput.traces,
13193
+ netLabelPlacements: previousOutput.netLabelPlacements
13194
+ });
12907
13195
  const previousTraceMap = new Map(
12908
13196
  previousOutput.traces.map((trace) => [trace.mspPairId, trace])
12909
13197
  );
12910
- let netLabelPlacements = previousOutput.netLabelPlacements;
12911
- for (const trace of alignmentOutput.traces) {
13198
+ let netLabelPlacements = connectorMovement.netLabelPlacements;
13199
+ for (const trace of connectorMovement.traces) {
12912
13200
  const previousTrace = previousTraceMap.get(trace.mspPairId);
12913
13201
  if (!previousTrace || previousTrace.tracePath === trace.tracePath) {
12914
13202
  continue;
@@ -12928,7 +13216,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
12928
13216
  return [
12929
13217
  {
12930
13218
  inputProblem: instance.inputProblem,
12931
- traces: alignmentOutput.traces,
13219
+ traces: connectorMovement.traces,
12932
13220
  netLabelPlacements
12933
13221
  }
12934
13222
  ];