← Commit history

adom-lbr 2.47.3: round SMD pads stay round in Fusion (roundness 100); no-paste/no-mask SMD pads get cream=no/stop=no

John Lauer ·ff24429de6 ·13d ago ·parent 09ac276
8 files changed +21−8
CHANGELOG.md+4
@@ -1,5 +1,9 @@ # Changelog +## 2.47.3 (2026-10-07)+- A round KiCad SMD pad (`circle`) is round in Fusion: `roundness="100"`. It used to be written with no roundness, so EAGLE drew it square; a 0.5 mm probe test point came out as a square pad.+- An SMD pad with no paste layer gets `cream="no"`, and one with no mask layer gets `stop="no"`, so a test point or a fiducial is not printed with solder paste in Fusion.+ ## 2.47.2 (2026-10-07) - Skill: how pin lengths convert to EAGLE (the 2.47.1 fix), under "EAGLE / Fusion path". 
Cargo.lock+1−1
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-lbr"-version = "2.47.2"+version = "2.47.3" dependencies = [  "altium-codec",  "anyhow",
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "adom-lbr"-version = "2.47.2"+version = "2.47.3" edition = "2021" description = "adom-lbr generator / EDA translator: the canonical adom-lbr JSON is the hub, with native converters to Altium (.SchLib/.PcbLib/.IntLib), EAGLE/Fusion (.lbr), and KiCad. Altium output is written natively — no Altium needed at conversion time." 
SKILL.md+2
@@ -169,6 +169,8 @@ the phrases, which come from the page's `dockbar.json`.  Pin lengths (2.47.1): EAGLE has four pin lengths (0, 2.54, 5.08, 7.62 mm). A KiCad pin of exactly one of them maps straight across; any other length becomes a `point` pin at KiCad's connection point plus a lead drawn on layer 94 at its true length, so the Fusion symbol looks like the KiCad one (a 1.27 mm inductor lead used to round up to 2.54 mm and run under the humps). Check a symbol in Fusion with `EDIT <part>.sym` if leads look wrong. +Pad shape and apertures (2.47.3): a KiCad `circle` SMD pad becomes `roundness="100"` (round, not square); an SMD pad whose KiCad layers have no paste gets `cream="no"`, no mask gets `stop="no"`. Test points and fiducials therefore stay bare copper with no solder in Fusion. Per-pad mask expansion still has no EAGLE equivalent (the library DRC stop rule applies).+ Fusion 360 Electronics shows "No Components" when an .lbr has an empty `<packages>` or `<connects/>`. adom-lbr's `generate` builds a COMPLETE deviceset: symbol + package + full pin-to-pad connects. KiCad users can round-trip any `.lbr` back to `.kicad_sym` with `adom-lbr import`.  ## Commands
VERSION+1−1
@@ -1 +1 @@-2.47.2+2.47.3
bin/adom-lbr
⋯ 1 unchanged line ⋯
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-lbr",-  "version": "2.47.2",+  "version": "2.47.3",   "type": "app",   "description": "adom-lbr \u2014 the EDA library translator. Bring a component in from any supported EDA tool and convert it to any other (KiCad \u21c4 Altium \u21c4 EAGLE/Fusion) through one canonical adom-lbr JSON \u2014 symbol + footprint + 3D + pin\u2194pad 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.",   "dependencies": {},
src/main.rs+11−4
@@ -1177,6 +1177,10 @@ struct ModPad {     /// `roundrect_rratio`. None for a pad that is not a roundrect. Altium rounds pads by     /// default to reduce copper lift-off; dropping this made every such pad import square.     roundness: Option<u32>,+    /// KiCad paste / mask layers on an SMD pad: none means EAGLE `cream="no"` / `stop="no"` (a test point+    /// takes no paste; leaving the default would print solder on it)+    paste: bool,+    mask: bool, }  /// Split a `.kicad_mod` into the raw text of each `(<tag> …)` s-expression,@@ -1262,7 +1266,10 @@ fn parse_mod_pads(km: &str) -> Vec<ModPad> {             let r: f64 = rr_re.captures(block).and_then(|c| c[1].parse().ok())                 .unwrap_or(if kshape == "oval" { 0.5 } else { 0.25 });             Some(((r * 200.0).round() as i64).clamp(0, 100) as u32)-        } else { None };+        } else if kshape == "circle" && smd { Some(100) } else { None };  // a round SMD pad, not a square one+        let layers = block.split("(layers").nth(1).map(|t| t.split(')').next().unwrap_or("")).unwrap_or("");+        let paste = !smd || layers.contains("Paste");+        let mask = !smd || layers.contains("Mask");         let drill = drill_re.captures(block).and_then(|c| c[1].parse().ok());         // KiCad pad shape → EAGLE pad shape.         let kicad_shape = nc.get(4).map(|m| m.as_str()).unwrap_or("").to_string();@@ -1273,7 +1280,7 @@ fn parse_mod_pads(km: &str) -> Vec<ModPad> {             "roundrect" | "custom" => "round",             _ => "round",         }.to_string();-        pads.push(ModPad { name, smd, x, y, dx, dy, angle, drill, shape, kicad_shape, roundness });+        pads.push(ModPad { name, smd, x, y, dx, dy, angle, drill, shape, kicad_shape, roundness, paste, mask });     }     pads }@@ -1451,7 +1458,7 @@ fn emit_variant_package(xml: &mut String, km: &str, name: &str, description: &st             xml.push_str(&format!(                 "  <smd name=\"{}\" x=\"{:.4}\" y=\"{:.4}\" dx=\"{:.4}\" dy=\"{:.4}\" layer=\"1\"{}{}/>\n",                 esc_xml(&out_names[i]), p.x, -p.y, p.dx, p.dy, smd_rotation(p.angle),-                    p.roundness.map(|r| format!(" roundness=\"{r}\"")).unwrap_or_default()));+                    format!("{}{}{}", p.roundness.map(|r| format!(" roundness=\"{r}\"")).unwrap_or_default(), if p.paste { "" } else { " cream=\"no\"" }, if p.mask { "" } else { " stop=\"no\"" })));         } else {             let drill = p.drill.unwrap_or_else(|| p.dx.min(p.dy) * 0.6);             let (tht_shape, diameter, extra_rot, warn) = tht_pad_geometry(&p.kicad_shape, p.dx, p.dy);@@ -2386,7 +2393,7 @@ pub(crate) fn generate_lbr_gates(sym: &ParsedSymbol, fp_content: Option<&str>, s                 xml.push_str(&format!(                     "  <smd name=\"{}\" x=\"{:.4}\" y=\"{:.4}\" dx=\"{:.4}\" dy=\"{:.4}\" layer=\"1\"{}{}/>\n",                     esc_xml(&out_names[i]), p.x, -p.y, p.dx, p.dy, smd_rotation(p.angle),-                    p.roundness.map(|r| format!(" roundness=\"{r}\"")).unwrap_or_default()));+                    format!("{}{}{}", p.roundness.map(|r| format!(" roundness=\"{r}\"")).unwrap_or_default(), if p.paste { "" } else { " cream=\"no\"" }, if p.mask { "" } else { " stop=\"no\"" })));             } else {                 // Round thru-hole pads: real drill + circular diameter (not oblong).                 let drill = p.drill.unwrap_or_else(|| p.dx.min(p.dy) * 0.6);