@tscircuit/schematic-trace-solver 0.0.125 → 0.0.127
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/README.md +28 -0
- package/dist/index.d.ts +159 -2
- package/dist/index.js +739 -36
- package/lib/index.ts +2 -0
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +424 -0
- package/lib/solvers/InlineNetLabelSolver/getAxisAlignedSegments.ts +73 -0
- package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +15 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +86 -11
- package/lib/solvers/TraceElbowTransitionSimplificationSolver/TraceElbowTransitionSimplificationSolver.ts +216 -0
- package/lib/solvers/TraceElbowTransitionSimplificationSolver/generateElbowTransitionSimplificationCandidates.ts +186 -0
- package/lib/solvers/TraceElbowTransitionSimplificationSolver/types.ts +10 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +3 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +16 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +56 -13
- package/lib/types/InputProblem.ts +24 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260806T093501Z.page.tsx +4 -0
- package/site/examples/inline-net-label01.page.tsx +6 -0
- package/tests/assets/inline-net-label01.json +47 -0
- package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +3 -3
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +6 -6
- package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +135 -0
- package/tests/bug-reports/bug-report-20260806T093501Z/bug-report-20260806T093501Z.json +505 -0
- package/tests/bug-reports/bug-report-20260806T093501Z/bug-report-20260806T093501Z.test.ts +66 -0
- package/tests/examples/__snapshots__/example33.snap.svg +1 -1
- package/tests/functions/getAxisAlignedSegments.test.ts +56 -0
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label01.snap.svg +54 -0
- package/tests/solvers/InlineNetLabelSolver/inline-net-label01.test.ts +44 -0
|
@@ -0,0 +1,54 @@
|
|
|
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 2.5,0" data-type="line" data-label="" points="215.29056603773586,71.69811320754715 426.611320754717,71.69811320754715" fill="none" stroke="hsl(48, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="0,-2.5 0,-4" data-type="line" data-label="" points="162.46037735849058,335.84905660377353 162.46037735849058,494.33962264150944" fill="none" stroke="hsl(136, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="0.5,0 2.5,0" data-type="line" data-label="" points="215.29056603773586,71.69811320754715 426.611320754717,71.69811320754715" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="0,-2.5 0,-4" data-type="line" data-label="" points="162.46037735849058,335.84905660377353 162.46037735849058,494.33962264150944" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="U1" data-x="0" data-y="0" x="109.6301886792453" y="39.999999999999986" width="105.66037735849056" height="63.39622641509433" fill="hsl(164, 100%, 50%, 0.8)" stroke="black" stroke-width="0.009464285714285715"/></g><g><rect data-type="rect" data-label="U2" data-x="3" data-y="0" x="426.611320754717" y="39.999999999999986" width="105.66037735849056" height="63.39622641509433" fill="hsl(165, 100%, 50%, 0.8)" stroke="black" stroke-width="0.009464285714285715"/></g><g><rect data-type="rect" data-label="U3" data-x="0" data-y="-2" x="130.7622641509434" y="230.18867924528303" width="63.39622641509436" height="105.66037735849054" fill="hsl(166, 100%, 50%, 0.8)" stroke="black" stroke-width="0.009464285714285715"/></g><g><rect data-type="rect" data-label="U4" data-x="0" data-y="-4.5" x="130.7622641509434" y="494.3396226415094" width="63.39622641509436" height="105.66037735849056" fill="hsl(167, 100%, 50%, 0.8)" stroke="black" stroke-width="0.009464285714285715"/></g><g><rect data-type="rect" data-label="INLINE netId: USER_LED_ANODE
|
|
2
|
+
axis: x
|
|
3
|
+
side: y+" data-x="1.5" data-y="0.14" x="225.85660377358494" y="47.39622641509432" width="190.18867924528297" height="19.01886792452831" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.009464285714285715"/></g><g><rect data-type="rect" data-label="INLINE netId: SPI_SCK
|
|
4
|
+
axis: y
|
|
5
|
+
side: x-" data-x="-0.14" data-y="-3.25" x="138.15849056603776" y="364.3773584905661" width="19.01886792452828" height="101.43396226415086" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.009464285714285715"/></g><text data-type="text" data-label="USER_LED_ANODE" data-x="1.5" data-y="0.14" x="320.9509433962264" y="56.90566037735847" fill="green" font-size="19.0188679245283" font-family="sans-serif" text-anchor="middle" dominant-baseline="central">USER_LED_ANODE</text><text data-type="text" data-label="SPI_SCK" data-x="-0.14" data-y="-3.25" x="147.6679245283019" y="415.09433962264154" fill="green" font-size="19.0188679245283" font-family="sans-serif" text-anchor="middle" dominant-baseline="central">SPI_SCK</text><g><circle data-type="point" data-label="U1.1
|
|
6
|
+
x+" data-x="0.5" data-y="0" cx="215.29056603773586" cy="71.69811320754715" r="3" fill="hsl(319, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U2.1
|
|
7
|
+
x-" data-x="2.5" data-y="0" cx="426.611320754717" cy="71.69811320754715" r="3" fill="hsl(200, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U3.1
|
|
8
|
+
y-" data-x="0" data-y="-2.5" cx="162.46037735849058" cy="335.84905660377353" r="3" fill="hsl(81, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="U4.1
|
|
9
|
+
y+" data-x="0" data-y="-4" cx="162.46037735849058" cy="494.33962264150944" r="3" fill="hsl(322, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="inline anchor
|
|
10
|
+
USER_LED_ANODE" data-x="1.5" data-y="0" cx="320.9509433962264" cy="71.69811320754715" r="3" fill="green"/></g><g><circle data-type="point" data-label="inline anchor
|
|
11
|
+
SPI_SCK" data-x="0" data-y="-3.25" cx="162.46037735849058" cy="415.09433962264154" 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[
|
|
12
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
13
|
+
const svg = e.currentTarget;
|
|
14
|
+
const rect = svg.getBoundingClientRect();
|
|
15
|
+
const x = e.clientX - rect.left;
|
|
16
|
+
const y = e.clientY - rect.top;
|
|
17
|
+
const crosshair = svg.getElementById('crosshair');
|
|
18
|
+
const h = svg.getElementById('crosshair-h');
|
|
19
|
+
const v = svg.getElementById('crosshair-v');
|
|
20
|
+
const coords = svg.getElementById('coordinates');
|
|
21
|
+
|
|
22
|
+
crosshair.style.display = 'block';
|
|
23
|
+
h.setAttribute('x1', '0');
|
|
24
|
+
h.setAttribute('x2', '640');
|
|
25
|
+
h.setAttribute('y1', y);
|
|
26
|
+
h.setAttribute('y2', y);
|
|
27
|
+
v.setAttribute('x1', x);
|
|
28
|
+
v.setAttribute('x2', x);
|
|
29
|
+
v.setAttribute('y1', '0');
|
|
30
|
+
v.setAttribute('y2', '640');
|
|
31
|
+
|
|
32
|
+
// Calculate real coordinates using inverse transformation
|
|
33
|
+
const matrix = {"a":105.66037735849056,"c":0,"e":162.46037735849058,"b":0,"d":-105.66037735849056,"f":71.69811320754715};
|
|
34
|
+
// Manually invert and apply the affine transform
|
|
35
|
+
// Since we only use translate and scale, we can directly compute:
|
|
36
|
+
// x' = (x - tx) / sx
|
|
37
|
+
// y' = (y - ty) / sy
|
|
38
|
+
const sx = matrix.a;
|
|
39
|
+
const sy = matrix.d;
|
|
40
|
+
const tx = matrix.e;
|
|
41
|
+
const ty = matrix.f;
|
|
42
|
+
const realPoint = {
|
|
43
|
+
x: (x - tx) / sx,
|
|
44
|
+
y: (y - ty) / sy // Flip y back since we used negative scale
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
|
|
48
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
49
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
50
|
+
});
|
|
51
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
52
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
53
|
+
});
|
|
54
|
+
]]></script></svg>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import inputProblem from "../../assets/inline-net-label01.json"
|
|
4
|
+
import "tests/fixtures/matcher"
|
|
5
|
+
|
|
6
|
+
test("inline-net-label01 places inline labels alongside point-to-point traces", () => {
|
|
7
|
+
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
8
|
+
|
|
9
|
+
solver.solve()
|
|
10
|
+
|
|
11
|
+
const inlinePlacements = solver
|
|
12
|
+
.inlineNetLabelSolver!.inlineNetLabelPlacements.slice()
|
|
13
|
+
.sort((a, b) => (a.netId ?? "").localeCompare(b.netId ?? ""))
|
|
14
|
+
|
|
15
|
+
expect(inlinePlacements.map((p) => p.netId)).toEqual([
|
|
16
|
+
"SPI_SCK",
|
|
17
|
+
"USER_LED_ANODE",
|
|
18
|
+
])
|
|
19
|
+
|
|
20
|
+
const [spiSck, userLedAnode] = inlinePlacements
|
|
21
|
+
|
|
22
|
+
// The horizontal trace gets a horizontal label sitting above the wire.
|
|
23
|
+
expect(userLedAnode!.axis).toBe("x")
|
|
24
|
+
expect(userLedAnode!.side).toBe("y+")
|
|
25
|
+
expect(userLedAnode!.center.x).toBeCloseTo(userLedAnode!.anchorPoint.x, 6)
|
|
26
|
+
expect(userLedAnode!.center.y).toBeGreaterThan(userLedAnode!.anchorPoint.y)
|
|
27
|
+
|
|
28
|
+
// The vertical trace gets a vertical label on the left of the wire, which is
|
|
29
|
+
// "above" it once the text is rotated.
|
|
30
|
+
expect(spiSck!.axis).toBe("y")
|
|
31
|
+
expect(spiSck!.side).toBe("x-")
|
|
32
|
+
expect(spiSck!.center.y).toBeCloseTo(spiSck!.anchorPoint.y, 6)
|
|
33
|
+
expect(spiSck!.center.x).toBeLessThan(spiSck!.anchorPoint.x)
|
|
34
|
+
|
|
35
|
+
// An inline label replaces the anchored label for that net, it never adds a
|
|
36
|
+
// second label.
|
|
37
|
+
const inlineNetIds = new Set(inlinePlacements.map((p) => p.globalConnNetId))
|
|
38
|
+
const anchoredNetIds = solver
|
|
39
|
+
.inlineNetLabelSolver!.getOutput()
|
|
40
|
+
.netLabelPlacements.map((p) => p.globalConnNetId)
|
|
41
|
+
expect(anchoredNetIds.filter((id) => inlineNetIds.has(id))).toEqual([])
|
|
42
|
+
|
|
43
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
44
|
+
})
|