@tscircuit/schematic-trace-solver 0.0.168 → 0.0.169
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 +77 -1
- package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +2 -2
- package/lib/solvers/NetLabelTraceRecovery/doesTraceRecoveryPathConflict.ts +112 -0
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +60 -222
- package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +46 -64
- package/tests/bug-reports/bug-report-20260707T141421Z/__snapshots__/bug-report-20260707T141421Z.snap.svg +7 -25
- package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +68 -104
- package/tests/examples/__snapshots__/example35.snap.svg +5 -23
- package/tests/examples/__snapshots__/example36.snap.svg +27 -45
- package/tests/examples/__snapshots__/example37.snap.svg +7 -25
- package/tests/examples/__snapshots__/example51.snap.svg +47 -83
- package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +5 -23
- package/tests/repros/__snapshots__/repro-bc107a-common-emitter-routing.snap.svg +141 -0
- package/tests/repros/__snapshots__/repro-dc-power-isolated-routing.snap.svg +6 -24
- package/tests/repros/__snapshots__/repro-powerbank-3v-system-power.snap.svg +255 -0
- package/tests/repros/assets/repro-bc107a-common-emitter-routing.input.json +687 -0
- package/tests/repros/assets/repro-powerbank-3v-system-power.input.json +1772 -0
- package/tests/repros/repro-bc107a-common-emitter-routing.test.ts +16 -0
- package/tests/repros/repro-powerbank-3v-system-power.test.ts +17 -0
package/dist/index.js
CHANGED
|
@@ -15296,6 +15296,82 @@ var getTraceRecoveryConnectivityMaps = (inputProblem) => {
|
|
|
15296
15296
|
return { chipMap, pinMap };
|
|
15297
15297
|
};
|
|
15298
15298
|
|
|
15299
|
+
// lib/solvers/NetLabelTraceRecovery/doesTraceRecoveryPathConflict.ts
|
|
15300
|
+
import { doSegmentsIntersect as doSegmentsIntersect4 } from "@tscircuit/math-utils";
|
|
15301
|
+
var EPSILON = 1e-6;
|
|
15302
|
+
var isStrictlyBetween2 = ({
|
|
15303
|
+
coordinate,
|
|
15304
|
+
segmentStartCoordinate,
|
|
15305
|
+
segmentEndCoordinate
|
|
15306
|
+
}) => coordinate > Math.min(segmentStartCoordinate, segmentEndCoordinate) + EPSILON && coordinate < Math.max(segmentStartCoordinate, segmentEndCoordinate) - EPSILON;
|
|
15307
|
+
var isStrictInteriorPerpendicularCrossing = ({
|
|
15308
|
+
firstSegmentStart,
|
|
15309
|
+
firstSegmentEnd,
|
|
15310
|
+
secondSegmentStart,
|
|
15311
|
+
secondSegmentEnd
|
|
15312
|
+
}) => {
|
|
15313
|
+
const firstIsHorizontal = Math.abs(firstSegmentStart.y - firstSegmentEnd.y) <= EPSILON;
|
|
15314
|
+
const firstIsVertical = Math.abs(firstSegmentStart.x - firstSegmentEnd.x) <= EPSILON;
|
|
15315
|
+
const secondIsHorizontal = Math.abs(secondSegmentStart.y - secondSegmentEnd.y) <= EPSILON;
|
|
15316
|
+
const secondIsVertical = Math.abs(secondSegmentStart.x - secondSegmentEnd.x) <= EPSILON;
|
|
15317
|
+
let horizontalStart;
|
|
15318
|
+
let horizontalEnd;
|
|
15319
|
+
if (firstIsHorizontal) {
|
|
15320
|
+
horizontalStart = firstSegmentStart;
|
|
15321
|
+
horizontalEnd = firstSegmentEnd;
|
|
15322
|
+
} else if (secondIsHorizontal) {
|
|
15323
|
+
horizontalStart = secondSegmentStart;
|
|
15324
|
+
horizontalEnd = secondSegmentEnd;
|
|
15325
|
+
} else {
|
|
15326
|
+
return false;
|
|
15327
|
+
}
|
|
15328
|
+
let verticalStart;
|
|
15329
|
+
let verticalEnd;
|
|
15330
|
+
if (firstIsVertical) {
|
|
15331
|
+
verticalStart = firstSegmentStart;
|
|
15332
|
+
verticalEnd = firstSegmentEnd;
|
|
15333
|
+
} else if (secondIsVertical) {
|
|
15334
|
+
verticalStart = secondSegmentStart;
|
|
15335
|
+
verticalEnd = secondSegmentEnd;
|
|
15336
|
+
} else {
|
|
15337
|
+
return false;
|
|
15338
|
+
}
|
|
15339
|
+
return isStrictlyBetween2({
|
|
15340
|
+
coordinate: verticalStart.x,
|
|
15341
|
+
segmentStartCoordinate: horizontalStart.x,
|
|
15342
|
+
segmentEndCoordinate: horizontalEnd.x
|
|
15343
|
+
}) && isStrictlyBetween2({
|
|
15344
|
+
coordinate: horizontalStart.y,
|
|
15345
|
+
segmentStartCoordinate: verticalStart.y,
|
|
15346
|
+
segmentEndCoordinate: verticalEnd.y
|
|
15347
|
+
});
|
|
15348
|
+
};
|
|
15349
|
+
var doesTraceRecoveryPathConflict = (tracePath, collisionTraces) => {
|
|
15350
|
+
for (let pathIndex = 0; pathIndex < tracePath.length - 1; pathIndex++) {
|
|
15351
|
+
const pathStart = tracePath[pathIndex];
|
|
15352
|
+
const pathEnd = tracePath[pathIndex + 1];
|
|
15353
|
+
for (const trace of collisionTraces) {
|
|
15354
|
+
for (let traceIndex = 0; traceIndex < trace.tracePath.length - 1; traceIndex++) {
|
|
15355
|
+
const traceStart = trace.tracePath[traceIndex];
|
|
15356
|
+
const traceEnd = trace.tracePath[traceIndex + 1];
|
|
15357
|
+
if (!doSegmentsIntersect4(pathStart, pathEnd, traceStart, traceEnd)) {
|
|
15358
|
+
continue;
|
|
15359
|
+
}
|
|
15360
|
+
if (isStrictInteriorPerpendicularCrossing({
|
|
15361
|
+
firstSegmentStart: pathStart,
|
|
15362
|
+
firstSegmentEnd: pathEnd,
|
|
15363
|
+
secondSegmentStart: traceStart,
|
|
15364
|
+
secondSegmentEnd: traceEnd
|
|
15365
|
+
})) {
|
|
15366
|
+
continue;
|
|
15367
|
+
}
|
|
15368
|
+
return true;
|
|
15369
|
+
}
|
|
15370
|
+
}
|
|
15371
|
+
}
|
|
15372
|
+
return false;
|
|
15373
|
+
};
|
|
15374
|
+
|
|
15299
15375
|
// lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts
|
|
15300
15376
|
var AVAILABLE_NET_ORIENTATION_PREFIX = "available-net-orientation-";
|
|
15301
15377
|
var RECOVERED_TRACE_PREFIX = "net-label-to-trace-";
|
|
@@ -15566,7 +15642,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15566
15642
|
(trace) => trace.globalConnNetId !== candidate.firstLabel.globalConnNetId
|
|
15567
15643
|
);
|
|
15568
15644
|
}
|
|
15569
|
-
if (
|
|
15645
|
+
if (doesTraceRecoveryPathConflict(tracePath, collisionTraces) || this.routeIntersectsRemainingLabels(tracePath, candidate)) {
|
|
15570
15646
|
return;
|
|
15571
15647
|
}
|
|
15572
15648
|
const [firstPin, secondPin] = candidate.pins;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getTraceRecoveryConnectivityMaps,
|
|
13
13
|
type TraceRecoveryPin,
|
|
14
14
|
} from "lib/solvers/NetLabelTraceRecovery/getTraceRecoveryConnectivityMaps"
|
|
15
|
+
import { doesTraceRecoveryPathConflict } from "lib/solvers/NetLabelTraceRecovery/doesTraceRecoveryPathConflict"
|
|
15
16
|
import { getTraceConnectedPinComponents } from "lib/solvers/SchematicTraceLinesSolver/getTraceConnectedPinComponents"
|
|
16
17
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
17
18
|
import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
|
|
@@ -22,7 +23,6 @@ import type {
|
|
|
22
23
|
PinId,
|
|
23
24
|
} from "lib/types/InputProblem"
|
|
24
25
|
import { arePinsInDifferentSchematicSections } from "lib/utils/arePinsInDifferentSchematicSections"
|
|
25
|
-
import { doesTraceOverlapWithExistingTraces } from "lib/utils/does-trace-overlap-with-existing-traces"
|
|
26
26
|
import {
|
|
27
27
|
type InlineNetLabelPlacement,
|
|
28
28
|
type InlineNetLabelOutput,
|
|
@@ -434,7 +434,7 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
434
434
|
)
|
|
435
435
|
}
|
|
436
436
|
if (
|
|
437
|
-
|
|
437
|
+
doesTraceRecoveryPathConflict(tracePath, collisionTraces) ||
|
|
438
438
|
this.routeIntersectsRemainingLabels(tracePath, candidate)
|
|
439
439
|
) {
|
|
440
440
|
return
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { doSegmentsIntersect, type Point } from "@tscircuit/math-utils"
|
|
2
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
3
|
+
|
|
4
|
+
const EPSILON = 1e-6
|
|
5
|
+
|
|
6
|
+
const isStrictlyBetween = ({
|
|
7
|
+
coordinate,
|
|
8
|
+
segmentStartCoordinate,
|
|
9
|
+
segmentEndCoordinate,
|
|
10
|
+
}: {
|
|
11
|
+
coordinate: number
|
|
12
|
+
segmentStartCoordinate: number
|
|
13
|
+
segmentEndCoordinate: number
|
|
14
|
+
}) =>
|
|
15
|
+
coordinate >
|
|
16
|
+
Math.min(segmentStartCoordinate, segmentEndCoordinate) + EPSILON &&
|
|
17
|
+
coordinate < Math.max(segmentStartCoordinate, segmentEndCoordinate) - EPSILON
|
|
18
|
+
|
|
19
|
+
const isStrictInteriorPerpendicularCrossing = ({
|
|
20
|
+
firstSegmentStart,
|
|
21
|
+
firstSegmentEnd,
|
|
22
|
+
secondSegmentStart,
|
|
23
|
+
secondSegmentEnd,
|
|
24
|
+
}: {
|
|
25
|
+
firstSegmentStart: Point
|
|
26
|
+
firstSegmentEnd: Point
|
|
27
|
+
secondSegmentStart: Point
|
|
28
|
+
secondSegmentEnd: Point
|
|
29
|
+
}) => {
|
|
30
|
+
const firstIsHorizontal =
|
|
31
|
+
Math.abs(firstSegmentStart.y - firstSegmentEnd.y) <= EPSILON
|
|
32
|
+
const firstIsVertical =
|
|
33
|
+
Math.abs(firstSegmentStart.x - firstSegmentEnd.x) <= EPSILON
|
|
34
|
+
const secondIsHorizontal =
|
|
35
|
+
Math.abs(secondSegmentStart.y - secondSegmentEnd.y) <= EPSILON
|
|
36
|
+
const secondIsVertical =
|
|
37
|
+
Math.abs(secondSegmentStart.x - secondSegmentEnd.x) <= EPSILON
|
|
38
|
+
|
|
39
|
+
let horizontalStart: Point
|
|
40
|
+
let horizontalEnd: Point
|
|
41
|
+
if (firstIsHorizontal) {
|
|
42
|
+
horizontalStart = firstSegmentStart
|
|
43
|
+
horizontalEnd = firstSegmentEnd
|
|
44
|
+
} else if (secondIsHorizontal) {
|
|
45
|
+
horizontalStart = secondSegmentStart
|
|
46
|
+
horizontalEnd = secondSegmentEnd
|
|
47
|
+
} else {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let verticalStart: Point
|
|
52
|
+
let verticalEnd: Point
|
|
53
|
+
if (firstIsVertical) {
|
|
54
|
+
verticalStart = firstSegmentStart
|
|
55
|
+
verticalEnd = firstSegmentEnd
|
|
56
|
+
} else if (secondIsVertical) {
|
|
57
|
+
verticalStart = secondSegmentStart
|
|
58
|
+
verticalEnd = secondSegmentEnd
|
|
59
|
+
} else {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
isStrictlyBetween({
|
|
65
|
+
coordinate: verticalStart.x,
|
|
66
|
+
segmentStartCoordinate: horizontalStart.x,
|
|
67
|
+
segmentEndCoordinate: horizontalEnd.x,
|
|
68
|
+
}) &&
|
|
69
|
+
isStrictlyBetween({
|
|
70
|
+
coordinate: horizontalStart.y,
|
|
71
|
+
segmentStartCoordinate: verticalStart.y,
|
|
72
|
+
segmentEndCoordinate: verticalEnd.y,
|
|
73
|
+
})
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const doesTraceRecoveryPathConflict = (
|
|
78
|
+
tracePath: Point[],
|
|
79
|
+
collisionTraces: SolvedTracePath[],
|
|
80
|
+
) => {
|
|
81
|
+
for (let pathIndex = 0; pathIndex < tracePath.length - 1; pathIndex++) {
|
|
82
|
+
const pathStart = tracePath[pathIndex]!
|
|
83
|
+
const pathEnd = tracePath[pathIndex + 1]!
|
|
84
|
+
|
|
85
|
+
for (const trace of collisionTraces) {
|
|
86
|
+
for (
|
|
87
|
+
let traceIndex = 0;
|
|
88
|
+
traceIndex < trace.tracePath.length - 1;
|
|
89
|
+
traceIndex++
|
|
90
|
+
) {
|
|
91
|
+
const traceStart = trace.tracePath[traceIndex]!
|
|
92
|
+
const traceEnd = trace.tracePath[traceIndex + 1]!
|
|
93
|
+
if (!doSegmentsIntersect(pathStart, pathEnd, traceStart, traceEnd)) {
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
if (
|
|
97
|
+
isStrictInteriorPerpendicularCrossing({
|
|
98
|
+
firstSegmentStart: pathStart,
|
|
99
|
+
firstSegmentEnd: pathEnd,
|
|
100
|
+
secondSegmentStart: traceStart,
|
|
101
|
+
secondSegmentEnd: traceEnd,
|
|
102
|
+
})
|
|
103
|
+
) {
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
106
|
+
return true
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return false
|
|
112
|
+
}
|