← Commit history

2.46.0: heatsink vias ride inside the footprint on every lane; import-kicad HINT names the vias under the exposed pad or says they are missing (John, 2026-09-19)

John Lauer ·6fd620ab35 ·1mo ago ·parent 486d5bc
5 files changed +42−3
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "adom-lbr"-version = "2.45.1"+version = "2.46.0" 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+11
@@ -67,6 +67,17 @@ read from `distributor_links.<dist>` as a string or an object with `pn` / `part_ are built from these fields, and the wiki link is how the next EE finds the reviewed data. One lookup fills the numbers: `adom-parts-search search <MPN>`. +**Heatsink vias ride inside the footprint (2.46.0).** A component page that defines heatsink+vias (`artwork/<MPN>-heatsink-via.json`: 0.3 mm drill, 0.8 mm pitch, 0.15 mm ring, tented)+carries them in the KiCad footprint as plated `thru_hole` pads numbered like the exposed pad+(KiCad `pad_prop_heatsink`; adom-footprint create `heatsinkVias` writes them, or the page's+build does). adom-lbr keeps them on every lane without any extra flag: `import-kicad` reads the+drill, the Altium PcbLib gets multi-layer pads with holes, `generate` writes EAGLE `<pad+drill=...>` with the same name (Fusion joins them to the exposed pad's net), so a user never+redraws thermal vias per tool. `import-kicad` prints a HINT naming the vias it found, or that+the largest SMD pad has none (John, 2026-09-19). The hero GLB shows the same vias as the+`Adom.HeatsinkVias` layer (65 % transparent copper through a 1.6 mm board).+ **Altium component Description (2.45.1).** The SchLib header's `ComponentDescription` (what Altium shows in Properties > General > Description and in the SCH Library panel list) is filled from the symbol's Description property; it is a different field from the Description
VERSION+1−1
@@ -1 +1 @@-2.45.1+2.46.0
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-lbr",-  "version": "2.45.1",+  "version": "2.46.0",   "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+28
@@ -1938,6 +1938,33 @@ fn page_brief(path: &std::path::Path) -> Option<String> { /// turns them into Altium parameters (plus Supplier N / Supplier Part Number N pairs) /// and generate writes them as EAGLE attributes. Say so when any are missing, and say /// where to get them: `adom-parts-search search <MPN>` looks all three up at once.+/// Heatsink (thermal) vias travel INSIDE the footprint as plated through-hole pads that+/// carry the exposed pad's own number (0.3 mm drill, 0.8 mm pitch, 0.15 mm ring, tented+/// is the wiki's standard rule; adom-footprint create `heatsinkVias` writes them). Every+/// lane keeps them: KiCad as the pads themselves, Altium as multi-layer pads with holes,+/// EAGLE/Fusion as <pad drill=...> - so a user never redraws them per tool (John,+/// 2026-09-19). Say when a large SMD pad has none.+fn heatsink_hint(fp_txt: &str, mpn: &str) {+    let pad_re = regex_lite::Regex::new(r#"\(pad\s+"([^"]+)"\s+(smd|thru_hole|np_thru_hole)\b[^\n]*?\(size\s+([-\d.]+)\s+([-\d.]+)\)"#).unwrap();+    let mut smd_big: Vec<(String, f64)> = Vec::new();+    let mut th: std::collections::HashMap<String, usize> = std::collections::HashMap::new();+    for c in pad_re.captures_iter(fp_txt) {+        let (name, kind) = (c[1].to_string(), c[2].to_string());+        let area = c[3].parse::<f64>().unwrap_or(0.0) * c[4].parse::<f64>().unwrap_or(0.0);+        if kind == "smd" { smd_big.push((name, area)); } else if kind == "thru_hole" { *th.entry(name).or_insert(0) += 1; }+    }+    let Some(max_area) = smd_big.iter().map(|(_, a)| *a).fold(None, |m: Option<f64>, a| Some(m.map_or(a, |x| x.max(a)))) else { return };+    let median = { let mut v: Vec<f64> = smd_big.iter().map(|(_, a)| *a).collect(); v.sort_by(|a, b| a.partial_cmp(b).unwrap()); v[v.len() / 2] };+    for (name, area) in &smd_big {+        if *area >= 1.0 && *area >= 3.0 * median && *area == max_area {+            match th.get(name) {+                Some(n) => println!("HINT: {mpn} exposed pad {name} carries {n} heatsink via(s) as plated holes with its own number; they reach Altium (multi-layer pads) and Fusion (<pad drill>) unchanged."),+                None => println!("HINT: {mpn} exposed pad {name} ({area:.1} mm2) has NO heatsink vias in the footprint. A thermal pad without vias into the ground plane is a part that overheats; add them once in the KiCad footprint as thru_hole pads numbered {name} (adom-footprint create `heatsinkVias`, or the wiki page's artwork/<MPN>-heatsink-via.json rule: 0.3 mm drill, 0.8 mm pitch, 0.15 mm ring, tented) and every lane carries them."),+            }+        }+    }+}+ fn sourcing_hint(sym_txt: &str, mpn: &str) {     let has = |n: &str| sym_txt.contains(&format!("(property \"{n}\""));     let missing: Vec<&str> = ["MPN", "Manufacturer", "Datasheet", "Description", "Mouser", "DigiKey", "LCSC", "Adom Wiki"].into_iter().filter(|n| !has(n)).collect();@@ -3616,6 +3643,7 @@ The geometry is fine but a BOM cannot order this part - pass --part-metadata or             let sym_txt = read_or_exit(&sym);             let fp_txt = read_or_exit(&fp);             sourcing_hint(&sym_txt, &mpn);+            heatsink_hint(&fp_txt, &mpn);             // Same pin-map convention as generate: explicit --pin-map wins, else             // a pinmap.json sidecar beside the symbol just works.             let pin_map_path = pin_map.or_else(|| {