Skip to main content

brep_kernel/props/
mass_properties.rs

1use crate::topology::{BrepSolid, EdgeRecord, FaceRecord, ShellRecord};
2use crate::{NurbsCurve, NurbsSurface, Vec3};
3use serde::Serialize;
4
5const GAUSS_X: [f64; 8] = [
6    -0.9602898564975363,
7    -0.7966664774136267,
8    -0.525532409916329,
9    -0.18343464249564978,
10    0.18343464249564978,
11    0.525532409916329,
12    0.7966664774136267,
13    0.9602898564975363,
14];
15const GAUSS_W: [f64; 8] = [
16    0.10122853629037669,
17    0.22238103445337445,
18    0.31370664587788727,
19    0.362683783378362,
20    0.362683783378362,
21    0.31370664587788727,
22    0.22238103445337445,
23    0.10122853629037669,
24];
25
26#[derive(Clone, Copy)]
27enum Integrand {
28    Area,
29    Volume,
30    VolumeAbout(Vec3),
31    /// Divergence-theorem first moments: ∫x dV = ∮ (x²/2)·nx dA, etc.
32    MomentX,
33    MomentY,
34    MomentZ,
35    /// Second moments: ∫x² dV = ∮ (x³/3)·nx dA, etc.
36    SecondXX,
37    SecondYY,
38    SecondZZ,
39    /// Products: ∫xy dV = ∮ (x²y/2)·nx dA, ∫xz, ∫yz analogously.
40    ProductXY,
41    ProductXZ,
42    ProductYZ,
43}
44
45#[derive(Clone, Copy, Debug, Serialize)]
46pub struct MassProperties {
47    pub surface_area: f64,
48    pub volume: f64,
49}
50
51#[derive(Clone, Copy, Debug, Serialize)]
52pub struct FullMassProperties {
53    pub surface_area: f64,
54    pub volume: f64,
55    /// Volume centroid (unit density center of mass).
56    pub centroid: Vec3,
57    /// Inertia tensor about the centroid for unit density, row-major
58    /// [[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]].
59    pub inertia: [[f64; 3]; 3],
60    /// Principal moments of inertia (eigenvalues of `inertia`) at unit
61    /// density, sorted ASCENDING: `principal_moments[0] <= [1] <= [2]`.
62    /// Golovanov §8.11.
63    pub principal_moments: [f64; 3],
64    /// Principal axes of inertia (eigenvectors of `inertia`), one per row:
65    /// `principal_axes[i]` is the unit axis whose moment is
66    /// `principal_moments[i]`. The three axes are orthonormal and arranged as
67    /// a RIGHT-HANDED frame (determinant +1); the sign of the third axis is
68    /// fixed to enforce handedness. Density-independent.
69    pub principal_axes: [[f64; 3]; 3],
70}
71
72/// Mass properties scaled to a physical density (Golovanov §8.11). The
73/// underlying `FullMassProperties` is a UNIT-density geometric result; here
74/// `mass = density * volume` and every inertia quantity scales linearly with
75/// density (`inertia = density * geometric_inertia`). The centroid and the
76/// principal AXES are density-independent; only the principal MOMENTS and the
77/// mass scale with density.
78#[derive(Clone, Copy, Debug, Serialize)]
79pub struct DensityMassProperties {
80    /// Density used to scale the geometric (unit-density) properties.
81    pub density: f64,
82    /// `mass = density * volume`.
83    pub mass: f64,
84    pub surface_area: f64,
85    pub volume: f64,
86    /// Center of mass (identical to the unit-density centroid).
87    pub centroid: Vec3,
88    /// Centroidal inertia tensor `= density * geometric_inertia`, row-major.
89    pub inertia: [[f64; 3]; 3],
90    /// Principal moments `= density * geometric_principal_moments`, ascending.
91    pub principal_moments: [f64; 3],
92    /// Principal axes (identical to the unit-density axes; density-invariant).
93    pub principal_axes: [[f64; 3]; 3],
94}
95
96impl FullMassProperties {
97    /// Scale the unit-density geometric result to a physical `density`
98    /// (Golovanov §8.11): `mass = density * volume`, `inertia` and the
99    /// principal moments scale by `density`, while the centroid and principal
100    /// axes are unchanged. Panics only on a non-finite density is avoided —
101    /// callers should pass a finite, positive density; a zero density yields a
102    /// massless (all-zero inertia) result, which is well defined.
103    pub fn with_density(&self, density: f64) -> DensityMassProperties {
104        let scale = |matrix: [[f64; 3]; 3]| {
105            let mut out = [[0.0f64; 3]; 3];
106            for row in 0..3 {
107                for column in 0..3 {
108                    out[row][column] = density * matrix[row][column];
109                }
110            }
111            out
112        };
113        DensityMassProperties {
114            density,
115            mass: density * self.volume,
116            surface_area: self.surface_area,
117            volume: self.volume,
118            centroid: self.centroid,
119            inertia: scale(self.inertia),
120            principal_moments: [
121                density * self.principal_moments[0],
122                density * self.principal_moments[1],
123                density * self.principal_moments[2],
124            ],
125            principal_axes: self.principal_axes,
126        }
127    }
128}
129
130
131#[path = "mass_properties/integration.rs"]
132mod integration;
133#[path = "mass_properties/measures.rs"]
134mod measures;
135#[path = "mass_properties/polygons.rs"]
136mod polygons;
137#[path = "mass_properties/solid_props.rs"]
138mod solid_props;
139#[path = "mass_properties/trimmed.rs"]
140mod trimmed;
141#[path = "mass_properties/winding.rs"]
142mod winding;
143// BREP private tests: cda382738d414fd5
144
145use integration::*;
146use measures::*;
147use polygons::*;
148use trimmed::*;
149
150pub use integration::parameter_space_area;
151pub use measures::{
152    curve_arc_length, edge_arc_length, face_area, face_boundary_length,
153    face_volume_contribution, solid_edge_length_total,
154};
155pub use solid_props::{solid_mass_properties, solid_mass_properties_full, solid_signed_volume};
156pub(crate) use solid_props::shell_signed_volume;
157pub use polygons::trim_polygons;