From d589f7a2cfc88656b81bf0f8da05ddeaa250ab48 Mon Sep 17 00:00:00 2001 From: Anas sarkiz <72264380+AnasSarkiz@users.noreply.github.com> Date: Thu, 19 Dec 2024 07:24:28 +0200 Subject: [PATCH] Introduced new footprint type: `breakoutheaders` (#98) * Introduced new footprint type: * renamed getTriangleDir to getTrianglePath and change defualt value of od to 1.5mm * renamed routes to silkscreenTriangleRoutes --- src/fn/breakoutheaders.ts | 239 ++++++++++++++++++ src/fn/index.ts | 1 + src/footprinter.ts | 4 + ...aders_left20_right20_w8mm_p2.54mm.snap.svg | 13 + tests/breakoutheaders.test.ts | 14 + 5 files changed, 271 insertions(+) create mode 100644 src/fn/breakoutheaders.ts create mode 100644 tests/__snapshots__/breakoutheaders_left20_right20_w8mm_p2.54mm.snap.svg create mode 100644 tests/breakoutheaders.test.ts diff --git a/src/fn/breakoutheaders.ts b/src/fn/breakoutheaders.ts new file mode 100644 index 0000000..e44eb7e --- /dev/null +++ b/src/fn/breakoutheaders.ts @@ -0,0 +1,239 @@ +import { + length, + type AnyCircuitElement, + type PcbPlatedHole, + type PcbSilkscreenPath, +} from "circuit-json" +import { z } from "zod" +import { rectpad } from "../helpers/rectpad" +import { silkscreenRef, type SilkscreenRef } from "src/helpers/silkscreenRef" +import { platedhole } from "src/helpers/platedhole" + +export const breakoutheaders_def = z.object({ + fn: z.string(), + w: length.default("10mm"), + h: length.optional(), + left: length.optional().default(20), + right: length.optional().default(20), + top: length.optional().default(0), + bottom: length.optional().default(0), + p: length.default(length.parse("2.54mm")), + id: length.optional().default(length.parse("1mm")), + od: length.optional().default(length.parse("1.5mm")), +}) + +export type breakoutheaders_def = z.input + +const getHeight = (parameters: breakoutheaders_def): number => { + const params = breakoutheaders_def.parse(parameters) + + // Calculate height based on the presence of left and right parameters + if (params.left && params.right) { + return Math.max(params.left, params.right) * params.p + } + + if (params.left) { + return params.left * params.p + } + + if (params.right) { + return params.right * params.p + } + + return 51 +} + +type Point = { x: number; y: number } +type Direction = "left" | "right" | "top" | "bottom" + +const getTrianglePath = ( + x: number, + y: number, + side: Direction, + triangleHeight = 1, + triangleWidth = 0.6, +): Point[] => { + const halfHeight = triangleHeight / 2 + const halfWidth = triangleWidth / 2 + + const silkscreenTriangleRoutes: Record = { + left: [ + { x: x + halfHeight, y }, + { x: x - halfHeight, y: y + halfWidth }, + { x: x - halfHeight, y: y - halfWidth }, + { x: x + halfHeight, y }, + ], + right: [ + { x: x - halfHeight, y }, + { x: x + halfHeight, y: y + halfWidth }, + { x: x + halfHeight, y: y - halfWidth }, + { x: x - halfHeight, y }, + ], + top: [ + { x, y: y - halfHeight }, + { x: x - halfWidth, y: y + halfHeight }, + { x: x + halfWidth, y: y + halfHeight }, + { x, y: y - halfHeight }, + ], + bottom: [ + { x, y: y + halfHeight }, + { x: x - halfWidth, y: y - halfHeight }, + { x: x + halfWidth, y: y - halfHeight }, + { x, y: y + halfHeight }, + ], + } + + return silkscreenTriangleRoutes[side] +} +export const breakoutheaders = ( + raw_params: breakoutheaders_def, +): { circuitJson: AnyCircuitElement[]; parameters: any } => { + const params = breakoutheaders_def.parse(raw_params) + const height = params.h ?? getHeight(params) + const holes: PcbPlatedHole[] = [] + const innerDiameter = params.id + const outerDiameter = params.od + let silkscreenTriangleRoutes: { x: number; y: number }[] = [] + if (params.right) { + const yoff = -((params.right - 1) / 2) * params.p + for (let i = 0; i < params.right; i++) { + if (i === 0 && !params.left && !params.bottom) { + silkscreenTriangleRoutes = getTrianglePath( + params.w / 2 + outerDiameter * 1.4, + yoff + i * params.p, + "right", + ) + } + holes.push( + platedhole( + i + 1 + params.left + (params.bottom ?? 0), + params.w / 2, + yoff + i * params.p, + innerDiameter, + outerDiameter, + ), + ) + } + } + if (params.left) { + const yoff = -((params.left - 1) / 2) * params.p + for (let i = 0; i < params.left; i++) { + if (i === params.left - 1) { + silkscreenTriangleRoutes = getTrianglePath( + -params.w / 2 - outerDiameter * 1.4, + yoff + i * params.p, + "left", + ) + } + holes.push( + platedhole( + i + 1, + -params.w / 2, + yoff + i * params.p, + innerDiameter, + outerDiameter, + ), + ) + } + } + if (params.top) { + const xoff = -((params.top - 1) / 2) * params.p + for (let i = 0; i < params.top; i++) { + if ( + i === params.top - 1 && + !params.left && + !params.bottom && + !params.right + ) { + silkscreenTriangleRoutes = getTrianglePath( + xoff + i * params.p, + height / 2 + outerDiameter * 1.4, + "top", + ) + } + holes.push( + platedhole( + i + 1 + params.right + (params.bottom ?? 0) + params.left, + xoff + i * params.p, + height / 2, + innerDiameter, + outerDiameter, + ), + ) + } + } + if (params.bottom) { + const xoff = -((params.bottom - 1) / 2) * params.p + for (let i = 0; i < params.bottom; i++) { + if (i === 0 && !params.left) { + silkscreenTriangleRoutes = getTrianglePath( + xoff + i * params.p, + -height / 2 - outerDiameter * 1.4, + "bottom", + ) + } + holes.push( + platedhole( + i + 1 + params.left, + xoff + i * params.p, + -height / 2, + innerDiameter, + outerDiameter, + ), + ) + } + } + + const silkscreenTriangle: PcbSilkscreenPath = { + type: "pcb_silkscreen_path", + pcb_silkscreen_path_id: "1", + pcb_component_id: "1", + layer: "top", + route: silkscreenTriangleRoutes, + stroke_width: 0.1, + } + + const silkscreenPath: PcbSilkscreenPath = { + type: "pcb_silkscreen_path", + pcb_silkscreen_path_id: "pcb_silkscreen_path_1", + pcb_component_id: "1", + route: [ + { + x: -params.w / 2 - outerDiameter, + y: height / 2 + outerDiameter, + }, + { + x: params.w / 2 + outerDiameter, + y: height / 2 + outerDiameter, + }, + { + x: params.w / 2 + outerDiameter, + y: -height / 2 - outerDiameter, + }, + { + x: -params.w / 2 - outerDiameter, + y: -height / 2 - outerDiameter, + }, + { + x: -params.w / 2 - outerDiameter, + y: height / 2 + outerDiameter, + }, + ], + stroke_width: 0.1, + layer: "top", + } + const silkscreenRefText: SilkscreenRef = silkscreenRef( + 0, + height / 1.7, + height / 25, + ) + return { + circuitJson: [ + ...holes, + silkscreenPath, + silkscreenRefText, + silkscreenTriangle, + ], + parameters: params, + } +} diff --git a/src/fn/index.ts b/src/fn/index.ts index 9b66301..860d521 100644 --- a/src/fn/index.ts +++ b/src/fn/index.ts @@ -27,3 +27,4 @@ export { stampboard } from "./stampboard" export { stampreceiver } from "./stampreceiver" export { lqfp } from "./lqfp" export { sot235 } from "./sot235" +export { breakoutheaders } from "./breakoutheaders" diff --git a/src/footprinter.ts b/src/footprinter.ts index 18ca869..da74db1 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -81,6 +81,10 @@ export type Footprinter = { | "innerhole" | "innerholeedgedistance" > + breakoutheaders: () => FootprinterParamsBuilder< + "w" | "h" | "left" | "right" | "top" | "bottom" | "p" | "id" | "od" + > + params: () => any /** @deprecated use circuitJson() instead */ soup: () => AnySoupElement[] diff --git a/tests/__snapshots__/breakoutheaders_left20_right20_w8mm_p2.54mm.snap.svg b/tests/__snapshots__/breakoutheaders_left20_right20_w8mm_p2.54mm.snap.svg new file mode 100644 index 0000000..072895f --- /dev/null +++ b/tests/__snapshots__/breakoutheaders_left20_right20_w8mm_p2.54mm.snap.svg @@ -0,0 +1,13 @@ +{REF} \ No newline at end of file diff --git a/tests/breakoutheaders.test.ts b/tests/breakoutheaders.test.ts new file mode 100644 index 0000000..9efa123 --- /dev/null +++ b/tests/breakoutheaders.test.ts @@ -0,0 +1,14 @@ +import { test, expect } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" + +test("breakoutheaders", () => { + const soup = fp + .string("breakoutheaders_left15_right15_w8mm_p1.54mm") + .circuitJson() + const svgContent = convertCircuitJsonToPcbSvg(soup) + expect(svgContent).toMatchSvgSnapshot( + import.meta.path, + "breakoutheaders_left20_right20_w8mm_p2.54mm", + ) +})