From d42f315efe9ab01af55328ef7f1449db6fffedd1 Mon Sep 17 00:00:00 2001 From: chengdazhi Date: Mon, 13 Jul 2026 15:47:16 +0800 Subject: [PATCH] feat: implement PDIP footprint (PDIP-8) Add pdip as a footprint function reusing the standard DIP layout (0.3in/7.62mm row width, 2.54mm pitch, 0.8mm holes, 1.6mm pads), matching KiCad's PDIP/DIP-8_W7.62mm. - src/fn/pdip.ts: pdip function and pdip_def (reuses dip) - footprinter string support: pdip8 and hyphenated PDIP-8 alias - snapshot tests + KiCad parity test vs DIP-8_W7.62mm (courtyard IoU 99.90%, pads fully overlap) --- src/fn/index.ts | 1 + src/fn/pdip.ts | 16 +++++++ src/footprinter.ts | 4 ++ tests/__snapshots__/pdip8.snap.svg | 1 + .../kicad-parity/__snapshots__/pdip8.snap.svg | 1 + .../pdip8_boolean_difference.snap.svg | 3 ++ tests/kicad-parity/pdip8_kicad_parity.test.ts | 25 ++++++++++ tests/pdip.test.ts | 48 +++++++++++++++++++ 8 files changed, 99 insertions(+) create mode 100644 src/fn/pdip.ts create mode 100644 tests/__snapshots__/pdip8.snap.svg create mode 100644 tests/kicad-parity/__snapshots__/pdip8.snap.svg create mode 100644 tests/kicad-parity/__snapshots__/pdip8_boolean_difference.snap.svg create mode 100644 tests/kicad-parity/pdip8_kicad_parity.test.ts create mode 100644 tests/pdip.test.ts diff --git a/src/fn/index.ts b/src/fn/index.ts index 94c0370e..87022c86 100644 --- a/src/fn/index.ts +++ b/src/fn/index.ts @@ -1,4 +1,5 @@ export { dip } from "./dip" +export { pdip } from "./pdip" export { diode } from "./diode" export { cap } from "./cap" export { led } from "./led" diff --git a/src/fn/pdip.ts b/src/fn/pdip.ts new file mode 100644 index 00000000..b1a055cf --- /dev/null +++ b/src/fn/pdip.ts @@ -0,0 +1,16 @@ +import type { AnyCircuitElement } from "circuit-json" +import { dip, dip_def } from "./dip" + +export const pdip_def = dip_def + +/** + * PDIP (Plastic Dual In-line Package) + * + * Standard DIP layout with a 0.3in (7.62mm) row width, 2.54mm pitch, + * 0.8mm holes and 1.6mm pads. PDIP-8 matches KiCad's DIP-8_W7.62mm. + */ +export const pdip = ( + raw_params: Parameters[0], +): { circuitJson: AnyCircuitElement[]; parameters: any } => { + return dip(raw_params) +} diff --git a/src/footprinter.ts b/src/footprinter.ts index fa51a3b4..dd0942b5 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -40,6 +40,9 @@ export type Footprinter = { dip: ( num_pins?: number, ) => FootprinterParamsBuilder<"w" | "p" | "id" | "od" | "wide" | "narrow"> + pdip: ( + num_pins?: number, + ) => FootprinterParamsBuilder<"w" | "p" | "id" | "od" | "wide" | "narrow"> cap: () => FootprinterParamsBuilder res: () => FootprinterParamsBuilder diode: () => FootprinterParamsBuilder @@ -273,6 +276,7 @@ const normalizeDefinition = (def: string): string => { .replace(/^pinheader(?=[\d_]|$)/i, "pinrow") .replace(/^sot23-(\d+)(?=_|$)/i, "sot23_$1") .replace(/^sot-223-(\d+)(?=_|$)/i, "sot223_$1") + .replace(/^pdip-(\d+)(?=_|$)/i, "pdip$1") .replace(/^to-220f-(\d+)(?=_|$)/i, "to220f_$1") .replace(/^jst_(ph|sh|zh)_(\d+)(?=_|$)/i, "jst$2_$1") } diff --git a/tests/__snapshots__/pdip8.snap.svg b/tests/__snapshots__/pdip8.snap.svg new file mode 100644 index 00000000..f73cac78 --- /dev/null +++ b/tests/__snapshots__/pdip8.snap.svg @@ -0,0 +1 @@ +{REF}{pin1}{pin2}{pin3}{pin4}{pin5}{pin6}{pin7}{pin8} \ No newline at end of file diff --git a/tests/kicad-parity/__snapshots__/pdip8.snap.svg b/tests/kicad-parity/__snapshots__/pdip8.snap.svg new file mode 100644 index 00000000..62d43f87 --- /dev/null +++ b/tests/kicad-parity/__snapshots__/pdip8.snap.svg @@ -0,0 +1 @@ +{REF}REF**Diff: 0.00%{pin1}{pin2}{pin3}{pin4}{pin5}{pin6}{pin7}{pin8} \ No newline at end of file diff --git a/tests/kicad-parity/__snapshots__/pdip8_boolean_difference.snap.svg b/tests/kicad-parity/__snapshots__/pdip8_boolean_difference.snap.svg new file mode 100644 index 00000000..d4260e91 --- /dev/null +++ b/tests/kicad-parity/__snapshots__/pdip8_boolean_difference.snap.svg @@ -0,0 +1,3 @@ +DIP-8_W7.62mm - Alignment Analysis (Footprinter vs KiCad)pdip8KiCad: DIP-8_W7.62mmPerfect alignment = complete overlap \ No newline at end of file diff --git a/tests/kicad-parity/pdip8_kicad_parity.test.ts b/tests/kicad-parity/pdip8_kicad_parity.test.ts new file mode 100644 index 00000000..118dc014 --- /dev/null +++ b/tests/kicad-parity/pdip8_kicad_parity.test.ts @@ -0,0 +1,25 @@ +import { expect, test } from "bun:test" +import { compareFootprinterVsKicad } from "../fixtures/compareFootprinterVsKicad" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" + +test("parity/pdip8", async () => { + const { + avgRelDiff, + combinedFootprintElements, + booleanDifferenceSvg, + courtyardDiffPercent, + } = await compareFootprinterVsKicad( + "pdip8", + "Package_DIP.pretty/DIP-8_W7.62mm.circuit.json", + ) + + const svgContent = convertCircuitJsonToPcbSvg(combinedFootprintElements, { + showCourtyards: true, + }) + expect(courtyardDiffPercent).toBeLessThan(0.5) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8") + expect(booleanDifferenceSvg).toMatchSvgSnapshot( + import.meta.path, + "pdip8_boolean_difference", + ) +}) diff --git a/tests/pdip.test.ts b/tests/pdip.test.ts new file mode 100644 index 00000000..f0d7afe1 --- /dev/null +++ b/tests/pdip.test.ts @@ -0,0 +1,48 @@ +import { test, expect } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" +import type { AnyCircuitElement } from "circuit-json" + +test("pdip8", () => { + const circuitJson = fp.string("pdip8").circuitJson() as AnyCircuitElement[] + const json = fp.string("pdip8").json() + + expect(json).toMatchInlineSnapshot( + { + fn: "pdip", + id: 0.8, + num_pins: 8, + od: 1.6, + p: 2.54, + w: 7.62, + }, + ` +{ + "fn": "pdip", + "id": 0.8, + "nosquareplating": false, + "num_pins": 8, + "od": 1.6, + "p": 2.54, + "w": 7.62, +} +`, + ) + + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8") +}) + +test("PDIP-8 (alias)", () => { + const aliasSvg = convertCircuitJsonToPcbSvg(fp.string("PDIP-8").circuitJson()) + const canonicalSvg = convertCircuitJsonToPcbSvg( + fp.string("pdip8").circuitJson(), + ) + expect(aliasSvg).toEqual(canonicalSvg) +}) + +test("PDIP-8 matches DIP-8 geometry", () => { + const pdipJson = fp.string("pdip8").circuitJson() + const dipJson = fp.string("dip8").circuitJson() + expect(pdipJson).toEqual(dipJson) +})