@tscircuit/schematic-trace-solver 0.0.128 → 0.0.130

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 (30) hide show
  1. package/dist/index.d.ts +15 -6
  2. package/dist/index.js +97 -10
  3. package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +142 -23
  4. package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +23 -2
  5. package/package.json +1 -1
  6. package/site/bug-reports/bug-report-20260811T075142Z.page.tsx +4 -0
  7. package/tests/assets/inline-net-label02.json +112 -0
  8. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +34 -22
  9. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +5 -3
  10. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +305 -303
  11. package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +5 -3
  12. package/tests/bug-reports/bug-report-20260811T075142Z/__snapshots__/bug-report-20260811T075142Z.snap.svg +74 -0
  13. package/tests/bug-reports/bug-report-20260811T075142Z/bug-report-20260811T075142Z.json +194 -0
  14. package/tests/bug-reports/bug-report-20260811T075142Z/bug-report-20260811T075142Z.test.ts +12 -0
  15. package/tests/examples/__snapshots__/example10.snap.svg +20 -16
  16. package/tests/examples/__snapshots__/example31.snap.svg +8 -6
  17. package/tests/examples/__snapshots__/example33.snap.svg +32 -30
  18. package/tests/examples/__snapshots__/example46.snap.svg +5 -3
  19. package/tests/examples/__snapshots__/example48.snap.svg +25 -23
  20. package/tests/examples/__snapshots__/example50.snap.svg +32 -30
  21. package/tests/repros/__snapshots__/repro-atmega328p-fault-pullup.snap.svg +48 -46
  22. package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +7 -5
  23. package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +24 -18
  24. package/tests/repros/__snapshots__/repro51-overlap-junction-crossing.snap.svg +25 -23
  25. package/tests/repros/__snapshots__/small-variant-resistor-facing-direction.snap.svg +46 -44
  26. package/tests/repros/repro-max-msp-pair-distance-fallback.test.ts +45 -0
  27. package/tests/repros/repro-missing-trace-netlabel.test.ts +9 -11
  28. package/tests/repros/rotated-components-rail-label.test.ts +5 -11
  29. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label02.snap.svg +57 -0
  30. package/tests/solvers/InlineNetLabelSolver/inline-net-label02.test.ts +59 -0
