Manifold.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  *
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 
28 #include <PlayRho/Defines.hpp>
29 
30 #include <array>
31 #include <bitset>
32 #include <algorithm>
33 
34 #define PLAYRHO_MAGIC(x) (x)
35 
36 namespace playrho {
37 namespace d2 {
38 
39 namespace {
40 
41 #ifdef DEFINE_GET_MANIFOLD
42 inline index_type GetEdgeIndex(VertexCounter i1, VertexCounter i2, VertexCounter count)
43 {
44  if (GetModuloNext(i1, count) == i2)
45  {
46  return i1;
47  }
48  if (GetModuloNext(i2, count) == i1)
49  {
50  return i2;
51  }
52  return InvalidVertex;
53 }
54 #endif
55 
56 using VertexCounterPair = std::pair<VertexCounter, VertexCounter>;
57 
58 VertexCounterPair
59 GetMostAntiParallelEdge(UnitVec shape0_rel_n0, const Transformation& xf0,
60  const DistanceProxy& shape1, const Transformation& xf1,
61  const VertexCounter2 indices1) noexcept
62 {
63  const auto firstIdx = std::get<0>(indices1);
64  const auto secondIdx = std::get<1>(indices1);
65  if (secondIdx == InvalidVertex)
66  {
67  // Gets most anti-parallel edge of either prevIdx or firstIdx.
68  const auto normal = InverseRotate(Rotate(shape0_rel_n0, xf0.q), xf1.q);
69  const auto count = shape1.GetVertexCount();
70  const auto prevIdx = GetModuloPrev(firstIdx, count);
71  const auto prevDot = Dot(normal, shape1.GetNormal(prevIdx));
72  const auto currDot = Dot(normal, shape1.GetNormal(firstIdx));
73  return (prevDot < currDot)?
74  std::make_pair(prevIdx, firstIdx):
75  std::make_pair(firstIdx, GetModuloNext(firstIdx, count));
76  }
77  return ((secondIdx > firstIdx) && ((firstIdx + 1) == secondIdx))?
78  std::make_pair(firstIdx, secondIdx): std::make_pair(secondIdx, firstIdx);
79 }
80 
81 ClipList GetClipPoints(Length2 shape0_abs_v0, Length2 shape0_abs_v1, VertexCounterPair shape0_e,
82  UnitVec shape0_abs_e0_dir,
83  Length2 shape1_abs_v0, Length2 shape1_abs_v1, VertexCounterPair shape1_e)
84 {
85  // Gets the two vertices in world coordinates and their face-vertex contact features
86  // of the incident edge of shape1
87  const auto ie = ClipList{
88  ClipVertex{shape1_abs_v0, GetFaceVertexContactFeature(shape0_e.first, shape1_e.first)},
89  ClipVertex{shape1_abs_v1, GetFaceVertexContactFeature(shape0_e.first, shape1_e.second)}
90  };
91  const auto shape0_dp_v0_e0 = -Dot(shape0_abs_e0_dir, shape0_abs_v0);
92  const auto shape0_dp_v1_e0 = +Dot(shape0_abs_e0_dir, shape0_abs_v1);
93 
94  const auto points = ClipSegmentToLine(ie, -shape0_abs_e0_dir, shape0_dp_v0_e0, shape0_e.first);
95  return ClipSegmentToLine(points, +shape0_abs_e0_dir, shape0_dp_v1_e0, shape0_e.second);
96 }
97 
98 } // anonymous namespace
99 
100 Manifold GetManifold(bool flipped,
101  const DistanceProxy& shape0, const Transformation& xf0,
102  const VertexCounter idx0,
103  const DistanceProxy& shape1, const Transformation& xf1,
104  const VertexCounter2 indices1,
105  const Manifold::Conf conf)
106 {
107  assert(shape0.GetVertexCount() > 1 && shape1.GetVertexCount() > 1);
108 
109  const auto r0 = shape0.GetVertexRadius();
110  const auto r1 = shape1.GetVertexRadius();
111  const auto totalRadius = Length{r0 + r1};
112 
113  const auto idx0Next = GetModuloNext(idx0, shape0.GetVertexCount());
114 
115  const auto shape0_rel_v0 = shape0.GetVertex(idx0);
116  const auto shape0_rel_v1 = shape0.GetVertex(idx0Next);
117  const auto shape0_abs_v0 = Transform(shape0_rel_v0, xf0);
118  const auto shape0_abs_v1 = Transform(shape0_rel_v1, xf0);
119 
120  auto shape0_len_edge0 = GetMagnitudeSquared(shape0_rel_v1 - shape0_rel_v0);
121 
122  // Clip incident edge against extruded edge1 side edges.
123  // Side offsets, extended by polytope skin thickness.
124 
125  const auto shape0_rel_n0 = shape0.GetNormal(idx0);
126  const auto shape0_rel_e0_dir = GetRevPerpendicular(shape0_rel_n0);
127  const auto shape1_e = GetMostAntiParallelEdge(shape0_rel_n0, xf0, shape1, xf1, indices1);
128  const auto shape1_rel_v0 = shape1.GetVertex(shape1_e.first);
129  const auto shape1_abs_v0 = Transform(shape1_rel_v0, xf1);
130  const auto shape1_rel_v1 = shape1.GetVertex(shape1_e.second);
131  const auto shape1_abs_v1 = Transform(shape1_rel_v1, xf1);
132  {
133  const auto shape0_abs_e0_dir = Rotate(shape0_rel_e0_dir, xf0.q);
134  const auto clipPoints = GetClipPoints(shape0_abs_v0, shape0_abs_v1, std::make_pair(idx0, idx0Next),
135  shape0_abs_e0_dir,
136  shape1_abs_v0, shape1_abs_v1, shape1_e);
137  if (size(clipPoints) == 2)
138  {
139  const auto abs_normal = GetFwdPerpendicular(shape0_abs_e0_dir);
140  const auto rel_midpoint = (shape0_rel_v0 + shape0_rel_v1) / 2;
141  const auto abs_offset = Dot(abs_normal, shape0_abs_v0);
142 
143  auto manifold = Manifold{};
144  if (!flipped)
145  {
146  manifold = Manifold::GetForFaceA(GetFwdPerpendicular(shape0_rel_e0_dir), rel_midpoint);
147  for (auto&& cp: clipPoints)
148  {
149  if ((Dot(abs_normal, cp.v) - abs_offset) <= totalRadius)
150  {
151  manifold.AddPoint(Manifold::Point{InverseTransform(cp.v, xf1), cp.cf});
152  }
153  }
154  }
155  else
156  {
157  manifold = Manifold::GetForFaceB(GetFwdPerpendicular(shape0_rel_e0_dir), rel_midpoint);
158  for (auto&& cp: clipPoints)
159  {
160  if ((Dot(abs_normal, cp.v) - abs_offset) <= totalRadius)
161  {
162  manifold.AddPoint(Manifold::Point{InverseTransform(cp.v, xf1), Flip(cp.cf)});
163  }
164  }
165  }
166  if (manifold.GetPointCount() > 0)
167  {
168  return manifold;
169  }
170  }
171  }
172 
173  // If the shapes are colliding, then they're colliding with each others corners.
174  // Using a circles manifold, means these corners will repell each other with a normal
175  // that's in the direction between the two vertices.
176  // That's problematic though for things like polygons sliding over edges where a face
177  // manifold that favors the primary edge can work better.
178  // Use a threshold against the ratio of the square of the vertex radius to the square
179  // of the length of the primary edge to determine whether to return a circles manifold
180  // or a face manifold.
181  const auto totalRadiusSquared = Square(totalRadius);
182  const auto mustUseFaceManifold = shape0_len_edge0 > Square(conf.maxCirclesRatio * r0);
183  if (GetMagnitudeSquared(shape0_abs_v0 - shape1_abs_v0) <= totalRadiusSquared)
184  {
185  // shape 1 vertex 1 is colliding with shape 2 vertex 1
186  // shape 1 vertex 1 is the vertex at index idx0, or one before idx0Next.
187  // shape 2 vertex 1 is the vertex at index shape1_e.first, or one before shape1_e.second.
188  if (!flipped) // face A
189  {
190  // shape 1 is shape A.
191  if (mustUseFaceManifold)
192  {
193  return Manifold::GetForFaceA(GetFwdPerpendicular(shape0_rel_e0_dir), idx0,
194  shape0_rel_v0, ContactFeature::e_vertex,
195  shape1_e.first, shape1_rel_v0);
196  }
197  return Manifold::GetForCircles(shape0_rel_v0, idx0, shape1_rel_v0, shape1_e.first);
198  }
199  // shape 2 is shape A.
200  if (mustUseFaceManifold)
201  {
202  return Manifold::GetForFaceB(GetFwdPerpendicular(shape0_rel_e0_dir), idx0,
203  shape0_rel_v0, ContactFeature::e_vertex,
204  shape1_e.first, shape1_rel_v0);
205  }
206  return Manifold::GetForCircles(shape1_rel_v0, shape1_e.first, shape0_rel_v0, idx0);
207  }
208  else if (GetMagnitudeSquared(shape0_abs_v0 - shape1_abs_v1) <= totalRadiusSquared)
209  {
210  // shape 1 vertex 1 is colliding with shape 2 vertex 2
211  if (!flipped)
212  {
213  if (mustUseFaceManifold)
214  {
215  return Manifold::GetForFaceA(GetFwdPerpendicular(shape0_rel_e0_dir), idx0,
216  shape0_rel_v0, ContactFeature::e_vertex,
217  shape1_e.second, shape1_rel_v1);
218  }
219  return Manifold::GetForCircles(shape0_rel_v0, idx0, shape1_rel_v1, shape1_e.second);
220  }
221  if (mustUseFaceManifold)
222  {
223  return Manifold::GetForFaceB(GetFwdPerpendicular(shape0_rel_e0_dir), idx0,
224  shape0_rel_v0, ContactFeature::e_vertex,
225  shape1_e.second, shape1_rel_v1);
226  }
227  return Manifold::GetForCircles(shape1_rel_v1, shape1_e.second, shape0_rel_v0, idx0);
228  }
229  else if (GetMagnitudeSquared(shape0_abs_v1 - shape1_abs_v1) <= totalRadiusSquared)
230  {
231  // shape 1 vertex 2 is colliding with shape 2 vertex 2
232  if (!flipped)
233  {
234  if (mustUseFaceManifold)
235  {
236  return Manifold::GetForFaceA(GetFwdPerpendicular(shape0_rel_e0_dir), idx0Next,
237  shape0_rel_v1, ContactFeature::e_vertex,
238  shape1_e.second, shape1_rel_v1);
239  }
240  return Manifold::GetForCircles(shape0_rel_v1, idx0Next, shape1_rel_v1, shape1_e.second);
241  }
242  if (mustUseFaceManifold)
243  {
244  return Manifold::GetForFaceB(GetFwdPerpendicular(shape0_rel_e0_dir), idx0Next,
245  shape0_rel_v1, ContactFeature::e_vertex,
246  shape1_e.second, shape1_rel_v1);
247  }
248  return Manifold::GetForCircles(shape1_rel_v1, shape1_e.second, shape0_rel_v1, idx0Next);
249  }
250  else if (GetMagnitudeSquared(shape0_abs_v1 - shape1_abs_v0) <= totalRadiusSquared)
251  {
252  // shape 1 vertex 2 is colliding with shape 2 vertex 1
253  if (!flipped)
254  {
255  if (mustUseFaceManifold)
256  {
257  return Manifold::GetForFaceA(GetFwdPerpendicular(shape0_rel_e0_dir), idx0Next,
258  shape0_rel_v1, ContactFeature::e_vertex,
259  shape1_e.first, shape1_rel_v0);
260  }
261  return Manifold::GetForCircles(shape0_rel_v1, idx0Next, shape1_rel_v0, shape1_e.first);
262  }
263  if (mustUseFaceManifold)
264  {
265  return Manifold::GetForFaceB(GetFwdPerpendicular(shape0_rel_e0_dir), idx0Next,
266  shape0_rel_v1, ContactFeature::e_vertex,
267  shape1_e.first, shape1_rel_v0);
268  }
269  return Manifold::GetForCircles(shape1_rel_v0, shape1_e.first, shape0_rel_v1, idx0Next);
270  }
271  return Manifold{};
272 }
273 
274 Manifold GetManifold(bool flipped, Length totalRadius,
275  const DistanceProxy& shape, const Transformation& sxf,
276  Length2 point, const Transformation& xfm)
277 {
278  // Computes the center of the circle in the frame of the polygon.
279  const auto cLocal = InverseTransform(Transform(point, xfm), sxf);
280 
281  const auto vertexCount = shape.GetVertexCount();
282 
283  // Find edge that circle is closest to.
284  auto indexOfMax = decltype(vertexCount){0};
285  auto maxSeparation = -MaxFloat * Meter;
286  {
287  for (auto i = decltype(vertexCount){0}; i < vertexCount; ++i)
288  {
289  // Get circle's distance from vertex[i] in direction of normal[i].
290  const auto s = Dot(shape.GetNormal(i), cLocal - shape.GetVertex(i));
291  if (s > totalRadius)
292  {
293  // Early out - no contact.
294  return Manifold{};
295  }
296  if (maxSeparation < s)
297  {
298  maxSeparation = s;
299  indexOfMax = i;
300  }
301  }
302  }
303  const auto indexOfMax2 = GetModuloNext(indexOfMax, vertexCount);
304  assert(maxSeparation <= totalRadius);
305 
306  // Vertices that subtend the incident face.
307  const auto v1 = shape.GetVertex(indexOfMax);
308  const auto v2 = shape.GetVertex(indexOfMax2);
309 
310  if (maxSeparation < 0_m)
311  {
312  const auto faceCenter = (v1 + v2) / Real{2};
313  // Circle's center is inside the polygon and closest to edge[indexOfMax].
314  if (!flipped)
315  {
316  return Manifold::GetForFaceA(shape.GetNormal(indexOfMax), indexOfMax, faceCenter,
317  ContactFeature::e_vertex, 0, point);
318  }
319  return Manifold::GetForFaceB(shape.GetNormal(indexOfMax), indexOfMax, faceCenter,
320  ContactFeature::e_vertex, 0, point);
321  }
322 
323  // Circle's center is outside polygon and closest to edge[indexOfMax].
324  // Compute barycentric coordinates.
325 
326  const auto cLocalV1 = cLocal - v1;
327  if (Dot(cLocalV1, v2 - v1) <= 0_m2)
328  {
329  // Circle's center right of v1 (in direction of v1 to v2).
330  if (GetMagnitudeSquared(cLocalV1) > Square(totalRadius))
331  {
332  return Manifold{};
333  }
334  if (!flipped)
335  {
336  return Manifold::GetForCircles(v1, indexOfMax, point, 0);
337  }
338  return Manifold::GetForCircles(point, 0, v1, indexOfMax);
339  }
340 
341  const auto ClocalV2 = cLocal - v2;
342  if (Dot(ClocalV2, v1 - v2) <= 0_m2)
343  {
344  // Circle's center left of v2 (in direction of v2 to v1).
345  if (GetMagnitudeSquared(ClocalV2) > Square(totalRadius))
346  {
347  return Manifold{};
348  }
349  if (!flipped)
350  {
351  return Manifold::GetForCircles(v2, indexOfMax2, point, 0);
352  }
353  return Manifold::GetForCircles(point, 0, v2, indexOfMax2);
354  }
355 
356  // Circle's center is between v1 and v2.
357  const auto faceCenter = (v1 + v2) / Real{2};
358  if (Dot(cLocal - faceCenter, shape.GetNormal(indexOfMax)) > totalRadius)
359  {
360  return Manifold{};
361  }
362  if (!flipped)
363  {
364  return Manifold::GetForFaceA(shape.GetNormal(indexOfMax), indexOfMax, faceCenter,
365  ContactFeature::e_vertex, 0, point);
366  }
367  return Manifold::GetForFaceB(shape.GetNormal(indexOfMax), indexOfMax, faceCenter,
368  ContactFeature::e_vertex, 0, point);
369 }
370 
372  Length2 locationB, const Transformation& xfB,
373  Length totalRadius) noexcept
374 {
375  const auto pA = Transform(locationA, xfA);
376  const auto pB = Transform(locationB, xfB);
377  // Intermediary results here for debugging...
378  const auto lenSq = GetMagnitudeSquared(pB - pA);
379  const auto totSq = Square(totalRadius);
380  return (lenSq > totSq)? Manifold{}: Manifold::GetForCircles(locationA, 0, locationB, 0);
381 }
382 
383 /*
384  * Definition of public CollideShapes functions.
385  * All CollideShapes functions return a Manifold object.
386  */
387 
389  const DistanceProxy& shapeB, const Transformation& xfB,
390  Manifold::Conf conf)
391 {
392  // Assumes called after detecting AABB overlap.
393  // Find edge normal of max separation on A - return if separating axis is found
394  // Find edge normal of max separation on B - return if separation axis is found
395  // Choose reference edge as min(minA, minB)
396  // Find incident edge
397  // Clip
398 
399  const auto totalRadius = shapeA.GetVertexRadius() + shapeB.GetVertexRadius();
400  const auto countA = shapeA.GetVertexCount();
401  const auto countB = shapeB.GetVertexCount();
402 
403  enum: unsigned { ZeroOneVert = 0x0u, OneVertA = 0x1u, OneVertB = 0x2u };
404  switch (((countA == 1)? OneVertA: ZeroOneVert) | ((countB == 1)? OneVertB: ZeroOneVert))
405  {
406  case OneVertA|OneVertB:
407  return GetManifold(shapeA.GetVertex(0), xfA, shapeB.GetVertex(0), xfB, totalRadius);
408  case OneVertA:
409  return GetManifold(true, totalRadius, shapeB, xfB, shapeA.GetVertex(0), xfA);
410  case OneVertB:
411  return GetManifold(false, totalRadius, shapeA, xfA, shapeB.GetVertex(0), xfB);
412  }
413 
414  const auto do4x4 = (countA == 4) && (countB == 4);
415 
416  const auto edgeSepA = do4x4?
417  GetMaxSeparation4x4(shapeA, xfA, shapeB, xfB):
418  GetMaxSeparation(shapeA, xfA, shapeB, xfB);
419  if (edgeSepA.distance > totalRadius)
420  {
421  return Manifold{};
422  }
423 
424  const auto edgeSepB = do4x4?
425  GetMaxSeparation4x4(shapeB, xfB, shapeA, xfA):
426  GetMaxSeparation(shapeB, xfB, shapeA, xfA);
427  if (edgeSepB.distance > totalRadius)
428  {
429  return Manifold{};
430  }
431 
432  const auto k_tol = PLAYRHO_MAGIC(conf.linearSlop / 10);
433  return (edgeSepB.distance > (edgeSepA.distance + k_tol))?
434  GetManifold(true,
435  shapeB, xfB, edgeSepB.firstShape,
436  shapeA, xfA, edgeSepB.secondShape,
437  conf):
438  GetManifold(false,
439  shapeA, xfA, edgeSepA.firstShape,
440  shapeB, xfB, edgeSepA.secondShape,
441  conf);
442 }
443 
444 #if 0
445 Manifold CollideCached(const DistanceProxy& shapeA, const Transformation& xfA,
446  const DistanceProxy& shapeB, const Transformation& xfB,
447  Manifold::Conf conf)
448 {
449  // Find edge normal of max separation on A - return if separating axis is found
450  // Find edge normal of max separation on B - return if separation axis is found
451  // Choose reference edge as min(minA, minB)
452  // Find incident edge
453  // Clip
454 
455  const auto vertexCountShapeA = shapeA.GetVertexCount();
456  const auto vertexCountShapeB = shapeB.GetVertexCount();
457  if (vertexCountShapeA == 1)
458  {
459  if (vertexCountShapeB > 1)
460  {
461  return CollideShapes(Manifold::e_faceB, shapeB, xfB,
462  shapeA.GetVertex(0), shapeA.GetVertexRadius(), xfA);
463  }
464  return CollideShapes(shapeA.GetVertex(0), shapeA.GetVertexRadius(), xfA,
465  shapeB.GetVertex(0), shapeB.GetVertexRadius(), xfB);
466  }
467  if (vertexCountShapeB == 1)
468  {
469  if (vertexCountShapeA > 1)
470  {
471  return CollideShapes(Manifold::e_faceA, shapeA, xfA,
472  shapeB.GetVertex(0), shapeB.GetVertexRadius(), xfB);
473  }
474  return CollideShapes(shapeA.GetVertex(0), shapeA.GetVertexRadius(), xfA,
475  shapeB.GetVertex(0), shapeB.GetVertexRadius(), xfB);
476  }
477 
478  const auto totalRadius = shapeA.GetVertexRadius() + shapeB.GetVertexRadius();
479 
480  IndexPairSeparation edgeSepA;
481  IndexPairSeparation edgeSepB;
482 
483  if (vertexCountShapeA == 4 && vertexCountShapeB == 4)
484  {
485  Length2 verticesA[4];
486  Length2 verticesB[4];
487  UnitVec normalsA[4];
488  UnitVec normalsB[4];
489 
490  verticesA[0] = Transform(shapeA.GetVertex(0), xfA);
491  verticesA[1] = Transform(shapeA.GetVertex(1), xfA);
492  verticesA[2] = Transform(shapeA.GetVertex(2), xfA);
493  verticesA[3] = Transform(shapeA.GetVertex(3), xfA);
494 
495  normalsA[0] = Rotate(shapeA.GetNormal(0), xfA.q);
496  normalsA[1] = Rotate(shapeA.GetNormal(1), xfA.q);
497  normalsA[2] = Rotate(shapeA.GetNormal(2), xfA.q);
498  normalsA[3] = Rotate(shapeA.GetNormal(3), xfA.q);
499 
500  verticesB[0] = Transform(shapeB.GetVertex(0), xfB);
501  verticesB[1] = Transform(shapeB.GetVertex(1), xfB);
502  verticesB[2] = Transform(shapeB.GetVertex(2), xfB);
503  verticesB[3] = Transform(shapeB.GetVertex(3), xfB);
504 
505  normalsB[0] = Rotate(shapeB.GetNormal(0), xfB.q);
506  normalsB[1] = Rotate(shapeB.GetNormal(1), xfB.q);
507  normalsB[2] = Rotate(shapeB.GetNormal(2), xfB.q);
508  normalsB[3] = Rotate(shapeB.GetNormal(3), xfB.q);
509 
510  const auto dpA = DistanceProxy{shapeA.GetVertexRadius(), vertexCountShapeA, verticesA, normalsA};
511  const auto dpB = DistanceProxy{shapeB.GetVertexRadius(), vertexCountShapeB, verticesB, normalsB};
512  edgeSepA = GetMaxSeparation(dpA, dpB, totalRadius);
513  if (edgeSepA.separation > totalRadius)
514  {
515  return Manifold{};
516  }
517  edgeSepB = GetMaxSeparation(dpB, dpA, totalRadius);
518  if (edgeSepB.separation > totalRadius)
519  {
520  return Manifold{};
521  }
522  }
523  else
524  {
525  edgeSepA = GetMaxSeparation(shapeA, xfA, shapeB, xfB, totalRadius);
526  if (edgeSepA.separation > totalRadius)
527  {
528  return Manifold{};
529  }
530  edgeSepB = GetMaxSeparation(shapeB, xfB, shapeA, xfA, totalRadius);
531  if (edgeSepB.separation > totalRadius)
532  {
533  return Manifold{};
534  }
535  }
536 
537  PLAYRHO_CONSTEXPR const auto k_tol = PLAYRHO_MAGIC(DefaultLinearSlop / Real{10});
538  return (edgeSepB.separation > (edgeSepA.separation + k_tol))?
540  shapeB, xfB, edgeSepB.index1,
541  shapeA, xfA, edgeSepB.index2,
542  conf):
543  GetManifold(Manifold::e_faceA,
544  shapeA, xfA, edgeSepA.index1,
545  shapeB, xfB, edgeSepA.index2,
546  conf);
547 }
548 #endif
549 
550 #ifdef DEFINE_GET_MANIFOLD
551 Manifold GetManifold(const DistanceProxy& proxyA, const Transformation& transformA,
552  const DistanceProxy& proxyB, const Transformation& transformB)
553 {
554  const auto distanceInfo = Distance(proxyA, transformA, proxyB, transformB);
555  const auto totalRadius = proxyA.GetVertexRadius() + proxyB.GetVertexRadius();
556  const auto witnessPoints = GetWitnessPoints(distanceInfo.simplex);
557 
558  const auto distance = sqrt(GetMagnitudeSquared(witnessPoints.a - witnessPoints.b));
559  if (distance > totalRadius)
560  {
561  // no collision
562  return Manifold{};
563  }
564 
565  const auto a_count = proxyA.GetVertexCount();
566  const auto b_count = proxyB.GetVertexCount();
567 
568  index_type a_indices_array[Simplex::MaxEdges];
569  index_type b_indices_array[Simplex::MaxEdges];
570  auto uniqA = std::size_t{0};
571  auto uniqB = std::size_t{0};
572  {
573  std::bitset<MaxShapeVertices> a_indices_set;
574  std::bitset<MaxShapeVertices> b_indices_set;
575  for (auto&& e: distanceInfo.simplex.GetEdges())
576  {
577  const auto indexA = e.GetIndexA();
578  if (!a_indices_set[indexA])
579  {
580  a_indices_set[indexA] = true;
581  a_indices_array[uniqA] = indexA;
582  ++uniqA;
583  }
584  const auto indexB = e.GetIndexB();
585  if (!b_indices_set[indexB])
586  {
587  b_indices_set[indexB] = true;
588  b_indices_array[uniqB] = indexB;
589  ++uniqB;
590  }
591  }
592  }
593 
594  assert(uniqA > 0 && uniqB > 0);
595 
596  std::sort(a_indices_array, a_indices_array + uniqA);
597  std::sort(b_indices_array, b_indices_array + uniqB);
598 
599  if (uniqA < uniqB)
600  {
601  switch (uniqA)
602  {
603  case 1: // uniqB must be 2 or 3
604  {
605  const auto b_idx0 = GetEdgeIndex(b_indices_array[0], b_indices_array[1], b_count);
606  assert(b_idx0 != InvalidVertex);
607  const auto b_idx1 = GetModuloNext(b_idx0, b_count);
608  const auto b_v0 = proxyB.GetVertex(b_idx0);
609  const auto b_v1 = proxyB.GetVertex(b_idx1);
610  const auto lp = (b_v0 + b_v1) / Real{2};
611  const auto ln = GetFwdPerpendicular(GetUnitVector(b_v1 - b_v0));
612  const auto mp0 = Manifold::Point{
613  proxyA.GetVertex(a_indices_array[0]),
614  ContactFeature{
616  a_indices_array[0],
618  b_idx0,
619  }
620  };
621  return Manifold::GetForFaceB(ln, lp, mp0);
622  }
623  case 2: // uniqB must be 3
624  {
625  auto mp0 = Manifold::Point{};
626  auto mp1 = Manifold::Point{};
627  mp0.contactFeature.typeA = ContactFeature::e_face;
628  mp1.contactFeature.typeA = ContactFeature::e_face;
629  const auto v0 = proxyA.GetVertex(a_indices_array[0]);
630  const auto v1 = proxyA.GetVertex(a_indices_array[1]);
631  const auto lp = (v0 + v1) / Real{2};
632  const auto count = proxyA.GetVertexCount();
633  if ((a_indices_array[1] - a_indices_array[0]) == 1)
634  {
635  mp0.contactFeature.indexA = a_indices_array[0];
636  mp1.contactFeature.indexA = a_indices_array[0];
637  const auto ln = GetFwdPerpendicular(GetUnitVector(v1 - v0));
638  return Manifold::GetForFaceA(ln, lp, mp0, mp1);
639  }
640  else if (GetModuloNext(a_indices_array[1], count) == a_indices_array[0])
641  {
642  mp0.contactFeature.indexA = a_indices_array[1];
643  mp1.contactFeature.indexA = a_indices_array[1];
644  const auto ln = GetFwdPerpendicular(GetUnitVector(v0 - v1));
645  return Manifold::GetForFaceA(ln, lp, mp0, mp1);
646  }
647  else
648  {
649  //assert(false);
650  }
651  return Manifold{};
652  }
653  default:
654  break;
655  }
656  }
657  else if (uniqB < uniqA)
658  {
659  switch (uniqB)
660  {
661  case 1: // uniqA must be 2 or 3
662  {
663  const auto a_idx0 = GetEdgeIndex(a_indices_array[0],a_indices_array[1], a_count);
664  assert(a_idx0 != InvalidVertex);
665  const auto a_idx1 = GetModuloNext(a_idx0, a_count);
666  const auto a_v0 = proxyA.GetVertex(a_idx0);
667  const auto a_v1 = proxyA.GetVertex(a_idx1);
668  const auto lp = (a_v0 + a_v1) / Real{2};
669  const auto ln = GetFwdPerpendicular(GetUnitVector(a_v1 - a_v0));
670  const auto mp0 = Manifold::Point{
671  proxyB.GetVertex(b_indices_array[0]),
672  ContactFeature{
674  a_idx0,
676  b_indices_array[0]
677  }
678  };
679  return Manifold::GetForFaceA(ln, lp, mp0);
680  }
681  case 2: // uniqA must be 3
682  {
683  auto mp0 = Manifold::Point{};
684  auto mp1 = Manifold::Point{};
685  mp0.contactFeature.typeB = ContactFeature::e_face;
686  mp1.contactFeature.typeB = ContactFeature::e_face;
687  const auto v0 = proxyB.GetVertex(b_indices_array[0]);
688  const auto v1 = proxyB.GetVertex(b_indices_array[1]);
689  const auto lp = (v0 + v1) / Real{2};
690  const auto count = proxyB.GetVertexCount();
691  if ((b_indices_array[1] - b_indices_array[0]) == 1)
692  {
693  mp0.contactFeature.indexB = b_indices_array[0];
694  mp1.contactFeature.indexB = b_indices_array[0];
695  const auto ln = GetFwdPerpendicular(GetUnitVector(v1 - v0));
696  return Manifold::GetForFaceB(ln, lp, mp0, mp1);
697  }
698  else if (GetModuloNext(b_indices_array[1], count) == b_indices_array[0])
699  {
700  mp0.contactFeature.indexB = b_indices_array[1];
701  mp1.contactFeature.indexB = b_indices_array[1];
702  const auto ln = GetFwdPerpendicular(GetUnitVector(v0 - v1));
703  return Manifold::GetForFaceB(ln, lp, mp0, mp1);
704  }
705  else
706  {
707  //assert(false);
708  }
709  return Manifold{};
710  }
711  default:
712  break;
713  }
714  }
715  else // uniqA == uniqB
716  {
717  switch (uniqA)
718  {
719  case 1:
720  {
721  return Manifold::GetForCircles(proxyA.GetVertex(a_indices_array[0]), a_indices_array[0],
722  proxyB.GetVertex(b_indices_array[0]), b_indices_array[0]);
723  }
724  case 2:
725  {
726  const auto v0 = proxyA.GetVertex(a_indices_array[0]);
727  const auto v1 = proxyA.GetVertex(a_indices_array[1]);
728  const auto lp = (v0 + v1) / Real{2};
729  const auto count = proxyA.GetVertexCount();
730  auto mp0 = Manifold::Point{};
731  auto mp1 = Manifold::Point{};
732  mp0.contactFeature.typeB = ContactFeature::e_vertex;
733  mp0.contactFeature.indexB = b_indices_array[0];
734  mp0.localPoint = proxyB.GetVertex(mp0.contactFeature.indexB);
735  mp1.contactFeature.typeB = ContactFeature::e_vertex;
736  mp1.contactFeature.indexB = b_indices_array[1];
737  mp1.localPoint = proxyB.GetVertex(mp1.contactFeature.indexB);
738  if ((a_indices_array[1] - a_indices_array[0]) == 1)
739  {
740  mp0.contactFeature.typeA = ContactFeature::e_face;
741  mp0.contactFeature.indexA = a_indices_array[0];
742  mp1.contactFeature.typeA = ContactFeature::e_face;
743  mp1.contactFeature.indexA = a_indices_array[0];
744  const auto ln = GetFwdPerpendicular(GetUnitVector(v1 - v0));
745  return Manifold::GetForFaceA(ln, lp, mp0, mp1);
746  }
747  if (GetModuloNext(a_indices_array[1], count) == a_indices_array[0])
748  {
749  mp0.contactFeature.typeA = ContactFeature::e_face;
750  mp0.contactFeature.indexA = a_indices_array[1];
751  mp1.contactFeature.typeA = ContactFeature::e_face;
752  mp1.contactFeature.indexA = a_indices_array[1];
753  const auto ln = GetFwdPerpendicular(GetUnitVector(v0 - v1));
754  return Manifold::GetForFaceA(ln, lp, mp0, mp1);
755  }
756  assert(false);
757  break;
758  }
759  case 3:
760  {
761  const auto ln = UnitVec::GetLeft();
762  const auto lp = Length2{};
763  return Manifold::GetForFaceA(ln, lp);
764  }
765  default:
766  break;
767  }
768  }
769 
770  return Manifold{};
771 }
772 #endif
773 
774 const char* GetName(Manifold::Type type) noexcept
775 {
776  switch (type)
777  {
778  case Manifold::e_unset: break;
779  case Manifold::e_circles: return "circles";
780  case Manifold::e_faceA: return "face-a";
781  case Manifold::e_faceB: return "face-b";
782  }
783  assert(type == Manifold::e_unset);
784  return "unset";
785 }
786 
787 bool operator==(const Manifold::Point& lhs, const Manifold::Point& rhs) noexcept
788 {
789  if (lhs.localPoint != rhs.localPoint)
790  {
791  return false;
792  }
793  if (lhs.contactFeature != rhs.contactFeature)
794  {
795  return false;
796  }
797  if (lhs.normalImpulse != rhs.normalImpulse)
798  {
799  return false;
800  }
801  if (lhs.tangentImpulse != rhs.tangentImpulse)
802  {
803  return false;
804  }
805  return true;
806 }
807 
808 bool operator!=(const Manifold::Point& lhs, const Manifold::Point& rhs) noexcept
809 {
810  return !(lhs == rhs);
811 }
812 
813 bool operator==(const Manifold& lhs, const Manifold& rhs) noexcept
814 {
815  if (lhs.GetType() != rhs.GetType())
816  {
817  return false;
818  }
819  if (lhs.GetPointCount() != rhs.GetPointCount())
820  {
821  return false;
822  }
823 
824  switch (lhs.GetType())
825  {
826  case Manifold::e_unset:
827  break;
828  case Manifold::e_circles:
829  if (lhs.GetLocalPoint() != rhs.GetLocalPoint())
830  {
831  return false;
832  }
833  break;
834  case Manifold::e_faceA:
835  if (lhs.GetLocalPoint() != rhs.GetLocalPoint())
836  {
837  return false;
838  }
839  if (lhs.GetLocalNormal() != rhs.GetLocalNormal())
840  {
841  return false;
842  }
843  break;
844  case Manifold::e_faceB:
845  if (lhs.GetLocalPoint() != rhs.GetLocalPoint())
846  {
847  return false;
848  }
849  if (lhs.GetLocalNormal() != rhs.GetLocalNormal())
850  {
851  return false;
852  }
853  break;
854  }
855 
856  const auto count = lhs.GetPointCount();
857  assert(count <= 2);
858  switch (count)
859  {
860  case 0:
861  break;
862  case 1:
863  if (lhs.GetPoint(0) != rhs.GetPoint(0))
864  {
865  return false;
866  }
867  break;
868  case 2:
869  if (lhs.GetPoint(0) != rhs.GetPoint(0))
870  {
871  if (lhs.GetPoint(0) != rhs.GetPoint(1))
872  {
873  return false;
874  }
875  if (lhs.GetPoint(1) != rhs.GetPoint(0))
876  {
877  return false;
878  }
879  }
880  else if (lhs.GetPoint(1) != rhs.GetPoint(1))
881  {
882  return false;
883  }
884  break;
885  }
886 
887  return true;
888 }
889 
890 bool operator!=(const Manifold& lhs, const Manifold& rhs) noexcept
891 {
892  return !(lhs == rhs);
893 }
894 
895 #if 0
896 Length2 GetLocalPoint(const DistanceProxy& proxy, ContactFeature::Type type,
897  ContactFeature::Index index)
898 {
899  switch (type)
900  {
902  return proxy.GetVertex(index);
904  {
905  return proxy.GetVertex(index);
906  }
907  }
908  return GetInvalid<Length2>();
909 }
910 #endif
911 
912 } // namespace d2
913 } // namespace playrho