Struct pasture_core::layout::PointLayout
source · pub struct PointLayout { /* private fields */ }
Expand description
Describes the data layout of a single point in a point cloud
§Detailed explanation
To understand PointLayout
, it is necessary to understand the memory model of Pasture. Pasture is a library
for handling point cloud data, so the first thing worth understanding is what ‘point cloud data’ means in the context
of Pasture:
A point cloud in Pasture is modeled as a collection of tuples of attributes (a_1, a_2, …, a_n). An
attribute can be any datum associated with a point, such as its position in 3D space, an intensity value, an object
classification etc. The set of all unique attributes in a point cloud make up the point clouds point layout, which
is represented in Pasture by the PointLayout
type. The Pasture memory model describes how the attributes for each
point in a point cloud are layed out in memory. There are two major memory layouts supported by Pasture: Interleaved
and PerAttribute. In an Interleaved layout, all attributes for a single point are stored together in memory:
[a_1(p_1), a_2(p_1), …, a_n(p_1), a_1(p_2), a_2(p_2), …, a_n(p_2), …]
This layout is equivalent to storing a type T
inside a Vec
, where T
has members a_1, a_2, …, a_n and is packed
tightly.
In a PerAttribute layout, all attributes of a single type are stored together in memory, often in separate memory regions:
[a_1(p_1), a_1(p_2), …, a_1(p_n)] [a_2(p_1), a_2(p_2), …, a_2(p_n)] … [a_n(p_1), a_n(p_2), …, a_n(p_n)]
These layouts are sometimes called ‘Array of Structs’ (Interleaved) and ‘Struct of Arrays’ (PerAttribute).
Most code in Pasture supports point clouds with either of these memory layouts. To correctly handle memory layout and access
in both Interleaved and PerAttribute layout, each buffer in Pasture that stores point cloud data requires a piece of metadata
that describes the attributes of the point cloud with their respective Rust types, their order, their memory alignment
and their potential offset within a single point entry in Interleaved format. All this information is stored inside the PointLayout
structure.
To support the different memory layouts, Pasture buffers store point data as raw binary buffers internally. To work with the data,
you will want to use strongly typed Rust structures. Any type T
that you want to use for accessing point data in a strongly typed manner
must implement the PointType
trait and thus provide Pasture with a way of figuring out the attributes and memory layout of this type T
.
Implementations§
source§impl PointLayout
impl PointLayout
sourcepub fn from_attributes(attributes: &[PointAttributeDefinition]) -> Self
pub fn from_attributes(attributes: &[PointAttributeDefinition]) -> Self
Creates a new PointLayout from the given sequence of attributes. The attributes will be aligned using the
default alignments for their respective datatypes, in accordance with the Rust alignment rules for repr(C)
structs
#Panics
If any two attributes within the sequence share the same attribute name.
let layout = PointLayout::from_attributes(&[attributes::POSITION_3D, attributes::INTENSITY]);
sourcepub fn from_attributes_packed(
attributes: &[PointAttributeDefinition],
max_alignment: u64,
) -> Self
pub fn from_attributes_packed( attributes: &[PointAttributeDefinition], max_alignment: u64, ) -> Self
Creates a new PointLayout from the given sequence of attributes. The attributes will be aligned to a 1 byte boundary
in accordance with the Rust alignment rules for repr(packed)
structs
#Panics
If any two attributes within the sequence share the same attribute name.
// Default INTENSITY attribute uses u16 datatype. In a packed(1) struct, the next field will have offset 2
// even though the POSITION_3D attribute has an alignment requirement of 8
let layout_packed_1 = PointLayout::from_attributes_packed(&[attributes::INTENSITY, attributes::POSITION_3D], 1);
assert_eq!(0, layout_packed_1.at(0).offset());
assert_eq!(2, layout_packed_1.at(1).offset());
// If we use packed(4), POSITION_3D will start at byte 4:
let layout_packed_4 = PointLayout::from_attributes_packed(&[attributes::INTENSITY, attributes::POSITION_3D], 4);
assert_eq!(4, layout_packed_4.at(1).offset());
sourcepub fn from_members_and_alignment(
attributes: &[PointAttributeMember],
type_alignment: u64,
) -> Self
pub fn from_members_and_alignment( attributes: &[PointAttributeMember], type_alignment: u64, ) -> Self
Creates a new PointLayout from the given PointAttributeMember
sequence as well as the given type_alignment
.
#Panics
If any two attributes within the sequence share the same attribute name, or if there is overlap between any two attributes based on their sizes and offsets.
let layout = PointLayout::from_members_and_alignment(&[attributes::INTENSITY.at_offset_in_type(2), attributes::POSITION_3D.at_offset_in_type(8)], 8);
assert_eq!(2, layout.at(0).offset());
assert_eq!(8, layout.at(1).offset());
assert_eq!(32, layout.size_of_point_entry());
sourcepub fn add_attribute(
&mut self,
point_attribute: PointAttributeDefinition,
field_alignment: FieldAlignment,
)
pub fn add_attribute( &mut self, point_attribute: PointAttributeDefinition, field_alignment: FieldAlignment, )
Adds the given PointAttributeDefinition to this PointLayout. Sets the offset of the new attribute
within the PointLayout
based on the given FieldAlignment
#Panics
If an attribute with the same name is already part of this PointLayout.
let mut layout = PointLayout::default();
layout.add_attribute(attributes::INTENSITY, FieldAlignment::Default);
layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
// Default field alignment respects the 8-byte alignment requirement of default POSITION_3D (Vector3<f64>), even though default INTENSITY takes only 2 bytes
assert_eq!(8, layout.at(1).offset());
sourcepub fn has_attribute_with_name(&self, attribute_name: &str) -> bool
pub fn has_attribute_with_name(&self, attribute_name: &str) -> bool
Returns true if an attribute with the given name is part of this PointLayout.
let mut layout = PointLayout::default();
layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
assert!(layout.has_attribute_with_name(attributes::POSITION_3D.name()));
sourcepub fn has_attribute(&self, attribute: &PointAttributeDefinition) -> bool
pub fn has_attribute(&self, attribute: &PointAttributeDefinition) -> bool
Returns true
if the associated PointLayout
contains the given attribute
. Both the name of attribute
as well as
its datatype must match for this method to return true
. This is a more strict form of has_attribute_with_name
§Example
let mut layout = PointLayout::default();
layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
assert!(layout.has_attribute(&attributes::POSITION_3D));
layout.add_attribute(attributes::INTENSITY.with_custom_datatype(PointAttributeDataType::U32), FieldAlignment::Default);
assert!(!layout.has_attribute(&attributes::INTENSITY));
sourcepub fn get_attribute(
&self,
attribute: &PointAttributeDefinition,
) -> Option<&PointAttributeMember>
pub fn get_attribute( &self, attribute: &PointAttributeDefinition, ) -> Option<&PointAttributeMember>
Returns the attribute that matches the given attribute
in name and datatype from the associated PointLayout
. Returns None
if
no attribute with the same name and datatype exists
let mut layout = PointLayout::default();
layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
let attribute = layout.get_attribute(&attributes::POSITION_3D);
assert!(attribute.is_some());
let invalid_attribute = layout.get_attribute(&attributes::POSITION_3D.with_custom_datatype(PointAttributeDataType::U32));
assert!(invalid_attribute.is_none());
sourcepub fn get_attribute_by_name(
&self,
attribute_name: &str,
) -> Option<&PointAttributeMember>
pub fn get_attribute_by_name( &self, attribute_name: &str, ) -> Option<&PointAttributeMember>
Returns the attribute with the given name from this PointLayout. Returns None if no such attribute exists.
let mut layout = PointLayout::default();
layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
let attribute = layout.get_attribute_by_name(attributes::POSITION_3D.name());
assert_eq!(attributes::POSITION_3D.at_offset_in_type(0), *attribute.unwrap());
sourcepub fn at(&self, index: usize) -> &PointAttributeMember
pub fn at(&self, index: usize) -> &PointAttributeMember
Returns the attribute at the given index from the associated PointLayout
§Panics
If index
is out of bounds
sourcepub fn attributes(&self) -> impl Iterator<Item = &PointAttributeMember>
pub fn attributes(&self) -> impl Iterator<Item = &PointAttributeMember>
Returns an iterator over all attributes in this PointLayout
. The attributes are returned in the order
in which they were added to this PointLayout
:
let mut layout = PointLayout::default();
layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
layout.add_attribute(attributes::INTENSITY, FieldAlignment::Default);
let attributes = layout.attributes().collect::<Vec<_>>();
assert_eq!(attributes::POSITION_3D.at_offset_in_type(0), *attributes[0]);
assert_eq!(attributes::INTENSITY.at_offset_in_type(24), *attributes[1]);
sourcepub const fn size_of_point_entry(&self) -> u64
pub const fn size_of_point_entry(&self) -> u64
Returns the size in bytes of a single point entry with the associated PointLayout
. Note that the size can be
larger than the sum of the sizes of all attributes because of alignment requirements!
§Example
let layout = PointLayout::from_attributes(&[attributes::POSITION_3D, attributes::INTENSITY]);
// from_attributes respects the alignment requirements of each attribute. Default POSITION_3D uses Vector3<f64> and as such
// has an 8-byte minimum alignment, so the whole PointLayout is aligned to an 8-byte boundary. This is reflected in its size:
assert_eq!(32, layout.size_of_point_entry());
sourcepub fn index_of(&self, attribute: &PointAttributeDefinition) -> Option<usize>
pub fn index_of(&self, attribute: &PointAttributeDefinition) -> Option<usize>
Returns the index of the given attribute within the associated PointLayout
, or None
if the attribute is not
part of the PointLayout
. The index depends on the order in which the attributes have been added to the associated
PointLayout
, but does not necessarily reflect the order of the attributes in memory.
§Example
let layout = PointLayout::from_attributes(&[attributes::POSITION_3D, attributes::INTENSITY]);
assert_eq!(Some(0), layout.index_of(&attributes::POSITION_3D));
assert_eq!(Some(1), layout.index_of(&attributes::INTENSITY));
// Create a layout where we add INTENSITY as first attribute, however in memory, INTENSITY comes after POSITION_3D
let reordered_layout = PointLayout::from_members_and_alignment(&[attributes::INTENSITY.at_offset_in_type(24), attributes::POSITION_3D.at_offset_in_type(0)], 8);
assert_eq!(Some(0), reordered_layout.index_of(&attributes::INTENSITY));
assert_eq!(Some(1), reordered_layout.index_of(&attributes::POSITION_3D));
sourcepub fn compare_without_offsets(&self, other: &PointLayout) -> bool
pub fn compare_without_offsets(&self, other: &PointLayout) -> bool
Compares the associated PointLayout
with the other
layout, ignoring the attribute offsets. This way, only the names and datatypes
of the attributes are compared. This method is useful when dealing with data in a non-interleaved format, where offsets are irrelevant
sourcepub fn offset_of(&self, attribute: &PointAttributeDefinition) -> Option<u64>
pub fn offset_of(&self, attribute: &PointAttributeDefinition) -> Option<u64>
Returns the offset from an attribute. If the attribute don’t exist in the layout this function returns None.
Trait Implementations§
source§impl Clone for PointLayout
impl Clone for PointLayout
source§fn clone(&self) -> PointLayout
fn clone(&self) -> PointLayout
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moresource§impl Debug for PointLayout
impl Debug for PointLayout
source§impl Default for PointLayout
impl Default for PointLayout
source§impl Display for PointLayout
impl Display for PointLayout
source§impl FromIterator<PointAttributeDefinition> for PointLayout
impl FromIterator<PointAttributeDefinition> for PointLayout
source§fn from_iter<T: IntoIterator<Item = PointAttributeDefinition>>(iter: T) -> Self
fn from_iter<T: IntoIterator<Item = PointAttributeDefinition>>(iter: T) -> Self
source§impl Hash for PointLayout
impl Hash for PointLayout
source§impl PartialEq for PointLayout
impl PartialEq for PointLayout
source§fn eq(&self, other: &PointLayout) -> bool
fn eq(&self, other: &PointLayout) -> bool
self
and other
values to be equal, and is used
by ==
.impl Eq for PointLayout
impl StructuralPartialEq for PointLayout
Auto Trait Implementations§
impl Freeze for PointLayout
impl RefUnwindSafe for PointLayout
impl Send for PointLayout
impl Sync for PointLayout
impl Unpin for PointLayout
impl UnwindSafe for PointLayout
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self
from the equivalent element of its
superset. Read moresource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self
is actually part of its subset T
(and can be converted to it).source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset
but without any property checks. Always succeeds.source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self
to the equivalent element of its superset.