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}