@tscircuit/schematic-trace-solver 0.0.169 → 0.0.171

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.
@@ -5,16 +5,17 @@ import {
5
5
  } from "@tscircuit/math-utils"
6
6
  import type { GraphicsObject } from "graphics-debug"
7
7
  import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
8
- import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
9
8
  import { doesPairCrossRestrictedCenterLines } from "lib/solvers/MspConnectionPairSolver/doesPairCrossRestrictedCenterLines"
9
+ import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
10
10
  import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
11
+ import { doesTraceRecoveryPathConflict } from "lib/solvers/NetLabelTraceRecovery/doesTraceRecoveryPathConflict"
11
12
  import {
12
13
  getTraceRecoveryConnectivityMaps,
13
14
  type TraceRecoveryPin,
14
15
  } from "lib/solvers/NetLabelTraceRecovery/getTraceRecoveryConnectivityMaps"
15
- import { doesTraceRecoveryPathConflict } from "lib/solvers/NetLabelTraceRecovery/doesTraceRecoveryPathConflict"
16
16
  import { getTraceConnectedPinComponents } from "lib/solvers/SchematicTraceLinesSolver/getTraceConnectedPinComponents"
17
17
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
18
+ import { findFirstCollision } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
18
19
  import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
19
20
  import type {
20
21
  ChipId,
@@ -24,10 +25,11 @@ import type {
24
25
  } from "lib/types/InputProblem"
25
26
  import { arePinsInDifferentSchematicSections } from "lib/utils/arePinsInDifferentSchematicSections"
26
27
  import {
27
- type InlineNetLabelPlacement,
28
28
  type InlineNetLabelOutput,
29
+ type InlineNetLabelPlacement,
29
30
  visualizeInlineNetLabelOutput,
30
31
  } from "../InlineNetLabelSolver/InlineNetLabelSolver"
32
+ import { reduceTraceCrossings } from "./reduceTraceCrossings"
31
33
 
32
34
  type GlobalConnNetId = NetLabelPlacement["globalConnNetId"]
33
35
 
@@ -420,7 +422,7 @@ export class NetLabelToTraceSolver extends BaseSolver {
420
422
 
421
423
  private tryAcceptCurrentRoute() {
422
424
  const candidate = this.currentCandidate
423
- const tracePath = this.activeSubSolver?.solvedTracePath
425
+ let tracePath = this.activeSubSolver?.solvedTracePath
424
426
  if (!candidate || !tracePath) return
425
427
 
426
428
  const retainedTraces = this.outputTraces.filter(
@@ -440,6 +442,17 @@ export class NetLabelToTraceSolver extends BaseSolver {
440
442
  return
441
443
  }
442
444
 
445
+ tracePath = reduceTraceCrossings({
446
+ tracePath,
447
+ globalConnNetId: candidate.firstLabel.globalConnNetId,
448
+ otherTraces: collisionTraces,
449
+ isCandidateValid: (candidatePath) =>
450
+ findFirstCollision(candidatePath, this.activeSubSolver!.obstacles) ===
451
+ null &&
452
+ !doesTraceRecoveryPathConflict(candidatePath, collisionTraces) &&
453
+ !this.routeIntersectsRemainingLabels(candidatePath, candidate),
454
+ })
455
+
443
456
  const [firstPin, secondPin] = candidate.pins
444
457
  const mspPairId = `${RECOVERED_TRACE_PREFIX}${candidate.key}`
445
458
  const recoveredTrace: SolvedTracePath = {
@@ -0,0 +1,180 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import {
3
+ countPathIntersections,
4
+ getPathLength,
5
+ segmentsIntersect,
6
+ } from "lib/solvers/Example28Solver/geometry"
7
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
8
+
9
+ const EPS = 1e-9
10
+
11
+ const isHorizontal = (start: Point, end: Point) =>
12
+ Math.abs(start.y - end.y) < EPS
13
+
14
+ const isVertical = (start: Point, end: Point) => Math.abs(start.x - end.x) < EPS
15
+
16
+ const compactConsecutivePoints = (path: Point[]) =>
17
+ path.filter((point, index) => {
18
+ const previousPoint = path[index - 1]
19
+ return (
20
+ !previousPoint ||
21
+ Math.abs(point.x - previousPoint.x) >= EPS ||
22
+ Math.abs(point.y - previousPoint.y) >= EPS
23
+ )
24
+ })
25
+
26
+ const isValidOrthogonalPath = (path: Point[]) => {
27
+ if (
28
+ path.length < 2 ||
29
+ !path.every((point, index) => {
30
+ const nextPoint = path[index + 1]
31
+ if (!nextPoint) return true
32
+ return isHorizontal(point, nextPoint) || isVertical(point, nextPoint)
33
+ })
34
+ ) {
35
+ return false
36
+ }
37
+
38
+ for (
39
+ let firstSegmentIndex = 0;
40
+ firstSegmentIndex < path.length - 1;
41
+ firstSegmentIndex++
42
+ ) {
43
+ for (
44
+ let secondSegmentIndex = firstSegmentIndex + 2;
45
+ secondSegmentIndex < path.length - 1;
46
+ secondSegmentIndex++
47
+ ) {
48
+ if (
49
+ segmentsIntersect(
50
+ path[firstSegmentIndex]!,
51
+ path[firstSegmentIndex + 1]!,
52
+ path[secondSegmentIndex]!,
53
+ path[secondSegmentIndex + 1]!,
54
+ )
55
+ ) {
56
+ return false
57
+ }
58
+ }
59
+ }
60
+
61
+ return true
62
+ }
63
+
64
+ const getEndpointAlignedSegmentCandidates = (tracePath: Point[]) => {
65
+ const candidates: Point[][] = []
66
+
67
+ // Preserve the terminal segment at each pin. For an interior segment whose
68
+ // neighboring segments are perpendicular, align it with either neighboring
69
+ // endpoint corridor. This keeps the same orthogonal topology while allowing
70
+ // a midpoint elbow to move away from avoidable trace crossings.
71
+ for (
72
+ let segmentIndex = 2;
73
+ segmentIndex <= tracePath.length - 4;
74
+ segmentIndex++
75
+ ) {
76
+ const previousPoint = tracePath[segmentIndex - 1]!
77
+ const start = tracePath[segmentIndex]!
78
+ const end = tracePath[segmentIndex + 1]!
79
+ const nextPoint = tracePath[segmentIndex + 2]!
80
+ const segmentIsVertical = isVertical(start, end)
81
+ const segmentIsHorizontal = isHorizontal(start, end)
82
+
83
+ if (
84
+ (!segmentIsVertical && !segmentIsHorizontal) ||
85
+ (segmentIsVertical &&
86
+ (!isHorizontal(previousPoint, start) ||
87
+ !isHorizontal(end, nextPoint))) ||
88
+ (segmentIsHorizontal &&
89
+ (!isVertical(previousPoint, start) || !isVertical(end, nextPoint)))
90
+ ) {
91
+ continue
92
+ }
93
+
94
+ const alignedCoordinates = segmentIsVertical
95
+ ? [previousPoint.x, nextPoint.x]
96
+ : [previousPoint.y, nextPoint.y]
97
+ for (const alignedCoordinate of alignedCoordinates) {
98
+ if (
99
+ Math.abs(alignedCoordinate - (segmentIsVertical ? start.x : start.y)) <
100
+ EPS
101
+ ) {
102
+ continue
103
+ }
104
+
105
+ const candidate = tracePath.map((point) => ({ ...point }))
106
+ if (segmentIsVertical) {
107
+ candidate[segmentIndex]!.x = alignedCoordinate
108
+ candidate[segmentIndex + 1]!.x = alignedCoordinate
109
+ } else {
110
+ candidate[segmentIndex]!.y = alignedCoordinate
111
+ candidate[segmentIndex + 1]!.y = alignedCoordinate
112
+ }
113
+
114
+ const compactedCandidate = compactConsecutivePoints(candidate)
115
+ if (isValidOrthogonalPath(compactedCandidate)) {
116
+ candidates.push(compactedCandidate)
117
+ }
118
+ }
119
+ }
120
+
121
+ return candidates
122
+ }
123
+
124
+ const countOtherNetCrossings = ({
125
+ tracePath,
126
+ globalConnNetId,
127
+ otherTraces,
128
+ }: {
129
+ tracePath: Point[]
130
+ globalConnNetId: string
131
+ otherTraces: SolvedTracePath[]
132
+ }) =>
133
+ otherTraces.reduce(
134
+ (crossingCount, otherTrace) =>
135
+ crossingCount +
136
+ (otherTrace.globalConnNetId === globalConnNetId
137
+ ? 0
138
+ : countPathIntersections(tracePath, otherTrace.tracePath)),
139
+ 0,
140
+ )
141
+
142
+ export const reduceTraceCrossings = ({
143
+ tracePath,
144
+ globalConnNetId,
145
+ otherTraces,
146
+ isCandidateValid,
147
+ }: {
148
+ tracePath: Point[]
149
+ globalConnNetId: string
150
+ otherTraces: SolvedTracePath[]
151
+ isCandidateValid: (candidate: Point[]) => boolean
152
+ }) => {
153
+ let bestPath = tracePath
154
+ let bestCrossingCount = countOtherNetCrossings({
155
+ tracePath,
156
+ globalConnNetId,
157
+ otherTraces,
158
+ })
159
+ let bestPathLength = getPathLength(tracePath)
160
+
161
+ for (const candidate of getEndpointAlignedSegmentCandidates(tracePath)) {
162
+ if (!isCandidateValid(candidate)) continue
163
+ const crossingCount = countOtherNetCrossings({
164
+ tracePath: candidate,
165
+ globalConnNetId,
166
+ otherTraces,
167
+ })
168
+ const pathLength = getPathLength(candidate)
169
+ if (
170
+ crossingCount < bestCrossingCount ||
171
+ (crossingCount === bestCrossingCount && pathLength < bestPathLength)
172
+ ) {
173
+ bestPath = candidate
174
+ bestCrossingCount = crossingCount
175
+ bestPathLength = pathLength
176
+ }
177
+ }
178
+
179
+ return bestPath
180
+ }
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.169",
8
+ "version": "0.0.171",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",