← Commit history

Skill: rewrite the lead-GLB recipe with all this session's learnings (Draco decode, 1.6mm board, 1oz copper, hemisphere paste, 3D-text-not-texture, apex-on-target arrows, interference-fit not gas-tight, text-no-shadow via alpha-blend, no assembly rotation, native .fsch/.fbrd, viewer refreshRate PR, pup workflow)

John Lauer ·56ffede630 ·3mo ago ·parent 53b87da
2 files changed +68−40
AdomMachinePins-complete.zip
⋯ 1 unchanged line ⋯
SKILL.md+68−40
@@ -175,43 +175,71 @@ for mesh in g.meshes:            # decode each primitive Render at rest AND at the animation peak (lift the PIN_* nodes) and LOOK at both. Only push once the pins visibly seat in their own holes and the solder balls ring each pad. -## The reliable lead-GLB recipe that shipped (use the real Fusion board, do not hand-build the slab)--The user explicitly wanted the REAL Fusion board, not a hand-modeled slab. So: export the Fusion 3D-PCB to STEP, `step2glb` it (real holes, pads, pins, colors), then Draco-DECODE it and rebuild an-uncompressed GLB with `trimesh` so you can add what STEP drops and can render-verify it:-  - board/soldermask meshes -> `alphaMode=BLEND` alpha ~0.45 (transparent FR4, see the tails through it);-  - merge each pin's many sub-meshes (nodes named `MpinContact*` / `=>[0:1:1:*]`) into ONE node per pin-    (`PIN_0..PIN_3`) so it animates as a unit;-  - 300um solder-paste balls: a ring of small metallic spheres just outside each barrel base, on the-    board top surface (auto-size the ring from each pin's detected barrel radius: ~9 balls medium, ~16 large);-  - add a gentle 2 to 3mm insertion animation on the PIN_* nodes (see requirement 9);-  - keep balls STATIC (paste stays on the pad; only the pin moves).-Scripts that shipped this: `/tmp/build_board_glb.py` (decode + rebuild + balls) and-`/tmp/add_anim_board.py` (4-pin animation).--## Recreate the lead GLB animation (all 4 pins, real holes, silk, solder balls)--The wiki lead 3D is a single hand-built GLB that the native Babylon viewer auto-plays. Build it with-`trimesh` + `pygltflib` (no Fusion export, so it can carry silk + animation):--1. **FR4 board with REAL holes:** `board = trimesh.creation.box([50,50,1.6])`, then for each pin-   `board = board.difference(trimesh.creation.cylinder(radius=drill/2, height=5))` translated to the-   pad center (needs `manifold3d`). Material: green, `alphaMode=BLEND`, `baseColorFactor` alpha 0.5,-   `doubleSided` (transparent FR4 so you see the press-fit through it).-2. **Per pin:** the real STEP-derived pin mesh (Draco-decode the pin `.glb` with node-transform-   composition, gold), annular copper pads top+bottom (`trimesh.creation.annulus(drill/2, pad/2)`), a-   plated barrel in the hole, and a ring of 300um **hemisphere** solder domes top+bottom (manual lathe;-   flat side on the copper). Reuse `adom-footprint/src/paste.rs` dot math (`DOT_DIA=0.30`).-3. **Silkscreen:** a flat textured quad on the board top per pin (PIL renders name/drill/ring as white-   text on transparent; material `alphaMode=MASK`, doubleSided). Position beside each pad like the Fusion-   silk.-4. **Animation:** one glTF translation animation with a sampler+channel per PIN node; keyframe the pin-   up by `-zmin` (its seated depth), hold, press down, loop. Append the accessors to the binary blob-   with the `add_anim.py` bytearray pattern (do NOT hand-splice bufferView offsets, it corrupts the GLB).-5. Assign PBR materials (FR4 transparent, gold pin, copper pads/barrel, teal paste, silk texture) with-   `pygltflib` and drop `COLOR_0` so `baseColorFactor` wins. Put the GLB on the page as `model_3d`.--Known limitation: the native viewer's ground-plane shadows do not track animated meshes (they look-static as the pins move). That is a viewer behavior, not the GLB; a viewer/git-wiki tweak-(dynamic shadow caster or `showGround:false`) is the real fix.+## The lead-GLB recipe that shipped (authoritative, every step below was learned the hard way)++The lead 3D is ONE GLB the native Babylon viewer auto-plays. Base it on the REAL Fusion board (the+user insisted, no hand-modeled slab for the pins). Full builder: [`scripts/build_board_glb.py`];+animation: [`scripts/add_anim_board.py`]. The exact ordered recipe:++1. **Decode the real Fusion board.** Export the Fusion 3D PCB to STEP, `step2glb` it -> a **Draco**+   GLB. `trimesh` CANNOT read Draco (returns zero-area meshes). Use `DracoPy.decode` per primitive ++   compose node transforms yourself. Pins are the nodes named `MpinContact*` / `=>[0:1:1:*]`; cluster+   them by xy into 4, merge each into ONE node (`PIN_0..PIN_3`) so it animates as a unit.+2. **1.6mm FR4 board with REAL holes.** The Fusion 3D export is only ~0.3mm thick, REBUILD it: a+   `trimesh.creation.box([50,50,1.6])`, `.difference()` a drill-diameter cylinder per pad (needs+   `manifold3d`). Green, `alphaMode=BLEND` alpha ~0.46, **single-sided** (`doubleSided=False`) so the+   transparent slab does not show its own back-face triangulation.+3. **1oz copper (~35um), NOT buried in the FR4.** Per pad: a `trimesh.creation.annulus(drill/2, pad/2)`+   ring sitting PROUD on the top surface, another PROUD on the bottom, and a thin barrel+   (`annulus(drill/2-CU, drill/2)`) lining the hole, sticking out a hair top+bottom. Copper is real+   1oz = a super-thin layer, do NOT make it thick.+4. **300um HEMISPHERE solder paste.** `icosphere` then `slice_plane([0,0,0],[0,0,1],cap=True)` = a dome+   with a flat bottom on the pad. Real 0.30mm dia (do NOT oversize), on the annular ring at ~0.45mm+   pitch. Keep balls STATIC (paste stays on the pad; only the pin moves).+5. **Silkscreen + callouts = 3D EXTRUDED TEXT, never a texture.** A flat texture on the board fights+   you forever: its UV + the GLB export's per-face mirror + any rotation keep DE-SYNCING the label+   positions from the pins (this caused a med<->large label swap that shipped). 3D text meshes+   (`text_mesh()` -> `extrude_polygon`) live in the same coordinate space as the pins, so labels stay+   glued to their pins. Cut-out letter counters via even-odd = full-polygon containment+   (`o.contains(other)`); a centroid point falls in the counter of O/D/e and misclassifies.+6. **Callout arrows: apex ON the target.** trimesh `cone` has its apex at +Z*height; after orienting+   the axis to the arrow direction, translate the BASE to `tip - height*dir` so the POINT lands on the+   target (ball / hole), not 1mm past it.+7. **Callout wording.** Show `hole Ø<drill>  pin Ø<body>` and `pin <Δ>mm wider = interference fit`. Do+   NOT say "gas-tight" (press-fit marketing jargon, overstated, not everyday EE language). Snap the+   SOLDER-PASTE arrow tip to the ACTUAL nearest ball centre (balls are discrete 300um dots).+8. **Text must cast NO shadow, and this is GLB-only.** The viewer's ShadowGenerator has+   `transparencyShadow=false`, so it SKIPS alpha-blended meshes when building the shadow map. Give the+   silk/callout materials alpha ~0.98 (`alphaMode=BLEND`, still visually solid) => they render but cast+   no shadow. No viewer change, no `nocast` naming needed.+9. **Orientation: do NOT rotate the assembly.** 3D text in its natural orientation already reads+   left-to-right at the viewer's DEFAULT camera. Rotating the assembly 180 (a tempting "face the+   camera" fix) flips the text upside-down. Leave `ROT_CENTER=None`.+10. **Animation:** one glTF translation animation, a sampler+channel per `PIN_*` node, gentle **2-3mm**+    lift (a big lift makes the pins look like giant floating pins over tiny holes). Append accessors to+    the binary blob with the `add_anim.py` bytearray pattern (do NOT hand-splice bufferView offsets).+11. **VERIFY BEFORE PUSH, every time.** Headless-render (DracoPy + pyrender EGL, see the section above)+    AND load in the real Babylon viewer via pup. Confirm by MEASUREMENT that each label's nearest pin+    is the right SIZE (Ø1.6 medium vs Ø4.8 large) — the label swap was invisible in code.++## Native Fusion Electronics files to ship++Provide `fusion/AdomPinsBoard.fsch` (schematic) + `.fbrd` (board) + `.f3d` (3D design) + `.step`.+**Fusion Electronics IS EAGLE 9.x**, so `.fsch`/`.fbrd` are just the EAGLE XML (`<eagle version="9.6.2">`)+with Fusion's extensions — rename the EAGLE `.sch`/`.brd`, they open directly in Fusion (File > Open).+The `.f3d` is the 3D BREP design (NOT the ECAD design); it does not contain the schematic/board.++## The ONE thing that still needs a viewer PR (adom-3d-viewer-babylon9, Colby)++Real cast shadows do NOT animate: the viewer freezes the shadow map (`AdomStatics.refreshShadows()`+sets `refreshRate = REFRESHRATE_RENDER_ONCE`). Fix = keep `REFRESHRATE_RENDER_ONEVERYFRAME` when the+scene has `animationGroups`. That is a viewer-repo change (staged), send it as a PR to Colby; it cannot+be fixed from the GLB.++## pup / show-the-user workflow (do not make them hunt for the window)++- The pup window does NOT auto-update on a push. After each push, **re-navigate** it with a `?v=N`+  cache-bust (the viewer caches the GLB) so it shows the new model.+- Do NOT `location.reload(true)` — it wedges the CDP session and you have to reopen the window.+- REUSE one maximized window; show it on the machine the user is **physically** at (ask which if a VNC+  is in play — a window on the VNC'd host is on the wrong screen).