#![forbid(unsafe_code)]
#![deny(missing_docs, missing_debug_implementations)]
mod look_up;
mod nearest;
mod sort;
pub use look_up::{Query, WithinBoundingBox, WithinDistance};
use std::marker::PhantomData;
use std::ops::Deref;
use num_traits::Num;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait Point {
const DIM: usize;
type Coord: Num + Copy + PartialOrd;
fn coord(&self, axis: usize) -> Self::Coord;
}
pub trait Distance: Point {
fn distance_2(&self, other: &Self) -> Self::Coord;
}
impl<T, const N: usize> Point for [T; N]
where
T: Num + Copy + PartialOrd,
{
const DIM: usize = N;
type Coord = T;
fn coord(&self, axis: usize) -> Self::Coord {
self[axis]
}
}
impl<T, const N: usize> Distance for [T; N]
where
T: Num + Copy + PartialOrd,
{
fn distance_2(&self, other: &Self) -> Self::Coord {
(0..N).fold(T::zero(), |res, axis| {
let diff = self[axis] - other[axis];
res + diff * diff
})
}
}
pub trait Object {
type Point: Point;
fn position(&self) -> &Self::Point;
}
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct KdTree<O, S = Box<[O]>>
where
S: AsRef<[O]>,
{
objects: S,
_marker: PhantomData<O>,
}
impl<O, S> KdTree<O, S>
where
O: Object,
S: AsRef<[O]>,
{
pub fn new_unchecked(objects: S) -> Self {
Self {
objects,
_marker: PhantomData,
}
}
}
impl<O, S> Deref for KdTree<O, S>
where
S: AsRef<[O]>,
{
type Target = [O];
fn deref(&self) -> &Self::Target {
self.objects.as_ref()
}
}
impl<O, S> AsRef<[O]> for KdTree<O, S>
where
S: AsRef<[O]>,
{
fn as_ref(&self) -> &[O] {
self.objects.as_ref()
}
}
fn split<O>(objects: &[O]) -> (&[O], &O, &[O]) {
let (left, objects) = objects.split_at(objects.len() / 2);
let (mid, right) = objects.split_first().unwrap();
(left, mid, right)
}
fn contains<P>(aabb: &(P, P), position: &P) -> bool
where
P: Point,
{
(0..P::DIM).all(|axis| {
aabb.0.coord(axis) <= position.coord(axis) && position.coord(axis) <= aabb.1.coord(axis)
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::cmp::Ordering;
use proptest::{collection::vec, strategy::Strategy};
pub fn random_points(len: usize) -> impl Strategy<Value = Vec<[f32; 2]>> {
(vec(0.0_f32..=1.0, len), vec(0.0_f32..=1.0, len))
.prop_map(|(x, y)| x.into_iter().zip(y).map(|(x, y)| [x, y]).collect())
}
#[derive(Debug, PartialEq)]
pub struct RandomObject(pub [f32; 2]);
impl Eq for RandomObject {}
impl PartialOrd for RandomObject {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for RandomObject {
fn cmp(&self, other: &Self) -> Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
impl Object for RandomObject {
type Point = [f32; 2];
fn position(&self) -> &Self::Point {
&self.0
}
}
pub fn random_objects(len: usize) -> impl Strategy<Value = Box<[RandomObject]>> {
random_points(len).prop_map(|points| points.into_iter().map(RandomObject).collect())
}
}