1use num::Zero;
2
3use crate::math::{AngularInertia, Point};
4use crate::volumetric::Volumetric;
5use na;
6use na::RealField;
7use na::{Matrix1, Point2};
8use ncollide::shape::ConvexPolygon;
9use ncollide::utils;
10
11pub fn convex_polyline_area_and_center_of_mass_unchecked<N: RealField + Copy>(
15 convex_polyline: &[Point<N>],
16) -> (N, Point<N>) {
17 let geometric_center = utils::center(convex_polyline);
18 let mut res = Point::origin();
19 let mut areasum = N::zero();
20
21 let mut iterpeek = convex_polyline.iter().peekable();
22 let firstelement = *iterpeek.peek().unwrap(); while let Some(elem) = iterpeek.next() {
24 let area = utils::triangle_area(
25 elem,
26 iterpeek.peek().unwrap_or(&firstelement),
27 &geometric_center,
28 );
29 let center = utils::triangle_center(
30 elem,
31 iterpeek.peek().unwrap_or(&firstelement),
32 &geometric_center,
33 );
34
35 res += center.coords * area;
36 areasum += area;
37 }
38
39 if areasum.is_zero() {
40 (areasum, geometric_center)
41 } else {
42 (areasum, res / areasum)
43 }
44}
45
46pub fn convex_polyline_mass_properties_unchecked<N: RealField + Copy>(
50 convex_polyline: &[Point<N>],
51 density: N,
52) -> (N, Point<N>, N) {
53 let (area, com) = convex_polyline_area_and_center_of_mass_unchecked(convex_polyline);
54
55 if area.is_zero() {
56 return (na::zero(), com, na::zero());
57 }
58
59 let mut itot = N::zero();
60 let factor: N = na::convert(0.5 * 1.0 / 3.0);
61
62 let mut iterpeek = convex_polyline.iter().peekable();
63 let firstelement = *iterpeek.peek().unwrap(); while let Some(elem) = iterpeek.next() {
65 let area = utils::triangle_area(&com, elem, iterpeek.peek().unwrap_or(&firstelement));
66
67 let e1 = *elem - com;
69 let e2 = **(iterpeek.peek().unwrap_or(&firstelement)) - com;
70
71 let ex1 = e1[0];
72 let ey1 = e1[1];
73 let ex2 = e2[0];
74 let ey2 = e2[1];
75
76 let intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2;
77 let inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2;
78
79 let ipart = factor * (intx2 + inty2);
80
81 itot += ipart * area;
82 }
83
84 (area * density, com, itot * density)
85}
86
87pub fn convex_polyline_area_unchecked<N: RealField + Copy>(convex_polyline: &[Point<N>]) -> N {
91 let geometric_center = utils::center(convex_polyline);
92 let mut areasum = N::zero();
93
94 let mut iterpeek = convex_polyline.iter().peekable();
95 let firstelement = *iterpeek.peek().unwrap(); while let Some(elem) = iterpeek.next() {
97 let area = utils::triangle_area(
98 elem,
99 iterpeek.peek().unwrap_or(&firstelement),
100 &geometric_center,
101 );
102
103 areasum += area;
104 }
105
106 areasum
107}
108
109pub fn convex_hull_area<N: RealField + Copy>(points: &[Point<N>]) -> N {
111 convex_polyline_area_unchecked(points)
112}
113
114pub fn convex_hull_volume<N: RealField + Copy>(points: &[Point<N>]) -> N {
116 convex_hull_area(points)
117}
118
119pub fn convex_hull_center_of_mass<N: RealField + Copy>(points: &[Point<N>]) -> Point<N> {
121 convex_polyline_area_and_center_of_mass_unchecked(points).1
122}
123
124pub fn convex_hull_unit_angular_inertia<N: RealField + Copy>(points: &[Point<N>]) -> AngularInertia<N> {
126 let (area, _, i): (_, _, N) = convex_polyline_mass_properties_unchecked(points, na::one());
127
128 let mut tensor = AngularInertia::zero();
129 tensor[(0, 0)] = i * (N::one() / area);
130
131 tensor
132}
133
134impl<N: RealField + Copy> Volumetric<N> for ConvexPolygon<N> {
135 fn area(&self) -> N {
136 convex_hull_area(self.points())
137 }
138
139 fn volume(&self) -> N {
140 convex_hull_volume(self.points())
141 }
142
143 fn center_of_mass(&self) -> Point2<N> {
144 convex_hull_center_of_mass(self.points())
145 }
146
147 fn unit_angular_inertia(&self) -> Matrix1<N> {
148 convex_hull_unit_angular_inertia(self.points())
149 }
150
151 fn mass_properties(&self, density: N) -> (N, Point2<N>, Matrix1<N>) {
152 let (r1, r2, r3) = convex_polyline_mass_properties_unchecked(self.points(), density);
153 (r1, r2, Matrix1::<N>::new(r3))
154 }
155}
156
157#[cfg(test)]
158mod test {
159 use na::{self, Matrix1, Point2, Vector2};
160 use ncollide::shape::{ConvexPolygon, Cuboid};
161
162 use crate::volumetric::Volumetric;
163
164 #[test]
165 fn test_inertia_tensor2() {
166 let a = 3.8f32; let half_a = a / 2.0;
169
170 let real_moi = a.powf(2.0) / 6.0;
172 let expected = Matrix1::new(real_moi);
173
174 let cube = Cuboid::new(Vector2::new(half_a, half_a));
176
177 let actual = cube.unit_angular_inertia();
178 assert!(
179 relative_eq!(actual, expected),
180 format!(
181 "Inertia values do not match: actual {:?}, expected: {:?}.",
182 actual, expected
183 )
184 );
185
186 let geom = {
188 let points = vec![
189 Point2::new(half_a, half_a),
190 Point2::new(-half_a, half_a),
191 Point2::new(-half_a, -half_a),
192 Point2::new(half_a, -half_a),
193 ];
194 ConvexPolygon::try_new(points).unwrap()
195 };
196 let actual = geom.unit_angular_inertia();
197 assert!(
198 relative_eq!(actual, expected),
199 format!(
200 "Inertia values do not match: actual {:?}, expected: {:?}.",
201 actual, expected
202 )
203 );
204
205 let a = 2.3f32; let b = 6.7f32;
208 let half_a = a / 2.0;
209 let half_b = b / 2.0;
210
211 let real_moi = (1.0 / 12.0) * (a.powf(2.0) + b.powf(2.0));
213 let expected = Matrix1::new(real_moi);
214
215 let cube = Cuboid::new(Vector2::new(half_a, half_b));
217
218 let actual = cube.unit_angular_inertia();
219 assert!(
220 relative_eq!(actual, expected),
221 format!(
222 "Inertia values do not match: actual {:?}, expected: {:?}.",
223 actual, expected
224 )
225 );
226
227 let geom = {
229 let points = vec![
230 Point2::new(half_a, half_b),
231 Point2::new(-half_a, half_b),
232 Point2::new(-half_a, -half_b),
233 Point2::new(half_a, -half_b),
234 ];
235 ConvexPolygon::try_new(points).unwrap()
236 };
237 let actual = geom.unit_angular_inertia();
238 assert!(
239 relative_eq!(actual, expected),
240 format!(
241 "Inertia values do not match: actual {:?}, expected: {:?}.",
242 actual, expected
243 )
244 );
245
246 let b = 6.7f32; let h = 3.8f32; let a = 2.3f32; let c_x = a + b / 3.0;
254 let c_y = h / 3.0;
255
256 let area = b * h / 2.0;
259 let real_moi =
260 (b.powf(3.0) * h - b.powf(2.0) * h * a + b * h * a.powf(2.0) + b * h.powf(3.0))
261 / (36.0 * area);
262 let expected = Matrix1::new(real_moi);
263
264 let geom = {
266 let points = vec![
267 Point2::new(0.0 - c_x, 0.0 - c_y),
268 Point2::new(b - c_x, 0.0 - c_y),
269 Point2::new(a - c_x, h - c_y),
270 ];
271 ConvexPolygon::try_new(points).unwrap()
272 };
273 let actual = geom.unit_angular_inertia();
274 assert!(
275 relative_eq!(actual, expected),
276 format!(
277 "Inertia values do not match: actual {:?}, expected: {:?}.",
278 actual, expected
279 )
280 );
281 }
282}