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}