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