From be9cc91cf9207b202f2270beb4283300d7e49d34 Mon Sep 17 00:00:00 2001 From: Kemalyavas Date: Tue, 13 Jan 2026 05:22:44 +0300 Subject: [PATCH 1/4] feat: add PDIP footprint support Add support for PDIP (Plastic Dual In-line Package) footprint string. PDIP is an alias for DIP packages, commonly used in datasheets and component listings. Closes #371 --- src/fn/index.ts | 1 + src/fn/pdip.ts | 20 +++++++++++ src/footprinter.ts | 3 ++ tests/__snapshots__/pdip14.snap.svg | 1 + tests/__snapshots__/pdip16.snap.svg | 1 + tests/__snapshots__/pdip8.snap.svg | 1 + tests/__snapshots__/pdip8_wide.snap.svg | 1 + tests/pdip.test.ts | 48 +++++++++++++++++++++++++ 8 files changed, 76 insertions(+) create mode 100644 src/fn/pdip.ts create mode 100644 tests/__snapshots__/pdip14.snap.svg create mode 100644 tests/__snapshots__/pdip16.snap.svg create mode 100644 tests/__snapshots__/pdip8.snap.svg create mode 100644 tests/__snapshots__/pdip8_wide.snap.svg create mode 100644 tests/pdip.test.ts diff --git a/src/fn/index.ts b/src/fn/index.ts index 07668c12..285b297a 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..84e63179 --- /dev/null +++ b/src/fn/pdip.ts @@ -0,0 +1,20 @@ +/** + * PDIP (Plastic Dual In-line Package) footprint + * + * PDIP is the standard plastic version of DIP packages. + * Common variants: PDIP-8, PDIP-14, PDIP-16, PDIP-20, PDIP-28, etc. + * + * Standard specifications: + * - Pin pitch: 2.54mm (100 mil) + * - Row spacing: 7.62mm (300 mil) for narrow, 15.24mm (600 mil) for wide + */ + +import { dip, dip_def, extendDipDef, getCcwDipCoords } from "./dip" + +// PDIP uses the same implementation as DIP +export const pdip = dip + +// Export the definition for type compatibility +export const pdip_def = dip_def +export const extendPdipDef = extendDipDef +export const getCcwPdipCoords = getCcwDipCoords diff --git a/src/footprinter.ts b/src/footprinter.ts index 04ed1419..86118dff 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -39,6 +39,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 diff --git a/tests/__snapshots__/pdip14.snap.svg b/tests/__snapshots__/pdip14.snap.svg new file mode 100644 index 00000000..dc26ac0b --- /dev/null +++ b/tests/__snapshots__/pdip14.snap.svg @@ -0,0 +1 @@ +{REF}{pin1}{pin2}{pin3}{pin4}{pin5}{pin6}{pin7}{pin8}{pin9}{pin10}{pin11}{pin12}{pin13}{pin14} \ No newline at end of file diff --git a/tests/__snapshots__/pdip16.snap.svg b/tests/__snapshots__/pdip16.snap.svg new file mode 100644 index 00000000..595c0386 --- /dev/null +++ b/tests/__snapshots__/pdip16.snap.svg @@ -0,0 +1 @@ +{REF}{pin1}{pin2}{pin3}{pin4}{pin5}{pin6}{pin7}{pin8}{pin9}{pin10}{pin11}{pin12}{pin13}{pin14}{pin15}{pin16} \ No newline at end of file diff --git a/tests/__snapshots__/pdip8.snap.svg b/tests/__snapshots__/pdip8.snap.svg new file mode 100644 index 00000000..4a51d472 --- /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/__snapshots__/pdip8_wide.snap.svg b/tests/__snapshots__/pdip8_wide.snap.svg new file mode 100644 index 00000000..168a13dc --- /dev/null +++ b/tests/__snapshots__/pdip8_wide.snap.svg @@ -0,0 +1 @@ +{REF}{pin1}{pin2}{pin3}{pin4}{pin5}{pin6}{pin7}{pin8} \ No newline at end of file diff --git a/tests/pdip.test.ts b/tests/pdip.test.ts new file mode 100644 index 00000000..9a6a3a6d --- /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 footprint", () => { + const circuitJson = fp.string("pdip8").circuitJson() as AnyCircuitElement[] + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8") +}) + +test("pdip8 parameters match dip8", () => { + const pdipJson = fp.string("pdip8").json() + const dipJson = fp.string("dip8").json() + + // PDIP should have same parameters as DIP (except fn name) + expect(pdipJson.num_pins).toBe(dipJson.num_pins) + expect(pdipJson.w).toBe(dipJson.w) + expect(pdipJson.p).toBe(dipJson.p) + expect(pdipJson.id).toBe(dipJson.id) + expect(pdipJson.od).toBe(dipJson.od) +}) + +test("pdip14 footprint", () => { + const circuitJson = fp.string("pdip14").circuitJson() as AnyCircuitElement[] + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip14") +}) + +test("pdip16 footprint", () => { + const circuitJson = fp.string("pdip16").circuitJson() as AnyCircuitElement[] + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip16") +}) + +test("pdip8_wide footprint", () => { + const circuitJson = fp + .string("pdip8_wide") + .circuitJson() as AnyCircuitElement[] + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8_wide") +}) + +test("PDIP8 string resolves using lowercase function", () => { + const uppercaseJson = fp.string("PDIP8").json() + const lowercaseJson = fp.string("pdip8").json() + expect(uppercaseJson).toEqual(lowercaseJson) +}) From 0cb9e30b7304875f1eadda817ac2537b9878009e Mon Sep 17 00:00:00 2001 From: Kemalyavas Date: Tue, 13 Jan 2026 05:30:58 +0300 Subject: [PATCH 2/4] fix: split test file into separate files (one test per file) --- tests/pdip.test.ts | 48 --------------------------------------------- tests/pdip1.test.ts | 10 ++++++++++ tests/pdip2.test.ts | 14 +++++++++++++ tests/pdip3.test.ts | 10 ++++++++++ tests/pdip4.test.ts | 10 ++++++++++ tests/pdip5.test.ts | 12 ++++++++++++ tests/pdip6.test.ts | 8 ++++++++ 7 files changed, 64 insertions(+), 48 deletions(-) delete mode 100644 tests/pdip.test.ts create mode 100644 tests/pdip1.test.ts create mode 100644 tests/pdip2.test.ts create mode 100644 tests/pdip3.test.ts create mode 100644 tests/pdip4.test.ts create mode 100644 tests/pdip5.test.ts create mode 100644 tests/pdip6.test.ts diff --git a/tests/pdip.test.ts b/tests/pdip.test.ts deleted file mode 100644 index 9a6a3a6d..00000000 --- a/tests/pdip.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -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 footprint", () => { - const circuitJson = fp.string("pdip8").circuitJson() as AnyCircuitElement[] - const svgContent = convertCircuitJsonToPcbSvg(circuitJson) - expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8") -}) - -test("pdip8 parameters match dip8", () => { - const pdipJson = fp.string("pdip8").json() - const dipJson = fp.string("dip8").json() - - // PDIP should have same parameters as DIP (except fn name) - expect(pdipJson.num_pins).toBe(dipJson.num_pins) - expect(pdipJson.w).toBe(dipJson.w) - expect(pdipJson.p).toBe(dipJson.p) - expect(pdipJson.id).toBe(dipJson.id) - expect(pdipJson.od).toBe(dipJson.od) -}) - -test("pdip14 footprint", () => { - const circuitJson = fp.string("pdip14").circuitJson() as AnyCircuitElement[] - const svgContent = convertCircuitJsonToPcbSvg(circuitJson) - expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip14") -}) - -test("pdip16 footprint", () => { - const circuitJson = fp.string("pdip16").circuitJson() as AnyCircuitElement[] - const svgContent = convertCircuitJsonToPcbSvg(circuitJson) - expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip16") -}) - -test("pdip8_wide footprint", () => { - const circuitJson = fp - .string("pdip8_wide") - .circuitJson() as AnyCircuitElement[] - const svgContent = convertCircuitJsonToPcbSvg(circuitJson) - expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8_wide") -}) - -test("PDIP8 string resolves using lowercase function", () => { - const uppercaseJson = fp.string("PDIP8").json() - const lowercaseJson = fp.string("pdip8").json() - expect(uppercaseJson).toEqual(lowercaseJson) -}) diff --git a/tests/pdip1.test.ts b/tests/pdip1.test.ts new file mode 100644 index 00000000..615e814b --- /dev/null +++ b/tests/pdip1.test.ts @@ -0,0 +1,10 @@ +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 footprint", () => { + const circuitJson = fp.string("pdip8").circuitJson() as AnyCircuitElement[] + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8") +}) diff --git a/tests/pdip2.test.ts b/tests/pdip2.test.ts new file mode 100644 index 00000000..29771dab --- /dev/null +++ b/tests/pdip2.test.ts @@ -0,0 +1,14 @@ +import { test, expect } from "bun:test" +import { fp } from "../src/footprinter" + +test("pdip8 parameters match dip8", () => { + const pdipJson = fp.string("pdip8").json() + const dipJson = fp.string("dip8").json() + + // PDIP should have same parameters as DIP (except fn name) + expect(pdipJson.num_pins).toBe(dipJson.num_pins) + expect(pdipJson.w).toBe(dipJson.w) + expect(pdipJson.p).toBe(dipJson.p) + expect(pdipJson.id).toBe(dipJson.id) + expect(pdipJson.od).toBe(dipJson.od) +}) diff --git a/tests/pdip3.test.ts b/tests/pdip3.test.ts new file mode 100644 index 00000000..90440879 --- /dev/null +++ b/tests/pdip3.test.ts @@ -0,0 +1,10 @@ +import { test, expect } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" +import type { AnyCircuitElement } from "circuit-json" + +test("pdip14 footprint", () => { + const circuitJson = fp.string("pdip14").circuitJson() as AnyCircuitElement[] + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip14") +}) diff --git a/tests/pdip4.test.ts b/tests/pdip4.test.ts new file mode 100644 index 00000000..a7bfe241 --- /dev/null +++ b/tests/pdip4.test.ts @@ -0,0 +1,10 @@ +import { test, expect } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" +import type { AnyCircuitElement } from "circuit-json" + +test("pdip16 footprint", () => { + const circuitJson = fp.string("pdip16").circuitJson() as AnyCircuitElement[] + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip16") +}) diff --git a/tests/pdip5.test.ts b/tests/pdip5.test.ts new file mode 100644 index 00000000..f1f8a5a4 --- /dev/null +++ b/tests/pdip5.test.ts @@ -0,0 +1,12 @@ +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_wide footprint", () => { + const circuitJson = fp + .string("pdip8_wide") + .circuitJson() as AnyCircuitElement[] + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8_wide") +}) diff --git a/tests/pdip6.test.ts b/tests/pdip6.test.ts new file mode 100644 index 00000000..36f769f8 --- /dev/null +++ b/tests/pdip6.test.ts @@ -0,0 +1,8 @@ +import { test, expect } from "bun:test" +import { fp } from "../src/footprinter" + +test("PDIP8 string resolves using lowercase function", () => { + const uppercaseJson = fp.string("PDIP8").json() + const lowercaseJson = fp.string("pdip8").json() + expect(uppercaseJson).toEqual(lowercaseJson) +}) From 747e4bde620144660c8743a91757842eca152211 Mon Sep 17 00:00:00 2001 From: Kemalyavas Date: Tue, 13 Jan 2026 10:27:03 +0300 Subject: [PATCH 3/4] fix: remove unnecessary comments and add kicad parity test --- src/fn/pdip.ts | 19 +------------------ .../kicad-parity/__snapshots__/pdip8.snap.svg | 1 + .../pdip8_boolean_difference.snap.svg | 3 +++ tests/kicad-parity/pdip8_kicad_parity.test.ts | 18 ++++++++++++++++++ 4 files changed, 23 insertions(+), 18 deletions(-) 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 diff --git a/src/fn/pdip.ts b/src/fn/pdip.ts index 84e63179..53eff3a8 100644 --- a/src/fn/pdip.ts +++ b/src/fn/pdip.ts @@ -1,20 +1,3 @@ -/** - * PDIP (Plastic Dual In-line Package) footprint - * - * PDIP is the standard plastic version of DIP packages. - * Common variants: PDIP-8, PDIP-14, PDIP-16, PDIP-20, PDIP-28, etc. - * - * Standard specifications: - * - Pin pitch: 2.54mm (100 mil) - * - Row spacing: 7.62mm (300 mil) for narrow, 15.24mm (600 mil) for wide - */ +import { dip } from "./dip" -import { dip, dip_def, extendDipDef, getCcwDipCoords } from "./dip" - -// PDIP uses the same implementation as DIP export const pdip = dip - -// Export the definition for type compatibility -export const pdip_def = dip_def -export const extendPdipDef = extendDipDef -export const getCcwPdipCoords = getCcwDipCoords diff --git a/tests/kicad-parity/__snapshots__/pdip8.snap.svg b/tests/kicad-parity/__snapshots__/pdip8.snap.svg new file mode 100644 index 00000000..a0d295df --- /dev/null +++ b/tests/kicad-parity/__snapshots__/pdip8.snap.svg @@ -0,0 +1 @@ +{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..349c164c --- /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)pdip8_w7.62mmKiCad: 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..da69b6f6 --- /dev/null +++ b/tests/kicad-parity/pdip8_kicad_parity.test.ts @@ -0,0 +1,18 @@ +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 } = + await compareFootprinterVsKicad( + "pdip8_w7.62mm", + "Package_DIP.pretty/DIP-8_W7.62mm.circuit.json", + ) + + const svgContent = convertCircuitJsonToPcbSvg(combinedFootprintElements) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8") + expect(booleanDifferenceSvg).toMatchSvgSnapshot( + import.meta.path, + "pdip8_boolean_difference", + ) +}) From ea0e0332f7ea7e8c8347c5124e631b7edc9f8a70 Mon Sep 17 00:00:00 2001 From: Kemalyavas Date: Tue, 13 Jan 2026 12:33:13 +0300 Subject: [PATCH 4/4] fix: replace kicad parity test with alias verification test PDIP is an alias for DIP (Plastic Dual In-line Package), so there's no separate PDIP footprint in KiCAD library. Instead, added a test that verifies pdip produces identical circuit json as dip. --- .../kicad-parity/__snapshots__/pdip8.snap.svg | 1 - .../pdip8_boolean_difference.snap.svg | 3 --- tests/kicad-parity/pdip8_kicad_parity.test.ts | 18 ----------------- tests/pdip7.test.ts | 20 +++++++++++++++++++ 4 files changed, 20 insertions(+), 22 deletions(-) delete mode 100644 tests/kicad-parity/__snapshots__/pdip8.snap.svg delete mode 100644 tests/kicad-parity/__snapshots__/pdip8_boolean_difference.snap.svg delete mode 100644 tests/kicad-parity/pdip8_kicad_parity.test.ts create mode 100644 tests/pdip7.test.ts diff --git a/tests/kicad-parity/__snapshots__/pdip8.snap.svg b/tests/kicad-parity/__snapshots__/pdip8.snap.svg deleted file mode 100644 index a0d295df..00000000 --- a/tests/kicad-parity/__snapshots__/pdip8.snap.svg +++ /dev/null @@ -1 +0,0 @@ -{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 deleted file mode 100644 index 349c164c..00000000 --- a/tests/kicad-parity/__snapshots__/pdip8_boolean_difference.snap.svg +++ /dev/null @@ -1,3 +0,0 @@ -DIP-8_W7.62mm - Alignment Analysis (Footprinter vs KiCad)pdip8_w7.62mmKiCad: 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 deleted file mode 100644 index da69b6f6..00000000 --- a/tests/kicad-parity/pdip8_kicad_parity.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -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 } = - await compareFootprinterVsKicad( - "pdip8_w7.62mm", - "Package_DIP.pretty/DIP-8_W7.62mm.circuit.json", - ) - - const svgContent = convertCircuitJsonToPcbSvg(combinedFootprintElements) - expect(svgContent).toMatchSvgSnapshot(import.meta.path, "pdip8") - expect(booleanDifferenceSvg).toMatchSvgSnapshot( - import.meta.path, - "pdip8_boolean_difference", - ) -}) diff --git a/tests/pdip7.test.ts b/tests/pdip7.test.ts new file mode 100644 index 00000000..d71e5f8e --- /dev/null +++ b/tests/pdip7.test.ts @@ -0,0 +1,20 @@ +import { test, expect } from "bun:test" +import { fp } from "../src/footprinter" + +test("pdip produces identical circuit json as dip", () => { + const pdipJson = fp.string("pdip8").circuitJson() + const dipJson = fp.string("dip8").circuitJson() + + // PDIP is an alias for DIP, so they should produce identical circuit elements + // Only difference might be internal IDs, so compare structure + expect(pdipJson.length).toBe(dipJson.length) + + // Compare each element type and position + for (let i = 0; i < pdipJson.length; i++) { + const pdipEl = pdipJson[i] as any + const dipEl = dipJson[i] as any + expect(pdipEl.type).toBe(dipEl.type) + if (pdipEl.x !== undefined) expect(pdipEl.x).toBe(dipEl.x) + if (pdipEl.y !== undefined) expect(pdipEl.y).toBe(dipEl.y) + } +})