1use std::{collections::HashMap, u16};
2
3use pasture_core::{
4 containers::{
5 BorrowedBuffer, BorrowedBufferExt, OwningBuffer, UntypedPoint, UntypedPointBuffer,
6 },
7 layout::{
8 attributes::{self, POSITION_3D},
9 PointAttributeDataType, PointAttributeDefinition, PointLayout,
10 },
11 nalgebra::Vector3,
12};
13
14use crate::bounds::calculate_bounds;
15
16pub struct Voxel {
17 pos: (usize, usize, usize),
18 points: Vec<usize>,
19}
20
21fn find_leaf(
23 p: Vector3<f64>,
24 markers_x: &[f64],
25 markers_y: &[f64],
26 markers_z: &[f64],
27) -> (usize, usize, usize) {
28 let mut index_x = 0;
29 let mut index_y = 0;
30 let mut index_z = 0;
31 while !markers_x.is_empty() && markers_x[index_x] < p.x {
32 index_x += 1;
33 }
34 while !markers_y.is_empty() && markers_y[index_y] < p.y {
35 index_y += 1;
36 }
37 while !markers_z.is_empty() && markers_z[index_z] < p.z {
38 index_z += 1;
39 }
40 if index_x > 0 && p.x - markers_x[index_x - 1] < markers_x[index_x] - p.x {
42 index_x -= 1;
43 }
44 if index_y > 0 && p.y - markers_y[index_y - 1] < markers_y[index_y] - p.y {
45 index_y -= 1;
46 }
47 if index_z > 0 && p.z - markers_z[index_z - 1] < markers_z[index_z] - p.z {
48 index_z -= 1;
49 }
50 (index_x, index_y, index_z)
51}
52
53fn create_markers_for_axis(
55 aabb: pasture_core::math::AABB<f64>,
56 leafsize_x: f64,
57 leafsize_y: f64,
58 leafsize_z: f64,
59) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
60 let mut markers_x = vec![];
61 let mut markers_y = vec![];
62 let mut markers_z = vec![];
63 let mut curr_x = aabb.min().x;
64 let mut curr_y = aabb.min().y;
65 let mut curr_z = aabb.min().z;
66 while curr_x < aabb.max().x {
67 curr_x += leafsize_x;
68 markers_x.push(curr_x);
69 }
70 while curr_y < aabb.max().y {
71 curr_y += leafsize_y;
72 markers_y.push(curr_y);
73 }
74 while curr_z < aabb.max().z {
75 curr_z += leafsize_z;
76 markers_z.push(curr_z);
77 }
78 (markers_x, markers_y, markers_z)
79}
80
81pub fn voxelgrid_filter<'a, 'b, PB: BorrowedBuffer<'a>, PBW: OwningBuffer<'b>>(
110 buffer: &'a PB,
111 leafsize_x: f64,
112 leafsize_y: f64,
113 leafsize_z: f64,
114 filtered_buffer: &'b mut PBW,
115) {
116 if !buffer
117 .point_layout()
118 .has_attribute(&attributes::POSITION_3D)
119 {
120 panic!("The PointBuffer does not have the attribute attributes::POSITION_3D which is needed for the creation of the voxel grid.");
121 }
122
123 let aabb = calculate_bounds(buffer).unwrap();
125
126 let (markers_x, markers_y, markers_z) =
128 create_markers_for_axis(aabb, leafsize_x, leafsize_y, leafsize_z);
129
130 let mut voxels: Vec<Voxel> = Vec::with_capacity(buffer.len());
131
132 for (i, p) in buffer
134 .view_attribute::<Vector3<f64>>(&POSITION_3D)
135 .into_iter()
136 .enumerate()
137 {
138 let pos = find_leaf(p, &markers_x, &markers_y, &markers_z);
140 match voxels.binary_search_by_key(&pos, |v| v.pos) {
142 Ok(index) => voxels[index].points.push(i),
144 Err(index) => voxels.insert(
146 index,
147 Voxel {
148 pos,
149 points: vec![i],
150 },
151 ),
152 }
153 }
154 for v in &mut voxels {
156 let layout = filtered_buffer.point_layout().clone();
157 let mut centroid = UntypedPointBuffer::new(&layout);
159 set_all_attributes(&layout, &mut centroid, v, buffer);
160 unsafe {
162 filtered_buffer.push_points(centroid.get_cursor().into_inner());
163 }
164 }
165}
166
167fn centroid_max_pool<'a, T: BorrowedBuffer<'a>>(
169 v: &Voxel,
170 buffer: &'a T,
171 attribute_definition: &PointAttributeDefinition,
172 point_type: PointAttributeDataType,
173) -> f64 {
174 let mut curr_max = 0.0;
175 let mut value;
176 for p in &v.points {
177 match point_type {
178 PointAttributeDataType::U8 => {
179 value = buffer.view_attribute::<u8>(attribute_definition).at(*p) as f64;
180 }
181 PointAttributeDataType::I8 => {
182 value = buffer.view_attribute::<i8>(attribute_definition).at(*p) as f64;
183 }
184 PointAttributeDataType::U16 => {
185 value = buffer.view_attribute::<u16>(attribute_definition).at(*p) as f64;
186 }
187 PointAttributeDataType::I16 => {
188 value = buffer.view_attribute::<i16>(attribute_definition).at(*p) as f64;
189 }
190 PointAttributeDataType::U32 => {
191 value = buffer.view_attribute::<u32>(attribute_definition).at(*p) as f64;
192 }
193 PointAttributeDataType::I32 => {
194 value = buffer.view_attribute::<i32>(attribute_definition).at(*p) as f64;
195 }
196 PointAttributeDataType::U64 => {
197 value = buffer.view_attribute::<u64>(attribute_definition).at(*p) as f64;
198 }
199 PointAttributeDataType::I64 => {
200 value = buffer.view_attribute::<i64>(attribute_definition).at(*p) as f64;
201 }
202 PointAttributeDataType::F32 => {
203 value = buffer.view_attribute::<f32>(attribute_definition).at(*p) as f64;
204 }
205 PointAttributeDataType::F64 => {
206 value = buffer.view_attribute::<f64>(attribute_definition).at(*p);
207 }
208 _ => unimplemented!(),
209 }
210 if value > curr_max {
211 curr_max = value;
212 }
213 }
214 curr_max
215}
216
217fn centroid_most_common<'a, T: BorrowedBuffer<'a>>(
219 v: &Voxel,
220 buffer: &'a T,
221 attribute_definition: &PointAttributeDefinition,
222 point_type: PointAttributeDataType,
223) -> isize {
224 let mut map = HashMap::new();
225 for p in &v.points {
226 match point_type {
227 PointAttributeDataType::U8 => {
228 *map.entry(
229 buffer
230 .view_attribute::<u8>(attribute_definition)
231 .at(*p)
232 .to_string(),
233 )
234 .or_insert(0) += 1
235 }
236 PointAttributeDataType::I8 => {
237 *map.entry(
238 buffer
239 .view_attribute::<i8>(attribute_definition)
240 .at(*p)
241 .to_string(),
242 )
243 .or_insert(0) += 1
244 }
245 PointAttributeDataType::U16 => {
246 *map.entry(
247 buffer
248 .view_attribute::<u16>(attribute_definition)
249 .at(*p)
250 .to_string(),
251 )
252 .or_insert(0) += 1
253 }
254 PointAttributeDataType::I16 => {
255 *map.entry(
256 buffer
257 .view_attribute::<i16>(attribute_definition)
258 .at(*p)
259 .to_string(),
260 )
261 .or_insert(0) += 1
262 }
263 PointAttributeDataType::U32 => {
264 *map.entry(
265 buffer
266 .view_attribute::<u32>(attribute_definition)
267 .at(*p)
268 .to_string(),
269 )
270 .or_insert(0) += 1
271 }
272 PointAttributeDataType::I32 => {
273 *map.entry(
274 buffer
275 .view_attribute::<i32>(attribute_definition)
276 .at(*p)
277 .to_string(),
278 )
279 .or_insert(0) += 1
280 }
281 PointAttributeDataType::U64 => {
282 *map.entry(
283 buffer
284 .view_attribute::<u64>(attribute_definition)
285 .at(*p)
286 .to_string(),
287 )
288 .or_insert(0) += 1
289 }
290 PointAttributeDataType::I64 => {
291 *map.entry(
292 buffer
293 .view_attribute::<i64>(attribute_definition)
294 .at(*p)
295 .to_string(),
296 )
297 .or_insert(0) += 1
298 }
299 PointAttributeDataType::F32 => {
300 *map.entry(
301 buffer
302 .view_attribute::<f32>(attribute_definition)
303 .at(*p)
304 .to_string(),
305 )
306 .or_insert(0) += 1
307 }
308 PointAttributeDataType::F64 => {
309 *map.entry(
310 buffer
311 .view_attribute::<f64>(attribute_definition)
312 .at(*p)
313 .to_string(),
314 )
315 .or_insert(0) += 1
316 }
317 _ => unimplemented!(),
318 }
319 }
320 let mut highest_count = 0;
321 let mut curr_key: String = "".to_string();
322 for (key, value) in map {
323 if value > highest_count {
324 highest_count = value;
325 curr_key = key;
326 }
327 }
328 curr_key.parse::<isize>().unwrap()
329}
330
331fn centroid_average_vec<'a, T: BorrowedBuffer<'a>>(
334 v: &Voxel,
335 buffer: &'a T,
336 attribute_definition: &PointAttributeDefinition,
337 point_type: PointAttributeDataType,
338) -> Vector3<f64> {
339 let mut x_sum = 0.0;
340 let mut y_sum = 0.0;
341 let mut z_sum = 0.0;
342 for p in &v.points {
343 match point_type {
344 PointAttributeDataType::Vec3u8 => {
345 let vec = buffer
346 .view_attribute::<Vector3<u8>>(attribute_definition)
347 .at(*p);
348 x_sum += vec.x as f64;
349 y_sum += vec.y as f64;
350 z_sum += vec.z as f64;
351 }
352 PointAttributeDataType::Vec3u16 => {
353 let vec = buffer
354 .view_attribute::<Vector3<u16>>(attribute_definition)
355 .at(*p);
356 x_sum += vec.x as f64;
357 y_sum += vec.y as f64;
358 z_sum += vec.z as f64;
359 }
360 PointAttributeDataType::Vec3f32 => {
361 let vec = buffer
362 .view_attribute::<Vector3<f32>>(attribute_definition)
363 .at(*p);
364 x_sum += vec.x as f64;
365 y_sum += vec.y as f64;
366 z_sum += vec.z as f64;
367 }
368 PointAttributeDataType::Vec3f64 => {
369 let vec = buffer
370 .view_attribute::<Vector3<f64>>(attribute_definition)
371 .at(*p);
372 x_sum += vec.x;
373 y_sum += vec.y;
374 z_sum += vec.z;
375 }
376 PointAttributeDataType::Vec4u8 => unimplemented!(),
377 _ => panic!("Invalid data type for centroid_average_vec"),
378 }
379 }
380
381 let num_of_points = v.points.len() as f64;
383 let centroid_x = x_sum / num_of_points;
384 let centroid_y = y_sum / num_of_points;
385 let centroid_z = z_sum / num_of_points;
386 Vector3::new(centroid_x, centroid_y, centroid_z)
387}
388
389fn centroid_average_num<'a, PB: BorrowedBuffer<'a>>(
392 v: &mut Voxel,
393 buffer: &'a PB,
394 attribute_definition: &PointAttributeDefinition,
395 point_type: PointAttributeDataType,
396) -> f64 {
397 let mut sum = 0.0;
398 for p in &v.points {
399 match point_type {
400 PointAttributeDataType::U8 => {
401 sum += buffer.view_attribute::<u8>(attribute_definition).at(*p) as f64
402 }
403 PointAttributeDataType::I8 => {
404 sum += buffer.view_attribute::<i8>(attribute_definition).at(*p) as f64
405 }
406 PointAttributeDataType::U16 => {
407 sum += buffer.view_attribute::<u16>(attribute_definition).at(*p) as f64
408 }
409 PointAttributeDataType::I16 => {
410 sum += buffer.view_attribute::<i16>(attribute_definition).at(*p) as f64
411 }
412 PointAttributeDataType::U32 => {
413 sum += buffer.view_attribute::<u32>(attribute_definition).at(*p) as f64
414 }
415 PointAttributeDataType::I32 => {
416 sum += buffer.view_attribute::<i32>(attribute_definition).at(*p) as f64
417 }
418 PointAttributeDataType::U64 => {
419 sum += buffer.view_attribute::<u64>(attribute_definition).at(*p) as f64
420 }
421 PointAttributeDataType::I64 => {
422 sum += buffer.view_attribute::<i64>(attribute_definition).at(*p) as f64
423 }
424 PointAttributeDataType::F32 => {
425 sum += buffer.view_attribute::<f32>(attribute_definition).at(*p) as f64
426 }
427 PointAttributeDataType::F64 => {
428 sum += buffer.view_attribute::<f64>(attribute_definition).at(*p)
429 }
430 PointAttributeDataType::Vec3u8 => panic!("For vector types use centroid_average_vec."),
431 PointAttributeDataType::Vec3u16 => panic!("For vector types use centroid_average_vec."),
432 PointAttributeDataType::Vec3f32 => panic!("For vector types use centroid_average_vec."),
433 PointAttributeDataType::Vec3f64 => panic!("For vector types use centroid_average_vec."),
434 PointAttributeDataType::Vec4u8 => panic!("For vector types use centroid_average_vec."),
435 _ => unimplemented!(),
436 }
437 }
438 sum / v.points.len() as f64
439}
440
441fn set_all_attributes<'a, PB: BorrowedBuffer<'a>>(
444 target_layout: &PointLayout,
445 centroid: &mut UntypedPointBuffer,
446 v: &mut Voxel,
447 buffer: &'a PB,
448) {
449 if target_layout.has_attribute(&attributes::WAVEFORM_DATA_OFFSET)
453 || target_layout.has_attribute(&attributes::WAVEFORM_PACKET_SIZE)
454 || target_layout.has_attribute(&attributes::WAVEFORM_PARAMETERS)
455 || target_layout.has_attribute(&attributes::WAVE_PACKET_DESCRIPTOR_INDEX)
456 || target_layout.has_attribute(&attributes::RETURN_POINT_WAVEFORM_LOCATION)
457 {
458 panic!("Waveform data currently not supported!");
459 }
460
461 for a in target_layout.attributes() {
462 if a.name() == attributes::POSITION_3D.name()
463 && a.datatype() == attributes::POSITION_3D.datatype()
464 {
465 let position = centroid_average_vec(v, buffer, &attributes::POSITION_3D, a.datatype());
466 let pos_slice = bytemuck::bytes_of(&position);
467 centroid
468 .set_raw_attribute(&attributes::POSITION_3D, pos_slice)
469 .unwrap();
470 } else if a.name() == attributes::INTENSITY.name()
471 && a.datatype() == attributes::INTENSITY.datatype()
472 {
473 let average =
474 centroid_average_num::<PB>(v, buffer, &attributes::INTENSITY, a.datatype()) as u16;
475 let avg_slice = bytemuck::bytes_of(&average);
476 centroid
477 .set_raw_attribute(&attributes::INTENSITY, avg_slice)
478 .unwrap();
479 } else if a.name() == attributes::RETURN_NUMBER.name()
480 && a.datatype() == attributes::RETURN_NUMBER.datatype()
481 {
482 let most_common =
483 centroid_most_common::<PB>(v, buffer, &attributes::RETURN_NUMBER, a.datatype())
484 as u8;
485 let mc_slice = bytemuck::bytes_of(&most_common);
486 centroid
487 .set_raw_attribute(&attributes::RETURN_NUMBER, mc_slice)
488 .unwrap();
489 } else if a.name() == attributes::NUMBER_OF_RETURNS.name()
490 && a.datatype() == attributes::NUMBER_OF_RETURNS.datatype()
491 {
492 let most_common =
493 centroid_most_common::<PB>(v, buffer, &attributes::NUMBER_OF_RETURNS, a.datatype())
494 as u8;
495 let mc_slice = bytemuck::bytes_of(&most_common);
496 centroid
497 .set_raw_attribute(&attributes::NUMBER_OF_RETURNS, mc_slice)
498 .unwrap();
499 } else if a.name() == attributes::CLASSIFICATION_FLAGS.name()
500 && a.datatype() == attributes::CLASSIFICATION_FLAGS.datatype()
501 {
502 let max =
503 centroid_max_pool::<PB>(v, buffer, &attributes::CLASSIFICATION_FLAGS, a.datatype())
504 as u8;
505 let m_slice = bytemuck::bytes_of(&max);
506 centroid
507 .set_raw_attribute(&attributes::CLASSIFICATION_FLAGS, m_slice)
508 .unwrap();
509 } else if a.name() == attributes::SCANNER_CHANNEL.name()
510 && a.datatype() == attributes::SCANNER_CHANNEL.datatype()
511 {
512 let most_common =
513 centroid_most_common::<PB>(v, buffer, &attributes::SCANNER_CHANNEL, a.datatype())
514 as u8;
515 let mc_slice = bytemuck::bytes_of(&most_common);
516 centroid
517 .set_raw_attribute(&attributes::SCANNER_CHANNEL, mc_slice)
518 .unwrap();
519 } else if a.name() == attributes::SCAN_DIRECTION_FLAG.name()
520 && a.datatype() == attributes::SCAN_DIRECTION_FLAG.datatype()
521 {
522 let most_common = centroid_most_common::<PB>(
523 v,
524 buffer,
525 &attributes::SCAN_DIRECTION_FLAG,
526 a.datatype(),
527 ) != 0;
528 let mc_slice = bytemuck::bytes_of(&most_common);
529 centroid
530 .set_raw_attribute(&attributes::SCAN_DIRECTION_FLAG, mc_slice)
531 .unwrap();
532 } else if a.name() == attributes::EDGE_OF_FLIGHT_LINE.name()
533 && a.datatype() == attributes::EDGE_OF_FLIGHT_LINE.datatype()
534 {
535 let most_common = centroid_most_common::<PB>(
536 v,
537 buffer,
538 &attributes::EDGE_OF_FLIGHT_LINE,
539 a.datatype(),
540 ) != 0;
541 let mc_slice = bytemuck::bytes_of(&most_common);
542 centroid
543 .set_raw_attribute(&attributes::EDGE_OF_FLIGHT_LINE, mc_slice)
544 .unwrap();
545 } else if a.name() == attributes::CLASSIFICATION.name()
546 && a.datatype() == attributes::CLASSIFICATION.datatype()
547 {
548 let most_common =
549 centroid_most_common::<PB>(v, buffer, &attributes::CLASSIFICATION, a.datatype())
550 as u8;
551 let mc_slice = bytemuck::bytes_of(&most_common);
552 centroid
553 .set_raw_attribute(&attributes::CLASSIFICATION, mc_slice)
554 .unwrap();
555 } else if a.name() == attributes::SCAN_ANGLE_RANK.name()
556 && a.datatype() == attributes::SCAN_ANGLE_RANK.datatype()
557 {
558 let most_common =
559 centroid_most_common::<PB>(v, buffer, &attributes::SCAN_ANGLE_RANK, a.datatype())
560 as i8;
561 let mc_slice = bytemuck::bytes_of(&most_common);
562 centroid
563 .set_raw_attribute(&attributes::SCAN_ANGLE_RANK, mc_slice)
564 .unwrap();
565 } else if a.name() == attributes::SCAN_ANGLE.name()
566 && a.datatype() == attributes::SCAN_ANGLE.datatype()
567 {
568 let most_common =
569 centroid_most_common::<PB>(v, buffer, &attributes::SCAN_ANGLE, a.datatype()) as i16;
570 let mc_slice = bytemuck::bytes_of(&most_common);
571 centroid
572 .set_raw_attribute(&attributes::SCAN_ANGLE, mc_slice)
573 .unwrap();
574 } else if a.name() == attributes::USER_DATA.name()
575 && a.datatype() == attributes::USER_DATA.datatype()
576 {
577 let most_common =
578 centroid_most_common::<PB>(v, buffer, &attributes::USER_DATA, a.datatype()) as u8;
579 let mc_slice = bytemuck::bytes_of(&most_common);
580 centroid
581 .set_raw_attribute(&attributes::USER_DATA, mc_slice)
582 .unwrap();
583 } else if a.name() == attributes::POINT_SOURCE_ID.name()
584 && a.datatype() == attributes::POINT_SOURCE_ID.datatype()
585 {
586 let most_common =
587 centroid_most_common::<PB>(v, buffer, &attributes::POINT_SOURCE_ID, a.datatype())
588 as u16;
589 let mc_slice = bytemuck::bytes_of(&most_common);
590 centroid
591 .set_raw_attribute(&attributes::POINT_SOURCE_ID, mc_slice)
592 .unwrap();
593 } else if a.name() == attributes::COLOR_RGB.name()
594 && a.datatype() == attributes::COLOR_RGB.datatype()
595 {
596 let color = centroid_average_vec(v, buffer, &attributes::COLOR_RGB, a.datatype());
597 let color_u16 = Vector3::new(color.x as u16, color.y as u16, color.z as u16);
598 let col_slice = bytemuck::bytes_of(&color_u16);
599 centroid
600 .set_raw_attribute(&attributes::COLOR_RGB, col_slice)
601 .unwrap();
602 } else if a.name() == attributes::GPS_TIME.name()
603 && a.datatype() == attributes::GPS_TIME.datatype()
604 {
605 let max = centroid_max_pool::<PB>(v, buffer, &attributes::GPS_TIME, a.datatype());
606 let m_slice = bytemuck::bytes_of(&max);
607 centroid
608 .set_raw_attribute(&attributes::GPS_TIME, m_slice)
609 .unwrap();
610 } else if a.name() == attributes::NIR.name() && a.datatype() == attributes::NIR.datatype() {
611 let average =
612 centroid_average_num::<PB>(v, buffer, &attributes::NIR, a.datatype()) as u16;
613 let avg_slice = bytemuck::bytes_of(&average);
614 centroid
615 .set_raw_attribute(&attributes::NIR, avg_slice)
616 .unwrap();
617
618 } else if a.name() == attributes::POINT_ID.name()
662 && a.datatype() == attributes::POINT_ID.datatype()
663 {
664 let max =
665 centroid_max_pool::<PB>(v, buffer, &attributes::POINT_ID, a.datatype()) as u64;
666 let m_slice = bytemuck::bytes_of(&max);
667 centroid
668 .set_raw_attribute(&attributes::POINT_ID, m_slice)
669 .unwrap();
670 } else if a.name() == attributes::NORMAL.name()
671 && a.datatype() == attributes::NORMAL.datatype()
672 {
673 let normal = centroid_average_vec(v, buffer, &attributes::NORMAL, a.datatype());
674 let normal_f32 = Vector3::new(normal.x as f32, normal.y as f32, normal.z as f32);
675 let nor_slice = bytemuck::bytes_of(&normal_f32);
676 centroid
677 .set_raw_attribute(&attributes::NORMAL, nor_slice)
678 .unwrap();
679 }
680 else {
683 panic!(
684 "attribute is non-standard which is not supported currently: {:?}",
685 a
686 );
687 }
688 }
689}
690
691#[cfg(test)]
692mod tests {
693
694 use crate::voxel_grid::voxelgrid_filter;
695 use pasture_core::{
696 containers::{BorrowedBuffer, BorrowedBufferExt, HashMapBuffer, MakeBufferFromLayout},
697 layout::attributes,
698 nalgebra::Vector3,
699 };
700 use pasture_derive::PointType;
701 use rand::{prelude::ThreadRng, Rng};
702
703 #[repr(C, packed)]
704 #[derive(PointType, Debug, Copy, Clone, bytemuck::AnyBitPattern, bytemuck::NoUninit)]
705 pub struct CompletePoint {
706 #[pasture(BUILTIN_POSITION_3D)]
707 pub position: Vector3<f64>,
708 #[pasture(BUILTIN_INTENSITY)]
709 pub intensity: u16,
710 #[pasture(BUILTIN_RETURN_NUMBER)]
711 pub return_number: u8,
712 #[pasture(BUILTIN_NUMBER_OF_RETURNS)]
713 pub num_of_returns: u8,
714 #[pasture(BUILTIN_CLASSIFICATION_FLAGS)]
715 pub classification_flags: u8,
716 #[pasture(BUILTIN_SCANNER_CHANNEL)]
717 pub scanner_channel: u8,
718 #[pasture(BUILTIN_SCAN_DIRECTION_FLAG)]
719 pub scan_dir_flag: u8,
720 #[pasture(BUILTIN_EDGE_OF_FLIGHT_LINE)]
721 pub edge_of_flight_line: u8,
722 #[pasture(BUILTIN_CLASSIFICATION)]
723 pub classification: u8,
724 #[pasture(BUILTIN_SCAN_ANGLE_RANK)]
725 pub scan_angle_rank: i8,
726 #[pasture(BUILTIN_SCAN_ANGLE)]
727 pub scan_angle: i16,
728 #[pasture(BUILTIN_USER_DATA)]
729 pub user_data: u8,
730 #[pasture(BUILTIN_POINT_SOURCE_ID)]
731 pub point_source_id: u16,
732 #[pasture(BUILTIN_COLOR_RGB)]
733 pub color_rgb: Vector3<u16>,
734 #[pasture(BUILTIN_GPS_TIME)]
735 pub gps_time: f64,
736 #[pasture(BUILTIN_NIR)]
737 pub nir: u16,
738 }
749
750 fn _generate_vec3f32(rng: &mut ThreadRng) -> Vector3<f32> {
751 Vector3::new(rng.gen_range(-30.0..10.0), rng.gen_range(-11.1..10.0), 31.0)
752 }
753 fn generate_vec3u16(rng: &mut ThreadRng) -> Vector3<u16> {
754 Vector3::new(rng.gen_range(11..120), rng.gen_range(11..120), 42)
755 }
756
757 fn setup_point_cloud() -> HashMapBuffer {
758 let mut rng = rand::thread_rng();
759 let mut points = vec![];
760 points.push(CompletePoint {
762 position: Vector3::new(0.0, 0.0, 0.0),
763 intensity: rng.gen_range(200..800),
764 return_number: rng.gen_range(20..80),
765 num_of_returns: rng.gen_range(20..80),
766 classification_flags: rng.gen_range(7..20),
767 scanner_channel: rng.gen_range(7..20),
768 scan_dir_flag: rng.gen_range(0..47),
769 edge_of_flight_line: rng.gen_range(0..81),
770 classification: rng.gen_range(121..200),
771 scan_angle_rank: rng.gen_range(-121..20),
772 scan_angle: rng.gen_range(-21..8),
773 user_data: rng.gen_range(1..8),
774 point_source_id: rng.gen_range(9..89),
775 color_rgb: generate_vec3u16(&mut rng),
776 gps_time: rng.gen_range(-22.4..81.3),
777 nir: rng.gen_range(4..82),
778 });
784 points.push(CompletePoint {
785 position: Vector3::new(10.0, 10.0, 10.0),
786 intensity: rng.gen_range(200..800),
787 return_number: rng.gen_range(20..80),
788 num_of_returns: rng.gen_range(20..80),
789 classification_flags: rng.gen_range(7..20),
790 scanner_channel: rng.gen_range(7..20),
791 scan_dir_flag: rng.gen_range(0..47),
792 edge_of_flight_line: rng.gen_range(0..81),
793 classification: rng.gen_range(121..200),
794 scan_angle_rank: rng.gen_range(-121..20),
795 scan_angle: rng.gen_range(-21..8),
796 user_data: rng.gen_range(1..8),
797 point_source_id: rng.gen_range(9..89),
798 color_rgb: generate_vec3u16(&mut rng),
799 gps_time: rng.gen_range(-22.4..81.3),
800 nir: rng.gen_range(4..82),
801 });
807 for i in 0..10 {
809 for j in 0..10 {
810 for k in 0..10 {
811 points.push(CompletePoint {
813 position: Vector3::new(
815 f64::from(i) + 0.5,
816 f64::from(j) + 0.5,
817 f64::from(k) + 0.5,
818 ),
819 intensity: 2,
821 return_number: 32,
823 num_of_returns: rng.gen_range(20..80),
824 classification_flags: 3,
826 scanner_channel: rng.gen_range(7..20),
827 scan_dir_flag: 0,
829 edge_of_flight_line: rng.gen_range(0..81),
830 classification: rng.gen_range(121..200),
831 scan_angle_rank: rng.gen_range(-121..20),
832 scan_angle: rng.gen_range(-21..8),
833 user_data: rng.gen_range(1..8),
834 point_source_id: rng.gen_range(9..89),
835 color_rgb: generate_vec3u16(&mut rng),
836 gps_time: rng.gen_range(-22.4..81.3),
837 nir: rng.gen_range(4..82),
838 });
844 points.push(CompletePoint {
846 position: Vector3::new(
847 f64::from(i) + 0.6,
848 f64::from(j) + 0.6,
849 f64::from(k) + 0.6,
850 ),
851 intensity: 4,
852 return_number: 42,
853 num_of_returns: rng.gen_range(20..80),
854 classification_flags: 7,
855 scanner_channel: rng.gen_range(7..20),
856 scan_dir_flag: 0,
857 edge_of_flight_line: rng.gen_range(0..81),
858 classification: rng.gen_range(121..200),
859 scan_angle_rank: rng.gen_range(-121..20),
860 scan_angle: rng.gen_range(-21..8),
861 user_data: rng.gen_range(1..8),
862 point_source_id: rng.gen_range(9..89),
863 color_rgb: generate_vec3u16(&mut rng),
864 gps_time: rng.gen_range(-22.4..81.3),
865 nir: rng.gen_range(4..82),
866 });
872 points.push(CompletePoint {
874 position: Vector3::new(
875 f64::from(i) + 0.7,
876 f64::from(j) + 0.7,
877 f64::from(k) + 0.7,
878 ),
879 intensity: 6,
880 return_number: 42,
881 num_of_returns: rng.gen_range(20..80),
882 classification_flags: 133,
883 scanner_channel: rng.gen_range(7..20),
884 scan_dir_flag: 1,
885 edge_of_flight_line: rng.gen_range(0..81),
886 classification: rng.gen_range(121..200),
887 scan_angle_rank: rng.gen_range(-121..20),
888 scan_angle: rng.gen_range(-21..8),
889 user_data: rng.gen_range(1..8),
890 point_source_id: rng.gen_range(9..89),
891 color_rgb: generate_vec3u16(&mut rng),
892 gps_time: rng.gen_range(-22.4..81.3),
893 nir: rng.gen_range(4..82),
894 });
900 }
901 }
902 }
903 points.into_iter().collect()
904 }
905
906 #[test]
907 fn test_voxel_grid_filter() {
908 let buffer = setup_point_cloud();
909 assert!(buffer.len() == 3002);
910 let mut filtered = HashMapBuffer::new_from_layout(buffer.point_layout().clone());
911 voxelgrid_filter(&buffer, 1.0, 1.0, 1.0, &mut filtered);
912 assert!(filtered.len() == 1000);
914
915 let first_pos = filtered
917 .view_attribute::<Vector3<f64>>(&attributes::POSITION_3D)
918 .at(1);
919 assert!(first_pos.x > 0.59 && first_pos.x < 0.61);
920 assert!(first_pos.y > 0.59 && first_pos.y < 0.61);
921 assert!(first_pos.z > 1.59 && first_pos.z < 1.61);
922 assert!(filtered.view_attribute::<u16>(&attributes::INTENSITY).at(1) == 4);
924 assert!(
926 filtered
927 .view_attribute::<u8>(&attributes::RETURN_NUMBER)
928 .at(1)
929 == 42
930 );
931 assert!(
933 filtered
934 .view_attribute::<u8>(&attributes::CLASSIFICATION_FLAGS)
935 .at(1)
936 == 133
937 );
938 }
939}