import numpy as np from scipy.spatial.transform import Rotation as R from vid2rig.rig_io import to_glb from vid2rig.retarget.bonemap import canonical, validate_target from vid2rig.retarget.rig import TargetRig from vid2rig.retarget.config import RetargetCfg from vid2rig.retarget.retarget import retarget from vid2rig.motion import MotionClip def test_canonical_collapses_separators(): assert canonical("mixamorig:Hips") == "mixamorigHips" assert canonical("mixamorig_Hips") == "mixamorigHips" assert canonical("mixamorigHips") == "mixamorigHips" def test_validate_target_separator_insensitive(): m = {"pelvis": "mixamorigHips"} assert validate_target(m, {"mixamorig:Hips"}) == [] # colon variant counts as present def test_to_glb_passthrough(tmp_path): p = tmp_path / "x.glb"; p.write_bytes(b"glTF") assert to_glb(str(p)) == str(p) def test_retarget_matches_colon_bone_names(): names = ["mixamorig:Hips", "mixamorig:Spine"]; parents = [-1, 0] offsets = np.array([[0, 0, 0], [0, 1, 0.0]]) rng = np.random.default_rng(1); F = 3 rots = np.stack([[R.from_rotvec(rng.normal(0, 0.3, 3)).as_quat() for _ in range(2)] for _ in range(F)]) clip = MotionClip(fps=30, num_frames=F, source="smpl", joint_names=["pelvis", "spine1"], local_rotations=rots, root_translation=np.zeros((F, 3)), root_rotation=rots[:, 0], rest_offsets=offsets, parents=parents, source_height_m=1.7) rig = TargetRig(node_index={n: i for i, n in enumerate(names)}, bone_names=names, parents=parents, bind_local_rot=np.tile([0, 0, 0, 1.0], (2, 1)), bind_local_trans=offsets, root_node_world_rot=np.array([0, 0, 0, 1.0]), height_m=1.7) anim = retarget(clip, rig, {"pelvis": "mixamorigHips", "spine1": "mixamorigSpine"}, RetargetCfg()) # rotations keyed by the RAW node name (so the baker finds the node), identity invariant holds assert "mixamorig:Spine" in anim.rotations for f in range(F): assert np.allclose(np.abs(anim.rotations["mixamorig:Spine"][f]), np.abs(rots[f, 1]), atol=1e-6)