use crate::mesh::{MeshSnapshot, TriangleSource};
use crate::model::Bounds3;
use serde::{Deserialize, Serialize};
mod tree;
pub use tree::{AnyIndex, TreeIndex, TreeOptions};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ProjectionMode {
#[default]
Xy,
Xz,
Yz,
Xyz,
}
impl ProjectionMode {
pub fn overlaps(self, a: &Bounds3, b: &Bounds3) -> bool {
let x = a.min.x <= b.max.x && b.min.x <= a.max.x;
let y = a.min.y <= b.max.y && b.min.y <= a.max.y;
let z = a.min.z <= b.max.z && b.min.z <= a.max.z;
match self {
ProjectionMode::Xy => x && y,
ProjectionMode::Xz => x && z,
ProjectionMode::Yz => y && z,
ProjectionMode::Xyz => x && y && z,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct IndexStats {
pub triangle_count: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub node_count: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub leaf_count: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_depth: Option<usize>,
pub implementation: String,
}
pub trait TriangleIndex: Sync {
fn query_aabb(&self, query: &Bounds3, mode: ProjectionMode, out: &mut Vec<u32>);
fn stats(&self) -> IndexStats;
fn query(&self, query: &Bounds3, mode: ProjectionMode) -> Vec<u32> {
let mut out = Vec::new();
self.query_aabb(query, mode, &mut out);
out
}
fn query_cutter_xy(&self, x: f64, y: f64, radius: f64, out: &mut Vec<u32>) {
let query = Bounds3::new(
crate::model::Point3::new(x - radius, y - radius, f64::NEG_INFINITY),
crate::model::Point3::new(x + radius, y + radius, f64::INFINITY),
);
self.query_aabb(&query, ProjectionMode::Xy, out);
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct BruteForceIndex {
bounds: Vec<Bounds3>,
}
impl BruteForceIndex {
pub fn build(mesh: &dyn TriangleSource) -> Self {
let bounds = (0..mesh.triangle_count()).map(|i| mesh.triangle(i as u32).bounds).collect();
Self { bounds }
}
pub fn from_snapshot(mesh: &MeshSnapshot) -> Self {
Self::build(mesh)
}
}
impl TriangleIndex for BruteForceIndex {
fn query_aabb(&self, query: &Bounds3, mode: ProjectionMode, out: &mut Vec<u32>) {
for (i, b) in self.bounds.iter().enumerate() {
if mode.overlaps(b, query) {
out.push(i as u32);
}
}
}
fn stats(&self) -> IndexStats {
IndexStats {
triangle_count: self.bounds.len(),
node_count: None,
leaf_count: None,
max_depth: None,
implementation: "brute-force".to_string(),
}
}
}