lib3mf/lib.rs
1//! # lib3mf
2//!
3//! A pure Rust implementation for reading and writing 3MF (3D Manufacturing Format) files.
4//!
5//! This library provides functionality to read, parse, create, and write 3MF files, which are ZIP-based
6//! containers following the Open Packaging Conventions (OPC) standard and containing
7//! XML-based 3D model data.
8//!
9//! ## Features
10//!
11//! - Pure Rust implementation with no unsafe code
12//! - Parse 3MF file structure (ZIP/OPC container)
13//! - Read 3D model data including meshes, vertices, and triangles
14//! - **Write and serialize 3MF files**
15//! - Support for materials and colors
16//! - Metadata extraction and writing
17//! - Round-trip support (read-write-read)
18//! - **Mesh-plane slicing with contour extraction**
19//!
20//! ## Reading Example
21//!
22//! ```no_run
23//! use lib3mf::Model;
24//! use std::fs::File;
25//!
26//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
27//! let file = File::open("model.3mf")?;
28//! let model = Model::from_reader(file)?;
29//!
30//! println!("Model contains {} objects", model.resources.objects.len());
31//! # Ok(())
32//! # }
33//! ```
34//!
35//! ## Writing Example
36//!
37//! ```no_run
38//! use lib3mf::{Model, Object, Mesh, Vertex, Triangle, BuildItem};
39//!
40//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
41//! // Create a new model
42//! let mut model = Model::new();
43//!
44//! // Create a mesh with a simple triangle
45//! let mut mesh = Mesh::new();
46//! mesh.vertices.push(Vertex::new(0.0, 0.0, 0.0));
47//! mesh.vertices.push(Vertex::new(10.0, 0.0, 0.0));
48//! mesh.vertices.push(Vertex::new(5.0, 10.0, 0.0));
49//! mesh.triangles.push(Triangle::new(0, 1, 2));
50//!
51//! // Create an object with the mesh
52//! let mut object = Object::new(1);
53//! object.mesh = Some(mesh);
54//!
55//! // Add to resources and build
56//! model.resources.objects.push(object);
57//! model.build.items.push(BuildItem::new(1));
58//!
59//! // Write to file
60//! model.write_to_file("output.3mf")?;
61//! # Ok(())
62//! # }
63//! ```
64//!
65//! ## Mesh Slicing Example
66//!
67//! Requires the `mesh-ops` feature (enabled by default).
68//!
69//! ```ignore
70//! use lib3mf::{collect_intersection_segments, assemble_contours, Mesh, Vertex, Triangle};
71//!
72//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
73//! // Create or load a mesh
74//! let mut mesh = Mesh::new();
75//! // ... add vertices and triangles ...
76//!
77//! // Slice the mesh at Z=5.0
78//! let segments = collect_intersection_segments(&mesh, 5.0);
79//!
80//! // Assemble segments into closed contours
81//! let contours = assemble_contours(segments, 1e-6);
82//!
83//! println!("Found {} contours at Z=5.0", contours.len());
84//! for (i, contour) in contours.iter().enumerate() {
85//! println!("Contour {} has {} vertices", i, contour.len());
86//! }
87//! # Ok(())
88//! # }
89//! ```
90
91#![forbid(unsafe_code)]
92#![warn(missing_docs)]
93
94pub mod error;
95pub mod extension;
96pub mod extensions;
97#[cfg(feature = "mesh-ops")]
98pub mod mesh_ops;
99pub mod model;
100pub mod opc;
101pub mod parser;
102#[cfg(feature = "polygon-ops")]
103pub mod polygon_clipping;
104#[cfg(feature = "polygon-ops")]
105pub mod polygon_triangulation;
106pub mod streaming;
107pub mod validator;
108mod writer;
109
110// Decryption support for SecureContent (test keys only, for Suite 8 validation)
111#[cfg(feature = "crypto")]
112pub mod decryption;
113
114// Key provider trait for custom encryption/decryption
115pub mod key_provider;
116
117pub use error::{Error, Result};
118pub use extension::{ExtensionHandler, ExtensionRegistry};
119pub use extensions::{
120 BeamLatticeExtensionHandler, BooleanOperationsExtensionHandler, DisplacementExtensionHandler,
121 MaterialExtensionHandler, ProductionExtensionHandler, SecureContentExtensionHandler,
122 SliceExtensionHandler, create_default_registry, register_all_handlers,
123};
124pub use key_provider::KeyProvider;
125#[cfg(feature = "mesh-ops")]
126pub use mesh_ops::{
127 Point2D, SubdivisionMethod, SubdivisionOptions, assemble_contours,
128 collect_intersection_segments, subdivide, subdivide_simple, triangle_plane_intersection,
129};
130pub use model::{
131 AccessRight, BaseMaterial, BaseMaterialGroup, Beam, BeamCapMode, BeamSet, BlendMethod,
132 BooleanOpType, BooleanRef, BooleanShape, Build, BuildItem, CEKParams, Channel, ColorGroup,
133 Component, Composite, CompositeMaterials, Consumer, CustomElementHandler, CustomElementResult,
134 CustomExtensionContext, CustomExtensionInfo, CustomValidationHandler, Disp2DCoords,
135 Disp2DGroup, Displacement2D, DisplacementMesh, DisplacementTriangle, Extension, FilterMode,
136 ImplicitVolume, KEKParams, Material, Mesh, MetadataEntry, Model, Multi, MultiProperties,
137 NormVector, NormVectorGroup, Object, ObjectType, ParserConfig, ProductionInfo, ResourceData,
138 ResourceDataGroup, Resources, SecureContentInfo, Slice, SlicePolygon, SliceRef, SliceSegment,
139 SliceStack, Tex2Coord, Texture2D, Texture2DGroup, Thumbnail, TileStyle, Triangle, Vertex,
140 Vertex2D, VolumetricBoundary, VolumetricData, VolumetricProperty, VolumetricPropertyGroup,
141 Voxel, VoxelGrid,
142};
143
144use std::io::Read;
145
146impl Model {
147 /// Parse a 3MF file from a reader
148 ///
149 /// This method uses the default parser configuration which supports all known extensions.
150 /// For backward compatibility, this will accept files with any required extensions.
151 ///
152 /// # Arguments
153 ///
154 /// * `reader` - A reader containing the 3MF file data
155 ///
156 /// # Example
157 ///
158 /// ```no_run
159 /// use lib3mf::Model;
160 /// use std::fs::File;
161 ///
162 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
163 /// let file = File::open("model.3mf")?;
164 /// let model = Model::from_reader(file)?;
165 /// # Ok(())
166 /// # }
167 /// ```
168 pub fn from_reader<R: Read + std::io::Seek>(reader: R) -> Result<Self> {
169 // Use default config which supports all extensions for backward compatibility
170 Self::from_reader_with_config(reader, ParserConfig::with_all_extensions())
171 }
172
173 /// Parse a 3MF file from a reader with custom configuration
174 ///
175 /// This method allows you to specify which extensions you support.
176 /// If the file requires an extension you don't support, an error will be returned.
177 ///
178 /// # Arguments
179 ///
180 /// * `reader` - A reader containing the 3MF file data
181 /// * `config` - Parser configuration specifying supported extensions
182 ///
183 /// # Example
184 ///
185 /// ```no_run
186 /// use lib3mf::{Model, ParserConfig, Extension};
187 /// use std::fs::File;
188 ///
189 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
190 /// let file = File::open("model.3mf")?;
191 ///
192 /// // Only support core and material extensions
193 /// let config = ParserConfig::new()
194 /// .with_extension(Extension::Material);
195 ///
196 /// let model = Model::from_reader_with_config(file, config)?;
197 /// # Ok(())
198 /// # }
199 /// ```
200 pub fn from_reader_with_config<R: Read + std::io::Seek>(
201 reader: R,
202 config: ParserConfig,
203 ) -> Result<Self> {
204 parser::parse_3mf_with_config(reader, config)
205 }
206
207 /// Read thumbnail binary data from a 3MF file
208 ///
209 /// Returns the thumbnail image data as a byte vector if a thumbnail is present.
210 /// Returns None if no thumbnail is found.
211 ///
212 /// This is a convenience method that reads the thumbnail from a separate reader.
213 /// The model metadata must have already been parsed to know if a thumbnail exists.
214 ///
215 /// # Arguments
216 ///
217 /// * `reader` - A reader containing the 3MF file data
218 ///
219 /// # Example
220 ///
221 /// ```no_run
222 /// use lib3mf::Model;
223 /// use std::fs::File;
224 ///
225 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
226 /// let file = File::open("model.3mf")?;
227 /// if let Some(thumbnail_data) = Model::read_thumbnail(file)? {
228 /// println!("Thumbnail size: {} bytes", thumbnail_data.len());
229 /// std::fs::write("thumbnail.png", thumbnail_data)?;
230 /// }
231 /// # Ok(())
232 /// # }
233 /// ```
234 pub fn read_thumbnail<R: Read + std::io::Seek>(reader: R) -> Result<Option<Vec<u8>>> {
235 parser::read_thumbnail(reader)
236 }
237
238 /// Write a 3MF file to a writer
239 ///
240 /// This method serializes the Model to a complete 3MF file (ZIP archive)
241 /// and writes it to the provided writer.
242 ///
243 /// # Arguments
244 ///
245 /// * `writer` - A writer to write the 3MF file data to
246 ///
247 /// # Example
248 ///
249 /// ```no_run
250 /// use lib3mf::Model;
251 /// use std::fs::File;
252 ///
253 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
254 /// let mut model = Model::new();
255 /// // ... populate model with data ...
256 ///
257 /// let file = File::create("output.3mf")?;
258 /// model.to_writer(file)?;
259 /// # Ok(())
260 /// # }
261 /// ```
262 pub fn to_writer<W: std::io::Write + std::io::Seek>(self, writer: W) -> Result<W> {
263 // Serialize model to XML
264 let mut xml_buffer = Vec::new();
265 writer::write_model_xml(&self, &mut xml_buffer)?;
266 let model_xml = String::from_utf8(xml_buffer)
267 .map_err(|e| Error::xml_write(format!("Failed to convert XML to UTF-8: {}", e)))?;
268
269 // Create OPC package
270 opc::create_package(writer, &model_xml)
271 }
272
273 /// Write a 3MF file to a writer with extension registry
274 ///
275 /// This method serializes the Model to a complete 3MF file (ZIP archive)
276 /// and writes it to the provided writer. Before serialization, it calls
277 /// `pre_write()` on all registered extension handlers, allowing extensions
278 /// to prepare or transform data before writing.
279 ///
280 /// # Arguments
281 ///
282 /// * `writer` - A writer to write the 3MF file data to
283 /// * `registry` - Extension registry containing handlers that will be called before writing
284 ///
285 /// # Example
286 ///
287 /// ```no_run
288 /// use lib3mf::{Model, create_default_registry};
289 /// use std::fs::File;
290 ///
291 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
292 /// let mut model = Model::new();
293 /// // ... populate model with data ...
294 ///
295 /// // Create registry with all standard extension handlers
296 /// let registry = create_default_registry();
297 ///
298 /// let file = File::create("output.3mf")?;
299 /// model.to_writer_with_registry(file, ®istry)?;
300 /// # Ok(())
301 /// # }
302 /// ```
303 pub fn to_writer_with_registry<W: std::io::Write + std::io::Seek>(
304 mut self,
305 writer: W,
306 registry: &ExtensionRegistry,
307 ) -> Result<W> {
308 // Call pre_write hooks on all registered extensions
309 registry.pre_write_all(&mut self)?;
310
311 // Serialize model to XML
312 let mut xml_buffer = Vec::new();
313 writer::write_model_xml(&self, &mut xml_buffer)?;
314 let model_xml = String::from_utf8(xml_buffer)
315 .map_err(|e| Error::xml_write(format!("Failed to convert XML to UTF-8: {}", e)))?;
316
317 // Create OPC package
318 opc::create_package(writer, &model_xml)
319 }
320
321 /// Write a 3MF file to a file path
322 ///
323 /// This is a convenience method that creates a file and writes the 3MF data to it.
324 /// The file is automatically flushed and closed when the method completes.
325 ///
326 /// # Arguments
327 ///
328 /// * `path` - Path to the output file
329 ///
330 /// # Example
331 ///
332 /// ```no_run
333 /// use lib3mf::Model;
334 ///
335 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
336 /// let mut model = Model::new();
337 /// // ... populate model with data ...
338 ///
339 /// model.write_to_file("output.3mf")?;
340 /// # Ok(())
341 /// # }
342 /// ```
343 pub fn write_to_file<P: AsRef<std::path::Path>>(self, path: P) -> Result<()> {
344 let file = std::fs::File::create(path)?;
345 // File is automatically flushed and closed when dropped
346 self.to_writer(file)?;
347 Ok(())
348 }
349
350 /// Write a 3MF file to a file path with extension registry
351 ///
352 /// This is a convenience method that creates a file and writes the 3MF data to it.
353 /// Before writing, it calls `pre_write()` on all registered extension handlers.
354 /// The file is automatically flushed and closed when the method completes.
355 ///
356 /// # Arguments
357 ///
358 /// * `path` - Path to the output file
359 /// * `registry` - Extension registry containing handlers that will be called before writing
360 ///
361 /// # Example
362 ///
363 /// ```no_run
364 /// use lib3mf::{Model, create_default_registry};
365 ///
366 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
367 /// let mut model = Model::new();
368 /// // ... populate model with data ...
369 ///
370 /// // Create registry with all standard extension handlers
371 /// let registry = create_default_registry();
372 ///
373 /// model.write_to_file_with_registry("output.3mf", ®istry)?;
374 /// # Ok(())
375 /// # }
376 /// ```
377 pub fn write_to_file_with_registry<P: AsRef<std::path::Path>>(
378 self,
379 path: P,
380 registry: &ExtensionRegistry,
381 ) -> Result<()> {
382 let file = std::fs::File::create(path)?;
383 // File is automatically flushed and closed when dropped
384 self.to_writer_with_registry(file, registry)?;
385 Ok(())
386 }
387}