@tscircuit/schematic-trace-solver 0.0.122 → 0.0.124
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 +102 -5
- package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +50 -5
- package/lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts +29 -0
- package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +7 -2
- package/lib/solvers/TraceCleanupSolver/shortenExcessiveLabelPaddingDetour.ts +64 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260804T171919Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260804T225912Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +355 -0
- package/tests/bug-reports/bug-report-20260804T171919Z/bug-report-20260804T171919Z.json +1775 -0
- package/tests/bug-reports/bug-report-20260804T171919Z/bug-report-20260804T171919Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +60 -0
- package/tests/bug-reports/bug-report-20260804T225912Z/bug-report-20260804T225912Z.json +148 -0
- package/tests/bug-reports/bug-report-20260804T225912Z/bug-report-20260804T225912Z.test.ts +30 -0
- package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +112 -0
- package/tests/repros/assets/repro161-power-section-autolayout.input.json +578 -0
- package/tests/repros/repro161-power-section-autolayout.test.ts +34 -0
package/dist/index.js
CHANGED
|
@@ -5108,6 +5108,51 @@ var getVisibleTraceMetrics = (traces) => {
|
|
|
5108
5108
|
var getVisibleTraceLength = (traces) => getVisibleTraceMetrics(traces).length;
|
|
5109
5109
|
var getVisibleTraceSegmentCount = (traces) => getVisibleTraceMetrics(traces).segmentCount;
|
|
5110
5110
|
|
|
5111
|
+
// lib/solvers/TraceCleanupSolver/shortenExcessiveLabelPaddingDetour.ts
|
|
5112
|
+
var MIN_EXCESSIVE_DETOUR_LENGTH_RATIO = 2.5;
|
|
5113
|
+
var getTracePathLength = (tracePath) => {
|
|
5114
|
+
let pathLength = 0;
|
|
5115
|
+
for (let pointIndex = 1; pointIndex < tracePath.length; pointIndex++) {
|
|
5116
|
+
const previousPoint = tracePath[pointIndex - 1];
|
|
5117
|
+
const point = tracePath[pointIndex];
|
|
5118
|
+
pathLength += Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
|
|
5119
|
+
}
|
|
5120
|
+
return pathLength;
|
|
5121
|
+
};
|
|
5122
|
+
var shortenExcessiveLabelPaddingDetour = ({
|
|
5123
|
+
inputProblem,
|
|
5124
|
+
trace
|
|
5125
|
+
}) => {
|
|
5126
|
+
const exactConnection = inputProblem.directConnections.find(
|
|
5127
|
+
(connection) => connection.pinIds.every((pinId) => trace.pinIds.includes(pinId))
|
|
5128
|
+
);
|
|
5129
|
+
if (!exactConnection) return trace;
|
|
5130
|
+
const chipMap = {};
|
|
5131
|
+
for (const chip of inputProblem.chips) chipMap[chip.chipId] = chip;
|
|
5132
|
+
const candidateSolver = new SchematicTraceSingleLineSolver2({
|
|
5133
|
+
pins: trace.pins,
|
|
5134
|
+
connectionPair: trace,
|
|
5135
|
+
inputProblem: {
|
|
5136
|
+
...inputProblem,
|
|
5137
|
+
directConnections: [
|
|
5138
|
+
exactConnection,
|
|
5139
|
+
...inputProblem.directConnections.filter(
|
|
5140
|
+
(connection) => connection !== exactConnection
|
|
5141
|
+
)
|
|
5142
|
+
]
|
|
5143
|
+
},
|
|
5144
|
+
chipMap
|
|
5145
|
+
});
|
|
5146
|
+
candidateSolver.solve();
|
|
5147
|
+
if (!candidateSolver.solvedTracePath) return trace;
|
|
5148
|
+
const currentLength = getTracePathLength(trace.tracePath);
|
|
5149
|
+
const candidateLength = getTracePathLength(candidateSolver.solvedTracePath);
|
|
5150
|
+
if (currentLength <= candidateLength * MIN_EXCESSIVE_DETOUR_LENGTH_RATIO) {
|
|
5151
|
+
return trace;
|
|
5152
|
+
}
|
|
5153
|
+
return { ...trace, tracePath: candidateSolver.solvedTracePath };
|
|
5154
|
+
};
|
|
5155
|
+
|
|
5111
5156
|
// lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
|
|
5112
5157
|
var minimizeTurnsWithFilteredLabels = ({
|
|
5113
5158
|
targetMspConnectionPairId,
|
|
@@ -5117,12 +5162,16 @@ var minimizeTurnsWithFilteredLabels = ({
|
|
|
5117
5162
|
mergedLabelNetIdMap,
|
|
5118
5163
|
paddingBuffer
|
|
5119
5164
|
}) => {
|
|
5120
|
-
const
|
|
5165
|
+
const originalTargetTrace = traces.find(
|
|
5121
5166
|
(t) => t.mspPairId === targetMspConnectionPairId
|
|
5122
5167
|
);
|
|
5123
|
-
if (!
|
|
5168
|
+
if (!originalTargetTrace) {
|
|
5124
5169
|
throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
|
|
5125
5170
|
}
|
|
5171
|
+
const targetTrace = shortenExcessiveLabelPaddingDetour({
|
|
5172
|
+
inputProblem,
|
|
5173
|
+
trace: originalTargetTrace
|
|
5174
|
+
});
|
|
5126
5175
|
const targetPinIds = new Set(targetTrace.pinIds);
|
|
5127
5176
|
const otherTraces = traces.filter(
|
|
5128
5177
|
(trace) => trace.mspPairId !== targetMspConnectionPairId
|
|
@@ -11181,6 +11230,7 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
|
|
|
11181
11230
|
};
|
|
11182
11231
|
|
|
11183
11232
|
// lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts
|
|
11233
|
+
var NET_LABEL_INTERIOR_INSET = 1e-6;
|
|
11184
11234
|
var pathIntersectsAnyNetLabel = ({
|
|
11185
11235
|
path,
|
|
11186
11236
|
netLabelPlacements
|
|
@@ -11195,9 +11245,30 @@ var pathIntersectsAnyNetLabel = ({
|
|
|
11195
11245
|
}
|
|
11196
11246
|
return false;
|
|
11197
11247
|
};
|
|
11248
|
+
var pathEntersAnyNetLabel = ({
|
|
11249
|
+
path,
|
|
11250
|
+
netLabelPlacements
|
|
11251
|
+
}) => {
|
|
11252
|
+
for (const label of netLabelPlacements) {
|
|
11253
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
11254
|
+
const labelInteriorBounds = {
|
|
11255
|
+
minX: labelBounds.minX + NET_LABEL_INTERIOR_INSET,
|
|
11256
|
+
minY: labelBounds.minY + NET_LABEL_INTERIOR_INSET,
|
|
11257
|
+
maxX: labelBounds.maxX - NET_LABEL_INTERIOR_INSET,
|
|
11258
|
+
maxY: labelBounds.maxY - NET_LABEL_INTERIOR_INSET
|
|
11259
|
+
};
|
|
11260
|
+
for (let index = 0; index < path.length - 1; index++) {
|
|
11261
|
+
if (segmentIntersectsRect2(path[index], path[index + 1], labelInteriorBounds)) {
|
|
11262
|
+
return true;
|
|
11263
|
+
}
|
|
11264
|
+
}
|
|
11265
|
+
}
|
|
11266
|
+
return false;
|
|
11267
|
+
};
|
|
11198
11268
|
|
|
11199
11269
|
// lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
|
|
11200
11270
|
var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
|
|
11271
|
+
var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
|
|
11201
11272
|
var getSharedPin = ({
|
|
11202
11273
|
donorTrace,
|
|
11203
11274
|
branchTrace
|
|
@@ -11274,6 +11345,7 @@ var getAlignedBranchPath = ({
|
|
|
11274
11345
|
};
|
|
11275
11346
|
var candidateIsClear = ({
|
|
11276
11347
|
candidateTrace,
|
|
11348
|
+
originalTrace,
|
|
11277
11349
|
traces,
|
|
11278
11350
|
inputProblem,
|
|
11279
11351
|
netLabelPlacements
|
|
@@ -11288,10 +11360,34 @@ var candidateIsClear = ({
|
|
|
11288
11360
|
if (doesPathCoincideWithTraces(candidateTrace.tracePath, otherNetTraces)) {
|
|
11289
11361
|
return false;
|
|
11290
11362
|
}
|
|
11291
|
-
|
|
11363
|
+
const otherNetLabelPlacements = netLabelPlacements.filter(
|
|
11364
|
+
(label) => label.globalConnNetId !== candidateTrace.globalConnNetId
|
|
11365
|
+
);
|
|
11366
|
+
if (pathIntersectsAnyNetLabel({
|
|
11292
11367
|
path: candidateTrace.tracePath,
|
|
11293
|
-
netLabelPlacements
|
|
11294
|
-
})
|
|
11368
|
+
netLabelPlacements: otherNetLabelPlacements
|
|
11369
|
+
})) {
|
|
11370
|
+
return false;
|
|
11371
|
+
}
|
|
11372
|
+
const sameNetLabelPlacements = netLabelPlacements.filter(
|
|
11373
|
+
(label) => label.globalConnNetId === candidateTrace.globalConnNetId
|
|
11374
|
+
);
|
|
11375
|
+
if (!pathIntersectsAnyNetLabel({
|
|
11376
|
+
path: candidateTrace.tracePath,
|
|
11377
|
+
netLabelPlacements: sameNetLabelPlacements
|
|
11378
|
+
})) {
|
|
11379
|
+
return true;
|
|
11380
|
+
}
|
|
11381
|
+
if (pathEntersAnyNetLabel({
|
|
11382
|
+
path: candidateTrace.tracePath,
|
|
11383
|
+
netLabelPlacements: sameNetLabelPlacements
|
|
11384
|
+
})) {
|
|
11385
|
+
return false;
|
|
11386
|
+
}
|
|
11387
|
+
const originalRail = getLongestHorizontalSegment(originalTrace);
|
|
11388
|
+
const candidateRail = getLongestHorizontalSegment(candidateTrace);
|
|
11389
|
+
if (!originalRail || !candidateRail) return false;
|
|
11390
|
+
return Math.abs(originalRail.start.y - candidateRail.start.y) <= MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET;
|
|
11295
11391
|
};
|
|
11296
11392
|
var alignSameNetJunctions = ({
|
|
11297
11393
|
inputProblem,
|
|
@@ -11324,6 +11420,7 @@ var alignSameNetJunctions = ({
|
|
|
11324
11420
|
}
|
|
11325
11421
|
if (!candidateIsClear({
|
|
11326
11422
|
candidateTrace,
|
|
11423
|
+
originalTrace: branchTrace,
|
|
11327
11424
|
traces: outputTraces,
|
|
11328
11425
|
inputProblem,
|
|
11329
11426
|
netLabelPlacements
|
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
} from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
|
|
13
13
|
import type { InputPin, InputProblem } from "lib/types/InputProblem"
|
|
14
14
|
import { doesPathCoincideWithTraces } from "lib/utils/doesPathCoincideWithTraces"
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
pathEntersAnyNetLabel,
|
|
17
|
+
pathIntersectsAnyNetLabel,
|
|
18
|
+
} from "./pathIntersectsAnyNetLabel"
|
|
16
19
|
|
|
17
20
|
interface AlignSameNetJunctionsInput {
|
|
18
21
|
inputProblem: InputProblem
|
|
@@ -25,7 +28,10 @@ interface HorizontalSegment {
|
|
|
25
28
|
end: Point
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
// Only near-level load pins should be combined onto one horizontal rail.
|
|
28
32
|
const MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2
|
|
33
|
+
// Limit label-boundary alignment to small corrections that cannot create spikes.
|
|
34
|
+
const MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2
|
|
29
35
|
|
|
30
36
|
const getSharedPin = ({
|
|
31
37
|
donorTrace,
|
|
@@ -135,11 +141,13 @@ const getAlignedBranchPath = ({
|
|
|
135
141
|
|
|
136
142
|
const candidateIsClear = ({
|
|
137
143
|
candidateTrace,
|
|
144
|
+
originalTrace,
|
|
138
145
|
traces,
|
|
139
146
|
inputProblem,
|
|
140
147
|
netLabelPlacements,
|
|
141
148
|
}: {
|
|
142
149
|
candidateTrace: SolvedTracePath
|
|
150
|
+
originalTrace: SolvedTracePath
|
|
143
151
|
traces: SolvedTracePath[]
|
|
144
152
|
inputProblem: InputProblem
|
|
145
153
|
netLabelPlacements: NetLabelPlacement[]
|
|
@@ -156,10 +164,46 @@ const candidateIsClear = ({
|
|
|
156
164
|
return false
|
|
157
165
|
}
|
|
158
166
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
const otherNetLabelPlacements = netLabelPlacements.filter(
|
|
168
|
+
(label) => label.globalConnNetId !== candidateTrace.globalConnNetId,
|
|
169
|
+
)
|
|
170
|
+
if (
|
|
171
|
+
pathIntersectsAnyNetLabel({
|
|
172
|
+
path: candidateTrace.tracePath,
|
|
173
|
+
netLabelPlacements: otherNetLabelPlacements,
|
|
174
|
+
})
|
|
175
|
+
) {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const sameNetLabelPlacements = netLabelPlacements.filter(
|
|
180
|
+
(label) => label.globalConnNetId === candidateTrace.globalConnNetId,
|
|
181
|
+
)
|
|
182
|
+
// A same-net rail may follow its label edge, but never enter the label body.
|
|
183
|
+
if (
|
|
184
|
+
!pathIntersectsAnyNetLabel({
|
|
185
|
+
path: candidateTrace.tracePath,
|
|
186
|
+
netLabelPlacements: sameNetLabelPlacements,
|
|
187
|
+
})
|
|
188
|
+
) {
|
|
189
|
+
return true
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
pathEntersAnyNetLabel({
|
|
193
|
+
path: candidateTrace.tracePath,
|
|
194
|
+
netLabelPlacements: sameNetLabelPlacements,
|
|
195
|
+
})
|
|
196
|
+
) {
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const originalRail = getLongestHorizontalSegment(originalTrace)
|
|
201
|
+
const candidateRail = getLongestHorizontalSegment(candidateTrace)
|
|
202
|
+
if (!originalRail || !candidateRail) return false
|
|
203
|
+
return (
|
|
204
|
+
Math.abs(originalRail.start.y - candidateRail.start.y) <=
|
|
205
|
+
MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET
|
|
206
|
+
)
|
|
163
207
|
}
|
|
164
208
|
|
|
165
209
|
export const alignSameNetJunctions = ({
|
|
@@ -202,6 +246,7 @@ export const alignSameNetJunctions = ({
|
|
|
202
246
|
if (
|
|
203
247
|
!candidateIsClear({
|
|
204
248
|
candidateTrace,
|
|
249
|
+
originalTrace: branchTrace,
|
|
205
250
|
traces: outputTraces,
|
|
206
251
|
inputProblem,
|
|
207
252
|
netLabelPlacements,
|
|
@@ -3,6 +3,9 @@ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetL
|
|
|
3
3
|
import { segmentIntersectsRect } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
|
|
4
4
|
import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
5
5
|
|
|
6
|
+
// Insetting distinguishes harmless edge contact from entering the label body.
|
|
7
|
+
const NET_LABEL_INTERIOR_INSET = 1e-6
|
|
8
|
+
|
|
6
9
|
export const pathIntersectsAnyNetLabel = ({
|
|
7
10
|
path,
|
|
8
11
|
netLabelPlacements,
|
|
@@ -20,3 +23,29 @@ export const pathIntersectsAnyNetLabel = ({
|
|
|
20
23
|
}
|
|
21
24
|
return false
|
|
22
25
|
}
|
|
26
|
+
|
|
27
|
+
export const pathEntersAnyNetLabel = ({
|
|
28
|
+
path,
|
|
29
|
+
netLabelPlacements,
|
|
30
|
+
}: {
|
|
31
|
+
path: Point[]
|
|
32
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
33
|
+
}) => {
|
|
34
|
+
for (const label of netLabelPlacements) {
|
|
35
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height)
|
|
36
|
+
const labelInteriorBounds = {
|
|
37
|
+
minX: labelBounds.minX + NET_LABEL_INTERIOR_INSET,
|
|
38
|
+
minY: labelBounds.minY + NET_LABEL_INTERIOR_INSET,
|
|
39
|
+
maxX: labelBounds.maxX - NET_LABEL_INTERIOR_INSET,
|
|
40
|
+
maxY: labelBounds.maxY - NET_LABEL_INTERIOR_INSET,
|
|
41
|
+
}
|
|
42
|
+
for (let index = 0; index < path.length - 1; index++) {
|
|
43
|
+
if (
|
|
44
|
+
segmentIntersectsRect(path[index], path[index + 1], labelInteriorBounds)
|
|
45
|
+
) {
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getVisibleTraceSegmentCount,
|
|
10
10
|
RAIL_ALIGNMENT_EPSILON,
|
|
11
11
|
} from "./sameNetRailAlignment/geometry"
|
|
12
|
+
import { shortenExcessiveLabelPaddingDetour } from "./shortenExcessiveLabelPaddingDetour"
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Minimizes turns with a strict pass that treats every other trace as an
|
|
@@ -31,12 +32,16 @@ export const minimizeTurnsWithFilteredLabels = ({
|
|
|
31
32
|
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
32
33
|
paddingBuffer: number
|
|
33
34
|
}): SolvedTracePath => {
|
|
34
|
-
const
|
|
35
|
+
const originalTargetTrace = traces.find(
|
|
35
36
|
(t) => t.mspPairId === targetMspConnectionPairId,
|
|
36
37
|
)
|
|
37
|
-
if (!
|
|
38
|
+
if (!originalTargetTrace) {
|
|
38
39
|
throw new Error(`Target trace ${targetMspConnectionPairId} not found`)
|
|
39
40
|
}
|
|
41
|
+
const targetTrace = shortenExcessiveLabelPaddingDetour({
|
|
42
|
+
inputProblem,
|
|
43
|
+
trace: originalTargetTrace,
|
|
44
|
+
})
|
|
40
45
|
|
|
41
46
|
const targetPinIds = new Set(targetTrace.pinIds)
|
|
42
47
|
const otherTraces = traces.filter(
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
2
|
+
import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
|
|
3
|
+
import type { InputChip, InputProblem } from "lib/types/InputProblem"
|
|
4
|
+
|
|
5
|
+
const MIN_EXCESSIVE_DETOUR_LENGTH_RATIO = 2.5
|
|
6
|
+
|
|
7
|
+
const getTracePathLength = (
|
|
8
|
+
tracePath: SolvedTracePath["tracePath"],
|
|
9
|
+
): number => {
|
|
10
|
+
let pathLength = 0
|
|
11
|
+
for (let pointIndex = 1; pointIndex < tracePath.length; pointIndex++) {
|
|
12
|
+
const previousPoint = tracePath[pointIndex - 1]!
|
|
13
|
+
const point = tracePath[pointIndex]!
|
|
14
|
+
pathLength +=
|
|
15
|
+
Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y)
|
|
16
|
+
}
|
|
17
|
+
return pathLength
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Retries an already-solved severe detour with its exact direct connection
|
|
22
|
+
* first, preventing a sibling branch's label width from inflating text padding.
|
|
23
|
+
* The shorter route is used only when it is less than 40% of the original.
|
|
24
|
+
*/
|
|
25
|
+
export const shortenExcessiveLabelPaddingDetour = ({
|
|
26
|
+
inputProblem,
|
|
27
|
+
trace,
|
|
28
|
+
}: {
|
|
29
|
+
inputProblem: InputProblem
|
|
30
|
+
trace: SolvedTracePath
|
|
31
|
+
}): SolvedTracePath => {
|
|
32
|
+
const exactConnection = inputProblem.directConnections.find((connection) =>
|
|
33
|
+
connection.pinIds.every((pinId) => trace.pinIds.includes(pinId)),
|
|
34
|
+
)
|
|
35
|
+
if (!exactConnection) return trace
|
|
36
|
+
|
|
37
|
+
const chipMap: Record<string, InputChip> = {}
|
|
38
|
+
for (const chip of inputProblem.chips) chipMap[chip.chipId] = chip
|
|
39
|
+
|
|
40
|
+
const candidateSolver = new SchematicTraceSingleLineSolver2({
|
|
41
|
+
pins: trace.pins,
|
|
42
|
+
connectionPair: trace,
|
|
43
|
+
inputProblem: {
|
|
44
|
+
...inputProblem,
|
|
45
|
+
directConnections: [
|
|
46
|
+
exactConnection,
|
|
47
|
+
...inputProblem.directConnections.filter(
|
|
48
|
+
(connection) => connection !== exactConnection,
|
|
49
|
+
),
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
chipMap,
|
|
53
|
+
})
|
|
54
|
+
candidateSolver.solve()
|
|
55
|
+
if (!candidateSolver.solvedTracePath) return trace
|
|
56
|
+
|
|
57
|
+
const currentLength = getTracePathLength(trace.tracePath)
|
|
58
|
+
const candidateLength = getTracePathLength(candidateSolver.solvedTracePath)
|
|
59
|
+
if (currentLength <= candidateLength * MIN_EXCESSIVE_DETOUR_LENGTH_RATIO) {
|
|
60
|
+
return trace
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { ...trace, tracePath: candidateSolver.solvedTracePath }
|
|
64
|
+
}
|
package/package.json
CHANGED