MassData.cpp
Go to the documentation of this file.
1 /*
2  * Original work Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
3  * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/PlayRho
4  *
5  * This software is provided 'as-is', without any express or implied
6  * warranty. In no event will the authors be held liable for any damages
7  * arising from the use of this software.
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  * 1. The origin of this software must not be misrepresented; you must not
12  * claim that you wrote the original software. If you use this software
13  * in a product, an acknowledgment in the product documentation would be
14  * appreciated but is not required.
15  * 2. Altered source versions must be plainly marked as such, and must not be
16  * misrepresented as being the original software.
17  * 3. This notice may not be removed or altered from any source distribution.
18  */
19 
28 
29 namespace playrho {
30 namespace d2 {
31 
33 {
34  // Uses parallel axis theorem, perpendicular axis theorem, and the second moment of area.
35  // See: https://en.wikipedia.org/wiki/Second_moment_of_area
36  //
37  // Ixp = Ix + A * dx^2
38  // Iyp = Iy + A * dy^2
39  // Iz = Ixp + Iyp = Ix + A * dx^2 + Iy + A * dy^2
40  // Ix = Pi * r^4 / 4
41  // Iy = Pi * r^4 / 4
42  // Iz = (Pi * r^4 / 4) + (Pi * r^4 / 4) + (A * dx^2) + (A * dy^2)
43  // = (Pi * r^4 / 2) + (A * (dx^2 + dy^2))
44  // A = Pi * r^2
45  // Iz = (Pi * r^4 / 2) + (2 * (Pi * r^2) * (dx^2 + dy^2))
46  // Iz = Pi * r^2 * ((r^2 / 2) + (dx^2 + dy^2))
47  const auto r_squared = r * r;
48  const auto area = r_squared * Pi;
49  const auto mass = Mass{AreaDensity{density} * area};
50  const auto Iz = SecondMomentOfArea{area * ((r_squared / Real{2}) + GetMagnitudeSquared(location))};
51  const auto I = RotInertia{Iz * AreaDensity{density} / SquareRadian};
52  return MassData{location, mass, I};
53 }
54 
56 {
57  const auto r_squared = Area{r * r};
58  const auto circle_area = r_squared * Pi;
59  const auto circle_mass = density * circle_area;
60  const auto d = v1 - v0;
61  const auto offset = GetRevPerpendicular(GetUnitVector(d, UnitVec::GetZero())) * r;
62  const auto b = GetMagnitude(d);
63  const auto h = r * Real{2};
64  const auto rect_mass = density * b * h;
65  const auto totalMass = circle_mass + rect_mass;
66  const auto center = (v0 + v1) / 2;
67 
70  const auto halfCircleArea = circle_area / 2;
71  const auto halfRSquared = r_squared / 2;
72 
73  const auto vertices = Vector<const Length2, 4>{
74  Length2{v0 + offset},
75  Length2{v0 - offset},
76  Length2{v1 - offset},
77  Length2{v1 + offset}
78  };
79  const auto I_z = GetPolarMoment(vertices);
80  const auto I0 = SecondMomentOfArea{halfCircleArea * (halfRSquared + GetMagnitudeSquared(v0))};
81  const auto I1 = SecondMomentOfArea{halfCircleArea * (halfRSquared + GetMagnitudeSquared(v1))};
82  assert(I0 >= SecondMomentOfArea{0});
83  assert(I1 >= SecondMomentOfArea{0});
84  assert(I_z >= SecondMomentOfArea{0});
85  const auto I = RotInertia{(I0 + I1 + I_z) * density / SquareRadian};
86  return MassData{center, totalMass, I};
87 }
88 
90  Span<const Length2> vertices)
91 {
92  // See: https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
93 
94  // Polygon mass, centroid, and inertia.
95  // Let rho be the polygon density in mass per unit area.
96  // Then:
97  // mass = rho * int(dA)
98  // centroid.x = (1/mass) * rho * int(x * dA)
99  // centroid.y = (1/mass) * rho * int(y * dA)
100  // I = rho * int((x*x + y*y) * dA)
101  //
102  // We can compute these integrals by summing all the integrals
103  // for each triangle of the polygon. To evaluate the integral
104  // for a single triangle, we make a change of variables to
105  // the (u,v) coordinates of the triangle:
106  // x = x0 + e1x * u + e2x * v
107  // y = y0 + e1y * u + e2y * v
108  // where 0 <= u && 0 <= v && u + v <= 1.
109  //
110  // We integrate u from [0,1-v] and then v from [0,1].
111  // We also need to use the Jacobian of the transformation:
112  // D = cross(e1, e2)
113  //
114  // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3)
115  //
116  // The rest of the derivation is handled by computer algebra.
117 
118  const auto count = size(vertices);
119  switch (count)
120  {
121  case 0:
122  return MassData{};
123  case 1:
124  return playrho::d2::GetMassData(vertexRadius, density, vertices[0]);
125  case 2:
126  return playrho::d2::GetMassData(vertexRadius, density, vertices[0], vertices[1]);
127  default:
128  break;
129  }
130 
131  auto center = Length2{};
132  auto area = 0_m2;
133  auto I = SecondMomentOfArea{0};
134 
135  // s is the reference point for forming triangles.
136  // It's location doesn't change the result (except for rounding error).
137  // This code puts the reference point inside the polygon.
138  const auto s = Average(vertices);
139 
140  for (auto i = decltype(count){0}; i < count; ++i)
141  {
142  // Triangle vertices.
143  const auto e1 = vertices[i] - s;
144  const auto e2 = vertices[GetModuloNext(i, count)] - s;
145 
146  const auto D = Cross(e1, e2);
147 
148  PLAYRHO_CONSTEXPR const auto RealReciprocalOfTwo = Real{1} / Real{2}; // .5
149  const auto triangleArea = D * RealReciprocalOfTwo;
150  area += triangleArea;
151 
152  // Area weighted centroid
153  PLAYRHO_CONSTEXPR const auto RealReciprocalOfThree = Real{1} / Real{3}; // .3333333...
154  center += StripUnit(triangleArea) * (e1 + e2) * RealReciprocalOfThree;
155 
156  const auto intx2 = Square(GetX(e1)) + GetX(e2) * GetX(e1) + Square(GetX(e2));
157  const auto inty2 = Square(GetY(e1)) + GetY(e2) * GetY(e1) + Square(GetY(e2));
158 
159  PLAYRHO_CONSTEXPR const auto RealReciprocalOfTwelve = Real{1} / Real{3 * 4}; // .083333..
160  const auto triangleI = D * (intx2 + inty2) * RealReciprocalOfTwelve;
161  I += triangleI;
162  }
163 
164  // Total mass
165  const auto mass = Mass{AreaDensity{density} * area};
166 
167  // Center of mass
168  assert((area > 0_m2) && !AlmostZero(StripUnit(area)));
169  center /= StripUnit(area);
170  const auto massDataCenter = center + s;
171 
172  // Inertia tensor relative to the local origin (point s).
173  // Shift to center of mass then to original body origin.
174  const auto massCenterOffset = GetMagnitudeSquared(massDataCenter);
175  const auto centerOffset = GetMagnitudeSquared(center);
176  const auto inertialLever = massCenterOffset - centerOffset;
177  const auto massDataI = RotInertia{((AreaDensity{density} * I) + (mass * inertialLever)) / SquareRadian};
178 
179  return MassData{massDataCenter, mass, massDataI};
180 }
181 
183 {
184  return GetMassData(f.GetShape());
185 }
186 
187 MassData ComputeMassData(const Body& body) noexcept
188 {
189  auto mass = 0_kg;
190  auto I = RotInertia{0};
191  auto center = Length2{};
192  for (auto&& f: body.GetFixtures())
193  {
194  const auto& fixture = GetRef(f);
195  if (fixture.GetDensity() > 0_kgpm2)
196  {
197  const auto massData = GetMassData(fixture);
198  mass += Mass{massData.mass};
199  center += Real{Mass{massData.mass} / Kilogram} * massData.center;
200  I += RotInertia{massData.I};
201  }
202  }
203  return MassData{center, mass, I};
204 }
205 
206 MassData GetMassData(const Body& body) noexcept
207 {
208  const auto I = GetLocalRotInertia(body);
209  return MassData{body.GetLocalCenter(), GetMass(body), I};
210 }
211 
212 } // namespace d2
213 } // namespace playrho