@tscircuit/schematic-trace-solver 0.0.165 → 0.0.166

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.js CHANGED
@@ -13009,6 +13009,214 @@ var alignSameNetJunctions = ({
13009
13009
  };
13010
13010
  };
13011
13011
 
13012
+ // lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts
13013
+ import { getSegmentIntersection as getSegmentIntersection4 } from "@tscircuit/math-utils/line-intersections";
13014
+ var TRACE_LENGTH_EPSILON = 1e-6;
13015
+ var getTracePathStartingAtPin = (trace, pin) => {
13016
+ const pathStart = trace.tracePath[0];
13017
+ const pathEnd = trace.tracePath.at(-1);
13018
+ if (!pathStart || !pathEnd) return null;
13019
+ if (pointsEqual(pathStart, pin)) return trace.tracePath;
13020
+ if (pointsEqual(pathEnd, pin)) return [...trace.tracePath].reverse();
13021
+ return null;
13022
+ };
13023
+ var getPerpendicularPathCrossings = (targetPath, donorPath) => {
13024
+ const crossings = [];
13025
+ const sharedEndpoint = targetPath[0];
13026
+ for (let targetSegmentIndex = 0; targetSegmentIndex < targetPath.length - 1; targetSegmentIndex++) {
13027
+ const targetStart = targetPath[targetSegmentIndex];
13028
+ const targetEnd = targetPath[targetSegmentIndex + 1];
13029
+ const targetOrientation = getRailOrientation(targetStart, targetEnd);
13030
+ if (!targetOrientation) continue;
13031
+ for (let donorSegmentIndex = 0; donorSegmentIndex < donorPath.length - 1; donorSegmentIndex++) {
13032
+ const donorStart = donorPath[donorSegmentIndex];
13033
+ const donorEnd = donorPath[donorSegmentIndex + 1];
13034
+ const donorOrientation = getRailOrientation(donorStart, donorEnd);
13035
+ if (!donorOrientation || donorOrientation === targetOrientation) continue;
13036
+ const intersectionPoint = getSegmentIntersection4(
13037
+ targetStart,
13038
+ targetEnd,
13039
+ donorStart,
13040
+ donorEnd
13041
+ );
13042
+ if (!intersectionPoint || pointsEqual(intersectionPoint, sharedEndpoint)) {
13043
+ continue;
13044
+ }
13045
+ crossings.push({
13046
+ intersectionPoint,
13047
+ targetSegmentIndex,
13048
+ donorSegmentIndex
13049
+ });
13050
+ }
13051
+ }
13052
+ return crossings;
13053
+ };
13054
+ var buildCollapsedCyclePath = ({
13055
+ targetTrace,
13056
+ targetPath,
13057
+ donorPath,
13058
+ crossing
13059
+ }) => {
13060
+ const pathFromSharedPin = simplifyPath([
13061
+ ...donorPath.slice(0, crossing.donorSegmentIndex + 1),
13062
+ crossing.intersectionPoint,
13063
+ ...targetPath.slice(crossing.targetSegmentIndex + 1)
13064
+ ]);
13065
+ if (pointsEqual(targetTrace.tracePath[0], targetPath[0])) {
13066
+ return pathFromSharedPin;
13067
+ }
13068
+ return pathFromSharedPin.reverse();
13069
+ };
13070
+ var getNetLabelPlacementsForCycleCollapse = ({
13071
+ targetTrace,
13072
+ tracePath,
13073
+ netLabelPlacements
13074
+ }) => {
13075
+ const candidateNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
13076
+ trace: targetTrace,
13077
+ originalTracePath: targetTrace.tracePath,
13078
+ reroutedTracePath: tracePath,
13079
+ netLabelPlacements
13080
+ });
13081
+ for (let labelIndex = 0; labelIndex < netLabelPlacements.length; labelIndex++) {
13082
+ const label = netLabelPlacements[labelIndex];
13083
+ if (label.globalConnNetId !== targetTrace.globalConnNetId) continue;
13084
+ if (!tracePathContainsPoint(targetTrace.tracePath, label.anchorPoint)) {
13085
+ continue;
13086
+ }
13087
+ const candidateLabel = candidateNetLabelPlacements[labelIndex];
13088
+ if (!tracePathContainsPoint(tracePath, candidateLabel.anchorPoint)) {
13089
+ return null;
13090
+ }
13091
+ }
13092
+ const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
13093
+ (label) => label.globalConnNetId !== targetTrace.globalConnNetId
13094
+ );
13095
+ if (pathIntersectsAnyNetLabel({
13096
+ path: tracePath,
13097
+ netLabelPlacements: otherNetLabelPlacements
13098
+ })) {
13099
+ return null;
13100
+ }
13101
+ const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
13102
+ (label) => label.globalConnNetId === targetTrace.globalConnNetId
13103
+ );
13104
+ if (pathEntersAnyNetLabel({
13105
+ path: tracePath,
13106
+ netLabelPlacements: sameNetLabelPlacements
13107
+ })) {
13108
+ return null;
13109
+ }
13110
+ return candidateNetLabelPlacements;
13111
+ };
13112
+ var candidateIsBetter = (candidate, bestCandidate) => {
13113
+ if (!bestCandidate) return true;
13114
+ const visibleLengthDelta = candidate.netVisibleLength - bestCandidate.netVisibleLength;
13115
+ if (Math.abs(visibleLengthDelta) > TRACE_LENGTH_EPSILON) {
13116
+ return visibleLengthDelta < 0;
13117
+ }
13118
+ return candidate.netVisibleSegmentCount < bestCandidate.netVisibleSegmentCount;
13119
+ };
13120
+ var getBestCycleCollapseCandidate = ({
13121
+ targetTrace,
13122
+ traces,
13123
+ netLabelPlacements
13124
+ }) => {
13125
+ const sameNetTraces = traces.filter(
13126
+ (trace) => trace.globalConnNetId === targetTrace.globalConnNetId
13127
+ );
13128
+ const baselineNetVisibleLength = getVisibleTraceLength(sameNetTraces);
13129
+ const baselineTargetVisibleLength = getVisibleTraceLength([targetTrace]);
13130
+ let bestCandidate = null;
13131
+ for (const donorTrace of sameNetTraces) {
13132
+ if (donorTrace.mspPairId === targetTrace.mspPairId) continue;
13133
+ const sharedPin = getSharedPin({
13134
+ donorTrace,
13135
+ branchTrace: targetTrace
13136
+ });
13137
+ if (!sharedPin) continue;
13138
+ const targetPath = getTracePathStartingAtPin(targetTrace, sharedPin);
13139
+ const donorPath = getTracePathStartingAtPin(donorTrace, sharedPin);
13140
+ if (!targetPath || !donorPath) continue;
13141
+ const crossings = getPerpendicularPathCrossings(targetPath, donorPath);
13142
+ for (const crossing of crossings) {
13143
+ const tracePath = buildCollapsedCyclePath({
13144
+ targetTrace,
13145
+ targetPath,
13146
+ donorPath,
13147
+ crossing
13148
+ });
13149
+ const candidateTrace = { ...targetTrace, tracePath };
13150
+ const candidateTargetVisibleLength = getVisibleTraceLength([
13151
+ candidateTrace
13152
+ ]);
13153
+ if (candidateTargetVisibleLength > baselineTargetVisibleLength + TRACE_LENGTH_EPSILON) {
13154
+ continue;
13155
+ }
13156
+ const candidateNetLabelPlacements = getNetLabelPlacementsForCycleCollapse(
13157
+ {
13158
+ targetTrace,
13159
+ tracePath,
13160
+ netLabelPlacements
13161
+ }
13162
+ );
13163
+ if (!candidateNetLabelPlacements) continue;
13164
+ const candidateNetTraces = sameNetTraces.map((trace) => {
13165
+ if (trace.mspPairId === targetTrace.mspPairId) return candidateTrace;
13166
+ return trace;
13167
+ });
13168
+ const netVisibleLength = getVisibleTraceLength(candidateNetTraces);
13169
+ if (netVisibleLength >= baselineNetVisibleLength - TRACE_LENGTH_EPSILON) {
13170
+ continue;
13171
+ }
13172
+ const netVisibleSegmentCount = getVisibleTraceSegmentCount(candidateNetTraces);
13173
+ const candidate = {
13174
+ tracePath,
13175
+ netLabelPlacements: candidateNetLabelPlacements,
13176
+ netVisibleLength,
13177
+ netVisibleSegmentCount
13178
+ };
13179
+ if (candidateIsBetter(candidate, bestCandidate)) {
13180
+ bestCandidate = candidate;
13181
+ }
13182
+ }
13183
+ }
13184
+ return bestCandidate;
13185
+ };
13186
+ var collapseSameNetCycles = ({
13187
+ traces,
13188
+ netLabelPlacements
13189
+ }) => {
13190
+ const outputTraces = [...traces];
13191
+ let outputNetLabelPlacements = [...netLabelPlacements];
13192
+ let collapsedCycleCount = 0;
13193
+ let traceIndex = 0;
13194
+ while (traceIndex < outputTraces.length) {
13195
+ const targetTrace = outputTraces[traceIndex];
13196
+ const candidate = getBestCycleCollapseCandidate({
13197
+ targetTrace,
13198
+ traces: outputTraces,
13199
+ netLabelPlacements: outputNetLabelPlacements
13200
+ });
13201
+ if (!candidate) {
13202
+ traceIndex++;
13203
+ continue;
13204
+ }
13205
+ outputTraces[traceIndex] = {
13206
+ ...targetTrace,
13207
+ tracePath: candidate.tracePath
13208
+ };
13209
+ outputNetLabelPlacements = candidate.netLabelPlacements;
13210
+ collapsedCycleCount++;
13211
+ traceIndex = 0;
13212
+ }
13213
+ return {
13214
+ traces: outputTraces,
13215
+ netLabelPlacements: outputNetLabelPlacements,
13216
+ collapsedCycleCount
13217
+ };
13218
+ };
13219
+
13012
13220
  // lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts
