Skip to content

Add parameterized SMD slide switch footprints - #749

Merged
seveibar merged 2 commits into
mainfrom
codex/add-smd-slide-switch
Jul 27, 2026
Merged

Add parameterized SMD slide switch footprints#749
seveibar merged 2 commits into
mainfrom
codex/add-smd-slide-switch

Conversation

@seveibar

@seveibar seveibar commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a new smdslideswitch7 footprint generator for three signal pads, four mounting pads, and optional alignment holes
  • support sparse signal rows through signalcols and missing(...)
  • add geometry assertions and visual SVG snapshots for MINI MSK12CO2 and MSK12C02
  • update @tscircuit/math-utils so clean installs provide the bounds helpers used by the generator

Why

The JLC5000 benchmark currently misclassifies these two related SMD slide-switch footprints and scores them at roughly 10% copper IoU. This generator represents both layouts without per-pad coordinates, including the larger switch's four-position signal row with position 2 absent.

Direct comparison against the imported JLC5000 footprints gives 100% copper IoU for both C2681570 and C431540.

Validation

  • bun install --frozen-lockfile — passed
  • bun test tests/smdslideswitch.test.ts — 3 passed
  • bun run build — passed
  • bunx biome check src/fn/smdslideswitch.ts src/fn/index.ts src/footprinter.ts tests/smdslideswitch.test.ts — passed
  • direct JLC5000 copper comparison — 100% IoU for both targets
  • local macOS full bun test — 496 passed; 5 unrelated SVG snapshot comparisons failed (melf, sod323fl, diode1210, diode0603, and sod923)

@seveibar
seveibar marked this pull request as ready for review July 27, 2026 23:24
@seveibar
seveibar requested a review from techmannih as a code owner July 27, 2026 23:24
@seveibar
seveibar merged commit 8728c29 into main Jul 27, 2026
5 checks passed
Comment on lines +18 to +118
test("smdslideswitch7 represents MINI MSK12CO2", () => {
const footprint =
"smdslideswitch7_p1mm_pw0.7mm_pl1.5mm_mounty-1.800098mm_mpx5.500116mm_mpy2.200148mm_mpw1mm_mpl0.8mm_holex1.499997mm_holed0.900024mm"
const circuitJson = fp.string(footprint).circuitJson()

expect(getPadGeometry(footprint)).toEqual([
{ x: -1, y: 0, width: 0.7, height: 1.5, port_hints: ["1"] },
{ x: 0, y: 0, width: 0.7, height: 1.5, port_hints: ["2"] },
{ x: 1, y: 0, width: 0.7, height: 1.5, port_hints: ["3"] },
{
x: -2.750058,
y: -0.700024,
width: 1,
height: 0.8,
port_hints: ["4"],
},
{
x: 2.750058,
y: -0.700024,
width: 1,
height: 0.8,
port_hints: ["5"],
},
{
x: -2.750058,
y: -2.900172,
width: 1,
height: 0.8,
port_hints: ["6"],
},
{
x: 2.750058,
y: -2.900172,
width: 1,
height: 0.8,
port_hints: ["7"],
},
])
expect(
circuitJson
.filter((element) => element.type === "pcb_hole")
.map(({ x, y, hole_diameter }) => ({ x, y, hole_diameter })),
).toEqual([
{ x: -1.499997, y: -1.800098, hole_diameter: 0.900024 },
{ x: 1.499997, y: -1.800098, hole_diameter: 0.900024 },
])
expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot(
import.meta.path,
"mini_msk12co2",
)
})

test("smdslideswitch7 supports a missing signal position", () => {
const footprint =
"smdslideswitch7_signalcols4_missing(2)_p1.5mm_pw0.6mm_pl1.524mm_mounty-2.250059mm_mpx7.199884mm_mpy2.29997mm_mpw1.2mm_mpl0.7mm_holex1.499997mm_holey-2.250186mm_holed0.900024mm"
const circuitJson = fp.string(footprint).circuitJson()

expect(getPadGeometry(footprint)).toEqual([
{ x: -2.25, y: 0, width: 0.6, height: 1.524, port_hints: ["1"] },
{ x: 0.75, y: 0, width: 0.6, height: 1.524, port_hints: ["2"] },
{ x: 2.25, y: 0, width: 0.6, height: 1.524, port_hints: ["3"] },
{
x: -3.599942,
y: -1.100074,
width: 1.2,
height: 0.7,
port_hints: ["4"],
},
{
x: 3.599942,
y: -1.100074,
width: 1.2,
height: 0.7,
port_hints: ["5"],
},
{
x: -3.599942,
y: -3.400044,
width: 1.2,
height: 0.7,
port_hints: ["6"],
},
{
x: 3.599942,
y: -3.400044,
width: 1.2,
height: 0.7,
port_hints: ["7"],
},
])
expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot(
import.meta.path,
"msk12c02",
)
})

