"""Joined printable arm links: link_i = yoke_{i-1} (transformed into servo_i frame) + connector_beauty_i +
holder_i, boolean-unioned. The connector engulf overlap IS the fusion (Cameron 2026-07-06). One rigid
kinematic link per piece; yoke bolt plates stay exposed. Holder = unfilleted (fillet source on phe)."""
import numpy as np, trimesh, trimesh.repair
import gen_two_servo as g
def load(f):
    m = trimesh.load(f, force="mesh")
    if not m.is_volume:
        m.merge_vertices(merge_tex=True, merge_norm=True)
        m.update_faces(m.nondegenerate_faces()); m.update_faces(m.unique_faces())
        trimesh.repair.fill_holes(m); m.fix_normals()
    return m
g.LAYOUT = g.read_layout("two_servo_from_blender.glb")
holder = load("holder_approx_drilled.stl")
for i in range(1, len(g.LAYOUT)):
    yoke = load("yoke_fillet.stl")
    yoke.apply_transform(g._mm(np.linalg.inv(g.LAYOUT[i]) @ g.LAYOUT[i - 1]))   # servo_{i-1} -> servo_i frame
    conn = load(f"connector_beauty_{i}.stl")
    link = trimesh.boolean.union([yoke, conn, holder.copy()], engine="manifold")
    link.export(f"arm_link_{i}.stl")
    b = len(link.split(only_watertight=False))
    flag = "  <-- DISJOINT!" if b > 1 else ""
    print(f"arm_link_{i}: wt={link.is_watertight} bodies={b} vol={link.volume/1000:.1f}cm3 bbox={[round(v) for v in link.extents]}mm{flag}")
