From 22f3e6322041f1aa67c15fb2d7b51ed1cf2e403e Mon Sep 17 00:00:00 2001 From: techmannih Date: Sun, 3 May 2026 22:45:05 +0530 Subject: [PATCH 1/2] Add KiCad nonpolarized resistor silkscreen support and _nonpolarized passive flag --- src/footprinter.ts | 1 + src/helpers/passive-fn.ts | 80 +++++++++++++++---- .../0402_nonpolarized_kicad_parity.test.ts | 28 +++++++ ...arized_parity._boolean_difference.snap.svg | 1 + .../0402_nonpolarized_parity.snap.svg | 1 + .../__snapshots__/res0402_parity.snap.svg | 2 +- 6 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 tests/kicad-parity/0402_nonpolarized_kicad_parity.test.ts create mode 100644 tests/kicad-parity/__snapshots__/0402_nonpolarized_parity._boolean_difference.snap.svg create mode 100644 tests/kicad-parity/__snapshots__/0402_nonpolarized_parity.snap.svg diff --git a/src/footprinter.ts b/src/footprinter.ts index f3d30daa..4efad2ed 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -33,6 +33,7 @@ type CommonPassiveOptionKey = | "ph" | "w" | "h" + | "nonpolarized" | "textbottom" export type Footprinter = { diff --git a/src/helpers/passive-fn.ts b/src/helpers/passive-fn.ts index 62db3112..a4d8d718 100644 --- a/src/helpers/passive-fn.ts +++ b/src/helpers/passive-fn.ts @@ -21,6 +21,11 @@ type StandardSize = { w_mm_min: number // body width courtyard_width_mm?: number courtyard_height_mm?: number + nonpolarizedSilkscreen?: { + line_half_length_mm?: number + line_y_mm?: number + stroke_width_mm?: number + } } // Updated footprint sizes @@ -55,6 +60,11 @@ export const footprintSizes: StandardSize[] = [ h_mm_min: 3.4, courtyard_width_mm: 5.9, courtyard_height_mm: 3.9, + nonpolarizedSilkscreen: { + line_half_length_mm: 1.386252, + line_y_mm: 1.71, + stroke_width_mm: 0.12, + }, }, { imperial: "0201", @@ -77,6 +87,11 @@ export const footprintSizes: StandardSize[] = [ h_mm_min: 0.64, courtyard_width_mm: 1.86, courtyard_height_mm: 0.94, + nonpolarizedSilkscreen: { + line_half_length_mm: 0.153641, + line_y_mm: 0.38, + stroke_width_mm: 0.12, + }, }, { imperial: "0603", @@ -88,6 +103,11 @@ export const footprintSizes: StandardSize[] = [ h_mm_min: 0.95, courtyard_width_mm: 2.96, courtyard_height_mm: 1.46, + nonpolarizedSilkscreen: { + line_half_length_mm: 0.237258, + line_y_mm: 0.5225, + stroke_width_mm: 0.12, + }, }, { imperial: "0805", @@ -99,6 +119,11 @@ export const footprintSizes: StandardSize[] = [ h_mm_min: 1.4, courtyard_width_mm: 3.36, courtyard_height_mm: 1.9, + nonpolarizedSilkscreen: { + line_half_length_mm: 0.227064, + line_y_mm: 0.735, + stroke_width_mm: 0.12, + }, }, { imperial: "1206", @@ -110,6 +135,11 @@ export const footprintSizes: StandardSize[] = [ h_mm_min: 1.75, courtyard_width_mm: 4.56, courtyard_height_mm: 2.26, + nonpolarizedSilkscreen: { + line_half_length_mm: 0.727064, + line_y_mm: 0.91, + stroke_width_mm: 0.12, + }, }, { imperial: "1210", @@ -121,6 +151,11 @@ export const footprintSizes: StandardSize[] = [ h_mm_min: 2.65, courtyard_width_mm: 4.56, courtyard_height_mm: 3.16, + nonpolarizedSilkscreen: { + line_half_length_mm: 0.723737, + line_y_mm: 1.355, + stroke_width_mm: 0.12, + }, }, { imperial: "2010", @@ -132,6 +167,11 @@ export const footprintSizes: StandardSize[] = [ h_mm_min: 2.65, courtyard_width_mm: 6.36, courtyard_height_mm: 3.16, + nonpolarizedSilkscreen: { + line_half_length_mm: 1.527064, + line_y_mm: 1.36, + stroke_width_mm: 0.12, + }, }, { imperial: "2512", @@ -143,6 +183,11 @@ export const footprintSizes: StandardSize[] = [ h_mm_min: 3.35, courtyard_width_mm: 7.66, courtyard_height_mm: 3.86, + nonpolarizedSilkscreen: { + line_half_length_mm: 2.177064, + line_y_mm: 1.71, + stroke_width_mm: 0.12, + }, }, ] @@ -175,6 +220,7 @@ export const passive_def = base_def.extend({ imperial: distance.optional(), w: length.optional(), h: length.optional(), + nonpolarized: z.boolean().optional(), textbottom: z.boolean().optional(), }) @@ -191,6 +237,7 @@ export const passive = (params: PassiveDef): AnyCircuitElement[] => { imperial, w, h, + nonpolarized, textbottom, string: footprintString, } = params @@ -223,28 +270,31 @@ export const passive = (params: PassiveDef): AnyCircuitElement[] => { throw new Error("Could not determine required pad dimensions (p, pw, ph)") } - const lineLength = (p - pw) * 0.6 - const lineY = ph / 2 + 0.06 let silkscreenLines: PcbSilkscreenPath[] = [] - - // `res0402` uses the nonpolarized silkscreen style: two symmetric lines - // instead of the polarized-style 3-sided outline used to show orientation. - const usesNonPolarized0402ResistorSilkscreen = + const nonpolarizedSilkscreen = fn === "res" && - typeof footprintString === "string" && - /^res0402(?:_|$)/i.test(footprintString) + (nonpolarized === true || + typeof footprintString !== "string" || + /^res(?:\d{4}|\d{5})(?:_|$)/i.test(footprintString)) + ? sz?.nonpolarizedSilkscreen + : undefined - if (usesNonPolarized0402ResistorSilkscreen) { + if (nonpolarizedSilkscreen?.stroke_width_mm) { + const { + line_half_length_mm = 0, + line_y_mm = 0, + stroke_width_mm, + } = nonpolarizedSilkscreen silkscreenLines = [ { type: "pcb_silkscreen_path", layer: "top", pcb_component_id: "", route: [ - { x: -lineLength / 2, y: lineY }, - { x: lineLength / 2, y: lineY }, + { x: -line_half_length_mm, y: line_y_mm }, + { x: line_half_length_mm, y: line_y_mm }, ], - stroke_width: 0.12, + stroke_width: stroke_width_mm, pcb_silkscreen_path_id: "", }, { @@ -252,10 +302,10 @@ export const passive = (params: PassiveDef): AnyCircuitElement[] => { layer: "top", pcb_component_id: "", route: [ - { x: -lineLength / 2, y: -lineY }, - { x: lineLength / 2, y: -lineY }, + { x: -line_half_length_mm, y: -line_y_mm }, + { x: line_half_length_mm, y: -line_y_mm }, ], - stroke_width: 0.12, + stroke_width: stroke_width_mm, pcb_silkscreen_path_id: "", }, ] diff --git a/tests/kicad-parity/0402_nonpolarized_kicad_parity.test.ts b/tests/kicad-parity/0402_nonpolarized_kicad_parity.test.ts new file mode 100644 index 00000000..ed956c9e --- /dev/null +++ b/tests/kicad-parity/0402_nonpolarized_kicad_parity.test.ts @@ -0,0 +1,28 @@ +import { expect, test } from "bun:test" +import { compareFootprinterVsKicad } from "../fixtures/compareFootprinterVsKicad" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" + +test("parity/0402_nonpolarized", async () => { + const { + avgRelDiff, + combinedFootprintElements, + booleanDifferenceSvg, + courtyardDiffPercent, + } = await compareFootprinterVsKicad( + "0402_nonpolarized", + "Resistor_SMD.pretty/R_0402_1005Metric.circuit.json", + ) + + const svgContent = convertCircuitJsonToPcbSvg(combinedFootprintElements, { + showCourtyards: true, + }) + expect(courtyardDiffPercent).toBeLessThan(5) + expect(svgContent).toMatchSvgSnapshot( + import.meta.path, + "0402_nonpolarized_parity", + ) + expect(booleanDifferenceSvg).toMatchSvgSnapshot( + import.meta.path, + "0402_nonpolarized_parity._boolean_difference", + ) +}, 15_000) diff --git a/tests/kicad-parity/__snapshots__/0402_nonpolarized_parity._boolean_difference.snap.svg b/tests/kicad-parity/__snapshots__/0402_nonpolarized_parity._boolean_difference.snap.svg new file mode 100644 index 00000000..29d89e32 --- /dev/null +++ b/tests/kicad-parity/__snapshots__/0402_nonpolarized_parity._boolean_difference.snap.svg @@ -0,0 +1 @@ +R_0402_1005Metric - Alignment Analysis (Footprinter vs KiCad)0402_nonpolarizedKiCad: R_0402_1005MetricPerfect alignment = complete overlap \ No newline at end of file diff --git a/tests/kicad-parity/__snapshots__/0402_nonpolarized_parity.snap.svg b/tests/kicad-parity/__snapshots__/0402_nonpolarized_parity.snap.svg new file mode 100644 index 00000000..66130dd7 --- /dev/null +++ b/tests/kicad-parity/__snapshots__/0402_nonpolarized_parity.snap.svg @@ -0,0 +1 @@ +{REF}Diff: 0.00% \ No newline at end of file diff --git a/tests/kicad-parity/__snapshots__/res0402_parity.snap.svg b/tests/kicad-parity/__snapshots__/res0402_parity.snap.svg index 802e55a2..66130dd7 100644 --- a/tests/kicad-parity/__snapshots__/res0402_parity.snap.svg +++ b/tests/kicad-parity/__snapshots__/res0402_parity.snap.svg @@ -1 +1 @@ -{REF}Diff: 0.00% +{REF}Diff: 0.00% \ No newline at end of file From 39b652a154ba95969514a1a88309b8febb129af9 Mon Sep 17 00:00:00 2001 From: techmannih Date: Sun, 3 May 2026 22:48:44 +0530 Subject: [PATCH 2/2] up --- tests/__snapshots__/0603_bottomleft_origin.snap.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/__snapshots__/0603_bottomleft_origin.snap.svg b/tests/__snapshots__/0603_bottomleft_origin.snap.svg index bb3aa8bc..ed94b1ae 100644 --- a/tests/__snapshots__/0603_bottomleft_origin.snap.svg +++ b/tests/__snapshots__/0603_bottomleft_origin.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file