cloudini 0.2.1

The cloudini point cloud compression library for Rust.
# cloudini

The [cloudini](https://github.com/facontidavide/cloudini) point cloud compression library as a Rust crate.

The crate provides Rust bindings of the original C++ library by default using the feature flag `use_cpp`. It requires CMake and a C++ 20 compiler to be installed. There is a Rust-only rewrite available by disabling the feature aforementioned feature (`no-default-features = true`).

## Quickstart

Using the `ros_pointcloud2` integration via the standard feature `ros`:

```toml
[dependencies]
cloudini = "0.2.0"
ros_pointcloud2 = "1.0.0-rc.3"
```

```rust
use cloudini::ros::{CompressExt, CompressionConfig, PointCloud2Msg};
use ros_pointcloud2::points::PointXYZI;

let resolution = 0.001_f32; // 1 mm

// Simulate a Lidar scan
let points: Vec<PointXYZI> = (0..200)
    .map(|i| {
        let t = i as f32 * 0.05;
        PointXYZI::new(t.sin() * 5.0, t.cos() * 5.0, t * 0.1, i as f32)
    })
    .collect();
let cloud = PointCloud2Msg::try_from_vec(points.clone())?;
let raw_size = cloud.data.len();

// Compress — 1 mm lossy + ZSTD
let compressed = cloud.compress(CompressionConfig::lossy_zstd(resolution))?;
// compressed is a CompressedPointCloud2 — ship it over DDS, store to disk, etc.
assert!(compressed.compressed_data.len() < raw_size);

// Decompress
let restored = compressed.decompress()?;

// Each float component is within resolution / 2 of the original
let tol = resolution / 2.0 + 1e-6;
let restored_points: Vec<PointXYZI> = restored.try_into_iter()?.collect();
for (orig, rest) in points.iter().zip(restored_points.iter()) {
    assert!((orig.x         - rest.x        ).abs() <= tol);
    assert!((orig.y         - rest.y        ).abs() <= tol);
    assert!((orig.z         - rest.z        ).abs() <= tol);
    assert!((orig.intensity - rest.intensity).abs() <= tol);
}
```

## Raw

You can disable the `ros_pointcloud2` convenience wrapper by setting `no-default-features = true` and use the underlying enoder and decoder.

### Encode

```rust
use cloudini::{
    CompressionOption, EncodingInfo, EncodingOptions, FieldType, PointField,
    PointcloudEncoder,
};

// Describe the point layout.
// point_step = sum of all field sizes = 4+4+4+1 = 13 bytes
let info = EncodingInfo {
    fields: vec![
        PointField { name: "x".into(), offset: 0,  field_type: FieldType::Float32, resolution: Some(0.001) },
        PointField { name: "y".into(), offset: 4,  field_type: FieldType::Float32, resolution: Some(0.001) },
        PointField { name: "z".into(), offset: 8,  field_type: FieldType::Float32, resolution: Some(0.001) },
        PointField { name: "intensity".into(), offset: 12, field_type: FieldType::Uint8, resolution: None },
    ],
    width: 1000,        // number of points
    height: 1,          // 1 for unorganised clouds
    point_step: 13,
    encoding_opt: EncodingOptions::Lossy,
    compression_opt: CompressionOption::Zstd,
    ..EncodingInfo::default()
};

let encoder = PointcloudEncoder::new(info);
let compressed: Vec<u8> = encoder.encode(&raw_cloud)?;
// compressed is a self-contained buffer: header + chunk data
```

### Decode

```rust
use cloudini::PointcloudDecoder;

let decoder = PointcloudDecoder::new();
let (info, decoded): (_, Vec<u8>) = decoder.decode(&compressed)?;

// decoded.len() == info.width * info.height * info.point_step
```

## Maintaining Note

We must fix the CMake when updating the cloudini library.

`cloudini-cpp/cloudini_lib/CMakeLists.txt`

old
```cmake
if(ament_cmake_FOUND)
  add_library(cloudini_lib SHARED
    ${CLOUDINI_SRC}
    ${PCL_SRC}
  )
```

new
```cmake
if(BUILD_SHARED_LIBS)
  add_library(cloudini_lib SHARED
    ${CLOUDINI_SRC}
    ${PCL_SRC}
  )
```

### License

The original Cloudini library is bundled and licensed under the Apache 2 License. See `./cloudini-cpp/LICENSE`.

The Rust code follows the typical dual-license model:

<sup>
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
</sup>

<br>

<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
</sub>