draco_io/traits.rs
1//! Common traits for geometry readers and writers.
2//!
3//! These traits define the shared mesh and point-cloud API for the OBJ, PLY,
4//! and FBX adapters in this crate. They intentionally do not model a lossless
5//! scene document; full glTF scene workflows use `draco-gltf::Document` and
6//! `draco-gltf::Import` instead.
7//!
8//! # Usage
9//!
10//! Import the trait to access its methods:
11//!
12//! ```no_run
13//! # #[cfg(feature = "obj-writer")]
14//! # fn main() -> Result<(), std::io::Error> {
15//! use draco_io::{Writer, ObjWriter};
16//! # let mesh = draco_core::mesh::Mesh::new();
17//!
18//! let mut writer = ObjWriter::new();
19//! writer.add_mesh(&mesh, Some("Name"))?; // Calls trait method
20//! writer.write("output.obj")?;
21//! # Ok(())
22//! # }
23//! # #[cfg(not(feature = "obj-writer"))]
24//! # fn main() {}
25//! ```
26//!
27//! This enables generic functions:
28//!
29//! ```no_run
30//! use std::io;
31//! use draco_core::mesh::Mesh;
32//! use draco_io::Writer;
33//!
34//! fn save<W: Writer>(mut w: W, mesh: &Mesh) -> io::Result<()> {
35//! w.add_mesh(mesh, Some("Model"))?;
36//! w.write("output.ext")
37//! }
38//! ```
39
40use std::io::{self, Write};
41use std::path::Path;
42
43use draco_core::mesh::Mesh;
44
45/// Common interface for geometry writers.
46///
47/// The mesh-format writers in this crate implement this trait:
48///
49/// ```no_run
50/// use std::io;
51/// use draco_core::mesh::Mesh;
52/// use draco_io::Writer;
53///
54/// fn write_mesh<W: Writer>(mut writer: W, mesh: &Mesh) -> io::Result<()> {
55/// writer.add_mesh(mesh, Some("MyMesh"))?;
56/// writer.write("output.ext")
57/// }
58/// ```
59pub trait Writer: Sized {
60 /// Create a new writer instance.
61 fn new() -> Self;
62
63 /// Add a mesh to be written.
64 ///
65 /// # Arguments
66 /// * `mesh` - The mesh to add
67 /// * `name` - Optional name for the mesh (if format supports naming)
68 ///
69 /// # Returns
70 /// * `Ok(())` on success
71 /// * `Err` if the format cannot handle this mesh (e.g., compression failure)
72 fn add_mesh(&mut self, mesh: &Mesh, name: Option<&str>) -> io::Result<()>;
73
74 /// Write all added meshes to a file.
75 ///
76 /// # Arguments
77 /// * `path` - Output file path
78 fn write<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
79
80 /// Get the number of meshes/vertices added.
81 fn vertex_count(&self) -> usize;
82
83 /// Get the number of faces added (if applicable).
84 fn face_count(&self) -> usize {
85 0
86 }
87}
88
89/// Common interface for geometry readers.
90///
91/// The mesh-format readers in this crate implement this trait:
92///
93/// ```no_run
94/// use std::io;
95/// use draco_core::mesh::Mesh;
96/// use draco_io::Reader;
97///
98/// fn load_mesh<R: Reader>(path: &str) -> io::Result<Mesh> {
99/// let mut reader = R::open(path)?;
100/// reader.read_mesh()
101/// }
102/// ```
103pub trait Reader: Sized {
104 /// Open a file for reading.
105 ///
106 /// # Arguments
107 /// * `path` - Input file path
108 fn open<P: AsRef<Path>>(path: P) -> io::Result<Self>;
109
110 /// Read multiple meshes (a scene) from the file.
111 ///
112 /// Formats that represent scenes or multiple mesh primitives should implement
113 /// this method and return all meshes in the file or scene.
114 fn read_meshes(&mut self) -> io::Result<Vec<Mesh>>;
115
116 /// Read a single mesh from the file.
117 ///
118 /// Default implementation returns the first mesh from `read_meshes()`.
119 fn read_mesh(&mut self) -> io::Result<Mesh> {
120 let meshes = self.read_meshes()?;
121 if let Some(m) = meshes.into_iter().next() {
122 Ok(m)
123 } else {
124 Err(io::Error::new(io::ErrorKind::InvalidData, "No mesh found"))
125 }
126 }
127}
128
129/// Common interface for readers that can be constructed from in-memory bytes.
130///
131/// This complements [`Reader::open`] for callers that already have file bytes
132/// loaded, or that are working in browser/embedded environments without direct
133/// filesystem access.
134pub trait ReadFromBytes: Sized {
135 /// Create a reader from a complete file payload.
136 fn from_bytes(bytes: &[u8]) -> io::Result<Self>;
137}
138
139/// Common interface for writers that can emit a complete file payload.
140///
141/// This complements [`Writer::write`] for callers that need to send bytes over
142/// the network, store them in an archive, or run roundtrips without temporary
143/// files.
144pub trait WriteToBytes: Writer {
145 /// Write all added data into a byte vector.
146 fn write_to_vec(&self) -> io::Result<Vec<u8>>;
147
148 /// Write all added data into an arbitrary byte sink.
149 fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
150 writer.write_all(&self.write_to_vec()?)
151 }
152}
153
154/// Extended writer trait for point cloud support.
155///
156/// Writers that can output point clouds (without faces) implement this trait.
157pub trait PointCloudWriter: Writer {
158 /// Add raw point positions.
159 fn add_points(&mut self, points: &[[f32; 3]]);
160
161 /// Add a single point.
162 fn add_point(&mut self, point: [f32; 3]) {
163 self.add_points(&[point]);
164 }
165}
166
167/// Extended reader trait for point cloud support.
168///
169/// Readers that can read point clouds implement this trait.
170pub trait PointCloudReader: Reader {
171 /// Read point positions only (no faces or topology).
172 fn read_points(&mut self) -> io::Result<Vec<[f32; 3]>>;
173}