17template <
typename Po
intT>
29 std::unordered_map<uint64_t, std::vector<uint32_t>>
cells;
46 auto ux =
static_cast<uint64_t
>(
static_cast<uint32_t
>(cx));
47 auto uy =
static_cast<uint64_t
>(
static_cast<uint32_t
>(cy));
48 auto uz =
static_cast<uint64_t
>(
static_cast<uint32_t
>(cz));
49 return (ux * 73856093ULL) ^ (uy * 19349663ULL) ^ (uz * 83492791ULL);
52 inline std::array<int32_t, 3>
cell_coords_3d(
const glm::vec3& p,
float inv_cell)
55 static_cast<int32_t
>(std::floor(p.x * inv_cell)),
56 static_cast<int32_t
>(std::floor(p.y * inv_cell)),
57 static_cast<int32_t
>(std::floor(p.z * inv_cell))
61 inline uint64_t
hash_cell_nd(
const Eigen::VectorXd& p,
float inv_cell)
64 constexpr uint64_t primes[] = {
65 73856093ULL, 19349663ULL, 83492791ULL,
66 48611ULL, 76963ULL, 15485863ULL,
67 32452843ULL, 49979687ULL
69 Eigen::Index dims = std::min(p.size(),
static_cast<Eigen::Index
>(8));
70 for (Eigen::Index i = 0; i < dims; ++i) {
71 auto ci =
static_cast<int32_t
>(std::floor(p(i) * inv_cell));
72 h ^=
static_cast<uint64_t
>(
static_cast<uint32_t
>(ci)) * primes[i];
103template <
typename Po
intT>
106 using DistanceFn = std::function<float(
const PointT&,
const PointT&)>;
164 const PointT& center,
173 [[nodiscard]] std::vector<QueryResult>
k_nearest(
174 const PointT& center,
182 [[nodiscard]] std::optional<PointT>
position_of(uint32_t
id)
const;
187 [[nodiscard]]
size_t count()
const;
193 [[nodiscard]] std::vector<std::pair<uint32_t, PointT>>
all()
const;
217 uint64_t
hash_cell(
const PointT& p)
const;
221 const PointT& center,
223 std::vector<QueryResult>& out)
const;
227 const PointT& center,
229 std::vector<QueryResult>& out)
const;
235#ifdef MAYAFLUX_PLATFORM_MACOS
236 std::atomic<const SpatialSnapshot<PointT>*>
m_snapshot {
nullptr };
238 static constexpr size_t MAX_READERS = 16;
239 mutable std::array<std::atomic<const SpatialSnapshot<PointT>*>, MAX_READERS> m_hazard_ptrs {};
240 mutable std::atomic<size_t> m_hazard_counter { 0 };
247 std::pair<const SpatialSnapshot<PointT>*,
size_t> acquire_snapshot()
const;
252 void release_snapshot(
size_t slot)
const;
257 void retire_snapshot(
const SpatialSnapshot<PointT>* old);
259 std::vector<const SpatialSnapshot<PointT>*> m_retired;
261 std::atomic<std::shared_ptr<const SpatialSnapshot<PointT>>>
m_snapshot;
290MAYAFLUX_API std::unique_ptr<SpatialIndexND>
make_spatial_index_nd(
float cell_size, uint32_t dimensions);
void query_grid(const SpatialSnapshot< PointT > &snap, const PointT ¢er, float radius_sq, std::vector< QueryResult > &out) const
uint32_t insert(const PointT &position)
Insert a new entity at the given position.
std::vector< uint32_t > m_free_slots
void clear()
Remove all entities from the write side.
SpatialIndex & operator=(SpatialIndex &&)=delete
void query_brute(const SpatialSnapshot< PointT > &snap, const PointT ¢er, float radius_sq, std::vector< QueryResult > &out) const
void publish()
Atomically publish the current write-side state as a new read snapshot.
SpatialIndex(const SpatialIndex &)=delete
std::vector< QueryResult > k_nearest(const PointT ¢er, uint32_t k) const
Find the k nearest entities to a point.
std::optional< PointT > position_of(uint32_t id) const
Read the position of an entity from the published snapshot.
uint64_t hash_cell(const PointT &p) const
std::vector< PointT > m_positions
std::vector< std::pair< uint32_t, PointT > > all() const
Return all entity ids and positions from the published snapshot.
void rebuild_grid(SpatialSnapshot< PointT > &snap) const
std::atomic< std::shared_ptr< const SpatialSnapshot< PointT > > > m_snapshot
SpatialIndex(SpatialIndex &&)=delete
void update(uint32_t id, const PointT &position)
Move an existing entity to a new position.
size_t count() const
Entity count in the published snapshot.
SpatialIndex & operator=(const SpatialIndex &)=delete
std::vector< QueryResult > within_radius(const PointT ¢er, float radius) const
Find all entities within a radius of a point.
std::function< float(const PointT &, const PointT &)> DistanceFn
std::unordered_map< uint32_t, uint32_t > m_id_to_slot
std::vector< uint32_t > m_slot_to_id
void remove(uint32_t id)
Remove an entity from the index.
Lock-free spatial acceleration structure with atomic snapshot publication.
uint64_t hash_cell_3d(int32_t cx, int32_t cy, int32_t cz)
uint64_t hash_cell_nd(const Eigen::VectorXd &p, float inv_cell)
std::array< int32_t, 3 > cell_coords_3d(const glm::vec3 &p, float inv_cell)
std::unique_ptr< SpatialIndexND > make_spatial_index_nd(float cell_size, uint32_t dimensions)
Create an N-dimensional spatial index with Euclidean squared distance.
SpatialField distance(const glm::vec3 &anchor, float radius, DistanceMetric metric=DistanceMetric::EUCLIDEAN)
Normalized distance from an anchor point using the specified metric.
std::unique_ptr< SpatialIndex3D > make_spatial_index_3d(float cell_size)
Create a 3D spatial index with Euclidean squared distance.
Result entry from a spatial query, carrying entity id and squared distance.
std::vector< PointT > positions
Packed position storage indexed by internal slot.
uint32_t dimensions
Dimensionality (3 for glm::vec3, runtime for Eigen).
std::vector< uint32_t > slot_to_id
Maps internal slot index back to external entity id.
std::unordered_map< uint64_t, std::vector< uint32_t > > cells
Grid cell contents. Key is hashed cell coordinate.
std::unordered_map< uint32_t, uint32_t > id_to_slot
Maps external entity id to internal slot index.
float cell_size
Cell size used when this snapshot was built.
Immutable spatial snapshot published atomically for lock-free reads.