Adom Library
Public Made by Adomby adom
adom-lbr — the EDA library translator. Bring a component in from any supported EDA tool and convert it to any other (KiCad ⇄ Altium ⇄ EAGLE/Fusion) through one canonical adom-lbr JSON — symbol + footprint + 3D + pin↔pad map. Altium is written natively (no Altium needed). Includes the adom-lbr Manager: browse wiki components and view symbol/footprint/3D + the per-EDA layer stackup.
pad-clearance: overlap stays an error, tight clearance becomes a warning (unblocks 550 stock KiCad footprints) #4
Fixes adom/adom-lbr#20.
pad-clearance rejected 559 of 1,314 stock KiCad footprints (43%) — including land patterns byte-identical to the ones KiCad ships. Because factory.py gates on adom-lbr validate and writes a FAIL into done.txt, every false positive was a component-page part dropped from the worklist permanently, not retried. lbr invalid was the top failure mode across factory waves 9 and 10.
Two defects, one line
if gap < 0.2 { ... }
1. The comparison had no epsilon. A gap of exactly 0.2 computes as 0.19999999999999998, so 225 footprints failed while the message printed 0.200 mm apart — the rule contradicting its own wording, copper closer than 0.2 mm. SOT-666 is the clean example.
2. A flat 0.2 mm floor is wrong for fine pitch. The other 334 sit below 0.2 mm because that is what their land pattern is. SOT-563 is 0.675 x 0.35 mm pads on 0.5 mm pitch, so the gap is 0.15 mm by construction — there is no defect to find. Package_DFN_QFN failed at 62%, which would have fought the QFN/pin-1 work directly.
What changed
Overlap stays an error; tight-but-separated copper becomes a warning under a new pad-clearance-tight code. That is what the rule's own comment always described — "A pad mapping that grows into its neighbour is a short" — and the case it was written for (adom/b2b-ph-k-s, pin 2's copper 0.35 mm inside pin 1, wiki #19) is a negative gap, still caught.
| before | after | |
|---|---|---|
| stock KiCad footprints rejected | 559 / 1314 | 9 / 1314 |
All 9 survivors are genuine overlaps of 0.175–1.150 mm (thermal-via and EP patterns where differently-named copper really does intersect) — correctly still errors.
Verified
RT6252BHGH6F(RT6252 in SOT-563, symbol and footprint agreeing on all 6 pins) — wasFAIL: lbr invalid, now passes with one warning.SOT-666, the exact-0.2 boundary — now passes clean, no warning.ss41f, a real 0.005 mm overlap — still fails as an error. The safety property is intact.cargo test --release: 22 passed, 0 failed, including two new assertions pinning both behaviours (a 0.15 mm gap warns but does not error; exactly 0.2 mm neither errors nor warns).
Version 2.32.0 → 2.33.0.
Diff Skip to comments (2)
@@ -1,4312 +1,4307 @@⋯ 2482 unchanged lines ⋯ struct P { name: String, x: f64, y: f64, hx: f64, hy: f64, round: bool } let num = |s: &str| s.parse::<f64>().unwrap_or(0.0); let rot90 = |r: Option<&str>| matches!(r, Some("R90") | Some("R270"));- let mut overlaps: Vec<String> = Vec::new();- let mut close: Vec<String> = Vec::new();+ let mut shorted: Vec<String> = Vec::new();+ let mut tight: Vec<String> = Vec::new(); let pkg_re = regex_lite::Regex::new(r#"(?s)<package name="([^"]*)">(.*?)</package>"#).unwrap(); // Match the whole element, then read each attribute from it. An optional (?:rot=...)? // group followed by a greedy [^>]* silently matches EMPTY and lets the tail eat the⋯ 46 unchanged lines ⋯ } else { gx.max(gy) };- // True copper OVERLAP is a short and stays a hard error. A small- // POSITIVE gap is what fine-pitch land patterns look like by- // construction (a 0.5 mm-pitch SOT-563 has 0.150 mm between pads in- // KiCad's own library), so it warns instead of failing: a flat 0.2 mm- // ERROR rejected 559 of 1314 stock KiCad footprints (wiki #20), each- // one a part silently dropped from the factory worklist. The epsilon- // keeps a gap of exactly the threshold clean: 1.2 - 1.0 is- // 0.19999999999999996 in floats, and a message that says "closer than- // 0.2 mm" must not reject a pair that is "0.200 mm apart".- const EPS: f64 = 1e-4;- if gap < -EPS {- overlaps.push(format!("{} pads {} and {} OVERLAP by {:.3} mm", pkg, a.name, b.name, -gap));- } else if gap < 0.2 - EPS {- close.push(format!("{} pads {} and {} are {:.3} mm apart", pkg, a.name, b.name, gap));+ // Overlapping copper is a SHORT and stays an ERROR - that is the case+ // this rule was written for. Copper merely CLOSE to its neighbour is a+ // different thing: a flat 0.2 mm floor rejected 559 of 1314 stock KiCad+ // footprints (43%), among them land patterns byte-identical to the ones+ // KiCad ships. SOT-563 is 0.675x0.35 mm pads on 0.5 mm pitch, so its+ // 0.15 mm gap is what the package IS, not a defect; 62% of+ // Package_DFN_QFN failed the same way. Downstream that became+ // `FAIL: lbr invalid` in the component factory, which writes the slug+ // to done.txt and drops the part from the worklist permanently. So a+ // positive-but-tight gap is a WARNING and overlap remains an error.+ //+ // The comparison needs an epsilon too: a gap of exactly 0.2 computes as+ // 0.19999999999999998 and failed while printing as "0.200 mm apart",+ // which is 225 of those 559 rejected by float noise alone.+ const CLEARANCE_MIN: f64 = 0.2;+ const CLEARANCE_EPS: f64 = 1e-6;+ if gap < 0.0 {+ shorted.push(format!("{} pads {} and {} OVERLAP by {:.3} mm", pkg, a.name, b.name, -gap));+ } else if gap < CLEARANCE_MIN - CLEARANCE_EPS {+ tight.push(format!("{} pads {} and {} are {:.3} mm apart", pkg, a.name, b.name, gap)); } } } }- if !overlaps.is_empty() {+ if !shorted.is_empty() { errors.push(LintError { code: "pad-clearance".into(), message: format!("copper OVERLAPS: {}. A pad mapping that grows into its neighbour is a short.",- overlaps.join("; ")),+ shorted.join("; ")), }); }- if !close.is_empty() {+ if !tight.is_empty() { warnings.push(LintError {- code: "pad-clearance".into(),- message: format!("copper closer than 0.2 mm: {}. Fine-pitch land patterns sit here by construction; \-check the source footprint intends it rather than a mapping having grown a pad.",- close.join("; ")),+ code: "pad-clearance-tight".into(),+ message: format!("copper closer than {} mm but not touching: {}. Normal for fine-pitch land patterns (a 0.5 mm-pitch SOT-563 is 0.15 mm by construction); check it against the board house's minimum spacing.",+ 0.2, tight.join("; ")), }); } }⋯ 1492 unchanged lines ⋯ <smd name="EP@2" x="0.5" y="0.5" dx="0.4" dy="0.4"/> <smd name="EP@3" x="-0.5" y="0.5" dx="0.4" dy="0.4"/>"#); assert!(!lint_lbr(&vias).errors.iter().any(|e| e.code == "pad-clearance"));- } - #[test]- fn fine_pitch_gaps_warn_instead_of_failing_and_the_exact_boundary_passes_clean() {- // wiki #20: a flat 0.2 mm ERROR rejected 559 of 1314 stock KiCad footprints,- // and 225 of them sat at exactly 0.200 mm because of float representation.- use super::lint_lbr;- let lib = |pads: &str| format!(r#"<eagle><drawing><layers></layers><library>- <packages><package name="P">{pads}</package></packages>- <symbols><symbol name="S"><pin name="A" x="0" y="0" length="short"/></symbol></symbols>- <devicesets><deviceset name="D"><gates><gate name="G$1" symbol="S" x="0" y="0"/></gates>- <devices><device name="" package="P"><connects><connect gate="G$1" pin="A" pad="1"/></connects>- <technologies><technology name=""/></technologies></device></devices></deviceset></devicesets>- </library></drawing></eagle>"#);- // a 0.5 mm-pitch SOT-563 row: 0.150 mm apart by construction. WARNING, never error.- let fine = lib(r#"<smd name="1" x="0" y="0" dx="0.675" dy="0.35"/>- <smd name="2" x="0" y="0.5" dx="0.675" dy="0.35"/>"#);- let r = lint_lbr(&fine);- assert!(r.errors.iter().all(|e| e.code != "pad-clearance"), "fine pitch must not FAIL: {:?}", r.errors);- assert!(r.warnings.iter().any(|w| w.code == "pad-clearance"), "but it should still WARN: {:?}", r.warnings);- // exactly 0.200 mm apart: 1.0 mm rect pads at x -0.6 and 0.6 compute a gap of- // 0.19999999999999996 in floats. The epsilon keeps the rule's own wording true:- // "closer than 0.2 mm" cannot reject "0.200 mm apart". Clean, not even a warning.- let boundary = lib(r#"<smd name="1" x="-0.6" y="0" dx="1.0" dy="1.0"/>- <smd name="2" x="0.6" y="0" dx="1.0" dy="1.0"/>"#);- let rb = lint_lbr(&boundary);- assert!(rb.errors.iter().all(|e| e.code != "pad-clearance"), "{:?}", rb.errors);- assert!(rb.warnings.iter().all(|w| w.code != "pad-clearance"), "{:?}", rb.warnings);- // negative control: genuinely overlapping copper is still a hard error.- let overlap = lib(r#"<smd name="1" x="0" y="0" dx="1.2" dy="1.2"/>- <smd name="2" x="1.0" y="0" dx="1.2" dy="1.2"/>"#);- assert!(lint_lbr(&overlap).errors.iter().any(|e| e.code == "pad-clearance"));+ // Fine-pitch land patterns are SEPARATED copper, not shorts. A flat 0.2 mm floor+ // failed 559 of 1314 stock KiCad footprints, so tight-but-positive is a warning:+ // 0.4-wide pads 0.55 apart leave a 0.15 mm gap, which is exactly a SOT-563.+ let tight = lib(r#"<smd name="1" x="0" y="0" dx="0.4" dy="1"/>+ <smd name="2" x="0.55" y="0" dx="0.4" dy="1"/>"#);+ let r = lint_lbr(&tight);+ assert!(!r.errors.iter().any(|e| e.code == "pad-clearance"),+ "a 0.15 mm gap is a fine-pitch land pattern, not a short: {:?}", r.errors);+ assert!(r.warnings.iter().any(|e| e.code == "pad-clearance-tight"),+ "it should still be reported, as a warning: {:?}", r.warnings);++ // A gap of EXACTLY the 0.2 mm threshold must pass clean. It computes as+ // 0.19999999999999998, which is what rejected 225 footprints while printing+ // "0.200 mm apart" - the rule contradicting its own message.+ let exact = lib(r#"<smd name="1" x="0" y="0" dx="0.4" dy="1"/>+ <smd name="2" x="0.6" y="0" dx="0.4" dy="1"/>"#);+ let r = lint_lbr(&exact);+ assert!(!r.errors.iter().any(|e| e.code == "pad-clearance"), "{:?}", r.errors);+ assert!(!r.warnings.iter().any(|e| e.code == "pad-clearance-tight"),+ "exactly 0.2 mm is not closer than 0.2 mm: {:?}", r.warnings); } #[test]⋯ 206 unchanged lines ⋯ assert!(r.errors.iter().any(|e| e.code == "empty-packages"), "{:?}", r.errors); } }+@@ -1,1 +1,2 @@ 2.33.0+@@ -1,28 +1,29 @@⋯ 25 unchanged lines ⋯ opt-level = "z" lto = true strip = true+
Comments
Log in to comment.