From 175a0a8871e159a633040da54c31f0d6d5a0d47e Mon Sep 17 00:00:00 2001
From: Joseph Mirabel <jmirabel@laas.fr>
Date: Thu, 29 Aug 2019 18:15:51 +0200
Subject: [PATCH] [GJK] Handle simplex of rank 3 + checks of backward compat.

---
 include/hpp/fcl/narrowphase/gjk.h |   3 +
 src/narrowphase/gjk.cpp           | 180 +++++++++++++++++++++++++++++-
 2 files changed, 178 insertions(+), 5 deletions(-)

diff --git a/include/hpp/fcl/narrowphase/gjk.h b/include/hpp/fcl/narrowphase/gjk.h
index c018747c..42f39c1a 100644
--- a/include/hpp/fcl/narrowphase/gjk.h
+++ b/include/hpp/fcl/narrowphase/gjk.h
@@ -190,6 +190,9 @@ private:
   /// @brief Project origin (0) onto line a-b
   FCL_REAL projectLineOrigin(const Simplex& current, Simplex& next);
 
+  /// @brief Project origin (0) onto triangle a-b-c
+  FCL_REAL projectTriangleOrigin(const Simplex& current, Simplex& next);
+
 };
 
 
diff --git a/src/narrowphase/gjk.cpp b/src/narrowphase/gjk.cpp
index a0d321a3..d1964e35 100644
--- a/src/narrowphase/gjk.cpp
+++ b/src/narrowphase/gjk.cpp
@@ -378,6 +378,8 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess)
       break;
     }
 
+    // This has been rewritten thanks to the excellent video:
+    // https://youtu.be/Qupqu1xe7Io
     switch(curr_simplex.rank)
     {
     case 2:
@@ -395,10 +397,11 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess)
         {
           if(project_res.encode & (1 << i))
           {
-            assert (next_simplex.vertex[k++] == curr_simplex.vertex[i]);
+            assert (next_simplex.vertex[k] == curr_simplex.vertex[i]);
             assert (
                 fabs(next_simplex.coefficient[k]-project_res.parameterization[i]) < 1e-10);
             _ray += curr_simplex.vertex[i]->w * project_res.parameterization[i];
+            ++k;
           }
         }
         //assert (_ray.isApprox (ray));
@@ -407,9 +410,31 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess)
       }
       break;
     case 3:
