@tscircuit/schematic-trace-solver 0.0.200 → 0.0.202

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
@@ -1149,6 +1149,8 @@ type Candidate = {
1149
1149
  status: CandidateStatus | null;
1150
1150
  };
1151
1151
  interface NetLabelNetLabelCollisionSolverParams {
1152
+ /** Move temporary port-only inline labels before moving their neighbors. */
1153
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions?: boolean;
1152
1154
  /** Use the final horizontal tag envelope after inline conversion. */
1153
1155
  useRenderedLabelBounds?: boolean;
1154
1156
  /** Already placed labels that are obstacles, but must not be moved. */
@@ -1181,6 +1183,10 @@ declare class NetLabelNetLabelCollisionSolver extends BaseSolver {
1181
1183
  };
1182
1184
  private labelBounds;
1183
1185
  private collisionKey;
1186
+ private isSingleTerminalInlineEligible;
1187
+ private hasSatisfiedSingleOrientation;
1188
+ private isAlignedWithHostTrace;
1189
+ private isProtectedAgainstTemporaryInlineCollision;
1184
1190
  private findNextCollidingPair;
1185
1191
  private netLabelWidthOf;
1186
1192
  private netLabelHeightOf;
package/dist/index.js CHANGED
@@ -12776,7 +12776,8 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
12776
12776
  traces: this.traces,
12777
12777
  netLabelPlacements: this.netLabelPlacements,
12778
12778
  fixedNetLabelPlacements: this.params.fixedNetLabelPlacements,
12779
- useRenderedLabelBounds: this.params.useRenderedLabelBounds
12779
+ useRenderedLabelBounds: this.params.useRenderedLabelBounds,
12780
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: this.params.preserveNonInlineLabelsAgainstInlineEligibleCollisions
12780
12781
  };
12781
12782
  }