@@ -0,0 +1,57 @@
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.6000000000000001,-0.30000000000000004 2.435,0" data-type="line" data-label="" points="201.34453781512605,376.47058823529414 448.06722689075633,336.1344537815126" fill="none" stroke="hsl(48, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="0.6000000000000001,-0.30000000000000004 1.5175,-0.30000000000000004 1.5175,0 2.435,0" data-type="line" data-label="" points="201.34453781512605,376.47058823529414 324.7058823529412,376.47058823529414 324.7058823529412,336.1344537815126 448.06722689075633,336.1344537815126" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="40" y="268.9075630252101" width="161.34453781512605" height="134.45378151260502" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0074375"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="3" data-y="0.13249999999999998" x="448.0672268907564" y="256.8067226890756" width="151.93277310924367" height="123.02521008403363" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.0074375"/></g><g><rect data-type="rect" data-label="U1" data-x="-0.08000000000000002" data-y="0.615" x="85.7142857142857" y="236.6386554621849" width="48.40336134453783" height="33.613445378151255" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.0074375"/></g><g><rect data-type="rect" data-label="INLINE netId: USER_LED_ANODE
2
+ axis: x
3
+ side: y+" data-x="1.5175" data-y="0.14" x="203.69747899159668" y="305.21008403361344" width="242.01680672268907" height="24.201680672268935" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.0074375"/></g><text data-type="text" data-label="USER_LED_ANODE" data-x="1.5175" data-y="0.14" x="324.7058823529412" y="317.3109243697479" fill="green" font-size="24.201680672268907" font-family="sans-serif" text-anchor="middle" dominant-baseline="central">USER_LED_ANODE</text><g><circle data-type="point" data-label="schematic_port_0
4
+ x-" data-x="-0.6000000000000001" data-y="0.30000000000000004" cx="39.999999999999986" cy="295.79831932773106" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_1
5
+ x-" data-x="-0.6000000000000001" data-y="0.10000000000000003" cx="39.999999999999986" cy="322.6890756302521" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_2
6
+ x-" data-x="-0.6000000000000001" data-y="-0.09999999999999998" cx="39.999999999999986" cy="349.5798319327731" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_3
7
+ x-" data-x="-0.6000000000000001" data-y="-0.30000000000000004" cx="39.999999999999986" cy="376.47058823529414" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_4
8
+ x+" data-x="0.6000000000000001" data-y="-0.30000000000000004" cx="201.34453781512605" cy="376.47058823529414" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_5
9
+ x+" data-x="0.6000000000000001" data-y="-0.10000000000000003" cx="201.34453781512605" cy="349.5798319327731" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_6
10
+ x+" data-x="0.6000000000000001" data-y="0.09999999999999998" cx="201.34453781512605" cy="322.6890756302521" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_7
11
+ x+" data-x="0.6000000000000001" data-y="0.30000000000000004" cx="201.34453781512605" cy="295.79831932773106" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_8
12
+ x-" data-x="2.435" data-y="0" cx="448.06722689075633" cy="336.1344537815126" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="schematic_port_9
13
+ x+" data-x="3.565" data-y="0" cx="600" cy="336.1344537815126" r="3" fill="hsl(248, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="inline anchor
14
+ USER_LED_ANODE" data-x="1.5175" data-y="0" cx="324.7058823529412" cy="336.1344537815126" 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[
15
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
16
+ const svg = e.currentTarget;
17
+ const rect = svg.getBoundingClientRect();
18
+ const x = e.clientX - rect.left;
19
+ const y = e.clientY - rect.top;
20
+ const crosshair = svg.getElementById('crosshair');
21
+ const h = svg.getElementById('crosshair-h');
22
+ const v = svg.getElementById('crosshair-v');
23
+ const coords = svg.getElementById('coordinates');
24
+
25
+ crosshair.style.display = 'block';
26
+ h.setAttribute('x1', '0');
27
+ h.setAttribute('x2', '640');
28
+ h.setAttribute('y1', y);
29
+ h.setAttribute('y2', y);
30
+ v.setAttribute('x1', x);
31
+ v.setAttribute('x2', x);
32
+ v.setAttribute('y1', '0');
33
+ v.setAttribute('y2', '640');
34
+
35
+ // Calculate real coordinates using inverse transformation
36
+ const matrix = {"a":134.45378151260505,"c":0,"e":120.67226890756302,"b":0,"d":-134.45378151260505,"f":336.1344537815126};
37
+ // Manually invert and apply the affine transform
38
+ // Since we only use translate and scale, we can directly compute:
39
+ // x' = (x - tx) / sx
40
+ // y' = (y - ty) / sy
41
+ const sx = matrix.a;
42
+ const sy = matrix.d;
43
+ const tx = matrix.e;
44
+ const ty = matrix.f;
45
+ const realPoint = {
46
+ x: (x - tx) / sx,
47
+ y: (y - ty) / sy // Flip y back since we used negative scale
48
+ }
49
+
50
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
51
+ coords.setAttribute('x', (x + 5).toString());
52
+ coords.setAttribute('y', (y - 5).toString());
53
+ });
54
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
55
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
56
+ });
57
+ ]]></script></svg>
@@ -0,0 +1,59 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import inputProblem from "../../assets/inline-net-label02.json"
4
+ import "tests/fixtures/matcher"
5
+
6
+ // Real geometry from @tscircuit/core: a soic8 and an 0603 LED 3 units apart.
7
+ // The label (width 1.8) is longer than every straight run of the routed trace
8
+ // (~0.95), but the route is straight except for a 0.3-unit elbow jog, so the
9
+ // name is centered over the route's overall span - bridging the jog - instead
10
+ // of falling back to an anchored label.
11
+ test("inline-net-label02 spans a route with a small jog", () => {
12
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
13
+
14
+ solver.solve()
15
+
16
+ const placements = solver.inlineNetLabelSolver!.inlineNetLabelPlacements
17
+ expect(placements.map((p) => p.netId)).toEqual(["USER_LED_ANODE"])
18
+
19
+ const [placement] = placements
20
+ expect(placement!.axis).toBe("x")
21
+
22
+ const labelBounds = {
23
+ minX: placement!.center.x - placement!.width / 2,
24
+ maxX: placement!.center.x + placement!.width / 2,
25
+ minY: placement!.center.y - placement!.height / 2,
26
+ maxY: placement!.center.y + placement!.height / 2,
27
+ }
28
+
29
+ // The label stays within the route's endpoints - no floating overhang.
30
+ const trace = solver.inlineNetLabelSolver!.traces.find(
31
+ (t) => t.mspPairId === placement!.mspPairId,
32
+ )!
33
+ const routeMinX = Math.min(...trace.tracePath.map((p) => p.x))
34
+ const routeMaxX = Math.max(...trace.tracePath.map((p) => p.x))
35
+ const routeMaxY = Math.max(...trace.tracePath.map((p) => p.y))
36
+ expect(labelBounds.minX).toBeGreaterThanOrEqual(routeMinX - 1e-9)
37
+ expect(labelBounds.maxX).toBeLessThanOrEqual(routeMaxX + 1e-9)
38
+
39
+ // It sits clear of the route's full perpendicular extent (above every jog).
40
+ expect(labelBounds.minY).toBeGreaterThan(routeMaxY)
41
+
42
+ // And never on top of a chip.
43
+ for (const chip of (inputProblem as any).chips) {
44
+ const chipBounds = {
45
+ minX: chip.center.x - chip.width / 2,
46
+ maxX: chip.center.x + chip.width / 2,
47
+ minY: chip.center.y - chip.height / 2,
48
+ maxY: chip.center.y + chip.height / 2,
49
+ }
50
+ const overlaps =
51
+ labelBounds.minX < chipBounds.maxX &&
52
+ labelBounds.maxX > chipBounds.minX &&
53
+ labelBounds.minY < chipBounds.maxY &&
54
+ labelBounds.maxY > chipBounds.minY
55
+ expect(overlaps).toBe(false)
56
+ }
57
+
58
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
59
+ })