added events

This commit is contained in:
2026-03-11 14:19:58 +03:00
parent 93fc93d4f6
commit 1d54039f57
3 changed files with 145 additions and 2 deletions

View File

@@ -6,6 +6,7 @@
#include "omath/linear_algebra/vector3.hpp" #include "omath/linear_algebra/vector3.hpp"
#include <expected> #include <expected>
#include <optional>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@@ -29,10 +30,20 @@ namespace omath::pathfinding
[[nodiscard]] [[nodiscard]]
bool empty() const; bool empty() const;
// Events -- per-vertex optional tag (e.g. "jump", "teleport")
void set_event(const Vector3<float>& vertex, std::string event_id);
void clear_event(const Vector3<float>& vertex);
[[nodiscard]]
std::optional<std::string> get_event(const Vector3<float>& vertex) const noexcept;
[[nodiscard]] std::string serialize() const noexcept; [[nodiscard]] std::string serialize() const noexcept;
void deserialize(const std::string& raw); void deserialize(const std::string& raw);
std::unordered_map<Vector3<float>, std::vector<Vector3<float>>> m_vertex_map; std::unordered_map<Vector3<float>, std::vector<Vector3<float>>> m_vertex_map;
private:
std::unordered_map<Vector3<float>, std::string> m_vertex_events;
}; };
} // namespace omath::pathfinding } // namespace omath::pathfinding

View File

@@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
namespace omath::pathfinding namespace omath::pathfinding
{ {
std::expected<Vector3<float>, std::string> std::expected<Vector3<float>, std::string>
@@ -29,13 +30,40 @@ namespace omath::pathfinding
return m_vertex_map.empty(); return m_vertex_map.empty();
} }
void NavigationMesh::set_event(const Vector3<float>& vertex, std::string event_id)
{
m_vertex_events[vertex] = std::move(event_id);
}
void NavigationMesh::clear_event(const Vector3<float>& vertex)
{
m_vertex_events.erase(vertex);
}
std::optional<std::string> NavigationMesh::get_event(const Vector3<float>& vertex) const noexcept
{
const auto it = m_vertex_events.find(vertex);
if (it == m_vertex_events.end())
return std::nullopt;
return it->second;
}
// Serialization format per vertex line:
// x y z neighbor_count event_id
// where event_id is "-" when no event is set.
// Neighbor lines follow: nx ny nz
std::string NavigationMesh::serialize() const noexcept std::string NavigationMesh::serialize() const noexcept
{ {
std::ostringstream oss; std::ostringstream oss;
for (const auto& [vertex, neighbors] : m_vertex_map) for (const auto& [vertex, neighbors] : m_vertex_map)
{ {
const auto event_it = m_vertex_events.find(vertex);
const std::string& event = (event_it != m_vertex_events.end()) ? event_it->second : "-";
oss << vertex.x << ' ' << vertex.y << ' ' << vertex.z oss << vertex.x << ' ' << vertex.y << ' ' << vertex.z
<< ' ' << neighbors.size() << '\n'; << ' ' << neighbors.size() << ' ' << event << '\n';
for (const auto& n : neighbors) for (const auto& n : neighbors)
oss << n.x << ' ' << n.y << ' ' << n.z << '\n'; oss << n.x << ' ' << n.y << ' ' << n.z << '\n';
} }
@@ -45,11 +73,13 @@ namespace omath::pathfinding
void NavigationMesh::deserialize(const std::string& raw) void NavigationMesh::deserialize(const std::string& raw)
{ {
m_vertex_map.clear(); m_vertex_map.clear();
m_vertex_events.clear();
std::istringstream iss(raw); std::istringstream iss(raw);
Vector3<float> vertex; Vector3<float> vertex;
std::size_t neighbors_count; std::size_t neighbors_count;
while (iss >> vertex.x >> vertex.y >> vertex.z >> neighbors_count) std::string event;
while (iss >> vertex.x >> vertex.y >> vertex.z >> neighbors_count >> event)
{ {
std::vector<Vector3<float>> neighbors; std::vector<Vector3<float>> neighbors;
neighbors.reserve(neighbors_count); neighbors.reserve(neighbors_count);
@@ -61,6 +91,9 @@ namespace omath::pathfinding
neighbors.push_back(n); neighbors.push_back(n);
} }
m_vertex_map.emplace(vertex, std::move(neighbors)); m_vertex_map.emplace(vertex, std::move(neighbors));
if (event != "-")
m_vertex_events.emplace(vertex, std::move(event));
} }
} }
} // namespace omath::pathfinding } // namespace omath::pathfinding

