# Fusion 360 library export (and Altium / OrCAD)

> Sub-page of [adom-lbr](../README.md). adom-lbr exports to four ECAD tools from
> one source; this page is the deep dive on the **EAGLE `.lbr`** path — the one
> Fusion 360, Altium, and OrCAD all import — including the part most generators
> get wrong (the real footprint) and the part they skip entirely (the 3D model).

## Why one `.lbr` covers three tools

Fusion 360 Electronics is the modern face of EAGLE, and the EAGLE `.lbr`
library format is read natively by **Fusion 360, Altium Designer, and OrCAD**.
So adom-lbr generates the `.lbr` once and all three import it. KiCad is the
exception — it doesn't read `.lbr`, so adom-lbr converts to `.kicad_sym` via
`import`.

A complete `.lbr` deviceset has three linked parts:

```
<symbol>   schematic pins + body          (layer 94)
<package>  the real footprint: <smd>/<pad> + silkscreen outline
<deviceset><device package="…"><connects>  pin-name → pad-number map
```

Miss any one and Fusion shows **"No Components."** adom-lbr's linter exists to
catch exactly that.

## The footprint must be real

The single most important flag is **`--fp`**. It points at the KiCad
`.kicad_mod` so the package is built from true pad geometry instead of a
placeholder.

```bash
adom-lbr generate --sym ADS8588SIPM.kicad_sym --fp ADS8588SIPM.kicad_mod -o ADS8588SIPM.lbr
```

What the writer does with each `.kicad_mod` pad:

| KiCad (`.kicad_mod`, mm, Y-down) | EAGLE (`.lbr`, mm, Y-up) |
|---|---|
| `(pad "17" smd rect (at -3.75 5.738 90) (size 1.475 0.3))` | `<smd name="17" x="-3.75" y="-5.738" dx="1.475" dy="0.3" layer="1" rot="R90"/>` |
| thru-hole `(pad … (at x y) (size …) (drill d))` | `<pad … x drill diameter shape="long"/>` |

- **Y is negated** (KiCad Y-down → EAGLE Y-up).
- **Rotation** maps the KiCad angle straight to `rot=R90/R180/R270`, matching
  Fusion's own library-export convention (verified by the Fusion bridge against a
  round-tripped `.flbr`): a KiCad-90 pad on a QFP/SOIC top/bottom side becomes
  `rot="R90"`, oriented along the lead.
- **Silkscreen outline** is derived from the footprint body — `F.SilkS` lines
  first, then `F.Fab`, then a fallback bounding box of the pads. Each `fp_line`
  is matched to its *own* layer (a balanced s-expression parse), so courtyard
  and body lines never bleed together.

### Worked example — ADS8588SIPM (LQFP-64)

| | Before (placeholder) | After (`--fp`) |
|---|---|---|
| Pads | 32 at x=−8, 32 at x=+8 (2 columns) | 16 left / 16 right / 16 top / 16 bottom |
| Extent | y only, ±7.5 | X **and** Y, ±5.74 mm |
| Shape in Fusion | a domino, not the chip | true 4-sided LQFP-64 |

![Generated LQFP-64 footprint, 4 sides, pin 1 top-left](screenshot-footprint-4sided.png)

Without `--fp` you still get a *valid* `.lbr` — just a two-column placeholder
package. That's intentional for symbol-only parts. For anything you'll fabricate,
pass `--fp`.

## The 3D model: a Fusion-side attach (not a flat-file write)

This is the part that can't be baked into a flat file. In EAGLE/Fusion a
package's 3D body is **not embedded** — it's a cloud reference, and the model
only **renders once Fusion brings it into a session**. So adom-lbr keeps the
`.lbr` 2D and the **Fusion bridge does the attach**; the sidecar just points the
bridge at the model and the package.

> **Verified by the Fusion bridge** (round-tripping a bound `.flbr` and reading
> the EAGLE XML). The earlier assumption that Fusion mints an
> `urn:adsk.eagle:package:NNN/V` was **wrong for self-uploaded models** — that
> form is for *managed* (library.io) parts only.

### The real markup — self-uploaded model

A model you upload yourself is linked by **`wip_urn`** (the *versioned file* URN),
with `urn` left **empty** and `locally_modified="yes"`:

```xml
<packages3d>
  <package3d name="ADS8588SIPM" urn="" wip_urn="urn:adsk.wipprod:fs.file:vf.<id>?version=1"
             locally_modified="yes" type="model">
    <packageinstances><packageinstance name="ADS8588SIPM"/></packageinstances>
  </package3d>
</packages3d>
…
<device name="" package="ADS8588SIPM">
  <connects>…</connects>
  <package3dinstances><package3dinstance package3d_urn="urn:adsk.wipprod:fs.file:vf.<id>?version=1"/></package3dinstances>
  <technologies><technology name=""/></technologies>
</device>
```

Use the `?version=1` **file** URN (`fs.file:vf`), **not** the `dm.lineage` URN
that an upload returns first. (`urn:adsk.eagle:package:NNN/V` is managed-only.)

### Step 1 — generate the `.lbr` + a 3D sidecar

```bash
adom-lbr generate --sym part.kicad_sym --fp part.kicad_mod --model part.step -o part.lbr
# → part.lbr            (2D: symbol + real footprint)
# → part.fusion3d.json  (the handoff manifest)
```