12782
12783
  getOutput() {
@@ -12790,6 +12791,36 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
12790
12791
  collisionKey(a, b) {
12791
12792
  return [a.globalConnNetId, b.globalConnNetId].sort().join("::");
12792
12793
  }
12794
+ isSingleTerminalInlineEligible(label) {
12795
+ if (!label.netId) return false;
12796
+ return [
12797
+ ...this.inputProblem.netConnections,
12798
+ ...this.inputProblem.directConnections
12799
+ ].some(
12800
+ (connection) => connection.allowInlineNetLabel && connection.pinIds.length === 1 && connection.netId === label.netId && label.pinIds.every((pinId) => connection.pinIds.includes(pinId))
12801
+ );
12802
+ }
12803
+ hasSatisfiedSingleOrientation(label) {
12804
+ if (!label.netId) return false;
12805
+ const orientations = this.inputProblem.availableNetLabelOrientations[label.netId];
12806
+ return orientations?.length === 1 && orientations[0] === label.orientation;
12807
+ }
12808
+ isAlignedWithHostTrace(label) {
12809
+ const orientationAxis = label.orientation.startsWith("x") ? "x" : "y";
12810
+ return label.mspConnectionPairIds.some((mspPairId) => {
12811
+ const trace = this.traceMap[mspPairId];
12812
+ return trace?.tracePath.slice(1).some((end, index) => {
12813
+ const start = trace.tracePath[index];
12814
+ if (!tracePathContainsPoint([start, end], label.anchorPoint))
12815
+ return false;
12816
+ const segmentAxis = Math.abs(start.x - end.x) < SEGMENT_PARALLEL_EPS ? "y" : "x";
12817
+ return segmentAxis === orientationAxis;
12818
+ });
12819
+ });
12820
+ }
12821
+ isProtectedAgainstTemporaryInlineCollision(label) {
12822
+ return !this.isSingleTerminalInlineEligible(label) && this.hasSatisfiedSingleOrientation(label) && this.isAlignedWithHostTrace(label);
12823
+ }
12793
12824
  findNextCollidingPair() {
12794
12825
  const labels = [
12795
12826
  ...this.outputNetLabelPlacements,
@@ -12962,6 +12993,18 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
12962
12993
  this.labelsToTry = [pair[1], pair[0]].filter(
12963
12994
  (label) => !(this.params.fixedNetLabelPlacements ?? []).includes(label)
12964
12995
  );
12996
+ if (this.params.preserveNonInlineLabelsAgainstInlineEligibleCollisions && pair.some((label) => this.isSingleTerminalInlineEligible(label)) && pair.some(
12997
+ (label) => this.isProtectedAgainstTemporaryInlineCollision(label)
12998
+ )) {
12999
+ this.labelsToTry = this.labelsToTry.filter(
13000
+ (label) => this.isSingleTerminalInlineEligible(label) || !this.isProtectedAgainstTemporaryInlineCollision(label)
13001
+ );
13002
+ }
13003
+ if (this.labelsToTry.length === 0) {
13004
+ this.skippedCollisionKeys.add(this.collisionKey(pair[0], pair[1]));
13005
+ this.clearActiveSearch();
13006
+ return;
13007
+ }
12965
13008
  this.beginSearchForLabel(this.labelsToTry.shift());
12966
13009
  return;
12967
13010
  }
@@ -15159,6 +15202,38 @@ var placeGroundRailLabelsAtOuterEnd = ({
15159
15202
  return output;
15160
15203
  };
15161
15204
 
15205
+ // lib/solvers/SameNetJunctionAlignmentSolver/shortenSameSideRailToLabelAnchor.ts
15206
+ var MAX_LOCAL_RAIL_SHIFT = 0.5;
15207
+ var MIN_ESCAPE_LENGTH = 1;
15208
+ var shortenSameSideRailToLabelAnchor = ({
15209
+ traces,
15210
+ netLabelPlacements
15211
+ }) => traces.map((trace) => {
15212
+ const path = trace.tracePath;
15213
+ const firstPin = path[0];
15214
+ const secondPin = path.at(-1);
15215
+ if (!firstPin || !secondPin || !nearlyEqual(firstPin.x, secondPin.x)) {
15216
+ return trace;
15217
+ }
15218
+ const railIndex = path.findIndex(
15219
+ (point, index) => index > 0 && index < path.length - 2 && isHorizontal2(path[index - 1], point) && isVertical2(point, path[index + 1]) && isHorizontal2(path[index + 1], path[index + 2])
15220
+ );
15221
+ if (railIndex < 0) return trace;
15222
+ const firstRail = path[railIndex];
15223
+ const secondRail = path[railIndex + 1];
15224
+ const escapeLength = Math.abs(firstRail.x - firstPin.x);
15225
+ const label = netLabelPlacements.find(
15226
+ ({ anchorPoint, orientation, mspConnectionPairIds }) => mspConnectionPairIds.includes(trace.mspPairId) && (orientation === "y-" && anchorPoint.y < Math.min(firstPin.y, secondPin.y) || orientation === "y+" && anchorPoint.y > Math.max(firstPin.y, secondPin.y)) && anchorPoint.x >= Math.min(firstPin.x, firstRail.x) && anchorPoint.x <= Math.max(firstPin.x, firstRail.x) && Math.abs(anchorPoint.x - firstPin.x) < escapeLength && Math.abs(anchorPoint.x - firstRail.x) <= MAX_LOCAL_RAIL_SHIFT && escapeLength >= MIN_ESCAPE_LENGTH
15227
+ );
15228
+ if (!label) return trace;
15229
+ return {
15230
+ ...trace,
15231
+ tracePath: path.map(
15232
+ (point, index) => index === railIndex || index === railIndex + 1 ? { ...point, x: label.anchorPoint.x } : point
15233
+ )
15234
+ };
15235
+ });
15236
+
15162
15237
  // lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts
15163
15238
  var SameNetJunctionAlignmentSolver = class extends BaseSolver {
15164
15239
  input;
@@ -15172,10 +15247,13 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
15172
15247
  }
15173
15248
  _step() {
15174
15249
  const alignment = alignSameNetJunctions(this.input);
15175
- this.outputTraces = alignment.traces;
15250
+ this.outputTraces = shortenSameSideRailToLabelAnchor({
15251
+ traces: alignment.traces,
15252
+ netLabelPlacements: alignment.netLabelPlacements
15253
+ });
15176
15254
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
15177
15255
  inputProblem: this.input.inputProblem,
15178
- traces: alignment.traces,
15256
+ traces: this.outputTraces,
15179
15257
  netLabelPlacements: alignment.netLabelPlacements
15180
15258
  });
15181
15259
  this.stats.alignedJunctionCount = alignment.alignedJunctionCount;
@@ -15593,6 +15671,11 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15593
15671
  minY: label.center.y - label.height / 2,
15594
15672
  maxY: label.center.y + label.height / 2
15595
15673
  }));
