omath::pathfinding::NavigationMesh — Lightweight vertex graph for A*
Header: your project’s
pathfinding/navigation_mesh.hppNamespace:omath::pathfindingNodes:Vector3<float>(3D points) Storage: adjacency mapunordered_map<Vector3<float>, std::vector<Vector3<float>>>
A minimal navigation mesh represented as a vertex/edge graph. Each vertex is a Vector3<float> and neighbors are stored in an adjacency list. Designed to pair with Astar::find_path.
API
class NavigationMesh final {
public:
// Nearest graph vertex to an arbitrary 3D point.
// On success -> closest vertex; on failure -> std::string error (e.g., empty mesh).
[[nodiscard]]
std::expected<Vector3<float>, std::string>
get_closest_vertex(const Vector3<float>& point) const noexcept;
// Read-only neighbor list for a vertex key.
// If vertex is absent, implementation should return an empty list (see notes).
[[nodiscard]]
const std::vector<Vector3<float>>&
get_neighbors(const Vector3<float>& vertex) const noexcept;
// True if the graph has no vertices/edges.
[[nodiscard]]
bool empty() const;
// Serialize/deserialize the graph (opaque binary).
[[nodiscard]] std::vector<uint8_t> serialize() const noexcept;
void deserialize(const std::vector<uint8_t>& raw) noexcept;
// Public adjacency (vertex -> neighbors)
std::unordered_map<Vector3<float>, std::vector<Vector3<float>>> m_vertex_map;
};
Quick start
using omath::Vector3;
using omath::pathfinding::NavigationMesh;
// Build a tiny mesh (triangle)
NavigationMesh nav;
nav.m_vertex_map[ {0,0,0} ] = { {1,0,0}, {0,0,1} };
nav.m_vertex_map[ {1,0,0} ] = { {0,0,0}, {0,0,1} };
nav.m_vertex_map[ {0,0,1} ] = { {0,0,0}, {1,0,0} };
// Query the closest node to an arbitrary point
auto q = nav.get_closest_vertex({0.3f, 0.0f, 0.2f});
if (q) {
const auto& v = *q;
const auto& nbrs = nav.get_neighbors(v);
(void)nbrs;
}
Semantics & expectations
-
Nearest vertex
get_closest_vertex(p)should scan known vertices and return the one with minimal Euclidean distance top. If the mesh is empty, expect an error (unexpectedwith a message). -
Neighbors
get_neighbors(v)returns the adjacency list forv. Ifvis not present, a conventional behavior is to return a reference to a static empty vector (since the API isnoexceptand returns by reference). Verify in your implementation. -
Graph invariants (recommended)
- Neighbor links are symmetric for undirected navigation (if
uhasv, thenvhasu). - No self-loops unless explicitly desired.
- Vertices are unique keys; hashing uses
std::hash<Vector3<float>>(be mindful of floating-point equality).
- Neighbor links are symmetric for undirected navigation (if
Serialization
serialize()→ opaque, implementation-defined binary of the currentm_vertex_map.deserialize(raw)→ restores the internal map fromraw. Keep versioning in mind if you evolve the format (e.g., add a header/magic/version).
Performance
Let V = m_vertex_map.size() and E = Σ|neighbors(v)|.
get_closest_vertex: O(V) (linear scan) unless you back it with a spatial index (KD-tree, grid, etc.).get_neighbors: O(1) average (hash lookup).- Memory: O(V + E).
Usage notes
- Floating-point keys: Using
Vector3<float>as an unordered_map key relies on yourstd::hash<omath::Vector3<float>>and exactoperator==. Avoid building meshes with numerically “close but not equal” duplicates; consider canonicalizing or using integer IDs if needed. - Pathfinding: Pair with
Astar::find_path(start, end, nav); the A* heuristic can use straight-line distance between vertex positions.
Minimal test ideas
- Empty mesh →
get_closest_vertexreturns error;empty() == true. - Single vertex → nearest always that vertex; neighbors empty.
- Symmetric edges →
get_neighbors(a)containsband vice versa. - Serialization round-trip preserves vertex/edge counts and neighbor lists.