The sidecar (`schema: "adom-lbr.fusion3d/2"`) carries no pre-minted URN — it just
names `model.path`, `package`, and the placement:

```json
{
  "schema": "adom-lbr.fusion3d/2",
  "package": "ADS8588SIPM",
  "model": { "path": "part.step", "format": "step", "units": "mm" },
  "placement": { "translation_mm": [0,0,0], "rotation_deg": [0,0,0], "note": "…run chipfit before FINISH" },
  "binding": { "scheme": "self-hosted", "wip_urn": null, "package3d_attrs": { "urn": "", "wip_urn": "<…vf…?version=1>", "locally_modified": "yes", "type": "model" } },
  "status": "pending-fusion-attach",
  "bridge_steps": [ "open .lbr (library ACTIVE)", "Create3DPackage → import model → orient → FINISH → Save", "…" ]
}
```

### Step 2 — the bridge attaches the model (this both binds AND renders)

The Fusion bridge: opens the `.lbr` (library **active**) → `Electron.Create3DPackage`
→ imports `model.path` → auto-orients (your `placement` is an **override** for its
"rotate smallest bbox dim to Z" default) → `Package3DStop` (**FINISH**) → **Save**.
The Content Manager tree then shows the 3D nested under the package. (Run
[chipfit](../../adom-chipfit) before FINISH to confirm pin-1 / seat-Z.)

This is the whole job for the normal case — the FINISH step is what makes the body
actually render. The **in-Fusion library is the runtime source of truth.**

### Step 3 — record the URN back (provenance)

After Save, the bridge reports the saved version's URN —
`fusion_aps_file_info {query:"<package>"}` returns the tip
`fs.file:vf.<id>?version=N`. adom-lbr **owns the sidecar record** and writes it:

```bash
adom-lbr bind-3d --record-only --sidecar part.fusion3d.json \
  --wip-urn "urn:adsk.wipprod:fs.file:vf.<id>?version=N" \
  --lineage-urn "urn:adsk.wipprod:dm.lineage:<id>"   # optional provenance
```

`--record-only` writes `binding.wip_urn` + `status: "bound"` (and the optional
`lineage_urn`) **without touching the `.lbr`** — bookkeeping for idempotency and
provenance. `dm.lineage` is kept here only as the stable cross-version id; it is
**never** written into the `.lbr` (Fusion pins the versioned `fs.file` URN).

### Optional — pre-write the binding into the `.lbr` (fast-path)

Once recorded/known-synced, you can bake the binding into the `.lbr` for a
self-contained library:

```bash
adom-lbr bind-3d --lbr part.lbr \
  --wip-urn "urn:adsk.wipprod:fs.file:vf.<id>?version=N" --sidecar part.fusion3d.json
# managed/library.io part instead?  --urn "urn:adsk.eagle:package:NNN/V"
```

`bind-3d` inserts the `<packages3d>` block + the device `<package3dinstance>`,
re-lints, and updates the sidecar. It's **idempotent**, and refuses a `dm.lineage`
URN for the `.lbr`. **Caveat:** a pre-written `wip_urn` shows a **gray placeholder**
on a cold open — Fusion renders the body only once the model is brought into a
session via FINISH. So this is for known-synced models, not a substitute for the
bridge attach.

## End-to-end

```bash
# 1. Generate the 2D .lbr + sidecar from the KiCad part + STEP
adom-lbr generate --sym part.kicad_sym --fp part.kicad_mod --model part.step -o part.lbr

# 2. Hand part.lbr + part.fusion3d.json + the STEP to the Fusion bridge.
#    It does Create3DPackage → import → orient → FINISH → Save (binds + renders).

# 3. Record the URN the bridge reports (fusion_aps_file_info) back into the sidecar:
adom-lbr bind-3d --record-only --sidecar part.fusion3d.json --wip-urn "urn:adsk.wipprod:fs.file:vf.<id>?version=N"

# 4. (Optional) bake the binding into the .lbr for a self-contained library:
adom-lbr bind-3d --lbr part.lbr --wip-urn "urn:adsk.wipprod:fs.file:vf.<id>?version=N" --sidecar part.fusion3d.json

# 5. Verify the 2D library
adom-lbr validate part.lbr        # complete deviceset, 0 errors
```

## Altium / OrCAD

Both import the same EAGLE `.lbr` directly — no separate generate step. A desktop
**bridge** for automated "Send to Altium/OrCAD" isn't built yet; until then, hand
the `.lbr` over and import it manually. Want that bridge? Build it and contribute
it on the wiki — adom-lbr's "Send to" already routes those targets.

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| Footprint is two columns / a domino | generated without `--fp` | regenerate with `--fp part.kicad_mod` |
| Fusion: "No Components" | empty `<packages>` or `<connects>` | `adom-lbr lint` — fix the reported code |
| 3D body is a gray placeholder | binding pre-written but model not in session | let the bridge attach it (Create3DPackage → FINISH); that renders it |
| 3D body offset / wrong pin 1 | placement not aligned | run chipfit, set `placement` in the sidecar before FINISH |
| `bind-3d` rejected the URN | wrong URN family | self-uploaded → `--wip-urn` (`fs.file:vf …?version=1`); managed → `--urn` (`eagle:package`) |
| Outline looks lopsided | (fixed in 1.1) | upgrade — silk lines are now matched per-layer |