15674
+ const pendingReplacementBounds = output.netLabelPlacements.filter(
15675
+ (label) => pendingInlinePlacements.some(
15676
+ (placement) => placement.globalConnNetId === label.globalConnNetId && placement.pinIds.length === label.pinIds.length && placement.pinIds.every((pinId) => label.pinIds.includes(pinId))
15677
+ )
15678
+ ).map(getAnchoredNetLabelRenderedBounds);
15596
15679
  const pinPositions = [
15597
15680
  ...inputProblem.chips.flatMap((chip) => chip.pins),
15598
15681
  ...output.traces.flatMap((trace) => trace.pins)
@@ -15604,13 +15687,26 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15604
15687
  const axis = Math.abs(a.x - b.x) < EPS16 ? "x" : "y";
15605
15688
  const along = axis === "x" ? "y" : "x";
15606
15689
  if (Math.abs(a[axis] - b[axis]) > EPS16 || same(a, b)) continue;
15607
- const blocking = [
15690
+ const candidateObstacles = [
15608
15691
  ...output.netLabelPlacements.filter((label) => label.globalConnNetId !== trace.globalConnNetId).map(getAnchoredNetLabelRenderedBounds),
15609
15692
  ...inlineBounds,
15610
15693
  ...terminalBounds.filter(
15611
15694
  ({ label }) => !trace.pinIds.some((pinId) => label.pinIds.includes(pinId))
15612
15695
  ).map(({ bounds }) => bounds)
15613
- ].filter((bounds) => segmentIntersectsRect(a, b, bounds));
15696
+ ];
15697
+ const attachedLabelBounds = output.netLabelPlacements.filter(
15698
+ (label) => label.globalConnNetId === trace.globalConnNetId && label.netId != null && inputProblem.availableNetLabelOrientations[label.netId]?.length === 1 && inputProblem.availableNetLabelOrientations[label.netId]?.[0] === label.orientation && (axis === "x" ? label.orientation === "y+" || label.orientation === "y-" : label.orientation === "x+" || label.orientation === "x-") && tracePathContainsPoint([a, b], label.anchorPoint)
15699
+ ).map(getAnchoredNetLabelRenderedBounds);
15700
+ const blocking = [
15701
+ ...candidateObstacles.filter(
15702
+ (bounds) => segmentIntersectsRect(a, b, bounds)
15703
+ ),
15704
+ ...pendingReplacementBounds.filter(
15705
+ (bounds) => attachedLabelBounds.some(
15706
+ (labelBounds) => boundsOverlap(labelBounds, bounds)
15707
+ )
15708
+ )
15709
+ ];
15614
15710
  if (!blocking.length) continue;
15615
15711
  let low = Math.min(a[along], b[along]);
15616
15712
  let high = Math.max(a[along], b[along]);
@@ -17524,7 +17620,8 @@ var InlineNetLabelSolver = class _InlineNetLabelSolver extends BaseSolver {
17524
17620
  netLabelPlacements: retainedNetLabelPlacements,
17525
17621
  fixedNetLabelPlacements: fixedLabels,
17526
17622
  netLabelConnectorTraceIds: this.netLabelConnectorTraceIds,
17527
- useRenderedLabelBounds: activeInlinePlacements.length > 0
17623
+ useRenderedLabelBounds: activeInlinePlacements.length > 0,
17624
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: true
17528
17625
  });
17529
17626
  if (activeInlinePlacements.length > 0) labelCollisionSolver.solve();
17530
17627
  const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
@@ -18975,7 +19072,8 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
18975
19072
  {
18976
19073
  inputProblem: instance.inputProblem,
18977
19074
  traces: simplificationOutput.traces,
18978
- netLabelPlacements: simplificationOutput.netLabelPlacements
19075
+ netLabelPlacements: simplificationOutput.netLabelPlacements,
19076
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: true
18979
19077
  }
18980
19078
  ];
18981
19079
  }
