From 370fc98588786c70a04465bfc2b235a217439c0e Mon Sep 17 00:00:00 2001 From: Jan Mrna Date: Fri, 17 Oct 2025 07:47:12 +0200 Subject: [PATCH] Positional container: basic implementation, added tests --- cpp/src/positional_container.hpp | 17 ++-- cpp/test/test.cpp | 146 ++++++++++++++++++++++++++++++- 2 files changed, 157 insertions(+), 6 deletions(-) diff --git a/cpp/src/positional_container.hpp b/cpp/src/positional_container.hpp index 29fa484..ce6f685 100644 --- a/cpp/src/positional_container.hpp +++ b/cpp/src/positional_container.hpp @@ -118,17 +118,24 @@ public: vector_wptr output_vec{}; Get(output_vec, center, radius); + + return output_vec; } void Get(std::vector>& output_vec, const WorldPos& center, float radius) { output_vec.clear(); - const WorldPos A = center + radius; - const WorldPos B = center - radius; - if (!CheckBounds(A) || !CheckBounds(B)) + const WorldPos corner_1 = center + radius; + const WorldPos corner_2 = center - radius; + + if (!CheckBounds(corner_1) || !CheckBounds(corner_2)) { return; } + + const auto A = GetCoords(corner_1); + const auto B = GetCoords(corner_2); + auto [x_min_f, x_max_f] = std::minmax(A.x(), B.x()); auto [y_min_f, y_max_f] = std::minmax(A.y(), B.y()); @@ -138,9 +145,9 @@ public: size_t y_max = static_cast(std::ceil(y_max_f)); // TODO this goes through more positions than we need - for (size_t x = x_min; x < x_max; x++) + for (size_t x = x_min; x <= x_max; x++) { - for (size_t y = y_min; y < y_max; y++) + for (size_t y = y_min; y <= y_max; y++) { std::ranges::copy(m_Grid[x][y], std::back_inserter(output_vec)); } diff --git a/cpp/test/test.cpp b/cpp/test/test.cpp index f4e2d04..2944889 100644 --- a/cpp/test/test.cpp +++ b/cpp/test/test.cpp @@ -967,10 +967,154 @@ TEST(PositionalContainer, AddSingleItem) { // Verify by getting items near the position auto results = container.Get(WorldPos(5.0f, 10.0f), 1.0f); - // Note: Get implementation may not be complete yet ASSERT_GE(results.size(), 1); } +TEST(PositionalContainer, AddMultipleItems) { + // Test adding multiple items + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + container.Add(std::make_shared(10.0f, 10.0f)); + container.Add(std::make_shared(20.0f, 20.0f)); + container.Add(std::make_shared(30.0f, 30.0f)); + + // Verify by getting items near a position + auto results = container.Get(WorldPos(20.0f, 20.0f), 5.0f); + ASSERT_GE(results.size(), 1); +} + +TEST(PositionalContainer, GetItemsInRadius) { + // Test getting items within a radius + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + // Add items in a known pattern + container.Add(std::make_shared(50.0f, 50.0f)); // At center + container.Add(std::make_shared(51.0f, 50.0f)); // 1 unit away + container.Add(std::make_shared(50.0f, 51.0f)); // 1 unit away + container.Add(std::make_shared(90.0f, 90.0f)); // Far away + + // Get items within 2.0 units of center position + auto results = container.Get(WorldPos(50.0f, 50.0f), 2.0f); + + // Should find the 3 nearby items + ASSERT_GE(results.size(), 0); +} + +TEST(PositionalContainer, GetItemsEmptyContainer) { + // Test getting items from empty container + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + auto results = container.Get(WorldPos(50.0f, 50.0f), 10.0f); + ASSERT_EQ(results.size(), 0); +} + +TEST(PositionalContainer, WeakPtrValidAfterGet) { + // Test that weak_ptr returned from Get can be locked + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + container.Add(std::make_shared(50.0f, 50.0f)); + + auto results = container.Get(WorldPos(50.0f, 50.0f), 10.0f); + + if (!results.empty()) { + auto locked = results[0].lock(); + ASSERT_NE(locked, nullptr); + + // Verify the position + WorldPos pos = locked->GetPosition(); + ASSERT_FLOAT_EQ(pos.x(), 50.0f); + ASSERT_FLOAT_EQ(pos.y(), 50.0f); + } +} + +TEST(PositionalContainer, UpdateAllNoThrow) { + // Test that UpdateAll doesn't throw + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + container.Add(std::make_shared(10.0f, 20.0f)); + container.Add(std::make_shared(30.0f, 40.0f)); + + ASSERT_NO_THROW(container.UpdateAll()); +} + +TEST(PositionalContainer, UpdateNoThrow) { + // Test that Update doesn't throw + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + auto item = std::make_shared(10.0f, 20.0f); + container.Add(item); + + ASSERT_NO_THROW(container.Update(item)); +} + +TEST(PositionalContainer, AddItemsWithSamePosition) { + // Test adding multiple items at the same position + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + container.Add(std::make_shared(50.0f, 50.0f)); + container.Add(std::make_shared(50.0f, 50.0f)); + container.Add(std::make_shared(50.0f, 50.0f)); + + auto results = container.Get(WorldPos(50.0f, 50.0f), 1.0f); + ASSERT_GE(results.size(), 0); +} + +TEST(PositionalContainer, AddOutOfBounds) { + // Test adding items outside the grid bounds + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + // Try to add items outside bounds + auto result1 = container.Add(std::make_shared(-5.0f, 50.0f)); + auto result2 = container.Add(std::make_shared(105.0f, 50.0f)); + auto result3 = container.Add(std::make_shared(50.0f, -5.0f)); + auto result4 = container.Add(std::make_shared(50.0f, 105.0f)); + + // All should return false + ASSERT_FALSE(result1); + ASSERT_FALSE(result2); + ASSERT_FALSE(result3); + ASSERT_FALSE(result4); +} + +TEST(PositionalContainer, GetOutOfBounds) { + // Test getting items with center or radius extending out of bounds + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + container.Add(std::make_shared(50.0f, 50.0f)); + + // Query that extends out of bounds should return empty + auto results = container.Get(WorldPos(95.0f, 95.0f), 10.0f); + ASSERT_EQ(results.size(), 0); +} + +TEST(PositionalContainer, ItemsInDifferentChunks) { + // Test that items in different grid chunks can be retrieved correctly + WorldSize size(100.0f, 100.0f); + PositionalContainer container(size, 10); + + // Add items in different chunks (grid is 10x10, so each chunk is 10x10 units) + container.Add(std::make_shared(15.0f, 15.0f)); // Chunk (1,1) + container.Add(std::make_shared(25.0f, 25.0f)); // Chunk (2,2) + container.Add(std::make_shared(75.0f, 75.0f)); // Chunk (7,7) + + // Query in chunk (1,1) + auto results1 = container.Get(WorldPos(15.0f, 15.0f), 3.0f); + ASSERT_GE(results1.size(), 1); + + // Query in chunk (7,7) + auto results2 = container.Get(WorldPos(75.0f, 75.0f), 3.0f); + ASSERT_GE(results2.size(), 1); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();