ChainShapeConf.cpp
Go to the documentation of this file.
1 /*
2  * Original work Copyright (c) 2006-2010 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 
24 #include <algorithm>
25 #include <iterator>
26 
27 namespace playrho {
28 namespace d2 {
29 
30 namespace {
31 #if 0
32  inline bool IsEachVertexFarEnoughApart(Span<const Length2> vertices)
33  {
34  for (auto i = decltype(size(vertices)){1}; i < size(vertices); ++i)
35  {
36  const auto delta = vertices[i-1] - vertices[i];
37 
38  // XXX not quite right unit-wise but this works well enough.
40  {
41  return false;
42  }
43  }
44  return true;
45  }
46 #endif
47 } // anonymous namespace
48 
50 
51 ChainShapeConf& ChainShapeConf::Set(std::vector<Length2> vertices)
52 {
53  const auto count = size(vertices);
54  if (count > MaxChildCount)
55  {
56  throw InvalidArgument("too many vertices");
57  }
58 
59  m_vertices = vertices;
60  ResetNormals();
61  return *this;
62 }
63 
64 void ChainShapeConf::ResetNormals()
65 {
66  m_normals.clear();
67  if (size(m_vertices) > std::size_t{1})
68  {
69  auto vprev = Length2{};
70  auto first = true;
71  for (const auto& v: m_vertices)
72  {
73  if (!first)
74  {
75  // Get the normal and push it and its reverse.
76  // This "doubling up" of the normals, makes the GetChild() method work.
77  const auto normal = GetUnitVector(GetFwdPerpendicular(v - vprev));
78  m_normals.push_back(normal);
79  m_normals.push_back(-normal);
80  }
81  else
82  {
83  first = false;
84  }
85  vprev = v;
86  }
87  }
88 }
89 
91 {
92  std::for_each(begin(m_vertices), end(m_vertices), [=](Length2& v){
93  v = m * v;
94  });
95  ResetNormals();
96  return *this;
97 }
98 
100 {
101  if (!empty(m_vertices))
102  {
103  auto vprev = m_vertices.back();
104  m_vertices.emplace_back(vertex);
105  const auto normal = GetUnitVector(GetFwdPerpendicular(vertex - vprev));
106  m_normals.push_back(normal);
107  m_normals.push_back(-normal);
108  }
109  else
110  {
111  m_vertices.emplace_back(vertex);
112  }
113  return *this;
114 }
115 
117 {
118  const auto density = this->density;
119  if (density > 0_kgpm2)
120  {
121  const auto vertexCount = GetVertexCount();
122  if (vertexCount > 1)
123  {
124  // XXX: This overcounts for the overlapping circle shape.
125  auto mass = 0_kg;
126  auto I = RotInertia{0};
127  auto area = 0_m2;
128  auto center = Length2{};
129  auto vprev = GetVertex(0);
130  const auto circle_area = Square(vertexRadius) * Pi;
131  for (auto i = decltype(vertexCount){1}; i < vertexCount; ++i)
132  {
133  const auto v = GetVertex(i);
134  const auto massData = playrho::d2::GetMassData(vertexRadius, density, vprev, v);
135  mass += Mass{massData.mass};
136  center += Real{Mass{massData.mass} / Kilogram} * massData.center;
137  I += RotInertia{massData.I};
138 
139  const auto d = v - vprev;
140  const auto b = GetMagnitude(d);
141  const auto h = vertexRadius * Real{2};
142  area += b * h + circle_area;
143 
144  vprev = v;
145  }
146  center /= StripUnit(area);
147  return MassData{center, mass, I};
148  }
149  if (vertexCount == 1)
150  {
152  }
153  }
154  return MassData{};
155 }
156 
158 {
159  if (index >= GetChildCount())
160  {
161  throw InvalidArgument("index out of range");
162  }
163  const auto vertexCount = GetVertexCount();
164  if (vertexCount > 1)
165  {
166  return DistanceProxy{vertexRadius, 2, &m_vertices[index], &m_normals[index * 2]};
167  }
168  return DistanceProxy{vertexRadius, 1, &m_vertices[0], nullptr};
169 }
170 
171 // Free functions...
172 
174 {
175  auto conf = ChainShapeConf{};
176 
177  const auto halfWidth = GetX(dimensions) / Real{2};
178  const auto halfHeight = GetY(dimensions) / Real{2};
179 
180  const auto btmLeft = Length2(-halfWidth, -halfHeight);
181  const auto btmRight = Length2(+halfWidth, -halfHeight);
182  const auto topLeft = Length2(-halfWidth, +halfHeight);
183  const auto topRight = Length2(+halfWidth, +halfHeight);
184 
185  conf.Add(btmRight);
186  conf.Add(topRight);
187  conf.Add(topLeft);
188  conf.Add(btmLeft);
189  conf.Add(conf.GetVertex(0));
190 
191  return conf;
192 }
193 
195 {
196  auto conf = ChainShapeConf{};
197 
198  const auto rangeX = arg.ranges[0];
199  const auto rangeY = arg.ranges[1];
200 
201  conf.Add(Length2{rangeX.GetMax(), rangeY.GetMin()}); // bottom right
202  conf.Add(Length2{rangeX.GetMax(), rangeY.GetMax()}); // top right
203  conf.Add(Length2{rangeX.GetMin(), rangeY.GetMax()}); // top left
204  conf.Add(Length2{rangeX.GetMin(), rangeY.GetMin()}); // bottom left
205  conf.Add(conf.GetVertex(0)); // close the chain around to first point
206 
207  return conf;
208 }
209 
210 } // namespace d2
211 } // namespace playrho