13013
13221
  var placeGroundRailLabelsAtOuterEnd = ({
13014
13222
  inputProblem,
@@ -13090,14 +13298,19 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
13090
13298
  this.outputNetLabelPlacements = input.netLabelPlacements;
13091
13299
  }
13092
13300
  _step() {
13093
- const result = alignSameNetJunctions(this.input);
13094
- this.outputTraces = result.traces;
13301
+ const alignment = alignSameNetJunctions(this.input);
13302
+ const cycleCollapse = collapseSameNetCycles({
13303
+ traces: alignment.traces,
13304
+ netLabelPlacements: alignment.netLabelPlacements
13305
+ });
13306
+ this.outputTraces = cycleCollapse.traces;
13095
13307
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
13096
13308
  inputProblem: this.input.inputProblem,
13097
- traces: result.traces,
13098
- netLabelPlacements: result.netLabelPlacements
13309
+ traces: cycleCollapse.traces,
13310
+ netLabelPlacements: cycleCollapse.netLabelPlacements
13099
13311
  });
13100
- this.stats.alignedJunctionCount = result.alignedJunctionCount;
13312
+ this.stats.alignedJunctionCount = alignment.alignedJunctionCount;
13313
+ this.stats.collapsedCycleCount = cycleCollapse.collapsedCycleCount;
13101
13314
  this.solved = true;