test("smdslideswitch7 rejects the wrong signal-pad count", () => {
expect(() => fp.string("smdslideswitch7_signalcols4").circuitJson()).toThrow(
"needs 3 signal pads",
)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains three test(...) calls (lines 18, 70, and 114), but the style guide rule states that a *.test.ts file may have AT MOST one test(...). After the first test, the remaining tests should be split into separate, numbered files. For example: smdslideswitch1.test.ts (for the MINI MSK12CO2 test), smdslideswitch2.test.ts (for the missing signal position test), and smdslideswitch3.test.ts (for the wrong signal-pad count rejection test).

Suggested change
test("smdslideswitch7 represents MINI MSK12CO2", () => {
const footprint =
"smdslideswitch7_p1mm_pw0.7mm_pl1.5mm_mounty-1.800098mm_mpx5.500116mm_mpy2.200148mm_mpw1mm_mpl0.8mm_holex1.499997mm_holed0.900024mm"
const circuitJson = fp.string(footprint).circuitJson()
expect(getPadGeometry(footprint)).toEqual([
{ x: -1, y: 0, width: 0.7, height: 1.5, port_hints: ["1"] },
{ x: 0, y: 0, width: 0.7, height: 1.5, port_hints: ["2"] },
{ x: 1, y: 0, width: 0.7, height: 1.5, port_hints: ["3"] },
{
x: -2.750058,
y: -0.700024,
width: 1,
height: 0.8,
port_hints: ["4"],
},
{
x: 2.750058,
y: -0.700024,
width: 1,
height: 0.8,
port_hints: ["5"],
},
{
x: -2.750058,
y: -2.900172,
width: 1,
height: 0.8,
port_hints: ["6"],
},
{
x: 2.750058,
y: -2.900172,
width: 1,
height: 0.8,
port_hints: ["7"],
},
])
expect(
circuitJson
.filter((element) => element.type === "pcb_hole")
.map(({ x, y, hole_diameter }) => ({ x, y, hole_diameter })),
).toEqual([
{ x: -1.499997, y: -1.800098, hole_diameter: 0.900024 },
{ x: 1.499997, y: -1.800098, hole_diameter: 0.900024 },
])
expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot(
import.meta.path,
"mini_msk12co2",
)
})
test("smdslideswitch7 supports a missing signal position", () => {
const footprint =
"smdslideswitch7_signalcols4_missing(2)_p1.5mm_pw0.6mm_pl1.524mm_mounty-2.250059mm_mpx7.199884mm_mpy2.29997mm_mpw1.2mm_mpl0.7mm_holex1.499997mm_holey-2.250186mm_holed0.900024mm"
const circuitJson = fp.string(footprint).circuitJson()
expect(getPadGeometry(footprint)).toEqual([
{ x: -2.25, y: 0, width: 0.6, height: 1.524, port_hints: ["1"] },
{ x: 0.75, y: 0, width: 0.6, height: 1.524, port_hints: ["2"] },
{ x: 2.25, y: 0, width: 0.6, height: 1.524, port_hints: ["3"] },
{
x: -3.599942,
y: -1.100074,
width: 1.2,
height: 0.7,
port_hints: ["4"],
},
{
x: 3.599942,
y: -1.100074,
width: 1.2,
height: 0.7,
port_hints: ["5"],
},
{
x: -3.599942,
y: -3.400044,
width: 1.2,
height: 0.7,
port_hints: ["6"],
},
{
x: 3.599942,
y: -3.400044,
width: 1.2,
height: 0.7,
port_hints: ["7"],
},
])
expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot(
import.meta.path,
"msk12c02",
)
})
test("smdslideswitch7 rejects the wrong signal-pad count", () => {
expect(() => fp.string("smdslideswitch7_signalcols4").circuitJson()).toThrow(
"needs 3 signal pads",
)
})
test("smdslideswitch7 represents MINI MSK12CO2", () => {
const footprint =
"smdslideswitch7_p1mm_pw0.7mm_pl1.5mm_mounty-1.800098mm_mpx5.500116mm_mpy2.200148mm_mpw1mm_mpl0.8mm_holex1.499997mm_holed0.900024mm"
const circuitJson = fp.string(footprint).circuitJson()
expect(getPadGeometry(footprint)).toEqual([
{ x: -1, y: 0, width: 0.7, height: 1.5, port_hints: ["1"] },
{ x: 0, y: 0, width: 0.7, height: 1.5, port_hints: ["2"] },
{ x: 1, y: 0, width: 0.7, height: 1.5, port_hints: ["3"] },
{
x: -2.750058,
y: -0.700024,
width: 1,
height: 0.8,
port_hints: ["4"],
},
{
x: 2.750058,
y: -0.700024,
width: 1,
height: 0.8,
port_hints: ["5"],
},
{
x: -2.750058,
y: -2.900172,
width: 1,
height: 0.8,
port_hints: ["6"],
},
{
x: 2.750058,
y: -2.900172,
width: 1,
height: 0.8,
port_hints: ["7"],
},
])
expect(
circuitJson
.filter((element) => element.type === "pcb_hole")
.map(({ x, y, hole_diameter }) => ({ x, y, hole_diameter })),
).toEqual([
{ x: -1.499997, y: -1.800098, hole_diameter: 0.900024 },
{ x: 1.499997, y: -1.800098, hole_diameter: 0.900024 },
])
expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot(
import.meta.path,
"mini_msk12co2",
)
})

Spotted by Graphite (based on custom rule: Custom rule)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@tscircuitbot

Copy link
Copy Markdown
Contributor

Thank you for your contribution! 🎉

PR Rating:
Impact: Tiny

Track your contributions and see the leaderboard at: tscircuit Contribution Tracker


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants