"""Diagnose cross-midline finger bindings: for every offending ref, compare the lever arm to the WRONG joint against the lever to its MIRRORED counterpart, so we can tell whether a straight _r <-> _l joint remap is geometrically correct (small mirrored lever) or would tear (vert nowhere near the mirrored bone either). Also reports each offending vert's full influence list, and the nearest correct finger joint. usage: crosshand_diagnose.py body.glb """ import json, struct, sys, math from pathlib import Path from collections import Counter, defaultdict FING = ("thumb", "index", "middle", "ring", "pinky") def read_glb(p): d = Path(p).read_bytes() length = struct.unpack_from(" 0.02) or (nl.endswith("_l") and p[0] < -0.02): bad.append((vi, n, w, p)) print(f"\n cross-midline finger refs: {len(bad)}") vids = sorted({v for v, _, _, _ in bad}) print(f" distinct verts affected : {len(vids)} (index range {min(vids)}..{max(vids)})") # lever comparison: wrong joint vs mirrored joint vs nearest correct-side finger joint print(f"\n {'joint':16s} {'n':>5s} {'lever_wrong':>12s} {'lever_mirror':>13s} {'nearest_correct'}") groups = defaultdict(list) for vi, n, w, p in bad: groups[n].append((vi, w, p)) for n in sorted(groups): rows = groups[n] mn = mirror_name(n) lw = [math.dist(p, jpos[n]) * 100 for _, _, p in rows] lm = [math.dist(p, jpos[mn]) * 100 for _, _, p in rows] if mn in jpos else [float("nan")] # nearest correct-side finger joint for a sample vert side = "_l" if rows[0][2][0] > 0 else "_r" cand = [(math.dist(rows[0][2], jpos[k]) * 100, k) for k in jpos if any(t in k.lower() for t in FING) and k.endswith(side)] cand.sort() print(f" {n:16s} {len(rows):5d} {sum(lw)/len(lw):9.1f}cm {sum(lm)/len(lm):10.1f}cm " f" {cand[0][1]} @ {cand[0][0]:.1f}cm") # full influence list for a few offenders print("\n sample offending verts (full influence list):") for vi in vids[:6]: p = P[vi] infl = [] for j, w in zip(J[vi], W[vi]): w *= wsc if w > 0.001: infl.append(f"{jname[j]}={w:.3f}") print(f" v{vi} pos=({p[0]*100:6.1f},{p[1]*100:6.1f},{p[2]*100:6.1f})cm {' '.join(infl)}") # how many offending verts are FULLY (>0.99) bound to a wrong joint full = sum(1 for vi, n, w, p in bad if w > 0.99) print(f"\n refs at weight > 0.99 (rigid, no blend to soften): {full}") # what fraction of total left-hand-region verts are affected hl = jpos.get("hand_l") if hl: near = [vi for vi, p in enumerate(P) if math.dist(p, hl) < 0.20] aff = set(vids) & set(near) print(f" verts within 20cm of hand_l: {len(near)}; of those affected: {len(aff)}")