Distance.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  * 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 
24 
25 namespace playrho {
26 namespace d2 {
27 namespace {
28 
29 inline bool HasKey(IndexPair3 pairs, IndexPair key)
30 {
31  return pairs[0] == key || pairs[1] == key || pairs[2] == key;
32 }
33 
34 inline SimplexEdge GetSimplexEdge(const DistanceProxy& proxyA,
35  const Transformation& xfA,
36  VertexCounter idxA,
37  const DistanceProxy& proxyB,
38  const Transformation& xfB,
39  VertexCounter idxB)
40 {
41  const auto wA = Transform(proxyA.GetVertex(idxA), xfA);
42  const auto wB = Transform(proxyB.GetVertex(idxB), xfB);
43  return SimplexEdge{wA, idxA, wB, idxB};
44 }
45 
46 inline SimplexEdges GetSimplexEdges(const IndexPair3 indexPairs,
47  const DistanceProxy& proxyA, const Transformation& xfA,
48  const DistanceProxy& proxyB, const Transformation& xfB)
49 {
51  using size_type = std::remove_const<decltype(MaxSimplexEdges)>::type;
52 
53  auto simplexEdges = SimplexEdges{};
54  const auto count = GetNumValidIndices(indexPairs);
55  switch (count)
56  {
57  case 3:
58  simplexEdges[2] = GetSimplexEdge(proxyA, xfA, std::get<0>(indexPairs[2]),
59  proxyB, xfB, std::get<1>(indexPairs[2]));
60  [[fallthrough]];
61  case 2:
62  simplexEdges[1] = GetSimplexEdge(proxyA, xfA, std::get<0>(indexPairs[1]),
63  proxyB, xfB, std::get<1>(indexPairs[1]));
64  [[fallthrough]];
65  case 1:
66  simplexEdges[0] = GetSimplexEdge(proxyA, xfA, std::get<0>(indexPairs[0]),
67  proxyB, xfB, std::get<1>(indexPairs[0]));
68  }
69  simplexEdges.size(static_cast<size_type>(count));
70  return simplexEdges;
71 }
72 
73 } // namespace
74 
75 DistanceConf GetDistanceConf(const ToiConf& conf) noexcept
76 {
77  auto distanceConf = DistanceConf{};
78  distanceConf.maxIterations = conf.maxDistIters;
79  return distanceConf;
80 }
81 
82 PairLength2 GetWitnessPoints(const Simplex& simplex) noexcept
83 {
84  auto pointA = Length2{};
85  auto pointB = Length2{};
86 
87  const auto numEdges = size(simplex);
88  for (auto i = decltype(numEdges){0}; i < numEdges; ++i)
89  {
90  const auto e = simplex.GetSimplexEdge(i);
91  const auto c = simplex.GetCoefficient(i);
92 
93  pointA += e.GetPointA() * c;
94  pointB += e.GetPointB() * c;
95  }
96 #if 0
97  // In the 3-simplex case, pointA and pointB are usually equal.
98  // XXX: Sometimes in the 3-simplex case, pointA is slightly different than pointB. Why??
99  if (size == 3 && pointA != pointB)
100  {
101  std::cout << "odd: " << pointA << " != " << pointB;
102  std::cout << std::endl;
103  }
104 #endif
105  return PairLength2{pointA, pointB};
106 }
107 
108 DistanceOutput Distance(const DistanceProxy& proxyA, const Transformation& transformA,
109  const DistanceProxy& proxyB, const Transformation& transformB,
110  DistanceConf conf)
111 {
112  using playrho::IsFull;
113 
114  assert(proxyA.GetVertexCount() > 0);
115  assert(IsValid(transformA.p));
116  assert(proxyB.GetVertexCount() > 0);
117  assert(IsValid(transformB.p));
118 
119  auto savedIndices = conf.cache.indices;
120 
121  // Initialize the simplex.
122  auto simplexEdges = GetSimplexEdges(savedIndices, proxyA, transformA, proxyB, transformB);
123 
124  // Compute the new simplex metric, if it is substantially different than
125  // old metric then flush the simplex.
126  if (size(simplexEdges) > 1)
127  {
128  const auto metric1 = conf.cache.metric;
129  const auto metric2 = Simplex::CalcMetric(simplexEdges);
130  if ((metric2 < (metric1 / 2)) || (metric2 > (metric1 * 2)) || (metric2 < 0) || AlmostZero(metric2))
131  {
132  simplexEdges.clear();
133  }
134  }
135 
136  if (empty(simplexEdges))
137  {
138  simplexEdges.push_back(GetSimplexEdge(proxyA, transformA, 0, proxyB, transformB, 0));
139  savedIndices = IndexPair3{{IndexPair{0, 0}, InvalidIndexPair, InvalidIndexPair}};
140  }
141 
142  auto simplex = Simplex{};
143  auto state = DistanceOutput::HitMaxIters;
144 
145 #if defined(DO_COMPUTE_CLOSEST_POINT)
146  auto distanceSqr1 = MaxFloat;
147 #endif
148 
149  // Main iteration loop.
150  auto iter = decltype(conf.maxIterations){0};
151  while (iter < conf.maxIterations)
152  {
153  ++iter;
154 
155  simplex = Simplex::Get(simplexEdges);
156  simplexEdges = simplex.GetEdges();
157 
158  // If have max simplex edges (3), then the origin is in corresponding triangle.
159  if (IsFull(simplexEdges))
160  {
162  break;
163  }
164 
165 #if defined(DO_COMPUTE_CLOSEST_POINT)
166  // Compute closest point.
167  const auto p = GetClosestPoint(simplexEdges);
168  const auto distanceSqr2 = GetMagnitudeSquared(p);
169 
170  // Ensure progress
171  if (distanceSqr2 >= distanceSqr1)
172  {
173  //break;
174  }
175  distanceSqr1 = distanceSqr2;
176 #endif
177  // Get search direction.
178  const auto d = CalcSearchDirection(simplexEdges);
179  assert(IsValid(d));
180 
181  // Ensure the search direction is numerically fit.
183  {
185 
186  // The origin is probably contained by a line segment
187  // or triangle. Thus the shapes are overlapped.
188 
189  // We can't return zero here even though there may be overlap.
190  // In case the simplex is a point, segment, or triangle it is difficult
191  // to determine if the origin is contained in the CSO or very close to it.
192  break;
193  }
194 
195  // Compute a tentative new simplex edge using support points.
196  const auto indexA = GetSupportIndex(proxyA, InverseRotate(-d, transformA.q));
197  const auto indexB = GetSupportIndex(proxyB, InverseRotate(d, transformB.q));
198 
199  // Check for duplicate support points. This is the main termination criteria.
200  // If there's a duplicate support point, code must exit loop to avoid cycling.
201  if (HasKey(savedIndices, IndexPair{indexA, indexB}))
202  {
204  break;
205  }
206 
207  // New edge is ok and needed.
208  simplexEdges.push_back(GetSimplexEdge(proxyA, transformA, indexA, proxyB, transformB, indexB));
209  savedIndices = GetIndexPairs(simplexEdges);
210  }
211 
212  // Note: simplexEdges is same here as simplex.GetSimplexEdges().
213  // GetWitnessPoints(simplex), iter, Simplex::GetCache(simplexEdges)
214  return DistanceOutput{simplex, iter, state};
215 }
216 
217 Area TestOverlap(const DistanceProxy& proxyA, const Transformation& xfA,
218  const DistanceProxy& proxyB, const Transformation& xfB,
219  DistanceConf conf)
220 {
221  const auto distanceInfo = Distance(proxyA, xfA, proxyB, xfB, conf);
222  assert(distanceInfo.state != DistanceOutput::Unknown && distanceInfo.state != DistanceOutput::HitMaxIters);
223 
224  const auto witnessPoints = GetWitnessPoints(distanceInfo.simplex);
225  const auto distanceSquared = GetMagnitudeSquared(GetDelta(witnessPoints));
226  const auto totalRadiusSquared = Square(proxyA.GetVertexRadius() + proxyB.GetVertexRadius());
227  return totalRadiusSquared - distanceSquared;
228 }
229 
230 } // namespace d2
231 } // namespace playrho