Skip to main content

spatialrust_core/
capabilities.rs

1use crate::{FieldSemantic, PointCloud, SpatialError, SpatialResult};
2
3/// Capability trait for point clouds with 3D positions.
4pub trait HasPositions3 {
5    /// Returns x/y/z columns as `f32` slices.
6    fn positions3(&self) -> SpatialResult<(&[f32], &[f32], &[f32])>;
7}
8
9/// Capability trait for point clouds with surface normals.
10pub trait HasNormals3 {
11    /// Returns normal x/y/z columns as `f32` slices.
12    fn normals3(&self) -> SpatialResult<(&[f32], &[f32], &[f32])>;
13}
14
15/// Capability trait for point clouds with intensity values.
16pub trait HasIntensity {
17    /// Returns the intensity column as an `f32` slice.
18    fn intensity(&self) -> SpatialResult<&[f32]>;
19}
20
21impl HasPositions3 for PointCloud {
22    fn positions3(&self) -> SpatialResult<(&[f32], &[f32], &[f32])> {
23        self.schema().validate_positions()?;
24        let x = self.field_name_for_semantic(FieldSemantic::PositionX)?;
25        let y = self.field_name_for_semantic(FieldSemantic::PositionY)?;
26        let z = self.field_name_for_semantic(FieldSemantic::PositionZ)?;
27        Ok((self.field(x)?.as_f32()?, self.field(y)?.as_f32()?, self.field(z)?.as_f32()?))
28    }
29}
30
31impl HasNormals3 for PointCloud {
32    fn normals3(&self) -> SpatialResult<(&[f32], &[f32], &[f32])> {
33        let x = self.field_name_for_semantic(FieldSemantic::NormalX)?;
34        let y = self.field_name_for_semantic(FieldSemantic::NormalY)?;
35        let z = self.field_name_for_semantic(FieldSemantic::NormalZ)?;
36        Ok((self.field(x)?.as_f32()?, self.field(y)?.as_f32()?, self.field(z)?.as_f32()?))
37    }
38}
39
40impl HasIntensity for PointCloud {
41    fn intensity(&self) -> SpatialResult<&[f32]> {
42        let name = self.field_name_for_semantic(FieldSemantic::Intensity)?;
43        self.field(name)?.as_f32()
44    }
45}
46
47impl PointCloud {
48    fn field_name_for_semantic(&self, semantic: FieldSemantic) -> SpatialResult<&str> {
49        self.schema()
50            .find_semantic(semantic)
51            .map(|field| field.name.as_str())
52            .ok_or_else(|| SpatialError::MissingField(format!("{semantic:?}")))
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::{HasIntensity, HasNormals3, HasPositions3};
59    use crate::{PointCloudBuilder, StandardSchemas};
60
61    #[test]
62    fn positions3_capability() {
63        let mut builder = PointCloudBuilder::new(StandardSchemas::point_xyz());
64        builder.push_point([1.0, 2.0, 3.0]).unwrap();
65        let cloud = builder.build().unwrap();
66        let (x, y, z) = cloud.positions3().unwrap();
67        assert_eq!(x, &[1.0]);
68        assert_eq!(y, &[2.0]);
69        assert_eq!(z, &[3.0]);
70    }
71
72    #[test]
73    fn normal_and_intensity_capabilities() {
74        let mut builder = PointCloudBuilder::new(StandardSchemas::point_xyzinormal());
75        builder.push_point([0.0, 0.0, 0.0, 0.5, 0.0, 1.0, 0.0]).unwrap();
76        let cloud = builder.build().unwrap();
77        let (_, _, nz) = cloud.normals3().unwrap();
78        assert_eq!(cloud.intensity().unwrap(), &[0.5]);
79        assert_eq!(nz, &[0.0]);
80    }
81}