diff --git a/src/fn/stampboard.ts b/src/fn/stampboard.ts index 6e12f4bb..1ef1aab9 100644 --- a/src/fn/stampboard.ts +++ b/src/fn/stampboard.ts @@ -10,6 +10,7 @@ import { rectpad } from "../helpers/rectpad" import { platedhole } from "src/helpers/platedhole" import { silkscreenRef, type SilkscreenRef } from "src/helpers/silkscreenRef" import { base_def } from "../helpers/zod/base_def" +import { dim2d } from "../helpers/zod/dim-2d" export const stampboard_def = base_def.extend({ fn: z.string(), @@ -22,6 +23,13 @@ export const stampboard_def = base_def.extend({ p: length.default(length.parse("2.54mm")), pw: length.default(length.parse("1.6mm")), pl: length.default(length.parse("2.4mm")), + sidey: length.default(0).describe("shared Y offset for left and right rows"), + innergrid: dim2d.optional().describe("columns and rows of inner SMT pads"), + innerp: length.default("1mm").describe("inner SMT pad grid pitch"), + innerpw: length.default("1mm").describe("inner SMT pad width"), + innerph: length.default("1mm").describe("inner SMT pad height"), + innerx: length.default(0).describe("inner SMT pad grid center X"), + innery: length.default(0).describe("inner SMT pad grid center Y"), innerhole: z.boolean().default(false), innerholeedgedistance: length.default(length.parse("1.61mm")), silkscreenlabels: z.boolean().default(false), @@ -141,12 +149,16 @@ export const stampboard = ( let routes: { x: number; y: number }[] = [] const innerDiameter = 1 const outerDiameter = innerDiameter - const totalPadsNumber = + const perimeterPadCount = params.left + params.right + (params.bottom ?? 0) + (params.top ?? 0) + const innerPadCount = params.innergrid + ? params.innergrid.x * params.innergrid.y + : 0 + const totalPadsNumber = perimeterPadCount + innerPadCount const maxLabelLength = `pin${totalPadsNumber}`.length const textHalf = (maxLabelLength * 0.7) / 2 if (params.right) { - const yoff = -((params.right - 1) / 2) * params.p + const yoff = -((params.right - 1) / 2) * params.p + params.sidey for (let i = 0; i < params.right; i++) { if ( i === 0 && @@ -213,7 +225,7 @@ export const stampboard = ( } } if (params.left) { - const yoff = ((params.left - 1) / 2) * params.p + const yoff = ((params.left - 1) / 2) * params.p + params.sidey for (let i = 0; i < params.left; i++) { if (i === 0 && !params.silkscreenlabels) { routes = getTriangleDir( @@ -417,6 +429,22 @@ export const stampboard = ( } } } + if (params.innergrid) { + const { x: columns, y: rows } = params.innergrid + for (let row = 0; row < rows; row++) { + for (let column = 0; column < columns; column++) { + rectpads.push( + rectpad( + perimeterPadCount + row * columns + column + 1, + params.innerx + (column - (columns - 1) / 2) * params.innerp, + params.innery + (row - (rows - 1) / 2) * params.innerp, + params.innerpw, + params.innerph, + ), + ) + } + } + } const silkscreenTriangle: PcbSilkscreenPath = { type: "pcb_silkscreen_path", diff --git a/src/footprinter.ts b/src/footprinter.ts index 73cd0595..1e0723b5 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -430,6 +430,13 @@ export type Footprinter = { | "p" | "pw" | "pl" + | "sidey" + | "innergrid" + | "innerp" + | "innerpw" + | "innerph" + | "innerx" + | "innery" | "innerhole" | "innerholeedgedistance" | "silkscreenlabels" diff --git a/tests/__snapshots__/stampboard_offset_side_rows_inner_smt_grid.snap.svg b/tests/__snapshots__/stampboard_offset_side_rows_inner_smt_grid.snap.svg new file mode 100644 index 00000000..45565df2 --- /dev/null +++ b/tests/__snapshots__/stampboard_offset_side_rows_inner_smt_grid.snap.svg @@ -0,0 +1 @@ +{REF} \ No newline at end of file diff --git a/tests/stampboard.test.ts b/tests/stampboard.test.ts index 0235a000..87320913 100644 --- a/tests/stampboard.test.ts +++ b/tests/stampboard.test.ts @@ -23,6 +23,64 @@ test("stampboard", () => { ) }) +test("stampboard with offset side rows and inner SMT grid", () => { + const def = + "stampboard_left14_right14_bottom12_top0_w19.000089mm_h18.9899036mm_p1.27mm_pw0.8999982mm_pl1.499997mm_sidey0.7899527mm_innergrid3x3_innerp1.400048mm_innerpw0.8999982mm_innerph0.8999982mm_innerx-1.500124mm_innery1.3248767mm" + const soup = fp.string(def).circuitJson() + const pads = soup.filter((element) => element.type === "pcb_smtpad") + const pad = (pinNumber: number) => + pads.find((element) => element.port_hints.includes(pinNumber.toString())) + const expectPad = ( + pinNumber: number, + expected: { x: number; y: number; width: number; height: number }, + ) => { + const actual = pad(pinNumber) + expect(actual).toBeDefined() + expect(actual!.x).toBeCloseTo(expected.x, 6) + expect(actual!.y).toBeCloseTo(expected.y, 6) + expect(actual!.width).toBeCloseTo(expected.width, 6) + expect(actual!.height).toBeCloseTo(expected.height, 6) + } + + expect(pads).toHaveLength(49) + expectPad(1, { + x: -8.750046, + y: 9.0449527, + width: 1.499997, + height: 0.8999982, + }) + expectPad(15, { + x: -6.985, + y: -8.7449533, + width: 0.8999982, + height: 1.499997, + }) + expectPad(27, { + x: 8.750046, + y: -7.4650473, + width: 1.499997, + height: 0.8999982, + }) + expectPad(41, { + x: -2.900172, + y: -0.0751713, + width: 0.8999982, + height: 0.8999982, + }) + expectPad(49, { + x: -0.100076, + y: 2.7249247, + width: 0.8999982, + height: 0.8999982, + }) + + const svgContent = convertCircuitJsonToPcbSvg(soup) + expect(svgContent).toMatchSvgSnapshot( + import.meta.path, + "stampboard_offset_side_rows_inner_smt_grid", + ) +}) + test("stampboard silkscreen labels", () => { const def = "stampboard_left10_right10_bottom4_top4_w21mm_p2.54mm_silkscreenlabels"