pcd-rs 0.13.0

Working with PCD file format in Rust
Documentation
//! Types for PCD metadata.

use crate::Result;
use std::{
    fs::File,
    io::{BufRead, BufReader},
    iter::FromIterator,
    ops::Index,
    path::Path,
};

/// The struct keep meta data of PCD file.
#[derive(Debug, Clone, PartialEq)]
pub struct PcdMeta {
    pub version: String,
    pub width: u64,
    pub height: u64,
    pub viewpoint: ViewPoint,
    pub num_points: u64,
    pub data: DataKind,
    pub field_defs: Schema,
}

impl PcdMeta {
    /// Read only the PCD header from a file path, without loading point data.
    pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
        let file = BufReader::new(File::open(path.as_ref())?);
        Self::from_reader(file)
    }

    /// Read only the PCD header from a reader, without loading point data.
    pub fn from_reader(mut reader: impl BufRead) -> Result<Self> {
        let mut line_count = 0;
        crate::utils::load_meta(&mut reader, &mut line_count)
    }
}

/// Represents VIEWPOINT field in meta data.
#[derive(Debug, Clone, PartialEq)]
pub struct ViewPoint {
    pub tx: f64,
    pub ty: f64,
    pub tz: f64,
    pub qw: f64,
    pub qx: f64,
    pub qy: f64,
    pub qz: f64,
}

impl Default for ViewPoint {
    fn default() -> Self {
        ViewPoint {
            tx: 0.0,
            ty: 0.0,
            tz: 0.0,
            qw: 1.0,
            qx: 0.0,
            qy: 0.0,
            qz: 0.0,
        }
    }
}

/// The enum indicates whether the point cloud data is encoded in Ascii, binary, or compressed binary.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataKind {
    Ascii,
    Binary,
    BinaryCompressed,
}

/// The enum specifies one of signed, unsigned integers, and floating point number type to the field.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeKind {
    I,
    U,
    F,
}

/// The enum specifies the exact type for each PCD field.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueKind {
    U8,
    U16,
    U32,
    U64,
    I8,
    I16,
    I32,
    I64,
    F32,
    F64,
}

impl ValueKind {
    /// Returns the byte size of a value of this kind.
    pub fn byte_size(&self) -> usize {
        match self {
            ValueKind::U8 | ValueKind::I8 => 1,
            ValueKind::U16 | ValueKind::I16 => 2,
            ValueKind::U32 | ValueKind::I32 | ValueKind::F32 => 4,
            ValueKind::U64 | ValueKind::I64 | ValueKind::F64 => 8,
        }
    }
}

/// Define the properties of a PCD field.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldDef {
    pub name: String,
    pub kind: ValueKind,
    pub count: u64,
}

impl FieldDef {
    /// Returns true if this field represents PCL-style padding (`_` name).
    pub fn is_padding(&self) -> bool {
        self.name == "_"
    }
}

/// Define the schema of PCD format.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Schema {
    pub fields: Vec<FieldDef>,
}

impl Schema {
    pub fn is_empty(&self) -> bool {
        self.fields.is_empty()
    }

    pub fn len(&self) -> usize {
        self.fields.len()
    }

    pub fn iter(&self) -> std::slice::Iter<'_, FieldDef> {
        self.into_iter()
    }
}

impl Index<usize> for Schema {
    type Output = FieldDef;

    fn index(&self, index: usize) -> &Self::Output {
        self.fields.index(index)
    }
}

impl IntoIterator for Schema {
    type Item = FieldDef;
    type IntoIter = std::vec::IntoIter<FieldDef>;

    fn into_iter(self) -> Self::IntoIter {
        self.fields.into_iter()
    }
}

impl<'a> IntoIterator for &'a Schema {
    type Item = &'a FieldDef;
    type IntoIter = std::slice::Iter<'a, FieldDef>;

    fn into_iter(self) -> Self::IntoIter {
        self.fields.iter()
    }
}

impl FromIterator<(String, ValueKind, u64)> for Schema {
    fn from_iter<T: IntoIterator<Item = (String, ValueKind, u64)>>(iter: T) -> Self {
        let fields = iter
            .into_iter()
            .map(|(name, kind, count)| FieldDef { name, kind, count })
            .collect();
        Self { fields }
    }
}

impl<'a> FromIterator<(&'a str, ValueKind, u64)> for Schema {
    fn from_iter<T: IntoIterator<Item = (&'a str, ValueKind, u64)>>(iter: T) -> Self {
        iter.into_iter()
            .map(|(name, kind, count)| (name.to_string(), kind, count))
            .collect()
    }
}

impl FromIterator<FieldDef> for Schema {
    fn from_iter<T: IntoIterator<Item = FieldDef>>(iter: T) -> Self {
        Self {
            fields: iter.into_iter().collect(),
        }
    }
}

impl<'a> FromIterator<&'a FieldDef> for Schema {
    fn from_iter<T: IntoIterator<Item = &'a FieldDef>>(iter: T) -> Self {
        Self {
            fields: iter.into_iter().map(|field| field.to_owned()).collect(),
        }
    }
}