parry3d/mass_properties/mass_properties_cuboid.rs
1use crate::mass_properties::MassProperties;
2use crate::math::{Point, PrincipalAngularInertia, Real, Vector};
3
4impl MassProperties {
5 pub(crate) fn cuboid_volume_unit_inertia(
6 half_extents: Vector<Real>,
7 ) -> (Real, PrincipalAngularInertia<Real>) {
8 #[cfg(feature = "dim2")]
9 {
10 let volume = half_extents.x * half_extents.y * 4.0;
11 let ix = (half_extents.x * half_extents.x) / 3.0;
12 let iy = (half_extents.y * half_extents.y) / 3.0;
13
14 (volume, ix + iy)
15 }
16
17 #[cfg(feature = "dim3")]
18 {
19 let volume = half_extents.x * half_extents.y * half_extents.z * 8.0;
20 let ix = (half_extents.x * half_extents.x) / 3.0;
21 let iy = (half_extents.y * half_extents.y) / 3.0;
22 let iz = (half_extents.z * half_extents.z) / 3.0;
23
24 (volume, Vector::new(iy + iz, ix + iz, ix + iy))
25 }
26 }
27
28 /// Computes the mass properties of a cuboid (box in 3D, rectangle in 2D).
29 ///
30 /// A cuboid is a box-shaped object with three dimensions (or two in 2D), where each
31 /// dimension can have a different size. The cuboid is centered at the origin, and
32 /// `half_extents` define the distance from the center to each face.
33 ///
34 /// # Arguments
35 ///
36 /// * `density` - The material density (mass per unit volume/area). Higher values make heavier objects.
37 /// - In 3D: units are typically kg/m³ (e.g., wood = 500-900, concrete = 2400)
38 /// - In 2D: units are typically kg/m² (mass per unit area)
39 /// * `half_extents` - Half the size along each axis (center to face distance).
40 /// - In 3D: `Vector3::new(hx, hy, hz)` creates a box with dimensions 2hx × 2hy × 2hz
41 /// - In 2D: `Vector2::new(hx, hy)` creates a rectangle with dimensions 2hx × 2hy
42 ///
43 /// # Returns
44 ///
45 /// A `MassProperties` struct containing:
46 /// - **mass**: Total mass calculated from volume and density
47 /// - **local_com**: Center of mass at the origin (cuboids are symmetric)
48 /// - **inv_principal_inertia**: Inverse angular inertia along each axis
49 ///
50 /// # Physics Background
51 ///
52 /// Cuboids have axis-aligned mass distribution:
53 /// - Center of mass is at the geometric center (origin)
54 /// - Angular inertia varies per axis based on dimensions
55 /// - Longer dimensions increase inertia around perpendicular axes
56 /// - In 3D, each axis has different inertia: I_x depends on y and z extents, etc.
57 ///
58 /// # Example (3D) - Wooden Crate
59 ///
60 /// ```
61 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
62 /// use parry3d::mass_properties::MassProperties;
63 /// use nalgebra::{Point3, Vector3};
64 ///
65 /// // Create a wooden crate: 2m × 1m × 1m (half_extents = 1.0, 0.5, 0.5)
66 /// // Wood density: approximately 600 kg/m³
67 /// let half_extents = Vector3::new(1.0, 0.5, 0.5);
68 /// let density = 600.0;
69 /// let crate_props = MassProperties::from_cuboid(density, half_extents);
70 ///
71 /// // Volume = (2 * 1.0) × (2 * 0.5) × (2 * 0.5) = 2 m³
72 /// // Mass = volume × density = 1200 kg
73 /// let mass = crate_props.mass();
74 /// assert!((mass - 1200.0).abs() < 0.1);
75 ///
76 /// // Longer dimension (x-axis) means higher inertia around y and z axes
77 /// let inertia = crate_props.principal_inertia();
78 /// println!("Inertia around x-axis: {:.2}", inertia.x); // Lowest (easier to spin around length)
79 /// println!("Inertia around y-axis: {:.2}", inertia.y); // Higher
80 /// println!("Inertia around z-axis: {:.2}", inertia.z); // Higher
81 ///
82 /// // Center of mass is at the origin
83 /// assert_eq!(crate_props.local_com, Point3::origin());
84 /// # }
85 /// ```
86 ///
87 /// # Example (3D) - Cube
88 ///
89 /// ```
90 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
91 /// use parry3d::mass_properties::MassProperties;
92 /// use nalgebra::Vector3;
93 ///
94 /// // Create a 1m × 1m × 1m cube (half_extents = 0.5 on all axes)
95 /// let half_extents = Vector3::new(0.5, 0.5, 0.5);
96 /// let density = 1000.0; // Water density
97 /// let cube_props = MassProperties::from_cuboid(density, half_extents);
98 ///
99 /// // Volume = 1 m³, Mass = 1000 kg
100 /// assert!((cube_props.mass() - 1000.0).abs() < 0.1);
101 ///
102 /// // For a cube, all axes have equal inertia (symmetric)
103 /// let inertia = cube_props.principal_inertia();
104 /// assert!((inertia.x - inertia.y).abs() < 0.01);
105 /// assert!((inertia.y - inertia.z).abs() < 0.01);
106 /// # }
107 /// ```
108 ///
109 /// # Example (2D) - Rectangular Platform
110 ///
111 /// ```
112 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
113 /// use parry2d::mass_properties::MassProperties;
114 /// use nalgebra::Vector2;
115 ///
116 /// // Create a 4m × 2m rectangular platform (half_extents = 2.0, 1.0)
117 /// let half_extents = Vector2::new(2.0, 1.0);
118 /// let density = 500.0; // kg/m²
119 /// let platform_props = MassProperties::from_cuboid(density, half_extents);
120 ///
121 /// // Area = (2 * 2.0) × (2 * 1.0) = 8 m²
122 /// // Mass = area × density = 4000 kg
123 /// let mass = platform_props.mass();
124 /// assert!((mass - 4000.0).abs() < 0.1);
125 ///
126 /// println!("Platform mass: {:.2} kg", mass);
127 /// println!("Moment of inertia: {:.2}", platform_props.principal_inertia());
128 /// # }
129 /// ```
130 ///
131 /// # Use Cases
132 ///
133 /// - **Boxes and crates**: Storage containers, shipping boxes
134 /// - **Building blocks**: Walls, floors, platforms
135 /// - **Vehicles**: Simplified car or truck bodies
136 /// - **Furniture**: Tables, chairs, cabinets
137 /// - **Terrain**: Rectangular ground segments
138 ///
139 /// # Common Mistakes
140 ///
141 /// - **Wrong dimensions**: Remember that `half_extents` are HALF the total size.
142 /// For a 2m × 2m × 2m box, use `Vector3::new(1.0, 1.0, 1.0)`, not `(2.0, 2.0, 2.0)`
143 /// - **Unit confusion**: Ensure density units match your distance units
144 /// (kg/m³ with meters, kg/cm³ with centimeters, etc.)
145 ///
146 /// # Performance Note
147 ///
148 /// This is a very fast computation (constant time). Cuboids are the second simplest
149 /// shape after balls and are highly efficient for collision detection.
150 pub fn from_cuboid(density: Real, half_extents: Vector<Real>) -> Self {
151 let (vol, unit_i) = Self::cuboid_volume_unit_inertia(half_extents);
152 let mass = vol * density;
153 Self::new(Point::origin(), mass, unit_i * mass)
154 }
155}