@tscircuit/schematic-trace-solver 0.0.67 → 0.0.68
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 +26 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +42 -0
- package/package.json +1 -1
- package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +3 -3
- package/tests/repros/repro130-bq27441-fuel-gauge-trace-through-c1.test.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -4550,6 +4550,7 @@ var visualizeCollision = (collisionInfo) => {
|
|
|
4550
4550
|
// lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts
|
|
4551
4551
|
var UntangleTraceSubsolver = class extends BaseSolver {
|
|
4552
4552
|
input;
|
|
4553
|
+
chipObstacleSpatialIndex;
|
|
4553
4554
|
lShapesToProcess = [];
|
|
4554
4555
|
visualizationMode = "l_shapes";
|
|
4555
4556
|
currentLShape = null;
|
|
@@ -4570,6 +4571,10 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
4570
4571
|
super();
|
|
4571
4572
|
this.input = solverInput;
|
|
4572
4573
|
this.visualizationMode = "l_shapes";
|
|
4574
|
+
this.chipObstacleSpatialIndex = this.input.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(this.input.inputProblem.chips);
|
|
4575
|
+
if (!this.input.inputProblem._chipObstacleSpatialIndex) {
|
|
4576
|
+
this.input.inputProblem._chipObstacleSpatialIndex = this.chipObstacleSpatialIndex;
|
|
4577
|
+
}
|
|
4573
4578
|
for (const trace of this.input.allTraces) {
|
|
4574
4579
|
const lShapes = findAllLShapedTurns(trace.tracePath);
|
|
4575
4580
|
this.lShapesToProcess.push(
|
|
@@ -4696,6 +4701,12 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
4696
4701
|
this.input.allTraces,
|
|
4697
4702
|
this.currentLShape.traceId
|
|
4698
4703
|
);
|
|
4704
|
+
if (!collisionResult?.isColliding && this._doesCandidateCrossChip(currentCandidate)) {
|
|
4705
|
+
this.lastCollision = null;
|
|
4706
|
+
this.collidingCandidate = currentCandidate;
|
|
4707
|
+
this.currentCandidateIndex++;
|
|
4708
|
+
return;
|
|
4709
|
+
}
|
|
4699
4710
|
if (!collisionResult?.isColliding) {
|
|
4700
4711
|
this.bestRouteFound = currentCandidate;
|
|
4701
4712
|
this.lastCollision = null;
|
|
@@ -4706,6 +4717,21 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
4706
4717
|
this.currentCandidateIndex++;
|
|
4707
4718
|
}
|
|
4708
4719
|
}
|
|
4720
|
+
/**
|
|
4721
|
+
* Returns true if any segment of the candidate reroute passes through a
|
|
4722
|
+
* schematic component (chip) body.
|
|
4723
|
+
*/
|
|
4724
|
+
_doesCandidateCrossChip(candidate) {
|
|
4725
|
+
for (let i = 0; i < candidate.length - 1; i++) {
|
|
4726
|
+
if (this.chipObstacleSpatialIndex.doesOrthogonalLineIntersectChip([
|
|
4727
|
+
candidate[i],
|
|
4728
|
+
candidate[i + 1]
|
|
4729
|
+
])) {
|
|
4730
|
+
return true;
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
return false;
|
|
4734
|
+
}
|
|
4709
4735
|
_applyBestRoute(bestRoute) {
|
|
4710
4736
|
this.bestRoute = bestRoute;
|
|
4711
4737
|
this.collidingCandidate = null;
|
|
@@ -2,6 +2,7 @@ import { BaseSolver } from "../../BaseSolver/BaseSolver"
|
|
|
2
2
|
import type { InputProblem } from "../../../types/InputProblem"
|
|
3
3
|
import type { SolvedTracePath } from "../../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
4
|
import type { NetLabelPlacement } from "../../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
5
|
+
import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex"
|
|
5
6
|
|
|
6
7
|
import { findAllLShapedTurns, type LShape } from "./findAllLShapedTurns"
|
|
7
8
|
import { getTraceObstacles } from "./getTraceObstacles"
|
|
@@ -57,6 +58,7 @@ type VisualizationMode =
|
|
|
57
58
|
*/
|
|
58
59
|
export class UntangleTraceSubsolver extends BaseSolver {
|
|
59
60
|
private input: UntangleTraceSubsolverInput
|
|
61
|
+
private chipObstacleSpatialIndex: ChipObstacleSpatialIndex
|
|
60
62
|
private lShapesToProcess: LShape[] = []
|
|
61
63
|
private visualizationMode: VisualizationMode = "l_shapes"
|
|
62
64
|
|
|
@@ -86,6 +88,14 @@ export class UntangleTraceSubsolver extends BaseSolver {
|
|
|
86
88
|
this.input = solverInput
|
|
87
89
|
this.visualizationMode = "l_shapes"
|
|
88
90
|
|
|
91
|
+
this.chipObstacleSpatialIndex =
|
|
92
|
+
this.input.inputProblem._chipObstacleSpatialIndex ??
|
|
93
|
+
new ChipObstacleSpatialIndex(this.input.inputProblem.chips)
|
|
94
|
+
if (!this.input.inputProblem._chipObstacleSpatialIndex) {
|
|
95
|
+
this.input.inputProblem._chipObstacleSpatialIndex =
|
|
96
|
+
this.chipObstacleSpatialIndex
|
|
97
|
+
}
|
|
98
|
+
|
|
89
99
|
for (const trace of this.input.allTraces) {
|
|
90
100
|
const lShapes = findAllLShapedTurns(trace.tracePath)
|
|
91
101
|
this.lShapesToProcess.push(
|
|
@@ -232,6 +242,20 @@ export class UntangleTraceSubsolver extends BaseSolver {
|
|
|
232
242
|
this.currentLShape!.traceId,
|
|
233
243
|
)
|
|
234
244
|
|
|
245
|
+
// Untangling must never move a trace through a component body. The candidate
|
|
246
|
+
// only covers the rerouted corner (not the pin-terminal segments), so reject
|
|
247
|
+
// any candidate that crosses a chip; the original (component-clear) path is
|
|
248
|
+
// kept instead, so this stage only ever improves or leaves the trace valid.
|
|
249
|
+
if (
|
|
250
|
+
!collisionResult?.isColliding &&
|
|
251
|
+
this._doesCandidateCrossChip(currentCandidate)
|
|
252
|
+
) {
|
|
253
|
+
this.lastCollision = null
|
|
254
|
+
this.collidingCandidate = currentCandidate
|
|
255
|
+
this.currentCandidateIndex++
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
|
|
235
259
|
if (!collisionResult?.isColliding) {
|
|
236
260
|
this.bestRouteFound = currentCandidate
|
|
237
261
|
this.lastCollision = null
|
|
@@ -243,6 +267,24 @@ export class UntangleTraceSubsolver extends BaseSolver {
|
|
|
243
267
|
}
|
|
244
268
|
}
|
|
245
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Returns true if any segment of the candidate reroute passes through a
|
|
272
|
+
* schematic component (chip) body.
|
|
273
|
+
*/
|
|
274
|
+
private _doesCandidateCrossChip(candidate: Point[]): boolean {
|
|
275
|
+
for (let i = 0; i < candidate.length - 1; i++) {
|
|
276
|
+
if (
|
|
277
|
+
this.chipObstacleSpatialIndex.doesOrthogonalLineIntersectChip([
|
|
278
|
+
candidate[i]!,
|
|
279
|
+
candidate[i + 1]!,
|
|
280
|
+
])
|
|
281
|
+
) {
|
|
282
|
+
return true
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return false
|
|
286
|
+
}
|
|
287
|
+
|
|
246
288
|
private _applyBestRoute(bestRoute: Point[]) {
|
|
247
289
|
this.bestRoute = bestRoute
|
|
248
290
|
this.collidingCandidate = null
|
package/package.json
CHANGED
|
@@ -138,7 +138,7 @@ orientation: x+" data-x="-4.7" data-y="-0.3999999999999999" cx="202.273458966275
|
|
|
138
138
|
</g>
|
|
139
139
|
<g>
|
|
140
140
|
<circle data-type="point" data-label="anchorPoint
|
|
141
|
-
orientation: x-" data-x="-3" data-y="-
|
|
141
|
+
orientation: x-" data-x="-3" data-y="-2.75" cx="258.57241411784" cy="368.01969704104056" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
142
142
|
</g>
|
|
143
143
|
<g>
|
|
144
144
|
<circle data-type="point" data-label="anchorPoint
|
|
@@ -318,7 +318,7 @@ orientation: x-" data-x="6" data-y="4.3" cx="556.6257060967127" cy="134.54461832
|
|
|
318
318
|
<polyline data-points="4.950000000000001,-4.8 5.150000000000001,-4.8 5.150000000000001,-0.42499999999999993 2.6999999999999997,-0.42499999999999993" data-type="line" data-label="" points="521.8528220325109,435.9096135473393 528.4762285209304,435.9096135473393 528.4762285209304,291.0225966131651 447.3394990377927,291.0225966131651" fill="none" stroke="purple" stroke-width="1" />
|
|
319
319
|
</g>
|
|
320
320
|
<g>
|
|
321
|
-
<polyline data-points="-2.7,1.0999999999999996 -
|
|
321
|
+
<polyline data-points="-2.7,1.0999999999999996 -3.7,1.0999999999999996 -3.7,-2.2 -4.7,-2.2 -4.7,-2" data-type="line" data-label="" points="268.50752385046906,240.51912213896725 235.39049140837213,240.51912213896725 235.39049140837213,349.8053291978872 202.27345896627517,349.8053291978872 202.27345896627517,343.1819227094678" fill="none" stroke="purple" stroke-width="1" />
|
|
322
322
|
</g>
|
|
323
323
|
<g>
|
|
324
324
|
<polyline data-points="0,-1.3499999999999999 0,-1.5499999999999998 -2.8,-1.5499999999999998 -2.8,1.1 -2.6999999999999997,1.1" data-type="line" data-label="" points="357.9235114441309,321.6558516221048 357.9235114441309,328.2792581105242 265.1958206062594,328.2792581105242 265.1958206062594,240.51912213896725 268.5075238504691,240.51912213896725" fill="none" stroke="purple" stroke-width="1" />
|
|
@@ -374,7 +374,7 @@ globalConnNetId: connectivity_net6" data-x="-4.4750000000000005" data-y="-0.3999
|
|
|
374
374
|
</g>
|
|
375
375
|
<g>
|
|
376
376
|
<rect data-type="rect" data-label="netId: PACKP
|
|
377
|
-
globalConnNetId: connectivity_net7" data-x="-3.36" data-y="-
|
|
377
|
+
globalConnNetId: connectivity_net7" data-x="-3.36" data-y="-2.75" x="234.7281507595302" y="364.70799379683086" width="23.84426335830983" height="6.623406488419391" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.030195942276785723" />
|
|
378
378
|
</g>
|
|
379
379
|
<g>
|
|
380
380
|
<rect data-type="rect" data-label="netId: PACKP
|
|
@@ -36,7 +36,7 @@ const pathIntersectsRect = (path: Array<{ x: number; y: number }>, rect: any) =>
|
|
|
36
36
|
return segmentIntersectsRect(path[index - 1], point, rect)
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
test("repro130 bq27441 fuel gauge trace
|
|
39
|
+
test("repro130 bq27441 fuel gauge trace does not cross C1 obstacle", () => {
|
|
40
40
|
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
41
41
|
solver.solve()
|
|
42
42
|
|
|
@@ -54,7 +54,7 @@ test("repro130 bq27441 fuel gauge trace crosses C1 obstacle", () => {
|
|
|
54
54
|
expect(c1).toBeDefined()
|
|
55
55
|
expect(traceThroughC1).toBeDefined()
|
|
56
56
|
expect(pathIntersectsRect(traceThroughC1!.tracePath, getChipRect(c1))).toBe(
|
|
57
|
-
|
|
57
|
+
false,
|
|
58
58
|
)
|
|
59
59
|
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
60
60
|
})
|