1use std::ops::Range;
2
3use crate::{
4 containers::{
5 BorrowedBuffer, BorrowedMutBuffer, ColumnarBuffer, ColumnarBufferMut, InterleavedBuffer,
6 InterleavedBufferMut, MakeBufferFromLayout, OwningBuffer,
7 },
8 layout::{PointAttributeDefinition, PointAttributeMember, PointLayout, PrimitiveType},
9};
10
11use super::{get_generic_converter, AttributeConversionFn};
12
13type AttributeTransformFn = Box<dyn Fn(&mut [u8])>;
15
16fn to_untyped_transform_fn<T: PrimitiveType, F: Fn(T) -> T + 'static>(
18 transform_fn: F,
19) -> AttributeTransformFn {
20 let untyped_transform_fn = move |attribute_memory: &mut [u8]| {
21 let attribute_ptr_typed = attribute_memory.as_mut_ptr() as *mut T;
22 unsafe {
25 let attribute_value = attribute_ptr_typed.read_unaligned();
26 let transformed_value = transform_fn(attribute_value);
27 attribute_ptr_typed.write_unaligned(transformed_value);
28 }
29 };
30 Box::new(untyped_transform_fn)
31}
32
33pub struct Transformation {
34 func: AttributeTransformFn,
35 apply_to_source_attribute: bool,
36}
37
38pub struct AttributeMapping<'a> {
42 target_attribute: &'a PointAttributeMember,
43 source_attribute: &'a PointAttributeMember,
44 converter: Option<AttributeConversionFn>,
45 transformation: Option<Transformation>,
46}
47
48impl<'a> AttributeMapping<'a> {
49 pub(crate) fn required_buffer_size(&self) -> usize {
51 self.source_attribute
52 .size()
53 .max(self.target_attribute.size()) as usize
54 }
55}
56
57pub struct BufferLayoutConverter<'a> {
99 from_layout: &'a PointLayout,
100 to_layout: &'a PointLayout,
101 mappings: Vec<AttributeMapping<'a>>,
102}
103
104impl<'a> BufferLayoutConverter<'a> {
105 pub fn for_layouts(from_layout: &'a PointLayout, to_layout: &'a PointLayout) -> Self {
113 let default_mappings = to_layout.attributes().map(|to_attribute| {
114 let from_attribute = from_layout.get_attribute_by_name(to_attribute.attribute_definition().name()).expect("Attribute not found in `from_layout`! When calling `BufferLayoutConverter::for_layouts`, the source PointLayout must contain all attributes from the target PointLayout. If you want to use default values for attributes that are not present in the source layout, use `BufferLayoutConverter::for_layouts_with_default` instead!");
115 Self::make_default_mapping(from_attribute, to_attribute)
116 }).collect();
117 Self {
118 from_layout,
119 to_layout,
120 mappings: default_mappings,
121 }
122 }
123
124 pub fn for_layouts_with_default(
127 from_layout: &'a PointLayout,
128 to_layout: &'a PointLayout,
129 ) -> Self {
130 let default_mappings = to_layout
131 .attributes()
132 .filter_map(|to_attribute| {
133 from_layout
134 .get_attribute_by_name(to_attribute.attribute_definition().name())
135 .map(|from_attribute| Self::make_default_mapping(from_attribute, to_attribute))
136 })
137 .collect();
138 Self {
139 from_layout,
140 to_layout,
141 mappings: default_mappings,
142 }
143 }
144
145 pub fn set_custom_mapping(
157 &mut self,
158 from_attribute: &PointAttributeDefinition,
159 to_attribute: &PointAttributeDefinition,
160 ) {
161 let from_attribute_member = self
162 .from_layout
163 .get_attribute(from_attribute)
164 .expect("from_attribute not found in source PointLayout");
165 let to_attribute_member = self
166 .to_layout
167 .get_attribute(to_attribute)
168 .expect("to_attribute not found in target PointLayout");
169
170 if let Some(previous_mapping) = self
171 .mappings
172 .iter_mut()
173 .find(|mapping| mapping.target_attribute.attribute_definition() == to_attribute)
174 {
175 *previous_mapping =
176 Self::make_default_mapping(from_attribute_member, to_attribute_member);
177 } else {
178 self.mappings.push(Self::make_default_mapping(
179 from_attribute_member,
180 to_attribute_member,
181 ));
182 }
183 }
184
185 pub fn set_custom_mapping_with_transformation<T: PrimitiveType, F: Fn(T) -> T + 'static>(
195 &mut self,
196 from_attribute: &PointAttributeDefinition,
197 to_attribute: &PointAttributeDefinition,
198 transform_fn: F,
199 apply_to_source_attribute: bool,
200 ) {
201 let from_attribute_member = self
202 .from_layout
203 .get_attribute(from_attribute)
204 .expect("from_attribute not found in source PointLayout");
205 let to_attribute_member = self
206 .to_layout
207 .get_attribute(to_attribute)
208 .expect("to_attribute not found in target PointLayout");
209 if apply_to_source_attribute {
210 assert_eq!(T::data_type(), from_attribute_member.datatype());
211 } else {
212 assert_eq!(T::data_type(), to_attribute_member.datatype());
213 }
214
215 if let Some(previous_mapping) = self
216 .mappings
217 .iter_mut()
218 .find(|mapping| mapping.target_attribute.attribute_definition() == to_attribute)
219 {
220 *previous_mapping = Self::make_transformed_mapping(
221 from_attribute_member,
222 to_attribute_member,
223 transform_fn,
224 apply_to_source_attribute,
225 );
226 } else {
227 self.mappings.push(Self::make_transformed_mapping(
228 from_attribute_member,
229 to_attribute_member,
230 transform_fn,
231 apply_to_source_attribute,
232 ));
233 }
234 }
235
236 pub fn convert<
243 'b,
244 'c,
245 'd,
246 OutBuffer: OwningBuffer<'c> + MakeBufferFromLayout<'c> + 'c,
247 InBuffer: BorrowedBuffer<'b>,
248 >(
249 &self,
250 source_buffer: &'d InBuffer,
251 ) -> OutBuffer
252 where
253 'b: 'd,
254 {
255 let mut target_buffer = OutBuffer::new_from_layout(self.to_layout.clone());
256 target_buffer.resize(source_buffer.len());
257 self.convert_into(source_buffer, &mut target_buffer);
258 target_buffer
259 }
260
261 pub fn convert_into<'b, 'c, 'd, 'e>(
269 &self,
270 source_buffer: &'c impl BorrowedBuffer<'b>,
271 target_buffer: &'e mut impl BorrowedMutBuffer<'d>,
272 ) where
273 'b: 'c,
274 'd: 'e,
275 {
276 let source_range = 0..source_buffer.len();
277 self.convert_into_range(
278 source_buffer,
279 source_range.clone(),
280 target_buffer,
281 source_range,
282 );
283 }
284
285 pub fn convert_into_range<'b, 'c, 'd, 'e>(
293 &self,
294 source_buffer: &'c impl BorrowedBuffer<'b>,
295 source_range: Range<usize>,
296 target_buffer: &'e mut impl BorrowedMutBuffer<'d>,
297 target_range: Range<usize>,
298 ) where
299 'b: 'c,
300 'd: 'e,
301 {
302 assert_eq!(source_buffer.point_layout(), self.from_layout);
303 assert_eq!(target_buffer.point_layout(), self.to_layout);
304 assert!(source_range.len() == target_range.len());
305 assert!(source_range.end <= source_buffer.len());
306 assert!(target_range.end <= target_buffer.len());
307
308 let max_attribute_size = self
309 .mappings
310 .iter()
311 .map(|mapping| mapping.required_buffer_size())
312 .max();
313 if let Some(max_attribute_size) = max_attribute_size {
314 match (source_buffer.as_columnar(), target_buffer.as_columnar_mut()) {
317 (Some(source_buffer), Some(target_buffer)) => {
318 self.convert_columnar_to_columnar(
319 source_buffer,
320 source_range,
321 target_buffer,
322 target_range,
323 );
324 }
325 (Some(source_buffer), None) => {
326 self.convert_columnar_to_interleaved(
327 source_buffer,
328 source_range,
329 target_buffer.as_interleaved_mut().expect(
330 "Target buffer must either be an interleaved or columnar buffer",
331 ),
332 target_range,
333 );
334 }
335 (None, Some(target_buffer)) => {
336 self.convert_interleaved_to_columnar(
337 source_buffer.as_interleaved().expect(
338 "Source buffer must either be an interleaved or columnar buffer",
339 ),
340 source_range,
341 target_buffer,
342 target_range,
343 max_attribute_size,
344 );
345 }
346 (None, None) => self.convert_interleaved_to_interleaved(
347 source_buffer
348 .as_interleaved()
349 .expect("Source buffer must either be an interleaved or columnar buffer"),
350 source_range,
351 target_buffer
352 .as_interleaved_mut()
353 .expect("Target buffer must either be an interleaved or columnar buffer"),
354 target_range,
355 max_attribute_size,
356 ),
357 }
358 }
359 }
360
361 fn make_default_mapping(
369 from_attribute: &'a PointAttributeMember,
370 to_attribute: &'a PointAttributeMember,
371 ) -> AttributeMapping<'a> {
372 if from_attribute.datatype() == to_attribute.datatype() {
373 AttributeMapping {
374 target_attribute: to_attribute,
375 source_attribute: from_attribute,
376 converter: None,
377 transformation: None,
378 }
379 } else {
380 let from_datatype = from_attribute.datatype();
381 let to_datatype = to_attribute.datatype();
382 let converter =
383 get_generic_converter(from_datatype, to_datatype).unwrap_or_else(|| {
384 panic!(
385 "No conversion from {} to {} possible",
386 from_datatype, to_datatype
387 )
388 });
389 AttributeMapping {
390 target_attribute: to_attribute,
391 source_attribute: from_attribute,
392 converter: Some(converter),
393 transformation: None,
394 }
395 }
396 }
397
398 fn make_transformed_mapping<T: PrimitiveType>(
405 from_attribute: &'a PointAttributeMember,
406 to_attribute: &'a PointAttributeMember,
407 transform_fn: impl Fn(T) -> T + 'static,
408 apply_to_source_attribute: bool,
409 ) -> AttributeMapping<'a> {
410 let mut mapping = Self::make_default_mapping(from_attribute, to_attribute);
411 mapping.transformation = Some(Transformation {
412 func: to_untyped_transform_fn(transform_fn),
413 apply_to_source_attribute,
414 });
415 mapping
416 }
417
418 fn convert_columnar_to_columnar(
419 &self,
420 source_buffer: &dyn ColumnarBuffer,
421 source_range: Range<usize>,
422 target_buffer: &mut dyn ColumnarBufferMut,
423 target_range: Range<usize>,
424 ) {
425 for mapping in &self.mappings {
426 let source_attribute_data = source_buffer.get_attribute_range_ref(
427 mapping.source_attribute.attribute_definition(),
428 source_range.clone(),
429 );
430 if let Some(converter) = mapping.converter {
431 let target_attribute_data = target_buffer.get_attribute_range_mut(
432 mapping.target_attribute.attribute_definition(),
433 target_range.clone(),
434 );
435 let source_attribute_size = mapping.source_attribute.size() as usize;
436 let target_attribute_size = mapping.target_attribute.size() as usize;
437 let mut source_tmp_buffer: Vec<u8> = vec![0; source_attribute_size];
438 for (source_chunk, target_chunk) in source_attribute_data
439 .chunks_exact(source_attribute_size)
440 .zip(target_attribute_data.chunks_exact_mut(target_attribute_size))
441 {
442 unsafe {
445 if let Some(transformation) = mapping.transformation.as_ref() {
446 if transformation.apply_to_source_attribute {
449 source_tmp_buffer.copy_from_slice(source_chunk);
450 (transformation.func)(&mut source_tmp_buffer[..]);
451 converter(&source_tmp_buffer[..], target_chunk);
452 } else {
453 converter(source_chunk, target_chunk);
454 (transformation.func)(target_chunk);
455 }
456 } else {
457 converter(source_chunk, target_chunk);
458 }
459 }
460 }
461 } else {
462 unsafe {
465 target_buffer.set_attribute_range(
466 mapping.target_attribute.attribute_definition(),
467 target_range.clone(),
468 source_attribute_data,
469 );
470 }
471 if let Some(transformation) = mapping.transformation.as_ref() {
472 let target_attribute_range = target_buffer.get_attribute_range_mut(
475 mapping.target_attribute.attribute_definition(),
476 target_range.clone(),
477 );
478 let target_attribute_size = mapping.target_attribute.size() as usize;
479 for target_chunk in
480 target_attribute_range.chunks_exact_mut(target_attribute_size)
481 {
482 (transformation.func)(target_chunk);
483 }
484 }
485 }
486 }
487 }
488
489 fn convert_columnar_to_interleaved<'b, B: InterleavedBufferMut<'b> + ?Sized>(
490 &self,
491 source_buffer: &dyn ColumnarBuffer,
492 source_range: Range<usize>,
493 target_buffer: &mut B,
494 target_range: Range<usize>,
495 ) {
496 for mapping in &self.mappings {
497 let source_attribute_data = source_buffer.get_attribute_range_ref(
498 mapping.source_attribute.attribute_definition(),
499 source_range.clone(),
500 );
501 let mut target_attribute_data =
502 target_buffer.view_raw_attribute_mut(mapping.target_attribute);
503 let source_attribute_size = mapping.source_attribute.size() as usize;
504
505 if let Some(converter) = mapping.converter {
506 let mut source_tmp_buffer: Vec<u8> = vec![0; source_attribute_size];
507 for (index, source_chunk) in source_attribute_data
508 .chunks_exact(source_attribute_size)
509 .enumerate()
510 {
511 let target_attribute_chunk =
512 &mut target_attribute_data[index + target_range.start];
513 unsafe {
516 if let Some(transformation) = mapping.transformation.as_ref() {
517 if transformation.apply_to_source_attribute {
518 source_tmp_buffer.copy_from_slice(source_chunk);
519 (transformation.func)(&mut source_tmp_buffer[..]);
520 converter(&source_tmp_buffer[..], target_attribute_chunk);
521 } else {
522 converter(source_chunk, target_attribute_chunk);
523 (transformation.func)(target_attribute_chunk);
524 }
525 } else {
526 converter(source_chunk, target_attribute_chunk);
527 }
528 }
529 }
530 } else {
531 for (index, attribute_data) in source_attribute_data
532 .chunks_exact(source_attribute_size)
533 .enumerate()
534 {
535 let target_attribute_chunk =
536 &mut target_attribute_data[index + target_range.start];
537 target_attribute_chunk.copy_from_slice(attribute_data);
538 if let Some(transformation) = mapping.transformation.as_ref() {
539 (transformation.func)(target_attribute_chunk);
540 }
541 }
542 }
543 }
544 }
545
546 fn convert_interleaved_to_columnar<'b, B: InterleavedBuffer<'b> + ?Sized>(
547 &self,
548 source_buffer: &B,
549 source_range: Range<usize>,
550 target_buffer: &mut dyn ColumnarBufferMut,
551 target_range: Range<usize>,
552 max_attribute_size: usize,
553 ) {
554 let mut buffer: Vec<u8> = vec![0; max_attribute_size];
555
556 for mapping in &self.mappings {
557 let source_attribute_data = source_buffer.view_raw_attribute(mapping.source_attribute);
558 let target_attribute_range = target_buffer.get_attribute_range_mut(
559 mapping.target_attribute.attribute_definition(),
560 target_range.clone(),
561 );
562 let target_attribute_size = mapping.target_attribute.size() as usize;
563
564 for (point_index, target_attribute_chunk) in target_attribute_range
565 .chunks_exact_mut(target_attribute_size)
566 .enumerate()
567 {
568 let source_attribute_chunk =
569 &source_attribute_data[point_index + source_range.start];
570
571 if let Some(converter) = mapping.converter {
572 if let Some(transformation) = mapping.transformation.as_ref() {
573 if transformation.apply_to_source_attribute {
574 let buf = &mut buffer[..source_attribute_chunk.len()];
575 buf.copy_from_slice(source_attribute_chunk);
576 (transformation.func)(buf);
577 unsafe {
578 converter(buf, target_attribute_chunk);
579 }
580 } else {
581 unsafe {
582 converter(source_attribute_chunk, target_attribute_chunk);
583 }
584 (transformation.func)(target_attribute_chunk);
585 }
586 } else {
587 unsafe {
591 converter(source_attribute_chunk, target_attribute_chunk);
592 }
593 }
594 } else if let Some(transformation) = mapping.transformation.as_ref() {
595 let buf = &mut buffer[..source_attribute_chunk.len()];
596 buf.copy_from_slice(source_attribute_chunk);
597 (transformation.func)(buf);
598 target_attribute_chunk.copy_from_slice(buf);
599 } else {
600 target_attribute_chunk.copy_from_slice(source_attribute_chunk);
601 }
602 }
603 }
604 }
605
606 fn convert_interleaved_to_interleaved<
607 'b,
608 'c,
609 InBuffer: InterleavedBuffer<'b> + ?Sized,
610 OutBuffer: InterleavedBufferMut<'c> + ?Sized,
611 >(
612 &self,
613 source_buffer: &InBuffer,
614 source_range: Range<usize>,
615 target_buffer: &mut OutBuffer,
616 target_range: Range<usize>,
617 max_attribute_size: usize,
618 ) {
619 let mut buffer: Vec<u8> = vec![0; max_attribute_size];
620
621 for mapping in &self.mappings {
623 let source_attribute_view = source_buffer.view_raw_attribute(mapping.source_attribute);
624 let mut target_attribute_view =
625 target_buffer.view_raw_attribute_mut(mapping.target_attribute);
626
627 for (source_index, target_index) in source_range.clone().zip(target_range.clone()) {
629 let source_attribute_data = &source_attribute_view[source_index];
630 let target_attribute_data = &mut target_attribute_view[target_index];
631
632 if let Some(converter) = mapping.converter {
633 if let Some(transformation) = mapping.transformation.as_ref() {
634 if transformation.apply_to_source_attribute {
635 let buf = &mut buffer[..mapping.source_attribute.size() as usize];
636 buf.copy_from_slice(source_attribute_data);
637 (transformation.func)(buf);
638 unsafe {
639 converter(buf, target_attribute_data);
640 }
641 } else {
642 unsafe {
643 converter(source_attribute_data, target_attribute_data);
644 }
645 (transformation.func)(target_attribute_data);
646 }
647 } else {
648 unsafe {
649 converter(source_attribute_data, target_attribute_data);
650 }
651 }
652 } else if let Some(transformation) = mapping.transformation.as_ref() {
653 let buf = &mut buffer[..mapping.source_attribute.size() as usize];
654 buf.copy_from_slice(source_attribute_data);
655 (transformation.func)(buf);
656 target_attribute_data.copy_from_slice(buf);
657 } else {
658 target_attribute_data.copy_from_slice(source_attribute_data);
659 }
660 }
661 }
662 }
663}
664
665#[cfg(test)]
666mod tests {
667 use std::iter::FromIterator;
668
669 use itertools::Itertools;
670 use nalgebra::Vector3;
671 use rand::{thread_rng, Rng};
672
673 use crate::{
674 containers::{BorrowedBufferExt, HashMapBuffer, VectorBuffer},
675 layout::{
676 attributes::{CLASSIFICATION, POSITION_3D, RETURN_NUMBER},
677 PointType,
678 },
679 test_utils::{CustomPointTypeBig, CustomPointTypeSmall, DefaultPointDistribution},
680 };
681
682 use super::*;
683
684 fn buffer_converter_default_generic<
685 TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
686 TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
687 >() {
688 let rng = thread_rng();
689 let source_points = rng
690 .sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
691 .take(16)
692 .collect::<TFrom>();
693
694 let target_layout = CustomPointTypeSmall::layout();
696 let converter =
697 BufferLayoutConverter::for_layouts(source_points.point_layout(), &target_layout);
698
699 let converted_points = converter.convert::<TTo, _>(&source_points);
700
701 assert_eq!(target_layout, *converted_points.point_layout());
702
703 let expected_positions = source_points
704 .view_attribute::<Vector3<f64>>(&POSITION_3D)
705 .into_iter()
706 .collect_vec();
707 let actual_positions = converted_points
708 .view_attribute::<Vector3<f64>>(&POSITION_3D)
709 .into_iter()
710 .collect_vec();
711 assert_eq!(expected_positions, actual_positions);
712
713 let expected_classifications = source_points
714 .view_attribute::<u8>(&CLASSIFICATION)
715 .into_iter()
716 .collect_vec();
717 let actual_classifications = converted_points
718 .view_attribute::<u8>(&CLASSIFICATION)
719 .into_iter()
720 .collect_vec();
721 assert_eq!(expected_classifications, actual_classifications);
722 }
723
724 fn buffer_converter_multiple_attributes_from_one_generic<
725 TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
726 TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
727 >() {
728 let rng = thread_rng();
729 let source_points = rng
730 .sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
731 .take(16)
732 .collect::<TFrom>();
733
734 let custom_layout = PointLayout::from_attributes(&[CLASSIFICATION, RETURN_NUMBER]);
736
737 let mut converter = BufferLayoutConverter::for_layouts_with_default(
738 source_points.point_layout(),
739 &custom_layout,
740 );
741 converter.set_custom_mapping(&CLASSIFICATION, &RETURN_NUMBER);
742
743 let converted_points = converter.convert::<TTo, _>(&source_points);
744 assert_eq!(custom_layout, *converted_points.point_layout());
745
746 let expected_classifications = source_points
747 .view_attribute::<u8>(&CLASSIFICATION)
748 .into_iter()
749 .collect_vec();
750 let actual_classifications = converted_points
751 .view_attribute::<u8>(&CLASSIFICATION)
752 .into_iter()
753 .collect_vec();
754 let actual_return_numbers = converted_points
755 .view_attribute::<u8>(&RETURN_NUMBER)
756 .into_iter()
757 .collect_vec();
758
759 assert_eq!(expected_classifications, actual_classifications);
760 assert_eq!(expected_classifications, actual_return_numbers);
762 }
763
764 fn buffer_converter_transformed_target_attribute_generic<
765 TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
766 TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
767 >() {
768 let rng = thread_rng();
769 let source_points = rng
770 .sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
771 .take(16)
772 .collect::<TFrom>();
773
774 let custom_layout = PointLayout::from_attributes(&[POSITION_3D]);
775
776 let mut converter = BufferLayoutConverter::for_layouts_with_default(
777 source_points.point_layout(),
778 &custom_layout,
779 );
780 const OFFSET: f64 = 42.0;
781 let transform_positions_fn =
782 |source_position: Vector3<f64>| -> Vector3<f64> { source_position.add_scalar(OFFSET) };
783 converter.set_custom_mapping_with_transformation(
784 &POSITION_3D,
785 &POSITION_3D,
786 transform_positions_fn,
787 false,
788 );
789
790 let converted_points = converter.convert::<TTo, _>(&source_points);
791 assert_eq!(custom_layout, *converted_points.point_layout());
792
793 let expected_positions = source_points
794 .view_attribute::<Vector3<f64>>(&POSITION_3D)
795 .into_iter()
796 .map(transform_positions_fn)
797 .collect_vec();
798 let actual_positions = converted_points
799 .view_attribute::<Vector3<f64>>(&POSITION_3D)
800 .into_iter()
801 .collect_vec();
802
803 assert_eq!(expected_positions, actual_positions);
804 }
805
806 fn buffer_converter_transformed_source_attribute_generic<
807 TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
808 TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
809 >() {
810 let rng = thread_rng();
811 let source_points = rng
812 .sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
813 .take(16)
814 .collect::<TFrom>();
815
816 let custom_layout = PointLayout::from_attributes(&[POSITION_3D]);
817
818 let mut converter = BufferLayoutConverter::for_layouts_with_default(
819 source_points.point_layout(),
820 &custom_layout,
821 );
822 const OFFSET: f64 = 42.0;
823 let transform_positions_fn =
824 |source_position: Vector3<f64>| -> Vector3<f64> { source_position.add_scalar(OFFSET) };
825 converter.set_custom_mapping_with_transformation(
826 &POSITION_3D,
827 &POSITION_3D,
828 transform_positions_fn,
829 true,
830 );
831
832 let converted_points = converter.convert::<TTo, _>(&source_points);
833 assert_eq!(custom_layout, *converted_points.point_layout());
834
835 let expected_positions = source_points
836 .view_attribute::<Vector3<f64>>(&POSITION_3D)
837 .into_iter()
838 .map(transform_positions_fn)
839 .collect_vec();
840 let actual_positions = converted_points
841 .view_attribute::<Vector3<f64>>(&POSITION_3D)
842 .into_iter()
843 .collect_vec();
844
845 assert_eq!(expected_positions, actual_positions);
846 }
847
848 fn buffer_converter_identity_generic<
849 TFrom: for<'a> BorrowedBuffer<'a> + FromIterator<CustomPointTypeBig>,
850 TTo: for<'a> OwningBuffer<'a> + for<'a> MakeBufferFromLayout<'a>,
851 >() {
852 let rng = thread_rng();
853 let source_points = rng
854 .sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
855 .take(16)
856 .collect::<TFrom>();
857
858 let converter = BufferLayoutConverter::for_layouts_with_default(
859 source_points.point_layout(),
860 source_points.point_layout(),
861 );
862 let converted_points = converter.convert::<TTo, _>(&source_points);
863
864 let expected_points = source_points
865 .view::<CustomPointTypeBig>()
866 .into_iter()
867 .collect_vec();
868 let actual_points = converted_points
869 .view::<CustomPointTypeBig>()
870 .into_iter()
871 .collect_vec();
872 assert_eq!(expected_points, actual_points);
873 }
874
875 #[test]
876 fn test_buffer_converter_default() {
877 buffer_converter_default_generic::<VectorBuffer, VectorBuffer>();
878 buffer_converter_default_generic::<VectorBuffer, HashMapBuffer>();
879 buffer_converter_default_generic::<HashMapBuffer, VectorBuffer>();
880 buffer_converter_default_generic::<HashMapBuffer, HashMapBuffer>();
881 }
882
883 #[test]
884 fn test_buffer_converter_multiple_attributes_from_one() {
885 buffer_converter_multiple_attributes_from_one_generic::<VectorBuffer, VectorBuffer>();
886 buffer_converter_multiple_attributes_from_one_generic::<VectorBuffer, HashMapBuffer>();
887 buffer_converter_multiple_attributes_from_one_generic::<HashMapBuffer, VectorBuffer>();
888 buffer_converter_multiple_attributes_from_one_generic::<HashMapBuffer, HashMapBuffer>();
889 }
890
891 #[test]
892 fn test_buffer_converter_transformed_attribute() {
893 buffer_converter_transformed_source_attribute_generic::<VectorBuffer, VectorBuffer>();
894 buffer_converter_transformed_source_attribute_generic::<VectorBuffer, HashMapBuffer>();
895 buffer_converter_transformed_source_attribute_generic::<HashMapBuffer, VectorBuffer>();
896 buffer_converter_transformed_source_attribute_generic::<HashMapBuffer, HashMapBuffer>();
897
898 buffer_converter_transformed_target_attribute_generic::<VectorBuffer, VectorBuffer>();
899 buffer_converter_transformed_target_attribute_generic::<VectorBuffer, HashMapBuffer>();
900 buffer_converter_transformed_target_attribute_generic::<HashMapBuffer, VectorBuffer>();
901 buffer_converter_transformed_target_attribute_generic::<HashMapBuffer, HashMapBuffer>();
902 }
903
904 #[test]
905 fn test_buffer_converter_identity() {
906 buffer_converter_identity_generic::<VectorBuffer, VectorBuffer>();
907 buffer_converter_identity_generic::<VectorBuffer, HashMapBuffer>();
908 buffer_converter_identity_generic::<HashMapBuffer, VectorBuffer>();
909 buffer_converter_identity_generic::<HashMapBuffer, HashMapBuffer>();
910 }
911
912 #[test]
913 #[should_panic]
914 fn test_buffer_converter_mismatched_len() {
915 const COUNT: usize = 16;
916 let rng = thread_rng();
917 let source_points = rng
918 .sample_iter::<CustomPointTypeBig, _>(DefaultPointDistribution)
919 .take(COUNT)
920 .collect::<VectorBuffer>();
921
922 let mut target_buffer =
923 VectorBuffer::with_capacity(COUNT / 2, source_points.point_layout().clone());
924
925 let converter: BufferLayoutConverter<'_> = BufferLayoutConverter::for_layouts_with_default(
926 source_points.point_layout(),
927 source_points.point_layout(),
928 );
929 converter.convert_into(&source_points, &mut target_buffer);
930 }
931}