parry2d/mass_properties/
mass_properties_triangle.rs

1use crate::mass_properties::MassProperties;
2use crate::math::{Point, Real};
3use crate::shape::Triangle;
4
5impl MassProperties {
6    /// Computes the mass properties of a triangle.
7    ///
8    /// A triangle is the simplest polygon, defined by three vertices. In 2D, this represents
9    /// a filled triangular region. In 3D, this represents a flat triangular surface with
10    /// negligible thickness (useful for thin sheets or as building blocks for meshes).
11    ///
12    /// # Arguments
13    ///
14    /// * `density` - The material density (mass per unit area in both 2D and 3D)
15    ///   - Units are typically kg/m² (surface density)
16    ///   - For 3D triangles, this represents the density of a thin sheet
17    /// * `a`, `b`, `c` - The three vertices of the triangle
18    ///
19    /// # Returns
20    ///
21    /// A `MassProperties` struct containing:
22    /// - **mass**: Total mass calculated from area and density
23    /// - **local_com**: Center of mass at the centroid (average of three vertices)
24    /// - **inv_principal_inertia**: Inverse angular inertia
25    ///
26    /// # Physics Background
27    ///
28    /// Triangles have specific geometric properties:
29    /// - Area (2D): Using cross product of edge vectors
30    /// - Area (3D): Same formula, treating triangle as flat surface
31    /// - Center of mass: Always at centroid = (a + b + c) / 3
32    /// - Angular inertia: Depends on vertex positions relative to centroid
33    /// - Degenerate cases: Zero-area triangles (collinear points) return zero mass
34    ///
35    /// # Example (2D) - Right Triangle
36    ///
37    /// ```
38    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
39    /// use parry2d::mass_properties::MassProperties;
40    /// use nalgebra::Point2;
41    ///
42    /// // Create a right triangle with legs of 3m and 4m
43    /// let a = Point2::origin();
44    /// let b = Point2::new(3.0, 0.0);
45    /// let c = Point2::new(0.0, 4.0);
46    /// let density = 100.0; // kg/m²
47    ///
48    /// let triangle_props = MassProperties::from_triangle(density, &a, &b, &c);
49    ///
50    /// // Area = (1/2) × base × height = (1/2) × 3 × 4 = 6 m²
51    /// // Mass = area × density = 600 kg
52    /// let mass = triangle_props.mass();
53    /// assert!((mass - 600.0).abs() < 0.1);
54    ///
55    /// // Center of mass at centroid: (0+3+0)/3, (0+0+4)/3 = (1, 1.333)
56    /// let com = triangle_props.local_com;
57    /// assert!((com.x - 1.0).abs() < 0.01);
58    /// assert!((com.y - 4.0/3.0).abs() < 0.01);
59    ///
60    /// println!("Triangle mass: {:.2} kg", mass);
61    /// println!("Center of mass: ({:.2}, {:.2})", com.x, com.y);
62    /// # }
63    /// ```
64    ///
65    /// # Example (2D) - Equilateral Triangle
66    ///
67    /// ```
68    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
69    /// use parry2d::mass_properties::MassProperties;
70    /// use nalgebra::Point2;
71    ///
72    /// // Equilateral triangle with side length 2m
73    /// let side = 2.0;
74    /// let height = side * (3.0_f32.sqrt() / 2.0);
75    ///
76    /// let a = Point2::origin();
77    /// let b = Point2::new(side, 0.0);
78    /// let c = Point2::new(side / 2.0, height);
79    /// let density = 50.0;
80    ///
81    /// let tri_props = MassProperties::from_triangle(density, &a, &b, &c);
82    ///
83    /// // For equilateral triangle: Area = (side² × √3) / 4
84    /// let expected_area = side * side * 3.0_f32.sqrt() / 4.0;
85    /// let mass = tri_props.mass();
86    /// assert!((mass - expected_area * density).abs() < 0.1);
87    ///
88    /// println!("Equilateral triangle mass: {:.2} kg", mass);
89    /// # }
90    /// ```
91    ///
92    /// # Example (3D) - Triangle as Thin Sheet
93    ///
94    /// ```
95    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
96    /// use parry3d::mass_properties::MassProperties;
97    /// use nalgebra::Point3;
98    ///
99    /// // Triangle in 3D space (e.g., a metal plate or sail)
100    /// let a = Point3::origin();
101    /// let b = Point3::new(2.0, 0.0, 0.0);
102    /// let c = Point3::new(1.0, 2.0, 0.0);
103    /// let density = 200.0; // kg/m² (sheet metal)
104    ///
105    /// let plate_props = MassProperties::from_triangle(density, &a, &b, &c);
106    ///
107    /// // Area = 2 m² (base=2, height=2, area=(1/2)×2×2=2)
108    /// // Mass = 400 kg
109    /// let mass = plate_props.mass();
110    /// assert!((mass - 400.0).abs() < 0.1);
111    ///
112    /// println!("Metal plate mass: {:.2} kg", mass);
113    /// # }
114    /// ```
115    ///
116    /// # Example - Degenerate Triangle (Collinear Points)
117    ///
118    /// ```
119    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
120    /// use parry2d::mass_properties::MassProperties;
121    /// use nalgebra::Point2;
122    ///
123    /// // Three points on a line (no area)
124    /// let a = Point2::origin();
125    /// let b = Point2::new(1.0, 1.0);
126    /// let c = Point2::new(2.0, 2.0);
127    /// let density = 100.0;
128    ///
129    /// let degenerate = MassProperties::from_triangle(density, &a, &b, &c);
130    ///
131    /// // Zero area means zero mass
132    /// assert_eq!(degenerate.mass(), 0.0);
133    /// println!("Degenerate triangle has zero mass");
134    /// # }
135    /// ```
136    ///
137    /// # Use Cases
138    ///
139    /// - **Mesh building blocks**: Triangles are the basis for triangle meshes
140    /// - **Thin surfaces**: Sails, flags, sheets of material
141    /// - **Terrain patches**: Small triangular ground segments
142    /// - **Simple shapes**: Quick prototyping with basic geometry
143    /// - **2D games**: Triangular platforms, obstacles, or decorations
144    ///
145    /// # Vertex Order
146    ///
147    /// - The order of vertices (a, b, c) matters for orientation
148    /// - Counter-clockwise order is conventional in 2D
149    /// - In 3D, vertex order determines the normal direction (right-hand rule)
150    /// - However, for mass properties, the orientation doesn't affect the result
151    ///
152    /// # Common Use with Meshes
153    ///
154    /// For complex shapes, use `from_trimesh()` instead, which handles multiple triangles:
155    ///
156    /// ```ignore
157    /// // For a single triangle, use from_triangle
158    /// let props = MassProperties::from_triangle(density, &a, &b, &c);
159    ///
160    /// // For multiple triangles, use from_trimesh
161    /// let vertices = vec![a, b, c, d, e, f];
162    /// let indices = vec![[0, 1, 2], [3, 4, 5]];
163    /// let mesh_props = MassProperties::from_trimesh(density, &vertices, &indices);
164    /// ```
165    ///
166    /// # Performance Note
167    ///
168    /// Computing triangle mass properties is very fast (constant time) and involves
169    /// only basic geometric calculations (area, centroid, and moment of inertia).
170    pub fn from_triangle(
171        density: Real,
172        a: &Point<Real>,
173        b: &Point<Real>,
174        c: &Point<Real>,
175    ) -> MassProperties {
176        let triangle = Triangle::new(*a, *b, *c);
177        let area = triangle.area();
178        let com = triangle.center();
179
180        if area == 0.0 {
181            return MassProperties::new(com, 0.0, 0.0);
182        }
183
184        let ipart = triangle.unit_angular_inertia();
185
186        Self::new(com, area * density, ipart * area * density)
187    }
188}