diff --git a/src/fn/diode.ts b/src/fn/diode.ts index b08e1162..b5e7cfc9 100644 --- a/src/fn/diode.ts +++ b/src/fn/diode.ts @@ -1,9 +1,11 @@ import type { AnySoupElement } from "circuit-json" -import { passive, type PassiveDef } from "src/helpers/passive-fn" +import { passive, type PassiveDef } from "../helpers/passive-fn" -export const diode = (parameters: { - tht: boolean - p: number -}): { circuitJson: AnySoupElement[]; parameters: PassiveDef } => { - return { circuitJson: passive(parameters), parameters } +export const diode = ( + parameters: PassiveDef, +): { circuitJson: AnySoupElement[]; parameters: PassiveDef } => { + return { + circuitJson: passive({ ...parameters, mark_cathode: true }), + parameters, + } } diff --git a/src/fn/led.ts b/src/fn/led.ts index 8ed0dee1..caa9e157 100644 --- a/src/fn/led.ts +++ b/src/fn/led.ts @@ -4,5 +4,8 @@ import { type PassiveDef, passive } from "../helpers/passive-fn" export const led = ( parameters: PassiveDef, ): { circuitJson: AnySoupElement[]; parameters: PassiveDef } => { - return { circuitJson: passive(parameters), parameters } + return { + circuitJson: passive({ ...parameters, mark_cathode: true }), + parameters, + } } diff --git a/src/footprinter.ts b/src/footprinter.ts index 25d43214..e892d667 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -340,6 +340,7 @@ export const footprinter = (): Footprinter & { getFootprintNames: string[] setString: (string) => void } => { + const { passiveFootprintNames } = getFootprintNamesByType() const proxy = new Proxy( {}, { @@ -406,7 +407,7 @@ export const footprinter = (): Footprinter & { } else { target[prop] = true target.fn = prop - if (prop === "res" || prop === "cap") { + if (passiveFootprintNames.includes(prop)) { if (v) { if (typeof v === "string" && v.includes("_metric")) { target.metric = v.split("_metric")[0] @@ -424,6 +425,12 @@ export const footprinter = (): Footprinter & { // handle dip_w or other invalid booleans if (!v && ["w", "h", "p"].includes(prop as string)) { // ignore + } else if ( + passiveFootprintNames.includes(target.fn) && + /^\d{4,5}$/.test(prop) + ) { + // Handle standalone size code for passives (e.g. .res().0603()) + target.imperial = prop } else { target[prop] = v ?? true } diff --git a/src/helpers/passive-fn.ts b/src/helpers/passive-fn.ts index 26ef1201..351c8248 100644 --- a/src/helpers/passive-fn.ts +++ b/src/helpers/passive-fn.ts @@ -139,12 +139,14 @@ export const passive_def = base_def.extend({ w: length.optional(), h: length.optional(), textbottom: z.boolean().optional(), + mark_cathode: z.boolean().optional(), }) export type PassiveDef = z.input export const passive = (params: PassiveDef): AnyCircuitElement[] => { - let { tht, p, pw, ph, metric, imperial, w, h, textbottom } = params + let { tht, p, pw, ph, metric, imperial, w, h, textbottom, mark_cathode } = + params if (typeof w === "string") w = mm(w) if (typeof h === "string") h = mm(h) @@ -188,6 +190,23 @@ export const passive = (params: PassiveDef): AnyCircuitElement[] => { pcb_silkscreen_path_id: "", } + const elements: AnyCircuitElement[] = [] + + if (mark_cathode) { + const cathodeLine: PcbSilkscreenPath = { + type: "pcb_silkscreen_path", + layer: "top", + pcb_component_id: "", + route: [ + { x: -p / 2 - pw / 2 - 0.4, y: ph / 2 + 0.4 }, + { x: -p / 2 - pw / 2 - 0.4, y: -ph / 2 - 0.4 }, + ], + stroke_width: 0.2, + pcb_silkscreen_path_id: "", + } + elements.push(cathodeLine) + } + const textY = textbottom ? -ph / 2 - 0.9 : ph / 2 + 0.9 const silkscreenRefText: SilkscreenRef = silkscreenRef(0, textY, 0.2) @@ -195,6 +214,16 @@ export const passive = (params: PassiveDef): AnyCircuitElement[] => { const excess = 0.25 const silkXs = silkscreenLine.route.map((pt) => pt.x) const silkYs = silkscreenLine.route.map((pt) => pt.y) + if (mark_cathode) { + const cathodeLines = elements.filter( + (e) => e.type === "pcb_silkscreen_path", + ) as PcbSilkscreenPath[] + for (const line of cathodeLines) { + silkXs.push(...line.route.map((pt) => pt.x)) + silkYs.push(...line.route.map((pt) => pt.y)) + } + } + const crtMinX = Math.min(-(w ?? 0) / 2, -(p / 2 + pw / 2), ...silkXs) - excess const crtMaxX = Math.max((w ?? 0) / 2, p / 2 + pw / 2, ...silkXs) + excess const crtMinY = Math.min(-(h ?? 0) / 2, -ph / 2, ...silkYs) - excess @@ -209,20 +238,19 @@ export const passive = (params: PassiveDef): AnyCircuitElement[] => { layer: "top", } + elements.push(silkscreenLine, silkscreenRefText, courtyard) + if (tht) { - return [ + elements.unshift( platedhole(1, -p / 2, 0, pw, (pw * 1) / 0.8), platedhole(2, p / 2, 0, pw, (pw * 1) / 0.8), - silkscreenLine, - silkscreenRefText, - courtyard, - ] + ) + } else { + elements.unshift( + rectpad(["1", "left"], -p / 2, 0, pw, ph), + rectpad(["2", "right"], p / 2, 0, pw, ph), + ) } - return [ - rectpad(["1", "left"], -p / 2, 0, pw, ph), - rectpad(["2", "right"], p / 2, 0, pw, ph), - silkscreenLine, - silkscreenRefText, - courtyard, - ] + + return elements } diff --git a/tests/string-parser.test.ts b/tests/string-parser.test.ts index 7ee639dc..ec868f43 100644 --- a/tests/string-parser.test.ts +++ b/tests/string-parser.test.ts @@ -4,3 +4,22 @@ import { fp } from "../src/footprinter" test("string builder ignores empty segments", () => { expect(() => fp.string("0603__").circuitJson()).not.toThrow() }) + +test("string builder works for led and diode imperial sizes", () => { + const led0402 = fp.string("led0402").circuitJson() + expect(led0402.some((e) => e.type === "pcb_pad")).toBe(true) + // Check for cathode marking (stroke_width 0.2) + expect( + led0402.some( + (e) => e.type === "pcb_silkscreen_path" && e.stroke_width === 0.2, + ), + ).toBe(true) + + const diode0603 = fp.string("diode0603").circuitJson() + expect(diode0603.some((e) => e.type === "pcb_pad")).toBe(true) + expect( + diode0603.some( + (e) => e.type === "pcb_silkscreen_path" && e.stroke_width === 0.2, + ), + ).toBe(true) +})