-      project_res = Project::projectTriangleOrigin(curr_simplex.vertex[0]->w,
-                                                   curr_simplex.vertex[1]->w,
-                                                   curr_simplex.vertex[2]->w); break;
+      project_res.sqr_distance = projectTriangleOrigin (curr_simplex, next_simplex);
+      { // This only checks that, so far, the behaviour did not change.
+        FCL_REAL d = project_res.sqr_distance;
+        project_res = Project::projectTriangleOrigin(curr_simplex.vertex[0]->w,
+                                                     curr_simplex.vertex[1]->w,
+                                                     curr_simplex.vertex[2]->w);
+        assert (fabs(d-project_res.sqr_distance) < 1e-10);
+
+        Vec3f _ray(0,0,0);
+        size_t k = 0;
+        for(size_t i = 0; i < curr_simplex.rank; ++i)
+        {
+          if(project_res.encode & (1 << i))
+          {
+            assert (next_simplex.vertex[k] == curr_simplex.vertex[i]);
+            assert (
+                fabs(next_simplex.coefficient[k]-project_res.parameterization[i]) < 1e-10);
+            _ray += curr_simplex.vertex[i]->w * project_res.parameterization[i];
+            ++k;
+          }
+        }
+        assert ((_ray - ray).array().abs().maxCoeff() < 1e-10);
+        assert (k = next_simplex.rank);
+      }
+      break;
     case 4:
       project_res = Project::projectTetrahedraOrigin(curr_simplex.vertex[0]->w,
                                                      curr_simplex.vertex[1]->w,
@@ -420,7 +445,7 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess)
     if(project_res.sqr_distance >= 0)
     {
       current = next;
-      if (curr_simplex.rank != 2) {
+      if (curr_simplex.rank > 3) {
       next_simplex.rank = 0;
       ray = Vec3f(0,0,0);
       for(size_t i = 0; i < curr_simplex.rank; ++i)
@@ -587,6 +612,151 @@ FCL_REAL GJK::projectLineOrigin(const Simplex& current, Simplex& next)
   }
 }
 
+FCL_REAL GJK::projectTriangleOrigin(const Simplex& current, Simplex& next)
+{
+  const int a = 2, b = 1, c = 0;
+  // A is the last point we added.
+  const Vec3f& A = current.vertex[a]->w,
+               B = current.vertex[b]->w,
+               C = current.vertex[c]->w;
+
+  const Vec3f AB = B - A,
+              AC = C - A,
+              ABC = AB.cross(AC);
+
+  FCL_REAL edgeAC2o = ABC.cross(AC).dot (-A);
+  if (edgeAC2o >= 0) {
+    FCL_REAL towardsC = AC.dot (-A);
+    if (towardsC >= 0) { // Region 1
+      // ray = - ( AC ^ AO ) ^ AC = (AC.C) A + (-AC.A) C
+      ray = AC.dot(C) * A + towardsC * C;
+      next.vertex[0] = current.vertex[c];
+      next.vertex[1] = current.vertex[a];
+      next.rank = 2;
+      free_v[nfree++] = current.vertex[b];
+      // To ensure backward compatibility
+
+      FCL_REAL alpha = towardsC / AC.squaredNorm();
+      next.coefficient[1] = 1 - alpha; // A
+      next.coefficient[0] =     alpha; // C
+      ray /= AC.squaredNorm();
+      return ray.squaredNorm();
+    } else { // Region 4 or 5
+      FCL_REAL towardsB = AB.dot(-A);
+      if (towardsB < 0) { // Region 5
+        // A is the closest to the origin
+        ray = A;
+        next.vertex[0] = current.vertex[a];
+        next.rank = 1;
+        free_v[nfree++] = current.vertex[c];
+        free_v[nfree++] = current.vertex[b];
+
+        // To ensure backward compatibility
+        next.coefficient[0] = 1;
+        return A.squaredNorm();
+      } else { // Region 4
+        // ray = - ( AB ^ AO ) ^ AB = (AB.B) A + (-AB.A) B
+        ray = AB.dot(B) * A + towardsB * B;
+
+        next.vertex[0] = current.vertex[b];
+        next.vertex[1] = current.vertex[a];
+        next.rank = 2;
+        free_v[nfree++] = current.vertex[c];
+
+        // To ensure backward compatibility
+        FCL_REAL alpha = towardsB / AB.squaredNorm();
+        next.coefficient[1] = 1 - alpha; // A
+        next.coefficient[0] =     alpha; // B
+        ray /= AB.squaredNorm();
+
+        return ray.squaredNorm();
+      }
+    }
+  } else {
+    FCL_REAL edgeAB2o = AB.cross(ABC).dot (-A);
+    if (edgeAB2o >= 0) { // Region 4 or 5
+      FCL_REAL towardsB = AB.dot(-A);
+      if (towardsB < 0) { // Region 5
+        // A is the closest to the origin
+        ray = A;
+        next.vertex[0] = current.vertex[a];
+        next.rank = 1;
+        free_v[nfree++] = current.vertex[c];
+        free_v[nfree++] = current.vertex[b];
+
+        // To ensure backward compatibility
+        next.coefficient[0] = 1;
+        return A.squaredNorm();
+      } else { // Region 4
+        // ray = - ( AB ^ AO ) ^ AB = (AB.B) A + (-AB.A) B
+        ray = AB.dot(B) * A + towardsB * B;
+
+        next.vertex[0] = current.vertex[b];
+        next.vertex[1] = current.vertex[a];
+        next.rank = 2;
+        free_v[nfree++] = current.vertex[c];
+
+        // To ensure backward compatibility
+        FCL_REAL alpha = towardsB / AB.squaredNorm();
+        next.coefficient[1] = 1 - alpha; // A
+        next.coefficient[0] =     alpha; // B
+        ray /= AB.squaredNorm();
+
+        return ray.squaredNorm();
+      }
+    } else {
+      FCL_REAL aboveTri = ABC.dot(-A);
+      if (aboveTri) { // Region 2
+        ray = ABC;
+
+        next.vertex[0] = current.vertex[c];
+        next.vertex[1] = current.vertex[b];
+        next.vertex[2] = current.vertex[a];
+        next.rank = 3;
+
+        // To ensure backward compatibility
+        FCL_REAL s = ABC.squaredNorm();
+        ray *= A.dot(ABC) / s;
+        Vec3f AQ (ray - A);
+        Vec3f m (ABC.cross(AB));
+        FCL_REAL _u2 = 1/AB.squaredNorm();
+        FCL_REAL wu_u2 = AQ.dot (AB) * _u2;
+        FCL_REAL vu_u2 = AC.dot (AB) * _u2;
+        FCL_REAL wm_vm = AQ.dot(m) / AC.dot (m);
+
+        next.coefficient[0] = wm_vm; // C
+        next.coefficient[1] = wu_u2 - wm_vm*vu_u2; // B
+        next.coefficient[2] = 1 - next.coefficient[0] - next.coefficient[1]; // A
+        assert (0. <= next.coefficient[0] && next.coefficient[0] <= 1.);
+        assert (0. <= next.coefficient[1] && next.coefficient[1] <= 1.);
+        assert (0. <= next.coefficient[2] && next.coefficient[2] <= 1.);
+
+        return ray.squaredNorm();
+      } else { // Region 3
+        ray = ABC;
+
+        //next.vertex[0] = current.vertex[b];
+        next.vertex[0] = current.vertex[c];
+        next.vertex[1] = current.vertex[b];
+        next.vertex[2] = current.vertex[a];
+        next.rank = 3;
+
+        // To ensure backward compatibility
+        FCL_REAL s = ABC.squaredNorm();
+        ray *= A.dot(ABC) / s;
+        next.coefficient[2] = -1;
+        next.coefficient[1] = -1;
+        next.coefficient[0] = -1;
+        assert (0. <= next.coefficient[0] && next.coefficient[0] <= 1.);
+        assert (0. <= next.coefficient[1] && next.coefficient[1] <= 1.);
+        assert (0. <= next.coefficient[2] && next.coefficient[2] <= 1.);
+
+        return ray.squaredNorm();
+      }
+    }
+  }
+}
+
 void EPA::initialize()
 {
   sv_store = new SimplexV[max_vertex_num];
-- 
GitLab