1use crate::{Error, PointCloud, PointData};
2use ecoord::HasAabb;
3use ecoord::octree::{OctantIndex, Octree, StorageMode};
4use itertools::Itertools;
5use nalgebra::Point3;
6use polars::prelude::NewChunkedArray;
7use std::collections::HashSet;
8
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct PointWithIndex {
11 index: usize,
12 point: Point3<f64>,
13}
14
15impl HasAabb for PointWithIndex {
16 fn center(&self) -> Point3<f64> {
17 self.point
18 }
19
20 fn min(&self) -> Point3<f64> {
21 self.point
22 }
23
24 fn max(&self) -> Point3<f64> {
25 self.point
26 }
27}
28
29impl PointData {
30 pub fn compute_octree(
31 &mut self,
32 max_items_per_octant: usize,
33 storage_mode: StorageMode,
34 shuffle_seed_number: Option<u64>,
35 ) -> Result<(), Error> {
36 let all_points: Vec<PointWithIndex> = self
37 .get_all_points()
38 .into_iter()
39 .enumerate()
40 .map(|(index, point)| PointWithIndex { index, point })
41 .collect();
42
43 let octree = Octree::new(
44 all_points,
45 max_items_per_octant,
46 storage_mode,
47 shuffle_seed_number,
48 )?;
49
50 let octant_indices: Vec<(usize, OctantIndex)> = octree
51 .cells()
52 .iter()
53 .flat_map(|(octant_index, points)| points.iter().map(move |p| (p.index, *octant_index)))
54 .sorted_by_key(|(i, _)| *i)
55 .collect();
56 self.add_octant_indices(octant_indices.into_iter().map(|p| p.1).collect())?;
57
58 Ok(())
59 }
60}
61
62#[derive(Debug, Clone)]
63pub struct PointCloudOctree {
64 pub point_cloud: PointCloud,
65 pub octree: Octree<PointWithIndex>,
66}
67
68impl PointCloudOctree {
69 pub fn new(
70 point_cloud: PointCloud,
71 max_points_per_octant: usize,
72 storage_mode: StorageMode,
73 shuffle_seed_number: Option<u64>,
74 ) -> Result<Self, Error> {
75 let all_points: Vec<PointWithIndex> = point_cloud
76 .point_data
77 .get_all_points()
78 .into_iter()
79 .enumerate()
80 .map(|(index, point)| PointWithIndex { index, point })
81 .collect();
82 let octree = Octree::new(
83 all_points,
84 max_points_per_octant,
85 storage_mode,
86 shuffle_seed_number,
87 )?;
88
89 let point_cloud_octree = PointCloudOctree {
90 point_cloud,
91 octree,
92 };
93 Ok(point_cloud_octree)
94 }
95
96 pub fn cell_indices(&self) -> HashSet<OctantIndex> {
98 self.octree.cell_indices()
99 }
100
101 pub fn extract_octant(&self, index: OctantIndex) -> Result<PointCloud, Error> {
102 let cell_content = self
103 .octree
104 .cell(index)
105 .ok_or(Error::NoData("selected octant"))?;
106 let indices = cell_content.iter().map(|p| p.index as u32);
107 let idx_series = polars::prelude::UInt32Chunked::from_iter_values("idx".into(), indices);
108 let filtered_data_frame = self.point_cloud.point_data.data_frame.take(&idx_series)?;
109
110 PointCloud::from_data_frame(
111 filtered_data_frame,
112 self.point_cloud.info.clone(),
113 self.point_cloud.transform_tree.clone(),
114 )
115 }
116}