improved code style

This commit is contained in:
2025-12-28 02:29:19 +03:00
parent 00fcdc86bc
commit ffcf448a07
6 changed files with 112 additions and 119 deletions

View File

@@ -1,8 +1,8 @@
// Extra collision tests: Simplex, MeshCollider, EPA
#include <gtest/gtest.h>
#include <omath/collision/simplex.hpp>
#include <omath/collision/mesh_collider.hpp>
#include <omath/collision/epa_algorithm.hpp>
#include <omath/collision/mesh_collider.hpp>
#include <omath/collision/simplex.hpp>
#include <omath/engines/source_engine/collider.hpp>
using namespace omath;
@@ -12,7 +12,7 @@ TEST(SimplexTest, HandleEmptySimplex)
{
Simplex<Vector3<float>> simplex;
Vector3<float> direction{1, 0, 0};
EXPECT_EQ(simplex.size(), 0);
EXPECT_FALSE(simplex.handle(direction));
}
@@ -21,21 +21,21 @@ TEST(SimplexTest, HandleLineCollinearWithXAxis)
{
using Vec3 = Vector3<float>;
Simplex<Vec3> simplex;
simplex.push_front(Vec3{1, 0, 0});
simplex.push_front(Vec3{-1, 0, 0});
Vec3 direction{};
std::ignore = simplex.handle(direction);
EXPECT_NEAR(direction.x, 0.f, 1e-6f);
}
TEST(CollisionExtra, SimplexLineHandle)
{
Simplex<Vector3<float>> s;
s = { Vector3<float>{1.f,0.f,0.f}, Vector3<float>{2.f,0.f,0.f} };
Vector3<float> dir{0,0,0};
s = {Vector3<float>{1.f, 0.f, 0.f}, Vector3<float>{2.f, 0.f, 0.f}};
Vector3<float> dir{0, 0, 0};
EXPECT_FALSE(s.handle(dir));
// direction should not be zero
EXPECT_GT(dir.length_sqr(), 0.0f);
@@ -44,8 +44,8 @@ TEST(CollisionExtra, SimplexLineHandle)
TEST(CollisionExtra, SimplexTriangleHandle)
{
Simplex<Vector3<float>> s;
s = { Vector3<float>{1.f,0.f,0.f}, Vector3<float>{0.f,1.f,0.f}, Vector3<float>{0.f,0.f,1.f} };
Vector3<float> dir{0,0,0};
s = {Vector3<float>{1.f, 0.f, 0.f}, Vector3<float>{0.f, 1.f, 0.f}, Vector3<float>{0.f, 0.f, 1.f}};
Vector3<float> dir{0, 0, 0};
EXPECT_FALSE(s.handle(dir));
EXPECT_GT(dir.length_sqr(), 0.0f);
}
@@ -54,8 +54,9 @@ TEST(CollisionExtra, SimplexTetrahedronInside)
{
Simplex<Vector3<float>> s;
// tetra that surrounds origin roughly
s = { Vector3<float>{1.f,0.f,0.f}, Vector3<float>{0.f,1.f,0.f}, Vector3<float>{0.f,0.f,1.f}, Vector3<float>{-1.f,-1.f,-1.f} };
Vector3<float> dir{0,0,0};
s = {Vector3<float>{1.f, 0.f, 0.f}, Vector3<float>{0.f, 1.f, 0.f}, Vector3<float>{0.f, 0.f, 1.f},
Vector3<float>{-1.f, -1.f, -1.f}};
Vector3<float> dir{0, 0, 0};
// if origin inside, handle returns true
const bool inside = s.handle(dir);
EXPECT_TRUE(inside);
@@ -63,44 +64,38 @@ TEST(CollisionExtra, SimplexTetrahedronInside)
TEST(CollisionExtra, MeshColliderOriginAndFurthest)
{
omath::source_engine::Mesh mesh = {
std::vector<omath::primitives::Vertex<>>{
{ { 1.f, 1.f, 1.f }, {}, {} },
{ {-1.f, -1.f, -1.f }, {}, {} }
},
{}
};
source_engine::Mesh mesh = {
std::vector<primitives::Vertex<>>{{{1.f, 1.f, 1.f}, {}, {}}, {{-1.f, -1.f, -1.f}, {}, {}}}, {}};
mesh.set_origin({0, 2, 0});
omath::source_engine::MeshCollider collider(mesh);
source_engine::MeshCollider collider(mesh);
EXPECT_EQ(collider.get_origin(), omath::Vector3<float>(0,2,0));
collider.set_origin({1,2,3});
EXPECT_EQ(collider.get_origin(), omath::Vector3<float>(1,2,3));
EXPECT_EQ(collider.get_origin(), omath::Vector3<float>(0, 2, 0));
collider.set_origin({1, 2, 3});
EXPECT_EQ(collider.get_origin(), omath::Vector3<float>(1, 2, 3));
const auto v = collider.find_abs_furthest_vertex_position({1.f,0.f,0.f});
const auto v = collider.find_abs_furthest_vertex_position({1.f, 0.f, 0.f});
// the original vertex at (1,1,1) translated by origin (1,2,3) becomes (2,3,4)
EXPECT_EQ(v, omath::Vector3<float>(2.f,3.f,4.f));
EXPECT_EQ(v, omath::Vector3<float>(2.f, 3.f, 4.f));
}
TEST(CollisionExtra, EPAConvergesOnSimpleCase)
{
// Build two simple colliders using simple meshes that overlap
omath::source_engine::Mesh meshA = {
std::vector<omath::primitives::Vertex<>>{{ {0.f,0.f,0.f}, {}, {} }, { {1.f,0.f,0.f}, {}, {} } },
{}
};
omath::source_engine::Mesh mesh_b = meshA;
source_engine::Mesh meshA = {
std::vector<primitives::Vertex<>>{{{0.f, 0.f, 0.f}, {}, {}}, {{1.f, 0.f, 0.f}, {}, {}}}, {}};
source_engine::Mesh mesh_b = meshA;
mesh_b.set_origin({0.5f, 0.f, 0.f}); // translate to overlap
omath::source_engine::MeshCollider a(meshA);
omath::source_engine::MeshCollider b(mesh_b);
source_engine::MeshCollider a(meshA);
source_engine::MeshCollider b(mesh_b);
// Create a simplex that approximately contains the origin in Minkowski space
Simplex<omath::Vector3<float>> simplex;
simplex = { omath::Vector3<float>{0.5f,0.f,0.f}, omath::Vector3<float>{-0.5f,0.f,0.f}, omath::Vector3<float>{0.f,0.5f,0.f}, omath::Vector3<float>{0.f,-0.5f,0.f} };
Simplex<Vector3<float>> simplex;
simplex = {omath::Vector3<float>{0.5f, 0.f, 0.f}, omath::Vector3<float>{-0.5f, 0.f, 0.f},
omath::Vector3<float>{0.f, 0.5f, 0.f}, omath::Vector3<float>{0.f, -0.5f, 0.f}};
auto pool = std::pmr::monotonic_buffer_resource(1024);
auto res = Epa<omath::source_engine::MeshCollider>::solve(a, b, simplex, {}, pool);
auto res = Epa<source_engine::MeshCollider>::solve(a, b, simplex, {}, pool);
// EPA may or may not converge depending on numerics; ensure it returns optionally
// but if it does, fields should be finite
if (res.has_value())

View File

@@ -9,7 +9,7 @@
using Mesh = omath::source_engine::Mesh;
using Collider = omath::source_engine::MeshCollider;
using GJK = omath::collision::GjkAlgorithm<Collider>;
using Gjk = omath::collision::GjkAlgorithm<Collider>;
using EPA = omath::collision::Epa<Collider>;
TEST(UnitTestEpa, TestCollisionTrue)
@@ -37,15 +37,15 @@ TEST(UnitTestEpa, TestCollisionTrue)
Collider A(a), B(b);
// GJK
auto gjk = GJK::is_collide_with_simplex_info(A, B);
ASSERT_TRUE(gjk.hit) << "GJK should report collision";
auto [hit, simplex] = Gjk::is_collide_with_simplex_info(A, B);
ASSERT_TRUE(hit) << "GJK should report collision";
// EPA
EPA::Params params;
auto pool = std::make_shared<std::pmr::monotonic_buffer_resource>(1024);
params.max_iterations = 64;
params.tolerance = 1e-4f;
auto epa = EPA::solve(A, B, gjk.simplex, params, *pool);
auto epa = EPA::solve(A, B, simplex, params, *pool);
ASSERT_TRUE(epa.has_value()) << "EPA should converge";
// Normal is unit
@@ -70,8 +70,8 @@ TEST(UnitTestEpa, TestCollisionTrue)
Collider B_plus(b_plus), B_minus(b_minus);
const bool sep_plus = !GJK::is_collide_with_simplex_info(A, B_plus).hit;
const bool sep_minus = !GJK::is_collide_with_simplex_info(A, B_minus).hit;
const bool sep_plus = !Gjk::is_collide_with_simplex_info(A, B_plus).hit;
const bool sep_minus = !Gjk::is_collide_with_simplex_info(A, B_minus).hit;
// Exactly one direction should separate
EXPECT_NE(sep_plus, sep_minus) << "Exactly one of ±penetration must separate";
@@ -81,12 +81,12 @@ TEST(UnitTestEpa, TestCollisionTrue)
Mesh b_resolved = b;
b_resolved.set_origin(b_resolved.get_origin() + resolve);
EXPECT_FALSE(GJK::is_collide(A, Collider(b_resolved))) << "Resolved position should be non-colliding";
EXPECT_FALSE(Gjk::is_collide(A, Collider(b_resolved))) << "Resolved position should be non-colliding";
// Moving the other way should still collide
Mesh b_wrong = b;
b_wrong.set_origin(b_wrong.get_origin() - resolve);
EXPECT_TRUE(GJK::is_collide(A, Collider(b_wrong)));
EXPECT_TRUE(Gjk::is_collide(A, Collider(b_wrong)));
}
TEST(UnitTestEpa, TestCollisionTrue2)
{
@@ -112,7 +112,7 @@ TEST(UnitTestEpa, TestCollisionTrue2)
Collider A(a), B(b);
// --- GJK must detect collision and provide simplex ---
auto gjk = GJK::is_collide_with_simplex_info(A, B);
auto gjk = Gjk::is_collide_with_simplex_info(A, B);
ASSERT_TRUE(gjk.hit) << "GJK should report collision for overlapping cubes";
// --- EPA penetration ---
EPA::Params params;
@@ -139,11 +139,11 @@ TEST(UnitTestEpa, TestCollisionTrue2)
// Apply once: B + pen must separate; the opposite must still collide
Mesh b_resolved = b;
b_resolved.set_origin(b_resolved.get_origin() + pen * margin);
EXPECT_FALSE(GJK::is_collide(A, Collider(b_resolved))) << "Applying penetration should separate";
EXPECT_FALSE(Gjk::is_collide(A, Collider(b_resolved))) << "Applying penetration should separate";
Mesh b_wrong = b;
b_wrong.set_origin(b_wrong.get_origin() - pen * margin);
EXPECT_TRUE(GJK::is_collide(A, Collider(b_wrong))) << "Opposite direction should still intersect";
EXPECT_TRUE(Gjk::is_collide(A, Collider(b_wrong))) << "Opposite direction should still intersect";
// Some book-keeping sanity
EXPECT_GT(epa->iterations, 0);

View File

@@ -32,10 +32,8 @@ TEST(EpaInternal, SolveHandlesSmallPolytope)
params.max_iterations = 16;
params.tolerance = 1e-6f;
const auto result = EpaDummy::solve(a, b, s, params);
// Should either return a valid result or gracefully return nullopt
if (result)
if (const auto result = EpaDummy::solve(a, b, s, params))
{
EXPECT_TRUE(std::isfinite(result->depth));
EXPECT_TRUE(std::isfinite(result->normal.x));

View File

@@ -10,7 +10,8 @@ struct DegenerateCollider
{
using VectorType = Vector3f;
// returns furthest point along dir
VectorType find_abs_furthest_vertex_position(const VectorType& dir) const noexcept
[[nodiscard]]
static VectorType find_abs_furthest_vertex_position(const VectorType& dir) noexcept
{
// Always return points on a small circle in XY plane so some faces become degenerate
if (dir.x > 0.5f) return {0.01f, 0.f, 0.f};

View File

@@ -29,9 +29,8 @@ TEST(LineTracerExtra, HitOnEdge)
{
constexpr Triangle<Vector3<float>> tri({0,0,0},{1,0,0},{0,1,0});
constexpr Ray ray{ {0.0f,0.0f,1.f}, {0.0f,0.0f,0.f}, false };
const auto hit = LineTracer::get_ray_hit_point(ray, tri);
// hitting exact vertex/edge may be considered miss; ensure function handles without crash
if (hit != ray.end)
if (const auto hit = LineTracer::get_ray_hit_point(ray, tri); hit != ray.end)
{
EXPECT_NEAR(hit.x, 0.0f, 1e-6f);
EXPECT_NEAR(hit.y, 0.0f, 1e-6f);