13102
13315
  }
13103
13316
  getOutput() {
@@ -6,6 +6,7 @@ import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/
6
6
  import type { InputProblem } from "lib/types/InputProblem"
7
7
  import { getColorFromString } from "lib/utils/getColorFromString"
8
8
  import { alignSameNetJunctions } from "./alignSameNetJunctions"
9
+ import { collapseSameNetCycles } from "./collapseSameNetCycles"
9
10
  import { placeGroundRailLabelsAtOuterEnd } from "./placeGroundRailLabelsAtOuterEnd"
10
11
 
11
12
  interface SameNetJunctionAlignmentSolverInput {
@@ -28,14 +29,19 @@ export class SameNetJunctionAlignmentSolver extends BaseSolver {
28
29
  }
29
30
 
30
31
  override _step() {
31
- const result = alignSameNetJunctions(this.input)
32
- this.outputTraces = result.traces
32
+ const alignment = alignSameNetJunctions(this.input)
33
+ const cycleCollapse = collapseSameNetCycles({
34
+ traces: alignment.traces,
35
+ netLabelPlacements: alignment.netLabelPlacements,
36
+ })
37
+ this.outputTraces = cycleCollapse.traces
33
38
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
34
39
  inputProblem: this.input.inputProblem,
35
- traces: result.traces,
36
- netLabelPlacements: result.netLabelPlacements,
40
+ traces: cycleCollapse.traces,
41
+ netLabelPlacements: cycleCollapse.netLabelPlacements,
37
42
  })
38
- this.stats.alignedJunctionCount = result.alignedJunctionCount
43
+ this.stats.alignedJunctionCount = alignment.alignedJunctionCount
44
+ this.stats.collapsedCycleCount = cycleCollapse.collapsedCycleCount
39
45
  this.solved = true
40
46
  }
