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#[derive(Debug, Clone, Default, Serialize, Deserialize)]
36pub struct ThumbnailStats {
37    pub package_thumbnail_present: bool,
38    pub object_thumbnail_count: usize,
39}
40
41#[derive(Debug, Clone, Default, Serialize, Deserialize)]
42pub struct MaterialsStats {
43    pub base_materials_count: usize,
44    pub color_groups_count: usize,
45    pub texture_2d_groups_count: usize,
46    pub composite_materials_count: usize,
47    pub multi_properties_count: usize,
48}
49
50/// Geometric statistics for the model.
51///
52/// Aggregates counts and measurements of the model's geometry including
53/// vertices, triangles, bounding box, surface area, and volume.
54#[derive(Debug, Clone, Default, Serialize, Deserialize)]
55pub struct GeometryStats {
56    /// Total number of object resources
57    pub object_count: usize,
58    /// Number of build items (instances to print)
59    pub instance_count: usize,
60    /// Total number of triangles across all meshes
61    pub triangle_count: u64,
62    /// Total number of vertices across all meshes
63    pub vertex_count: u64,
64    /// Axis-aligned bounding box of the entire model
65    pub bounding_box: Option<BoundingBox>,
66    /// Total surface area in square model units
67    pub surface_area: f64,
68    /// Total volume in cubic model units
69    pub volume: f64,
70    /// Whether all meshes are manifold (watertight)
71    pub is_manifold: bool,
72    /// Count of objects by type (e.g., {"model": 5, "support": 2})
73    #[serde(default)]
74    pub type_counts: HashMap<String, usize>,
75}
76
77/// An axis-aligned bounding box in 3D space.
78///
79/// Represents the smallest box (aligned with coordinate axes) that
80/// contains all geometry. Useful for understanding model size and
81/// for spatial queries.
82#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
83pub struct BoundingBox {
84    /// Minimum corner coordinates [x, y, z]
85    pub min: [f32; 3],
86    /// Maximum corner coordinates [x, y, z]
87    pub max: [f32; 3],
88}
89
90impl BoundingBox {
91    pub fn transform(&self, matrix: glam::Mat4) -> Self {
92        let corners = [
93            glam::Vec3::new(self.min[0], self.min[1], self.min[2]),
94            glam::Vec3::new(self.min[0], self.min[1], self.max[2]),
95            glam::Vec3::new(self.min[0], self.max[1], self.min[2]),
96            glam::Vec3::new(self.min[0], self.max[1], self.max[2]),
97            glam::Vec3::new(self.max[0], self.min[1], self.min[2]),
98            glam::Vec3::new(self.max[0], self.min[1], self.max[2]),
99            glam::Vec3::new(self.max[0], self.max[1], self.min[2]),
100            glam::Vec3::new(self.max[0], self.max[1], self.max[2]),
101        ];
102
103        let mut transformed_min = glam::Vec3::splat(f32::INFINITY);
104        let mut transformed_max = glam::Vec3::splat(f32::NEG_INFINITY);
105
106        for corner in corners {
107            let transformed = matrix.transform_point3(corner);
108            transformed_min = transformed_min.min(transformed);
109            transformed_max = transformed_max.max(transformed);
110        }
111
112        Self {
113            min: [transformed_min.x, transformed_min.y, transformed_min.z],
114            max: [transformed_max.x, transformed_max.y, transformed_max.z],
115        }
116    }
117}
118
119#[derive(Debug, Clone, Default, Serialize, Deserialize)]
120pub struct ProductionStats {
121    pub uuid_count: usize,
122}
123
124#[derive(Debug, Clone, Default, Serialize, Deserialize)]
125pub struct DisplacementStats {
126    pub mesh_count: usize,
127    pub texture_count: usize,
128    pub normal_count: u64,
129    pub gradient_count: u64,
130    pub displaced_triangle_count: u64,
131    pub total_triangle_count: u64,
132}
133
134#[derive(Debug, Clone, Default, Serialize, Deserialize)]
135pub struct VendorData {
136    pub printer_model: Option<String>,
137    pub filaments: Vec<FilamentInfo>,
138    pub plates: Vec<PlateInfo>,
139    pub print_time_estimate: Option<String>,
140}
141
142#[derive(Debug, Clone, Default, Serialize, Deserialize)]
143pub struct FilamentInfo {
144    pub type_: String,
145    pub color: Option<String>,
146}
147
148#[derive(Debug, Clone, Default, Serialize, Deserialize)]
149pub struct PlateInfo {
150    pub id: u32,
151    pub name: Option<String>,
152    // pub items: Vec<ResourceId>,
153}