@@ -1195,6 +1195,7 @@ export class InlineNetLabelSolver extends BaseSolver {
1195
1195
  fixedNetLabelPlacements: fixedLabels,
1196
1196
  netLabelConnectorTraceIds: this.netLabelConnectorTraceIds,
1197
1197
  useRenderedLabelBounds: activeInlinePlacements.length > 0,
1198
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: true,
1198
1199
  })
1199
1200
  if (activeInlinePlacements.length > 0) labelCollisionSolver.solve()
1200
1201
  const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
@@ -75,6 +75,16 @@ export function* getLocalTraceLabelShifts(
75
75
  minY: label.center.y - label.height / 2,
76
76
  maxY: label.center.y + label.height / 2,
77
77
  }))
78
+ const pendingReplacementBounds = output.netLabelPlacements
79
+ .filter((label) =>
80
+ pendingInlinePlacements.some(
81
+ (placement) =>
82
+ placement.globalConnNetId === label.globalConnNetId &&
83
+ placement.pinIds.length === label.pinIds.length &&
84
+ placement.pinIds.every((pinId) => label.pinIds.includes(pinId)),
85
+ ),
86
+ )
87
+ .map(getAnchoredNetLabelRenderedBounds)
78
88
  const pinPositions = [
79
89
  ...inputProblem.chips.flatMap((chip) => chip.pins),
80
90
  ...output.traces.flatMap((trace) => trace.pins),
@@ -87,7 +97,7 @@ export function* getLocalTraceLabelShifts(
87
97
  const axis = Math.abs(a.x - b.x) < EPS ? "x" : "y"
88
98
  const along = axis === "x" ? "y" : "x"
89
99
  if (Math.abs(a[axis] - b[axis]) > EPS || same(a, b)) continue
90
- const blocking = [
100
+ const candidateObstacles = [
91
101
  ...output.netLabelPlacements
92
102
  .filter((label) => label.globalConnNetId !== trace.globalConnNetId)
93
103
  .map(getAnchoredNetLabelRenderedBounds),
@@ -98,7 +108,32 @@ export function* getLocalTraceLabelShifts(
98
108
  !trace.pinIds.some((pinId) => label.pinIds.includes(pinId)),
99
109
  )
100
110
  .map(({ bounds }) => bounds),
101
- ].filter((bounds) => segmentIntersectsRect(a, b, bounds))
111
+ ]
112
+ const attachedLabelBounds = output.netLabelPlacements
113
+ .filter(
114
+ (label) =>
115
+ label.globalConnNetId === trace.globalConnNetId &&
116
+ label.netId != null &&
117
+ inputProblem.availableNetLabelOrientations[label.netId]?.length ===
118
+ 1 &&
119
+ inputProblem.availableNetLabelOrientations[label.netId]?.[0] ===
120
+ label.orientation &&
121
+ (axis === "x"
122
+ ? label.orientation === "y+" || label.orientation === "y-"
123
+ : label.orientation === "x+" || label.orientation === "x-") &&
124
+ tracePathContainsPoint([a, b], label.anchorPoint),
125
+ )
126
+ .map(getAnchoredNetLabelRenderedBounds)
127
+ const blocking = [
128
+ ...candidateObstacles.filter((bounds) =>
129
+ segmentIntersectsRect(a, b, bounds),
130
+ ),
131
+ ...pendingReplacementBounds.filter((bounds) =>
132
+ attachedLabelBounds.some((labelBounds) =>
133
+ boundsOverlap(labelBounds, bounds),
134
+ ),
135
+ ),
136
+ ]
102
137
  if (!blocking.length) continue
103
138
  // Cleanup can split one rail across several trace records, including a
104
139
  // branch that starts at a junction instead of at its pin. Shift the
@@ -93,6 +93,8 @@ function sampleAnchorsAlongSegment(
93
93
  }
94
94
 
95
95
  export interface NetLabelNetLabelCollisionSolverParams {
96
+ /** Move temporary port-only inline labels before moving their neighbors. */
97
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions?: boolean
96
98
  /** Use the final horizontal tag envelope after inline conversion. */
97
99
  useRenderedLabelBounds?: boolean
98
100
  /** Already placed labels that are obstacles, but must not be moved. */
@@ -148,6 +150,8 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
148
150
  netLabelPlacements: this.netLabelPlacements,
149
151
  fixedNetLabelPlacements: this.params.fixedNetLabelPlacements,
150
152
  useRenderedLabelBounds: this.params.useRenderedLabelBounds,
153
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions:
154
+ this.params.preserveNonInlineLabelsAgainstInlineEligibleCollisions,
151
155
  }
152
156
  }
153
157
 
@@ -168,6 +172,50 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
168
172
  return [a.globalConnNetId, b.globalConnNetId].sort().join("::")
169
173
  }
