@tscircuit/schematic-trace-solver 0.0.101 → 0.0.102

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 (32) hide show
  1. package/dist/index.d.ts +13 -6
  2. package/dist/index.js +213 -64
  3. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +13 -5
  4. package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts +168 -24
  5. package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts +128 -48
  6. package/package.json +1 -1
  7. package/site/bug-reports/bug-report-20260716T144856Z.page.tsx +4 -0
  8. package/site/bug-reports/bug-report-20260717T022934Z.page.tsx +4 -0
  9. package/site/bug-reports/bug-report-20260717T031704Z.page.tsx +4 -0
  10. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +3 -3
  11. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +3 -3
  12. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +5 -5
  13. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +3 -3
  14. package/tests/bug-reports/bug-report-20260716T144856Z/__snapshots__/bug-report-20260716T144856Z.snap.svg +149 -0
  15. package/tests/bug-reports/bug-report-20260716T144856Z/bug-report-20260716T144856Z.json +693 -0
  16. package/tests/bug-reports/bug-report-20260716T144856Z/bug-report-20260716T144856Z.test.ts +12 -0
  17. package/tests/bug-reports/bug-report-20260717T022934Z/__snapshots__/bug-report-20260717T022934Z.snap.svg +130 -0
  18. package/tests/bug-reports/bug-report-20260717T022934Z/bug-report-20260717T022934Z.json +543 -0
  19. package/tests/bug-reports/bug-report-20260717T022934Z/bug-report-20260717T022934Z.test.ts +12 -0
  20. package/tests/bug-reports/bug-report-20260717T031704Z/__snapshots__/bug-report-20260717T031704Z.snap.svg +48 -0
  21. package/tests/bug-reports/bug-report-20260717T031704Z/bug-report-20260717T031704Z.json +77 -0
  22. package/tests/bug-reports/bug-report-20260717T031704Z/bug-report-20260717T031704Z.test.ts +12 -0
  23. package/tests/examples/__snapshots__/example11.snap.svg +19 -19
  24. package/tests/examples/__snapshots__/example19.snap.svg +3 -5
  25. package/tests/examples/__snapshots__/example29.snap.svg +13 -13
  26. package/tests/examples/__snapshots__/example37.snap.svg +4 -2
  27. package/tests/examples/example16.test.ts +22 -0
  28. package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +2 -8
  29. package/tests/repros/__snapshots__/repro47-endpoint-obstacle-detour.snap.svg +19 -19
  30. package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +10 -10
  31. package/tests/repros/repro47-endpoint-obstacle-detour.test.ts +30 -0
  32. package/tests/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.test.ts +174 -0
@@ -1,3 +1,4 @@
1
+ import { doSegmentsIntersect } from "@tscircuit/math-utils"
1
2
  import type { GraphicsObject } from "graphics-debug"
2
3
  import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
3
4
  import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
@@ -19,8 +20,11 @@ export interface OverlappingTraceSegmentLocator {
19
20
  }>
20
21
  }
21
22
 
