"""Regenerate the gripper attachment pieces for the raised fin-ray layout:
- horn_mount = bare horn plate, ALL 4 bolt holes.
- yoke_conn (MOVING): lofted strut horn plate -> finger root. If Cameron repositioned the yoke_conn node in
  Blender, its translation delta vs the canonical strut is measured and replicated (his placement wins).
- fixed_conn (STATIC): lofted strut servo_to_finger plate -> finger_adj."""
import numpy as np, trimesh
from gen_curvy import _rrect, _stitch
import gripper_gen as gg

s = trimesh.load("gripper_from_blender.glb")
def wbox(node):
    T, gn = s.graph[node]
    w = trimesh.transform_points(s.geometry[gn].vertices, np.array(T, float)) * 100.0
    return w.min(0), w.max(0)

flo, fhi = wbox("finger"); plo, phi = wbox("servo_to_finger"); alo, ahi = wbox("finger_adj")
fc = (flo + fhi) / 2

def loft(p0, p1, e1ref, a0, b0, a1, b1, r=5.0, N=14):
    p0, p1 = np.array(p0, float), np.array(p1, float)
    u = p1 - p0; L = np.linalg.norm(u); u /= L
    e1 = np.array(e1ref, float); e1 -= np.dot(e1, u) * u; e1 /= np.linalg.norm(e1)
    e2 = np.cross(u, e1)
    rings = []
    for i in range(N):
        t = i / (N - 1)
        a, b = a0 * (1 - t) + a1 * t, b0 * (1 - t) + b1 * t
        q = _rrect(a, b, min(r, a - 0.5, b - 0.5))
        rings.append(p0 + u * L * t + np.outer(q[:, 0], e1) + np.outer(q[:, 1], e2))
    return _stitch(rings)

plate = gg._horn_plate(); plate.export("gnode_horn_mount_new.stl")

# --- yoke -> finger strut (moving) ---
a1y, b1y = (fhi[2] - flo[2]) / 2 - 1, (fhi[1] - flo[1]) / 2 - 1
p0 = np.array([-25.5, 13.0, fc[2]])
p1 = np.array([fhi[0] - 6.5, fc[1], fc[2]])
if "yoke_conn" in s.graph.nodes_geometry:                     # replicate Cameron's hand placement
    ylo, yhi = wbox("yoke_conn")
    canon = loft(p0, p1, [0, 0, 1], 9.0, 4.0, a1y, b1y, r=4.0)
    d = (ylo + yhi) / 2 - (canon.bounds[0] + canon.bounds[1]) / 2
    p0, p1 = p0 + d, p1 + d
    print(f"replicating Blender shift: {np.round(d, 1).tolist()} mm")
yk = loft(p0, p1, [0, 0, 1], 9.0, 4.0, a1y, b1y, r=4.0)
yk.export("gnode_yoke_conn.stl")

# --- plate -> finger_adj strut (static) ---
ac = (alo + ahi) / 2
q0 = [plo[0] + 3.0, ac[1] - 8.0, (plo[2] + phi[2]) / 2]
q1 = [alo[0] + 12.0, ac[1], ac[2]]
fx = loft(q0, q1, [0, 0, 1], a0=6.5, b0=11.0, a1=(ahi[2]-alo[2])/2 - 0.8, b1=(ahi[1]-alo[1])/2 - 0.8, r=5.0)
fx.export("gnode_fixed_conn.stl")
print(f"plate wt={plate.is_watertight}; yoke_conn wt={yk.is_watertight} bodies={len(yk.split(only_watertight=False))}; fixed_conn wt={fx.is_watertight}")
