dmc/duals.rs
1//! Defines the dual grid generation for a given octree with the [`DualGrid::from_octree`] function.
2//!
3//! For more information about what is a dual grid, check out the [`DualGrid`] struct.
4
5use crate::octree::*;
6use rayon::prelude::*;
7
8/// Type that defines a grid made from the dual edges of an octree.
9///
10/// # Visual Example
11/// [Here's an example simplified in a quadtree.](https://imgur.com/7YJrNLK)
12///
13/// # Possible Implementation
14/// [Here's a possible implementation simplified in a quadtree](https://editor.p5js.org/alexinfdev/sketches/4I0506NqA),
15/// without comments or nice code: It's meant mostly as a visual guide of what the dual grid looks
16/// like.
17pub struct DualGrid {
18 /// The cells or volumes that make up this dual grid.
19 /// Each one is composed by, at most, 8 different dual vertices.
20 /// They are always presented in the same order: The same one as defined in
21 /// [`dmc::octree::OctreeIndex`].
22 ///
23 /// # Notes
24 /// Some cells will have shared vertices, however, their topology will be the same one as a
25 /// cube, so the Marching Cubes algorithm will work over them.
26 pub volumes: Vec<[MortonKey; 8]>,
27}
28
29impl DualGrid {
30 /// Constructs a dual grid from a given octree.
31 /// You won't require to create an object of this type yourself unless you plan on processing
32 /// the duals. If you want to generate a mesh from an octree, use
33 /// [`crate::dmc::mesh_from_octree`].
34 /// # Example
35 /// ```rust
36 /// use dmc::octree::*;
37 /// use dmc::duals::*;
38 ///
39 /// let mut octree = HashedOctree::new(1);
40 /// octree.subdivide(MortonKey::root()).for_each(drop);
41 ///
42 /// assert!(octree.is_subdivided(MortonKey::root()));
43 /// assert_eq!(
44 /// octree.leaves(MortonKey::root()),
45 /// vec![
46 /// MortonKey(0b1111),
47 /// MortonKey(0b1110),
48 /// MortonKey(0b1101),
49 /// MortonKey(0b1100),
50 /// MortonKey(0b1011),
51 /// MortonKey(0b1010),
52 /// MortonKey(0b1001),
53 /// MortonKey(0b1000),
54 /// ]
55 /// );
56 ///
57 /// let duals = DualGrid::from_octree(&octree);
58 ///
59 /// assert_eq!(
60 /// duals.volumes,
61 /// vec![[
62 /// MortonKey(0b1111),
63 /// MortonKey(0b1110),
64 /// MortonKey(0b1101),
65 /// MortonKey(0b1100),
66 /// MortonKey(0b1011),
67 /// MortonKey(0b1010),
68 /// MortonKey(0b1001),
69 /// MortonKey(0b1000),
70 /// ]]
71 /// );
72 /// ```
73 pub fn from_octree<T: Copy + Send + Sync>(octree: &HashedOctree<T>) -> Self {
74 let leaves = octree.leaves(MortonKey::root());
75
76 let volumes = leaves
77 .into_par_iter()
78 .map(|leaf| {
79 // Get the vertex codes of the leaf.
80 leaf2vert(leaf)
81 })
82 .flat_map_iter(|(vertex_lv, vertex_keys)| {
83 // To obtain all the vertices and cells contained in the dual grid, we need to execute and filter
84 // everything in a precise order in order to only process each vertex exactly once.
85 std::array::IntoIter::new(vertex_keys)
86 .enumerate()
87 .take_while(|(_vertex_i, vertex_k)| vertex_k != &MortonKey::none())
88 .flat_map(move |(vertex_i, vertex_k)| {
89 // Get all the neighbours to this vertex.
90 let neighbours = vert2leaf(vertex_k, vertex_lv);
91
92 // Filter the neighbours and only accept these with the same level as this leaf.
93 std::iter::once(())
94 .take_while(move |()| {
95 for neighbour_i in 0..8 {
96 // Skip processing the node we came from!
97 if neighbour_i == vertex_i {
98 continue;
99 }
100
101 let neighbour_k = neighbours[neighbour_i];
102
103 // Skip the neighbour if the leaf `leaf` is deeper.
104 if !octree.node_exists(neighbour_k) {
105 continue;
106 }
107
108 // Skip the whole vertex if the neighbour is deeper than the leaf,
109 // since it will be processed by that neighbour.
110 if octree.is_subdivided(neighbour_k) {
111 return false;
112 }
113
114 // Neighbour has the same level as this leaf: It's a tie. Process the vertex
115 // only if neighbour_i < vertex_i.
116 if neighbour_i < vertex_i {
117 return false;
118 }
119 }
120 true
121 })
122 .map(move |()| {
123 let mut keys: [MortonKey; 8] =
124 unsafe { std::mem::MaybeUninit::uninit().assume_init() };
125 (0..8).for_each(|neighbour_i| {
126 let mut neighbour_k = neighbours[neighbour_i];
127 while neighbour_k != MortonKey::none()
128 && !octree.node_exists(neighbour_k)
129 {
130 neighbour_k = neighbour_k.parent();
131 }
132 keys[neighbour_i] = neighbour_k;
133 });
134 keys
135 })
136 })
137 })
138 .collect();
139
140 Self { volumes }
141 }
142}
143
144// Dilation constants
145const DIL_X: u64 = 0b001001001001001001001001001001001001001001001001001001001001001u64;
146const DIL_Y: u64 = 0b010010010010010010010010010010010010010010010010010010010010010u64;
147const DIL_Z: u64 = 0b100100100100100100100100100100100100100100100100100100100100100u64;
148
149/// Returns morton-like keys for the 8 vertices belonging to a leaf node, along with their level.
150fn leaf2vert(key: MortonKey) -> (u32, [MortonKey; 8]) {
151 let level = key.level();
152 let level_key: u64 = 1 << (3 * level);
153
154 let mut keys: [MortonKey; 8] = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
155 (0..8u64).into_iter().for_each(|vertex_i| {
156 // Perform the dilated integer addition of k + i.
157 let vertex_key = (((key.0 | !DIL_X) + (vertex_i & DIL_X)) & DIL_X)
158 | (((key.0 | !DIL_Y) + (vertex_i & DIL_Y)) & DIL_Y)
159 | (((key.0 | !DIL_Z) + (vertex_i & DIL_Z)) & DIL_Z);
160
161 // Check if the vertex is within the volume of the octree and not on its surface
162 // (Check for overflows)
163 keys[vertex_i as usize] = if (vertex_key >= (level_key << 1))
164 || ((vertex_key - level_key) & DIL_X) == 0
165 || ((vertex_key - level_key) & DIL_Y) == 0
166 || ((vertex_key - level_key) & DIL_Z) == 0
167 {
168 MortonKey::none()
169 } else {
170 MortonKey(vertex_key)
171 };
172 });
173
174 (level, keys)
175}
176
177/// Returns the 8 Morton keys for the adjacent nodes to a vertex, given its Morton-like key and level.
178fn vert2leaf(vertex_k: MortonKey, vertex_lv: u32) -> [MortonKey; 8] {
179 let dc = vertex_k.0 << (vertex_lv - vertex_k.level()) * 3;
180
181 let mut keys: [MortonKey; 8] = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
182 (0..8u64).into_iter().for_each(|node_i| {
183 // Perform the dilated integer substraction of dc - i.
184 keys[node_i as usize] = MortonKey(
185 (((dc & DIL_X) - (node_i & DIL_X)) & DIL_X)
186 | (((dc & DIL_Y) - (node_i & DIL_Y)) & DIL_Y)
187 | (((dc & DIL_Z) - (node_i & DIL_Z)) & DIL_Z),
188 );
189 });
190
191 keys
192}