170
174
 
175
+ private isSingleTerminalInlineEligible(label: NetLabelPlacement) {
176
+ if (!label.netId) return false
177
+ return [
178
+ ...this.inputProblem.netConnections,
179
+ ...this.inputProblem.directConnections,
180
+ ].some(
181
+ (connection) =>
182
+ connection.allowInlineNetLabel &&
183
+ connection.pinIds.length === 1 &&
184
+ connection.netId === label.netId &&
185
+ label.pinIds.every((pinId) => connection.pinIds.includes(pinId)),
186
+ )
187
+ }
188
+
189
+ private hasSatisfiedSingleOrientation(label: NetLabelPlacement) {
190
+ if (!label.netId) return false
191
+ const orientations =
192
+ this.inputProblem.availableNetLabelOrientations[label.netId]
193
+ return orientations?.length === 1 && orientations[0] === label.orientation
194
+ }
195
+
196
+ private isAlignedWithHostTrace(label: NetLabelPlacement) {
197
+ const orientationAxis = label.orientation.startsWith("x") ? "x" : "y"
198
+ return label.mspConnectionPairIds.some((mspPairId) => {
199
+ const trace = this.traceMap[mspPairId]
200
+ return trace?.tracePath.slice(1).some((end, index) => {
201
+ const start = trace.tracePath[index]!
202
+ if (!tracePathContainsPoint([start, end], label.anchorPoint))
203
+ return false
204
+ const segmentAxis =
205
+ Math.abs(start.x - end.x) < SEGMENT_PARALLEL_EPS ? "y" : "x"
206
+ return segmentAxis === orientationAxis
207
+ })
208
+ })
209
+ }
210
+
211
+ private isProtectedAgainstTemporaryInlineCollision(label: NetLabelPlacement) {
212
+ return (
213
+ !this.isSingleTerminalInlineEligible(label) &&
214
+ this.hasSatisfiedSingleOrientation(label) &&
215
+ this.isAlignedWithHostTrace(label)
216
+ )
217
+ }
218
+
171
219
  private findNextCollidingPair():
172
220
  | [NetLabelPlacement, NetLabelPlacement]
173
221
  | null {
@@ -394,6 +442,24 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
394
442
  this.labelsToTry = [pair[1], pair[0]].filter(
395
443
  (label) => !(this.params.fixedNetLabelPlacements ?? []).includes(label),
396
444
  )
445
+ if (
446
+ this.params.preserveNonInlineLabelsAgainstInlineEligibleCollisions &&
447
+ pair.some((label) => this.isSingleTerminalInlineEligible(label)) &&
448
+ pair.some((label) =>
449
+ this.isProtectedAgainstTemporaryInlineCollision(label),
450
+ )
451
+ ) {
452
+ this.labelsToTry = this.labelsToTry.filter(
453
+ (label) =>
454
+ this.isSingleTerminalInlineEligible(label) ||
455
+ !this.isProtectedAgainstTemporaryInlineCollision(label),
456
+ )
457
+ }
458
+ if (this.labelsToTry.length === 0) {
459
+ this.skippedCollisionKeys.add(this.collisionKey(pair[0], pair[1]))
460
+ this.clearActiveSearch()
461
+ return
462
+ }
397
463
  this.beginSearchForLabel(this.labelsToTry.shift()!)
398
464
  return
399
465
  }
@@ -8,6 +8,7 @@ import type { InputProblem } from "lib/types/InputProblem"
8
8
  import { getColorFromString } from "lib/utils/getColorFromString"
9
9
  import { alignSameNetJunctions } from "./alignSameNetJunctions"
10
10
  import { placeGroundRailLabelsAtOuterEnd } from "./placeGroundRailLabelsAtOuterEnd"