41
47
 
@@ -41,7 +41,7 @@ const MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2
41
41
  const MAX_SHARED_PIN_RAIL_OFFSET = 0.05
42
42
  const MIN_RETURN_STEM_LENGTH = 0.05
43
43
 
44
- const getSharedPin = ({
44
+ export const getSharedPin = ({
45
45
  donorTrace,
46
46
  branchTrace,
47
47
  }: {
@@ -0,0 +1,310 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections"
3
+ import { moveAttachedLabelsToReroutedTrace } from "lib/solvers/Example28Solver/labelMovement"
4
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
5
+ import { tracePathContainsPoint } from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
6
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
7
+ import {
8
+ getRailOrientation,
9
+ getVisibleTraceLength,
10
+ getVisibleTraceSegmentCount,
11
+ pointsEqual,
12
+ } from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
13
+ import { simplifyPath } from "lib/solvers/TraceCleanupSolver/simplifyPath"
14
+ import { getSharedPin } from "./alignSameNetJunctions"
15
+ import {
16
+ pathEntersAnyNetLabel,
17
+ pathIntersectsAnyNetLabel,
18
+ } from "./pathIntersectsAnyNetLabel"
19
+
20
+ interface CollapseSameNetCyclesInput {
21
+ traces: SolvedTracePath[]
22
+ netLabelPlacements: NetLabelPlacement[]
23
+ }
24
+
25
+ interface PathCrossing {
26
+ intersectionPoint: Point
27
+ targetSegmentIndex: number
28
+ donorSegmentIndex: number
29
+ }
30
+
31
+ interface CycleCollapseCandidate {
32
+ tracePath: Point[]
33
+ netLabelPlacements: NetLabelPlacement[]
34
+ netVisibleLength: number
35
+ netVisibleSegmentCount: number
36
+ }
37
+
38
+ const TRACE_LENGTH_EPSILON = 1e-6
39
+
40
+ const getTracePathStartingAtPin = (
41
+ trace: SolvedTracePath,
42
+ pin: Point,
43
+ ): Point[] | null => {
44
+ const pathStart = trace.tracePath[0]
45
+ const pathEnd = trace.tracePath.at(-1)
46
+ if (!pathStart || !pathEnd) return null
47
+ if (pointsEqual(pathStart, pin)) return trace.tracePath
48
+ if (pointsEqual(pathEnd, pin)) return [...trace.tracePath].reverse()
49
+ return null
50
+ }
51
+
52
+ const getPerpendicularPathCrossings = (
53
+ targetPath: Point[],
54
+ donorPath: Point[],
55
+ ): PathCrossing[] => {
56
+ const crossings: PathCrossing[] = []
57
+ const sharedEndpoint = targetPath[0]!
58
+
59
+ for (
60
+ let targetSegmentIndex = 0;
61
+ targetSegmentIndex < targetPath.length - 1;
62
+ targetSegmentIndex++
63
+ ) {
64
+ const targetStart = targetPath[targetSegmentIndex]!
65
+ const targetEnd = targetPath[targetSegmentIndex + 1]!
66
+ const targetOrientation = getRailOrientation(targetStart, targetEnd)
67
+ if (!targetOrientation) continue
68
+
69
+ for (
70
+ let donorSegmentIndex = 0;
71
+ donorSegmentIndex < donorPath.length - 1;
72
+ donorSegmentIndex++
73
+ ) {
74
+ const donorStart = donorPath[donorSegmentIndex]!
75
+ const donorEnd = donorPath[donorSegmentIndex + 1]!
76
+ const donorOrientation = getRailOrientation(donorStart, donorEnd)
77
+ if (!donorOrientation || donorOrientation === targetOrientation) continue
78
+
79
+ const intersectionPoint = getSegmentIntersection(
80
+ targetStart,
81
+ targetEnd,
82
+ donorStart,
83
+ donorEnd,
84
+ )
85
+ if (
86
+ !intersectionPoint ||
87
+ pointsEqual(intersectionPoint, sharedEndpoint)
88
+ ) {
89
+ continue
90
+ }
91
+
92
+ crossings.push({
93
+ intersectionPoint,
94
+ targetSegmentIndex,
95
+ donorSegmentIndex,
96
+ })
97
+ }
98
+ }
99
+
100
+ return crossings
101
+ }
102
+
103
+ const buildCollapsedCyclePath = ({
104
+ targetTrace,
105
+ targetPath,
106
+ donorPath,
107
+ crossing,
108
+ }: {
109
+ targetTrace: SolvedTracePath
110
+ targetPath: Point[]
111
+ donorPath: Point[]
112
+ crossing: PathCrossing
113
+ }) => {
114
+ const pathFromSharedPin = simplifyPath([
115
+ ...donorPath.slice(0, crossing.donorSegmentIndex + 1),
116
+ crossing.intersectionPoint,
117
+ ...targetPath.slice(crossing.targetSegmentIndex + 1),
118
+ ])
119
+ if (pointsEqual(targetTrace.tracePath[0]!, targetPath[0]!)) {
120
+ return pathFromSharedPin
121
+ }
122
+ return pathFromSharedPin.reverse()
123
+ }
124
+
125
+ const getNetLabelPlacementsForCycleCollapse = ({
126
+ targetTrace,
127
+ tracePath,
128
+ netLabelPlacements,
129
+ }: {
130
+ targetTrace: SolvedTracePath
131
+ tracePath: Point[]
132
+ netLabelPlacements: NetLabelPlacement[]
133
+ }): NetLabelPlacement[] | null => {
134
+ const candidateNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
135
+ trace: targetTrace,
136
+ originalTracePath: targetTrace.tracePath,
137
+ reroutedTracePath: tracePath,
138
+ netLabelPlacements,
139
+ })
140
+
141
+ for (
142
+ let labelIndex = 0;
143
+ labelIndex < netLabelPlacements.length;
144
+ labelIndex++
145
+ ) {
146
+ const label = netLabelPlacements[labelIndex]!
147
+ if (label.globalConnNetId !== targetTrace.globalConnNetId) continue
148
+ if (!tracePathContainsPoint(targetTrace.tracePath, label.anchorPoint)) {
149
+ continue
150
+ }
151
+ const candidateLabel = candidateNetLabelPlacements[labelIndex]!
152
+ if (!tracePathContainsPoint(tracePath, candidateLabel.anchorPoint)) {
153
+ return null
154
+ }
155
+ }
156
+
157
+ const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
158
+ (label) => label.globalConnNetId !== targetTrace.globalConnNetId,
159
+ )
160
+ if (
161
+ pathIntersectsAnyNetLabel({
162
+ path: tracePath,
163
+ netLabelPlacements: otherNetLabelPlacements,
164
+ })
165
+ ) {
166
+ return null
167
+ }
168
+
169
+ const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
170
+ (label) => label.globalConnNetId === targetTrace.globalConnNetId,
171
+ )
172
+ if (
173
+ pathEntersAnyNetLabel({
174
+ path: tracePath,
175
+ netLabelPlacements: sameNetLabelPlacements,
176
+ })
177
+ ) {
178
+ return null
179
+ }
180
+ return candidateNetLabelPlacements
181
+ }
182
+
183
+ const candidateIsBetter = (
184
+ candidate: CycleCollapseCandidate,
185
+ bestCandidate: CycleCollapseCandidate | null,
186
+ ) => {
187
+ if (!bestCandidate) return true
188
+ const visibleLengthDelta =
189
+ candidate.netVisibleLength - bestCandidate.netVisibleLength
190
+ if (Math.abs(visibleLengthDelta) > TRACE_LENGTH_EPSILON) {
191
+ return visibleLengthDelta < 0
192
+ }
193
+ return candidate.netVisibleSegmentCount < bestCandidate.netVisibleSegmentCount
194
+ }
195
+
196
+ const getBestCycleCollapseCandidate = ({
197
+ targetTrace,
198
+ traces,
199
+ netLabelPlacements,
200
+ }: {
201
+ targetTrace: SolvedTracePath
202
+ traces: SolvedTracePath[]
203
+ netLabelPlacements: NetLabelPlacement[]
204
+ }): CycleCollapseCandidate | null => {
205
+ const sameNetTraces = traces.filter(
206
+ (trace) => trace.globalConnNetId === targetTrace.globalConnNetId,
207
+ )
208
+ const baselineNetVisibleLength = getVisibleTraceLength(sameNetTraces)
209
+ const baselineTargetVisibleLength = getVisibleTraceLength([targetTrace])
210
+ let bestCandidate: CycleCollapseCandidate | null = null
211
+
212
+ for (const donorTrace of sameNetTraces) {
213
+ if (donorTrace.mspPairId === targetTrace.mspPairId) continue
214
+ const sharedPin = getSharedPin({
215
+ donorTrace,
216
+ branchTrace: targetTrace,
217
+ })
218
+ if (!sharedPin) continue
219
+ const targetPath = getTracePathStartingAtPin(targetTrace, sharedPin)
220
+ const donorPath = getTracePathStartingAtPin(donorTrace, sharedPin)
221
+ if (!targetPath || !donorPath) continue
222
+
223
+ const crossings = getPerpendicularPathCrossings(targetPath, donorPath)
224
+ for (const crossing of crossings) {
225
+ const tracePath = buildCollapsedCyclePath({
226
+ targetTrace,
227
+ targetPath,
228
+ donorPath,
229
+ crossing,
230
+ })
231
+ const candidateTrace = { ...targetTrace, tracePath }
232
+ const candidateTargetVisibleLength = getVisibleTraceLength([
233
+ candidateTrace,
234
+ ])
235
+ if (
236
+ candidateTargetVisibleLength >
237
+ baselineTargetVisibleLength + TRACE_LENGTH_EPSILON
238
+ ) {
239
+ continue
240
+ }
241
+ const candidateNetLabelPlacements = getNetLabelPlacementsForCycleCollapse(
242
+ {
243
+ targetTrace,
244
+ tracePath,
245
+ netLabelPlacements,
246
+ },
247
+ )
248
+ if (!candidateNetLabelPlacements) continue
249
+
250
+ const candidateNetTraces = sameNetTraces.map((trace) => {
251
+ if (trace.mspPairId === targetTrace.mspPairId) return candidateTrace
252
+ return trace
253
+ })
254
+ const netVisibleLength = getVisibleTraceLength(candidateNetTraces)
255
+ if (netVisibleLength >= baselineNetVisibleLength - TRACE_LENGTH_EPSILON) {
256
+ continue
257
+ }
258
+ const netVisibleSegmentCount =
259
+ getVisibleTraceSegmentCount(candidateNetTraces)
260
+ const candidate = {
261
+ tracePath,
262
+ netLabelPlacements: candidateNetLabelPlacements,
263
+ netVisibleLength,
264
+ netVisibleSegmentCount,
265
+ }
266
+ if (candidateIsBetter(candidate, bestCandidate)) {
267
+ bestCandidate = candidate
268
+ }
269
+ }
270
+ }
271
+
272
+ return bestCandidate
273
+ }
274
+
275
+ export const collapseSameNetCycles = ({
276
+ traces,
277
+ netLabelPlacements,
278
+ }: CollapseSameNetCyclesInput) => {
279
+ const outputTraces = [...traces]
280
+ let outputNetLabelPlacements = [...netLabelPlacements]
281
+ let collapsedCycleCount = 0
282
+ let traceIndex = 0
283
+
284
+ while (traceIndex < outputTraces.length) {
285
+ const targetTrace = outputTraces[traceIndex]!
286
+ const candidate = getBestCycleCollapseCandidate({
287
+ targetTrace,
288
+ traces: outputTraces,
289
+ netLabelPlacements: outputNetLabelPlacements,
290
+ })
291
+ if (!candidate) {
292
+ traceIndex++
293
+ continue
294
+ }
295
+
296
+ outputTraces[traceIndex] = {
297
+ ...targetTrace,
298
+ tracePath: candidate.tracePath,
299
+ }
300
+ outputNetLabelPlacements = candidate.netLabelPlacements
301
+ collapsedCycleCount++
302
+ traceIndex = 0
303
+ }
304
+
305
+ return {
306
+ traces: outputTraces,
307
+ netLabelPlacements: outputNetLabelPlacements,
308
+ collapsedCycleCount,
309
+ }
310
+ }
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.165",
8
+ "version": "0.0.166",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",