Skip to main content

lib3mf_core/model/
core.rs

1use super::units::Unit;
2use crate::model::{Build, ResourceCollection};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Root element of a 3MF document.
7///
8/// The `Model` contains all information required to describe a 3D model, including:
9/// - Resources (Meshes, Materials, Textures)
10/// - Build instructions (Item positioning)
11/// - Metadata (Authors, Copyright, etc.)
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Model {
14    /// The unit of measurement for geometry coordinates.
15    #[serde(default)]
16    pub unit: Unit,
17
18    /// The language of the model content (e.g., "en-US").
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub language: Option<String>,
21
22    /// Arbitrary metadata key-value pairs.
23    #[serde(default)]
24    pub metadata: HashMap<String, String>,
25
26    /// Collection of all resources (objects, materials) used in the build.
27    #[serde(default)]
28    pub resources: ResourceCollection,
29
30    /// The build definition, containing instances of objects to be printed.
31    #[serde(default)]
32    pub build: Build,
33
34    /// Binary attachments (Textures, Thumbnails, etc.) stored by package path.
35    /// Key: Path in archive (e.g., "Metadata/thumbnail.png", "3D/Textures/diffuse.png")
36    /// Value: Binary content
37    #[serde(skip)]
38    pub attachments: HashMap<String, Vec<u8>>,
39
40    /// Existing OPC relationships loaded from the archive.
41    /// Key: Relationship file path (e.g., "3D/_rels/3dmodel.model.rels")
42    /// Value: Parsed relationships
43    #[serde(skip)]
44    pub existing_relationships: HashMap<String, Vec<crate::archive::opc::Relationship>>,
45
46    /// Extra XML namespace declarations from the model element (e.g., vendor namespaces).
47    /// Key: prefix (e.g., `"BambuStudio"`), Value: URI (e.g., `"http://schemas.bambulab.com/..."`).
48    #[serde(default)]
49    pub extra_namespaces: HashMap<String, String>,
50}
51
52impl Model {
53    /// Validates the 3MF model at the specified validation level.
54    ///
55    /// The validation system is progressive, with four levels of increasing strictness:
56    ///
57    /// - **Minimal**: Basic structural checks (required attributes, valid XML structure)
58    /// - **Standard**: Reference integrity checks (resource IDs exist, build references valid objects)
59    /// - **Strict**: Full spec compliance (metadata presence, no unknown attributes)
60    /// - **Paranoid**: Deep geometry analysis (manifoldness, self-intersection, orientation consistency)
61    ///
62    /// # Parameters
63    ///
64    /// - `level`: The [`ValidationLevel`](crate::validation::ValidationLevel) to apply. Higher levels
65    ///   include all checks from lower levels.
66    ///
67    /// # Returns
68    ///
69    /// A [`ValidationReport`](crate::validation::ValidationReport) containing all errors, warnings,
70    /// and info messages found during validation. Check [`has_errors()`](crate::validation::ValidationReport::has_errors)
71    /// to determine if the model passed validation.
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use lib3mf_core::{Model, validation::ValidationLevel};
77    ///
78    /// let model = Model::default();
79    ///
80    /// // Quick structural check
81    /// let report = model.validate(ValidationLevel::Minimal);
82    /// assert!(!report.has_errors());
83    ///
84    /// // Recommended for production use
85    /// let report = model.validate(ValidationLevel::Standard);
86    /// if report.has_errors() {
87    ///     for item in &report.items {
88    ///         eprintln!("Error: {}", item.message);
89    ///     }
90    /// }
91    ///
92    /// // Deep inspection (expensive, for critical applications)
93    /// let report = model.validate(ValidationLevel::Paranoid);
94    /// ```
95    ///
96    /// # Performance
97    ///
98    /// - **Minimal**: Very fast, suitable for quick checks
99    /// - **Standard**: Fast, recommended for most use cases
100    /// - **Strict**: Moderate, includes metadata and attribute checks
101    /// - **Paranoid**: Slow, performs O(n²) geometry checks with BVH acceleration
102    pub fn validate(
103        &self,
104        level: crate::validation::ValidationLevel,
105    ) -> crate::validation::ValidationReport {
106        use crate::validation::{ValidationLevel, displacement, geometry, schema, semantic};
107
108        let mut report = crate::validation::ValidationReport::new();
109
110        // Minimal: Schema validation (placeholders usually checked by parser, but explicit invariants here)
111        if level >= ValidationLevel::Minimal {
112            schema::validate_schema(self, &mut report);
113        }
114
115        // Standard: Semantic validation (integrity)
116        if level >= ValidationLevel::Standard {
117            semantic::validate_semantic(self, &mut report);
118        }
119
120        // All levels: Displacement validation (progressive checks)
121        displacement::validate_displacement(self, level, &mut report);
122
123        // Paranoid: Geometry validation
124        if level >= ValidationLevel::Paranoid {
125            geometry::validate_geometry(self, level, &mut report);
126        }
127
128        report
129    }
130}
131
132impl Default for Model {
133    fn default() -> Self {
134        Self {
135            unit: Unit::Millimeter,
136            language: None,
137            metadata: HashMap::new(),
138            resources: ResourceCollection::default(),
139            build: Build::default(),
140            attachments: HashMap::new(),
141            existing_relationships: HashMap::new(),
142            extra_namespaces: HashMap::new(),
143        }
144    }
145}
146
147// Unit enum moved to definition in units.rs