Files
test/source/blender/animrig/intern/bone_collections_test.cc
luz paz f29382bd7b Cleanup: Fix typos in source/blender/animrig
Fixes user-facing and non-user-facing typos.

Pull Request: https://projects.blender.org/blender/blender/pulls/146098
2025-09-12 07:19:27 +02:00

1624 lines
75 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLT_translation.hh"
#include "BKE_global.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "ANIM_bone_collections.hh"
#include "intern/bone_collections_internal.hh"
#include "testing/testing.h"
namespace blender::animrig::tests {
TEST(ANIM_bone_collections, bonecoll_new_free)
{
BoneCollection *bcoll = ANIM_bonecoll_new("some name");
EXPECT_NE(nullptr, bcoll);
EXPECT_EQ("some name", std::string(bcoll->name));
EXPECT_TRUE(BLI_listbase_is_empty(&bcoll->bones));
EXPECT_EQ(BONE_COLLECTION_VISIBLE | BONE_COLLECTION_SELECTABLE |
BONE_COLLECTION_ANCESTORS_VISIBLE,
bcoll->flags);
ANIM_bonecoll_free(bcoll);
}
TEST(ANIM_bone_collections, bonecoll_default_name)
{
{
BoneCollection *bcoll = ANIM_bonecoll_new("");
EXPECT_EQ(DATA_("Bones"), std::string(bcoll->name));
ANIM_bonecoll_free(bcoll);
}
{
BoneCollection *bcoll = ANIM_bonecoll_new(nullptr);
EXPECT_EQ(DATA_("Bones"), std::string(bcoll->name));
ANIM_bonecoll_free(bcoll);
}
}
class ArmatureBoneCollections : public testing::Test {
protected:
bArmature arm = {};
Bone bone1 = {}, bone2 = {}, bone3 = {};
Main *bmain;
void SetUp() override
{
bmain = BKE_main_new();
G_MAIN = bmain;
STRNCPY(arm.id.name, "ARArmature");
STRNCPY(bone1.name, "bone1");
STRNCPY(bone2.name, "bone2");
STRNCPY(bone3.name, "bone3");
BLI_addtail(&arm.bonebase, &bone1); /* bone1 is root bone. */
BLI_addtail(&arm.bonebase, &bone2); /* bone2 is root bone. */
BLI_addtail(&bone2.childbase, &bone3); /* bone3 has bone2 as parent. */
BKE_armature_bone_hash_make(&arm);
}
void TearDown() override
{
/* Avoid freeing the bones, as they are part of this struct and not owned by
* the armature. */
BLI_listbase_clear(&arm.bonebase);
BKE_idtype_init();
BKE_libblock_free_datablock(&arm.id, 0);
BKE_main_free(bmain);
G_MAIN = nullptr;
}
};
TEST_F(ArmatureBoneCollections, armature_owned_collections)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "collection");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "collection");
EXPECT_EQ(std::string("collection"), std::string(bcoll1->name));
EXPECT_EQ(std::string("collection.001"), std::string(bcoll2->name));
ANIM_armature_bonecoll_remove(&arm, bcoll1);
ANIM_armature_bonecoll_remove(&arm, bcoll2);
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_creation)
{
/* Implicit root: */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "wortel");
/* Explicit root: */
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "wortel", -1);
ASSERT_EQ(0, armature_bonecoll_find_index(&arm, bcoll_root_0));
ASSERT_EQ(1, armature_bonecoll_find_index(&arm, bcoll_root_1));
/* Child of bcoll at index 0: */
BoneCollection *bcoll_child_of_0 = ANIM_armature_bonecoll_new(&arm, "koter", 0);
/* Child of bcoll at index 1: */
BoneCollection *bcoll_child_of_1 = ANIM_armature_bonecoll_new(&arm, "koter", 1);
ASSERT_EQ(4, arm.collection_array_num);
EXPECT_EQ(0, armature_bonecoll_find_index(&arm, bcoll_root_0));
EXPECT_EQ(1, armature_bonecoll_find_index(&arm, bcoll_root_1));
EXPECT_EQ(2, armature_bonecoll_find_index(&arm, bcoll_child_of_0));
EXPECT_EQ(3, armature_bonecoll_find_index(&arm, bcoll_child_of_1));
/* Add another child of bcoll_root_0, which should push bcoll_child_of_1 further down the array.
*/
BoneCollection *bcoll_another_child_of_0 = ANIM_armature_bonecoll_new(&arm, "koter", 0);
ASSERT_EQ(5, arm.collection_array_num);
EXPECT_EQ(0, armature_bonecoll_find_index(&arm, bcoll_root_0));
EXPECT_EQ(1, armature_bonecoll_find_index(&arm, bcoll_root_1));
EXPECT_EQ(2, armature_bonecoll_find_index(&arm, bcoll_child_of_0));
EXPECT_EQ(3, armature_bonecoll_find_index(&arm, bcoll_another_child_of_0));
EXPECT_EQ(4, armature_bonecoll_find_index(&arm, bcoll_child_of_1));
/* Make sure the names remain unique within the entire Armature, and not just between siblings
* (i.e. a unique 'path' is not strong enough). */
EXPECT_EQ(std::string("wortel"), std::string(bcoll_root_0->name));
EXPECT_EQ(std::string("wortel.001"), std::string(bcoll_root_1->name));
EXPECT_EQ(std::string("koter"), std::string(bcoll_child_of_0->name));
EXPECT_EQ(std::string("koter.001"), std::string(bcoll_child_of_1->name));
EXPECT_EQ(std::string("koter.002"), std::string(bcoll_another_child_of_0->name));
/* Test the internal hierarchy bookkeeping. */
EXPECT_EQ(2, arm.collection_root_count);
EXPECT_EQ(2, bcoll_root_0->child_count);
EXPECT_EQ(1, bcoll_root_1->child_count);
EXPECT_EQ(0, bcoll_child_of_0->child_count);
EXPECT_EQ(0, bcoll_another_child_of_0->child_count);
EXPECT_EQ(0, bcoll_child_of_1->child_count);
EXPECT_EQ(2, bcoll_root_0->child_index);
EXPECT_EQ(4, bcoll_root_1->child_index);
EXPECT_EQ(0, bcoll_child_of_0->child_index);
EXPECT_EQ(0, bcoll_another_child_of_0->child_index);
EXPECT_EQ(0, bcoll_child_of_1->child_index);
/* TODO: test with deeper hierarchy. */
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_removal)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(5, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(0, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
/* Remove the middle child of root_0. */
ANIM_armature_bonecoll_remove(&arm, bcoll_r0_child1);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(5, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(4, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
/* Remove the first child of root_0. */
ANIM_armature_bonecoll_remove(&arm, bcoll_r0_child0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(4, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[3]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(3, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(1, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
/* Remove root_1 itself, which should make its only child a new root. */
ANIM_armature_bonecoll_remove(&arm, bcoll_root_1);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(3, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[2]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(1, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_removal__more_complex_remove_inner_child)
{
/* Set up a slightly bigger hierarchy. Contrary to the other tests these are
* actually declared in array order. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
BoneCollection *bcoll_r0c0_child0 = ANIM_armature_bonecoll_new(&arm, "r0c0_child0", 2);
BoneCollection *bcoll_r0c0_child1 = ANIM_armature_bonecoll_new(&arm, "r0c0_child1", 2);
BoneCollection *bcoll_r0c0_child2 = ANIM_armature_bonecoll_new(&arm, "r0c0_child2", 2);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(8, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children of root_0. */
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[5]->name); /* Kids of r0_child0. */
ASSERT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[6]->name);
ASSERT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[7]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(0, arm.collection_array[1]->child_index);
ASSERT_EQ(5, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(0, arm.collection_array[6]->child_index);
ASSERT_EQ(0, arm.collection_array[7]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(0, arm.collection_array[1]->child_count);
ASSERT_EQ(3, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
ASSERT_EQ(0, arm.collection_array[6]->child_count);
ASSERT_EQ(0, arm.collection_array[7]->child_count);
/* Remove bcoll_r0_child0, which should make all of its children a child of root_0. */
ANIM_armature_bonecoll_remove(&arm, bcoll_r0_child0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[2]->name); /* Children of root_0. */
EXPECT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[6]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
EXPECT_EQ(5, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Remove root_0, which should make all of its children new roots. */
ANIM_armature_bonecoll_remove(&arm, bcoll_root_0);
ASSERT_EQ(6, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[5]->name);
EXPECT_EQ(0, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_removal__more_complex_remove_root)
{
/* Set up a slightly bigger hierarchy. Contrary to the other tests these are
* actually declared in array order. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
BoneCollection *bcoll_r0c0_child0 = ANIM_armature_bonecoll_new(&arm, "r0c0_child0", 2);
BoneCollection *bcoll_r0c0_child1 = ANIM_armature_bonecoll_new(&arm, "r0c0_child1", 2);
BoneCollection *bcoll_r0c0_child2 = ANIM_armature_bonecoll_new(&arm, "r0c0_child2", 2);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(8, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children of root_0. */
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[5]->name); /* Kids of r0_child0. */
ASSERT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[6]->name);
ASSERT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[7]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(0, arm.collection_array[1]->child_index);
ASSERT_EQ(5, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(0, arm.collection_array[6]->child_index);
ASSERT_EQ(0, arm.collection_array[7]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(0, arm.collection_array[1]->child_count);
ASSERT_EQ(3, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
ASSERT_EQ(0, arm.collection_array[6]->child_count);
ASSERT_EQ(0, arm.collection_array[7]->child_count);
/* Remove root_0, which should make all of its children new roots. */
ANIM_armature_bonecoll_remove(&arm, bcoll_root_0);
ASSERT_EQ(4, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[4]->name); /* Kids of r0_child0. */
ASSERT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[5]->name);
ASSERT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[6]->name);
EXPECT_EQ(4, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
}
TEST_F(ArmatureBoneCollections, find_parent_index)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0c0_child0 = ANIM_armature_bonecoll_new(&arm, "r0c0_child0", 2);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(4, arm.collection_array[1]->child_index);
ASSERT_EQ(5, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(2, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(1, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, -1));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 500000));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 0));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 1));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 2));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 3));
EXPECT_EQ(1, armature_bonecoll_find_parent_index(&arm, 4));
EXPECT_EQ(2, armature_bonecoll_find_parent_index(&arm, 5));
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_visibility)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root0 = ANIM_armature_bonecoll_new(&arm, "root0");
BoneCollection *bcoll_root1 = ANIM_armature_bonecoll_new(&arm, "root1");
const int root0_index = armature_bonecoll_find_index(&arm, bcoll_root0);
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", root0_index);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", root0_index);
const int child0_index = armature_bonecoll_find_index(&arm, bcoll_r0_child0);
BoneCollection *bcoll_c0_child0 = ANIM_armature_bonecoll_new(&arm, "c0_child0", child0_index);
/* Initially, all bone collections should be marked as visible. */
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
/* Initially, all bone collections should have visible ancestors. */
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
/* Mark root_0 as invisible, this should also update its children. */
ANIM_bonecoll_hide(&arm, bcoll_root0);
EXPECT_FALSE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_FALSE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_FALSE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_FALSE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
/* Move r0_child0 to root1, that should change its BONE_COLLECTION_ANCESTORS_VISIBLE */
const int root1_index = armature_bonecoll_find_index(&arm, bcoll_root1);
armature_bonecoll_move_to_parent(&arm, child0_index, 0, root0_index, root1_index);
EXPECT_FALSE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
<< "The child that was moved to a visible root should be affected";
EXPECT_FALSE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
<< "The child that wasn't moved should not be affected.";
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
<< "The grandchild that was indirectly moved to a visible root should be affected";
/* Add a new child to root0, it should have the right flags. */
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", root0_index);
EXPECT_TRUE(bcoll_r0_child2->flags & BONE_COLLECTION_VISIBLE);
EXPECT_FALSE(bcoll_r0_child2->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
}
TEST_F(ArmatureBoneCollections, bones_assign_unassign)
{
BoneCollection *bcoll = ANIM_armature_bonecoll_new(&arm, "collection");
ANIM_armature_bonecoll_assign(bcoll, &bone1);
ANIM_armature_bonecoll_assign(bcoll, &bone2);
ASSERT_EQ(2, BLI_listbase_count(&bcoll->bones)) << "expecting two bones in collection";
EXPECT_EQ(&bone1, static_cast<BoneCollectionMember *>(BLI_findlink(&bcoll->bones, 0))->bone);
EXPECT_EQ(&bone2, static_cast<BoneCollectionMember *>(BLI_findlink(&bcoll->bones, 1))->bone);
EXPECT_EQ(bcoll, static_cast<BoneCollectionReference *>(bone1.runtime.collections.first)->bcoll)
<< "expecting back-reference to collection in bone1 runtime data";
EXPECT_EQ(bcoll, static_cast<BoneCollectionReference *>(bone2.runtime.collections.first)->bcoll)
<< "expecting back-reference to collection in bone2 runtime data";
ANIM_armature_bonecoll_unassign(bcoll, &bone1);
ANIM_armature_bonecoll_unassign(bcoll, &bone2);
EXPECT_EQ(0, BLI_listbase_count(&bone1.runtime.collections))
<< "expecting back-references in bone1 runtime data to be cleared when unassigned";
EXPECT_EQ(0, BLI_listbase_count(&bone2.runtime.collections))
<< "expecting back-references in bone2 runtime data to be cleared when unassigned";
ANIM_armature_bonecoll_remove(&arm, bcoll);
}
TEST_F(ArmatureBoneCollections, bones_assign_remove)
{
BoneCollection *bcoll = ANIM_armature_bonecoll_new(&arm, "collection");
ANIM_armature_bonecoll_assign(bcoll, &bone1);
ANIM_armature_bonecoll_assign(bcoll, &bone2);
ANIM_armature_bonecoll_remove(&arm, bcoll);
EXPECT_EQ(0, BLI_listbase_count(&bone1.runtime.collections))
<< "expecting back-references in bone1 runtime data to be cleared when the collection is "
"removed";
EXPECT_EQ(0, BLI_listbase_count(&bone2.runtime.collections))
<< "expecting back-references in bone2 runtime data to be cleared when the collection is "
"removed";
}
TEST_F(ArmatureBoneCollections, active_set_clear_by_pointer)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "Bones 1");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "Bones 2");
BoneCollection *bcoll3 = ANIM_bonecoll_new("Alien Bones");
ANIM_armature_bonecoll_active_set(&arm, bcoll1);
EXPECT_EQ(0, arm.runtime.active_collection_index);
EXPECT_EQ(bcoll1, arm.runtime.active_collection);
EXPECT_STREQ(bcoll1->name, arm.active_collection_name);
ANIM_armature_bonecoll_active_set(&arm, nullptr);
EXPECT_EQ(-1, arm.runtime.active_collection_index);
EXPECT_EQ(nullptr, arm.runtime.active_collection);
EXPECT_STREQ("", arm.active_collection_name);
ANIM_armature_bonecoll_active_set(&arm, bcoll2);
EXPECT_EQ(1, arm.runtime.active_collection_index);
EXPECT_EQ(bcoll2, arm.runtime.active_collection);
EXPECT_STREQ(bcoll2->name, arm.active_collection_name);
ANIM_armature_bonecoll_active_set(&arm, bcoll3);
EXPECT_EQ(-1, arm.runtime.active_collection_index);
EXPECT_EQ(nullptr, arm.runtime.active_collection);
EXPECT_STREQ("", arm.active_collection_name);
ANIM_bonecoll_free(bcoll3);
}
TEST_F(ArmatureBoneCollections, active_set_clear_by_index)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "Bones 1");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "Bones 2");
ANIM_armature_bonecoll_active_index_set(&arm, 0);
EXPECT_EQ(0, arm.runtime.active_collection_index);
EXPECT_EQ(bcoll1, arm.runtime.active_collection);
EXPECT_STREQ(bcoll1->name, arm.active_collection_name);
ANIM_armature_bonecoll_active_index_set(&arm, -1);
EXPECT_EQ(-1, arm.runtime.active_collection_index);
EXPECT_EQ(nullptr, arm.runtime.active_collection);
EXPECT_STREQ("", arm.active_collection_name);
ANIM_armature_bonecoll_active_index_set(&arm, 1);
EXPECT_EQ(1, arm.runtime.active_collection_index);
EXPECT_EQ(bcoll2, arm.runtime.active_collection);
EXPECT_STREQ(bcoll2->name, arm.active_collection_name);
ANIM_armature_bonecoll_active_index_set(&arm, 47);
EXPECT_EQ(-1, arm.runtime.active_collection_index);
EXPECT_EQ(nullptr, arm.runtime.active_collection);
EXPECT_STREQ("", arm.active_collection_name);
}
TEST_F(ArmatureBoneCollections, bcoll_is_editable)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "Bones 1");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "Bones 2");
EXPECT_EQ(0, bcoll1->flags & BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL);
EXPECT_EQ(0, bcoll2->flags & BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL);
EXPECT_TRUE(ANIM_armature_bonecoll_is_editable(&arm, bcoll1))
<< "Expecting local armature to be editable";
/* Fake that the armature is linked from another blend file. */
Library fake_lib = {};
arm.id.lib = &fake_lib;
EXPECT_FALSE(ANIM_armature_bonecoll_is_editable(&arm, bcoll1))
<< "Expecting local armature to not be editable";
/* Fake that the armature is an override, but linked from another blend file. */
IDOverrideLibrary fake_override = {};
bArmature fake_reference = {};
fake_override.reference = &fake_reference.id;
arm.id.override_library = &fake_override;
EXPECT_FALSE(ANIM_armature_bonecoll_is_editable(&arm, bcoll1))
<< "Expecting linked armature override to not be editable";
/* Fake that the armature is a local override. */
arm.id.lib = nullptr;
bcoll2->flags |= BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL;
EXPECT_FALSE(ANIM_armature_bonecoll_is_editable(&arm, bcoll1))
<< "Expecting linked bone collection on local armature override to not be editable";
EXPECT_TRUE(ANIM_armature_bonecoll_is_editable(&arm, bcoll2))
<< "Expecting local bone collection on local armature override to be editable";
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_index__roots)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "collection");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "collection");
BoneCollection *bcoll3 = ANIM_armature_bonecoll_new(&arm, "collection");
BoneCollection *bcoll4 = ANIM_armature_bonecoll_new(&arm, "collection");
EXPECT_EQ(arm.collection_array[0], bcoll1);
EXPECT_EQ(arm.collection_array[1], bcoll2);
EXPECT_EQ(arm.collection_array[2], bcoll3);
EXPECT_EQ(arm.collection_array[3], bcoll4);
EXPECT_TRUE(ANIM_armature_bonecoll_move_to_index(&arm, 2, 1));
EXPECT_EQ(arm.collection_array[0], bcoll1);
EXPECT_EQ(arm.collection_array[1], bcoll3);
EXPECT_EQ(arm.collection_array[2], bcoll2);
EXPECT_EQ(arm.collection_array[3], bcoll4);
EXPECT_TRUE(ANIM_armature_bonecoll_move_to_index(&arm, 0, 3));
EXPECT_EQ(arm.collection_array[0], bcoll3);
EXPECT_EQ(arm.collection_array[1], bcoll2);
EXPECT_EQ(arm.collection_array[2], bcoll4);
EXPECT_EQ(arm.collection_array[3], bcoll1);
/* Out of bounds should not be accepted. */
EXPECT_FALSE(ANIM_armature_bonecoll_move_to_index(&arm, 0, 327));
EXPECT_EQ(arm.collection_array[0], bcoll3);
EXPECT_EQ(arm.collection_array[1], bcoll2);
EXPECT_EQ(arm.collection_array[2], bcoll4);
EXPECT_EQ(arm.collection_array[3], bcoll1);
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_index__siblings)
{
BoneCollection *root = ANIM_armature_bonecoll_new(&arm, "root");
BoneCollection *child0 = ANIM_armature_bonecoll_new(&arm, "child0", 0);
BoneCollection *child1 = ANIM_armature_bonecoll_new(&arm, "child1", 0);
BoneCollection *child2 = ANIM_armature_bonecoll_new(&arm, "child2", 0);
BoneCollection *child1_0 = ANIM_armature_bonecoll_new(&arm, "child1_0", 2);
ASSERT_STREQ(root->name, arm.collection_array[0]->name);
ASSERT_STREQ(child0->name, arm.collection_array[1]->name);
ASSERT_STREQ(child1->name, arm.collection_array[2]->name);
ASSERT_STREQ(child2->name, arm.collection_array[3]->name);
ASSERT_STREQ(child1_0->name, arm.collection_array[4]->name);
/* Move child2 to child0, i.e. a move 'to the left'. */
EXPECT_TRUE(ANIM_armature_bonecoll_move_to_index(&arm, 3, 1));
EXPECT_STREQ(root->name, arm.collection_array[0]->name);
EXPECT_STREQ(child2->name, arm.collection_array[1]->name);
EXPECT_STREQ(child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(child1_0->name, arm.collection_array[4]->name);
/* Move child2 to child1, i.e. a move 'to the right'. */
EXPECT_TRUE(ANIM_armature_bonecoll_move_to_index(&arm, 1, 3));
EXPECT_STREQ(root->name, arm.collection_array[0]->name);
EXPECT_STREQ(child0->name, arm.collection_array[1]->name);
EXPECT_STREQ(child1->name, arm.collection_array[2]->name);
EXPECT_STREQ(child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(child1_0->name, arm.collection_array[4]->name);
/* Move child2 to root, should not be allowed. */
EXPECT_FALSE(ANIM_armature_bonecoll_move_to_index(&arm, 3, 0));
EXPECT_STREQ(root->name, arm.collection_array[0]->name);
EXPECT_STREQ(child0->name, arm.collection_array[1]->name);
EXPECT_STREQ(child1->name, arm.collection_array[2]->name);
EXPECT_STREQ(child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(child1_0->name, arm.collection_array[4]->name);
/* Move child1_0 to child_2, should not be allowed. */
EXPECT_FALSE(ANIM_armature_bonecoll_move_to_index(&arm, 4, 3));
EXPECT_STREQ(root->name, arm.collection_array[0]->name);
EXPECT_STREQ(child0->name, arm.collection_array[1]->name);
EXPECT_STREQ(child1->name, arm.collection_array[2]->name);
EXPECT_STREQ(child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(child1_0->name, arm.collection_array[4]->name);
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_parent)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(5, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(0, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
/* Move the middle child of root_0 to root_1. */
EXPECT_EQ(5, armature_bonecoll_move_to_parent(&arm, 3, bcoll_root_1->child_count, 0, 1));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(4, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(2, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* Move the first child of root_1 to root_0. This shouldn't change its index. */
EXPECT_EQ(4, armature_bonecoll_move_to_parent(&arm, 4, bcoll_root_0->child_count, 1, 0));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(5, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* Move the final child of root_1 to root_0. This shouldn't change its index
* again, but leave root_1 without children. */
EXPECT_EQ(5, armature_bonecoll_move_to_parent(&arm, 5, bcoll_root_0->child_count, 1, 0));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(4, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* Move the first child of root_0 (bcoll_r0_child0) to bcoll_r0_child2. */
EXPECT_EQ(5, armature_bonecoll_move_to_parent(&arm, 2, bcoll_r0_child2->child_count, 0, 3));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(5, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(1, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_parent__root_unroot)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(5, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(0, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
/* Make a leaf node (bcoll_r0_child1) a root. */
EXPECT_EQ(2, armature_bonecoll_move_to_parent(&arm, 3, arm.collection_root_count, 0, -1));
ASSERT_EQ(3, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[2]->name); /* Became a root. */
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(3, arm.collection_array[0]->child_index);
EXPECT_EQ(5, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* Make a root node (root_1) a child of root_0. */
EXPECT_EQ(4, armature_bonecoll_move_to_parent(&arm, 1, bcoll_root_0->child_count, -1, 0));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[1]->name); /* Actually a root. */
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[4]->name); /* Became a child. */
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(5, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(1, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* TODO: test with circular parenthood. */
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_parent__within_siblings)
{
/* Set up a small hierarchy. */
auto *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
auto *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
auto *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
auto *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
auto *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
auto *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
auto *bcoll_r0_child3 = ANIM_armature_bonecoll_new(&arm, "r0_child3", 0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[2]->name); /* Children root_1. */
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[3]->name); /* Children root_0. */
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[5]->name);
ASSERT_STREQ(bcoll_r0_child3->name, arm.collection_array[6]->name);
ASSERT_EQ(3, arm.collection_array[0]->child_index);
ASSERT_EQ(2, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(0, arm.collection_array[6]->child_index);
ASSERT_EQ(4, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(0, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
ASSERT_EQ(0, arm.collection_array[6]->child_count);
/* First half of the test, move 3 children from root_1 to root_0. */
/* Move r0_child0 to become 1st child of root_1, before r1_child0. */
EXPECT_EQ(2,
armature_bonecoll_move_to_parent(&arm,
3, /* From index. */
0, /* To child number. */
0, /* From parent. */
1 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[4]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[6]->name);
EXPECT_EQ(4, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(2, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Move r0_child1 to become the 2nd child of root_1. */
EXPECT_EQ(3,
armature_bonecoll_move_to_parent(&arm,
4, /* From index. */
1, /* To child number. */
0, /* From parent. */
1 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[5]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[6]->name);
EXPECT_EQ(5, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(3, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Move r0_child3 to become the last child of root_1. */
EXPECT_EQ(5,
armature_bonecoll_move_to_parent(&arm,
6, /* From index. */
3, /* To child number. */
0, /* From parent. */
1 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[6]->name); /* Children root_0. */
EXPECT_EQ(6, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(1, arm.collection_array[0]->child_count);
EXPECT_EQ(4, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* 2nd half of the test: move the children back to root_0 to test moving in
* the other direction. */
/* Move r0_child3 to become the first child of root_0. */
EXPECT_EQ(5,
armature_bonecoll_move_to_parent(&arm,
5, /* From index. */
0, /* To child number. */
1, /* From parent. */
0 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[5]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[6]->name);
EXPECT_EQ(5, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(3, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Move r0_child0 to become the last child of root_0. */
EXPECT_EQ(6,
armature_bonecoll_move_to_parent(&arm,
2, /* From index. */
2, /* To child number. */
1, /* From parent. */
0 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[4]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[6]->name);
EXPECT_EQ(4, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(2, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Move r0_child1 to become the 3rd child of root_0. */
EXPECT_EQ(5,
armature_bonecoll_move_to_parent(&arm,
2, /* From index. */
2, /* To child number. */
1, /* From parent. */
0 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[3]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[6]->name);
EXPECT_EQ(3, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(4, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
}
TEST_F(ArmatureBoneCollections, internal__bonecolls_rotate_block)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
/* The tests below compare the collection names, instead of their pointers, so
* that we get human-readable messages on failure. */
/* Unnecessary assertions, just to make it easier to understand in which order
* the array starts out. */
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(5, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
/* Move [0,1,2] to [1,2,3]. */
internal::bonecolls_rotate_block(&arm, 0, 3, 1);
ASSERT_EQ(6, arm.collection_array_num) << "array size should not change";
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(0, arm.collection_array[0]->child_index);
EXPECT_EQ(3, arm.collection_array[1]->child_index);
EXPECT_EQ(5, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
/* Move [4,5] to [3,4]. */
internal::bonecolls_rotate_block(&arm, 4, 2, -1);
ASSERT_EQ(6, arm.collection_array_num) << "array size should not change";
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(0, arm.collection_array[0]->child_index);
EXPECT_EQ(3, arm.collection_array[1]->child_index);
EXPECT_EQ(4, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
}
class ArmatureBoneCollectionsTestList : public testing::Test {
protected:
bArmature arm = {};
BoneCollection *root = nullptr;
BoneCollection *child0 = nullptr;
BoneCollection *child1 = nullptr;
BoneCollection *child2 = nullptr;
BoneCollection *child1_0 = nullptr;
void SetUp() override
{
STRNCPY(arm.id.name, "ARArmature");
root = ANIM_armature_bonecoll_new(&arm, "root");
child0 = ANIM_armature_bonecoll_new(&arm, "child0", 0);
child1 = ANIM_armature_bonecoll_new(&arm, "child1", 0);
child2 = ANIM_armature_bonecoll_new(&arm, "child2", 0);
child1_0 = ANIM_armature_bonecoll_new(&arm, "child1_0", 2);
ASSERT_STREQ(root->name, arm.collection_array[0]->name);
ASSERT_STREQ(child0->name, arm.collection_array[1]->name);
ASSERT_STREQ(child1->name, arm.collection_array[2]->name);
ASSERT_STREQ(child2->name, arm.collection_array[3]->name);
ASSERT_STREQ(child1_0->name, arm.collection_array[4]->name);
}
void TearDown() override
{
BKE_idtype_init();
BKE_libblock_free_datablock(&arm.id, 0);
}
testing::AssertionResult expect_bcolls(std::vector<std::string> expect_names)
{
std::vector<std::string> actual_names;
for (const BoneCollection *bcoll : arm.collections_span()) {
actual_names.emplace_back(bcoll->name);
}
if (expect_names == actual_names) {
return testing::AssertionSuccess();
}
testing::AssertionResult failure = testing::AssertionFailure();
failure << "Expected bone collections differ from actual ones:" << std::endl;
/* This is what you get when C++ doesn't even have a standard library
* function to do something like `expect_names.join(", ")`. */
failure << "Expected collections: [";
for (int i = 0; i < expect_names.size() - 1; i++) {
failure << expect_names[i] << ", ";
}
failure << expect_names.back() << "]" << std::endl;
failure << "Actual collections : [";
for (int i = 0; i < actual_names.size() - 1; i++) {
failure << actual_names[i] << ", ";
}
failure << actual_names.back() << "]" << std::endl;
internal::bonecolls_debug_list(&arm);
return failure;
}
};
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__before_first_sibling)
{
/* Set the active index to be one of the affected bone collections. */
ANIM_armature_bonecoll_active_name_set(&arm, "child2");
ASSERT_EQ(3, arm.runtime.active_collection_index);
EXPECT_EQ(1, ANIM_armature_bonecoll_move_before_after_index(&arm, 3, 1, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"root", "child2", "child0", "child1", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 1));
/* The three indicators of the active collection should still be in sync. */
EXPECT_EQ(1, arm.runtime.active_collection_index);
EXPECT_EQ(child2, arm.runtime.active_collection);
EXPECT_STREQ("child2", arm.active_collection_name);
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__after_first_sibling)
{
EXPECT_EQ(2, ANIM_armature_bonecoll_move_before_after_index(&arm, 3, 1, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child2", "child1", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 2));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__before_last_sibling)
{
/* Set the active index to be one of the affected bone collections. */
ANIM_armature_bonecoll_active_name_set(&arm, "child1");
ASSERT_EQ(2, arm.runtime.active_collection_index);
EXPECT_EQ(2, ANIM_armature_bonecoll_move_before_after_index(&arm, 1, 3, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child0", "child2", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 2));
/* The three indicators of the active collection should still be in sync. */
EXPECT_EQ(1, arm.runtime.active_collection_index);
EXPECT_EQ(child1, arm.runtime.active_collection);
EXPECT_STREQ("child1", arm.active_collection_name);
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__after_last_sibling)
{
EXPECT_EQ(3, ANIM_armature_bonecoll_move_before_after_index(&arm, 1, 3, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child0", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 3));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__other_parent_before__move_left)
{
EXPECT_EQ(1, ANIM_armature_bonecoll_move_before_after_index(&arm, 4, 1, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"root", "child1_0", "child0", "child1", "child2"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 1));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__other_parent_after__move_left)
{
EXPECT_EQ(2, ANIM_armature_bonecoll_move_before_after_index(&arm, 4, 1, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1_0", "child1", "child2"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 2));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__other_parent_before__move_right)
{
EXPECT_EQ(3, ANIM_armature_bonecoll_move_before_after_index(&arm, 1, 4, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child0", "child1_0"}));
EXPECT_EQ(1, armature_bonecoll_find_parent_index(&arm, 3));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__other_parent_after__move_right)
{
EXPECT_EQ(4, ANIM_armature_bonecoll_move_before_after_index(&arm, 1, 4, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child1_0", "child0"}));
EXPECT_EQ(1, armature_bonecoll_find_parent_index(&arm, 4));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__to_root__before)
{
EXPECT_EQ(0, ANIM_armature_bonecoll_move_before_after_index(&arm, 4, 0, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"child1_0", "root", "child0", "child1", "child2"}));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 0));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__to_root__after)
{
EXPECT_EQ(1, ANIM_armature_bonecoll_move_before_after_index(&arm, 4, 0, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child1_0", "child0", "child1", "child2"}));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 1));
}
TEST_F(ArmatureBoneCollectionsTestList, child_number_set__roots)
{
/* Test with only one root. */
EXPECT_EQ(0, armature_bonecoll_child_number_set(&arm, root, 0));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Move to "after the last child", which is the one root itself. */
EXPECT_EQ(0, armature_bonecoll_child_number_set(&arm, root, -1));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_child_number_set(&arm, root, 0));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Going beyond the number of children is not allowed. */
EXPECT_EQ(-1, armature_bonecoll_child_number_set(&arm, root, 1));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Add two roots to be able to play. */
ANIM_armature_bonecoll_new(&arm, "root1");
ANIM_armature_bonecoll_new(&arm, "root2");
EXPECT_TRUE(expect_bcolls({"root", "root1", "root2", "child0", "child1", "child2", "child1_0"}));
/* Move the old root in between the two new ones. */
EXPECT_EQ(1, armature_bonecoll_child_number_set(&arm, root, 1));
EXPECT_TRUE(expect_bcolls({"root1", "root", "root2", "child0", "child1", "child2", "child1_0"}));
/* And to the last one. */
EXPECT_EQ(2, armature_bonecoll_child_number_set(&arm, root, 2));
EXPECT_TRUE(expect_bcolls({"root1", "root2", "root", "child0", "child1", "child2", "child1_0"}));
}
TEST_F(ArmatureBoneCollectionsTestList, child_number_set__siblings)
{
/* Move child0 to itself. */
EXPECT_EQ(1, armature_bonecoll_child_number_set(&arm, child0, 0));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Move child2 to itself. */
EXPECT_EQ(3, armature_bonecoll_child_number_set(&arm, child2, -1));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Going beyond the number of children is not allowed. */
EXPECT_EQ(-1, armature_bonecoll_child_number_set(&arm, child0, 3));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Move child0 to in between child1 and child2. */
EXPECT_EQ(2, armature_bonecoll_child_number_set(&arm, child0, 1));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child0", "child2", "child1_0"}));
/* Move child0 to the last spot. */
EXPECT_EQ(3, armature_bonecoll_child_number_set(&arm, child0, 2));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child0", "child1_0"}));
}
TEST_F(ArmatureBoneCollectionsTestList, bone_collection_solo)
{
EXPECT_FALSE(arm.flag & ARM_BCOLL_SOLO_ACTIVE) << "By default no solo'ing should be active";
/* Enable solo. */
EXPECT_FALSE(child1->flags & BONE_COLLECTION_SOLO);
ANIM_armature_bonecoll_solo_set(&arm, child1, true);
EXPECT_TRUE(child1->flags & BONE_COLLECTION_SOLO);
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
/* Enable solo on another bone collection. */
EXPECT_FALSE(child1_0->flags & BONE_COLLECTION_SOLO);
ANIM_armature_bonecoll_solo_set(&arm, child1_0, true);
EXPECT_TRUE(child1_0->flags & BONE_COLLECTION_SOLO);
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
/* Disable the first solo flag. */
EXPECT_TRUE(child1->flags & BONE_COLLECTION_SOLO);
ANIM_armature_bonecoll_solo_set(&arm, child1, false);
EXPECT_FALSE(child1->flags & BONE_COLLECTION_SOLO);
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
/* Disable the second solo flag. This should also disable the ARM_BCOLL_SOLO_ACTIVE flag. */
EXPECT_TRUE(child1_0->flags & BONE_COLLECTION_SOLO);
ANIM_armature_bonecoll_solo_set(&arm, child1_0, false);
EXPECT_FALSE(child1_0->flags & BONE_COLLECTION_SOLO);
EXPECT_FALSE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
}
class ArmatureBoneCollectionsLiboverrides : public ArmatureBoneCollectionsTestList {
protected:
bArmature dst_arm = {};
BoneCollection *dst_root = nullptr;
BoneCollection *dst_child0 = nullptr;
BoneCollection *dst_child1 = nullptr;
BoneCollection *dst_child2 = nullptr;
BoneCollection *dst_child1_0 = nullptr;
void SetUp() override
{
ArmatureBoneCollectionsTestList::SetUp();
/* TODO: make this clone `arm` into `dst_arm`, instead of assuming the below
* code is still in sync with the super-class. */
STRNCPY(dst_arm.id.name, "ARArmatureDST");
dst_root = ANIM_armature_bonecoll_new(&dst_arm, "root");
dst_child0 = ANIM_armature_bonecoll_new(&dst_arm, "child0", 0);
dst_child1 = ANIM_armature_bonecoll_new(&dst_arm, "child1", 0);
dst_child2 = ANIM_armature_bonecoll_new(&dst_arm, "child2", 0);
dst_child1_0 = ANIM_armature_bonecoll_new(&dst_arm, "child1_0", 2);
ASSERT_STREQ(dst_root->name, dst_arm.collection_array[0]->name);
ASSERT_STREQ(dst_child0->name, dst_arm.collection_array[1]->name);
ASSERT_STREQ(dst_child1->name, dst_arm.collection_array[2]->name);
ASSERT_STREQ(dst_child2->name, dst_arm.collection_array[3]->name);
ASSERT_STREQ(dst_child1_0->name, dst_arm.collection_array[4]->name);
BKE_armature_bone_hash_make(&arm);
BKE_armature_bone_hash_make(&dst_arm);
}
void TearDown() override
{
ArmatureBoneCollectionsTestList::TearDown();
BKE_libblock_free_datablock(&dst_arm.id, 0);
}
};
TEST_F(ArmatureBoneCollectionsLiboverrides, bcoll_insert_copy_after)
{
/* Mimic that a new root, two children, and two grandchildren were added via library overrides.
* These were saved in `arm`, and now need to be copied into `dst_arm`. */
BoneCollection *src_root = ANIM_armature_bonecoll_new(&arm, "new_root");
const int root_index = armature_bonecoll_find_index(&arm, src_root);
BoneCollection *src_child1 = ANIM_armature_bonecoll_new(&arm, "new_child1", root_index);
ANIM_armature_bonecoll_new(&arm, "new_child2", root_index);
const int child1_index = armature_bonecoll_find_index(&arm, src_child1);
ANIM_armature_bonecoll_new(&arm, "new_gchild1", child1_index);
ANIM_armature_bonecoll_new(&arm, "new_gchild2", child1_index);
/* Copy the root. This should be the only change that's recorded by a library override operation.
* It should also copy the entire subtree of that root. */
const BoneCollection *anchor = dst_arm.collection_array[0];
ASSERT_STREQ("root", anchor->name);
BoneCollection *copy_root = ANIM_armature_bonecoll_insert_copy_after(
&dst_arm, &arm, anchor, src_root);
/* Check the array order. */
EXPECT_TRUE(expect_bcolls({"root",
"new_root",
"child0",
"child1",
"child2",
"child1_0",
"new_child1",
"new_child2",
"new_gchild1",
"new_gchild2"}));
/* Check that the copied root is actually stored in the destination armature array. */
const int new_root_index = armature_bonecoll_find_index(&dst_arm, copy_root);
EXPECT_EQ(1, new_root_index);
/* Check the hierarchy. */
const int new_child1_index = ANIM_armature_bonecoll_get_index_by_name(&dst_arm, "new_child1");
EXPECT_TRUE(armature_bonecoll_is_root(&dst_arm, new_root_index));
EXPECT_TRUE(armature_bonecoll_is_child_of(&dst_arm, new_root_index, new_child1_index));
EXPECT_TRUE(armature_bonecoll_is_child_of(
&dst_arm, new_root_index, ANIM_armature_bonecoll_get_index_by_name(&dst_arm, "new_child2")));
EXPECT_TRUE(armature_bonecoll_is_child_of(
&dst_arm,
new_child1_index,
ANIM_armature_bonecoll_get_index_by_name(&dst_arm, "new_gchild1")));
EXPECT_TRUE(armature_bonecoll_is_child_of(
&dst_arm,
new_child1_index,
ANIM_armature_bonecoll_get_index_by_name(&dst_arm, "new_gchild2")));
if (HasFailure()) {
internal::bonecolls_debug_list(&dst_arm);
}
}
} // namespace blender::animrig::tests