From fd3b80de3e0af3bdfee4e6eeb19369a2cc0ea1b4 Mon Sep 17 00:00:00 2001 From: Furox-Art <177975472+Furox-Art@users.noreply.github.com> Date: Fri, 18 Sep 2026 17:12:41 +0300 Subject: [PATCH 1/4] fix: use the standard 2.54mm JEDEC pitch for to220 and honor the p parameter The lead pitch was derived from the plastic body width (2.6mm at the default w=13mm) instead of the fixed JEDEC TO-220 pitch of 0.1in / 2.54mm, and the declared p parameter was parsed but never read. Pitch is now fixed at 2.54mm (matching the sibling to220f, which already hardcodes 2.54mm to match KiCad), overridable via p, and independent of body width. Closes #790 --- .github/workflows/update-snapshots-temp.yml | 37 +++++++++++++ src/fn/to220.ts | 15 +++--- tests/to220-pitch.test.ts | 60 +++++++++++++++++++++ 3 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/update-snapshots-temp.yml create mode 100644 tests/to220-pitch.test.ts diff --git a/.github/workflows/update-snapshots-temp.yml b/.github/workflows/update-snapshots-temp.yml new file mode 100644 index 000000000..2f49581ac --- /dev/null +++ b/.github/workflows/update-snapshots-temp.yml @@ -0,0 +1,37 @@ +name: Update to220 snapshots (temp) + +on: + workflow_dispatch: + +jobs: + update-snapshots: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + repository: Furox-Art/footprinter + ref: fix/to220-standard-pitch + + - name: Setup bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install + + - name: Update snapshots + run: bun test tests/to220.test.ts -u + + - name: Commit snapshots + run: | + git config user.name "Furox-Art" + git config user.email "177975472+Furox-Art@users.noreply.github.com" + git add tests/__snapshots__/to220*.snap.svg + if git diff --cached --quiet; then + echo "No snapshot changes" + else + git commit -m "chore: update to220 snapshots (ubuntu ci)" + git push origin fix/to220-standard-pitch + fi diff --git a/src/fn/to220.ts b/src/fn/to220.ts index 3cbe0a86e..c0ec8c611 100644 --- a/src/fn/to220.ts +++ b/src/fn/to220.ts @@ -4,6 +4,7 @@ import { type PcbSilkscreenPath, length, } from "circuit-json" +import { mm } from "@tscircuit/mm" import { platedhole } from "src/helpers/platedhole" import { z } from "zod" import { type SilkscreenRef, silkscreenRef } from "../helpers/silkscreenRef" @@ -11,7 +12,7 @@ import { base_def } from "../helpers/zod/base_def" export const to220_def = base_def.extend({ fn: z.string(), - p: length.optional().default("5.0mm"), + p: length.optional().default("2.54mm"), id: length.optional().default("1.0mm"), od: length.optional().default("1.9mm"), w: length.optional().default("13mm"), @@ -36,15 +37,15 @@ export const to220 = ( const halfWidth = w / 2 const halfHeight = h / 2 - const minPitch = 2.5 - const maxHoleWidth = w * 0.4 - const computedPitch = Math.max(minPitch, maxHoleWidth / (numPins - 1)) + // TO-220 lead pitch is fixed (JEDEC: 0.1in = 2.54mm) and must not scale with + // the plastic body width; `p` keeps the pitch explicit and overridable. + const pitch = mm(parameters.p) const plated_holes = Array.from({ length: numPins }, (_, i) => { const x = numPins % 2 === 0 - ? (i - numPins / 2 + 0.5) * computedPitch - : (i - Math.floor(numPins / 2)) * computedPitch + ? (i - numPins / 2 + 0.5) * pitch + : (i - Math.floor(numPins / 2)) * pitch return platedhole(i + 1, x, holeY, id, od) }) @@ -135,6 +136,6 @@ export const to220 = ( silkscreenRefText as AnyCircuitElement, courtyard, ], - parameters: { ...parameters, p: computedPitch }, + parameters: { ...parameters, p: pitch }, } } diff --git a/tests/to220-pitch.test.ts b/tests/to220-pitch.test.ts new file mode 100644 index 000000000..b04ee013c --- /dev/null +++ b/tests/to220-pitch.test.ts @@ -0,0 +1,60 @@ +import { expect, test } from "bun:test" +import { fp } from "src/footprinter" +import { to220 } from "src/fn/to220" + +/** + * TO-220 lead pitch is fixed by JEDEC at 0.1in (2.54mm). It used to be derived + * from the plastic body width (`max(2.5, w * 0.4 / (numPins - 1))` → 2.6mm at + * the default w=13mm), and the declared `p` parameter (default "5.0mm") was + * parsed but never read. + * + * Issue #790: pitch must be the standard 2.54mm regardless of body width, with + * `p` honored as an explicit override — matching the sibling `to220f`, which + * already hardcodes 2.54mm "to match KiCad". + */ + +const holeXs = (cj: any[]) => + cj + .filter((e: any) => e.type === "pcb_plated_hole") + .map((e: any) => e.x) + .sort((a, b) => a - b) + +const gaps = (xs: number[]) => + xs.slice(1).map((x, i) => Math.round((x - xs[i]!) * 1e6) / 1e6) + +test("to220_3 uses the standard 2.54mm JEDEC pitch by default", () => { + const xs = holeXs(fp.string("to220_3").circuitJson()) + expect(xs).toHaveLength(3) + expect(gaps(xs)).toEqual([2.54, 2.54]) +}) + +test("declared p parameter overrides the pitch", () => { + const xs = holeXs( + to220({ fn: "to220", num_pins: 3, p: "5mm" }).circuitJson as any, + ) + expect(gaps(xs)).toEqual([5, 5]) +}) + +test("pitch does not scale with body width", () => { + const xs = holeXs( + to220({ fn: "to220", num_pins: 3, w: "25.4mm" }).circuitJson as any, + ) + expect(gaps(xs)).toEqual([2.54, 2.54]) +}) + +test("even pin counts stay centered at the standard pitch", () => { + const xs = holeXs(fp.string("to220_4").circuitJson()) + expect(xs).toHaveLength(4) + expect((xs[0]! + xs[xs.length - 1]!) / 2).toBeCloseTo(0, 9) + expect(gaps(xs)).toEqual([2.54, 2.54, 2.54]) +}) + +test("5-pin variant uses the standard pitch", () => { + const xs = holeXs(fp.string("to220_5").circuitJson()) + expect(gaps(xs)).toEqual([2.54, 2.54, 2.54, 2.54]) +}) + +test("reported parameters reflect the effective pitch", () => { + const { parameters } = to220({ fn: "to220", num_pins: 3 }) + expect(parameters.p).toBe(2.54) +}) From 98c2213bfcafcd2dc9ebe8fcab670d79b6b8a160 Mon Sep 17 00:00:00 2001 From: Furox-Art <177975472+Furox-Art@users.noreply.github.com> Date: Fri, 18 Sep 2026 17:14:11 +0300 Subject: [PATCH 2/4] fix: use BUN_UPDATE_SNAPSHOTS env in temp workflow --- .github/workflows/update-snapshots-temp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-snapshots-temp.yml b/.github/workflows/update-snapshots-temp.yml index 2f49581ac..9f59eabfc 100644 --- a/.github/workflows/update-snapshots-temp.yml +++ b/.github/workflows/update-snapshots-temp.yml @@ -22,7 +22,7 @@ jobs: run: bun install - name: Update snapshots - run: bun test tests/to220.test.ts -u + run: BUN_UPDATE_SNAPSHOTS=1 bun test tests/to220.test.ts - name: Commit snapshots run: | From 9a8dc8cd47c6356a36e9baa01e506f385a3b271e Mon Sep 17 00:00:00 2001 From: Furox-Art <177975472+Furox-Art@users.noreply.github.com> Date: Fri, 18 Sep 2026 14:15:09 +0000 Subject: [PATCH 3/4] chore: update to220 snapshots (ubuntu ci) --- tests/__snapshots__/to220_2.snap.svg | 2 +- tests/__snapshots__/to220_3.snap.svg | 2 +- tests/__snapshots__/to220_4.snap.svg | 2 +- tests/__snapshots__/to220_5.snap.svg | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/__snapshots__/to220_2.snap.svg b/tests/__snapshots__/to220_2.snap.svg index 7844a8e6e..bb65e0a72 100644 --- a/tests/__snapshots__/to220_2.snap.svg +++ b/tests/__snapshots__/to220_2.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file diff --git a/tests/__snapshots__/to220_3.snap.svg b/tests/__snapshots__/to220_3.snap.svg index 8a345fa7a..ce43eb9aa 100644 --- a/tests/__snapshots__/to220_3.snap.svg +++ b/tests/__snapshots__/to220_3.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file diff --git a/tests/__snapshots__/to220_4.snap.svg b/tests/__snapshots__/to220_4.snap.svg index e75600a94..af4cdcaaf 100644 --- a/tests/__snapshots__/to220_4.snap.svg +++ b/tests/__snapshots__/to220_4.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file diff --git a/tests/__snapshots__/to220_5.snap.svg b/tests/__snapshots__/to220_5.snap.svg index 45c1d5ade..c8d5cca70 100644 --- a/tests/__snapshots__/to220_5.snap.svg +++ b/tests/__snapshots__/to220_5.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file From 02ea63ffc7c8ce79d336ea141b2e2ab077437ecf Mon Sep 17 00:00:00 2001 From: Furox-Art <177975472+Furox-Art@users.noreply.github.com> Date: Fri, 18 Sep 2026 17:15:27 +0300 Subject: [PATCH 4/4] chore: remove temp snapshot workflow --- .github/workflows/update-snapshots-temp.yml | 37 --------------------- 1 file changed, 37 deletions(-) delete mode 100644 .github/workflows/update-snapshots-temp.yml diff --git a/.github/workflows/update-snapshots-temp.yml b/.github/workflows/update-snapshots-temp.yml deleted file mode 100644 index 9f59eabfc..000000000 --- a/.github/workflows/update-snapshots-temp.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Update to220 snapshots (temp) - -on: - workflow_dispatch: - -jobs: - update-snapshots: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - repository: Furox-Art/footprinter - ref: fix/to220-standard-pitch - - - name: Setup bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: latest - - - name: Install dependencies - run: bun install - - - name: Update snapshots - run: BUN_UPDATE_SNAPSHOTS=1 bun test tests/to220.test.ts - - - name: Commit snapshots - run: | - git config user.name "Furox-Art" - git config user.email "177975472+Furox-Art@users.noreply.github.com" - git add tests/__snapshots__/to220*.snap.svg - if git diff --cached --quiet; then - echo "No snapshot changes" - else - git commit -m "chore: update to220 snapshots (ubuntu ci)" - git push origin fix/to220-standard-pitch - fi