11
+ import { shortenSameSideRailToLabelAnchor } from "./shortenSameSideRailToLabelAnchor"
11
12
 
12
13
  interface SameNetJunctionAlignmentSolverInput {
13
14
  inputProblem: InputProblem
@@ -31,10 +32,13 @@ export class SameNetJunctionAlignmentSolver extends BaseSolver {
31
32
 
32
33
  override _step() {
33
34
  const alignment = alignSameNetJunctions(this.input)
34
- this.outputTraces = alignment.traces
35
+ this.outputTraces = shortenSameSideRailToLabelAnchor({
36
+ traces: alignment.traces,
37
+ netLabelPlacements: alignment.netLabelPlacements,
38
+ })
35
39
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
36
40
  inputProblem: this.input.inputProblem,
37
- traces: alignment.traces,
41
+ traces: this.outputTraces,
38
42
  netLabelPlacements: alignment.netLabelPlacements,
39
43
  })
40
44
  this.stats.alignedJunctionCount = alignment.alignedJunctionCount
@@ -0,0 +1,62 @@
1
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
2
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
3
+ import {
4
+ isHorizontal,
5
+ isVertical,
6
+ nearlyEqual,
7
+ } from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
8
+
9
+ const MAX_LOCAL_RAIL_SHIFT = 0.5
10
+ const MIN_ESCAPE_LENGTH = 1
11
+
12
+ export const shortenSameSideRailToLabelAnchor = ({
13
+ traces,
14
+ netLabelPlacements,
15
+ }: {
16
+ traces: SolvedTracePath[]
17
+ netLabelPlacements: NetLabelPlacement[]
18
+ }) =>
19
+ traces.map((trace) => {
20
+ const path = trace.tracePath
21
+ const firstPin = path[0]
22
+ const secondPin = path.at(-1)
23
+ if (!firstPin || !secondPin || !nearlyEqual(firstPin.x, secondPin.x)) {
24
+ return trace
25
+ }
26
+
27
+ const railIndex = path.findIndex(
28
+ (point, index) =>
29
+ index > 0 &&
30
+ index < path.length - 2 &&
31
+ isHorizontal(path[index - 1]!, point) &&
32
+ isVertical(point, path[index + 1]!) &&
33
+ isHorizontal(path[index + 1]!, path[index + 2]!),
34
+ )
35
+ if (railIndex < 0) return trace
36
+ const firstRail = path[railIndex]!
37
+ const secondRail = path[railIndex + 1]!
38
+ const escapeLength = Math.abs(firstRail.x - firstPin.x)
39
+ const label = netLabelPlacements.find(
40
+ ({ anchorPoint, orientation, mspConnectionPairIds }) =>
41
+ mspConnectionPairIds.includes(trace.mspPairId) &&
42
+ ((orientation === "y-" &&
43
+ anchorPoint.y < Math.min(firstPin.y, secondPin.y)) ||
44
+ (orientation === "y+" &&
45
+ anchorPoint.y > Math.max(firstPin.y, secondPin.y))) &&
46
+ anchorPoint.x >= Math.min(firstPin.x, firstRail.x) &&
47
+ anchorPoint.x <= Math.max(firstPin.x, firstRail.x) &&
48
+ Math.abs(anchorPoint.x - firstPin.x) < escapeLength &&
49
+ Math.abs(anchorPoint.x - firstRail.x) <= MAX_LOCAL_RAIL_SHIFT &&
50
+ escapeLength >= MIN_ESCAPE_LENGTH,
51
+ )
52
+ if (!label) return trace
53
+
54
+ return {
55
+ ...trace,
56
+ tracePath: path.map((point, index) =>
57
+ index === railIndex || index === railIndex + 1
58
+ ? { ...point, x: label.anchorPoint.x }
59
+ : point,
60
+ ),
61
+ }
62
+ })
@@ -559,6 +559,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
559
559
  inputProblem: instance.inputProblem,
560
560
  traces: simplificationOutput.traces,
561
561
  netLabelPlacements: simplificationOutput.netLabelPlacements,
562
+ preserveNonInlineLabelsAgainstInlineEligibleCollisions: true,
562
563
  },
563
564
  ]
564
565
  },
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.200",
8
+ "version": "0.0.202",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",