From 5209e054e59cf1bfb779bedcbf835e59290bb01c Mon Sep 17 00:00:00 2001 From: Jan Mrna Date: Thu, 16 Oct 2025 18:02:31 +0200 Subject: [PATCH] Positional container WIP --- cpp/src/positional_container.hpp | 172 +++++++++++++++++++++++++++++ cpp/test/collision_performance.cpp | 24 ++++ 2 files changed, 196 insertions(+) create mode 100644 cpp/src/positional_container.hpp diff --git a/cpp/src/positional_container.hpp b/cpp/src/positional_container.hpp new file mode 100644 index 0000000..0b4e94d --- /dev/null +++ b/cpp/src/positional_container.hpp @@ -0,0 +1,172 @@ +#pragma once + +#include +#include +#include + +#include "math.hpp" +#include "log.hpp" + +template +concept HasPosition = requires(T t, WorldPos pos) { + { t.GetPosition() } -> std::convertible_to; + t.SetPosition(pos); +}; + +template +concept HasCollisions = requires(T t) { + t.Dummy(); // TODO +}; + +template +requires HasPosition +class IPositionalContainer +{ +public: + virtual ~IPositionalContainer() = default; + virtual bool Add(const T& t) = 0; + virtual std::vector> Get(const WorldPos& p, float radius) = 0; + virtual void UpdateAll() = 0; + virtual void Update(std::shared_ptr item) = 0; +}; + + +template +class IColliderContainer : public IPositionalContainer +{ +public: + virtual std::vector> GetCollisions() = 0; +}; + +template +class SimpleContainer : IPositionalContainer +{ +public: + bool Add(const T& t) override + { + m_Items.push_back(std::make_shared(t)); + } + + std::vector> Get(const WorldPos& center, float radius) override + { + std::vector> matched_items; + for (const auto& item : m_Items) + { + const auto& A = item->GetPosition(); + if (center.DistanceTo(item->GetPosition()) < radius) + { + matched_items.push_back(item); + } + } + return matched_items; + } + + // no update needed here, as we have no smart lookup scheme + void UpdateAll() override {} + void Update(std::shared_ptr item) override {} + +private: + std::vector> m_Items; +}; + + +template +class PositionalContainer : IPositionalContainer +{ +public: + + PositionalContainer(const WorldSize& size, size_t chunks) : + m_GridSize{size}, + m_GridStep{size / chunks}, + m_ChunksPerAxis{chunks} + { + LOG_INFO("Size: ", m_GridSize, " step: ", m_GridStep); + m_Grid.reserve(chunks); + for (size_t i = 0; i < chunks; i++) + { + //m_Grid.push_back(std::vector{chunks}); + m_Grid.emplace_back(chunks); + for (size_t j = 0; j < chunks; j++) + { + // let's try reserving place for 16 items + m_Grid[i][j].reserve(16); + } + } + } + + // calling Add on object that is already in the container is UB + bool Add(const T& item) override + { + const auto& world_pos = item.GetPosition(); + if (!CheckBounds(world_pos)) + { + return false; + } + auto ptr = std::make_shared(item); + m_Items.push_back(ptr); + auto coords = GetCoords(world_pos); + // here we assume that the object is not already in the grid! + m_Grid[coords.x()][coords.y()].push_back(ptr); + + // TODO should we call Update instead? + //Update(ptr); + } + + std::vector> Get(const WorldPos& center, float radius) override + { + return vector_wptr{}; + } + + void Get(std::vector>& output_vec, const WorldPos& center, float radius) + { + + } + + void UpdateAll() override + { + for (auto ptr : m_Items) + { + // TODO is this efficient? Maybe use const ref? + Update(ptr); + } + } + void Update(std::shared_ptr item) override + { + auto coords = GetCoords(item->GetPosition()); + // remove the old weak ptr from the map + + // add new weak ptr to the map + m_Grid[coords.x()][coords.y()].push_back(item); + } + +private: + + using coord_type = vec; + using vector_wptr = std::vector>; + using grid_type = std::vector>; + + coord_type GetCoords(const WorldPos &wp) + { + auto coord_float = wp / m_ChunksPerAxis; + return coord_type{ + static_cast(coord_float.x()), + static_cast(coord_float.y()) + }; + } + + bool CheckBounds(const WorldPos& pos) + { + auto [x,y] = pos; + bool x_in_bounds = 0.0f < x && x < m_GridSize.x(); + bool y_in_bounds = 0.0f < y && y < m_GridSize.y(); + return x_in_bounds && y_in_bounds; + } + + WorldSize m_GridSize; + WorldSize m_GridStep; + size_t m_ChunksPerAxis; + std::vector> m_Items; + + + grid_type m_Grid; +}; diff --git a/cpp/test/collision_performance.cpp b/cpp/test/collision_performance.cpp index 3860f9f..3db6c73 100644 --- a/cpp/test/collision_performance.cpp +++ b/cpp/test/collision_performance.cpp @@ -3,6 +3,9 @@ #include #include + +#include "positional_container.hpp" + // TODO: Add necessary includes for collision testing // #include "collision_shapes.hpp" // #include "entities.hpp" @@ -74,8 +77,29 @@ void benchmark_function(const std::string& name, int iterations, Func func) { << " ops/sec" << std::endl; } +/** + * @brief Simple dummy class that conforms to HasPosition concept + * Used for testing PositionalContainer without heavy dependencies + */ +class Dummy { +public: + Dummy() : m_Position(0.0f, 0.0f) {} + Dummy(float x, float y) : m_Position(x, y) {} + Dummy(WorldPos pos) : m_Position(pos) {} + + WorldPos GetPosition() const { return m_Position; } + void SetPosition(WorldPos pos) { m_Position = pos; } + +private: + WorldPos m_Position; +}; + // Example test function 1 void test_function_1() { + + PositionalContainer pos_cont{WorldSize{10.0f, 10.0f}, 10}; + SimpleContainer simp_cont; + // TODO: Implement actual collision test volatile int sum = 0; for (int i = 0; i < 1000; ++i) {