78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
|
|
"""Edge-stretch comparison across OBJ dirs, with a rest-length split.
|
||
|
|
|
||
|
|
usage: stretch_cmp.py <rest_dir> <label>=<dir> [<label>=<dir> ...]
|
||
|
|
Rest OBJs (rest_hand_l/r.obj) come from <rest_dir>; every compared dir must share the
|
||
|
|
body's vertex order. Reports, per pose and hand: max / p99.9 / >2x / >5x over ALL edges,
|
||
|
|
then the subset that is real geometry (>=1mm rest length) and the subset that is
|
||
|
|
VISIBLE (posed length >=1cm) — the count that decides whether a render shows a needle.
|
||
|
|
"""
|
||
|
|
import math
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
|
||
|
|
|
||
|
|
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(t.split("/")[0]) - 1 for t 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)))
|
||
|
|
|
||
|
|
|
||
|
|
rest_dir = sys.argv[1]
|
||
|
|
cols = [a.split("=", 1) for a in sys.argv[2:]]
|
||
|
|
|
||
|
|
for hand in ("l", "r"):
|
||
|
|
rest_v, rest_f = load_obj(os.path.join(rest_dir, f"rest_hand_{hand}.obj"))
|
||
|
|
edges = edge_set(rest_f)
|
||
|
|
rest_len = [dist(rest_v[a], rest_v[b]) for a, b in edges]
|
||
|
|
print(f"\n=== hand_{hand} ({len(rest_v)} verts, {len(edges)} edges) ===")
|
||
|
|
print(f"{'pose / build':22s} {'max':>8s} {'p99.9':>7s} {'>2x':>6s} {'>5x':>6s}"
|
||
|
|
f" {'>5x real':>9s} {'>=1cm':>7s}")
|
||
|
|
for pose in ("flat", "fist", "grip"):
|
||
|
|
for label, d in cols:
|
||
|
|
f = os.path.join(d, f"{pose}_hand_{hand}.obj")
|
||
|
|
if not os.path.exists(f):
|
||
|
|
continue
|
||
|
|
v, _ = load_obj(f)
|
||
|
|
if len(v) != len(rest_v):
|
||
|
|
print(f"{pose+' '+label:22s} VERT COUNT MISMATCH {len(v)} vs {len(rest_v)}")
|
||
|
|
continue
|
||
|
|
rs = []
|
||
|
|
gt5 = gt2 = real5 = vis = 0
|
||
|
|
for (a, b), rl in zip(edges, rest_len):
|
||
|
|
if rl <= 1e-9:
|
||
|
|
continue
|
||
|
|
lq = dist(v[a], v[b])
|
||
|
|
r = lq / rl
|
||
|
|
rs.append(r)
|
||
|
|
if r > 2:
|
||
|
|
gt2 += 1
|
||
|
|
if r > 5:
|
||
|
|
gt5 += 1
|
||
|
|
if rl >= 0.001:
|
||
|
|
real5 += 1
|
||
|
|
if lq >= 0.01:
|
||
|
|
vis += 1
|
||
|
|
rs.sort()
|
||
|
|
print(f"{pose+' '+label:22s} {rs[-1]:7.1f}x {rs[int(len(rs)*0.999)]:6.2f}x"
|
||
|
|
f" {gt2:6d} {gt5:6d} {real5:9d} {vis:7d}")
|