conspire/geometry/grid/from/mesh/
mod.rs1#[cfg(test)]
2mod test;
3
4use crate::{
5 geometry::{grid::Voxels, mesh::Mesh},
6 math::Quantity,
7 units::Length,
8};
9use std::array::from_fn;
10
11const TETS_4: [[usize; 4]; 1] = [[0, 1, 2, 3]];
12const TETS_5: [[usize; 4]; 2] = [[0, 1, 2, 4], [0, 2, 3, 4]];
13const TETS_6: [[usize; 4]; 3] = [[0, 1, 2, 3], [1, 4, 5, 3], [1, 2, 5, 3]];
14const TETS_8: [[usize; 4]; 6] = [
15 [0, 1, 2, 6],
16 [0, 2, 3, 6],
17 [0, 3, 7, 6],
18 [0, 7, 4, 6],
19 [0, 4, 5, 6],
20 [0, 5, 1, 6],
21];
22
23impl Voxels<usize> {
24 pub fn from_finite_elements(mesh: &Mesh<3>, size: Quantity<Length>) -> Self {
25 let size = size.value();
26 let coordinates = mesh.coordinates();
27 let mut min = [f64::INFINITY; 3];
28 let mut max = [f64::NEG_INFINITY; 3];
29 for point in coordinates {
30 (0..3).for_each(|ax| {
31 min[ax] = min[ax].min(point[ax].value());
32 max[ax] = max[ax].max(point[ax].value());
33 });
34 }
35 let nel: [usize; 3] = from_fn(|ax| (((max[ax] - min[ax]) / size).ceil() as usize).max(1));
36 let [nx, ny, nz] = nel;
37 let mut data = vec![0usize; nx * ny * nz];
38 let numbers = mesh.blocks();
39 for (block, connectivity) in mesh.iter().enumerate() {
40 let material = numbers.map_or(block + 1, |numbers| numbers[block]);
41 for nodes in connectivity {
42 let tets: &[[usize; 4]] = match nodes.len() {
43 4 => &TETS_4,
44 5 => &TETS_5,
45 6 => &TETS_6,
46 8 => &TETS_8,
47 _ => continue,
48 };
49 let points: Vec<[f64; 3]> = nodes
50 .iter()
51 .map(|&node| from_fn(|ax| coordinates[node][ax].value()))
52 .collect();
53 let mut lo = [usize::MAX; 3];
54 let mut hi = [0usize; 3];
55 for point in &points {
56 (0..3).for_each(|ax| {
57 let l = ((point[ax] - min[ax]) / size).floor().max(0.0) as usize;
58 let h = (((point[ax] - min[ax]) / size).floor() as usize + 1).min(nel[ax]);
59 lo[ax] = lo[ax].min(l);
60 hi[ax] = hi[ax].max(h);
61 });
62 }
63 for k in lo[2]..hi[2] {
64 for j in lo[1]..hi[1] {
65 for i in lo[0]..hi[0] {
66 let center =
67 from_fn(|ax| min[ax] + ([i, j, k][ax] as f64 + 0.5) * size);
68 if tets
69 .iter()
70 .any(|tet| inside(center, from_fn(|vertex| points[tet[vertex]])))
71 {
72 data[i + nx * j + nx * ny * k] = material;
73 }
74 }
75 }
76 }
77 }
78 }
79 Voxels::new(data, nel)
80 }
81}
82
83fn inside(query: [f64; 3], tet: [[f64; 3]; 4]) -> bool {
84 let volume = orient(tet[0], tet[1], tet[2], tet[3]);
85 if volume == 0.0 {
86 return false;
87 }
88 let sign = volume.signum();
89 let tolerance = -1e-9 * volume.abs();
90 sign * orient(query, tet[1], tet[2], tet[3]) >= tolerance
91 && sign * orient(tet[0], query, tet[2], tet[3]) >= tolerance
92 && sign * orient(tet[0], tet[1], query, tet[3]) >= tolerance
93 && sign * orient(tet[0], tet[1], tet[2], query) >= tolerance
94}
95
96fn orient(a: [f64; 3], b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> f64 {
97 let u = from_fn::<_, 3, _>(|ax| b[ax] - a[ax]);
98 let v = from_fn::<_, 3, _>(|ax| c[ax] - a[ax]);
99 let w = from_fn::<_, 3, _>(|ax| d[ax] - a[ax]);
100 u[0] * (v[1] * w[2] - v[2] * w[1]) - u[1] * (v[0] * w[2] - v[2] * w[0])
101 + u[2] * (v[0] * w[1] - v[1] * w[0])
102}