@tscircuit/schematic-trace-solver 0.0.103 → 0.0.105
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 +37 -14
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +45 -15
- package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +7 -3
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260721T221026Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260708T053736Z/__snapshots__/bug-report-20260708T053736Z.snap.svg +2 -6
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +345 -0
- package/tests/bug-reports/bug-report-20260721T221026Z/bug-report-20260721T221026Z.json +1750 -0
- package/tests/bug-reports/bug-report-20260721T221026Z/bug-report-20260721T221026Z.test.ts +12 -0
- package/tests/examples/__snapshots__/example13.snap.svg +3 -3
- package/tests/examples/__snapshots__/example19.snap.svg +3 -3
- package/tests/examples/__snapshots__/example29.snap.svg +23 -23
- package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-core-subcircuit-missing-ground.snap.svg +56 -0
- package/tests/repros/__snapshots__/repro-vcc-pin1-detour.snap.svg +47 -0
- package/tests/repros/assets/repro-core-subcircuit-missing-ground.input.json +103 -0
- package/tests/repros/repro-core-subcircuit-missing-ground.test.ts +14 -0
- package/tests/repros/repro-vcc-pin1-detour.test.ts +166 -0
package/dist/index.js
CHANGED
|
@@ -1051,6 +1051,23 @@ var getObstacleRects = (problem, opts = {}) => {
|
|
|
1051
1051
|
};
|
|
1052
1052
|
|
|
1053
1053
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
|
|
1054
|
+
var calculateElbowForPins = ({
|
|
1055
|
+
pin1,
|
|
1056
|
+
pin2,
|
|
1057
|
+
overshoot
|
|
1058
|
+
}) => calculateElbow2(
|
|
1059
|
+
{
|
|
1060
|
+
x: pin1.x,
|
|
1061
|
+
y: pin1.y,
|
|
1062
|
+
facingDirection: pin1._facingDirection
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
x: pin2.x,
|
|
1066
|
+
y: pin2.y,
|
|
1067
|
+
facingDirection: pin2._facingDirection
|
|
1068
|
+
},
|
|
1069
|
+
{ overshoot }
|
|
1070
|
+
);
|
|
1054
1071
|
var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
1055
1072
|
pins;
|
|
1056
1073
|
connectionPair;
|
|
@@ -1090,19 +1107,25 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1090
1107
|
);
|
|
1091
1108
|
const [pin1, pin2] = this.pins;
|
|
1092
1109
|
const directShortPath = calculateDirectShortPath(pin1, pin2);
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1110
|
+
const defaultElbow = calculateElbowForPins({
|
|
1111
|
+
pin1,
|
|
1112
|
+
pin2,
|
|
1113
|
+
overshoot: 0.2
|
|
1114
|
+
});
|
|
1115
|
+
const routingDistance = Math.abs(pin1.x - pin2.x) + Math.abs(pin1.y - pin2.y);
|
|
1116
|
+
const adaptiveElbow = calculateElbowForPins({
|
|
1117
|
+
pin1,
|
|
1118
|
+
pin2,
|
|
1119
|
+
overshoot: Math.min(0.2, Math.max(0.02, routingDistance / 4))
|
|
1120
|
+
});
|
|
1121
|
+
const shouldUseAdaptiveElbow = findFirstCollision(defaultElbow, this.obstacles) !== null && findFirstCollision(adaptiveElbow, this.obstacles) === null;
|
|
1122
|
+
this.baseElbow = defaultElbow;
|
|
1123
|
+
if (shouldUseAdaptiveElbow) {
|
|
1124
|
+
this.baseElbow = adaptiveElbow;
|
|
1125
|
+
}
|
|
1126
|
+
if (directShortPath) {
|
|
1127
|
+
this.baseElbow = directShortPath;
|
|
1128
|
+
}
|
|
1106
1129
|
this.solvedTracePath = directShortPath;
|
|
1107
1130
|
this.aabb = aabbFromPoints(
|
|
1108
1131
|
{ x: pin1.x, y: pin1.y },
|
|
@@ -4767,7 +4790,7 @@ var minimizeTurnsWithFilteredLabels = ({
|
|
|
4767
4790
|
throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
|
|
4768
4791
|
}
|
|
4769
4792
|
const obstacleTraces = traces.filter(
|
|
4770
|
-
(t) => t.mspPairId !== targetMspConnectionPairId
|
|
4793
|
+
(t) => t.mspPairId !== targetMspConnectionPairId && t.globalConnNetId !== targetTrace.globalConnNetId
|
|
4771
4794
|
);
|
|
4772
4795
|
const TRACE_WIDTH = 0.01;
|
|
4773
4796
|
const traceObstacles = obstacleTraces.flatMap(
|
|
@@ -28,6 +28,29 @@ import { getObstacleRects, type ObstacleRect } from "./rect"
|
|
|
28
28
|
|
|
29
29
|
type PathKey = string
|
|
30
30
|
|
|
31
|
+
const calculateElbowForPins = ({
|
|
32
|
+
pin1,
|
|
33
|
+
pin2,
|
|
34
|
+
overshoot,
|
|
35
|
+
}: {
|
|
36
|
+
pin1: MspConnectionPair["pins"][number]
|
|
37
|
+
pin2: MspConnectionPair["pins"][number]
|
|
38
|
+
overshoot: number
|
|
39
|
+
}) =>
|
|
40
|
+
calculateElbow(
|
|
41
|
+
{
|
|
42
|
+
x: pin1.x,
|
|
43
|
+
y: pin1.y,
|
|
44
|
+
facingDirection: pin1._facingDirection!,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
x: pin2.x,
|
|
48
|
+
y: pin2.y,
|
|
49
|
+
facingDirection: pin2._facingDirection!,
|
|
50
|
+
},
|
|
51
|
+
{ overshoot },
|
|
52
|
+
)
|
|
53
|
+
|
|
31
54
|
export class SchematicTraceSingleLineSolver2 extends BaseSolver {
|
|
32
55
|
pins: MspConnectionPair["pins"]
|
|
33
56
|
connectionPair?: MspConnectionPair
|
|
@@ -89,23 +112,30 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver {
|
|
|
89
112
|
|
|
90
113
|
const [pin1, pin2] = this.pins
|
|
91
114
|
const directShortPath = calculateDirectShortPath(pin1, pin2)
|
|
115
|
+
const defaultElbow = calculateElbowForPins({
|
|
116
|
+
pin1,
|
|
117
|
+
pin2,
|
|
118
|
+
overshoot: 0.2,
|
|
119
|
+
})
|
|
120
|
+
const routingDistance =
|
|
121
|
+
Math.abs(pin1.x - pin2.x) + Math.abs(pin1.y - pin2.y)
|
|
122
|
+
const adaptiveElbow = calculateElbowForPins({
|
|
123
|
+
pin1,
|
|
124
|
+
pin2,
|
|
125
|
+
overshoot: Math.min(0.2, Math.max(0.02, routingDistance / 4)),
|
|
126
|
+
})
|
|
127
|
+
const shouldUseAdaptiveElbow =
|
|
128
|
+
findFirstCollision(defaultElbow, this.obstacles) !== null &&
|
|
129
|
+
findFirstCollision(adaptiveElbow, this.obstacles) === null
|
|
92
130
|
|
|
93
131
|
// Build initial elbow path
|
|
94
|
-
this.baseElbow =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
x: pin2.x,
|
|
104
|
-
y: pin2.y,
|
|
105
|
-
facingDirection: pin2._facingDirection!,
|
|
106
|
-
},
|
|
107
|
-
{ overshoot: 0.2 },
|
|
108
|
-
)
|
|
132
|
+
this.baseElbow = defaultElbow
|
|
133
|
+
if (shouldUseAdaptiveElbow) {
|
|
134
|
+
this.baseElbow = adaptiveElbow
|
|
135
|
+
}
|
|
136
|
+
if (directShortPath) {
|
|
137
|
+
this.baseElbow = directShortPath
|
|
138
|
+
}
|
|
109
139
|
this.solvedTracePath = directShortPath
|
|
110
140
|
|
|
111
141
|
// Bounds defined by PA and PB
|
|
@@ -5,8 +5,8 @@ import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/Schemati
|
|
|
5
5
|
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Minimizes the turns of a target trace while considering
|
|
9
|
-
* This function first identifies the target trace and separates
|
|
8
|
+
* Minimizes the turns of a target trace while considering different-net traces and labels as obstacles.
|
|
9
|
+
* This function first identifies the target trace and separates traces from other nets, which are then treated as obstacles.
|
|
10
10
|
* It also filters out labels that belong to the target trace's net, so they don't act as obstacles.
|
|
11
11
|
* The function then combines static obstacles (from the input problem) with the other traces and filtered labels to create a comprehensive set of obstacles.
|
|
12
12
|
* Finally, it uses a turn minimization algorithm to find a new path for the target trace that avoids these combined obstacles.
|
|
@@ -33,8 +33,12 @@ export const minimizeTurnsWithFilteredLabels = ({
|
|
|
33
33
|
throw new Error(`Target trace ${targetMspConnectionPairId} not found`)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// Same-net traces are valid connection targets: letting the candidate path
|
|
37
|
+
// coincide with them removes unnecessary detours and forms clean junctions.
|
|
36
38
|
const obstacleTraces = traces.filter(
|
|
37
|
-
(t) =>
|
|
39
|
+
(t) =>
|
|
40
|
+
t.mspPairId !== targetMspConnectionPairId &&
|
|
41
|
+
t.globalConnNetId !== targetTrace.globalConnNetId,
|
|
38
42
|
)
|
|
39
43
|
|
|
40
44
|
const TRACE_WIDTH = 0.01
|
package/package.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="0.5279125,3.0973754500000004 0.7,3" data-type="line" data-label="" points="363.46296663543797,319.86511717601365 416.18376153125485,349.6970987396827" fill="none" stroke="hsl(72, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="1" data-y="3" x="416.18376153125485" y="245.5345636073937" width="183.81623846874515" height="208.32507026457802" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="0" data-y="3.1" x="40" y="186.14036612802857" width="323.46296663543797" height="265.8413857337265" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><
|
|
2
|
-
globalConnNetId: connectivity_net0" data-x="0.7539125" data-y="3.0973754500000004" x="363.7693270328858" y="289.2290774312228" width="137.86217885155884" height="61.272079489581756" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><rect data-type="rect" data-label="netId: .L1 > .pin2 to .R1 > .pin1
|
|
3
|
-
globalConnNetId: connectivity_net0" data-x="0.474" data-y="3" x="278.01522228224815" y="319.0610589948918" width="137.86217885155884" height="61.272079489581756" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><circle data-type="point" data-label="R1.1
|
|
1
|
+
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="0.5279125,3.0973754500000004 0.7,3" data-type="line" data-label="" points="363.46296663543797,319.86511717601365 416.18376153125485,349.6970987396827" fill="none" stroke="hsl(72, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="0.5279125,3.0973754500000004 0.61395625,3.0973754500000004 0.61395625,3 0.7,3" data-type="line" data-label="" points="363.46296663543797,319.86511717601365 389.8233640833464,319.86511717601365 389.8233640833464,349.6970987396827 416.18376153125485,349.6970987396827" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="1" data-y="3" x="416.18376153125485" y="245.5345636073937" width="183.81623846874515" height="208.32507026457802" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="0" data-y="3.1" x="40" y="186.14036612802857" width="323.46296663543797" height="265.8413857337265" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0032641294642857146"/></g><g><circle data-type="point" data-label="R1.1
|
|
4
2
|
x-" data-x="0.7" data-y="3" cx="416.18376153125485" cy="349.6970987396827" r="3" fill="hsl(226, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="R1.2
|
|
5
3
|
x+" data-x="1.3" data-y="3" cx="600" cy="349.6970987396827" r="3" fill="hsl(227, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="L1.1
|
|
6
4
|
x-" data-x="-0.5279125" data-y="3.10262455" cx="40.00000000000003" cy="318.25700081377" r="3" fill="hsl(40, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="L1.2
|
|
7
|
-
x+" data-x="0.5279125" data-y="3.0973754500000004" cx="363.46296663543797" cy="319.86511717601365" r="3" fill="hsl(41, 100%, 50%, 0.8)"/></g><g><
|
|
8
|
-
orientation: x+" data-x="0.5279125" data-y="3.0973754500000004" cx="363.46296663543797" cy="319.86511717601365" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
9
|
-
orientation: x-" data-x="0.7" data-y="3" cx="416.18376153125485" cy="349.6970987396827" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
5
|
+
x+" data-x="0.5279125" data-y="3.0973754500000004" cx="363.46296663543797" cy="319.86511717601365" r="3" fill="hsl(41, 100%, 50%, 0.8)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
10
6
|
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
11
7
|
const svg = e.currentTarget;
|
|
12
8
|
const rect = svg.getBoundingClientRect();
|