Skip to main content

spatialrust_io/ply/
schema.rs

1use spatialrust_core::{DType, FieldSemantic, PointField, PointSchema};
2
3use crate::error::IoError;
4use crate::ply::header::{PlyProperty, PlyPropertyKind};
5
6/// Maps a PLY property name to a SpatialRust semantic.
7#[must_use]
8pub fn infer_property_semantic(name: &str) -> FieldSemantic {
9    match name.to_ascii_lowercase().as_str() {
10        "x" => FieldSemantic::PositionX,
11        "y" => FieldSemantic::PositionY,
12        "z" => FieldSemantic::PositionZ,
13        "nx" | "normal_x" => FieldSemantic::NormalX,
14        "ny" | "normal_y" => FieldSemantic::NormalY,
15        "nz" | "normal_z" => FieldSemantic::NormalZ,
16        "intensity" | "i" => FieldSemantic::Intensity,
17        "curvature" => FieldSemantic::Curvature,
18        "ring" => FieldSemantic::Ring,
19        "timestamp" | "t" | "time_offset" => FieldSemantic::TimeOffset,
20        "label" => FieldSemantic::Label,
21        "r" | "red" => FieldSemantic::ColorR,
22        "g" | "green" => FieldSemantic::ColorG,
23        "b" | "blue" => FieldSemantic::ColorB,
24        _ => FieldSemantic::Unknown,
25    }
26}
27
28impl PlyProperty {
29    /// Maps this property to a SpatialRust dtype.
30    pub fn dtype(&self) -> Result<DType, IoError> {
31        match self.kind {
32            PlyPropertyKind::Float => Ok(DType::F32),
33            PlyPropertyKind::Double => Ok(DType::F64),
34            PlyPropertyKind::Int => Ok(DType::I32),
35            PlyPropertyKind::UInt => Ok(DType::U32),
36            PlyPropertyKind::UChar => Ok(DType::U8),
37            PlyPropertyKind::UShort => Ok(DType::U16),
38            PlyPropertyKind::Char | PlyPropertyKind::Short => Ok(DType::I32),
39        }
40    }
41}
42
43/// Builds a SpatialRust schema from PLY vertex properties.
44pub fn schema_from_ply_properties(properties: &[PlyProperty]) -> Result<PointSchema, IoError> {
45    let mut schema = PointSchema::new();
46    for property in properties {
47        let dtype = property.dtype()?;
48        let semantic = infer_property_semantic(&property.name);
49        schema = schema.with_field(PointField::scalar(property.name.clone(), semantic, dtype));
50    }
51    Ok(schema)
52}
53
54/// Builds a PLY property description from a SpatialRust field.
55pub fn ply_property_from_field(field: &PointField) -> Result<PlyProperty, IoError> {
56    let name = match field.semantic {
57        FieldSemantic::PositionX => "x",
58        FieldSemantic::PositionY => "y",
59        FieldSemantic::PositionZ => "z",
60        FieldSemantic::NormalX => "nx",
61        FieldSemantic::NormalY => "ny",
62        FieldSemantic::NormalZ => "nz",
63        FieldSemantic::Intensity => "intensity",
64        FieldSemantic::Curvature => "curvature",
65        FieldSemantic::Ring => "ring",
66        FieldSemantic::TimeOffset => "timestamp",
67        FieldSemantic::Label => "label",
68        FieldSemantic::ColorR => "red",
69        FieldSemantic::ColorG => "green",
70        FieldSemantic::ColorB => "blue",
71        _ => field.name.as_str(),
72    }
73    .to_owned();
74
75    let kind = match field.dtype {
76        DType::F32 | DType::F16 => PlyPropertyKind::Float,
77        DType::F64 => PlyPropertyKind::Double,
78        DType::I32 => PlyPropertyKind::Int,
79        DType::U32 => PlyPropertyKind::UInt,
80        DType::U8 => PlyPropertyKind::UChar,
81        DType::U16 => PlyPropertyKind::UShort,
82    };
83
84    Ok(PlyProperty { name, kind })
85}
86
87#[cfg(test)]
88mod tests {
89    use super::{infer_property_semantic, schema_from_ply_properties};
90    use crate::ply::header::{PlyProperty, PlyPropertyKind};
91    use spatialrust_core::FieldSemantic;
92
93    #[test]
94    fn maps_xyz_semantics() {
95        assert_eq!(infer_property_semantic("x"), FieldSemantic::PositionX);
96        assert_eq!(infer_property_semantic("red"), FieldSemantic::ColorR);
97    }
98
99    #[test]
100    fn builds_xyz_schema() {
101        let properties = vec![
102            PlyProperty { name: "x".into(), kind: PlyPropertyKind::Float },
103            PlyProperty { name: "y".into(), kind: PlyPropertyKind::Float },
104            PlyProperty { name: "z".into(), kind: PlyPropertyKind::Float },
105        ];
106        let schema = schema_from_ply_properties(&properties).unwrap();
107        assert_eq!(schema.len(), 3);
108    }
109}