1use crate::mesh::{MeshSnapshot, TriangleSource};
11use crate::model::Bounds3;
12use serde::{Deserialize, Serialize};
13
14mod tree;
15
16pub use tree::{AnyIndex, TreeIndex, TreeOptions};
17
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
20#[serde(rename_all = "lowercase")]
21pub enum ProjectionMode {
22 #[default]
24 Xy,
25 Xz,
27 Yz,
29 Xyz,
31}
32
33impl ProjectionMode {
34 pub fn overlaps(self, a: &Bounds3, b: &Bounds3) -> bool {
36 let x = a.min.x <= b.max.x && b.min.x <= a.max.x;
37 let y = a.min.y <= b.max.y && b.min.y <= a.max.y;
38 let z = a.min.z <= b.max.z && b.min.z <= a.max.z;
39 match self {
40 ProjectionMode::Xy => x && y,
41 ProjectionMode::Xz => x && z,
42 ProjectionMode::Yz => y && z,
43 ProjectionMode::Xyz => x && y && z,
44 }
45 }
46}
47
48#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub struct IndexStats {
52 pub triangle_count: usize,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub node_count: Option<usize>,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub leaf_count: Option<usize>,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub max_depth: Option<usize>,
59 pub implementation: String,
61}
62
63pub trait TriangleIndex: Sync {
67 fn query_aabb(&self, query: &Bounds3, mode: ProjectionMode, out: &mut Vec<u32>);
71
72 fn stats(&self) -> IndexStats;
73
74 fn query(&self, query: &Bounds3, mode: ProjectionMode) -> Vec<u32> {
76 let mut out = Vec::new();
77 self.query_aabb(query, mode, &mut out);
78 out
79 }
80
81 fn query_cutter_xy(&self, x: f64, y: f64, radius: f64, out: &mut Vec<u32>) {
84 let query = Bounds3::new(
85 crate::model::Point3::new(x - radius, y - radius, f64::NEG_INFINITY),
86 crate::model::Point3::new(x + radius, y + radius, f64::INFINITY),
87 );
88 self.query_aabb(&query, ProjectionMode::Xy, out);
89 }
90}
91
92#[derive(Clone, Debug, Default, PartialEq)]
95pub struct BruteForceIndex {
96 bounds: Vec<Bounds3>,
97}
98
99impl BruteForceIndex {
100 pub fn build(mesh: &dyn TriangleSource) -> Self {
101 let bounds = (0..mesh.triangle_count()).map(|i| mesh.triangle(i as u32).bounds).collect();
102 Self { bounds }
103 }
104
105 pub fn from_snapshot(mesh: &MeshSnapshot) -> Self {
106 Self::build(mesh)
107 }
108}
109
110impl TriangleIndex for BruteForceIndex {
111 fn query_aabb(&self, query: &Bounds3, mode: ProjectionMode, out: &mut Vec<u32>) {
112 for (i, b) in self.bounds.iter().enumerate() {
113 if mode.overlaps(b, query) {
114 out.push(i as u32);
115 }
116 }
117 }
118
119 fn stats(&self) -> IndexStats {
120 IndexStats {
121 triangle_count: self.bounds.len(),
122 node_count: None,
123 leaf_count: None,
124 max_depth: None,
125 implementation: "brute-force".to_string(),
126 }
127 }
128}
129