Skip to main content

spatialrust_tensor/
spatial.rs

1//! Zero-copy bridges from schema-aware point-cloud columns.
2
3use spatialrust_core::{SpatialError, SpatialTensor};
4
5use crate::{DataType, Device, TensorDescriptor, TensorError, TensorView};
6
7/// Errors raised while exposing a point-cloud column as a generic tensor.
8#[derive(Debug, thiserror::Error)]
9pub enum SpatialTensorBridgeError {
10    /// The requested field is absent or does not have the required dtype.
11    #[error(transparent)]
12    Spatial(#[from] SpatialError),
13    /// The generic tensor descriptor or storage is invalid.
14    #[error(transparent)]
15    Tensor(#[from] TensorError),
16}
17
18/// Borrows one `f32` point field as a zero-copy `[point_count]` CPU tensor.
19///
20/// Point fields remain separate Schema-SoA columns. Interleaving XYZ or other
21/// fields requires a separately named packing operation and is never hidden by
22/// this bridge.
23pub fn spatial_f32_field_view<'a>(
24    tensor: &SpatialTensor<'a>,
25    field_name: &str,
26) -> Result<TensorView<'a>, SpatialTensorBridgeError> {
27    let values = tensor.cloud().field(field_name)?.as_f32()?;
28    let descriptor = TensorDescriptor::contiguous(DataType::F32, vec![values.len()], Device::CPU);
29    Ok(TensorView::try_new(bytemuck::cast_slice(values), descriptor)?)
30}
31
32#[cfg(test)]
33mod tests {
34    use super::spatial_f32_field_view;
35    use spatialrust_core::{PointCloudBuilder, SpatialTensor};
36
37    #[test]
38    fn point_field_is_borrowed_without_interleaving() {
39        let mut builder = PointCloudBuilder::xyz();
40        builder.push_point([1.0, 2.0, 3.0]).unwrap();
41        builder.push_point([4.0, 5.0, 6.0]).unwrap();
42        let cloud = builder.build().unwrap();
43        let spatial = SpatialTensor::new(&cloud, 1).unwrap();
44        let tensor = spatial_f32_field_view(&spatial, "x").unwrap();
45        assert_eq!(tensor.descriptor().shape(), &[2]);
46        assert_eq!(bytemuck::cast_slice::<u8, f32>(tensor.allocation_bytes()), &[1.0, 4.0]);
47        assert_eq!(
48            tensor.allocation_bytes().as_ptr(),
49            cloud.field("x").unwrap().as_f32().unwrap().as_ptr().cast()
50        );
51    }
52}