use crate::geometry_attribute::{GeometryAttributeType, PointAttribute};
use crate::metadata::{AttributeMetadata, GeometryMetadata, Metadata};
use crate::status::DracoError;
#[derive(Debug, Default, Clone)]
pub struct PointCloud {
attributes: Vec<PointAttribute>,
num_points: usize,
metadata: Option<GeometryMetadata>,
}
impl PointCloud {
pub fn new() -> Self {
Self::default()
}
pub fn set_num_points(&mut self, num_points: usize) {
self.num_points = num_points;
}
pub fn add_attribute(&mut self, mut attribute: PointAttribute) -> i32 {
if self.num_points == 0 && attribute.size() > 0 {
self.num_points = attribute.size();
}
let id = self.attributes.len() as i32;
attribute.set_unique_id(id as u32);
self.attributes.push(attribute);
id
}
pub fn add_attribute_preserve_unique_id(&mut self, attribute: PointAttribute) -> i32 {
if self.num_points == 0 && attribute.size() > 0 {
self.num_points = attribute.size();
}
let id = self.attributes.len() as i32;
self.attributes.push(attribute);
id
}
pub fn set_attribute(&mut self, att_id: i32, mut attribute: PointAttribute) {
debug_assert!(att_id >= 0);
let index = att_id as usize;
if index >= self.attributes.len() {
self.attributes.resize_with(index + 1, PointAttribute::new);
}
attribute.set_unique_id(att_id as u32);
self.attributes[index] = attribute;
}
pub fn num_attributes(&self) -> i32 {
self.attributes.len() as i32
}
pub fn attribute_id_by_unique_id(&self, unique_id: u32) -> i32 {
for (i, att) in self.attributes.iter().enumerate() {
if att.unique_id() == unique_id {
return i as i32;
}
}
-1
}
pub fn attribute_by_unique_id(&self, unique_id: u32) -> Option<&PointAttribute> {
let id = self.attribute_id_by_unique_id(unique_id);
(id >= 0).then(|| &self.attributes[id as usize])
}
pub fn attribute(&self, att_id: i32) -> &PointAttribute {
&self.attributes[att_id as usize]
}
pub fn try_attribute(&self, att_id: i32) -> Result<&PointAttribute, DracoError> {
let Some(attribute) = (att_id >= 0)
.then_some(att_id as usize)
.and_then(|index| self.attributes.get(index))
else {
return Err(DracoError::DracoError(
"Point cloud attribute id out of range".to_string(),
));
};
Ok(attribute)
}
pub fn attribute_mut(&mut self, att_id: i32) -> &mut PointAttribute {
&mut self.attributes[att_id as usize]
}
pub fn try_attribute_mut(&mut self, att_id: i32) -> Result<&mut PointAttribute, DracoError> {
let Some(attribute) = (att_id >= 0)
.then_some(att_id as usize)
.and_then(|index| self.attributes.get_mut(index))
else {
return Err(DracoError::DracoError(
"Point cloud attribute id out of range".to_string(),
));
};
Ok(attribute)
}
pub fn named_attribute_id(&self, att_type: GeometryAttributeType) -> i32 {
for (i, att) in self.attributes.iter().enumerate() {
if att.attribute_type() == att_type {
return i as i32;
}
}
-1
}
pub fn named_attribute(&self, att_type: GeometryAttributeType) -> Option<&PointAttribute> {
let id = self.named_attribute_id(att_type);
if id >= 0 {
Some(&self.attributes[id as usize])
} else {
None
}
}
pub fn num_points(&self) -> usize {
self.num_points
}
pub fn metadata(&self) -> Option<&GeometryMetadata> {
self.metadata.as_ref()
}
pub fn metadata_mut(&mut self) -> Option<&mut GeometryMetadata> {
self.metadata.as_mut()
}
pub fn metadata_or_insert(&mut self) -> &mut GeometryMetadata {
self.metadata.get_or_insert_with(GeometryMetadata::new)
}
pub fn set_metadata(&mut self, metadata: Option<GeometryMetadata>) {
self.metadata = metadata;
}
pub fn attribute_metadata_by_unique_id(
&self,
attribute_unique_id: u32,
) -> Option<&AttributeMetadata> {
self.metadata
.as_ref()
.and_then(|metadata| metadata.attribute_metadata_by_unique_id(attribute_unique_id))
}
pub fn attribute_metadata_by_string_entry(
&self,
entry_name: &str,
entry_value: &str,
) -> Option<&AttributeMetadata> {
self.metadata.as_ref().and_then(|metadata| {
metadata.attribute_metadata_by_string_entry(entry_name, entry_value)
})
}
pub fn set_attribute_metadata(
&mut self,
att_id: i32,
metadata: Metadata,
) -> Result<(), DracoError> {
let unique_id = self.try_attribute(att_id)?.unique_id();
self.metadata_or_insert()
.set_attribute_metadata(unique_id, metadata);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_attribute_rejects_out_of_range_ids() {
let mut point_cloud = PointCloud::new();
assert!(point_cloud.try_attribute(-1).is_err());
assert!(point_cloud.try_attribute(0).is_err());
assert!(point_cloud.try_attribute_mut(-1).is_err());
assert!(point_cloud.try_attribute_mut(0).is_err());
}
}