diff --git a/src/fn/fuseclip.ts b/src/fn/fuseclip.ts new file mode 100644 index 00000000..d5c7632d --- /dev/null +++ b/src/fn/fuseclip.ts @@ -0,0 +1,73 @@ +import type { + AnyCircuitElement, + PcbCourtyardRect, + PcbPlatedHole, +} from "circuit-json" +import { length } from "circuit-json" +import { z } from "zod" +import { silkscreenRef } from "../helpers/silkscreenRef" +import { silkscreenpath } from "../helpers/silkscreenpath" +import { base_def } from "../helpers/zod/base_def" + +export const fuseclip_def = base_def.extend({ + fn: z.literal("fuseclip"), + num_pins: z.literal(2).default(2), + p: length.default("5.150104mm"), + holewidth: length.default("1.999996mm"), + holeheight: length.default("0.599948mm"), + outerwidth: length.default("2.400046mm"), + outerheight: length.default("0.999998mm"), +}) + +export const fuseclip = ( + rawParams: z.input, +): { circuitJson: AnyCircuitElement[]; parameters: any } => { + const parameters = fuseclip_def.parse(rawParams) + const { p, holewidth, holeheight, outerwidth, outerheight } = parameters + const makeSlot = (pin: number, x: number): PcbPlatedHole => ({ + type: "pcb_plated_hole", + pcb_plated_hole_id: "", + pcb_component_id: "", + pcb_port_id: "", + x, + y: 0, + shape: "pill", + hole_width: holewidth, + hole_height: holeheight, + outer_width: outerwidth, + outer_height: outerheight, + ccw_rotation: 90, + layers: ["top", "bottom"], + port_hints: [pin.toString()], + }) + const halfPitch = p / 2 + const pads = [makeSlot(1, -halfPitch), makeSlot(2, halfPitch)] + const halfBodyWidth = 2.75 + const halfBodyHeight = 3.6 + const silkscreen = silkscreenpath([ + { x: -halfBodyWidth, y: -halfBodyHeight }, + { x: halfBodyWidth, y: -halfBodyHeight }, + { x: halfBodyWidth, y: halfBodyHeight }, + { x: -halfBodyWidth, y: halfBodyHeight }, + { x: -halfBodyWidth, y: -halfBodyHeight }, + ]) + const courtyard: PcbCourtyardRect = { + type: "pcb_courtyard_rect", + pcb_courtyard_rect_id: "", + pcb_component_id: "", + center: { x: 0, y: 0 }, + width: Math.max(halfBodyWidth * 2, p + outerheight) + 0.5, + height: Math.max(halfBodyHeight * 2, outerwidth) + 0.5, + layer: "top", + } + + return { + circuitJson: [ + ...pads, + silkscreen, + silkscreenRef(0, halfBodyHeight + 0.8, 0.5), + courtyard, + ], + parameters, + } +} diff --git a/src/fn/index.ts b/src/fn/index.ts index b719f9a4..ee879e72 100644 --- a/src/fn/index.ts +++ b/src/fn/index.ts @@ -84,6 +84,7 @@ export { sot963 } from "./sot963" export { potentiometer } from "./potentiometer" export { electrolytic } from "./electrolytic" export { fpc } from "./fpc" +export { fuseclip } from "./fuseclip" export { smbf } from "./smbf" export { sot323 } from "./sot323" export { smtpad } from "./smtpad" diff --git a/src/footprinter.ts b/src/footprinter.ts index 6275af64..8c9e2eb7 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -454,6 +454,9 @@ export type Footprinter = { | "mpw" | "mpl" > + fuseclip: () => FootprinterParamsBuilder< + "p" | "holewidth" | "holeheight" | "outerwidth" | "outerheight" + > stampboard: () => FootprinterParamsBuilder< | "w" | "h" diff --git a/tests/__snapshots__/fuseclip_c3130.snap.svg b/tests/__snapshots__/fuseclip_c3130.snap.svg new file mode 100644 index 00000000..3ec09b43 --- /dev/null +++ b/tests/__snapshots__/fuseclip_c3130.snap.svg @@ -0,0 +1 @@ +{REF} \ No newline at end of file diff --git a/tests/fuseclip.test.ts b/tests/fuseclip.test.ts new file mode 100644 index 00000000..a5cacc15 --- /dev/null +++ b/tests/fuseclip.test.ts @@ -0,0 +1,24 @@ +import { expect, test } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" + +test("fuseclip matches C3130", () => { + const circuitJson = fp.string("fuseclip").circuitJson() + const pads = circuitJson.filter( + (element) => element.type === "pcb_plated_hole", + ) + + expect(pads).toHaveLength(2) + expect(pads.every((pad) => pad.shape === "pill")).toBe(true) + expect(pads).toEqual( + expect.arrayContaining([ + expect.objectContaining({ x: -2.575052, ccw_rotation: 90 }), + expect.objectContaining({ x: 2.575052, ccw_rotation: 90 }), + ]), + ) + + expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot( + import.meta.path, + "fuseclip_c3130", + ) +})