axiolid_spatial/index.rs
1//! Zero-allocation spatial index query contract.
2
3use core::ops::ControlFlow;
4
5use axiolid_core::{Aabb, Ray3, Scalar};
6
7/// Key and bounds supplied to an index builder.
8#[derive(Debug, Clone, PartialEq)]
9pub struct SpatialItem<K> {
10 /// Caller-owned identity.
11 pub key: K,
12 /// Broad-phase bounds.
13 pub bounds: Aabb,
14}
15
16impl<K> SpatialItem<K> {
17 /// Construct one caller-keyed broad-phase item.
18 pub const fn new(key: K, bounds: Aabb) -> Self {
19 Self { key, bounds }
20 }
21}
22
23/// Ray hit returned in ascending distance order where supported.
24#[derive(Debug, Clone, PartialEq)]
25pub struct RayHit<K> {
26 /// Caller key.
27 pub key: K,
28 /// Nonnegative ray parameter.
29 pub distance: Scalar,
30}
31
32/// Read-only broad-phase query API. Callback visitation avoids allocating a
33/// result vector for large clash batches.
34pub trait SpatialIndex<K>: core::fmt::Debug + Send + Sync {
35 /// Visit keys whose stored bounds overlap `query`. Returning `Break` stops.
36 fn visit_aabb(&self, query: &Aabb, visitor: &mut dyn FnMut(&K) -> ControlFlow<()>);
37
38 /// Visit broad-phase ray candidates.
39 fn visit_ray(&self, ray: &Ray3, visitor: &mut dyn FnMut(RayHit<&K>) -> ControlFlow<()>);
40
41 /// Number of indexed items.
42 fn len(&self) -> usize;
43
44 /// Whether no items are indexed.
45 fn is_empty(&self) -> bool {
46 self.len() == 0
47 }
48}