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