Skip to main content

conspire/geometry/ntree/sizing/
mod.rs

1#[cfg(test)]
2mod test;
3
4pub mod curvature;
5
6use crate::{
7    geometry::{
8        Coordinate, Coordinates,
9        mesh::{
10            Tessellation,
11            differential::sizing::{Creases, Unresolved, sizing_field},
12        },
13        ntree::{node::cell::Cell, sizing::curvature::CurvatureSizing},
14    },
15    math::{Quantity, Scalar, Tensor, TensorVec},
16    units::Length,
17};
18use std::{array::from_fn, f64::consts::FRAC_PI_4};
19
20const D: usize = 3;
21
22/// Parameters controlling octree refinement.
23pub struct Sizing<'a> {
24    pub(crate) center: Coordinate<D>,
25    pub(crate) coordinates: &'a Coordinates<D>,
26    pub(crate) elements: Vec<&'a [usize]>,
27    pub(crate) levels: u32,
28    pub(crate) min_length: Quantity<Length>,
29    pub(crate) scale: Scalar,
30    pub(crate) targets: Vec<Quantity<Length>>,
31}
32
33impl<'a> Sizing<'a> {
34    pub(crate) fn fits<T: Cell>(&self) -> bool {
35        1_usize
36            .checked_shl(self.levels)
37            .and_then(T::length)
38            .is_some()
39    }
40    pub fn levels(&self) -> u32 {
41        self.levels
42    }
43    pub fn new(
44        tessellation: &'a Tessellation,
45        scale: Scalar,
46        curvature: CurvatureSizing,
47        padding: u16,
48    ) -> Self {
49        let CurvatureSizing {
50            tolerance,
51            gradation,
52            floor_fraction,
53        } = curvature;
54        let sdf = tessellation.shape_diameter_function(FRAC_PI_4, 3, 10);
55        let coordinates = tessellation.mesh().coordinates();
56        if coordinates.is_empty() {
57            return Self {
58                center: Coordinate::const_from([0.0; D]),
59                coordinates,
60                elements: Vec::new(),
61                levels: 0,
62                min_length: Quantity::new(1.0),
63                scale,
64                targets: Vec::new(),
65            };
66        }
67        let mut min_coord = [Quantity::<Length>::new(Scalar::INFINITY); D];
68        let mut max_coord = [Quantity::<Length>::new(Scalar::NEG_INFINITY); D];
69        for point in coordinates {
70            for ax in 0..D {
71                min_coord[ax] = min_coord[ax].min(point[ax]);
72                max_coord[ax] = max_coord[ax].max(point[ax]);
73            }
74        }
75        let max_extent = (0..D)
76            .map(|ax| max_coord[ax] - min_coord[ax])
77            .fold(Quantity::default(), Quantity::max);
78        let min_sdf = sdf
79            .iter()
80            .copied()
81            .filter(|&value| value > Quantity::default())
82            .fold(Quantity::new(Scalar::INFINITY), Quantity::min);
83        let elements: Vec<&[usize]> = tessellation
84            .mesh()
85            .connectivities()
86            .iter()
87            .flatten()
88            .collect();
89        let triangles: Vec<[usize; 3]> = elements
90            .iter()
91            .map(|element| from_fn(|i| element[i]))
92            .collect();
93        let curvature = match tolerance {
94            Some(tolerance) => sizing_field(
95                &triangles,
96                coordinates,
97                tolerance,
98                max_extent * floor_fraction,
99                max_extent,
100                gradation,
101                Unresolved::Radius,
102                Creases::Excluded,
103            ),
104            None => vec![max_extent; coordinates.len()],
105        };
106        let min_curvature = curvature
107            .iter()
108            .copied()
109            .fold(Quantity::new(Scalar::INFINITY), Quantity::min);
110        let thickness_length = if min_sdf.value().is_finite() {
111            min_sdf / scale
112        } else {
113            max_extent
114        };
115        let min_length = thickness_length.min(min_curvature);
116        let zero = Quantity::default();
117        let levels = if max_extent <= zero || min_length <= zero {
118            0u32
119        } else {
120            (max_extent / min_length + 2.0 * padding as Scalar)
121                .log2()
122                .ceil()
123                .max(Quantity::default())
124                .value() as u32
125        };
126        let targets: Vec<Quantity<Length>> = elements
127            .iter()
128            .map(|element| {
129                let thickness = sdf[element[0]].min(sdf[element[1]]).min(sdf[element[2]]);
130                let feature = curvature[element[0]]
131                    .min(curvature[element[1]])
132                    .min(curvature[element[2]])
133                    * scale;
134                thickness.min(feature)
135            })
136            .collect();
137        Self {
138            center: Coordinate::<D>::from(from_fn::<_, D, _>(|ax| {
139                (min_coord[ax] + max_coord[ax]) / 2.0
140            })),
141            coordinates,
142            elements,
143            levels,
144            min_length,
145            scale,
146            targets,
147        }
148    }
149}