spatialrust_io/ply/
schema.rs1use spatialrust_core::{DType, FieldSemantic, PointField, PointSchema};
2
3use crate::error::IoError;
4use crate::ply::header::{PlyProperty, PlyPropertyKind};
5
6#[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 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
43pub 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
54pub 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}