pasture_core/containers/
buffer_views.rs

1use anyhow::{anyhow, Result};
2use std::{cell::RefCell, marker::PhantomData};
3
4use crate::layout::{
5    conversion::{convert_unit, get_converter_for_attributes, AttributeConversionFn},
6    PointAttributeDefinition, PointAttributeMember, PointType, PrimitiveType,
7};
8
9use super::{
10    attribute_iterators::{
11        AttributeIteratorByMut, AttributeIteratorByRef, AttributeIteratorByValue,
12    },
13    point_buffer::{
14        BorrowedBuffer, BorrowedMutBuffer, ColumnarBuffer, ColumnarBufferMut, InterleavedBuffer,
15        InterleavedBufferMut,
16    },
17    point_iterators::{PointIteratorByMut, PointIteratorByRef, PointIteratorByValue},
18    OwningBuffer,
19};
20
21/// A strongly typed view over the point data of a buffer. This allows accessing the point data in
22/// the buffer using type `T` instead of only through raw memory (i.e. as `&[u8]`).
23/// Depending on the memory layout of the buffer type `B`, this view supports accessing the point data
24/// only by value (for non-interleaved buffers) or by immutable borrow (for interleaved buffers).
25/// The `PointView` supports no type conversion, so `T::layout()` must match
26/// the `PointLayout` of the buffer. You cannot create instances of `PointView` directly but instead
27/// have to use [`BorrowedBuffer::view`] function and its variations, which perform the necessary type
28/// checks internally!
29///
30/// # Lifetime bounds
31///
32/// Since the `PointView` borrows the buffer internally, and the buffer itself has a borrow lifetime,
33/// `PointView` stores two lifetimes so that it can borrow its buffer for a potentially shorter lifetime
34/// `'b` than the lifetime `'a` of the buffer itself.
35#[derive(Debug, Copy, Clone)]
36pub struct PointView<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PointType>
37where
38    'a: 'b,
39{
40    buffer: &'b B,
41    _phantom: PhantomData<&'a T>,
42}
43
44impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PointType> PointView<'a, 'b, B, T>
45where
46    'a: 'b,
47{
48    pub(crate) fn new(buffer: &'b B) -> Self {
49        assert_eq!(
50            T::layout(),
51            *buffer.point_layout(),
52            "Layout mismatch\nBuffer layout:\n{}\nRequested layout:\n{}",
53            buffer.point_layout(),
54            T::layout()
55        );
56        Self {
57            buffer,
58            _phantom: Default::default(),
59        }
60    }
61
62    /// Access the point at `index`
63    ///
64    /// # Panics
65    ///
66    /// If `index` is out of bounds
67    pub fn at(&self, index: usize) -> T {
68        let mut point = T::zeroed();
69        self.buffer
70            .get_point(index, bytemuck::bytes_of_mut(&mut point));
71        point
72    }
73}
74
75impl<'a, 'b, B: InterleavedBuffer<'a> + ?Sized, T: PointType> PointView<'a, 'b, B, T>
76where
77    'a: 'b,
78{
79    /// Access the point at `index` by reference
80    ///
81    /// # Lifetime bounds
82    ///
83    /// Just as the `PointView` can borrow its underlying buffer for a shorter lifetime `'b` than
84    /// the lifetime `'a` of the buffer, it should be possible to borrow a single point from a `PointView`
85    /// for a shorter lifetime `'c` than the lifetime `'b` of the `PointView`, hence the additional
86    /// lifetime bounds.
87    ///
88    /// # Panics
89    ///
90    /// If `index` is out of bounds
91    pub fn at_ref<'c>(&'c self, index: usize) -> &'c T
92    where
93        'b: 'c,
94    {
95        bytemuck::from_bytes(self.buffer.get_point_ref(index))
96    }
97
98    /// Return an iterator over strongly typed point data by reference
99    pub fn iter<'c>(&'c self) -> PointIteratorByRef<'c, T>
100    where
101        'b: 'c,
102    {
103        self.buffer.into()
104    }
105}
106
107impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized + 'a, T: PointType> IntoIterator
108    for PointView<'a, 'b, B, T>
109where
110    'a: 'b,
111{
112    type Item = T;
113    type IntoIter = PointIteratorByValue<'a, 'b, T, B>;
114
115    fn into_iter(self) -> Self::IntoIter {
116        self.buffer.into()
117    }
118}
119
120impl<
121        'a,
122        'b,
123        'c,
124        'd,
125        B1: BorrowedBuffer<'a> + ?Sized + 'a,
126        B2: BorrowedBuffer<'c> + ?Sized + 'c,
127        T: PointType + PartialEq,
128    > PartialEq<PointView<'c, 'd, B2, T>> for PointView<'a, 'b, B1, T>
129{
130    fn eq(&self, other: &PointView<'c, 'd, B2, T>) -> bool {
131        if self.buffer.len() != other.buffer.len() {
132            false
133        } else {
134            (0..self.buffer.len()).all(|idx| self.at(idx) == other.at(idx))
135        }
136    }
137}
138
139impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized + 'a, T: PointType + Eq> Eq
140    for PointView<'a, 'b, B, T>
141{
142}
143
144/// Like [`PointView`], but provides mutable access to the strongly typed point data. For buffers with unknown
145/// memory layout, this means that you have to use [`PointViewMut::set_at`], but if the underlying buffer
146/// implements [`InterleavedBufferMut`], you can also access strongly-typed points by mutable borrow!
147#[derive(Debug)]
148pub struct PointViewMut<'a, 'b, B: BorrowedMutBuffer<'a> + ?Sized, T: PointType>
149where
150    'a: 'b,
151{
152    buffer: &'b mut B,
153    _phantom: PhantomData<&'a T>,
154}
155
156impl<'a, 'b, B: BorrowedMutBuffer<'a> + ?Sized, T: PointType> PointViewMut<'a, 'b, B, T> {
157    pub(crate) fn new(buffer: &'b mut B) -> Self {
158        assert_eq!(
159            T::layout(),
160            *buffer.point_layout(),
161            "Layout mismatch\nBuffer layout:\n{}\nRequested layout:\n{}",
162            buffer.point_layout(),
163            T::layout()
164        );
165        Self {
166            buffer,
167            _phantom: Default::default(),
168        }
169    }
170
171    /// Access the point at `index`
172    ///
173    /// # Panics
174    ///
175    /// If `index` is out of bounds
176    pub fn at(&self, index: usize) -> T {
177        let mut point = T::zeroed();
178        self.buffer
179            .get_point(index, bytemuck::bytes_of_mut(&mut point));
180        point
181    }
182
183    /// Sets the data for the point at `index`
184    ///
185    /// # Panics
186    ///
187    /// If `index` is out of bounds
188    pub fn set_at(&mut self, index: usize, point: T) {
189        // Safe because `new` asserts that the point layout of `T` and `buffer` match
190        unsafe {
191            self.buffer.set_point(index, bytemuck::bytes_of(&point));
192        }
193    }
194}
195
196impl<'a, 'b, B: InterleavedBuffer<'a> + BorrowedMutBuffer<'a> + ?Sized, T: PointType>
197    PointViewMut<'a, 'b, B, T>
198{
199    /// Access the point at `index` as an immutable reference
200    ///
201    /// # Panics
202    ///
203    /// If `index` is out of bounds
204    pub fn at_ref<'c>(&'c self, index: usize) -> &'c T
205    where
206        'b: 'c,
207    {
208        bytemuck::from_bytes(self.buffer.get_point_ref(index))
209    }
210
211    /// Return an iterator over point data by immutable reference
212    pub fn iter<'c>(&'c self) -> PointIteratorByRef<'c, T>
213    where
214        'b: 'c,
215    {
216        (&*self.buffer).into()
217    }
218}
219
220impl<'a, 'b, B: InterleavedBufferMut<'a> + ?Sized, T: PointType> PointViewMut<'a, 'b, B, T> {
221    /// Access the point at `index` as a mutable reference
222    ///
223    /// # Panics
224    ///
225    /// If `index` is out of bounds
226    pub fn at_mut<'c>(&'c mut self, index: usize) -> &'c mut T
227    where
228        'b: 'c,
229    {
230        bytemuck::from_bytes_mut(self.buffer.get_point_mut(index))
231    }
232
233    /// Returns an iterator over point data by mutable reference
234    pub fn iter_mut<'c>(&'c mut self) -> PointIteratorByMut<'c, T>
235    where
236        'b: 'c,
237    {
238        self.buffer.into()
239    }
240
241    /// Sorts the point buffer using the given `comparator` function
242    pub fn sort_by<F: Fn(&T, &T) -> std::cmp::Ordering>(&mut self, comparator: F) {
243        let typed_points: &mut [T] =
244            bytemuck::cast_slice_mut(self.buffer.get_point_range_mut(0..self.buffer.len()));
245        typed_points.sort_by(comparator);
246    }
247}
248
249impl<'a, 'b, B: OwningBuffer<'a> + ?Sized, T: PointType> PointViewMut<'a, 'b, B, T> {
250    /// Push the given `point` into the underlying buffer
251    pub fn push_point(&mut self, point: T) {
252        // Safe because we know that a `PointViewMut` can never be created for a `T` that is different from
253        // the `PointLayout` of the underlying buffer (see the check in `new`)
254        unsafe {
255            self.buffer.push_points(bytemuck::bytes_of(&point));
256        }
257    }
258}
259
260impl<
261        'a,
262        'b,
263        'c,
264        'd,
265        B1: BorrowedMutBuffer<'a> + ?Sized + 'a,
266        B2: BorrowedMutBuffer<'c> + ?Sized + 'c,
267        T: PointType + PartialEq,
268    > PartialEq<PointViewMut<'c, 'd, B2, T>> for PointViewMut<'a, 'b, B1, T>
269{
270    fn eq(&self, other: &PointViewMut<'c, 'd, B2, T>) -> bool {
271        if self.buffer.len() != other.buffer.len() {
272            false
273        } else {
274            (0..self.buffer.len()).all(|idx| self.at(idx) == other.at(idx))
275        }
276    }
277}
278
279impl<'a, 'b, B: BorrowedMutBuffer<'a> + ?Sized + 'a, T: PointType + Eq> Eq
280    for PointViewMut<'a, 'b, B, T>
281{
282}
283
284/// A strongly typed view over attribute data of a point buffer. This allows accessing the data for a specific
285/// attribute of a `PointType` using the strong type `T` instead of as raw memory (i.e. `&[u8]`). This type makes
286/// no assumptions about the memory layout of the underlying buffer, so it only provides access to the attribute
287/// data by value. Just as with the [`PointView`] type, you cannot create instances of `AttributeView` directly.
288/// Instead, use the [`BorrowedBuffer::view_attribute`] function and its variations, which perform the necessary
289/// type checking.
290#[derive(Debug, Copy, Clone)]
291pub struct AttributeView<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PrimitiveType>
292where
293    'a: 'b,
294{
295    buffer: &'b B,
296    attribute: &'b PointAttributeMember,
297    _phantom: PhantomData<&'a T>,
298}
299
300impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PrimitiveType> AttributeView<'a, 'b, B, T> {
301    pub(crate) fn new(buffer: &'b B, attribute: &PointAttributeDefinition) -> Self {
302        assert_eq!(T::data_type(), attribute.datatype());
303        Self {
304            attribute: buffer
305                .point_layout()
306                .get_attribute(attribute)
307                .expect("Attribute not found in PointLayout of buffer"),
308            buffer,
309            _phantom: Default::default(),
310        }
311    }
312
313    /// Get the attribute value at `index`
314    ///
315    /// # Panics
316    ///
317    ///  If `index` is out of bounds
318    pub fn at(&self, index: usize) -> T {
319        let mut attribute = T::zeroed();
320        // Is safe because we get the attribute_member from the PointLayout of the buffer in `new`
321        unsafe {
322            self.buffer.get_attribute_unchecked(
323                self.attribute,
324                index,
325                bytemuck::bytes_of_mut(&mut attribute),
326            );
327        }
328        attribute
329    }
330}
331
332impl<'a, 'b, B: ColumnarBuffer<'a> + ?Sized, T: PrimitiveType> AttributeView<'a, 'b, B, T>
333where
334    'a: 'b,
335{
336    /// Get the attribute value at `index` as an immutable borrow
337    ///
338    /// # Panics
339    ///
340    ///  If `index` is out of bounds
341    pub fn at_ref<'c>(&'c self, index: usize) -> &'c T
342    where
343        'b: 'c,
344    {
345        bytemuck::from_bytes(
346            self.buffer
347                .get_attribute_ref(self.attribute.attribute_definition(), index),
348        )
349    }
350
351    /// Returns an iterator over attribute values by immutable reference
352    pub fn iter<'c>(&'c self) -> AttributeIteratorByRef<'c, T>
353    where
354        'b: 'c,
355    {
356        AttributeIteratorByRef::new(self.buffer, self.attribute.attribute_definition())
357    }
358}
359
360impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized + 'a, T: PrimitiveType> IntoIterator
361    for AttributeView<'a, 'b, B, T>
362{
363    type Item = T;
364    type IntoIter = AttributeIteratorByValue<'a, 'b, T, B>;
365
366    fn into_iter(self) -> Self::IntoIter {
367        AttributeIteratorByValue::new(self.buffer, self.attribute.attribute_definition())
368    }
369}
370
371impl<
372        'a,
373        'b,
374        'c,
375        'd,
376        B1: BorrowedBuffer<'a> + ?Sized + 'a,
377        B2: BorrowedBuffer<'c> + ?Sized + 'c,
378        T: PrimitiveType + PartialEq,
379    > PartialEq<AttributeView<'c, 'd, B2, T>> for AttributeView<'a, 'b, B1, T>
380{
381    fn eq(&self, other: &AttributeView<'c, 'd, B2, T>) -> bool {
382        self.buffer.len() == other.buffer.len()
383            && self.attribute.attribute_definition() == other.attribute.attribute_definition()
384            && (0..self.buffer.len()).all(|idx| self.at(idx) == other.at(idx))
385    }
386}
387
388impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized + 'a, T: PrimitiveType + Eq> Eq
389    for AttributeView<'a, 'b, B, T>
390{
391}
392
393/// Like [`AttributeView`], but provides mutable access to the attribute data
394#[derive(Debug)]
395pub struct AttributeViewMut<'a, 'b, B: BorrowedMutBuffer<'a> + ?Sized, T: PrimitiveType>
396where
397    'a: 'b,
398{
399    buffer: &'b mut B,
400    attribute: PointAttributeMember,
401    _phantom: PhantomData<&'a T>,
402}
403
404impl<'a, 'b, B: BorrowedMutBuffer<'a> + ?Sized, T: PrimitiveType> AttributeViewMut<'a, 'b, B, T>
405where
406    'a: 'b,
407{
408    pub(crate) fn new(buffer: &'b mut B, attribute: &PointAttributeDefinition) -> Self {
409        assert_eq!(T::data_type(), attribute.datatype());
410        Self {
411            attribute: buffer
412                .point_layout()
413                .get_attribute(attribute)
414                .expect("Attribute not found in PointLayout of buffer")
415                .clone(),
416            buffer,
417            _phantom: Default::default(),
418        }
419    }
420
421    /// Get the attribute value at `index`
422    ///
423    /// # Panics
424    ///
425    ///  If `index` is out of bounds
426    pub fn at(&self, index: usize) -> T {
427        let mut attribute = T::zeroed();
428        // Is safe because we get the attribute_member from the PointLayout of the buffer in `new`
429        unsafe {
430            self.buffer.get_attribute_unchecked(
431                &self.attribute,
432                index,
433                bytemuck::bytes_of_mut(&mut attribute),
434            );
435        }
436        attribute
437    }
438
439    /// Sets the value of the attribute at `index` to `attribute_value`
440    ///
441    /// # Panics
442    ///
443    ///  If `index` is out of bounds
444    pub fn set_at(&mut self, index: usize, attribute_value: T) {
445        // Safe because `new` checks that the data type of `T` and `self.attribute` match
446        unsafe {
447            self.buffer.set_attribute(
448                self.attribute.attribute_definition(),
449                index,
450                bytemuck::bytes_of(&attribute_value),
451            );
452        }
453    }
454}
455
456impl<'a, 'b, B: ColumnarBuffer<'a> + BorrowedMutBuffer<'a> + ?Sized, T: PrimitiveType>
457    AttributeViewMut<'a, 'b, B, T>
458where
459    'a: 'b,
460{
461    /// Get the attribute value at `index` as an immutable borrow
462    ///
463    /// # Panics
464    ///
465    ///  If `index` is out of bounds
466    pub fn at_ref<'c>(&'c self, index: usize) -> &'c T
467    where
468        'b: 'c,
469    {
470        bytemuck::from_bytes(
471            self.buffer
472                .get_attribute_ref(self.attribute.attribute_definition(), index),
473        )
474    }
475
476    /// Returns an iterator over attribute values as immutable borrows
477    pub fn iter<'c>(&'c self) -> AttributeIteratorByRef<'c, T>
478    where
479        'b: 'c,
480    {
481        AttributeIteratorByRef::new(self.buffer, self.attribute.attribute_definition())
482    }
483}
484
485impl<'a, 'b, B: ColumnarBufferMut<'a> + BorrowedMutBuffer<'a> + ?Sized, T: PrimitiveType>
486    AttributeViewMut<'a, 'b, B, T>
487{
488    /// Get the attribute value at `index` as a mutable borrow
489    ///
490    /// # Panics
491    ///
492    ///  If `index` is out of bounds
493    pub fn at_mut(&'b mut self, index: usize) -> &'b mut T {
494        bytemuck::from_bytes_mut(
495            self.buffer
496                .get_attribute_mut(self.attribute.attribute_definition(), index),
497        )
498    }
499
500    /// Returns an iterator over attribute values as mutable borrows
501    pub fn iter_mut(&'b mut self) -> AttributeIteratorByMut<'b, T> {
502        AttributeIteratorByMut::new(self.buffer, self.attribute.attribute_definition())
503    }
504}
505
506impl<
507        'a,
508        'b,
509        'c,
510        'd,
511        B1: BorrowedMutBuffer<'a> + ?Sized + 'a,
512        B2: BorrowedMutBuffer<'c> + ?Sized + 'c,
513        T: PrimitiveType + PartialEq,
514    > PartialEq<AttributeViewMut<'c, 'd, B2, T>> for AttributeViewMut<'a, 'b, B1, T>
515{
516    fn eq(&self, other: &AttributeViewMut<'c, 'd, B2, T>) -> bool {
517        self.buffer.len() == other.buffer.len()
518            && self.attribute.attribute_definition() == other.attribute.attribute_definition()
519            && (0..self.buffer.len()).all(|idx| self.at(idx) == other.at(idx))
520    }
521}
522
523impl<'a, 'b, B: BorrowedMutBuffer<'a> + ?Sized + 'a, T: PrimitiveType + Eq> Eq
524    for AttributeViewMut<'a, 'b, B, T>
525{
526}
527
528/// A view over a strongly typed point attribute that supports type conversion. This means that the
529/// `PointAttributeDataType` of the attribute does not have to match the type `T` that this view returns.
530/// For an explanation on how attribute type conversion works in pasture, see the [`conversion`](crate::layout::conversion)
531/// module
532#[derive(Debug)]
533pub struct AttributeViewConverting<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PrimitiveType>
534where
535    'a: 'b,
536{
537    buffer: &'b B,
538    attribute: PointAttributeMember,
539    converter_fn: AttributeConversionFn,
540    converter_buffer: RefCell<Vec<u8>>,
541    _phantom: PhantomData<&'a T>,
542}
543
544impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PrimitiveType>
545    AttributeViewConverting<'a, 'b, B, T>
546{
547    pub(crate) fn new(buffer: &'b B, attribute: &PointAttributeDefinition) -> Result<Self> {
548        assert_eq!(T::data_type(), attribute.datatype());
549        let attribute_in_layout: &PointAttributeMember = buffer
550            .point_layout()
551            .get_attribute_by_name(attribute.name())
552            .expect("Attribute not found in PointLayout of buffer");
553        let converter_fn = if attribute_in_layout.datatype() == T::data_type() {
554            convert_unit
555        } else {
556            get_converter_for_attributes(
557                attribute_in_layout.attribute_definition(),
558                &attribute.with_custom_datatype(T::data_type()),
559            )
560            .ok_or(anyhow!("Conversion between attribute types is impossible"))?
561        };
562        let converter_buffer = vec![0; attribute_in_layout.size() as usize];
563        Ok(Self {
564            attribute: attribute_in_layout.clone(),
565            buffer,
566            converter_fn,
567            converter_buffer: RefCell::new(converter_buffer),
568            _phantom: Default::default(),
569        })
570    }
571
572    /// Get the attribute value at `index`
573    pub fn at(&self, index: usize) -> T {
574        let mut value = T::zeroed();
575        // Is safe because we took 'attribute' from the point layout of the buffer
576        // conversion is safe because we checked the source and destination types in `new`
577        unsafe {
578            self.buffer.get_attribute_unchecked(
579                &self.attribute,
580                index,
581                self.converter_buffer.borrow_mut().as_mut_slice(),
582            );
583            (self.converter_fn)(
584                self.converter_buffer.borrow().as_slice(),
585                bytemuck::bytes_of_mut(&mut value),
586            );
587        }
588        value
589    }
590}
591
592impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PrimitiveType> IntoIterator
593    for AttributeViewConverting<'a, 'b, B, T>
594{
595    type Item = T;
596    type IntoIter = AttributeViewConvertingIterator<'a, 'b, B, T>;
597
598    fn into_iter(self) -> Self::IntoIter {
599        AttributeViewConvertingIterator {
600            current_index: 0,
601            view: self,
602        }
603    }
604}
605
606impl<
607        'a,
608        'b,
609        'c,
610        'd,
611        B1: BorrowedBuffer<'a> + ?Sized + 'a,
612        B2: BorrowedBuffer<'c> + ?Sized + 'c,
613        T: PrimitiveType + PartialEq,
614    > PartialEq<AttributeViewConverting<'c, 'd, B2, T>> for AttributeViewConverting<'a, 'b, B1, T>
615{
616    fn eq(&self, other: &AttributeViewConverting<'c, 'd, B2, T>) -> bool {
617        self.buffer.len() == other.buffer.len()
618            && self.attribute.attribute_definition() == other.attribute.attribute_definition()
619            && (0..self.buffer.len()).all(|idx| self.at(idx) == other.at(idx))
620    }
621}
622
623impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized + 'a, T: PrimitiveType + Eq> Eq
624    for AttributeViewConverting<'a, 'b, B, T>
625{
626}
627
628/// An iterator that performs attribute value conversion on the fly. This allows iterating over an
629/// attribute that has internal datatype `U` as if it had datatype `T`
630pub struct AttributeViewConvertingIterator<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PrimitiveType>
631{
632    view: AttributeViewConverting<'a, 'b, B, T>,
633    current_index: usize,
634}
635
636impl<'a, 'b, B: BorrowedBuffer<'a> + ?Sized, T: PrimitiveType> Iterator
637    for AttributeViewConvertingIterator<'a, 'b, B, T>
638{
639    type Item = T;
640
641    fn next(&mut self) -> Option<Self::Item> {
642        if self.current_index == self.view.buffer.len() {
643            None
644        } else {
645            let ret = self.view.at(self.current_index);
646            self.current_index += 1;
647            Some(ret)
648        }
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use nalgebra::Vector3;
655    use rand::{thread_rng, Rng};
656
657    use crate::{
658        containers::{BorrowedBufferExt, BorrowedMutBufferExt, HashMapBuffer, VectorBuffer},
659        layout::{attributes::POSITION_3D, PointAttributeDataType},
660        test_utils::*,
661    };
662
663    #[test]
664    fn test_sort_buffer() {
665        let rng = thread_rng();
666        let mut test_points = rng
667            .sample_iter::<CustomPointTypeSmall, _>(DefaultPointDistribution)
668            .take(10)
669            .collect::<VectorBuffer>();
670
671        test_points
672            .view_mut::<CustomPointTypeSmall>()
673            .sort_by(|a, b| a.classification.cmp(&b.classification));
674
675        let points = test_points
676            .view::<CustomPointTypeSmall>()
677            .into_iter()
678            .collect::<Vec<_>>();
679        let are_sorted = points
680            .iter()
681            .zip(points.iter().skip(1))
682            .all(|(low, high)| low.classification <= high.classification);
683        assert!(are_sorted, "Points not sorted: {:#?}", test_points);
684    }
685
686    #[test]
687    fn test_point_views_eq() {
688        let rng = thread_rng();
689        let test_points = rng
690            .sample_iter::<CustomPointTypeSmall, _>(DefaultPointDistribution)
691            .take(10)
692            .collect::<Vec<_>>();
693
694        let mut buffer1 = test_points.iter().copied().collect::<VectorBuffer>();
695        let mut buffer2 = test_points.iter().copied().collect::<HashMapBuffer>();
696
697        assert_eq!(
698            buffer1.view::<CustomPointTypeSmall>(),
699            buffer2.view::<CustomPointTypeSmall>()
700        );
701        assert_eq!(
702            buffer1.view_mut::<CustomPointTypeSmall>(),
703            buffer2.view_mut::<CustomPointTypeSmall>()
704        );
705
706        buffer2 = thread_rng()
707            .sample_iter::<CustomPointTypeSmall, _>(DefaultPointDistribution)
708            .take(10)
709            .collect();
710        assert_ne!(
711            buffer1.view::<CustomPointTypeSmall>(),
712            buffer2.view::<CustomPointTypeSmall>()
713        );
714        assert_ne!(
715            buffer1.view_mut::<CustomPointTypeSmall>(),
716            buffer2.view_mut::<CustomPointTypeSmall>()
717        );
718    }
719
720    #[test]
721    fn test_attribute_views_eq() {
722        let rng = thread_rng();
723        let test_points = rng
724            .sample_iter::<CustomPointTypeSmall, _>(DefaultPointDistribution)
725            .take(10)
726            .collect::<Vec<_>>();
727
728        let mut buffer1 = test_points.iter().copied().collect::<VectorBuffer>();
729        let mut buffer2 = test_points.iter().copied().collect::<HashMapBuffer>();
730
731        assert_eq!(
732            buffer1.view_attribute::<Vector3<f64>>(&POSITION_3D),
733            buffer2.view_attribute::<Vector3<f64>>(&POSITION_3D),
734        );
735        assert_eq!(
736            buffer1.view_attribute_mut::<Vector3<f64>>(&POSITION_3D),
737            buffer2.view_attribute_mut::<Vector3<f64>>(&POSITION_3D),
738        );
739        let f32_position = POSITION_3D.with_custom_datatype(PointAttributeDataType::Vec3f32);
740        assert_eq!(
741            buffer1
742                .view_attribute_with_conversion::<Vector3<f32>>(&f32_position)
743                .expect("Invalid attribute conversion"),
744            buffer2
745                .view_attribute_with_conversion::<Vector3<f32>>(&f32_position)
746                .expect("Invalid attribute conversion"),
747        );
748
749        buffer2 = thread_rng()
750            .sample_iter::<CustomPointTypeSmall, _>(DefaultPointDistribution)
751            .take(10)
752            .collect();
753
754        assert_ne!(
755            buffer1.view_attribute::<Vector3<f64>>(&POSITION_3D),
756            buffer2.view_attribute::<Vector3<f64>>(&POSITION_3D),
757        );
758        assert_ne!(
759            buffer1.view_attribute_mut::<Vector3<f64>>(&POSITION_3D),
760            buffer2.view_attribute_mut::<Vector3<f64>>(&POSITION_3D),
761        );
762        assert_ne!(
763            buffer1
764                .view_attribute_with_conversion::<Vector3<f32>>(&f32_position)
765                .expect("Invalid attribute conversion"),
766            buffer2
767                .view_attribute_with_conversion::<Vector3<f32>>(&f32_position)
768                .expect("Invalid attribute conversion"),
769        );
770    }
771}