/* * © 2026 TiedUp! Remake Contributors, distributed under GPLv3. */ package com.tiedup.remake.rig.anim; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import com.tiedup.remake.rig.armature.JointTransform; import com.tiedup.remake.rig.math.Vec3f; /** * Tests de {@link Pose} : interpolatePose, orElseEmpty, load (SET / OVERWRITE / * APPEND_ABSENT), disableAllJoints. * * Aucun MC runtime requis — Pose utilise uniquement des collections Java et * JointTransform (JOML + Vec3f, pas de Minecraft bootstrap). */ class PoseTest { // --- helpers --- private static JointTransform translation(float x, float y, float z) { return JointTransform.translation(new Vec3f(x, y, z)); } // --- orElseEmpty --- /** Un joint absent retourne JointTransform.empty() (pas null). */ @Test void orElseEmpty_unknownJoint_returnsEmpty() { Pose pose = new Pose(); JointTransform result = pose.orElseEmpty("nonexistent"); assertNotNull(result, "orElseEmpty ne doit pas retourner null"); // Verifier que la translation est zeros (empty = identite) assertEquals(0.0f, result.translation().x, 1e-5f); assertEquals(0.0f, result.translation().y, 1e-5f); assertEquals(0.0f, result.translation().z, 1e-5f); } /** Un joint present retourne son transform, pas le fallback. */ @Test void orElseEmpty_knownJoint_returnsTransform() { Pose pose = new Pose(); JointTransform jt = translation(1.0f, 2.0f, 3.0f); pose.putJointData("spine", jt); assertEquals(jt, pose.orElseEmpty("spine")); } // --- interpolatePose --- /** * interpolatePose a progression 0.0 doit retourner les valeurs de pose1. */ @Test void interpolatePose_progressionZero_returnsFirstPose() { Pose pose1 = new Pose(); pose1.putJointData("arm", translation(0.0f, 0.0f, 0.0f)); Pose pose2 = new Pose(); pose2.putJointData("arm", translation(1.0f, 0.0f, 0.0f)); Pose result = Pose.interpolatePose(pose1, pose2, 0.0f); assertTrue(result.hasTransform("arm")); assertEquals(0.0f, result.get("arm").translation().x, 1e-5f); } /** * interpolatePose a progression 1.0 doit retourner les valeurs de pose2. */ @Test void interpolatePose_progressionOne_returnsSecondPose() { Pose pose1 = new Pose(); pose1.putJointData("arm", translation(0.0f, 0.0f, 0.0f)); Pose pose2 = new Pose(); pose2.putJointData("arm", translation(10.0f, 0.0f, 0.0f)); Pose result = Pose.interpolatePose(pose1, pose2, 1.0f); assertEquals(10.0f, result.get("arm").translation().x, 1e-4f); } /** * interpolatePose fusionne les joints des deux poses. * Un joint present dans pose2 seulement doit apparaitre dans le resultat * (interpole avec JointTransform.empty() comme pose1). */ @Test void interpolatePose_mergesJointsFromBothPoses() { Pose pose1 = new Pose(); pose1.putJointData("head", translation(0.0f, 1.0f, 0.0f)); Pose pose2 = new Pose(); pose2.putJointData("hand", translation(2.0f, 0.0f, 0.0f)); Pose result = Pose.interpolatePose(pose1, pose2, 0.5f); // Les deux joints doivent etre presents assertTrue(result.hasTransform("head"), "head from pose1 doit etre dans le resultat"); assertTrue(result.hasTransform("hand"), "hand from pose2 doit etre dans le resultat"); } /** * interpolatePose a 0.5 doit etre a mi-chemin de la translation. */ @Test void interpolatePose_halfProgression_midpointTranslation() { Pose pose1 = new Pose(); pose1.putJointData("leg", translation(0.0f, 0.0f, 0.0f)); Pose pose2 = new Pose(); pose2.putJointData("leg", translation(4.0f, 0.0f, 0.0f)); Pose result = Pose.interpolatePose(pose1, pose2, 0.5f); assertEquals(2.0f, result.get("leg").translation().x, 1e-4f); } // --- load(LoadOperation) --- /** SET efface les joints existants puis copie depuis la source. */ @Test void load_set_replacesAllJoints() { Pose dest = new Pose(); dest.putJointData("old_joint", JointTransform.empty()); Pose src = new Pose(); src.putJointData("new_joint", translation(1.0f, 0.0f, 0.0f)); dest.load(src, Pose.LoadOperation.SET); assertFalse(dest.hasTransform("old_joint"), "SET doit supprimer les joints de la destination avant fusion"); assertTrue(dest.hasTransform("new_joint"), "SET doit copier les joints source"); } /** OVERWRITE ecrase les joints communs sans supprimer les joints uniquement dans dest. */ @Test void load_overwrite_keepsExistingAndOverwritesCommon() { Pose dest = new Pose(); dest.putJointData("kept", JointTransform.empty()); dest.putJointData("shared", translation(0.0f, 0.0f, 0.0f)); Pose src = new Pose(); src.putJointData("shared", translation(5.0f, 0.0f, 0.0f)); dest.load(src, Pose.LoadOperation.OVERWRITE); assertTrue(dest.hasTransform("kept"), "OVERWRITE doit conserver les joints de dest non presents dans src"); assertEquals(5.0f, dest.get("shared").translation().x, 1e-4f, "OVERWRITE doit ecraser le joint commun avec la valeur src"); } /** APPEND_ABSENT n'ajoute que les joints absents de dest. */ @Test void load_appendAbsent_doesNotOverwriteExisting() { Pose dest = new Pose(); dest.putJointData("existing", translation(9.0f, 0.0f, 0.0f)); Pose src = new Pose(); src.putJointData("existing", translation(1.0f, 0.0f, 0.0f)); src.putJointData("added", translation(2.0f, 0.0f, 0.0f)); dest.load(src, Pose.LoadOperation.APPEND_ABSENT); assertEquals(9.0f, dest.get("existing").translation().x, 1e-4f, "APPEND_ABSENT ne doit pas ecraser un joint existant"); assertTrue(dest.hasTransform("added"), "APPEND_ABSENT doit ajouter les joints absents"); } // --- disableAllJoints --- /** disableAllJoints vide la map de transforms. */ @Test void disableAllJoints_clearsTransforms() { Pose pose = new Pose(); pose.putJointData("head", JointTransform.empty()); pose.putJointData("arm", JointTransform.empty()); pose.disableAllJoints(); assertTrue(pose.getJointTransformData().isEmpty(), "disableAllJoints doit vider la map de transforms"); } }