conspire/geometry/ntree/sizing/
mod.rs1#[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
22pub 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}