Skip to main content

conspire/geometry/mesh/tessellation/base/
mod.rs

1#[cfg(test)]
2mod test;
3
4use crate::{
5    geometry::{
6        Coordinates,
7        bvh::BoundingVolumeHierarchy,
8        mesh::{
9            Connectivity, Mesh,
10            smooth::Smoothing,
11            tessellation::{D, Normals, Tessellation},
12        },
13    },
14    math::{Scalar, Tensor, TensorVec},
15};
16use std::{array::from_fn, cell::OnceCell, collections::HashMap};
17
18const WELD_TOLERANCE: Scalar = 1e-6;
19
20impl Tessellation {
21    pub fn mesh(&self) -> &Mesh<D> {
22        &self.mesh
23    }
24    pub fn normals(&self) -> &Normals {
25        &self.normals
26    }
27    pub fn bvh(&self) -> &BoundingVolumeHierarchy<D> {
28        self.bvh
29            .get_or_init(|| BoundingVolumeHierarchy::from(&self.mesh))
30    }
31    pub fn smooth(&mut self, smoothing: Smoothing) {
32        self.mesh.smooth(smoothing);
33        self.refresh();
34    }
35    pub fn smooth_welded(&mut self, smoothing: Smoothing) {
36        let mut min = [f64::INFINITY; D];
37        let mut max = [f64::NEG_INFINITY; D];
38        for point in self.mesh.coordinates() {
39            (0..D).for_each(|axis| {
40                min[axis] = min[axis].min(point[axis]);
41                max[axis] = max[axis].max(point[axis]);
42            });
43        }
44        let diagonal = (0..D)
45            .map(|axis| (max[axis] - min[axis]).powi(2))
46            .sum::<f64>()
47            .sqrt();
48        self.smooth_welded_with_tolerance(smoothing, WELD_TOLERANCE * diagonal);
49    }
50    pub(crate) fn smooth_welded_with_tolerance(&mut self, smoothing: Smoothing, tolerance: f64) {
51        let mut representatives = Vec::with_capacity(self.mesh.number_of_nodes());
52        let mut anchors: HashMap<[i64; D], Vec<usize>> = HashMap::new();
53        let mut welded = Coordinates::new();
54        for point in self.mesh.coordinates() {
55            let cell = from_fn(|axis| (point[axis] / tolerance).floor() as i64);
56            let mut representative = None;
57            'search: for dz in -1i64..=1 {
58                for dy in -1i64..=1 {
59                    for dx in -1i64..=1 {
60                        if let Some(indices) =
61                            anchors.get(&[cell[0] + dx, cell[1] + dy, cell[2] + dz])
62                        {
63                            for &index in indices {
64                                if (point - &welded[index]).norm() <= tolerance {
65                                    representative = Some(index);
66                                    break 'search;
67                                }
68                            }
69                        }
70                    }
71                }
72            }
73            representatives.push(representative.unwrap_or_else(|| {
74                let index = welded.len();
75                welded.push(point.clone());
76                anchors.entry(cell).or_default().push(index);
77                index
78            }));
79        }
80        let triangles = match &self.mesh.connectivities()[0] {
81            Connectivity::Triangular(triangles) => triangles
82                .iter()
83                .map(|&[a, b, c]| [representatives[a], representatives[b], representatives[c]])
84                .collect::<Vec<_>>(),
85            _ => panic!(),
86        };
87        let mut mesh = Mesh::from((vec![Connectivity::Triangular(triangles.into())], welded));
88        mesh.smooth(smoothing);
89        let smoothed = mesh.coordinates();
90        self.mesh
91            .coordinates
92            .iter_mut()
93            .zip(&representatives)
94            .for_each(|(point, &representative)| *point = smoothed[representative].clone());
95        self.refresh();
96    }
97    fn refresh(&mut self) {
98        self.normals = self.mesh.normals();
99        self.bvh = OnceCell::new();
100    }
101}