"""Verify the emitted hand-morph GLB numerically: 1. CROSS-HAND INDEPENDENCE: each hand__ shape must move ONLY that side's verts. 2. CURL DIRECTION: per shape, left-hand fingertip-region deltas must point toward the palm (dot with palm normal < 0) — catches a flipped normal making fingers bend backward. """ import json, struct, sys from pathlib import Path import numpy as np def read_glb(path): d = Path(path).read_bytes() ln = struct.unpack_from("= 0 else False for l in label]) other &= dmin < 0.08 cross = float(mag[other].max()) if other.any() else 0.0 # 2) curl direction: fingertip verts of THIS side (non-thumb, nearest <= 3.5cm of _03 bone) tip = np.zeros(len(P), dtype=bool) for d in ("index", "middle", "ring", "pinky"): bn = f"{d}_03_{side}" if bn in heads: tip |= (np.linalg.norm(P - heads[bn], axis=1) < 0.035) if tip.any(): h = head(f"hand_{side}") mid, ix, pk = head(f"middle_01_{side}"), head(f"index_01_{side}"), head(f"pinky_01_{side}") n = np.cross(mid - h, pk - ix); n /= np.linalg.norm(n) dots = delta[tip] @ n toward = float((dots < 0).mean()) else: toward = -1 own = ~other & (dmin < 0.08) ownmax = float(mag[own].max()) * 100 if own.any() else -1.0 print(f"{tname}: max|delta| other-hand={cross*100:.2f}cm tip-toward-palm={toward*100:.0f}% " f"max|delta| own={ownmax:.1f}cm")