23
+ export type TraceInteractionKind = "overlap" | "point_contact"
24
+
22
25
  export class TraceOverlapIssueSolver extends BaseSolver {
23
26
  overlappingTraceSegments: OverlappingTraceSegmentLocator[]
27
+ interactionKind: TraceInteractionKind
24
28
  traceNetIslands: Record<ConnNetId, Array<SolvedTracePath>>
25
29
  obstacleRects: ReturnType<typeof getObstacleRects>
26
30
 
@@ -31,10 +35,12 @@ export class TraceOverlapIssueSolver extends BaseSolver {
31
35
  constructor(params: {
32
36
  inputProblem: InputProblem
33
37
  overlappingTraceSegments: OverlappingTraceSegmentLocator[]
38
+ interactionKind: TraceInteractionKind
34
39
  traceNetIslands: Record<ConnNetId, Array<SolvedTracePath>>
35
40
  }) {
36
41
  super()
37
42
  this.overlappingTraceSegments = params.overlappingTraceSegments
43
+ this.interactionKind = params.interactionKind
38
44
  this.traceNetIslands = params.traceNetIslands
39
45
  this.obstacleRects = getObstacleRects(params.inputProblem)
40
46
 
@@ -76,17 +82,87 @@ export class TraceOverlapIssueSolver extends BaseSolver {
76
82
  (shouldStay) => !shouldStay,
77
83
  )
78
84
 
79
- // Compute offsets for each island involved: alternate directions
80
- const offsets = this.overlappingTraceSegments.map((group, idx) => {
81
- if (someGroupCanShift && groupShouldStayInPlace[idx]) return 0
82
- const n = Math.floor(idx / 2) + 1
83
- const signed = idx % 2 === 0 ? -n : n
84
- return this.getObstacleAwareOffset({
85
- group,
86
- offset: signed * this.SHIFT_DISTANCE,
85
+ const getSeparationOffsets = (firstDirection: number) =>
86
+ this.overlappingTraceSegments.map((group, groupIndex) => {
87
+ if (someGroupCanShift && groupShouldStayInPlace[groupIndex]) return 0
88
+ const n = Math.floor(groupIndex / 2) + 1
89
+ const direction =
90
+ groupIndex % 2 === 0 ? firstDirection : -firstDirection
91
+ return this.getObstacleAwareOffset({
92
+ group,
93
+ offset: direction * n * this.SHIFT_DISTANCE,
94
+ })
87
95
  })
96
+
97
+ const establishedOffsets = getSeparationOffsets(-1)
98
+ const isCompoundOverlap =
99
+ this.interactionKind === "overlap" &&
100
+ this.overlappingTraceSegments.some(
101
+ (group) => group.pathsWithOverlap.length > 1,
102
+ )
103
+
104
+ // A compound overlap moves multiple rails as one correction. Preserve the
105
+ // established deterministic separation because a single candidate score
106
+ // cannot attribute downstream intersections to one participating rail.
107
+ if (isCompoundOverlap) {
108
+ this.correctedTraceMap = this.applyOffsets(establishedOffsets)
109
+ this.solved = true
110
+ return
111
+ }
112
+
113
+ const offsetCandidates: number[][] =
114
+ this.interactionKind === "overlap"
115
+ ? [establishedOffsets, getSeparationOffsets(1)]
116
+ : []
117
+
118
+ if (this.interactionKind === "point_contact") {
119
+ // A point contact needs only one island to move. Try later islands first
120
+ // so an established earlier trace remains stable when scores are equal.
121
+ const movableGroupIndexes = this.overlappingTraceSegments
122
+ .map((_, groupIndex) => groupIndex)
123
+ .filter(
124
+ (groupIndex) =>
125
+ !someGroupCanShift || !groupShouldStayInPlace[groupIndex],
126
+ )
127
+ .reverse()
128
+ for (const groupIndex of movableGroupIndexes) {
129
+ const group = this.overlappingTraceSegments[groupIndex]!
130
+ for (const direction of [-1, 1]) {
131
+ const offsets = this.overlappingTraceSegments.map(() => 0)
132
+ offsets[groupIndex] = this.getObstacleAwareOffset({
133
+ group,
134
+ offset: direction * this.SHIFT_DISTANCE,
135
+ })
136
+ offsetCandidates.push(offsets)
137
+ }
138
+ }
139
+ }
140
+
141
+ const candidates = offsetCandidates.map((offsets) => {
142
+ const correctedTraceMap = this.applyOffsets(offsets)
143
+ return {
144
+ correctedTraceMap,
145
+ intersectionCount:
146
+ this.countDifferentNetIntersections(correctedTraceMap),
147
+ totalDisplacement: offsets.reduce(
148
+ (total, offset) => total + Math.abs(offset),
149
+ 0,
150
+ ),
151
+ }
88
152
  })
89
153
 
154
+ candidates.sort(
155
+ (a, b) =>
156
+ a.intersectionCount - b.intersectionCount ||
157
+ a.totalDisplacement - b.totalDisplacement,
158
+ )
159
+ this.correctedTraceMap = candidates[0]!.correctedTraceMap
160
+ this.solved = true
161
+ }
162
+
163
+ private applyOffsets(offsets: number[]) {
164
+ const correctedTraceMap = { ...this.correctedTraceMap }
165
+
90
166
  const eq = (a: number, b: number) => Math.abs(a - b) < EPS
91
167
  const samePoint = (
92
168
  p: { x: number; y: number } | undefined,
@@ -94,8 +170,8 @@ export class TraceOverlapIssueSolver extends BaseSolver {
94
170
  ) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y)
95
171
 
96
172
  // For each net island group, shift only its overlapping segments and adjust adjacent joints
97
- this.overlappingTraceSegments.forEach((group, gidx) => {
98
- const offset = offsets[gidx]!
173
+ this.overlappingTraceSegments.forEach((group, groupIndex) => {
174
+ const offset = offsets[groupIndex]!
99
175
  if (offset === 0) return
100
176
 
101
177
  // Gather unique segment indices per path
@@ -109,12 +185,25 @@ export class TraceOverlapIssueSolver extends BaseSolver {
109
185
 
110
186
  for (const [pathIdx, segIdxSet] of byPath) {
111
187
  const original = this.traceNetIslands[group.connNetId][pathIdx]!
112
- const current = this.correctedTraceMap[original.mspPairId] ?? original
188
+ const current = correctedTraceMap[original.mspPairId] ?? original
113
189
  const pts = current.tracePath.map((p) => ({ ...p }))
114
190
 
115
191
  const segIdxsRev = Array.from(segIdxSet)
116
192
  .sort((a, b) => a - b)
117
193
  .reverse()
194
+ const segmentAxes = new Map<number, "vertical" | "horizontal">()
195
+ for (const si of segIdxsRev) {
196
+ const start = pts[si]
197
+ const end = pts[si + 1]
198
+ if (!start || !end) continue
199
+ if (Math.abs(start.x - end.x) < EPS) {
200
+ segmentAxes.set(si, "vertical")
201
+ } else if (Math.abs(start.y - end.y) < EPS) {
202
+ segmentAxes.set(si, "horizontal")
203
+ }
204
+ }
205
+ const shiftedXPointIndexes = new Set<number>()
206
+ const shiftedYPointIndexes = new Set<number>()
118
207
 
119
208
  const JOG_SIZE = this.SHIFT_DISTANCE
120
209
 
@@ -134,17 +223,26 @@ export class TraceOverlapIssueSolver extends BaseSolver {
134
223
  // Internal segment - shift both points
135
224
  const start = pts[si]!
136
225
  const end = pts[si + 1]!
137
- const isVertical = Math.abs(start.x - end.x) < EPS
138
- const isHorizontal = Math.abs(start.y - end.y) < EPS
139
- if (!isVertical && !isHorizontal) continue
140
-
141
- if (isVertical) {
142
- start.x += offset
143
- end.x += offset
144
- } else {
145
- // Horizontal
146
- start.y += offset
147
- end.y += offset
226
+ const axis = segmentAxes.get(si)
227
+
228
+ if (axis === "vertical") {
229
+ if (!shiftedXPointIndexes.has(si)) {
230
+ start.x += offset
231
+ shiftedXPointIndexes.add(si)
232
+ }
233
+ if (!shiftedXPointIndexes.has(si + 1)) {
234
+ end.x += offset
235
+ shiftedXPointIndexes.add(si + 1)
236
+ }
237
+ } else if (axis === "horizontal") {
238
+ if (!shiftedYPointIndexes.has(si)) {
239
+ start.y += offset
240
+ shiftedYPointIndexes.add(si)
241
+ }
242
+ if (!shiftedYPointIndexes.has(si + 1)) {
243
+ end.y += offset
244
+ shiftedYPointIndexes.add(si + 1)
245
+ }
148
246
  }
149
247
  }
150
248
  }
@@ -160,14 +258,60 @@ export class TraceOverlapIssueSolver extends BaseSolver {
160
258
  }
161
259
  }
162
260
 
163
- this.correctedTraceMap[original.mspPairId] = {
261
+ correctedTraceMap[original.mspPairId] = {
164
262
  ...current,
165
263
  tracePath: cleaned,
166
264
  }
167
265
  }
168
266
  })
169
267
 
170
- this.solved = true
268
+ return correctedTraceMap
269
+ }
270
+
271
+ private countDifferentNetIntersections(
272
+ correctedTraceMap: Record<MspConnectionPairId, SolvedTracePath>,
273
+ ) {
274
+ const traces = Object.values(this.traceNetIslands).flatMap((island) =>
275
+ island.map((trace) => correctedTraceMap[trace.mspPairId] ?? trace),
276
+ )
277
+ let intersectionCount = 0
278
+
279
+ for (let traceIndex = 0; traceIndex < traces.length; traceIndex++) {
280
+ const trace = traces[traceIndex]!
281
+ for (
282
+ let otherTraceIndex = traceIndex + 1;
283
+ otherTraceIndex < traces.length;
284
+ otherTraceIndex++
285
+ ) {
286
+ const otherTrace = traces[otherTraceIndex]!
287
+ if (trace.globalConnNetId === otherTrace.globalConnNetId) continue
288
+
289
+ for (
290
+ let segmentIndex = 0;
291
+ segmentIndex < trace.tracePath.length - 1;
292
+ segmentIndex++
293
+ ) {
294
+ for (
295
+ let otherSegmentIndex = 0;
296
+ otherSegmentIndex < otherTrace.tracePath.length - 1;
297
+ otherSegmentIndex++
298
+ ) {
299
+ if (
300
+ doSegmentsIntersect(
301
+ trace.tracePath[segmentIndex]!,
302
+ trace.tracePath[segmentIndex + 1]!,
303
+ otherTrace.tracePath[otherSegmentIndex]!,
304
+ otherTrace.tracePath[otherSegmentIndex + 1]!,
305
+ )
306
+ ) {
307
+ intersectionCount++
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ return intersectionCount
171
315
  }
172
316
 
173
317
  private getObstacleAwareOffset({
@@ -6,20 +6,22 @@ import type { ConnectivityMap } from "connectivity-map"
6
6
  import {
7
7
  TraceOverlapIssueSolver,
8
8
  type OverlappingTraceSegmentLocator,
9
+ type TraceInteractionKind,
9
10
  } from "./TraceOverlapIssueSolver/TraceOverlapIssueSolver"
10
11
  import type { MspConnectionPairId } from "../MspConnectionPairSolver/MspConnectionPairSolver"
11
12
 
12
13
  type ConnNetId = string
13
14
 
14
15
  /**
15
- * This solver finds traces that overlap (coincident and parallel) and aren't
16
- * connected via the globalConnMap, then shifts them to avoid the overlap in
17
- * such a way that minimizes the resulting intersections
16
+ * This solver finds traces that overlap or meet collinearly and aren't
17
+ * connected via the globalConnMap, then shifts them apart in the direction
18
+ * that minimizes the resulting intersections.
18
19
  *
19
20
  * All traces are orthogonal, so for traces to be considered overlapping, they
20
- * need to each have a segment where both are horizontal or both are vertical
21
- * AND the segments must be within 1e-6 of each other in X (if vertical) or
22
- * Y (if horizontal)
21
+ * need to each have a segment where both are horizontal or both are vertical,
22
+ * the segments must be on the same axis, and their ranges must overlap. A
23
+ * point contact is also handled when both segments are internal rails, where
24
+ * it can be separated without moving an anchored terminal segment.
23
25
  *
24
26
  * Each iteration, we find overlapping traces that aren't part of the same net.
25
27
  * This is the same as finding two "trace net islands" that have an overlap.
@@ -89,6 +91,7 @@ export class TraceOverlapShiftSolver extends BaseSolver {
89
91
 
90
92
  findNextOverlapIssue(): {
91
93
  overlappingTraceSegments: Array<OverlappingTraceSegmentLocator>
94
+ interactionKind: TraceInteractionKind
92
95
  } | null {
93
96
  // Detect the next set of overlapping segments between two different net islands.
94
97
  const EPS = 2e-3
@@ -111,23 +114,89 @@ export class TraceOverlapShiftSolver extends BaseSolver {
111
114
  solvedTracePathIndex: number
112
115
  traceSegmentIndex: number
113
116
  }> = []
117
+ const pointContacts: Array<{
118
+ pathAIndex: number
119
+ segmentAIndex: number
120
+ pathBIndex: number
121
+ segmentBIndex: number
122
+ combinedLength: number
123
+ }> = []
114
124
 
115
125
  // Track to avoid duplicates
116
126
  const seenA = new Set<string>()
117
127
  const seenB = new Set<string>()
118
128
 
119
- const overlaps1D = (
129
+ const getRangeOverlap1D = (
120
130
  a1: number,
121
131
  a2: number,
122
132
  b1: number,
123
133
  b2: number,
124
- ): boolean => {
134
+ ) => {
125
135
  const minA = Math.min(a1, a2)
126
136
  const maxA = Math.max(a1, a2)
127
137
  const minB = Math.min(b1, b2)
128
138
  const maxB = Math.max(b1, b2)
129
- const overlap = Math.min(maxA, maxB) - Math.max(minA, minB)
130
- return overlap > EPS
139
+ return Math.min(maxA, maxB) - Math.max(minA, minB)
140
+ }
141
+
142
+ const recordCollinearInteraction = ({
143
+ pathAIndex,
144
+ segmentAIndex,
145
+ pathBIndex,
146
+ segmentBIndex,
147
+ rangeOverlap,
148
+ }: {
149
+ pathAIndex: number
150
+ segmentAIndex: number
151
+ pathBIndex: number
152
+ segmentBIndex: number
153
+ rangeOverlap: number
154
+ }) => {
155
+ if (rangeOverlap > EPS) {
156
+ const keyA = `${pathAIndex}:${segmentAIndex}`
157
+ const keyB = `${pathBIndex}:${segmentBIndex}`
158
+ if (!seenA.has(keyA)) {
159
+ overlapsA.push({
160
+ solvedTracePathIndex: pathAIndex,
161
+ traceSegmentIndex: segmentAIndex,
162
+ })
163
+ seenA.add(keyA)
164
+ }
165
+ if (!seenB.has(keyB)) {
166
+ overlapsB.push({
167
+ solvedTracePathIndex: pathBIndex,
168
+ traceSegmentIndex: segmentBIndex,
169
+ })
170
+ seenB.add(keyB)
171
+ }
172
+ return
173
+ }
174
+ if (rangeOverlap < -EPS) return
175
+
176
+ const pathA = pathsA[pathAIndex]!.tracePath
177
+ const pathB = pathsB[pathBIndex]!.tracePath
178
+ const segmentAStart = pathA[segmentAIndex]!
179
+ const segmentAEnd = pathA[segmentAIndex + 1]!
180
+ const segmentBStart = pathB[segmentBIndex]!
181
+ const segmentBEnd = pathB[segmentBIndex + 1]!
182
+ const hasTerminalSegment =
183
+ segmentAIndex === 0 ||
184
+ segmentAIndex === pathA.length - 2 ||
185
+ segmentBIndex === 0 ||
186
+ segmentBIndex === pathB.length - 2
187
+ if (hasTerminalSegment) return
188
+
189
+ pointContacts.push({
190
+ pathAIndex,
191
+ segmentAIndex,
192
+ pathBIndex,
193
+ segmentBIndex,
194
+ combinedLength:
195
+ Math.abs(segmentAStart.x - segmentAEnd.x) +
196
+ Math.abs(segmentAStart.y - segmentAEnd.y) +
197
+ Math.abs(segmentBStart.x - segmentBEnd.x) +
198
+ Math.abs(segmentBStart.y - segmentBEnd.y),
199
+ })
131
200
  }
132
201
 
133
202
  for (let pa = 0; pa < pathsA.length; pa++) {
@@ -150,48 +219,26 @@ export class TraceOverlapShiftSolver extends BaseSolver {
150
219
  const bHorz = Math.abs(b1.y - b2.y) < EPS
151
220
  if (!bVert && !bHorz) continue
152
221
 
153
- // Only consider colinear, parallel orientation overlaps
222
+ // Only consider collinear, parallel interactions.
154
223
  if (aVert && bVert) {
155
224
  if (Math.abs(a1.x - b1.x) < EPS) {
156
- if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
157
- const keyA = `${pa}:${sa}`
158
- const keyB = `${pb}:${sb}`
159
- if (!seenA.has(keyA)) {
160
- overlapsA.push({
161
- solvedTracePathIndex: pa,
162
- traceSegmentIndex: sa,
163
- })
164
- seenA.add(keyA)
165
- }
166
- if (!seenB.has(keyB)) {
167
- overlapsB.push({
168
- solvedTracePathIndex: pb,
169
- traceSegmentIndex: sb,
170
- })
171
- seenB.add(keyB)
172
- }
173
- }
225
+ recordCollinearInteraction({
226
+ pathAIndex: pa,
227
+ segmentAIndex: sa,
228
+ pathBIndex: pb,
229
+ segmentBIndex: sb,
230
+ rangeOverlap: getRangeOverlap1D(a1.y, a2.y, b1.y, b2.y),
231
+ })
174
232
  }
175
233
  } else if (aHorz && bHorz) {
176
234
  if (Math.abs(a1.y - b1.y) < EPS) {
177
- if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
178
- const keyA = `${pa}:${sa}`
179
- const keyB = `${pb}:${sb}`
180
- if (!seenA.has(keyA)) {
181
- overlapsA.push({
182
- solvedTracePathIndex: pa,
183
- traceSegmentIndex: sa,
184
- })
185
- seenA.add(keyA)
186
- }
187
- if (!seenB.has(keyB)) {
188
- overlapsB.push({
189
- solvedTracePathIndex: pb,
190
- traceSegmentIndex: sb,
191
- })
192
- seenB.add(keyB)
193
- }
194
- }
235
+ recordCollinearInteraction({
236
+ pathAIndex: pa,
237
+ segmentAIndex: sa,
238
+ pathBIndex: pb,
239
+ segmentBIndex: sb,
240
+ rangeOverlap: getRangeOverlap1D(a1.x, a2.x, b1.x, b2.x),
241
+ })
195
242
  }
196
243
  }
197
244
  }
@@ -201,12 +248,44 @@ export class TraceOverlapShiftSolver extends BaseSolver {
201
248
 
202
249
  if (overlapsA.length > 0 && overlapsB.length > 0) {
203
250
  return {
251
+ interactionKind: "overlap",
204
252
  overlappingTraceSegments: [
205
253
  { connNetId: netA, pathsWithOverlap: overlapsA },
206
254
  { connNetId: netB, pathsWithOverlap: overlapsB },
207
255
  ],
208
256
  }
209
257
  }
258
+
259
+ // An elbow can touch on both axes. Separating the longest internal
260
+ // rail changes one coordinate and avoids moving both sides of it.
261
+ const bestPointContact = pointContacts.sort(
262
+ (a, b) => b.combinedLength - a.combinedLength,
263
+ )[0]
264
+ if (bestPointContact) {
265
+ return {
266
+ interactionKind: "point_contact",
267
+ overlappingTraceSegments: [
268
+ {
269
+ connNetId: netA,
270
+ pathsWithOverlap: [
271
+ {
272
+ solvedTracePathIndex: bestPointContact.pathAIndex,
273
+ traceSegmentIndex: bestPointContact.segmentAIndex,
274
+ },
275
+ ],
276
+ },
277
+ {
278
+ connNetId: netB,
279
+ pathsWithOverlap: [
280
+ {
281
+ solvedTracePathIndex: bestPointContact.pathBIndex,
282
+ traceSegmentIndex: bestPointContact.segmentBIndex,
283
+ },
284
+ ],
285
+ },
286
+ ],
287
+ }
288
+ }
210
289
  }
211
290
  }
212
291
 
@@ -306,10 +385,11 @@ export class TraceOverlapShiftSolver extends BaseSolver {
306
385
  return
307
386
  }
308
387
 
309
- const { overlappingTraceSegments } = overlapIssue
388
+ const { interactionKind, overlappingTraceSegments } = overlapIssue
310
389
 
311
390
  this.activeSubSolver = new TraceOverlapIssueSolver({
312
391
  inputProblem: this.inputProblem,
392
+ interactionKind,
313
393
  overlappingTraceSegments,
314
394
  traceNetIslands: this.traceNetIslands,
315
395
  })
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.101",
4
+ "version": "0.0.102",
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-20260716T144856Z/bug-report-20260716T144856Z.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />
@@ -0,0 +1,4 @@
1
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
2
+ import inputProblem from "../../tests/bug-reports/bug-report-20260717T022934Z/bug-report-20260717T022934Z.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />
@@ -0,0 +1,4 @@
1
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
2
+ import inputProblem from "../../tests/bug-reports/bug-report-20260717T031704Z/bug-report-20260717T031704Z.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />