1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! # 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 |
pub use PointcloudDecoder;
pub use PointcloudEncoder;
pub use ;
pub use ;
pub use ;
pub use CppPointcloudDecoder;
pub use CppPointcloudEncoder;