"""ONE-PIECE arm base (Cameron 2026-07-11): holder + mount + connector + fiducial board fused — no
detach pins. Board shrunk to 80x80mm (the UMI-grid sheet size), same 4mm thickness, flush with the
table, near edge kept where the old 160 board started (connector reach unchanged)."""
import numpy as np, trimesh
import gen_two_servo as g

scene = trimesh.load("two_servo_from_blender.glb")
base_inv = np.linalg.inv(np.array(scene.graph["servo_0"][0], float))
P = {}
for node in sorted(scene.graph.nodes_geometry):
    if node.startswith("base_cube"):
        T, gn = scene.graph[node]
        m = scene.geometry[gn].copy()
        m.apply_transform(base_inv @ np.array(T, float))
        m.apply_scale(1000.0 / g.GLB_SCALE)
        e = sorted(m.extents, reverse=True)
        kind = "board" if e[1] > 100 else ("connector" if e[0] > 100 else "mount")
        P[kind] = m.bounds
def box(b0, b1):
    b = trimesh.creation.box(extents=np.array(b1) - np.array(b0))
    b.apply_translation((np.array(b0) + np.array(b1)) / 2); return b
(bx0, by0, bz0), (bx1, by1, bz1) = P["board"]
mount_zc = P["mount"].mean(0)[2]
near, far = (bz1, bz0) if abs(bz1 - mount_zc) < abs(bz0 - mount_zc) else (bz0, bz1)
sgn = float(np.sign(far - near))
(ccx0, _, _), (ccx1, _, _) = P["connector"]
xc = (ccx0 + ccx1) / 2                     # center the small board ON THE CONNECTOR (old 160 board was offset)

new_board = box([xc - 40.0, by0, min(near, near + sgn * 80.0)],
                [xc + 40.0, by1, max(near, near + sgn * 80.0)])
fused = trimesh.boolean.union([trimesh.load("holder_fillet.stl", force="mesh"),
                               box(*[list(v) for v in P["mount"]]),
                               box(*[list(v) for v in P["connector"]]),
                               new_board], engine="manifold")
fused.export("base_fused_smallboard.stl")
print(f"base_fused_smallboard: wt={fused.is_watertight} bodies={len(fused.split(only_watertight=False))} "
      f"vol={fused.volume/1000:.1f}cm3 bbox={[round(v) for v in fused.extents]}mm")
print(f"board: 80x80 at x {xc-40:.0f}..{xc+40:.0f}, z {min(near, near+sgn*80):.0f}..{max(near, near+sgn*80):.0f}, "
      f"bottom y {min(by0,by1):.1f} (flush = same as before)")
