diff --git a/include/omath/3d_primitives/aabb.hpp b/include/omath/3d_primitives/aabb.hpp index fddccfd..62e83cf 100644 --- a/include/omath/3d_primitives/aabb.hpp +++ b/include/omath/3d_primitives/aabb.hpp @@ -24,5 +24,11 @@ namespace omath::primitives { return (max - min) / static_cast(2); } + [[nodiscard]] + constexpr bool is_collide(const Aabb& other) const noexcept + { + return min.x <= other.max.x && max.x >= other.min.x && + min.y <= other.max.y && max.y >= other.min.y &&min.z <= other.max.z && max.z >= other.min.z; + } }; } // namespace omath::primitives diff --git a/tests/general/mem_fd_helper.hpp b/tests/general/mem_fd_helper.hpp index 9bd27f1..81d5c23 100644 --- a/tests/general/mem_fd_helper.hpp +++ b/tests/general/mem_fd_helper.hpp @@ -20,19 +20,19 @@ #include #if defined(__linux__) -# include -# include -# if defined(__ANDROID__) -# if __ANDROID_API__ >= 30 -# include -# define OMATH_TEST_USE_MEMFD 1 -# endif +#include +#include +#if defined(__ANDROID__) +#if __ANDROID_API__ >= 30 +#include +#define OMATH_TEST_USE_MEMFD 1 +#endif // Android < 30: fall through to tmpfile() path below -# else +#else // Desktop Linux: memfd_create available since glibc 2.27 / kernel 3.17 -# include -# define OMATH_TEST_USE_MEMFD 1 -# endif +#include +#define OMATH_TEST_USE_MEMFD 1 +#endif #endif class MemFdFile @@ -57,9 +57,11 @@ public: MemFdFile(MemFdFile&& o) noexcept : m_path(std::move(o.m_path)) #if defined(OMATH_TEST_USE_MEMFD) - , m_fd(o.m_fd) + , + m_fd(o.m_fd) #else - , m_temp_path(std::move(o.m_temp_path)) + , + m_temp_path(std::move(o.m_temp_path)) #endif { #if defined(OMATH_TEST_USE_MEMFD) @@ -69,9 +71,15 @@ public: #endif } - [[nodiscard]] bool valid() const { return !m_path.empty(); } + [[nodiscard]] bool valid() const + { + return !m_path.empty(); + } - [[nodiscard]] const std::filesystem::path& path() const { return m_path; } + [[nodiscard]] const std::filesystem::path& path() const + { + return m_path; + } static MemFdFile create(const std::vector& data) { @@ -101,7 +109,7 @@ public: std::mt19937_64 rng(std::random_device{}()); const auto unique_name = "omath_test_" + std::to_string(rng()) + ".bin"; f.m_temp_path = (tmp_dir / unique_name).string(); - f.m_path = f.m_temp_path; + f.m_path = f.m_temp_path; std::ofstream out(f.m_temp_path, std::ios::binary | std::ios::trunc); if (!out.is_open()) @@ -153,38 +161,40 @@ private: // --------------------------------------------------------------------------- inline std::vector build_minimal_pe(const std::vector& section_bytes) { - constexpr std::uint32_t e_lfanew = 0x80u; - constexpr std::uint16_t size_opt = 0xF0u; - constexpr std::size_t nt_off = e_lfanew; - constexpr std::size_t fh_off = nt_off + 4; - constexpr std::size_t oh_off = fh_off + 20; - constexpr std::size_t sh_off = oh_off + size_opt; - constexpr std::size_t data_off = sh_off + 44; + constexpr std::uint32_t e_lfanew = 0x80u; + constexpr std::uint16_t size_opt = 0xF0u; + constexpr std::size_t nt_off = e_lfanew; + constexpr std::size_t fh_off = nt_off + 4; + constexpr std::size_t oh_off = fh_off + 20; + constexpr std::size_t sh_off = oh_off + size_opt; + constexpr std::size_t data_off = sh_off + 44; std::vector buf(data_off + section_bytes.size(), 0u); - buf[0] = 'M'; buf[1] = 'Z'; + buf[0] = 'M'; + buf[1] = 'Z'; std::memcpy(buf.data() + 0x3Cu, &e_lfanew, 4); - buf[nt_off] = 'P'; buf[nt_off + 1] = 'E'; + buf[nt_off] = 'P'; + buf[nt_off + 1] = 'E'; - const std::uint16_t machine = 0x8664u, num_sections = 1u; - std::memcpy(buf.data() + fh_off, &machine, 2); - std::memcpy(buf.data() + fh_off + 2, &num_sections, 2); - std::memcpy(buf.data() + fh_off + 16, &size_opt, 2); + constexpr std::uint16_t machine = 0x8664u, num_sections = 1u; + std::memcpy(buf.data() + fh_off, &machine, 2); + std::memcpy(buf.data() + fh_off + 2, &num_sections, 2); + std::memcpy(buf.data() + fh_off + 16, &size_opt, 2); - const std::uint16_t magic = 0x20Bu; + constexpr std::uint16_t magic = 0x20Bu; std::memcpy(buf.data() + oh_off, &magic, 2); - const char name[8] = {'.','t','e','x','t',0,0,0}; + constexpr char name[8] = {'.', 't', 'e', 'x', 't', 0, 0, 0}; std::memcpy(buf.data() + sh_off, name, 8); - const auto vsize = static_cast(section_bytes.size()); - const std::uint32_t vaddr = 0x1000u; - const auto ptr_raw = static_cast(data_off); - std::memcpy(buf.data() + sh_off + 8, &vsize, 4); - std::memcpy(buf.data() + sh_off + 12, &vaddr, 4); - std::memcpy(buf.data() + sh_off + 16, &vsize, 4); + const auto vsize = static_cast(section_bytes.size()); + constexpr std::uint32_t vaddr = 0x1000u; + constexpr auto ptr_raw = static_cast(data_off); + std::memcpy(buf.data() + sh_off + 8, &vsize, 4); + std::memcpy(buf.data() + sh_off + 12, &vaddr, 4); + std::memcpy(buf.data() + sh_off + 16, &vsize, 4); std::memcpy(buf.data() + sh_off + 20, &ptr_raw, 4); std::memcpy(buf.data() + data_off, section_bytes.data(), section_bytes.size()); diff --git a/tests/general/unit_test_aabb.cpp b/tests/general/unit_test_aabb.cpp new file mode 100644 index 0000000..11a1e7e --- /dev/null +++ b/tests/general/unit_test_aabb.cpp @@ -0,0 +1,143 @@ +// +// Created by Vladislav on 19.04.2026. +// +#include +#include "omath/3d_primitives/aabb.hpp" + +using AABB = omath::primitives::Aabb; +using Vec3 = omath::Vector3; + +// --- center() --- + +TEST(AabbTests, CenterOfSymmetricBox) +{ + const AABB box{{-1.f, -1.f, -1.f}, {1.f, 1.f, 1.f}}; + const auto c = box.center(); + EXPECT_FLOAT_EQ(c.x, 0.f); + EXPECT_FLOAT_EQ(c.y, 0.f); + EXPECT_FLOAT_EQ(c.z, 0.f); +} + +TEST(AabbTests, CenterOfOffsetBox) +{ + const AABB box{{1.f, 2.f, 3.f}, {3.f, 6.f, 7.f}}; + const auto c = box.center(); + EXPECT_FLOAT_EQ(c.x, 2.f); + EXPECT_FLOAT_EQ(c.y, 4.f); + EXPECT_FLOAT_EQ(c.z, 5.f); +} + +TEST(AabbTests, CenterOfDegenerateBox) +{ + const AABB box{{5.f, 5.f, 5.f}, {5.f, 5.f, 5.f}}; + const auto c = box.center(); + EXPECT_FLOAT_EQ(c.x, 5.f); + EXPECT_FLOAT_EQ(c.y, 5.f); + EXPECT_FLOAT_EQ(c.z, 5.f); +} + +// --- extents() --- + +TEST(AabbTests, ExtentsOfSymmetricBox) +{ + const AABB box{{-2.f, -3.f, -4.f}, {2.f, 3.f, 4.f}}; + const auto e = box.extents(); + EXPECT_FLOAT_EQ(e.x, 2.f); + EXPECT_FLOAT_EQ(e.y, 3.f); + EXPECT_FLOAT_EQ(e.z, 4.f); +} + +TEST(AabbTests, ExtentsOfUnitBox) +{ + const AABB box{{0.f, 0.f, 0.f}, {2.f, 2.f, 2.f}}; + const auto e = box.extents(); + EXPECT_FLOAT_EQ(e.x, 1.f); + EXPECT_FLOAT_EQ(e.y, 1.f); + EXPECT_FLOAT_EQ(e.z, 1.f); +} + +TEST(AabbTests, ExtentsOfDegenerateBox) +{ + const AABB box{{3.f, 3.f, 3.f}, {3.f, 3.f, 3.f}}; + const auto e = box.extents(); + EXPECT_FLOAT_EQ(e.x, 0.f); + EXPECT_FLOAT_EQ(e.y, 0.f); + EXPECT_FLOAT_EQ(e.z, 0.f); +} + +// --- is_collide() --- + +TEST(AabbTests, OverlappingBoxesCollide) +{ + const AABB a{{-1.f, -1.f, -1.f}, {1.f, 1.f, 1.f}}; + const AABB b{{0.f, 0.f, 0.f}, {2.f, 2.f, 2.f}}; + EXPECT_TRUE(a.is_collide(b)); + EXPECT_TRUE(b.is_collide(a)); +} + +TEST(AabbTests, SeparatedBoxesDoNotCollide) +{ + const AABB a{{-1.f, -1.f, -1.f}, {1.f, 1.f, 1.f}}; + const AABB b{{2.f, 2.f, 2.f}, {4.f, 4.f, 4.f}}; + EXPECT_FALSE(a.is_collide(b)); + EXPECT_FALSE(b.is_collide(a)); +} + +TEST(AabbTests, TouchingFacesCollide) +{ + const AABB a{{-1.f, -1.f, -1.f}, {1.f, 1.f, 1.f}}; + const AABB b{{1.f, -1.f, -1.f}, {3.f, 1.f, 1.f}}; + EXPECT_TRUE(a.is_collide(b)); + EXPECT_TRUE(b.is_collide(a)); +} + +TEST(AabbTests, ContainedBoxCollides) +{ + const AABB outer{{-3.f, -3.f, -3.f}, {3.f, 3.f, 3.f}}; + const AABB inner{{-1.f, -1.f, -1.f}, {1.f, 1.f, 1.f}}; + EXPECT_TRUE(outer.is_collide(inner)); + EXPECT_TRUE(inner.is_collide(outer)); +} + +TEST(AabbTests, SeparatedOnXAxisDoNotCollide) +{ + const AABB a{{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}; + const AABB b{{2.f, 0.f, 0.f}, {3.f, 1.f, 1.f}}; + EXPECT_FALSE(a.is_collide(b)); +} + +TEST(AabbTests, SeparatedOnYAxisDoNotCollide) +{ + const AABB a{{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}; + const AABB b{{0.f, 2.f, 0.f}, {1.f, 3.f, 1.f}}; + EXPECT_FALSE(a.is_collide(b)); +} + +TEST(AabbTests, SeparatedOnZAxisDoNotCollide) +{ + const AABB a{{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}; + const AABB b{{0.f, 0.f, 2.f}, {1.f, 1.f, 3.f}}; + EXPECT_FALSE(a.is_collide(b)); +} + +TEST(AabbTests, IdenticalBoxesCollide) +{ + const AABB a{{-1.f, -1.f, -1.f}, {1.f, 1.f, 1.f}}; + EXPECT_TRUE(a.is_collide(a)); +} + +TEST(AabbTests, DegeneratePointBoxCollidesWhenInsideOther) +{ + const AABB box{{-1.f, -1.f, -1.f}, {1.f, 1.f, 1.f}}; + const AABB point{{0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}; + EXPECT_TRUE(box.is_collide(point)); + EXPECT_TRUE(point.is_collide(box)); +} + +TEST(AabbTests, DegeneratePointBoxDoesNotCollideWhenOutside) +{ + const AABB box{{-1.f, -1.f, -1.f}, {1.f, 1.f, 1.f}}; + const AABB point{{5.f, 0.f, 0.f}, {5.f, 0.f, 0.f}}; + EXPECT_FALSE(box.is_collide(point)); + EXPECT_FALSE(point.is_collide(box)); +}