lib3mf_core/model/stats.rs
1use crate::model::Unit;
2use crate::utils::hardware::HardwareCapabilities;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Comprehensive statistics and metadata for a 3MF model.
7///
8/// Aggregates various statistics about the model's geometry, materials,
9/// production metadata, and vendor-specific information. Used by the CLI
10/// `stats` command and for model analysis.
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct ModelStats {
13 /// Unit of measurement for the model
14 pub unit: Unit,
15 /// Software that generated the model (from metadata)
16 pub generator: Option<String>,
17 /// Custom metadata key-value pairs from the model
18 pub metadata: HashMap<String, String>,
19 /// Geometric statistics (vertices, triangles, volume, etc.)
20 pub geometry: GeometryStats,
21 /// Material and property statistics
22 pub materials: MaterialsStats,
23 /// Production extension metadata statistics
24 pub production: ProductionStats,
25 /// Displacement extension statistics
26 pub displacement: DisplacementStats,
27 /// Vendor-specific data (e.g., Bambu Studio project info)
28 pub vendor: VendorData,
29 /// System hardware capabilities info
30 pub system_info: HardwareCapabilities,
31 /// Thumbnail statistics
32 pub thumbnails: ThumbnailStats,
33}
34
35/// Statistics about thumbnails in a 3MF package.
36#[derive(Debug, Clone, Default, Serialize, Deserialize)]
37pub struct ThumbnailStats {
38 /// Whether a package-level thumbnail is present.
39 pub package_thumbnail_present: bool,
40 /// Number of object-level thumbnails in the package.
41 pub object_thumbnail_count: usize,
42}
43
44/// Statistics about material and property resources in a 3MF model.
45#[derive(Debug, Clone, Default, Serialize, Deserialize)]
46pub struct MaterialsStats {
47 /// Number of base material groups.
48 pub base_materials_count: usize,
49 /// Number of color groups.
50 pub color_groups_count: usize,
51 /// Number of 2D texture coordinate groups.
52 pub texture_2d_groups_count: usize,
53 /// Number of composite materials groups.
54 pub composite_materials_count: usize,
55 /// Number of multi-properties groups.
56 pub multi_properties_count: usize,
57}
58
59/// Geometric statistics for the model.
60///
61/// Aggregates counts and measurements of the model's geometry including
62/// vertices, triangles, bounding box, surface area, and volume.
63#[derive(Debug, Clone, Default, Serialize, Deserialize)]
64pub struct GeometryStats {
65 /// Total number of object resources
66 pub object_count: usize,
67 /// Number of build items (instances to print)
68 pub instance_count: usize,
69 /// Total number of triangles across all meshes
70 pub triangle_count: u64,
71 /// Total number of vertices across all meshes
72 pub vertex_count: u64,
73 /// Axis-aligned bounding box of the entire model
74 pub bounding_box: Option<BoundingBox>,
75 /// Total surface area in square model units
76 pub surface_area: f64,
77 /// Total volume in cubic model units
78 pub volume: f64,
79 /// Whether all meshes are manifold (watertight)
80 pub is_manifold: bool,
81 /// Count of objects by type (e.g., {"model": 5, "support": 2})
82 #[serde(default)]
83 pub type_counts: HashMap<String, usize>,
84}
85
86/// An axis-aligned bounding box in 3D space.
87///
88/// Represents the smallest box (aligned with coordinate axes) that
89/// contains all geometry. Useful for understanding model size and
90/// for spatial queries.
91#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
92pub struct BoundingBox {
93 /// Minimum corner coordinates [x, y, z]
94 pub min: [f32; 3],
95 /// Maximum corner coordinates [x, y, z]
96 pub max: [f32; 3],
97}
98
99impl BoundingBox {
100 /// Transforms the bounding box by the given 4x4 matrix, returning a new axis-aligned bounding box.
101 pub fn transform(&self, matrix: glam::Mat4) -> Self {
102 let corners = [
103 glam::Vec3::new(self.min[0], self.min[1], self.min[2]),
104 glam::Vec3::new(self.min[0], self.min[1], self.max[2]),
105 glam::Vec3::new(self.min[0], self.max[1], self.min[2]),
106 glam::Vec3::new(self.min[0], self.max[1], self.max[2]),
107 glam::Vec3::new(self.max[0], self.min[1], self.min[2]),
108 glam::Vec3::new(self.max[0], self.min[1], self.max[2]),
109 glam::Vec3::new(self.max[0], self.max[1], self.min[2]),
110 glam::Vec3::new(self.max[0], self.max[1], self.max[2]),
111 ];
112
113 let mut transformed_min = glam::Vec3::splat(f32::INFINITY);
114 let mut transformed_max = glam::Vec3::splat(f32::NEG_INFINITY);
115
116 for corner in corners {
117 let transformed = matrix.transform_point3(corner);
118 transformed_min = transformed_min.min(transformed);
119 transformed_max = transformed_max.max(transformed);
120 }
121
122 Self {
123 min: [transformed_min.x, transformed_min.y, transformed_min.z],
124 max: [transformed_max.x, transformed_max.y, transformed_max.z],
125 }
126 }
127}
128
129/// Statistics from the Production Extension (UUIDs).
130#[derive(Debug, Clone, Default, Serialize, Deserialize)]
131pub struct ProductionStats {
132 /// Number of objects and build items that have UUIDs assigned.
133 pub uuid_count: usize,
134}
135
136/// Statistics from the Displacement Extension.
137#[derive(Debug, Clone, Default, Serialize, Deserialize)]
138pub struct DisplacementStats {
139 /// Number of displacement meshes in the model.
140 pub mesh_count: usize,
141 /// Number of displacement texture resources.
142 pub texture_count: usize,
143 /// Total number of normal vectors across all displacement meshes.
144 pub normal_count: u64,
145 /// Total number of gradient vectors across all displacement meshes.
146 pub gradient_count: u64,
147 /// Total number of displaced triangles (triangles with displacement coordinate indices).
148 pub displaced_triangle_count: u64,
149 /// Total number of triangles in all displacement meshes.
150 pub total_triangle_count: u64,
151}
152
153/// Vendor-specific data extracted from Bambu Studio 3MF files.
154#[derive(Debug, Clone, Default, Serialize, Deserialize)]
155pub struct VendorData {
156 /// Printer model name from Bambu Studio metadata.
157 pub printer_model: Option<String>,
158 /// List of filament configurations used in the print.
159 pub filaments: Vec<FilamentInfo>,
160 /// List of plate/build plate configurations.
161 pub plates: Vec<PlateInfo>,
162 /// Estimated print time from the slicer.
163 pub print_time_estimate: Option<String>,
164 /// Version string of the slicer that generated the file.
165 pub slicer_version: Option<String>,
166 /// Nozzle diameter in millimeters.
167 pub nozzle_diameter: Option<f32>,
168 /// Warnings generated by the slicer during slicing.
169 pub slicer_warnings: Vec<SlicerWarning>,
170 /// Per-object metadata from Bambu Studio project files.
171 pub object_metadata: Vec<BambuObjectMetadata>,
172 /// Global project settings from Bambu Studio.
173 pub project_settings: Option<BambuProjectSettings>,
174 /// Slicer profile configurations embedded in the file.
175 pub profile_configs: Vec<BambuProfileConfig>,
176 /// Assembly information from Bambu Studio files.
177 pub assembly_info: Vec<AssemblyItem>,
178 /// Path to Bambu cover thumbnail (from OPC relationship), e.g., "Metadata/plate_1.png"
179 pub bambu_cover_thumbnail: Option<String>,
180 /// Path to Bambu embedded gcode (from OPC relationship), e.g., "Metadata/plate_1.gcode"
181 pub bambu_gcode: Option<String>,
182}
183
184/// Information about a single filament used in a Bambu Studio print.
185#[derive(Debug, Clone, Default, Serialize, Deserialize)]
186pub struct FilamentInfo {
187 /// Filament slot index.
188 pub id: u32,
189 /// Tray info index from the AMS system.
190 pub tray_info_idx: Option<String>,
191 /// Filament type string (e.g., `"PLA"`, `"PETG"`).
192 pub type_: String,
193 /// Display color in hex format.
194 pub color: Option<String>,
195 /// Estimated filament used in meters.
196 pub used_m: Option<f32>,
197 /// Estimated filament used in grams.
198 pub used_g: Option<f32>,
199}
200
201/// Information about a single build plate in a Bambu Studio project.
202#[derive(Debug, Clone, Default, Serialize, Deserialize)]
203pub struct PlateInfo {
204 /// Plate index (1-based).
205 pub id: u32,
206 /// Optional display name for this plate.
207 pub name: Option<String>,
208 /// Whether this plate is locked in the slicer.
209 pub locked: bool,
210 /// Path to the pre-sliced G-code file for this plate.
211 pub gcode_file: Option<String>,
212 /// Path to the thumbnail image for this plate.
213 pub thumbnail_file: Option<String>,
214 /// List of object instances on this plate.
215 pub items: Vec<PlateModelInstance>,
216}
217
218/// An instance of an object on a Bambu Studio build plate.
219#[derive(Debug, Clone, Default, Serialize, Deserialize)]
220pub struct PlateModelInstance {
221 /// Object resource ID.
222 pub object_id: u32,
223 /// Instance index for multi-instance objects.
224 pub instance_id: u32,
225 /// Optional identify ID from the Bambu project metadata.
226 pub identify_id: Option<u32>,
227}
228
229/// A warning message generated by the slicer during the slicing process.
230#[derive(Debug, Clone, Default, Serialize, Deserialize)]
231pub struct SlicerWarning {
232 /// Warning message text.
233 pub msg: String,
234 /// Warning severity level string.
235 pub level: Option<String>,
236 /// Machine-readable error code.
237 pub error_code: Option<String>,
238}
239
240/// Per-object metadata from a Bambu Studio project file.
241#[derive(Debug, Clone, Default, Serialize, Deserialize)]
242pub struct BambuObjectMetadata {
243 /// Object resource ID.
244 pub id: u32,
245 /// Object display name.
246 pub name: Option<String>,
247 /// Extruder index assigned to this object.
248 pub extruder: Option<u32>,
249 /// Number of triangular faces in this object.
250 pub face_count: Option<u64>,
251 /// Sub-part metadata for multi-part objects.
252 pub parts: Vec<BambuPartMetadata>,
253}
254
255/// Metadata for a single part within a Bambu Studio object.
256#[derive(Debug, Clone, Default, Serialize, Deserialize)]
257pub struct BambuPartMetadata {
258 /// Part index.
259 pub id: u32,
260 /// Part subtype (normal, modifier, support blocker/enforcer, etc.).
261 pub subtype: PartSubtype,
262 /// Part display name.
263 pub name: Option<String>,
264 /// 3x4 transform matrix string.
265 pub matrix: Option<String>,
266 /// Source volume information for the part.
267 pub source: Option<BambuPartSource>,
268 /// Mesh repair statistics.
269 pub mesh_stat: Option<BambuMeshStat>,
270 /// Per-part print setting overrides.
271 pub print_overrides: HashMap<String, String>,
272}
273
274/// Classification of a Bambu Studio object part.
275#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
276pub enum PartSubtype {
277 /// A normal printable part (default).
278 #[default]
279 NormalPart,
280 /// A modifier volume that changes settings in a region.
281 ModifierPart,
282 /// A support blocker volume.
283 SupportBlocker,
284 /// A support enforcer volume.
285 SupportEnforcer,
286 /// An unrecognized subtype string.
287 Other(String),
288}
289
290impl PartSubtype {
291 /// Parse a Bambu part subtype string into a `PartSubtype` variant.
292 pub fn parse(s: &str) -> Self {
293 match s {
294 "normal_part" => Self::NormalPart,
295 "modifier_part" => Self::ModifierPart,
296 "support_blocker" => Self::SupportBlocker,
297 "support_enforcer" => Self::SupportEnforcer,
298 other => Self::Other(other.to_string()),
299 }
300 }
301}
302
303/// Source volume information for a Bambu Studio part.
304#[derive(Debug, Clone, Default, Serialize, Deserialize)]
305pub struct BambuPartSource {
306 /// Source volume ID (used to identify the originating volume).
307 pub volume_id: Option<u32>,
308 /// X offset of the source volume.
309 pub offset_x: Option<f64>,
310 /// Y offset of the source volume.
311 pub offset_y: Option<f64>,
312 /// Z offset of the source volume.
313 pub offset_z: Option<f64>,
314}
315
316/// Mesh repair statistics from Bambu Studio's automatic repair.
317#[derive(Debug, Clone, Default, Serialize, Deserialize)]
318pub struct BambuMeshStat {
319 /// Number of edges fixed during repair.
320 pub edges_fixed: Option<u32>,
321 /// Number of degenerate faces removed.
322 pub degenerate_facets: Option<u32>,
323 /// Number of faces removed during repair.
324 pub facets_removed: Option<u32>,
325 /// Number of faces whose winding was reversed.
326 pub facets_reversed: Option<u32>,
327 /// Number of backwards edges corrected.
328 pub backwards_edges: Option<u32>,
329}
330
331/// Global project settings from a Bambu Studio project file.
332#[derive(Debug, Clone, Default, Serialize, Deserialize)]
333pub struct BambuProjectSettings {
334 /// Printer model name.
335 pub printer_model: Option<String>,
336 /// Name of the printer profile this inherits from.
337 pub printer_inherits: Option<String>,
338 /// Bed/plate type (e.g., `"Engineering Plate"`).
339 pub bed_type: Option<String>,
340 /// Layer height in millimeters.
341 pub layer_height: Option<f32>,
342 /// First layer height in millimeters.
343 pub first_layer_height: Option<f32>,
344 /// Filament type strings per extruder.
345 pub filament_type: Vec<String>,
346 /// Filament display colors per extruder.
347 pub filament_colour: Vec<String>,
348 /// Nozzle diameters per extruder.
349 pub nozzle_diameter: Vec<f32>,
350 /// Print sequence (e.g., `"by_layer"` or `"by_object"`).
351 pub print_sequence: Option<String>,
352 /// Number of perimeter walls.
353 pub wall_loops: Option<u32>,
354 /// Infill density percentage string.
355 pub infill_density: Option<String>,
356 /// Support generation type.
357 pub support_type: Option<String>,
358 /// Additional key-value settings not covered by named fields.
359 pub extras: HashMap<String, serde_json::Value>,
360}
361
362/// A slicer profile configuration embedded in a Bambu Studio project file.
363#[derive(Debug, Clone, Default, Serialize, Deserialize)]
364pub struct BambuProfileConfig {
365 /// Profile category: `"filament"`, `"machine"`, or `"process"`.
366 pub config_type: String, // "filament", "machine", "process"
367 /// Index suffix (the N in `filament_settings_N.config`).
368 pub index: u32, // the N in filament_settings_N.config
369 /// Name of the profile this inherits from.
370 pub inherits: Option<String>,
371 /// Display name of this profile.
372 pub name: Option<String>,
373 /// Additional settings not covered by named fields.
374 pub extras: HashMap<String, serde_json::Value>,
375}
376
377/// An assembly item from a Bambu Studio project file.
378#[derive(Debug, Clone, Default, Serialize, Deserialize)]
379pub struct AssemblyItem {
380 /// Object resource ID.
381 pub object_id: u32,
382 /// Number of instances of this object in the assembly.
383 pub instance_count: u32,
384 /// Transform matrix string.
385 pub transform: Option<String>,
386 /// Offset string.
387 pub offset: Option<String>,
388}