use nalgebra::Vector3;
use crate::cloud::PointCloud;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Neighbor {
pub index: u32,
pub distance_squared: f64,
}
pub fn compare_neighbors(a: &Neighbor, b: &Neighbor) -> std::cmp::Ordering {
a.distance_squared
.total_cmp(&b.distance_squared)
.then(a.index.cmp(&b.index))
}
pub trait NeighborSearch {
fn knn_into(&self, query: &Vector3<f64>, k: usize, out: &mut Vec<Neighbor>);
fn radius_into(&self, query: &Vector3<f64>, radius: f64, out: &mut Vec<Neighbor>);
fn knn(&self, query: &Vector3<f64>, k: usize) -> Vec<Neighbor> {
let mut out = Vec::with_capacity(k);
self.knn_into(query, k, &mut out);
out
}
fn radius(&self, query: &Vector3<f64>, radius: f64) -> Vec<Neighbor> {
let mut out = Vec::new();
self.radius_into(query, radius, &mut out);
out
}
}
pub struct BruteForce<'a> {
cloud: &'a PointCloud,
}
impl<'a> BruteForce<'a> {
pub fn new(cloud: &'a PointCloud) -> Self {
Self { cloud }
}
fn all_distances(&self, query: &Vector3<f64>, out: &mut Vec<Neighbor>) {
out.clear();
out.reserve(self.cloud.len());
for i in 0..self.cloud.len() {
out.push(Neighbor {
index: i as u32,
distance_squared: (self.cloud.point(i) - query).norm_squared(),
});
}
}
}
impl NeighborSearch for BruteForce<'_> {
fn knn_into(&self, query: &Vector3<f64>, k: usize, out: &mut Vec<Neighbor>) {
self.all_distances(query, out);
out.sort_unstable_by(compare_neighbors);
out.truncate(k);
}
fn radius_into(&self, query: &Vector3<f64>, radius: f64, out: &mut Vec<Neighbor>) {
let limit = radius * radius;
self.all_distances(query, out);
out.retain(|n| n.distance_squared <= limit);
out.sort_unstable_by(compare_neighbors);
}
}