app
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.
← Commit history
Publish 1.3.0
10 files changed
+187−8
Cargo.lock+1−1@@ -4,7 +4,7 @@ version = 4 [[package]] name = "adom-lbr"-version = "1.2.1"+version = "1.3.0" dependencies = [ "anyhow", "clap",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-lbr"-version = "1.2.2"+version = "1.3.0" edition = "2021" description = "EAGLE .lbr generator, linter, and validator for Fusion 360 Electronics. Converts KiCad symbols + footprints to complete EAGLE libraries."
README.md+2@@ -95,6 +95,7 @@ This is the whole point. Fusion will silently show "No Components" or refuse to - **`bind-3d`** — pre-write a 3D binding (self-hosted `wip_urn`, or managed `urn`) into a `.lbr` so the body travels with the library; idempotent. The normal path is the Fusion bridge attach. - **`lint` / `validate`** — catch the things Fusion rejects: missing connects, incomplete devicesets, bad pad maps. - **`import`** — EAGLE `.lbr` → KiCad `.kicad_sym`.+- **`generate --symbol-art <outline.svg>`** — draws adom-symbol's vector chip outline onto the schematic symbol as raw lines (layer 94), sized to the body like adom-symbol does (`--symbol-art-scale` to tune). One `.lbr` → the outline shows in Fusion / Altium / OrCAD. - **`embed`** — a self-contained 3-column (symbol / footprint / 3D) viewer as one `.html`. - **Live app** — a viewer that lints the loaded `.lbr` and shows symbols / packages / devicesets / pins / pads at a glance. @@ -121,6 +122,7 @@ Used by **chip-fetcher** and the Send-to flow across **adom-symbol** and **adom- adom-lbr generate --sym part.kicad_sym --fp part.kicad_mod --name PART --output part.lbr adom-lbr generate --sym part.kicad_sym --fp part.kicad_mod --model part.step -o part.lbr # + 3D sidecar adom-lbr bind-3d --lbr part.lbr --wip-urn "urn:adsk.wipprod:fs.file:vf.<id>?version=1" --sidecar part.fusion3d.json+adom-lbr generate --sym part.kicad_sym --fp part.kicad_mod --symbol-art part-3d-outline.svg -o part.lbr # draw the chip on the symbol adom-lbr import part.lbr -o part.kicad_sym # reverse: EAGLE → KiCad adom-lbr serve --port 8784 # live linter app (AI-drivable) adom-lbr embed --dir <chip-dir> # Fusion-style 3-column viewer as one .html
SKILL.md+16@@ -61,6 +61,22 @@ almost always: regenerate with `--fp`. Auto-lints before writing; exits 2 if lint fails. +## Chip outline on the symbol — `--symbol-art`++If the adom-symbol input carries a **3D chip outline** (`<mpn>-3d-outline-*.svg`),+pass it with `--symbol-art` and adom-lbr draws it onto the schematic symbol as raw+`<wire>` lines (layer 94), centred and scaled to ~0.38× the body — matching+adom-symbol's own placement. Tune with `--symbol-art-scale`. The one `.lbr` carries+it into Fusion / Altium / OrCAD.++**Standing directive (owner):** whenever adom-symbol gives you a 3D outline, embed+it — as close to adom-symbol's rendering as you can — on **every** export, not just+Fusion. Today the `.lbr` covers Fusion/Altium/OrCAD; the KiCad `.kicad_sym`+conversion and future native exporters are TODO. See [docs/ROADMAP.md](docs/ROADMAP.md).++Heavy outlines bloat the library (a `-blueprint` ≈ 3000 wires / ~300 KB) — the tool+warns and suggests a lighter variant (`-bold`/`-thin`).+ ## The Fusion 3D attach A 3D body only renders once Fusion brings the model into a session, so the `.lbr`
VERSION+1−1@@ -1 +1 @@-1.2.2+1.3.0
bin/adom-lbr⋯ 1 unchanged line ⋯
docs/ROADMAP.mdadded+41@@ -0,0 +1,41 @@+# adom-lbr roadmap / standing directives++## Standing directive — match adom-symbol's 3D chip outline on EVERY export++**When a thread asks adom-lbr to make a library and the adom-symbol input carries+a 3D chip outline, ALWAYS embed that outline on the schematic symbol, placed as+close to what adom-symbol renders as possible — for every export endpoint, not+just Fusion.** (Owner's directive, 2026-06-28.)++adom-symbol does the hard work of drawing the chip's vector outline and sizing it+to the symbol body + pin layout. adom-lbr should stay faithful to that, never+invent its own look.++### Status by endpoint++| Endpoint | Path | Outline embedded? |+|---|---|---|+| **Fusion 360** | EAGLE `.lbr` (`generate --symbol-art`) | ✅ done — wires on layer 94 |+| **Altium** | imports the **same** `.lbr` | ✅ done (free — same file) |+| **OrCAD** | imports the **same** `.lbr` | ✅ done (free — same file) |+| **KiCad** | `import` / `deliver kicad` → `.kicad_sym` | ⬜ **TODO** — carry the outline into the `.kicad_sym` (gfx polylines). NB adom-symbol is KiCad-native, so this only matters for the `.lbr → .kicad_sym` round-trip. |+| Future native exporters (Altium `.SchLib`, OrCAD `.olb`, …) | not built yet | ⬜ **TODO** when those land — embed the outline there too. |++So one `.lbr` already covers the three EAGLE-family tools. The open work is the+**KiCad conversion path** and any **future native exporters**.++### How the matching works today (and how to make it exact)++- `generate --symbol-art <mpn>-3d-outline-*.svg` transcribes adom-symbol's+ flattened outline polylines → EAGLE `<wire>`s, centred and scaled to ~0.38× of+ the symbol body's smaller side (tunable with `--symbol-art-scale`). The 0.38 is+ reverse-engineered from adom-symbol's own `-symbol.svg` placement — it's close,+ not exact.+- **To make it pixel-exact**, adom-symbol should emit either (a) the outline+ already in symbol mm-space (same viewBox as `-symbol.svg`), or (b) a sidecar+ with the `{center, scale}` transform + the canonical default variant. See the+ contract proposed to the adom-symbol thread (2026-06-28). When that lands,+ switch adom-lbr to a 1:1 transcription and drop the 0.38 guess.+- Detailed outlines are heavy (`-blueprint` ≈ 3000 `<wire>`s, ~300 KB). adom-lbr+ warns and suggests a lighter variant; ideally adom-symbol emits a decimated+ "lite" outline for embedding.
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-lbr",- "version": "1.2.2",+ "version": "1.3.0", "type": "app", "description": "KiCad \u2192 EAGLE .lbr generator for the whole EAGLE family (Fusion 360 / Altium / OrCAD), real-footprint accurate, with 3D-model handoff. Bidirectional KiCad converter + 7-check linter. Includes a focused fusion-export sub-skill.", "dependencies": {},
page.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-lbr",- "version": "1.2.2",+ "version": "1.3.0", "type": "app", "description": "KiCad → EAGLE .lbr generator for the whole EAGLE family (Fusion 360 / Altium / OrCAD), real-footprint accurate, with 3D-model handoff. Bidirectional KiCad converter + 7-check linter. Includes a focused fusion-export sub-skill.", "dependencies": {},
src/main.rs+123−3@@ -37,6 +37,15 @@ enum Commands { /// Symbol name override #[arg(long)] name: Option<String>,+ /// Path to an adom-symbol outline SVG (a vector line-drawing of the+ /// chip). When given, it's drawn as raw `<wire>` lines on the schematic+ /// symbol, centred in the symbol body.+ #[arg(long)]+ symbol_art: Option<PathBuf>,+ /// Size of the symbol art as a fraction of the symbol body's smaller+ /// side (default 0.38, matching adom-symbol's placement). Higher = bigger.+ #[arg(long, default_value_t = 0.38)]+ symbol_art_scale: f64, /// Path to a 3D model (STEP/STP/GLB) to associate with the package. /// The .lbr stays 2D-only (a valid Fusion 3D ref needs an Autodesk /// cloud URN that can only be minted by Fusion). Instead, a sidecar@@ -882,7 +891,88 @@ fn bind_3d_urn(content: &str, link: &str, managed: bool, package: Option<&str>) Ok(out) } -fn generate_lbr(sym: &ParsedSymbol, fp_content: Option<&str>) -> String {+/// Transcribe an adom-symbol outline SVG (a flattened line drawing of the chip)+/// into EAGLE symbol `<wire>`s on layer 94, so the schematic symbol carries a+/// little vector picture of the part. The SVG uses `<polyline>`/`<polygon>`+/// `points=` in a Y-down pixel space; we decimate the dense sampling, fit the+/// drawing inside the symbol body (centred), and negate Y for EAGLE's Y-up.+/// Returns the `<wire>` block plus the segment count (for a hint).+fn symbol_art_wires(svg: &str, body: Option<&KicadBody>, scale_frac: f64) -> (String, usize) {+ let poly_re = regex_lite::Regex::new(r#"<(polyline|polygon)\b[^>]*\bpoints="([^"]+)""#).unwrap();+ let mut polys: Vec<Vec<(f64, f64)>> = Vec::new();+ for c in poly_re.captures_iter(svg) {+ let closed = &c[1] == "polygon";+ let mut pts: Vec<(f64, f64)> = c[2]+ .split_whitespace()+ .filter_map(|p| {+ let mut it = p.split(',');+ let x: f64 = it.next()?.trim().parse().ok()?;+ let y: f64 = it.next()?.trim().parse().ok()?;+ Some((x, y))+ })+ .collect();+ if closed {+ if let Some(&first) = pts.first() { pts.push(first); }+ }+ polys.push(pts);+ }+ // Global bbox of every point (in SVG units).+ let (mut minx, mut miny, mut maxx, mut maxy) = (f64::MAX, f64::MAX, f64::MIN, f64::MIN);+ for poly in &polys {+ for &(x, y) in poly {+ minx = minx.min(x); miny = miny.min(y);+ maxx = maxx.max(x); maxy = maxy.max(y);+ }+ }+ let (sw, sh) = (maxx - minx, maxy - miny);+ if !(sw > 0.0 && sh > 0.0) {+ return (String::new(), 0);+ }+ // Fit a modest, centred icon — matching adom-symbol's own placement, which+ // sizes the outline to ~half the body's smaller side so it sits between the+ // pins/labels without crowding them.+ let (target, cx, cy) = match body {+ Some(b) => {+ let bw = (b.right - b.left).abs();+ let bh = (b.top - b.bottom).abs();+ (bw.min(bh) * scale_frac, (b.left + b.right) / 2.0, (b.top + b.bottom) / 2.0)+ }+ None => (26.0 * scale_frac, 0.0, 0.0),+ };+ let scale = target / sw.max(sh);+ // Decimate in OUTPUT space: drop points within `tol_mm` of the last kept one,+ // so curves stay smooth but the dense sampling collapses.+ let tol_mm = 0.12;+ let (smx, smy) = ((minx + maxx) / 2.0, (miny + maxy) / 2.0);+ let map = |x: f64, y: f64| (cx + (x - smx) * scale, cy - (y - smy) * scale);++ let mut out = String::new();+ let mut segs = 0usize;+ for poly in &polys {+ let mut kept: Vec<(f64, f64)> = Vec::new();+ let n = poly.len();+ for (i, &(px, py)) in poly.iter().enumerate() {+ let p = map(px, py);+ if i == 0 || i == n - 1 {+ kept.push(p);+ } else if let Some(&last) = kept.last() {+ if ((p.0 - last.0).powi(2) + (p.1 - last.1).powi(2)).sqrt() >= tol_mm {+ kept.push(p);+ }+ }+ }+ for w in kept.windows(2) {+ out.push_str(&format!(+ " <wire x1=\"{:.4}\" y1=\"{:.4}\" x2=\"{:.4}\" y2=\"{:.4}\" width=\"0.1016\" layer=\"94\"/>\n",+ w[0].0, w[0].1, w[1].0, w[1].1+ ));+ segs += 1;+ }+ }+ (out, segs)+}++fn generate_lbr(sym: &ParsedSymbol, fp_content: Option<&str>, symbol_art: Option<&str>, symbol_art_scale: f64) -> String { let mut xml = String::with_capacity(8192); xml.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");@@ -999,6 +1089,13 @@ fn generate_lbr(sym: &ParsedSymbol, fp_content: Option<&str>) -> String { xml.push_str(&format!(" <wire x1=\"{}\" y1=\"{}\" x2=\"{}\" y2=\"{}\" width=\"0.254\" layer=\"94\"/>\n", body.left, body.bottom, body.left, body.top)); } + // Optional: a vector drawing of the chip (adom-symbol's outline SVG) drawn+ // as raw lines, centred inside the symbol body.+ if let Some(svg) = symbol_art {+ let (art, _) = symbol_art_wires(svg, sym.body.as_ref(), symbol_art_scale);+ xml.push_str(&art);+ }+ let name_y = sym.body.as_ref().map(|b| b.top + 2.54).unwrap_or(10.0); let value_y = sym.body.as_ref().map(|b| b.bottom - 2.54).unwrap_or(-10.0); xml.push_str(&format!(" <text x=\"0\" y=\"{}\" size=\"1.27\" layer=\"95\" align=\"bottom-center\">>NAME</text>\n", name_y));@@ -1209,7 +1306,13 @@ fn main() { let cli = Cli::parse(); match cli.command {- Commands::Generate { sym, fp, output, name, model } => {+ Commands::Generate { sym, fp, output, name, model, symbol_art, symbol_art_scale } => {+ let symbol_art_content = symbol_art.as_ref().map(|p| {+ fs::read_to_string(p).unwrap_or_else(|e| {+ eprintln!("ERROR: Cannot read symbol art {}: {}", p.display(), e);+ process::exit(1);+ })+ }); let fp_content = fp.as_ref().map(|p| { fs::read_to_string(p).unwrap_or_else(|e| { eprintln!("ERROR: Cannot read footprint {}: {}", p.display(), e);@@ -1244,7 +1347,7 @@ fn main() { }; let out_default = sym.as_ref().or(fp.as_ref()).cloned().unwrap_or_else(|| PathBuf::from(&parsed.name));- let lbr = generate_lbr(&parsed, fp_content.as_deref());+ let lbr = generate_lbr(&parsed, fp_content.as_deref(), symbol_art_content.as_deref(), symbol_art_scale); // Lint before writing let lint = lint_lbr(&lbr);@@ -1283,6 +1386,23 @@ fn main() { println!("HINT: add --model <part.step> to also emit a Fusion 3D handoff sidecar (the bridge attaches the body)."); } }+ if let Some(svg) = &symbol_art_content {+ let (_, segs) = symbol_art_wires(svg, parsed.body.as_ref(), symbol_art_scale);+ let kb = lbr.len() / 1024;+ if segs == 0 {+ println!("HINT: --symbol-art SVG had NO <polyline>/<polygon> points — nothing drawn. Give a flattened outline SVG (adom-symbol's `<mpn>-3d-outline-*.svg`), not a raster or a <path>-only file.");+ } else {+ println!("HINT: drew the chip outline on the symbol — {segs} segments on layer 94, centred + scaled to the symbol body (matching adom-symbol's placement).");+ if segs > 2500 || kb > 200 {+ println!("WARN: HEAVY symbol art — {segs} segments, ~{kb} KB .lbr. Detailed outlines (e.g. `-blueprint`) bloat the library and can lag Fusion's symbol editor.");+ println!(" → Pass a SIMPLER outline instead (`<mpn>-3d-outline-bold.svg` or `-thin.svg` are ~1/3 the size), or drop the art for a 19 KB symbol.");+ } else if kb > 80 {+ println!("HINT: moderate weight (~{kb} KB). Fine for one part; if you're batching a whole library, prefer the lighter `-bold`/`-thin` outlines.");+ } else {+ println!("HINT: light + clean (~{kb} KB). Want the full iso wireframe? a `-blueprint` outline has more detail (bigger file).");+ }+ }+ } println!("NEXT: this .lbr imports directly into Fusion 360 / Altium / OrCAD. For KiCad: `adom-lbr import {} -o part.kicad_sym`.", out_path.display()); // 3D model: emit a sidecar manifest for the Fusion bridge. A valid