Math.cpp
Go to the documentation of this file.
1 /*
2  * Original work Copyright (c) 2007-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  *
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  *
13  * 1. The origin of this software must not be misrepresented; you must not
14  * claim that you wrote the original software. If you use this software
15  * in a product, an acknowledgment in the product documentation would be
16  * appreciated but is not required.
17  * 2. Altered source versions must be plainly marked as such, and must not be
18  * misrepresented as being the original software.
19  * 3. This notice may not be removed or altered from any source distribution.
20  */
21 
22 #include <PlayRho/Common/Math.hpp>
23 
24 namespace playrho {
25 
26 Angle GetDelta(Angle a1, Angle a2) noexcept
27 {
28  a1 = GetNormalized(a1);
29  a2 = GetNormalized(a2);
30  const auto a12 = a2 - a1;
31  if (a12 > Pi * Radian)
32  {
33  // 190_deg becomes 190_deg - 360_deg = -170_deg
34  return a12 - 2 * Pi * Radian;
35  }
36  if (a12 < -Pi * Radian)
37  {
38  // -200_deg becomes -200_deg + 360_deg = 100_deg
39  return a12 + 2 * Pi * Radian;
40  }
41  return a12;
42 }
43 
45 {
46  assert(size(vertices) >= 3);
47 
48  auto c = Length2{} * 0_m2;
49  auto area = 0_m2;
50 
51  // <code>pRef</code> is the reference point for forming triangles.
52  // It's location doesn't change the result (except for rounding error).
53  const auto pRef = Average(vertices);
54 
55  for (auto i = decltype(size(vertices)){0}; i < size(vertices); ++i)
56  {
57  // Triangle vertices.
58  const auto p1 = pRef;
59  const auto p2 = vertices[i];
60  const auto p3 = vertices[GetModuloNext(i, size(vertices))];
61 
62  const auto e1 = p2 - p1;
63  const auto e2 = p3 - p1;
64 
65  PLAYRHO_CONSTEXPR const auto RealInverseOfTwo = Real{1} / Real{2};
66  const auto triangleArea = Area{Cross(e1, e2) * RealInverseOfTwo};
67  area += triangleArea;
68 
69  // Area weighted centroid
70  PLAYRHO_CONSTEXPR const auto RealInverseOfThree = Real{1} / Real{3};
71  const auto aveP = (p1 + p2 + p3) * RealInverseOfThree;
72  c += triangleArea * aveP;
73  }
74 
75  // Centroid
76  assert((area > 0_m2) && !AlmostZero(area / SquareMeter));
77  return c / area;
78 }
79 
80 std::vector<Length2> GetCircleVertices(Length radius, unsigned slices, Angle start, Real turns)
81 {
82  std::vector<Length2> vertices;
83  if (slices > 0)
84  {
85  const auto integralTurns = static_cast<long int>(turns);
86  const auto wholeNum = (turns == static_cast<Real>(integralTurns));
87  const auto deltaAngle = (Pi * 2_rad * turns) / static_cast<Real>(slices);
88  auto i = decltype(slices){0};
89  while (i < slices)
90  {
91  const auto angleInRadians = Real{(start + (static_cast<Real>(i) * deltaAngle)) / Radian};
92  const auto x = radius * cos(angleInRadians);
93  const auto y = radius * sin(angleInRadians);
94  vertices.emplace_back(x, y);
95  ++i;
96  }
97  if (wholeNum)
98  {
99  // Ensure whole circles come back to original point EXACTLY.
100  vertices.push_back(vertices[0]);
101  }
102  else
103  {
104  const auto angleInRadians = Real{(start + (static_cast<Real>(i) * deltaAngle)) / Radian};
105  const auto x = radius * cos(angleInRadians);
106  const auto y = radius * sin(angleInRadians);
107  vertices.emplace_back(x, y);
108  }
109  }
110  return vertices;
111 }
112 
114 {
115  return Area{radius * radius * Pi};
116 }
117 
119 {
120  // Uses the "Shoelace formula".
121  // See: https://en.wikipedia.org/wiki/Shoelace_formula
122  auto sum = 0_m2;
123  const auto count = size(vertices);
124  for (auto i = decltype(count){0}; i < count; ++i)
125  {
126  const auto last_v = vertices[GetModuloPrev(i, count)];
127  const auto this_v = vertices[i];
128  const auto next_v = vertices[GetModuloNext(i, count)];
129  sum += GetX(this_v) * (GetY(next_v) - GetY(last_v));
130  }
131 
132  // Note that using the absolute value isn't necessary for vertices in counter-clockwise
133  // ordering; only needed for clockwise ordering.
134  PLAYRHO_CONSTEXPR const auto RealInverseOfTwo = Real{1} / Real{2};
135  return abs(sum) * RealInverseOfTwo;
136 }
137 
139 {
140  assert(size(vertices) > 2);
141 
142  // Use formulas Ix and Iy for second moment of area of any simple polygon and apply
143  // the perpendicular axis theorem on these to get the desired answer.
144  //
145  // See:
146  // https://en.wikipedia.org/wiki/Second_moment_of_area#Any_polygon
147  // https://en.wikipedia.org/wiki/Second_moment_of_area#Perpendicular_axis_theorem
148  auto sum_x = SquareMeter * SquareMeter * 0;
149  auto sum_y = SquareMeter * SquareMeter * 0;
150  const auto count = size(vertices);
151  for (auto i = decltype(count){0}; i < count; ++i)
152  {
153  const auto this_v = vertices[i];
154  const auto next_v = vertices[GetModuloNext(i, count)];
155  const auto fact_b = Cross(this_v, next_v);
156  sum_x += [&]() {
157  const auto fact_a = Square(GetY(this_v)) + GetY(this_v) * GetY(next_v) + Square(GetY(next_v));
158  return fact_a * fact_b;
159  }();
160  sum_y += [&]() {
161  const auto fact_a = Square(GetX(this_v)) + GetX(this_v) * GetX(next_v) + Square(GetX(next_v));
162  return fact_a * fact_b;
163  }();
164  }
165  const auto secondMomentOfAreaX = SecondMomentOfArea{sum_x};
166  const auto secondMomentOfAreaY = SecondMomentOfArea{sum_y};
167  PLAYRHO_CONSTEXPR const auto RealInverseOfTwelve = Real{1} / Real{12};
168  return (secondMomentOfAreaX + secondMomentOfAreaY) * RealInverseOfTwelve;
169 }
170 
171 namespace d2 {
172 
174  const Velocity velB, const Length2 relB) noexcept
175 {
176 #if 0 // Using std::fma appears to be slower!
177  const auto revPerpRelB = GetRevPerpendicular(relB);
178  const auto xRevPerpRelB = StripUnit(revPerpRelB.x);
179  const auto yRevPerpRelB = StripUnit(revPerpRelB.y);
180  const auto angVelB = StripUnit(velB.angular);
181  const auto xLinVelB = StripUnit(velB.linear.x);
182  const auto yLinVelB = StripUnit(velB.linear.y);
183  const auto xFmaB = std::fma(xRevPerpRelB, angVelB, xLinVelB);
184  const auto yFmaB = std::fma(yRevPerpRelB, angVelB, yLinVelB);
185 
186  const auto revPerpRelA = GetRevPerpendicular(relA);
187  const auto xRevPerpRelA = StripUnit(revPerpRelA.x);
188  const auto yRevPerpRelA = StripUnit(revPerpRelA.y);
189  const auto angVelA = StripUnit(velA.angular);
190  const auto xLinVelA = StripUnit(velA.linear.x);
191  const auto yLinVelA = StripUnit(velA.linear.y);
192  const auto xFmaA = std::fma(xRevPerpRelA, angVelA, xLinVelA);
193  const auto yFmaA = std::fma(yRevPerpRelA, angVelA, yLinVelA);
194 
195  const auto deltaFmaX = xFmaB - xFmaA;
196  const auto deltaFmaY = yFmaB - yFmaA;
197 
198  return Vec2{deltaFmaX, deltaFmaY} * MeterPerSecond;
199 #else
200  const auto velBrot = GetRevPerpendicular(relB) * (velB.angular / Radian);
201  const auto velArot = GetRevPerpendicular(relA) * (velA.angular / Radian);
202  return (velB.linear + velBrot) - (velA.linear + velArot);
203 #endif
204 }
205 
206 } // namespace d2
207 
208 } // namespace playrho