epoint_transform/
transform.rs1use epoint_core::PointCloud;
2use std::collections::HashSet;
3
4use crate::Error;
5use crate::Error::InvalidNumber;
6use epoint_core::PointDataColumnType;
7use nalgebra::{Isometry3, Point3, Vector3};
8use rand::{Rng, SeedableRng};
9use rand_chacha::ChaCha8Rng;
10use rayon::prelude::*;
11
12pub fn translate(point_cloud: &PointCloud, translation: Vector3<f64>) -> Result<PointCloud, Error> {
13 let mut translated_data = point_cloud.point_data.data_frame.clone();
14 translated_data.apply(PointDataColumnType::X.as_str(), |x| x + translation.x)?;
15 translated_data.apply(PointDataColumnType::Y.as_str(), |y| y + translation.y)?;
16 translated_data.apply(PointDataColumnType::Z.as_str(), |z| z + translation.z)?;
17
18 if point_cloud.contains_sensor_translation() {
19 translated_data.apply(PointDataColumnType::SensorTranslationX.as_str(), |x| {
20 x + translation.x
21 })?;
22 translated_data.apply(PointDataColumnType::SensorTranslationY.as_str(), |y| {
23 y + translation.y
24 })?;
25 translated_data.apply(PointDataColumnType::SensorTranslationZ.as_str(), |z| {
26 z + translation.z
27 })?;
28 }
29
30 let info = point_cloud.info().clone();
31 let frames = point_cloud.reference_frames().clone();
32 let point_cloud = PointCloud::from_data_frame(translated_data, info, frames)?;
33 Ok(point_cloud)
34}
35
36pub fn apply_isometry(
37 point_cloud: &PointCloud,
38 isometry: Isometry3<f64>,
39) -> Result<PointCloud, Error> {
40 let transformed_points: Vec<Point3<f64>> = point_cloud
41 .point_data
42 .get_all_points()
43 .par_iter()
44 .map(|p| isometry * p)
45 .collect();
46 let mut transformed_point_cloud = point_cloud.clone();
47 transformed_point_cloud
48 .point_data
49 .update_points_in_place(transformed_points)?;
50
51 if let Ok(all_sensor_translations) = point_cloud.point_data.get_all_sensor_translations() {
52 let transformed_sensor_translations: Vec<Point3<f64>> = all_sensor_translations
53 .par_iter()
54 .map(|p| isometry * p)
55 .collect();
56
57 transformed_point_cloud
58 .point_data
59 .update_sensor_translations_in_place(transformed_sensor_translations)?;
60 }
61
62 Ok(transformed_point_cloud)
63}
64
65pub fn deterministic_downsample(
66 point_cloud: &PointCloud,
67 target_size: usize,
68 seed_number: Option<u64>,
69) -> Result<PointCloud, Error> {
70 if point_cloud.size() < target_size {
71 return Ok(point_cloud.clone());
72 }
73
74 let rng = ChaCha8Rng::seed_from_u64(seed_number.unwrap_or_default());
75 let row_indices = generate_random_numbers(rng, point_cloud.size(), target_size)?;
76
77 let downsampled_point_cloud = point_cloud.filter_by_row_indices(row_indices)?;
78 Ok(downsampled_point_cloud)
79}
80
81fn generate_random_numbers(
82 mut rng: ChaCha8Rng,
83 number_max: usize,
84 len: usize,
85) -> Result<HashSet<usize>, Error> {
86 if number_max < len {
87 return Err(InvalidNumber);
88 }
89
90 let mut numbers: HashSet<usize> = HashSet::with_capacity(len);
91 while numbers.len() < len {
92 let n: usize = rng.random_range(0..number_max);
93 numbers.insert(n);
94 }
95 Ok(numbers)
96}