cloudini 0.3.1

The cloudini point cloud compression library for Rust.
Documentation
//! # 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 aforementioned feature (`no-default-features = true`).
//!
//! ## Quickstart
//!
//! ```rust
//! use cloudini::ros::{CompressExt, CompressionConfig, PointCloud2Msg};
//! use ros_pointcloud2::points::PointXYZI;
//!
//! let resolution = 0.001_f32; // 1 mm
//!
//! // Build a PointCloud2Msg from a vec of typed points (simulated 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()).unwrap();
//! let raw_size = cloud.data.len();
//!
//! // Compress — 1 mm lossy + ZSTD
//! let compressed = cloud.compress(CompressionConfig::lossy_zstd(resolution)).unwrap();
//! assert_eq!(compressed.format, "cloudini/lossy/zstd");
//! assert!(
//!     compressed.compressed_data.len() < raw_size,
//!     "compressed ({} B) should be smaller than raw ({} B)",
//!     compressed.compressed_data.len(), raw_size,
//! );
//!
//! // Decompress — no metadata needed, it is all in the header
//! let restored = compressed.decompress().unwrap();
//! assert_eq!(restored.dimensions.width, 200);
//!
//! // 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().unwrap().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);
//! }
//! ```
//!
//! ### Compression presets
//!
//! | Constructor | Encoding | Compression | Max float error |
//! |---|---|---|---|
//! | [`ros::CompressionConfig::lossy_zstd`] | Lossy | ZSTD | `res / 2` |
//! | [`ros::CompressionConfig::lossy_lz4`] | Lossy | LZ4 | `res / 2` |
//! | [`ros::CompressionConfig::lossless_zstd`] | Lossless XOR | ZSTD | 0 |
//! | [`ros::CompressionConfig::lossless_lz4`] | Lossless XOR | LZ4 | 0 |
//!
//! [`ros::CompressionConfig::default`] is `lossy_zstd(0.001)` — suitable for most Lidar workloads.
//!
//! ## Raw API
//!
//! Disable the default feature and drive the encoder/decoder directly:
//!
//! ```toml
//! cloudini = { version = "0.1.0", default-features = false }
//! ```
//!
//! ```rust,no_run
//! use cloudini::{
//!     CompressionOption, EncodingInfo, EncodingOptions, FieldType, PointField,
//!     PointcloudDecoder, PointcloudEncoder,
//! };
//!
//! // Describe the point layout: x, y, z (f32) + intensity (u8) = 13 bytes/point
//! 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,
//!     height: 1,
//!     point_step: 13,
//!     encoding_opt: EncodingOptions::Lossy,
//!     compression_opt: CompressionOption::Zstd,
//!     ..EncodingInfo::default()
//! };
//!
//! // raw_cloud: width * height * point_step bytes of packed point data
//! let raw_cloud: Vec<u8> = vec![0u8; 1000 * 13];
//! let compressed = PointcloudEncoder::new(info).encode(&raw_cloud).unwrap();
//!
//! let (_info, decoded) = PointcloudDecoder::new().decode(&compressed).unwrap();
//! assert_eq!(decoded.len(), raw_cloud.len());
//! ```
//!
//! ## Encoding modes
//!
//! | Mode | Float32/64 with `resolution` | Float64 without `resolution` | Integers |
//! |---|---|---|---|
//! | [`EncodingOptions::None`] | raw copy | raw copy | raw copy |
//! | [`EncodingOptions::Lossy`] | quantise → delta → varint | raw copy | delta → varint |
//! | [`EncodingOptions::Lossless`] | raw copy (f32) / XOR (f64) | XOR with prev bits | delta → varint |
//!
//! ## Compression backends
//!
//! | [`CompressionOption`] | Notes |
//! |---|---|
//! | `None` | field encoding only, no block compression |
//! | `Lz4` | fast, moderate ratio |
//! | `Zstd` | higher ratio, higher CPU cost |
#![crate_type = "lib"]

pub mod decoder;
pub mod encoder;
pub mod error;
pub mod field_decoder;
pub mod field_encoder;
pub mod header;
pub mod types;
pub mod varint;

#[cfg(feature = "ros")]
pub mod ros;

#[cfg(feature = "use_cpp")]
pub mod cpp_decoder;
#[cfg(feature = "use_cpp")]
pub mod cpp_encoder;
#[cfg(feature = "use_cpp")]
pub mod ffi;

pub use decoder::PointcloudDecoder;
pub use encoder::PointcloudEncoder;
pub use error::{Error, Result};
pub use header::{decode_header, encode_header, encoding_info_from_yaml, encoding_info_to_yaml};
pub use types::{
    CompressionOption, ENCODING_VERSION, EncodingInfo, EncodingOptions, FieldType, MAGIC_HEADER,
    POINTS_PER_CHUNK, PointField,
};

#[cfg(feature = "use_cpp")]
pub use cpp_decoder::CppPointcloudDecoder;
#[cfg(feature = "use_cpp")]
pub use cpp_encoder::CppPointcloudEncoder;