← Commit history

2.28.0: honour adom-symbol's free_rect/icon_rect/designators

John Lauer ·26e9a6c0fa ·1mo ago ·parent fdb5829
7 files changed +72−9
Cargo.lock+1−1
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-lbr"-version = "2.27.1"+version = "2.28.0" dependencies = [  "altium-codec",  "anyhow",
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "adom-lbr"-version = "2.27.1"+version = "2.28.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." 
VERSION+1−1
@@ -1 +1 @@-2.27.1+2.28.0
bin/adom-lbr
⋯ 1 unchanged line ⋯
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-lbr",-  "version": "2.27.1",+  "version": "2.28.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": {},
page.json+2−2
@@ -1,6 +1,6 @@ {   "slug": "adom-lbr",-  "version": "2.27.1",+  "version": "2.28.0",   "type": "app",   "description": "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.",   "dependencies": {},@@ -88,4 +88,4 @@       "label": "Browse parts"     }   ]-}\ No newline at end of file+}
src/main.rs+66−3
@@ -1300,8 +1300,25 @@ fn package_graphics(km: &str) -> String { /// polylines are ALREADY placed in the symbol's mm space; the sidecar gives the /// exact Y-up transform, so it's transcribed 1:1 with zero guessing. Anything /// else is a `Legacy` raw outline we centre + scale by eye.+/// What adom-symbol states about a symbol's interior, all in kicad-symbol-local mm, Y-up,+/// the same space as the pins. adom-symbol is AUTHORITATIVE for these: it is the tool that+/// fits the icon and writes the KiCad Reference/Value properties, so taking its numbers is+/// what makes the KiCad render and the Fusion render agree pin for pin and label for label.+/// Two tools computing the same rectangle from different font models will always disagree+/// slightly, and the disagreement is invisible until someone measures it.+#[derive(Debug, Clone, Default)]+struct ContractLayout {+    /// Where the icon was fitted, already clear of the inside designators. When present it+    /// is honoured as given: re-centring it here would undo that fitting.+    icon_rect: Option<(f64, f64, f64, f64)>,+    /// Text CENTRES for >NAME and >VALUE. None means adom-symbol hid that designator.+    reference: Option<(f64, f64)>,+    value: Option<(f64, f64)>,+    inside_body: bool,+}+ enum SymbolArt {-    Contract { mm_svg: String, dx: f64, svg_cy: f64, e_cy: f64 },+    Contract { mm_svg: String, dx: f64, svg_cy: f64, e_cy: f64, layout: ContractLayout },     Legacy(String), } @@ -1319,7 +1336,25 @@ fn resolve_symbol_art(path: &std::path::Path) -> Result<SymbolArt, String> {                     g("placement_svg_mm", "center_x"), g("placement_svg_mm", "center_y"),                     g("placement_eagle_mm", "center_x"), g("placement_eagle_mm", "center_y"),                 ) {-                    return Ok(SymbolArt::Contract { mm_svg: content, dx: svg_cx - e_cx, svg_cy, e_cy });+                    // adom-symbol 1.1.12 and later also state the interior it laid out.+                    let arr4 = |k: &str| -> Option<(f64, f64, f64, f64)> {+                        let a = v.get(k)?.as_array()?;+                        if a.len() < 4 { return None; }+                        Some((a[0].as_f64()?, a[1].as_f64()?, a[2].as_f64()?, a[3].as_f64()?))+                    };+                    let pt = |k: &str| -> Option<(f64, f64)> {+                        let a = v.get("designators")?.get(k)?.as_array()?;+                        if a.len() < 2 { return None; }+                        Some((a[0].as_f64()?, a[1].as_f64()?))+                    };+                    let layout = ContractLayout {+                        icon_rect: arr4("icon_rect"),+                        reference: pt("reference"),+                        value: pt("value"),+                        inside_body: v.get("designators").and_then(|d| d.get("inside_body"))+                                      .and_then(|b| b.as_bool()).unwrap_or(false),+                    };+                    return Ok(SymbolArt::Contract { mm_svg: content, dx: svg_cx - e_cx, svg_cy, e_cy, layout });                 }             }         }@@ -1433,7 +1468,12 @@ fn symbol_art_wires(svg: &str, body: Option<&KicadBody>, scale_frac: f64) -> (St /// Wire block for a resolved symbol-art input. fn symbol_art_block(art: &SymbolArt, body: Option<&KicadBody>, scale_frac: f64) -> (String, usize) {     match art {-        SymbolArt::Contract { mm_svg, dx, svg_cy, e_cy } => contract_wires(mm_svg, *dx, *svg_cy, *e_cy, body),+        SymbolArt::Contract { mm_svg, dx, svg_cy, e_cy, layout } => {+            // A contract that states where the icon goes has already fitted it clear of the+            // inside designators. Re-centring it on the body would undo exactly that.+            let recentre = if layout.icon_rect.is_some() { None } else { body };+            contract_wires(mm_svg, *dx, *svg_cy, *e_cy, recentre)+        }         SymbolArt::Legacy(svg) => symbol_art_wires(svg, body, scale_frac),     } }@@ -1717,6 +1757,28 @@ fn emit_symbol_block(xml: &mut String, sym: &ParsedSymbol, symbol_art: Option<&S     // net a user draws to those pins runs straight through the label (John, on the CH340G-SQ     // frame). When the symbol has pins on the top or bottom edge, both labels move INSIDE     // the body, into the interior rectangle left free by the pin names.+    // ADOM-SYMBOL IS AUTHORITATIVE when it states where the designators go. Its points are+    // the text centres it wrote into the .kicad_sym Reference/Value properties, so emitting+    // >NAME and >VALUE there with align="center" makes the KiCad render and the Fusion+    // render agree 1:1, which is the whole point of having a contract. A null means it+    // deliberately hid that designator, and hidden is honoured rather than second-guessed.+    // Note the two tools measure text differently (adom-symbol 1.5 mm per character at the+    // 1.27 mm font, adom-lbr 0.6 x height), so the rects they compute differ; that is+    // precisely why one of them has to own the number rather than both approximating it.+    let contract_designators = match symbol_art {+        Some(SymbolArt::Contract { layout, .. }) if layout.inside_body+            && (layout.reference.is_some() || layout.value.is_some()) => Some(layout),+        _ => None,+    };+    if let Some(layout) = contract_designators {+        if let Some((x, y)) = layout.reference {+            xml.push_str(&format!("  <text x=\"{:.4}\" y=\"{:.4}\" size=\"1.27\" layer=\"95\" align=\"center\">&gt;NAME</text>\n", x, y));+        }+        if let Some((x, y)) = layout.value {+            xml.push_str(&format!("  <text x=\"{:.4}\" y=\"{:.4}\" size=\"1.27\" layer=\"96\" align=\"center\">&gt;VALUE</text>\n", x, y));+        }+        println!("HINT: >NAME/>VALUE placed from the outline contract (adom-symbol's own designator points), so the KiCad and Fusion renders match.");+    } else {     let has_vertical_pins = sym.pins.iter().any(|p| p.angle == 90 || p.angle == 270);     let pin_name_w = |p: &KicadPin| dedup_pin_names(std::slice::from_ref(p))[0].chars().count() as f64 * 0.6 * 1.778;     match (&sym.body, has_vertical_pins) {@@ -1786,6 +1848,7 @@ fn emit_symbol_block(xml: &mut String, sym: &ParsedSymbol, symbol_art: Option<&S             xml.push_str(&format!("  <text x=\"0\" y=\"{}\" size=\"1.27\" layer=\"96\" align=\"top-center\">&gt;VALUE</text>\n", value_y));         }     }+    }      let deduped_names = dedup_pin_names(&sym.pins);     // A GLYPH SYMBOL HIDES ITS PIN NAMES. EAGLE draws a pin's name at its INNER end, which