"""Assign the 3 random companions to Hunyuan3D-2mv slots (side->right, back->back, oside->left) by relative azimuth, identical cost to fb150/build_fb150.py:assign_slots. Builds a symlinked inputs dir with renamed tags; writes per-object mapping.""" import json, itertools, os, sys, numpy as np R='/lp-dev/jonghoon/mv-mesh' def horiz(v): v=np.array(v,float).copy(); v[2]=0; return v/(np.linalg.norm(v)+1e-12) def feats_of(exp,o,front,comps): C=lambda t: np.load(f'{exp}/renders/{o}_{t}.npz')['c2w_cv'] F=horiz(C(front)[:3,3]); out={} for c in comps: M=C(c); d=horiz(M[:3,3]); r=horiz(M[:3,0]) out[c]=dict(cos_to_front=round(float(F@d),4),F_dot_right=round(float(F@r),4), rel_azimuth_deg=round(float(np.degrees(np.arctan2(np.cross(F,d)[2],F@d))),2)) return out def assign(feats): ideal={"side":(0.0,1.0),"back":(-1.0,0.0),"oside":(0.0,-1.0)}; best=None for perm in itertools.permutations(list(feats)): cost=sum((feats[c]["cos_to_front"]-ideal[t][0])**2+(feats[c]["F_dot_right"]-ideal[t][1])**2 for t,c in zip(["side","back","oside"],perm)) if best is None or cost