Extra2D/Tests/test_node.cpp

413 lines
11 KiB
C++
Raw Normal View History

/**
* @file test_node.cpp
* @brief Node Transform
*
*
*/
#include "test_framework.h"
#include <extra2d/node/node.h>
#include <extra2d/node/scene_graph.h>
#include <extra2d/core/math_extended.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <cmath>
using namespace extra2d;
using namespace extra2d::test;
namespace {
constexpr f32 EPSILON = 0.0001f;
bool mat3Equal(const Mat3& a, const Mat3& b, f32 eps = EPSILON) {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (std::abs(a[i][j] - b[i][j]) > eps) {
return false;
}
}
}
return true;
}
bool vec2Equal(const Vec2& a, const Vec2& b, f32 eps = EPSILON) {
return std::abs(a.x - b.x) < eps && std::abs(a.y - b.y) < eps;
}
}
// ---------------------------------------------------------------------------
// Node 基本测试
// ---------------------------------------------------------------------------
TEST(Node, Create) {
auto node = ptr::make<Node>();
TEST_ASSERT_NOT_NULL(node.get());
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(0, 0)));
TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(1, 1)));
TEST_ASSERT_TRUE(std::abs(node->rot) < EPSILON);
TEST_ASSERT_TRUE(vec2Equal(node->anchor, Vec2(0.5f, 0.5f)));
}
TEST(Node, SetPosition) {
auto node = ptr::make<Node>();
node->setPos(100, 200);
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 200)));
node->setPos(Vec2(50, 75));
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 75)));
}
TEST(Node, SetScale) {
auto node = ptr::make<Node>();
node->setScale(2.0f, 3.0f);
TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(2, 3)));
node->setScale(Vec2(1.5f, 2.5f));
TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(1.5f, 2.5f)));
node->setScale(2.0f);
TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(2, 2)));
}
TEST(Node, SetRotation) {
auto node = ptr::make<Node>();
node->setRot(45.0f);
TEST_ASSERT_TRUE(std::abs(node->rot - 45.0f) < EPSILON);
node->setRot(-30.0f);
TEST_ASSERT_TRUE(std::abs(node->rot - (-30.0f)) < EPSILON);
}
TEST(Node, SetAnchor) {
auto node = ptr::make<Node>();
node->setAnchor(0.0f, 0.0f);
TEST_ASSERT_TRUE(vec2Equal(node->anchor, Vec2(0, 0)));
node->setAnchor(Anchor::TopRight);
TEST_ASSERT_TRUE(vec2Equal(node->anchor, Vec2(1, 1)));
node->setAnchor(Anchor::Center);
TEST_ASSERT_TRUE(vec2Equal(node->anchor, Vec2(0.5f, 0.5f)));
}
// ---------------------------------------------------------------------------
// Transform 矩阵测试
// ---------------------------------------------------------------------------
TEST(Node, LocalMatrixIdentity) {
auto node = ptr::make<Node>();
Mat3 local = node->local();
Mat3 identity(1.0f);
TEST_ASSERT_TRUE(mat3Equal(local, identity));
}
TEST(Node, LocalMatrixTranslation) {
auto node = ptr::make<Node>();
node->setPos(100, 50);
Mat3 local = node->local();
glm::vec3 origin(0, 0, 1.0f);
glm::vec3 transformed = local * origin;
TEST_ASSERT_TRUE(std::abs(transformed.x - 100) < EPSILON);
TEST_ASSERT_TRUE(std::abs(transformed.y - 50) < EPSILON);
}
TEST(Node, LocalMatrixScale) {
auto node = ptr::make<Node>();
node->setScale(2.0f, 3.0f);
Mat3 local = node->local();
glm::vec3 point(10, 10, 1.0f);
glm::vec3 transformed = local * point;
TEST_ASSERT_TRUE(std::abs(transformed.x - 20) < EPSILON);
TEST_ASSERT_TRUE(std::abs(transformed.y - 30) < EPSILON);
}
TEST(Node, LocalMatrixRotation) {
auto node = ptr::make<Node>();
node->setRot(90.0f);
Mat3 local = node->local();
glm::vec3 point(1, 0, 1.0f);
glm::vec3 transformed = local * point;
TEST_ASSERT_TRUE(std::abs(transformed.x - 0.0f) < EPSILON);
TEST_ASSERT_TRUE(std::abs(transformed.y - 1.0f) < EPSILON);
}
TEST(Node, LocalMatrixCombined) {
auto node = ptr::make<Node>();
node->setPos(100, 100);
node->setScale(2.0f, 2.0f);
node->setRot(0.0f);
Mat3 local = node->local();
glm::vec3 point(50, 50, 1.0f);
glm::vec3 transformed = local * point;
TEST_ASSERT_TRUE(std::abs(transformed.x - 200) < EPSILON);
TEST_ASSERT_TRUE(std::abs(transformed.y - 200) < EPSILON);
}
// ---------------------------------------------------------------------------
// 父子关系测试
// ---------------------------------------------------------------------------
TEST(Node, AddChild) {
auto parent = ptr::make<Node>();
auto child = ptr::make<Node>();
parent->addChild(child);
TEST_ASSERT_EQ(parent.get(), child->parent());
TEST_ASSERT_EQ(1u, parent->children().size());
TEST_ASSERT_EQ(child.get(), parent->children()[0].get());
}
TEST(Node, RemoveChild) {
auto parent = ptr::make<Node>();
auto child = ptr::make<Node>();
parent->addChild(child);
parent->removeChild(child);
TEST_ASSERT_NULL(child->parent());
TEST_ASSERT_EQ(0u, parent->children().size());
}
TEST(Node, RemoveFromParent) {
auto parent = ptr::make<Node>();
auto child = ptr::make<Node>();
parent->addChild(child);
child->removeFromParent();
TEST_ASSERT_NULL(child->parent());
TEST_ASSERT_EQ(0u, parent->children().size());
}
TEST(Node, NestedHierarchy) {
auto root = ptr::make<Node>();
auto child1 = ptr::make<Node>();
auto child2 = ptr::make<Node>();
auto grandchild = ptr::make<Node>();
root->addChild(child1);
root->addChild(child2);
child1->addChild(grandchild);
TEST_ASSERT_EQ(root.get(), child1->parent());
TEST_ASSERT_EQ(root.get(), child2->parent());
TEST_ASSERT_EQ(child1.get(), grandchild->parent());
TEST_ASSERT_EQ(2u, root->children().size());
TEST_ASSERT_EQ(1u, child1->children().size());
}
// ---------------------------------------------------------------------------
// 世界变换测试
// ---------------------------------------------------------------------------
TEST(Node, WorldPosition) {
auto parent = ptr::make<Node>();
parent->setPos(100, 100);
auto child = ptr::make<Node>();
child->setPos(50, 50);
parent->addChild(child);
Vec2 worldPos = child->worldPos();
TEST_ASSERT_TRUE(vec2Equal(worldPos, Vec2(150, 150)));
}
TEST(Node, WorldScale) {
auto parent = ptr::make<Node>();
parent->setScale(2.0f, 2.0f);
auto child = ptr::make<Node>();
child->setScale(1.5f, 1.5f);
parent->addChild(child);
Vec2 worldScale = child->worldScale();
TEST_ASSERT_TRUE(vec2Equal(worldScale, Vec2(3.0f, 3.0f)));
}
TEST(Node, WorldRotation) {
auto parent = ptr::make<Node>();
parent->setRot(45.0f);
auto child = ptr::make<Node>();
child->setRot(30.0f);
parent->addChild(child);
f32 worldRot = child->worldRot();
TEST_ASSERT_TRUE(std::abs(worldRot - 75.0f) < EPSILON);
}
TEST(Node, WorldMatrix4x4) {
auto parent = ptr::make<Node>();
parent->setPos(100, 100);
auto child = ptr::make<Node>();
child->setPos(50, 50);
parent->addChild(child);
Mat4 world4x4 = child->world4x4();
Vec4 origin(0, 0, 0, 1);
Vec4 transformed = world4x4 * origin;
TEST_ASSERT_TRUE(std::abs(transformed.x - 150.0f) < EPSILON);
TEST_ASSERT_TRUE(std::abs(transformed.y - 150.0f) < EPSILON);
}
// ---------------------------------------------------------------------------
// 组件系统测试
// ---------------------------------------------------------------------------
class TestComp : public Comp {
public:
const char* type() const override { return "TestComp"; }
void update(f32 dt) override {
updateCount++;
lastDt = dt;
}
int updateCount = 0;
f32 lastDt = 0.0f;
};
TEST(Node, AddComponent) {
auto node = ptr::make<Node>();
TestComp* comp = node->add<TestComp>();
TEST_ASSERT_NOT_NULL(comp);
TEST_ASSERT_EQ(node.get(), comp->owner());
}
TEST(Node, GetComponent) {
auto node = ptr::make<Node>();
node->add<TestComp>();
TestComp* comp = node->get<TestComp>();
TEST_ASSERT_NOT_NULL(comp);
}
TEST(Node, RemoveComponent) {
auto node = ptr::make<Node>();
node->add<TestComp>();
node->remove<TestComp>();
TestComp* comp = node->get<TestComp>();
TEST_ASSERT_NULL(comp);
}
TEST(Node, UpdateComponents) {
auto node = ptr::make<Node>();
TestComp* comp = node->add<TestComp>();
node->updateComps(0.016f);
TEST_ASSERT_EQ(1, comp->updateCount);
TEST_ASSERT_TRUE(std::abs(comp->lastDt - 0.016f) < EPSILON);
}
// ---------------------------------------------------------------------------
// 可见性和标签测试
// ---------------------------------------------------------------------------
TEST(Node, Visibility) {
auto node = ptr::make<Node>();
TEST_ASSERT_TRUE(node->visible());
node->setVisible(false);
TEST_ASSERT_FALSE(node->visible());
node->setVisible(true);
TEST_ASSERT_TRUE(node->visible());
}
TEST(Node, Tag) {
auto node = ptr::make<Node>();
TEST_ASSERT_EQ(0, node->tag());
node->setTag(42);
TEST_ASSERT_EQ(42, node->tag());
}
TEST(Node, Name) {
auto node = ptr::make<Node>();
TEST_ASSERT_TRUE(node->name().empty());
node->setName("TestNode");
TEST_ASSERT_EQ("TestNode", node->name());
}
// ---------------------------------------------------------------------------
// 边界框测试
// ---------------------------------------------------------------------------
TEST(BoundingBox, Create) {
BoundingBox box(Vec2(0, 0), Vec2(100, 100));
TEST_ASSERT_TRUE(vec2Equal(box.min, Vec2(0, 0)));
TEST_ASSERT_TRUE(vec2Equal(box.max, Vec2(100, 100)));
}
TEST(BoundingBox, FromCenter) {
BoundingBox box = BoundingBox::fromCenter(Vec2(50, 50), Vec2(25, 25));
TEST_ASSERT_TRUE(vec2Equal(box.min, Vec2(25, 25)));
TEST_ASSERT_TRUE(vec2Equal(box.max, Vec2(75, 75)));
}
TEST(BoundingBox, Contains) {
BoundingBox box(Vec2(0, 0), Vec2(100, 100));
TEST_ASSERT_TRUE(box.contains(Vec2(50, 50)));
TEST_ASSERT_TRUE(box.contains(Vec2(0, 0)));
TEST_ASSERT_TRUE(box.contains(Vec2(100, 100)));
TEST_ASSERT_FALSE(box.contains(Vec2(150, 50)));
}
TEST(BoundingBox, Intersects) {
BoundingBox box1(Vec2(0, 0), Vec2(100, 100));
BoundingBox box2(Vec2(50, 50), Vec2(150, 150));
BoundingBox box3(Vec2(200, 200), Vec2(300, 300));
TEST_ASSERT_TRUE(box1.intersects(box2));
TEST_ASSERT_FALSE(box1.intersects(box3));
}
TEST(BoundingBox, Merged) {
BoundingBox box1(Vec2(0, 0), Vec2(100, 100));
BoundingBox box2(Vec2(50, 50), Vec2(150, 150));
BoundingBox merged = box1.merged(box2);
TEST_ASSERT_TRUE(vec2Equal(merged.min, Vec2(0, 0)));
TEST_ASSERT_TRUE(vec2Equal(merged.max, Vec2(150, 150)));
}