Skip to main content

del_msh_cpu/
mortons.rs

1use num_traits::AsPrimitive;
2
3fn expand_bits2(x: u32) -> u32 {
4    let x = (x | (x << 8)) & 0x00ff00ff;
5    let x = (x | (x << 4)) & 0x0f0f0f0f;
6    let x = (x | (x << 2)) & 0x33333333;
7    (x | (x << 1)) & 0x55555555
8}
9
10#[test]
11fn test_expand_bits2() {
12    assert_eq!(expand_bits2(0b11111111), 0b0101010101010101);
13    assert_eq!(expand_bits2(0b10001001), 0b0100000001000001);
14}
15
16/// compute morton code for 2D point
17/// 16-bits for each coordinate
18/// * `x` - float number between 0 and 1
19/// * `y` - float number between 0 and 1
20pub fn morton_code2(x: f32, y: f32) -> u32 {
21    // 2^16 = 65536
22    let ix = (x * 65536_f32).clamp(0_f32, 65535_f32) as u32;
23    let iy = (y * 65536_f32).clamp(0_f32, 65535_f32) as u32;
24    let ix = expand_bits2(ix);
25    let iy = expand_bits2(iy);
26    ix * 2 + iy
27}
28
29#[test]
30fn test_morton_code2() {
31    assert_eq!(morton_code2(0., 0.), 0u32); // all zero
32    assert_eq!(morton_code2(0., 1.), 0b01010101010101010101010101010101);
33}
34
35pub fn sorted_morten_code2<Index>(
36    idx2vtx: &mut [Index],
37    idx2morton: &mut [u32],
38    vtx2morton: &mut [u32],
39    vtx2xy: &[f32],
40    transform_xy2uni: &[f32; 9],
41) where
42    Index: num_traits::PrimInt + 'static + AsPrimitive<usize>,
43    usize: AsPrimitive<Index>,
44{
45    assert_eq!(idx2vtx.len(), idx2morton.len());
46    assert_eq!(idx2vtx.len(), vtx2morton.len());
47    assert_eq!(idx2vtx.len(), vtx2xy.len() / 2);
48    vtx2xy
49        .chunks(2)
50        .zip(vtx2morton.iter_mut())
51        .for_each(|(xy, m)| {
52            let xy = del_geo_core::mat3_col_major::transform_homogeneous(
53                transform_xy2uni,
54                &[xy[0], xy[1]],
55            )
56            .unwrap();
57            *m = morton_code2(xy[0], xy[1]);
58        });
59    idx2vtx
60        .iter_mut()
61        .enumerate()
62        .for_each(|(iv, idx)| *idx = iv.as_());
63    idx2vtx.sort_by_key(|iv| vtx2morton[(*iv).as_()]);
64    for idx in 0..idx2vtx.len() {
65        idx2morton[idx] = vtx2morton[idx2vtx[idx].as_()];
66    }
67}
68
69// above: 2D related
70// ------------------------
71// below 3D related
72
73/// Expands a 10-bit integer into 30 bits
74/// by putting two zeros before each bit
75/// "1011011111" -> "001000001001000001001001001001"
76fn expand_bits3(x: u32) -> u32 {
77    let x = (x | (x << 16)) & 0x030000FF;
78    let x = (x | (x << 8)) & 0x0300F00F;
79    let x = (x | (x << 4)) & 0x030C30C3;
80    (x | (x << 2)) & 0x09249249
81}
82
83#[test]
84fn test_expand_bits3() {
85    assert_eq!(expand_bits3(0b11111111), 0b001001001001001001001001);
86    assert_eq!(expand_bits3(0b10001001), 0b001000000000001000000001);
87}
88
89/// compute morton code for 3D point
90/// 10-bits for each coordinate
91/// * `x` - float number between 0 and 1
92/// * `y` - float number between 0 and 1
93/// * `z` - float number between 0 and 1
94pub fn morton_code3(x: f32, y: f32, z: f32) -> u32 {
95    let ix = (x * 1024_f32).clamp(0_f32, 1023_f32) as u32;
96    let iy = (y * 1024_f32).clamp(0_f32, 1023_f32) as u32;
97    let iz = (z * 1024_f32).clamp(0_f32, 1023_f32) as u32;
98    let ix = expand_bits3(ix);
99    let iy = expand_bits3(iy);
100    let iz = expand_bits3(iz);
101    ix * 4 + iy * 2 + iz
102}
103
104#[test]
105fn test_morton_code3() {
106    assert_eq!(morton_code3(0., 0., 0.), 0u32); // all zero
107    assert_eq!(morton_code3(0., 0., 1.), 0b001001001001001001001001001001);
108    assert_eq!(morton_code3(1., 0., 1.), 0b101101101101101101101101101101);
109    assert_eq!(morton_code3(1., 1., 1.), 0xFFFFFFFF >> 2);
110}
111
112// above: 3D related
113// --------------------
114
115pub fn sorted_morten_code3<Index>(
116    idx2vtx: &mut [Index],
117    idx2morton: &mut [u32],
118    vtx2morton: &mut [u32],
119    vtx2xyz: &[f32],
120    transform_xy2uni: &[f32; 16],
121) where
122    Index: num_traits::PrimInt + 'static + AsPrimitive<usize>,
123    usize: AsPrimitive<Index>,
124{
125    assert_eq!(idx2vtx.len(), idx2morton.len());
126    assert_eq!(idx2vtx.len(), vtx2morton.len());
127    assert_eq!(idx2vtx.len(), vtx2xyz.len() / 3);
128    vtx2xyz
129        .chunks(3)
130        .zip(vtx2morton.iter_mut())
131        .for_each(|(xyz, m)| {
132            let xyz = del_geo_core::mat4_col_major::transform_homogeneous(
133                transform_xy2uni,
134                &[xyz[0], xyz[1], xyz[2]],
135            )
136            .unwrap();
137            *m = morton_code3(xyz[0], xyz[1], xyz[2])
138        });
139    idx2vtx
140        .iter_mut()
141        .enumerate()
142        .for_each(|(iv, idx)| *idx = iv.as_());
143    idx2vtx.sort_by_key(|iv| vtx2morton[(*iv).as_()]);
144    for idx in 0..idx2vtx.len() {
145        idx2morton[idx] = vtx2morton[idx2vtx[idx].as_()];
146    }
147}
148
149#[test]
150fn test_sorted_morten_code() {
151    let vtx2xyz = vec![1., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 1., 0., 0., 1.];
152    let num_vtx = vtx2xyz.len() / 3;
153    let mut vtx2morton = vec![0u32; num_vtx];
154    let mut idx2morton = vec![0u32; num_vtx];
155    let mut idx2vtx = vec![0usize; num_vtx];
156    sorted_morten_code3(
157        &mut idx2vtx,
158        &mut idx2morton,
159        &mut vtx2morton,
160        &vtx2xyz,
161        &del_geo_core::mat4_col_major::from_identity(),
162    );
163    for idx in 0..num_vtx - 1 {
164        let jdx = idx + 1;
165        assert!(idx2morton[idx] <= idx2morton[jdx]);
166    }
167}
168
169// ---------------
170
171pub fn vtx2morton_from_vtx2co(
172    num_dim: usize,
173    vtx2co: &[f32],
174    transform_co2unit: &[f32],
175    vtx2morton: &mut [u32],
176) {
177    match num_dim {
178        2 => {
179            assert_eq!(transform_co2unit.len(), 9);
180            let transform_co2unit: &[f32; 9] = arrayref::array_ref![transform_co2unit, 0, 9];
181            vtx2co
182                .chunks(2)
183                .zip(vtx2morton.iter_mut())
184                .for_each(|(xy, m)| {
185                    let xy = del_geo_core::mat3_col_major::transform_homogeneous(
186                        transform_co2unit,
187                        &[xy[0], xy[1]],
188                    )
189                    .unwrap();
190                    *m = morton_code2(xy[0], xy[1]);
191                });
192        }
193        3 => {
194            assert_eq!(transform_co2unit.len(), 16);
195            let transform_co2unit: &[f32; 16] = arrayref::array_ref![transform_co2unit, 0, 16];
196            vtx2co
197                .chunks(3)
198                .zip(vtx2morton.iter_mut())
199                .for_each(|(xyz, m)| {
200                    let xyz = del_geo_core::mat4_col_major::transform_homogeneous(
201                        transform_co2unit,
202                        &[xyz[0], xyz[1], xyz[2]],
203                    )
204                    .unwrap();
205                    *m = morton_code3(xyz[0], xyz[1], xyz[2])
206                });
207        }
208        _ => {
209            panic!()
210        }
211    }
212}
213
214// ---------------
215
216fn delta(idx0: usize, idx1: usize, idx2morton: &[u32]) -> i64 {
217    (idx2morton[idx0] ^ idx2morton[idx1]).leading_zeros().into()
218}
219
220/// coverage of this node
221pub fn range_of_binary_radix_tree_node(idx2morton: &[u32], idx1: usize) -> (usize, usize) {
222    let num_mc = idx2morton.len();
223    assert!(!idx2morton.is_empty());
224    if idx1 == 0 {
225        return (0, num_mc - 1);
226    }
227    if idx1 == num_mc - 1 {
228        // this is only happen in the assertion by "check_morton_code_range_split"
229        return (num_mc - 1, num_mc - 1);
230    }
231    // ----------------------
232    let mc0: u32 = idx2morton[idx1 - 1];
233    let mc1: u32 = idx2morton[idx1];
234    let mc2: u32 = idx2morton[idx1 + 1];
235    if mc0 == mc1 && mc1 == mc2 {
236        // for hash value collision
237        let mut jdx = idx1 + 1;
238        while jdx < num_mc - 1 {
239            jdx += 1;
240            if idx2morton[jdx] != mc1 {
241                return (idx1, jdx - 1);
242            }
243        }
244        return (idx1, jdx);
245    }
246    // get direction
247    // (d==+1) -> imc is left-end, move forward
248    // (d==-1) -> imc is right-end, move backward
249    let d = delta(idx1, idx1 + 1, idx2morton) - delta(idx1, idx1 - 1, idx2morton);
250    let d: i64 = if d > 0 { 1 } else { -1 };
251
252    //compute the upper bound for the length of the range
253    let delta_min = delta(idx1, (idx1 as i64 - d) as usize, idx2morton);
254    let mut lmax: i64 = 2;
255    loop {
256        let jdx = idx1 as i64 + lmax * d;
257        if jdx < 0 || jdx >= idx2morton.len() as i64 {
258            break;
259        }
260        if delta(idx1, jdx.try_into().unwrap(), idx2morton) <= delta_min {
261            break;
262        }
263        lmax *= 2;
264    }
265
266    //find the other end using binary search
267    let l = {
268        let mut l = 0;
269        let mut t = lmax / 2;
270        while t >= 1 {
271            let jdx = idx1 as i64 + (l + t) * d;
272            if jdx >= 0
273                && jdx < idx2morton.len() as i64
274                && delta(idx1, jdx as usize, idx2morton) > delta_min
275            {
276                l += t;
277            }
278            t /= 2;
279        }
280        l
281    };
282    let jdx = (idx1 as i64 + l * d) as usize;
283    if idx1 <= jdx {
284        (idx1, jdx)
285    } else {
286        (jdx, idx1)
287    }
288}
289
290pub fn split_of_binary_radix_tree_node(
291    idx2morton: &[u32],
292    i_mc_start: usize,
293    i_mc_end: usize,
294) -> usize {
295    if i_mc_start == i_mc_end {
296        return usize::MAX;
297    }
298
299    let mc_start: u32 = idx2morton[i_mc_start];
300    let nbitcommon0: u32 = (mc_start ^ idx2morton[i_mc_end]).leading_zeros();
301
302    // handle duplicated morton code
303    if nbitcommon0 == 32 {
304        return i_mc_start;
305    } // sizeof(std::uint32_t)*8
306
307    // Use binary search to find where the next bit differs.
308    // Specifically, we are looking for the highest object that
309    // shares more than commonPrefix bits with the first one.
310    let mut i_mc_split: usize = i_mc_start; // initial guess
311    assert!(i_mc_start <= i_mc_end);
312    let mut step: usize = i_mc_end - i_mc_start;
313    while step > 1 {
314        step = step.div_ceil(2); // (step + 1) / 2; // half step
315        let i_mc_new: usize = i_mc_split + step; // proposed new position
316        if i_mc_new >= i_mc_end {
317            continue;
318        }
319        let nbitcommon1: u32 = (mc_start ^ idx2morton[i_mc_new]).leading_zeros();
320        if nbitcommon1 > nbitcommon0 {
321            i_mc_split = i_mc_new; // accept proposal
322        }
323    }
324    i_mc_split
325}
326
327/// check sorted morton codes
328/// panic if there is a bug in the sorted morton codes
329#[allow(dead_code)]
330pub fn check_morton_code_range_split(idx2morton: &[u32]) {
331    let num_vtx = idx2morton.len();
332    assert!(!idx2morton.is_empty());
333    for idx in 0..num_vtx - 1 {
334        let jdx = idx + 1;
335        assert!(idx2morton[idx] <= idx2morton[jdx]);
336    }
337    for ini in 0..idx2morton.len() - 1 {
338        let range = range_of_binary_radix_tree_node(idx2morton, ini);
339        let isplit = split_of_binary_radix_tree_node(idx2morton, range.0, range.1);
340        let range_a = range_of_binary_radix_tree_node(idx2morton, isplit);
341        let range_b = range_of_binary_radix_tree_node(idx2morton, isplit + 1);
342        assert_eq!(range.0, range_a.0);
343        assert_eq!(range.1, range_b.1);
344        let last1 = if isplit == range.0 { isplit } else { range_a.1 };
345        let first1 = if isplit + 1 == range.1 {
346            isplit + 1
347        } else {
348            range_b.0
349        };
350        assert_eq!(last1 + 1, first1);
351    }
352}
353
354pub fn update_sorted_morton_code<Index>(
355    idx2tri: &mut [Index],
356    idx2morton: &mut [u32],
357    tri2morton: &mut [u32],
358    vtx2xyz: &[f32],
359    num_dim: usize,
360) where
361    Index: num_traits::PrimInt + num_traits::AsPrimitive<usize>,
362    usize: AsPrimitive<Index>,
363{
364    match num_dim {
365        2 => {
366            let aabb = crate::vtx2xy::aabb2(vtx2xyz);
367            let transform_xy2uni =
368                del_geo_core::aabb2::to_transformation_world2unit_ortho_preserve_asp(&aabb);
369            sorted_morten_code2(idx2tri, idx2morton, tri2morton, vtx2xyz, &transform_xy2uni);
370        }
371        3 => {
372            let aabb = crate::vtx2xyz::aabb3(vtx2xyz, 0f32);
373            let transform_xy2uni =
374                del_geo_core::mat4_col_major::from_aabb3_fit_into_unit_preserve_asp(&aabb);
375            // del_geo_core::mat4_col_major::from_aabb3_fit_into_unit(&aabb);
376            sorted_morten_code3(idx2tri, idx2morton, tri2morton, vtx2xyz, &transform_xy2uni);
377        }
378        _ => {
379            panic!();
380        }
381    }
382}
383
384pub fn check_binary_radix_tree(bnodes: &[u32], idx2morton: &[u32]) {
385    let num_vtx = idx2morton.len();
386    assert_eq!(bnodes.len(), (num_vtx - 1) * 3);
387    pub fn increment_leaf_binary_radix_tree<INDEX>(
388        bvhnodes: &[INDEX],
389        i_node: usize,
390        idx2flag: &mut [usize],
391    ) where
392        INDEX: num_traits::PrimInt + num_traits::AsPrimitive<usize>,
393    {
394        let num_idx = idx2flag.len();
395        assert_eq!(bvhnodes.len(), (num_idx - 1) * 3);
396        assert!(i_node < num_idx - 1);
397        let i0_node = bvhnodes[i_node * 3 + 1].as_();
398        if i0_node >= num_idx - 1 {
399            let idx = i0_node - (num_idx - 1);
400            idx2flag[idx] += 1;
401        } else {
402            increment_leaf_binary_radix_tree(bvhnodes, i0_node, idx2flag);
403        }
404        let i1_node = bvhnodes[i_node * 3 + 2].as_();
405        if i1_node >= num_idx - 1 {
406            let idx = i1_node - (num_idx - 1);
407            idx2flag[idx] += 1;
408        } else {
409            increment_leaf_binary_radix_tree(bvhnodes, i1_node, idx2flag);
410        }
411    }
412
413    // check binary radix tree
414    let mut idx2flag = vec![0usize; num_vtx];
415    increment_leaf_binary_radix_tree(bnodes, 0, &mut idx2flag);
416    assert_eq!(idx2flag, vec!(1; num_vtx));
417    for i_branch in 0..num_vtx - 1 {
418        let i_left = bnodes[i_branch * 3 + 1] as usize;
419        let i_split = if i_left >= num_vtx - 1 {
420            i_left - (num_vtx - 1)
421        } else {
422            i_left
423        };
424        let range = range_of_binary_radix_tree_node(idx2morton, i_branch);
425        let i_split0 = split_of_binary_radix_tree_node(idx2morton, range.0, range.1);
426        assert_eq!(i_split0, i_split);
427    }
428    /*
429    for i_branch in 0..num_vtx - 1 {
430        println!("{} --> {} {} {}", i_branch, bnodes[i_branch*3], bnodes[i_branch*3+1], bnodes[i_branch*3+2]);
431    }
432     */
433}