@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.
Files changed (28) hide show
  1. package/README.md +28 -0
  2. package/dist/index.d.ts +159 -2
  3. package/dist/index.js +739 -36
  4. package/lib/index.ts +2 -0
  5. package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +424 -0
  6. package/lib/solvers/InlineNetLabelSolver/getAxisAlignedSegments.ts +73 -0
  7. package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +15 -0
  8. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +86 -11
  9. package/lib/solvers/TraceElbowTransitionSimplificationSolver/TraceElbowTransitionSimplificationSolver.ts +216 -0
  10. package/lib/solvers/TraceElbowTransitionSimplificationSolver/generateElbowTransitionSimplificationCandidates.ts +186 -0
  11. package/lib/solvers/TraceElbowTransitionSimplificationSolver/types.ts +10 -0
  12. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +3 -0
  13. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +16 -0
  14. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +56 -13
  15. package/lib/types/InputProblem.ts +24 -0
  16. package/package.json +1 -1
  17. package/site/bug-reports/bug-report-20260806T093501Z.page.tsx +4 -0
  18. package/site/examples/inline-net-label01.page.tsx +6 -0
  19. package/tests/assets/inline-net-label01.json +47 -0
  20. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +3 -3
  21. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +6 -6
  22. package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +135 -0
  23. package/tests/bug-reports/bug-report-20260806T093501Z/bug-report-20260806T093501Z.json +505 -0
  24. package/tests/bug-reports/bug-report-20260806T093501Z/bug-report-20260806T093501Z.test.ts +66 -0
  25. package/tests/examples/__snapshots__/example33.snap.svg +1 -1
  26. package/tests/functions/getAxisAlignedSegments.test.ts +56 -0
  27. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label01.snap.svg +54 -0
  28. 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
+ })