View File

@@ -140,3 +140,102 @@ TEST(NavigationMeshTests, VertexWithNoNeighborsRoundTrip)
ASSERT_EQ(nav2.m_vertex_map.count(v), 1u); ASSERT_EQ(nav2.m_vertex_map.count(v), 1u);
EXPECT_TRUE(nav2.get_neighbors(v).empty()); EXPECT_TRUE(nav2.get_neighbors(v).empty());
} }
// ---------------------------------------------------------------------------
// Vertex events
// ---------------------------------------------------------------------------
TEST(NavigationMeshTests, EventNotSetByDefault)
{
NavigationMesh nav;
const Vector3<float> v{0.f, 0.f, 0.f};
nav.m_vertex_map.emplace(v, std::vector<Vector3<float>>{});
EXPECT_FALSE(nav.get_event(v).has_value());
}
TEST(NavigationMeshTests, SetAndGetEvent)
{
NavigationMesh nav;
const Vector3<float> v{1.f, 0.f, 0.f};
nav.m_vertex_map.emplace(v, std::vector<Vector3<float>>{});
nav.set_event(v, "jump");
const auto event = nav.get_event(v);
ASSERT_TRUE(event.has_value());
EXPECT_EQ(event.value(), "jump");
}
TEST(NavigationMeshTests, OverwriteEvent)
{
NavigationMesh nav;
const Vector3<float> v{1.f, 0.f, 0.f};
nav.m_vertex_map.emplace(v, std::vector<Vector3<float>>{});
nav.set_event(v, "jump");
nav.set_event(v, "teleport");
EXPECT_EQ(nav.get_event(v).value(), "teleport");
}
TEST(NavigationMeshTests, ClearEvent)
{
NavigationMesh nav;
const Vector3<float> v{1.f, 0.f, 0.f};
nav.m_vertex_map.emplace(v, std::vector<Vector3<float>>{});
nav.set_event(v, "jump");
nav.clear_event(v);
EXPECT_FALSE(nav.get_event(v).has_value());
}
TEST(NavigationMeshTests, EventRoundTripSerialization)
{
NavigationMesh nav;
const Vector3<float> a{0.f, 0.f, 0.f};
const Vector3<float> b{1.f, 0.f, 0.f};
nav.m_vertex_map.emplace(a, std::vector<Vector3<float>>{b});
nav.m_vertex_map.emplace(b, std::vector<Vector3<float>>{});
nav.set_event(b, "jump");
NavigationMesh nav2;
nav2.deserialize(nav.serialize());
ASSERT_FALSE(nav2.get_event(a).has_value());
ASSERT_TRUE(nav2.get_event(b).has_value());
EXPECT_EQ(nav2.get_event(b).value(), "jump");
}
TEST(NavigationMeshTests, MultipleEventsRoundTrip)
{
NavigationMesh nav;
const Vector3<float> a{0.f, 0.f, 0.f};
const Vector3<float> b{1.f, 0.f, 0.f};
const Vector3<float> c{2.f, 0.f, 0.f};
nav.m_vertex_map.emplace(a, std::vector<Vector3<float>>{});
nav.m_vertex_map.emplace(b, std::vector<Vector3<float>>{});
nav.m_vertex_map.emplace(c, std::vector<Vector3<float>>{});
nav.set_event(a, "spawn");
nav.set_event(c, "teleport");
NavigationMesh nav2;
nav2.deserialize(nav.serialize());
EXPECT_EQ(nav2.get_event(a).value(), "spawn");
EXPECT_FALSE(nav2.get_event(b).has_value());
EXPECT_EQ(nav2.get_event(c).value(), "teleport");
}
TEST(NavigationMeshTests, DeserializeClearsOldEvents)
{
NavigationMesh nav;
const Vector3<float> v{0.f, 0.f, 0.f};
nav.m_vertex_map.emplace(v, std::vector<Vector3<float>>{});
nav.set_event(v, "jump");
// Deserialize a mesh that has no events
NavigationMesh empty_events;
empty_events.m_vertex_map.emplace(v, std::vector<Vector3<float>>{});
nav.deserialize(empty_events.serialize());
EXPECT_FALSE(nav.get_event(v).has_value());
}