@tscircuit/schematic-trace-solver 0.0.147 → 0.0.149

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.
@@ -0,0 +1,136 @@
1
+ import { expect, test } from "bun:test"
2
+ import { doSegmentsIntersect } from "@tscircuit/math-utils"
3
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
4
+ import type { InputProblem } from "lib/types/InputProblem"
5
+ import "tests/fixtures/matcher"
6
+
7
+ // Reduced from @tscircuit/core's schematic-section-2 fixture. The BTN_IN trace
8
+ // crosses the GND trace immediately below SW1.
9
+ const inputProblem: InputProblem = {
10
+ chips: [
11
+ {
12
+ chipId: "schematic_component_0",
13
+ center: { x: -4, y: 3 },
14
+ width: 1,
15
+ height: 0.65,
16
+ pins: [
17
+ {
18
+ pinId: "schematic_port_0",
19
+ x: -4.5,
20
+ y: 3,
21
+ _facingDirection: "x-",
22
+ },
23
+ {
24
+ pinId: "schematic_port_1",
25
+ x: -3.5,
26
+ y: 3,
27
+ _facingDirection: "x+",
28
+ },
29
+ ],
30
+ sectionId: "power",
31
+ },
32
+ {
33
+ chipId: "schematic_component_1",
34
+ center: { x: -2, y: 3 },
35
+ width: 0.76,
36
+ height: 0.5,
37
+ pins: [
38
+ {
39
+ pinId: "schematic_port_2",
40
+ x: -2.38,
41
+ y: 3,
42
+ _facingDirection: "x-",
43
+ },
44
+ {
45
+ pinId: "schematic_port_3",
46
+ x: -1.62,
47
+ y: 3,
48
+ _facingDirection: "x+",
49
+ },
50
+ ],
51
+ sectionId: "power",
52
+ },
53
+ {
54
+ chipId: "schematic_component_2",
55
+ center: { x: -3, y: 1.5 },
56
+ width: 0.6,
57
+ height: 0.84,
58
+ pins: [
59
+ {
60
+ pinId: "schematic_port_4",
61
+ x: -3.3,
62
+ y: 1.5,
63
+ _facingDirection: "x-",
64
+ },
65
+ {
66
+ pinId: "schematic_port_5",
67
+ x: -2.7,
68
+ y: 1.5,
69
+ _facingDirection: "x+",
70
+ },
71
+ ],
72
+ sectionId: "power",
73
+ },
74
+ ],
75
+ directConnections: [
76
+ {
77
+ netId: ".B1 > .pin1 to .SW1 > .pin1",
78
+ allowInlineNetLabel: true,
79
+ inlineNetLabelHeight: 0.12,
80
+ inlineNetLabelWidth: 0.56,
81
+ pinIds: ["schematic_port_0", "schematic_port_2"],
82
+ },
83
+ ],
84
+ netConnections: [
85
+ {
86
+ netId: "GND",
87
+ netLabelWidth: 0.42,
88
+ netLabelHeight: 0.48,
89
+ pinIds: ["schematic_port_1", "schematic_port_5"],
90
+ },
91
+ {
92
+ netId: "VCC",
93
+ netLabelWidth: 0.42,
94
+ netLabelHeight: 0.48,
95
+ pinIds: ["schematic_port_3", "schematic_port_4"],
96
+ },
97
+ ],
98
+ textBoxes: [],
99
+ availableNetLabelOrientations: {
100
+ VCC: ["y+"],
101
+ GND: ["y-"],
102
+ },
103
+ maxMspPairDistance: 2.4,
104
+ _hideRatsNet: false,
105
+ }
106
+
107
+ test("core 555 power section avoids a cross-net overlap below SW1", () => {
108
+ const solver = new SchematicTracePipelineSolver(inputProblem)
109
+
110
+ solver.solve()
111
+
112
+ const traces = solver.sameNetJunctionAlignmentSolver!.outputTraces
113
+ const btnTrace = traces.find(
114
+ (trace) => trace.mspPairId === "schematic_port_0-schematic_port_2",
115
+ )!
116
+ const groundTrace = traces.find(
117
+ (trace) => trace.mspPairId === "schematic_port_1-schematic_port_5",
118
+ )!
119
+ const hasIntersection = btnTrace.tracePath
120
+ .slice(0, -1)
121
+ .some((start, index) =>
122
+ groundTrace.tracePath
123
+ .slice(0, -1)
124
+ .some((otherStart, otherIndex) =>
125
+ doSegmentsIntersect(
126
+ start,
127
+ btnTrace.tracePath[index + 1]!,
128
+ otherStart,
129
+ groundTrace.tracePath[otherIndex + 1]!,
130
+ ),
131
+ ),
132
+ )
133
+
134
+ expect(hasIntersection).toBeFalse()
135
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
136
+ })
@@ -0,0 +1,40 @@
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 inputProblem from "./assets/repro-core-tb67s579ftg-inline-label-routing.input.json"
5
+ import "tests/fixtures/matcher"
6
+
7
+ const getPinPair = (pinIds: string[]): [string, string] => {
8
+ if (pinIds.length !== 2) {
9
+ throw new Error(`Expected two pin IDs, received ${pinIds.length}`)
10
+ }
11
+
12
+ return [pinIds[0]!, pinIds[1]!]
13
+ }
14
+
15
+ // Captured from @tscircuit/core@12b2ef60's TB67S579FTG breakout repro. The
16
+ // fallback widths supplied for inline labels affect the routed traces beside J4.
17
+ test("core TB67S579FTG inline label routing", () => {
18
+ const solverInput: InputProblem = {
19
+ ...inputProblem,
20
+ directConnections: inputProblem.directConnections.map((connection) => ({
21
+ ...connection,
22
+ pinIds: getPinPair(connection.pinIds),
23
+ })),
24
+ }
25
+ const solver = new SchematicTracePipelineSolver(solverInput)
26
+
27
+ solver.solve()
28
+
29
+ expect(
30
+ solver.schematicTraceLinesSolver!.failedConnectionPairs.map(
31
+ (connection) => connection.userNetId,
32
+ ),
33
+ ).toEqual(["AGC_OUT"])
34
+ expect(
35
+ inputProblem.directConnections.find(
36
+ (connection) => connection.netId === "AGC_OUT",
37
+ )?.fallbackNetLabelWidth,
38
+ ).toBe(0.96)
39
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
40
+ })
@@ -0,0 +1,53 @@
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.1 1,-0.1 1,0.5" data-type="line" data-label="" points="413.33333333333337,394.66666666666663 600,394.66666666666663 600,170.66666666666666" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="0.5,0.3 0.95,0.3" data-type="line" data-label="" points="413.33333333333337,245.33333333333331 581.3333333333333,245.33333333333331" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="0.5,0.1 0.95,0.1" data-type="line" data-label="" points="413.33333333333337,320 581.3333333333333,320" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="U1" data-x="0" data-y="0.1" x="40" y="170.66666666666669" width="373.33333333333337" height="298.66666666666663" fill="hsl(164, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0026785714285714286"/></g><g><rect data-type="rect" data-label="INLINE netId: SCL
2
+ axis: x
3
+ side: y+" data-x="0.725" data-y="0.41" x="441.33333333333337" y="181.86666666666667" width="111.99999999999994" height="44.79999999999998" fill="hsl(196, 100%, 50%, 0.35)" stroke="black" stroke-width="0.0026785714285714286"/></g><g><rect data-type="rect" data-label="INLINE netId: SDA
4
+ axis: x
5
+ side: y+" data-x="0.725" data-y="0.21000000000000002" x="441.33333333333337" y="256.5333333333333" width="111.99999999999994" height="44.80000000000001" fill="hsl(216, 100%, 50%, 0.35)" stroke="black" stroke-width="0.0026785714285714286"/></g><text data-type="text" data-label="SCL" data-x="0.575" data-y="0.41" x="441.3333333333333" y="204.26666666666665" fill="green" font-size="44.8" font-family="sans-serif" text-anchor="start" dominant-baseline="central">SCL</text><text data-type="text" data-label="SDA" data-x="0.575" data-y="0.21000000000000002" x="441.3333333333333" y="278.9333333333333" fill="green" font-size="44.8" font-family="sans-serif" text-anchor="start" dominant-baseline="central">SDA</text><g><circle data-type="point" data-label="U1.scl
6
+ x+" data-x="0.5" data-y="0.3" cx="413.33333333333337" cy="245.33333333333331" r="3" fill="hsl(202, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U1.sda
7
+ x+" data-x="0.5" data-y="0.1" cx="413.33333333333337" cy="320" r="3" fill="hsl(222, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U1.vcc
8
+ x+" data-x="0.5" data-y="-0.1" cx="413.33333333333337" cy="394.66666666666663" r="3" fill="hsl(196, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="inline anchor
9
+ SCL" data-x="0.725" data-y="0.3" cx="497.3333333333333" cy="245.33333333333331" r="3" fill="green"/></g><g><circle data-type="point" data-label="inline anchor
10
+ SDA" data-x="0.725" data-y="0.1" cx="497.3333333333333" cy="320" 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[
11
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
12
+ const svg = e.currentTarget;
13
+ const rect = svg.getBoundingClientRect();
14
+ const x = e.clientX - rect.left;
15
+ const y = e.clientY - rect.top;
16
+ const crosshair = svg.getElementById('crosshair');
17
+ const h = svg.getElementById('crosshair-h');
18
+ const v = svg.getElementById('crosshair-v');
19
+ const coords = svg.getElementById('coordinates');
20
+
21
+ crosshair.style.display = 'block';
22
+ h.setAttribute('x1', '0');
23
+ h.setAttribute('x2', '640');
24
+ h.setAttribute('y1', y);
25
+ h.setAttribute('y2', y);
26
+ v.setAttribute('x1', x);
27
+ v.setAttribute('x2', x);
28
+ v.setAttribute('y1', '0');
29
+ v.setAttribute('y2', '640');
30
+
31
+ // Calculate real coordinates using inverse transformation
32
+ const matrix = {"a":373.3333333333333,"c":0,"e":226.66666666666669,"b":0,"d":-373.3333333333333,"f":357.3333333333333};
33
+ // Manually invert and apply the affine transform
34
+ // Since we only use translate and scale, we can directly compute:
35
+ // x' = (x - tx) / sx
36
+ // y' = (y - ty) / sy
37
+ const sx = matrix.a;
38
+ const sy = matrix.d;
39
+ const tx = matrix.e;
40
+ const ty = matrix.f;
41
+ const realPoint = {
42
+ x: (x - tx) / sx,
43
+ y: (y - ty) / sy // Flip y back since we used negative scale
44
+ }
45
+
46
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
47
+ coords.setAttribute('x', (x + 5).toString());
48
+ coords.setAttribute('y', (y - 5).toString());
49
+ });
50
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
51
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
52
+ });
53
+ ]]></script></svg>
@@ -0,0 +1,102 @@
1
+ import { expect, test } from "bun:test"
2
+ import { InlineNetLabelSolver } from "lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver"
3
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
4
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
5
+ import type { InputProblem } from "lib/types/InputProblem"
6
+ import "tests/fixtures/matcher"
7
+
8
+ test("terminal inline-label stubs stop before an unrelated trace", () => {
9
+ const inputProblem: InputProblem = {
10
+ chips: [
11
+ {
12
+ chipId: "U1",
13
+ center: { x: 0, y: 0.1 },
14
+ width: 1,
15
+ height: 0.8,
16
+ pins: [
17
+ { pinId: "U1.scl", x: 0.5, y: 0.3, _facingDirection: "x+" },
18
+ { pinId: "U1.sda", x: 0.5, y: 0.1, _facingDirection: "x+" },
19
+ { pinId: "U1.vcc", x: 0.5, y: -0.1, _facingDirection: "x+" },
20
+ ],
21
+ },
22
+ ],
23
+ directConnections: [],
24
+ netConnections: [
25
+ {
26
+ netId: "SCL",
27
+ pinIds: ["U1.scl"],
28
+ allowInlineNetLabel: true,
29
+ inlineNetLabelWidth: 0.3,
30
+ inlineNetLabelHeight: 0.12,
31
+ },
32
+ {
33
+ netId: "SDA",
34
+ pinIds: ["U1.sda"],
35
+ allowInlineNetLabel: true,
36
+ inlineNetLabelWidth: 0.3,
37
+ inlineNetLabelHeight: 0.12,
38
+ },
39
+ ],
40
+ availableNetLabelOrientations: {
41
+ SCL: ["x+"],
42
+ SDA: ["x+"],
43
+ },
44
+ }
45
+ const netLabelPlacements: NetLabelPlacement[] = [
46
+ {
47
+ globalConnNetId: "SCL",
48
+ netId: "SCL",
49
+ mspConnectionPairIds: [],
50
+ pinIds: ["U1.scl"],
51
+ orientation: "x+",
52
+ anchorPoint: { x: 0.5, y: 0.3 },
53
+ center: { x: 0.8, y: 0.3 },
54
+ width: 0.3,
55
+ height: 0.12,
56
+ },
57
+ {
58
+ globalConnNetId: "SDA",
59
+ netId: "SDA",
60
+ mspConnectionPairIds: [],
61
+ pinIds: ["U1.sda"],
62
+ orientation: "x+",
63
+ anchorPoint: { x: 0.5, y: 0.1 },
64
+ center: { x: 0.8, y: 0.1 },
65
+ width: 0.3,
66
+ height: 0.12,
67
+ },
68
+ ]
69
+ const traces: SolvedTracePath[] = [
70
+ {
71
+ mspPairId: "available-net-orientation-VCC",
72
+ dcConnNetId: "VCC",
73
+ globalConnNetId: "VCC",
74
+ pins: [] as any,
75
+ tracePath: [
76
+ { x: 0.5, y: -0.1 },
77
+ { x: 1, y: -0.1 },
78
+ { x: 1, y: 0.5 },
79
+ ],
80
+ mspConnectionPairIds: [],
81
+ pinIds: ["U1.vcc"],
82
+ },
83
+ ]
84
+
85
+ const solver = new InlineNetLabelSolver({
86
+ inputProblem,
87
+ traces,
88
+ netLabelPlacements,
89
+ })
90
+ solver.solve()
91
+
92
+ const placements = solver.getOutput().inlineNetLabelPlacements
93
+ expect(placements).toHaveLength(2)
94
+ for (const placement of placements) {
95
+ const [start, end] = placement.stubTracePath!
96
+ expect(end.x).toBeCloseTo(0.95)
97
+ expect(end.x).toBeLessThan(1)
98
+ expect(end.x - start.x).toBeGreaterThanOrEqual(placement.width + 0.1)
99
+ }
100
+
101
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
102
+ })