diff --git a/src/fn/dip.ts b/src/fn/dip.ts index 9d3f84d5..abb98e7d 100644 --- a/src/fn/dip.ts +++ b/src/fn/dip.ts @@ -4,6 +4,7 @@ import type { PcbFabricationNoteText, PcbSilkscreenPath, } from "circuit-json" +import { length } from "circuit-json" import { type SilkscreenRef, silkscreenRef } from "src/helpers/silkscreenRef" import { base_def } from "../helpers/zod/base_def" @@ -15,19 +16,13 @@ import { u_curve } from "../helpers/u-curve" import type { NowDefined } from "../helpers/zod/now-defined" import { createRectUnionOutline } from "src/helpers/rect-union-outline" -function convertMilToMm(value: string | number): number { - if (typeof value === "string") { - if (value.trim().toLowerCase().endsWith("mil")) { - return parseFloat(value) * 0.0254 - } - return parseFloat(value) - } - return Number(value) -} - const lengthInMm = z .union([z.string(), z.number()]) - .transform((val) => convertMilToMm(val)) + .transform((dimension) => + length.parse( + typeof dimension === "string" ? dimension.toLowerCase() : dimension, + ), + ) export const extendDipDef = (newDefaults: { w?: string; p?: string }) => base_def @@ -49,11 +44,11 @@ export const extendDipDef = (newDefaults: { w?: string; p?: string }) => .transform((v) => { if (!v.id && !v.od) { if (Math.abs(v.p - 1.27) < 0.01) { - v.id = convertMilToMm("0.55mm") - v.od = convertMilToMm("0.95mm") + v.id = lengthInMm.parse("0.55mm") + v.od = lengthInMm.parse("0.95mm") } else { - v.id = convertMilToMm("0.8mm") - v.od = convertMilToMm("1.6mm") + v.id = lengthInMm.parse("0.8mm") + v.od = lengthInMm.parse("1.6mm") } } else if (!v.id) { v.id = v.od! * (1.0 / 1.5) @@ -63,11 +58,11 @@ export const extendDipDef = (newDefaults: { w?: string; p?: string }) => if (!v.w) { if (v.wide) { - v.w = convertMilToMm("600mil") + v.w = lengthInMm.parse("600mil") } else if (v.narrow) { - v.w = convertMilToMm("300mil") + v.w = lengthInMm.parse("300mil") } else { - v.w = convertMilToMm(newDefaults.w ?? "300mil") + v.w = lengthInMm.parse(newDefaults.w ?? "300mil") } } return v as NowDefined diff --git a/tests/__snapshots__/dip-centimeter-dimensions.snap.svg b/tests/__snapshots__/dip-centimeter-dimensions.snap.svg new file mode 100644 index 00000000..e0412d0d --- /dev/null +++ b/tests/__snapshots__/dip-centimeter-dimensions.snap.svg @@ -0,0 +1 @@ +{REF}{pin1}{pin2}{pin3}{pin4} \ No newline at end of file diff --git a/tests/__snapshots__/dip-inch-dimensions.snap.svg b/tests/__snapshots__/dip-inch-dimensions.snap.svg new file mode 100644 index 00000000..43c08f22 --- /dev/null +++ b/tests/__snapshots__/dip-inch-dimensions.snap.svg @@ -0,0 +1 @@ +{REF}{pin1}{pin2}{pin3}{pin4} \ No newline at end of file diff --git a/tests/dip-units.test.ts b/tests/dip-units.test.ts new file mode 100644 index 00000000..e3c158ec --- /dev/null +++ b/tests/dip-units.test.ts @@ -0,0 +1,77 @@ +import { expect, test } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" +import { dip_def } from "../src/fn/dip" + +test("DIP string and builder convert inch dimensions to millimeters", () => { + const circuitJson = fp + .string("dip4_w0.3in_p0.1in_id0.03in_od0.06in") + .circuitJson() + expect(circuitJson).toEqual( + fp().dip(4).w("0.3in").p("0.1in").id("0.03in").od("0.06in").circuitJson(), + ) + const holes = circuitJson.filter( + (element) => element.type === "pcb_plated_hole", + ) + + expect(holes).toHaveLength(4) + const expectedCenters = [ + { x: -3.81, y: 1.27 }, + { x: -3.81, y: -1.27 }, + { x: 3.81, y: -1.27 }, + { x: 3.81, y: 1.27 }, + ] + for (const [index, expectedCenter] of expectedCenters.entries()) { + expect(holes[index]?.x).toBeCloseTo(expectedCenter.x) + expect(holes[index]?.y).toBeCloseTo(expectedCenter.y) + } + const [pin1, pin2] = holes + if ( + pin1?.shape !== "circular_hole_with_rect_pad" || + pin2?.shape !== "circle" + ) { + throw new Error("Expected a rectangular pin 1 pad and a circular pin 2 pad") + } + expect(pin1.hole_diameter).toBeCloseTo(0.762) + expect(pin1.rect_pad_width).toBeCloseTo(1.524) + expect(pin1.rect_pad_height).toBeCloseTo(1.524) + expect(pin2.hole_diameter).toBeCloseTo(0.762) + expect(pin2.outer_diameter).toBeCloseTo(1.524) + expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot( + import.meta.path, + "dip-inch-dimensions", + ) +}) + +test("DIP builder converts centimeter dimensions to millimeters", () => { + const circuitJson = fp() + .dip(4) + .w("0.762cm") + .p("0.254cm") + .id("0.08cm") + .od("0.16cm") + .circuitJson() + + expect(circuitJson).toEqual(fp.string("dip4").circuitJson()) + expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot( + import.meta.path, + "dip-centimeter-dimensions", + ) +}) + +test.each([7.62, "7.62mm", "300mil", "300MIL", "300Mil", "0.3in", "0.762cm"])( + "DIP dimensions accept %s using the shared length conversion", + (dimension) => { + const parameters = dip_def.parse({ + fn: "dip", + w: dimension, + p: dimension, + id: dimension, + od: dimension, + }) + + for (const dimensionName of ["w", "p", "id", "od"] as const) { + expect(parameters[dimensionName]).toBeCloseTo(7.62) + } + }, +)