chore(mako): handfix keepers — clip-curl pose recipe, repair masks, fin_locate
pose_clipcurl_idle.json is the recipe for extracting a clip's pinned curl; the .txt files are the crosshand-repair vertex masks. The 230 MB of A/B bake GLBs and render dirs stay out as regenerable scratch (now gitignored); review/ evidence PNGs were already in the ignored tier. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
"""Cluster the worst-stretched edges by REST position to name the digit region."""
|
||||
import sys, os, math
|
||||
|
||||
def load_obj(path):
|
||||
vs, faces = [], []
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
if line.startswith('v '):
|
||||
p = line.split()
|
||||
vs.append((float(p[1]), float(p[2]), float(p[3])))
|
||||
elif line.startswith('f '):
|
||||
idx = [int(tok.split('/')[0]) - 1 for tok in line.split()[1:]]
|
||||
for i in range(1, len(idx) - 1):
|
||||
faces.append((idx[0], idx[i], idx[i + 1]))
|
||||
return vs, faces
|
||||
|
||||
def edge_set(faces):
|
||||
es = set()
|
||||
for a, b, c in faces:
|
||||
for u, v in ((a, b), (b, c), (c, a)):
|
||||
es.add((u, v) if u < v else (v, u))
|
||||
return sorted(es)
|
||||
|
||||
def dist(p, q):
|
||||
return math.sqrt(sum((p[i]-q[i])**2 for i in range(3)))
|
||||
|
||||
d = sys.argv[1]
|
||||
for hand in ('l', 'r'):
|
||||
rest_v, rest_f = load_obj(os.path.join(d, f'rest_hand_{hand}.obj'))
|
||||
edges = edge_set(rest_f)
|
||||
xs = sorted(v[0] for v in rest_v)
|
||||
print(f'hand_{hand}: rest x range {xs[0]*100:.1f}..{xs[-1]*100:.1f} cm, '
|
||||
f'y range {min(v[1] for v in rest_v)*100:.1f}..{max(v[1] for v in rest_v)*100:.1f}, '
|
||||
f'z range {min(v[2] for v in rest_v)*100:.1f}..{max(v[2] for v in rest_v)*100:.1f}')
|
||||
for pose in ('fist', 'grip'):
|
||||
v, _ = load_obj(os.path.join(d, f'{pose}_hand_{hand}.obj'))
|
||||
bad = []
|
||||
for a, b in edges:
|
||||
rl = dist(rest_v[a], rest_v[b])
|
||||
if rl <= 1e-9:
|
||||
continue
|
||||
r = dist(v[a], v[b]) / rl
|
||||
if r > 5:
|
||||
bad.append((r, a))
|
||||
# bounding box of bad verts in rest space
|
||||
pts = [rest_v[a] for _, a in bad]
|
||||
if not pts:
|
||||
print(f' {pose}: no >5x edges')
|
||||
continue
|
||||
bx = (min(p[0] for p in pts)*100, max(p[0] for p in pts)*100)
|
||||
by = (min(p[1] for p in pts)*100, max(p[1] for p in pts)*100)
|
||||
bz = (min(p[2] for p in pts)*100, max(p[2] for p in pts)*100)
|
||||
cx = sum(p[0] for p in pts)/len(pts)*100
|
||||
cy = sum(p[1] for p in pts)/len(pts)*100
|
||||
cz = sum(p[2] for p in pts)/len(pts)*100
|
||||
print(f' {pose}: {len(bad)} edges>5x rest-bbox x[{bx[0]:.1f},{bx[1]:.1f}] '
|
||||
f'y[{by[0]:.1f},{by[1]:.1f}] z[{bz[0]:.1f},{bz[1]:.1f}] centroid ({cx:.1f},{cy:.1f},{cz:.1f}) cm')
|
||||
# z-histogram (palm axis?) to see if it's one digit or spread
|
||||
zs = sorted(p[2]*100 for p in pts)
|
||||
q = lambda f: zs[int(f*(len(zs)-1))]
|
||||
print(f' rest z quartiles: {q(0):.1f} / {q(0.25):.1f} / {q(0.5):.1f} / {q(0.75):.1f} / {q(1):.1f}')
|
||||
Reference in New Issue
Block a user