1#[cfg(feature = "compression")]
7pub mod compression;
8#[cfg(feature = "e57")]
9pub mod e57;
10pub mod error;
11pub mod lidar;
12pub mod mesh_attributes;
13#[cfg(feature = "io-mmap")]
14pub mod mmap;
15pub mod obj;
16#[cfg(feature = "las_laz")]
17pub mod pasture;
18pub mod pcd;
19pub mod ply;
20mod records;
21pub mod registry;
22pub mod render;
23#[cfg(feature = "ros2")]
24pub mod ros2;
25#[cfg(feature = "rosbag")]
26pub mod rosbag;
27pub mod serialization;
28pub mod stl;
29pub mod xyz_csv;
30
31#[cfg(test)]
32pub mod tests;
33
34#[cfg(feature = "compression")]
35pub use compression::{draco_decode, draco_encode, DracoCompressorPipeline, DracoConfig};
36#[cfg(feature = "e57")]
37pub use e57::{E57WriteOptions, RobustE57Reader, RobustE57Writer};
38pub use error::*;
39pub use lidar::{
40 LivoxLvx2Reader, LivoxLvxReader, OusterPcapReader, VelodyneKittiBinReader, VelodyneModel,
41 VelodynePcapReader,
42};
43pub use mesh_attributes::{ExtendedTriangleMesh, MeshAttributeOptions, MeshMetadata, Tangent, UV};
44pub use obj::{
45 Face, FaceVertex, Group, Material, ObjData, ObjWriteOptions, RobustObjReader, RobustObjWriter,
46};
47pub use pcd::{
48 PcdDataFormat, PcdFieldType, PcdHeader, PcdValue, PcdWriteOptions, RobustPcdReader,
49 RobustPcdWriter,
50};
51pub use ply::{PlyFormat, PlyValue, PlyWriteOptions, RobustPlyReader, RobustPlyWriter};
52pub use registry::{FormatHandler, IoRegistry};
53#[cfg(feature = "ros2")]
54pub use ros2::{
55 colored_normals_to_pointcloud2, colored_to_pointcloud2, normals_to_pointcloud2,
56 organized_xyz_to_pointcloud2, pointcloud2_to_colored, pointcloud2_to_colored_normals,
57 pointcloud2_to_normals, pointcloud2_to_organized_xyz, pointcloud2_to_xyz, xyz_to_pointcloud2,
58 PointCloud2Data, PointCloud2Info, PointField,
59};
60pub use serialization::{
61 AttributePreservingReader, AttributePreservingWriter, SerializationOptions,
62};
63pub use stl::{read_stl, write_stl, StlReader, StlWriteOptions, StlWriter};
64pub use xyz_csv::{
65 ColumnType, Delimiter, XyzCsvPoint, XyzCsvReader, XyzCsvSchema, XyzCsvStreamingReader,
66 XyzCsvWriteOptions, XyzCsvWriter,
67};
68
69use std::path::Path;
70use threecrate_core::{Point3f, PointCloud, Result, TriangleMesh};
71
72pub(crate) fn capacity_hint(count: usize) -> usize {
76 count.min(1 << 20)
77}
78
79pub trait PointCloudReader {
82 fn read_point_cloud<P: AsRef<std::path::Path>>(path: P) -> Result<PointCloud<Point3f>>;
83}
84
85pub trait PointCloudWriter {
87 fn write_point_cloud<P: AsRef<std::path::Path>>(
88 cloud: &PointCloud<Point3f>,
89 path: P,
90 ) -> Result<()>;
91}
92
93pub trait MeshReader {
95 fn read_mesh<P: AsRef<std::path::Path>>(path: P) -> Result<TriangleMesh>;
96}
97
98pub trait MeshWriter {
100 fn write_mesh<P: AsRef<std::path::Path>>(mesh: &TriangleMesh, path: P) -> Result<()>;
101}
102
103lazy_static::lazy_static! {
105 static ref IO_REGISTRY: IoRegistry = {
106 let mut registry = IoRegistry::new();
107
108 registry.register_point_cloud_handler("ply", Box::new(ply::PlyReader));
110 registry.register_mesh_handler("ply", Box::new(ply::PlyReader));
111 registry.register_point_cloud_writer("ply", Box::new(ply::PlyWriter));
112 registry.register_mesh_writer("ply", Box::new(ply::PlyWriter));
113
114 registry.register_mesh_handler("obj", Box::new(obj::ObjReader));
116 registry.register_mesh_writer("obj", Box::new(obj::ObjWriter));
117
118 registry.register_mesh_handler("stl", Box::new(stl::StlReader));
120 registry.register_mesh_writer("stl", Box::new(stl::StlWriter));
121
122 #[cfg(feature = "las_laz")]
124 {
125 registry.register_point_cloud_handler("las", Box::new(pasture::PastureReader));
126 registry.register_point_cloud_handler("laz", Box::new(pasture::PastureReader));
127 registry.register_point_cloud_writer("las", Box::new(pasture::PastureWriter));
128 registry.register_point_cloud_writer("laz", Box::new(pasture::PastureWriter));
129 }
130 registry.register_point_cloud_handler("pcd", Box::new(pcd::PcdReader));
131 registry.register_point_cloud_writer("pcd", Box::new(pcd::PcdWriter));
132
133 registry.register_point_cloud_handler("xyz", Box::new(xyz_csv::XyzCsvReader));
135 registry.register_point_cloud_handler("csv", Box::new(xyz_csv::XyzCsvReader));
136 registry.register_point_cloud_handler("txt", Box::new(xyz_csv::XyzCsvReader));
137 registry.register_point_cloud_writer("xyz", Box::new(xyz_csv::XyzCsvWriter));
138 registry.register_point_cloud_writer("csv", Box::new(xyz_csv::XyzCsvWriter));
139 registry.register_point_cloud_writer("txt", Box::new(xyz_csv::XyzCsvWriter));
140
141 registry.register_point_cloud_handler("bin", Box::new(lidar::VelodyneBinRegistryReader));
143 registry.register_point_cloud_handler("pcap", Box::new(lidar::VelodynePcapRegistryReader));
144 registry.register_point_cloud_handler("lvx", Box::new(lidar::LivoxLvxRegistryReader));
145 registry.register_point_cloud_handler("lvx2", Box::new(lidar::LivoxLvx2RegistryReader));
146
147 #[cfg(feature = "e57")]
149 {
150 registry.register_point_cloud_handler("e57", Box::new(e57::E57Reader));
151 registry.register_mesh_handler("e57", Box::new(e57::E57Reader));
152 registry.register_point_cloud_writer("e57", Box::new(e57::E57Writer));
153 registry.register_mesh_writer("e57", Box::new(e57::E57Writer));
154 }
155
156 #[cfg(feature = "compression")]
158 {
159 registry.register_point_cloud_handler("drc", Box::new(compression::DracoReader));
160 registry.register_point_cloud_writer("drc", Box::new(compression::DracoWriter));
161 }
162
163 registry
164 };
165}
166
167pub fn read_point_cloud<P: AsRef<Path>>(path: P) -> Result<PointCloud<Point3f>> {
169 let path = path.as_ref();
170 let extension = path.extension().and_then(|s| s.to_str()).ok_or_else(|| {
171 threecrate_core::Error::UnsupportedFormat("No file extension found".to_string())
172 })?;
173
174 IO_REGISTRY.read_point_cloud(path, extension)
175}
176
177pub fn read_mesh<P: AsRef<Path>>(path: P) -> Result<TriangleMesh> {
179 let path = path.as_ref();
180 let extension = path.extension().and_then(|s| s.to_str()).ok_or_else(|| {
181 threecrate_core::Error::UnsupportedFormat("No file extension found".to_string())
182 })?;
183
184 IO_REGISTRY.read_mesh(path, extension)
185}
186
187pub fn write_point_cloud<P: AsRef<Path>>(cloud: &PointCloud<Point3f>, path: P) -> Result<()> {
189 let path = path.as_ref();
190 let extension = path.extension().and_then(|s| s.to_str()).ok_or_else(|| {
191 threecrate_core::Error::UnsupportedFormat("No file extension found".to_string())
192 })?;
193
194 IO_REGISTRY.write_point_cloud(cloud, path, extension)
195}
196
197pub fn write_mesh<P: AsRef<Path>>(mesh: &TriangleMesh, path: P) -> Result<()> {
199 let path = path.as_ref();
200 let extension = path.extension().and_then(|s| s.to_str()).ok_or_else(|| {
201 threecrate_core::Error::UnsupportedFormat("No file extension found".to_string())
202 })?;
203
204 IO_REGISTRY.write_mesh(mesh, path, extension)
205}
206
207pub fn get_io_registry() -> &'static IoRegistry {
209 &IO_REGISTRY
210}
211
212pub fn read_point_cloud_iter<P: AsRef<Path>>(
243 path: P,
244 chunk_size: Option<usize>,
245) -> Result<Box<dyn Iterator<Item = Result<Point3f>> + Send + Sync>> {
246 let path = path.as_ref();
247 let extension = path.extension().and_then(|s| s.to_str()).ok_or_else(|| {
248 threecrate_core::Error::UnsupportedFormat("No file extension found".to_string())
249 })?;
250
251 match extension.to_ascii_lowercase().as_str() {
252 "ply" => {
253 let iter = ply::PlyStreamingReader::new(path, chunk_size.unwrap_or(1000))?;
254 Ok(Box::new(iter))
255 }
256 "obj" => {
257 let iter = obj::ObjStreamingReader::new(path, chunk_size.unwrap_or(1000))?;
258 Ok(Box::new(iter))
259 }
260 "xyz" | "csv" | "txt" => {
261 let iter = xyz_csv::XyzCsvStreamingReader::new(path, chunk_size.unwrap_or(1000))?;
262 Ok(Box::new(iter))
263 }
264 _ => Err(threecrate_core::Error::UnsupportedFormat(format!(
265 "Streaming not supported for format: {}",
266 extension
267 ))),
268 }
269}
270
271pub fn read_mesh_iter<P: AsRef<Path>>(
302 path: P,
303 chunk_size: Option<usize>,
304) -> Result<Box<dyn Iterator<Item = Result<[usize; 3]>> + Send + Sync>> {
305 let path = path.as_ref();
306 let extension = path.extension().and_then(|s| s.to_str()).ok_or_else(|| {
307 threecrate_core::Error::UnsupportedFormat("No file extension found".to_string())
308 })?;
309
310 match extension.to_ascii_lowercase().as_str() {
311 "ply" => {
312 let iter = ply::PlyMeshStreamingReader::new(path, chunk_size.unwrap_or(1000))?;
313 Ok(Box::new(iter))
314 }
315 "obj" => {
316 let iter = obj::ObjMeshStreamingReader::new(path, chunk_size.unwrap_or(1000))?;
317 Ok(Box::new(iter))
318 }
319 _ => Err(threecrate_core::Error::UnsupportedFormat(format!(
320 "Streaming not supported for format: {}",
321 extension
322 ))),
323 }
324}
325
326