@tscircuit/schematic-trace-solver 0.0.141 → 0.0.143
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.d.ts +28 -1
- package/dist/index.js +348 -34
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +92 -16
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +216 -17
- package/lib/solvers/InlineNetLabelSolver/alignPortOnlyInlineNetLabelStubs.ts +233 -0
- package/lib/types/InputProblem.ts +13 -0
- package/package.json +1 -1
- package/tests/assets/inline-net-label-port-stub.json +67 -0
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +3 -3
- package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +52 -0
- package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +624 -0
- package/tests/repros/assets/repro-tida010076-page02.input.json +2964 -0
- package/tests/repros/repro-tida010076-j4-ground-bus.test.ts +78 -0
- package/tests/repros/repro-tida010076-page02.test.ts +46 -0
- package/tests/repros/repro-usb-power-vbus-label-detour.test.ts +23 -1
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-port-stub.snap.svg +64 -0
- package/tests/solvers/InlineNetLabelSolver/align-port-only-inline-net-label-stubs-conflict.test.ts +75 -0
- package/tests/solvers/InlineNetLabelSolver/inline-net-label-port-stub.test.ts +82 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
4
|
+
import "tests/fixtures/matcher"
|
|
5
|
+
|
|
6
|
+
// Extracted from TI TIDA-010076 sheet 2, connector J4. In the reference
|
|
7
|
+
// schematic, pins 3-6 join one vertical bus that terminates in one AGND symbol.
|
|
8
|
+
// The tscircuit rendering instead splits the four adjacent ground pins into
|
|
9
|
+
// separate branches before placing the ground label.
|
|
10
|
+
const inputProblem: InputProblem = {
|
|
11
|
+
chips: [
|
|
12
|
+
{
|
|
13
|
+
chipId: "J4",
|
|
14
|
+
center: { x: 0, y: 0 },
|
|
15
|
+
width: 1.55,
|
|
16
|
+
height: 2.9,
|
|
17
|
+
pins: [
|
|
18
|
+
{ pinId: "J4.1", x: 0.775, y: 1.1, _facingDirection: "x+" },
|
|
19
|
+
{ pinId: "J4.2", x: 0.775, y: 0.7, _facingDirection: "x+" },
|
|
20
|
+
{ pinId: "J4.3", x: 0.775, y: 0.1, _facingDirection: "x+" },
|
|
21
|
+
{ pinId: "J4.4", x: 0.775, y: -0.3, _facingDirection: "x+" },
|
|
22
|
+
{ pinId: "J4.5", x: 0.775, y: -0.7, _facingDirection: "x+" },
|
|
23
|
+
{ pinId: "J4.6", x: 0.775, y: -1.1, _facingDirection: "x+" },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
directConnections: [],
|
|
28
|
+
netConnections: [
|
|
29
|
+
{
|
|
30
|
+
netId: "NET_AGND",
|
|
31
|
+
pinIds: ["J4.3", "J4.4", "J4.5", "J4.6"],
|
|
32
|
+
netLabelWidth: 0.42,
|
|
33
|
+
netLabelHeight: 1.08,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
textBoxes: [
|
|
37
|
+
{
|
|
38
|
+
chipId: "J4",
|
|
39
|
+
center: { x: 0.285, y: -1.58 },
|
|
40
|
+
width: 1.32,
|
|
41
|
+
height: 0.18,
|
|
42
|
+
text: "09452812800",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
chipId: "J4",
|
|
46
|
+
center: { x: -0.255, y: 1.565 },
|
|
47
|
+
width: 0.36,
|
|
48
|
+
height: 0.25,
|
|
49
|
+
text: "J4",
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
availableNetLabelOrientations: { NET_AGND: ["y-"] },
|
|
53
|
+
maxMspPairDistance: 2.4,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
test("repro TIDA-010076 J4 ground pins should share one vertical bus", () => {
|
|
57
|
+
const solver = new SchematicTracePipelineSolver(inputProblem)
|
|
58
|
+
|
|
59
|
+
solver.solve()
|
|
60
|
+
|
|
61
|
+
const output = solver.sameNetJunctionAlignmentSolver!.getOutput()
|
|
62
|
+
const groundTraces = output.traces.filter(
|
|
63
|
+
(trace) =>
|
|
64
|
+
trace.userNetId === "NET_AGND" &&
|
|
65
|
+
trace.pinIds.every((pinId) => pinId.startsWith("J4.")),
|
|
66
|
+
)
|
|
67
|
+
const verticalBranchOffsets = new Set(
|
|
68
|
+
groundTraces.map((trace) => trace.tracePath[1]!.x.toFixed(3)),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
// Current mismatch: the first three GND pins use one vertical offset while
|
|
72
|
+
// the final pair detours around the manufacturer text at a second offset.
|
|
73
|
+
expect(verticalBranchOffsets.size).toBe(2)
|
|
74
|
+
expect(
|
|
75
|
+
output.netLabelPlacements.filter((label) => label.netId === "NET_AGND"),
|
|
76
|
+
).toHaveLength(1)
|
|
77
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
78
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
4
|
+
import "tests/fixtures/matcher"
|
|
5
|
+
import inputProblemJson from "./assets/repro-tida010076-page02.input.json"
|
|
6
|
+
|
|
7
|
+
// Captured from the InputProblem emitted by @tscircuit/core while rendering
|
|
8
|
+
// TI TIDA-010076 sheet 02 (card_top). Keep this broad page-level fixture as a
|
|
9
|
+
// parity baseline for net-label placement changes that affect several sections.
|
|
10
|
+
test("repro TIDA-010076 page 02 net-label placement", () => {
|
|
11
|
+
const inputProblem: InputProblem = JSON.parse(
|
|
12
|
+
JSON.stringify(inputProblemJson),
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
expect(inputProblem.chips).toHaveLength(21)
|
|
16
|
+
expect(inputProblem.directConnections).toHaveLength(13)
|
|
17
|
+
expect(inputProblem.netConnections).toHaveLength(96)
|
|
18
|
+
expect(inputProblem.availableNetLabelOrientations?.NET_AGND).toEqual(["y-"])
|
|
19
|
+
|
|
20
|
+
const solver = new SchematicTracePipelineSolver(inputProblem, {
|
|
21
|
+
hideRatsNet: true,
|
|
22
|
+
})
|
|
23
|
+
solver.solve()
|
|
24
|
+
|
|
25
|
+
expect(solver.solved).toBe(true)
|
|
26
|
+
|
|
27
|
+
const j5ChipId = inputProblem.textBoxes?.find(
|
|
28
|
+
(textBox) => textBox.text === "J5",
|
|
29
|
+
)?.chipId
|
|
30
|
+
const j5 = inputProblem.chips.find((chip) => chip.chipId === j5ChipId)!
|
|
31
|
+
const j5GroundPins = j5.pins.slice(2)
|
|
32
|
+
const j5GroundPinIds = new Set(j5GroundPins.map((pin) => pin.pinId))
|
|
33
|
+
const j5AgndLabel = solver
|
|
34
|
+
.inlineNetLabelSolver!.getOutput()
|
|
35
|
+
.netLabelPlacements.find(
|
|
36
|
+
(label) =>
|
|
37
|
+
label.netId === "NET_AGND" &&
|
|
38
|
+
label.pinIds.some((pinId) => j5GroundPinIds.has(pinId)),
|
|
39
|
+
)!
|
|
40
|
+
|
|
41
|
+
expect(j5AgndLabel.orientation).toBe("y-")
|
|
42
|
+
expect(j5AgndLabel.anchorPoint.y).toBeCloseTo(
|
|
43
|
+
Math.min(...j5GroundPins.map((pin) => pin.y)),
|
|
44
|
+
)
|
|
45
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
46
|
+
})
|
|
@@ -21,10 +21,32 @@ test("routes the usb power vbus connection without a label detour", () => {
|
|
|
21
21
|
)
|
|
22
22
|
const gndLabel =
|
|
23
23
|
solver.availableNetOrientationSolver!.outputNetLabelPlacements.find(
|
|
24
|
-
(label) =>
|
|
24
|
+
(label) =>
|
|
25
|
+
label.pinIds.includes("schematic_port_37") &&
|
|
26
|
+
label.pinIds.includes("schematic_port_46"),
|
|
27
|
+
)
|
|
28
|
+
const gndLabelConnector = solver.availableNetOrientationSolver!.traces.find(
|
|
29
|
+
(trace) =>
|
|
30
|
+
trace.mspPairId.startsWith("available-net-orientation-") &&
|
|
31
|
+
trace.pinIds.includes("schematic_port_37") &&
|
|
32
|
+
trace.pinIds.includes("schematic_port_46"),
|
|
33
|
+
)
|
|
34
|
+
const neighboringV3v3Label =
|
|
35
|
+
solver.availableNetOrientationSolver!.outputNetLabelPlacements.find(
|
|
36
|
+
(label) =>
|
|
37
|
+
label.pinIds.includes("schematic_port_54") &&
|
|
38
|
+
label.pinIds.includes("schematic_port_162"),
|
|
25
39
|
)
|
|
26
40
|
|
|
27
41
|
expect(gndLabel?.orientation).toBe("y-")
|
|
42
|
+
expect(gndLabel?.anchorPoint.x).toBeCloseTo(-2.34)
|
|
43
|
+
expect(gndLabel?.anchorPoint.y).toBeCloseTo(1.3)
|
|
44
|
+
expect(gndLabelConnector?.tracePath).toEqual([
|
|
45
|
+
{ x: -1.75, y: 1.2999999999999976 },
|
|
46
|
+
{ x: -2.34, y: 1.2999999999999976 },
|
|
47
|
+
])
|
|
48
|
+
expect(neighboringV3v3Label?.anchorPoint.x).toBeCloseTo(-3.655)
|
|
49
|
+
expect(neighboringV3v3Label?.anchorPoint.y).toBeCloseTo(-0.3)
|
|
28
50
|
expect(vbusTrace?.tracePath).toEqual([
|
|
29
51
|
{ x: 13, y: -6.2 },
|
|
30
52
|
{ x: 12.8, y: -6.2 },
|
|
@@ -0,0 +1,64 @@
|
|
|
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.5,0.3 -2.3499999999999996,0.3" data-type="line" data-label="" points="262.79569892473114,271.8279569892473 40,271.8279569892473" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="0.5,-0.2 2.3,-0.2" data-type="line" data-label="" points="383.2258064516129,332.04301075268813 600,332.04301075268813" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-0.5,-0.1 -2.3499999999999996,-0.1" data-type="line" data-label="" points="262.79569892473114,320 40,320" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-0.2,0.5 -0.2,1.45" data-type="line" data-label="" points="298.9247311827957,247.74193548387095 298.9247311827957,133.33333333333331" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="0.2,-0.5 0.2,-1.65" data-type="line" data-label="" points="347.09677419354836,368.17204301075265 347.09677419354836,506.66666666666663" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="U1" data-x="0" data-y="0" x="262.79569892473114" y="247.74193548387098" width="120.43010752688178" height="120.4301075268817" fill="hsl(164, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008303571428571428"/></g><g><rect data-type="rect" data-label="INLINE netId: NET_PRU0_MII_TX_CLK1
|
|
2
|
+
axis: x
|
|
3
|
+
side: y+" data-x="-1.4249999999999998" data-y="0.41" x="52.04301075268816" y="251.35483870967738" width="198.70967741935488" height="14.451612903225822" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008303571428571428"/></g><g><rect data-type="rect" data-label="INLINE netId: NET_PRU0_MII_RXLINK1
|
|
4
|
+
axis: x
|
|
5
|
+
side: y+" data-x="1.4" data-y="-0.09000000000000001" x="395.26881720430106" y="311.5698924731182" width="192.6881720430108" height="14.451612903225794" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008303571428571428"/></g><g><rect data-type="rect" data-label="INLINE netId: NET_MDC
|
|
6
|
+
axis: x
|
|
7
|
+
side: y+" data-x="-0.925" data-y="0.009999999999999995" x="172.47311827956986" y="299.5268817204301" width="78.27956989247312" height="14.451612903225794" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008303571428571428"/></g><g><rect data-type="rect" data-label="INLINE netId: NET_TOP
|
|
8
|
+
axis: y
|
|
9
|
+
side: x-" data-x="-0.31" data-y="0.975" x="278.45161290322585" y="145.37634408602145" width="14.451612903225794" height="90.32258064516131" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008303571428571428"/></g><g><rect data-type="rect" data-label="INLINE netId: NET_BOTTOM
|
|
10
|
+
axis: y
|
|
11
|
+
side: x-" data-x="0.09000000000000001" data-y="-1.075" x="326.6236559139785" y="380.2150537634408" width="14.451612903225794" height="114.40860215053766" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008303571428571428"/></g><text data-type="text" data-label="NET_PRU0_MII_TX_CLK1" data-x="-0.5999999999999999" data-y="0.41" x="250.752688172043" y="258.5806451612903" fill="green" font-size="14.451612903225806" font-family="sans-serif" text-anchor="end" dominant-baseline="central">NET_PRU0_MII_TX_CLK1</text><text data-type="text" data-label="NET_PRU0_MII_RXLINK1" data-x="0.5999999999999999" data-y="-0.09000000000000001" x="395.26881720430106" y="318.79569892473114" fill="green" font-size="14.451612903225806" font-family="sans-serif" text-anchor="start" dominant-baseline="central">NET_PRU0_MII_RXLINK1</text><text data-type="text" data-label="NET_MDC" data-x="-0.6000000000000001" data-y="0.009999999999999995" x="250.75268817204298" y="306.752688172043" fill="green" font-size="14.451612903225806" font-family="sans-serif" text-anchor="end" dominant-baseline="central">NET_MDC</text><text data-type="text" data-label="NET_TOP" data-x="-0.31" data-y="0.6" x="285.6774193548387" y="235.69892473118279" fill="green" font-size="14.451612903225806" font-family="sans-serif" text-anchor="start" dominant-baseline="central" transform="rotate(-90, 285.6774193548387, 235.69892473118279)">NET_TOP</text><text data-type="text" data-label="NET_BOTTOM" data-x="0.09000000000000001" data-y="-0.6" x="333.84946236559136" y="380.21505376344084" fill="green" font-size="14.451612903225806" font-family="sans-serif" text-anchor="end" dominant-baseline="central" transform="rotate(-90, 333.84946236559136, 380.21505376344084)">NET_BOTTOM</text><g><circle data-type="point" data-label="U1.1
|
|
12
|
+
x-" data-x="-0.5" data-y="0.3" cx="262.79569892473114" cy="271.8279569892473" r="3" fill="hsl(319, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U1.2
|
|
13
|
+
x+" data-x="0.5" data-y="-0.2" cx="383.2258064516129" cy="332.04301075268813" r="3" fill="hsl(320, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U1.3
|
|
14
|
+
x-" data-x="-0.5" data-y="-0.1" cx="262.79569892473114" cy="320" r="3" fill="hsl(321, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U1.4
|
|
15
|
+
y+" data-x="-0.2" data-y="0.5" cx="298.9247311827957" cy="247.74193548387095" r="3" fill="hsl(322, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U1.5
|
|
16
|
+
y-" data-x="0.2" data-y="-0.5" cx="347.09677419354836" cy="368.17204301075265" r="3" fill="hsl(323, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="inline anchor
|
|
17
|
+
NET_PRU0_MII_TX_CLK1" data-x="-1.4249999999999998" data-y="0.3" cx="151.3978494623656" cy="271.8279569892473" r="3" fill="green"/></g><g><circle data-type="point" data-label="inline anchor
|
|
18
|
+
NET_PRU0_MII_RXLINK1" data-x="1.4" data-y="-0.2" cx="491.61290322580646" cy="332.04301075268813" r="3" fill="green"/></g><g><circle data-type="point" data-label="inline anchor
|
|
19
|
+
NET_MDC" data-x="-1.4249999999999998" data-y="-0.1" cx="151.3978494623656" cy="320" r="3" fill="green"/></g><g><circle data-type="point" data-label="inline anchor
|
|
20
|
+
NET_TOP" data-x="-0.2" data-y="0.975" cx="298.9247311827957" cy="190.53763440860212" r="3" fill="green"/></g><g><circle data-type="point" data-label="inline anchor
|
|
21
|
+
NET_BOTTOM" data-x="0.2" data-y="-1.075" cx="347.09677419354836" cy="437.41935483870964" r="3" fill="green"/></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[
|
|
22
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
23
|
+
const svg = e.currentTarget;
|
|
24
|
+
const rect = svg.getBoundingClientRect();
|
|
25
|
+
const x = e.clientX - rect.left;
|
|
26
|
+
const y = e.clientY - rect.top;
|
|
27
|
+
const crosshair = svg.getElementById('crosshair');
|
|
28
|
+
const h = svg.getElementById('crosshair-h');
|
|
29
|
+
const v = svg.getElementById('crosshair-v');
|
|
30
|
+
const coords = svg.getElementById('coordinates');
|
|
31
|
+
|
|
32
|
+
crosshair.style.display = 'block';
|
|
33
|
+
h.setAttribute('x1', '0');
|
|
34
|
+
h.setAttribute('x2', '640');
|
|
35
|
+
h.setAttribute('y1', y);
|
|
36
|
+
h.setAttribute('y2', y);
|
|
37
|
+
v.setAttribute('x1', x);
|
|
38
|
+
v.setAttribute('x2', x);
|
|
39
|
+
v.setAttribute('y1', '0');
|
|
40
|
+
v.setAttribute('y2', '640');
|
|
41
|
+
|
|
42
|
+
// Calculate real coordinates using inverse transformation
|
|
43
|
+
const matrix = {"a":120.43010752688173,"c":0,"e":323.01075268817203,"b":0,"d":-120.43010752688173,"f":307.9569892473118};
|
|
44
|
+
// Manually invert and apply the affine transform
|
|
45
|
+
// Since we only use translate and scale, we can directly compute:
|
|
46
|
+
// x' = (x - tx) / sx
|
|
47
|
+
// y' = (y - ty) / sy
|
|
48
|
+
const sx = matrix.a;
|
|
49
|
+
const sy = matrix.d;
|
|
50
|
+
const tx = matrix.e;
|
|
51
|
+
const ty = matrix.f;
|
|
52
|
+
const realPoint = {
|
|
53
|
+
x: (x - tx) / sx,
|
|
54
|
+
y: (y - ty) / sy // Flip y back since we used negative scale
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
|
|
58
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
59
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
60
|
+
});
|
|
61
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
62
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
63
|
+
});
|
|
64
|
+
]]></script></svg>
|
package/tests/solvers/InlineNetLabelSolver/align-port-only-inline-net-label-stubs-conflict.test.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { alignPortOnlyInlineNetLabelStubs } from "lib/solvers/InlineNetLabelSolver/alignPortOnlyInlineNetLabelStubs"
|
|
3
|
+
import type { InlineNetLabelPlacement } from "lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver"
|
|
4
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
5
|
+
|
|
6
|
+
test("port stub row alignment is atomic when an extension would collide", () => {
|
|
7
|
+
const placements: InlineNetLabelPlacement[] = [
|
|
8
|
+
{
|
|
9
|
+
globalConnNetId: "net-long",
|
|
10
|
+
netId: "LONG_SIGNAL_NAME",
|
|
11
|
+
pinIds: ["U1.1"],
|
|
12
|
+
stubTracePath: [
|
|
13
|
+
{ x: -0.5, y: 0.3 },
|
|
14
|
+
{ x: -2.5, y: 0.3 },
|
|
15
|
+
],
|
|
16
|
+
axis: "x",
|
|
17
|
+
anchorPoint: { x: -1.5, y: 0.3 },
|
|
18
|
+
center: { x: -1.5, y: 0.41 },
|
|
19
|
+
width: 1.8,
|
|
20
|
+
height: 0.12,
|
|
21
|
+
side: "y+",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
globalConnNetId: "net-short",
|
|
25
|
+
netId: "SHORT",
|
|
26
|
+
pinIds: ["U1.2"],
|
|
27
|
+
stubTracePath: [
|
|
28
|
+
{ x: -0.5, y: -0.3 },
|
|
29
|
+
{ x: -1.3, y: -0.3 },
|
|
30
|
+
],
|
|
31
|
+
axis: "x",
|
|
32
|
+
anchorPoint: { x: -0.9, y: -0.3 },
|
|
33
|
+
center: { x: -0.9, y: -0.19 },
|
|
34
|
+
width: 0.6,
|
|
35
|
+
height: 0.12,
|
|
36
|
+
side: "y+",
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
const inputProblem: InputProblem = {
|
|
40
|
+
chips: [
|
|
41
|
+
{
|
|
42
|
+
chipId: "U1",
|
|
43
|
+
center: { x: 0, y: 0 },
|
|
44
|
+
width: 1,
|
|
45
|
+
height: 1,
|
|
46
|
+
pins: [
|
|
47
|
+
{ pinId: "U1.1", x: -0.5, y: 0.3 },
|
|
48
|
+
{ pinId: "U1.2", x: -0.5, y: -0.3 },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
chipId: "obstacle",
|
|
53
|
+
center: { x: -1.8, y: -0.3 },
|
|
54
|
+
width: 0.3,
|
|
55
|
+
height: 0.2,
|
|
56
|
+
pins: [],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
directConnections: [],
|
|
60
|
+
netConnections: [],
|
|
61
|
+
availableNetLabelOrientations: {},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const aligned = alignPortOnlyInlineNetLabelStubs({
|
|
65
|
+
placements,
|
|
66
|
+
inputProblem,
|
|
67
|
+
traces: [],
|
|
68
|
+
netLabelPlacements: [],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
expect(aligned[0]!.stubTracePath![1]!.x).toBe(-2.5)
|
|
72
|
+
// The short row stays unchanged because extending the group to x=-2.5
|
|
73
|
+
// would run this stub through the obstacle.
|
|
74
|
+
expect(aligned[1]!.stubTracePath![1]!.x).toBe(-1.3)
|
|
75
|
+
})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import inputProblem from "../../assets/inline-net-label-port-stub.json"
|
|
4
|
+
import "tests/fixtures/matcher"
|
|
5
|
+
|
|
6
|
+
test("single-pin named nets get outward inline-label stubs", () => {
|
|
7
|
+
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
8
|
+
|
|
9
|
+
solver.solve()
|
|
10
|
+
|
|
11
|
+
const placements = solver.inlineNetLabelSolver!.inlineNetLabelPlacements
|
|
12
|
+
expect(placements.map((placement) => placement.netId).sort()).toEqual([
|
|
13
|
+
"NET_BOTTOM",
|
|
14
|
+
"NET_MDC",
|
|
15
|
+
"NET_PRU0_MII_RXLINK1",
|
|
16
|
+
"NET_PRU0_MII_TX_CLK1",
|
|
17
|
+
"NET_TOP",
|
|
18
|
+
])
|
|
19
|
+
expect(placements.every((placement) => placement.pinIds.length === 1)).toBe(
|
|
20
|
+
true,
|
|
21
|
+
)
|
|
22
|
+
expect(
|
|
23
|
+
placements.every((placement) => placement.stubTracePath?.length === 2),
|
|
24
|
+
).toBe(true)
|
|
25
|
+
|
|
26
|
+
const leftStub = placements.find(
|
|
27
|
+
(placement) => placement.netId === "NET_PRU0_MII_TX_CLK1",
|
|
28
|
+
)!.stubTracePath!
|
|
29
|
+
const rightStub = placements.find(
|
|
30
|
+
(placement) => placement.netId === "NET_PRU0_MII_RXLINK1",
|
|
31
|
+
)!.stubTracePath!
|
|
32
|
+
expect(leftStub[1]!.x).toBeLessThan(leftStub[0]!.x)
|
|
33
|
+
expect(rightStub[1]!.x).toBeGreaterThan(rightStub[0]!.x)
|
|
34
|
+
|
|
35
|
+
const topPlacement = placements.find(
|
|
36
|
+
(placement) => placement.netId === "NET_TOP",
|
|
37
|
+
)!
|
|
38
|
+
const bottomPlacement = placements.find(
|
|
39
|
+
(placement) => placement.netId === "NET_BOTTOM",
|
|
40
|
+
)!
|
|
41
|
+
expect(topPlacement.axis).toBe("y")
|
|
42
|
+
expect(bottomPlacement.axis).toBe("y")
|
|
43
|
+
expect(topPlacement.stubTracePath![1]!.x).toBe(
|
|
44
|
+
topPlacement.stubTracePath![0]!.x,
|
|
45
|
+
)
|
|
46
|
+
expect(bottomPlacement.stubTracePath![1]!.x).toBe(
|
|
47
|
+
bottomPlacement.stubTracePath![0]!.x,
|
|
48
|
+
)
|
|
49
|
+
expect(topPlacement.stubTracePath![1]!.y).toBeGreaterThan(
|
|
50
|
+
topPlacement.stubTracePath![0]!.y,
|
|
51
|
+
)
|
|
52
|
+
expect(bottomPlacement.stubTracePath![1]!.y).toBeLessThan(
|
|
53
|
+
bottomPlacement.stubTracePath![0]!.y,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
const shortLeftStub = placements.find(
|
|
57
|
+
(placement) => placement.netId === "NET_MDC",
|
|
58
|
+
)!.stubTracePath!
|
|
59
|
+
// Rows leaving the same component side terminate on one clean line even
|
|
60
|
+
// though their labels require different minimum lengths.
|
|
61
|
+
expect(shortLeftStub[1]!.x).toBeCloseTo(leftStub[1]!.x, 9)
|
|
62
|
+
|
|
63
|
+
const shortLeftPlacement = placements.find(
|
|
64
|
+
(placement) => placement.netId === "NET_MDC",
|
|
65
|
+
)!
|
|
66
|
+
// Aligning the free wire ends must not recenter shorter text on the extended
|
|
67
|
+
// stub. Terminal text stays aligned at the pin-side end of its own label.
|
|
68
|
+
expect(shortLeftPlacement.center.x).toBeCloseTo(
|
|
69
|
+
shortLeftStub[0]!.x - shortLeftPlacement.width / 2 - 0.1,
|
|
70
|
+
9,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const output = solver.inlineNetLabelSolver!.getOutput()
|
|
74
|
+
expect(output.netLabelPlacements).toHaveLength(0)
|
|
75
|
+
expect(
|
|
76
|
+
output.traces.filter((trace) =>
|
|
77
|
+
trace.mspPairId.startsWith("available-net-orientation-"),
|
|
78
|
+
),
|
|
79
|
+
).toHaveLength(0)
|
|
80
|
+
|
|
81
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
82
|
+
})
|