"""Extract a hand pose (40 finger-bone quaternions) from a GLB clip at a chosen frame, report per-bone curl (deviation from skeleton rest), optionally dump JSON. usage: python extract_pose.py [--frame N | --max-curl] [--dump out.json] python extract_pose.py --rest --dump out.json (rest pose itself) """ import json, struct, sys, math from pathlib import Path FINGER_TOKENS = ("thumb", "index", "middle", "ring", "pinky") def read_glb(path): data = Path(path).read_bytes() magic, ver, length = struct.unpack_from(" best: best, bestf = curl, f frame = bestf elif "--frame" in sys.argv: frame = int(sys.argv[sys.argv.index("--frame")+1]) else: frame = 0 t = times_ref[min(frame, len(times_ref)-1)][0] if times_ref else 0 pose = {names[i]: list(tracks[i][frame]) for i in tracks} # fill missing finger bones from rest for i in finger_idx: pose.setdefault(names[i], list(rest[i])) label = f"{aname} frame {frame} (t={t:.2f}s)" # curl report per finger chain (sum of deviations from rest), L hand only for brevity print(f"pose: {label} ({len(pose)} bones)") for hand in ("_l", "_r"): parts = [] for fing in ("thumb", "index", "middle", "ring", "pinky"): tot = sum(qangle(pose[n], rest[i]) for i, n in finger_idx.items() if n.startswith(fing) and n.endswith(hand)) parts.append(f"{fing} {tot:.0f}") print(f" {hand}: curl-vs-rest deg " + " ".join(parts)) if dump: Path(dump).write_text(json.dumps({"source": f"{Path(glb).name}:{label}", "bones": pose}, indent=1)) print("dumped ->", dump) main()