From 030b72a8f3aad25e3755105d76276f706e7d6935 Mon Sep 17 00:00:00 2001
From: Tobias Leemann <tobias.leemann@fau.de>
Date: Fri, 24 Nov 2017 20:05:03 +0100
Subject: [PATCH] Implemented Iterative Collide Functor.

---
 src/pe/fcd/IterativeCollideFunctor.h | 218 +++++++++++++++++++++++++++
 tests/pe/CollisionTobiasGJK.cpp      | 112 +++++++++++---
 2 files changed, 305 insertions(+), 25 deletions(-)
 create mode 100644 src/pe/fcd/IterativeCollideFunctor.h

diff --git a/src/pe/fcd/IterativeCollideFunctor.h b/src/pe/fcd/IterativeCollideFunctor.h
new file mode 100644
index 000000000..afdccd914
--- /dev/null
+++ b/src/pe/fcd/IterativeCollideFunctor.h
@@ -0,0 +1,218 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file IterativeCollideFunctor.h
+//! \author Tobias Leemann
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+
+#include "pe/Types.h"
+#include "pe/collision/EPA.h"
+#include "pe/collision/GJK.h"
+#include "pe/rigidbody/Plane.h"
+#include "pe/rigidbody/Union.h"
+#include <pe/Thresholds.h>
+#include <boost/tuple/tuple.hpp>
+
+namespace walberla{
+namespace pe{
+namespace fcd {
+namespace iterative{
+
+   //function for all single rigid bodies.
+   template<typename Container>
+   inline bool generateContacts(GeomPrimitive *a, GeomPrimitive *b, Container& contacts_);
+
+   //Planes
+   template<typename Container>
+   inline bool generateContacts(Plane *a, GeomPrimitive *b, Container& contacts_);
+
+   template<typename Container>
+   inline bool generateContacts(GeomPrimitive *a, Plane *b, Container& contacts_);
+
+   template< typename Container>
+   inline bool generateContacts(Plane *a, Plane *b, Container& contacts_);
+
+   //Unions
+   template<typename BodyTupleA, typename BodyB, typename Container>
+   inline bool generateContacts(Union<BodyTupleA> *a, BodyB *b, Container& contacts_);
+
+   template<typename BodyA, typename BodyTupleB, typename Container>
+   inline bool generateContacts(BodyA *a, Union<BodyTupleB> *b, Container& contacts_);
+
+   template<typename BodyTupleA, typename BodyTupleB, typename Container>
+   inline bool generateContacts(Union<BodyTupleA> *a, Union<BodyTupleB>  *b, Container& contacts_);
+
+   //Union and Plane
+   template<typename BodyTupleA, typename Container>
+   inline bool generateContacts(Union<BodyTupleA> *a, Plane *b, Container& contacts_);
+
+   template<typename BodyTupleB, typename Container>
+   inline bool generateContacts(Plane *a, Union<BodyTupleB> *b, Container& contacts_);
+}
+
+/* Iterative Collide Functor for contact Generation with iterative collision detection.
+ * Usage: fcd::GenericFCD<BodyTuple, fcd::IterativeCollideFunctor> testFCD;
+ * testFCD.generateContacts(...);
+ */
+template <typename Container>
+struct IterativeCollideFunctor
+{
+   Container& contacts_;
+
+   IterativeCollideFunctor(Container& contacts) : contacts_(contacts) {}
+
+   template< typename BodyType1, typename BodyType2 >
+   bool operator()( BodyType1* bd1, BodyType2* bd2) {
+      using namespace iterative;
+      return generateContacts(bd1, bd2, contacts_);
+   }
+};
+
+template <typename BodyType1, typename Container>
+struct IterativeSingleCollideFunctor
+{
+   BodyType1* bd1_;
+   Container& contacts_;
+
+   IterativeSingleCollideFunctor(BodyType1* bd1, Container& contacts) : bd1_(bd1), contacts_(contacts) {}
+
+   template< typename BodyType2 >
+   bool operator()( BodyType2* bd2) {
+      using namespace iterative;
+      return generateContacts( bd1_, bd2, contacts_);
+   }
+};
+
+
+namespace iterative{
+
+   //function for all single rigid bodies.
+   template<typename Container>
+   inline bool generateContacts(GeomPrimitive *a, GeomPrimitive *b, Container& contacts_){
+      Vec3 normal;
+      Vec3 contactPoint;
+      real_t penetrationDepth;
+
+      real_t margin = real_t(1e-4);
+      GJK gjk;
+      if(gjk.doGJKmargin(*a, *b, margin)){
+         //2. If collision is possible perform EPA.
+         EPA epa;
+         if(epa.doEPAmargin(*a, *b, gjk, normal, contactPoint, penetrationDepth, margin)){
+            contacts_.push_back( Contact(a, b, contactPoint, normal, penetrationDepth) );
+            return true;
+         }else{
+            return false;
+         }
+
+      }else{
+         return false;
+      }
+   }
+
+   //Planes
+   template<typename Container>
+   inline bool generateContacts(Plane *a, GeomPrimitive *b, Container& contacts_){
+      Vec3 normal;
+      Vec3 contactPoint;
+      real_t penetrationDepth;
+
+      Vec3 support_dir = -a->getNormal();
+      // We now have a direction facing to the "wall".
+      // Compute support point of body b in this direction. This will be the furthest point overlapping.
+      Vec3 contactp = b->support(support_dir);
+      real_t pdepth = contactp * a->getNormal() - a->getDisplacement();
+      if(pdepth < contactThreshold){ //We have a collision
+         normal = support_dir;
+         penetrationDepth = pdepth;
+         contactPoint = contactp + real_t(0.5) * penetrationDepth * normal;
+         contacts_.push_back( Contact(a, b, contactPoint, normal, penetrationDepth) );
+         return true;
+      }else{ //No collision
+         return false;
+      }
+   }
+
+   template<typename Container>
+   inline bool generateContacts(GeomPrimitive *a, Plane *b, Container& contacts_){
+      return generateContacts(b, a, contacts_);
+   }
+
+   //Planes cannot collide with each other
+   template< typename Container>
+   inline bool generateContacts(Plane*, Plane*, Container&){
+      return false;
+   }
+
+   //Unions
+   template<typename BodyTupleA, typename BodyB, typename Container>
+   inline bool generateContacts(Union<BodyTupleA> *a, BodyB *b, Container& contacts_){
+      IterativeSingleCollideFunctor<BodyB, Container> func(b, contacts_);
+      bool collision = false;
+      for( auto it=a->begin(); it!=a->end(); ++it )
+      {
+         collision |= SingleCast<BodyTupleA, IterativeSingleCollideFunctor<BodyB, Container>, bool>::execute(*it, func);
+      }
+      return collision;
+   }
+
+   template<typename BodyA, typename BodyTupleB, typename Container>
+   inline bool generateContacts(BodyA *a, Union<BodyTupleB> *b, Container& contacts_){
+      return generateContacts(b, a, contacts_);
+   }
+
+   template<typename BodyTupleA, typename BodyTupleB, typename Container>
+   inline bool generateContacts(Union<BodyTupleA> *a, Union<BodyTupleB>  *b, Container& contacts_){
+      IterativeCollideFunctor<Container> func(contacts_);
+      bool collision = false;
+      for( auto it1=a->begin(); it1!=a->end(); ++it1 )
+      {
+         for( auto it2=b->begin(); it2!=b->end(); ++it2 )
+         {
+            collision |= DoubleCast<BodyTupleA, BodyTupleB, IterativeCollideFunctor<Container>, bool>::execute(*it1, *it2, func);
+         }
+      }
+      return collision;
+   }
+
+   //Union and Plane (these calls are ambigous if not implemented seperatly)
+   template<typename BodyTupleA, typename Container>
+   inline bool generateContacts(Union<BodyTupleA> *a, Plane *b, Container& contacts_){
+      IterativeSingleCollideFunctor<Plane, Container> func(b, contacts_);
+      bool collision = false;
+      for( auto it=a->begin(); it!=a->end(); ++it )
+      {
+         collision |= SingleCast<BodyTupleA, IterativeSingleCollideFunctor<Plane, Container>, bool>::execute(*it, func);
+      }
+      return collision;
+   }
+
+   template<typename BodyTupleB, typename Container>
+   inline bool generateContacts(Plane *a, Union<BodyTupleB> *b, Container& contacts_){
+      return generateContacts(b, a, contacts_);
+   }
+
+
+} //namespace iterative
+
+
+} //fcd
+} //pe
+} //walberla
diff --git a/tests/pe/CollisionTobiasGJK.cpp b/tests/pe/CollisionTobiasGJK.cpp
index 3ce9c22fc..af02c7f7a 100644
--- a/tests/pe/CollisionTobiasGJK.cpp
+++ b/tests/pe/CollisionTobiasGJK.cpp
@@ -22,8 +22,10 @@
 
 #include "pe/contact/Contact.h"
 #include "pe/fcd/IFCD.h"
+#include "pe/fcd/GenericFCD.h"
 #include "pe/fcd/IterativeFCD.h"
 #include "pe/fcd/AnalyticCollisionDetection.h"
+#include "pe/fcd/IterativeCollideFunctor.h"
 #include "pe/Materials.h"
 
 #include "pe/rigidbody/Box.h"
@@ -47,7 +49,7 @@
 using namespace walberla;
 using namespace walberla::pe;
 
-typedef boost::tuple<Box, Capsule, Plane, Sphere, Union<boost::tuple<Sphere>>, Ellipsoid> BodyTuple ;
+typedef boost::tuple<Box, Capsule, Plane, Sphere, Union<boost::tuple<Sphere, Union<boost::tuple<Sphere>>>>, Ellipsoid> BodyTuple ;
 
 bool gjkEPAcollideHybrid(GeomPrimitive &geom1, GeomPrimitive &geom2, Vec3& normal, Vec3& contactPoint, real_t& penetrationDepth)
 {
@@ -286,24 +288,46 @@ void PlaneTest()
 {
    WALBERLA_LOG_INFO("PLANE AND INTERFACE TEST");
    MaterialID iron = Material::find("iron");
-   fcd::IterativeFCD<BodyTuple> testFCD;
+   fcd::GenericFCD<BodyTuple, fcd::IterativeCollideFunctor> testFCD;
 
    Plane pl(1, 1, Vec3(0, 1, 0), Vec3(0, 1, 0), real_t(1.0), iron );
    Sphere sphere(2, 2, Vec3(0, real_t(1.9), 0), Vec3(0,0,0), Quat(), 1, iron, false, true, false);
+   Sphere sphere2(3, 3, Vec3(0, real_t(0.1), 0), Vec3(0,0,0), Quat(), 1, iron, false, true, false);
 
    PossibleContacts pcs;
-   pcs.push_back(std::pair<Plane*, Sphere*>(&pl, &sphere));
 
+   pcs.push_back(std::pair<Sphere*, Sphere*>(&sphere, &sphere2));
    Contacts& container = testFCD.generateContacts(pcs);
-
    WALBERLA_CHECK(container.size() == 1);
 
    Contact &c = container.back();
-
+   //
    WALBERLA_LOG_DEVEL( c.getDistance() << " " << c.getNormal() << " " << c.getPosition() );
-   checkContact( c, Contact(&pl, &sphere,  Vec3(0, real_t(0.95), 0), Vec3(0, -1, 0), real_t(-0.1)), Vec3(0,0,0));
+   if(c.getBody1()->getID() == 2) {
+      checkContact( c, Contact(&sphere, &sphere2,  Vec3(0, real_t(1), 0), Vec3(0, 1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else if (c.getBody1()->getID() == 3) {
+      checkContact( c, Contact(&sphere2, &sphere, Vec3(0, real_t(1), 0), Vec3(0, -1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else {
+      WALBERLA_ABORT("Unknown ID!");
+   }
+   pcs.clear();
+
+   pcs.push_back(std::pair<Plane*, Sphere*>(&pl, &sphere));
+   container = testFCD.generateContacts(pcs);
+   WALBERLA_CHECK(container.size() == 1);
 
+   c = container.back();
+   //
+   WALBERLA_LOG_DEVEL( c.getDistance() << " " << c.getNormal() << " " << c.getPosition() );
+   if(c.getBody1()->getID() == 1) {
+      checkContact( c, Contact(&pl, &sphere,  Vec3(0, real_t(0.95), 0), Vec3(0, -1, 0), real_t(-0.1)), Vec3(0,0,0));
+   } else if (c.getBody1()->getID() == 2) {
+      checkContact( c, Contact(&sphere, &pl, Vec3(0, real_t(0.95), 0), Vec3(0, 1, 0), real_t(-0.1)), Vec3(0,0,0));
+   } else {
+      WALBERLA_ABORT("Unknown ID!");
+   }
    pcs.clear();
+
    pcs.push_back(std::pair<Sphere*, Plane*>(&sphere, &pl));
 
    container = testFCD.generateContacts(pcs);
@@ -311,7 +335,13 @@ void PlaneTest()
    c = container.back();
 
    WALBERLA_LOG_DEVEL( c.getDistance() << " " << c.getNormal() << " " << c.getPosition() );
-   checkContact( c, Contact(&sphere, &pl, Vec3(0, real_t(0.95), 0), Vec3(0, 1, 0), real_t(-0.1)), Vec3(0,0,0));
+   if(c.getBody1()->getID() == 1) {
+      checkContact( c, Contact(&pl, &sphere,  Vec3(0, real_t(0.95), 0), Vec3(0, -1, 0), real_t(-0.1)), Vec3(0,0,0));
+   } else if (c.getBody1()->getID() == 2) {
+      checkContact( c, Contact(&sphere, &pl, Vec3(0, real_t(0.95), 0), Vec3(0, 1, 0), real_t(-0.1)), Vec3(0,0,0));
+   } else {
+      WALBERLA_ABORT("Unknown ID!");
+   }
 }
 
 /** Test the GJK-EPA implementation for a collision 
@@ -319,48 +349,80 @@ void PlaneTest()
 void UnionTest(){
    WALBERLA_LOG_INFO("UNION AND INTERFACE TEST");
    MaterialID iron = Material::find("iron");
-   fcd::IterativeFCD<BodyTuple> testFCD;
+   fcd::GenericFCD<BodyTuple, fcd::IterativeCollideFunctor> testFCD;
 
-   //A union of two spheres is dropped on a box.
+   //A recursive union of three spheres is dropped on a box.
    Box box(179, 179, Vec3(0,0,0), Vec3(0,0,0), Quat(), Vec3(real_t(10),real_t(2), real_t(10)), iron, false, true, false);
 
 
-   Union<boost::tuple<Sphere>> *un= new Union<boost::tuple<Sphere>>(182, 182, Vec3(0,real_t(3.8),0), Vec3(0,0,0), Quat(), false, true, false);
+   Union<boost::tuple<Sphere>> *unsub = new Union<boost::tuple<Sphere>>(192, 192, Vec3(0,real_t(3.8),0), Vec3(0,0,0), Quat(), false, true, false);
 
    Sphere sp1( 180, 180, Vec3(-3,real_t(3.8),0), Vec3(0,0,0), Quat(), real_t(3.0)  , iron, false, true, false );
    Sphere sp2( 181, 181, Vec3(3,real_t(3.8),0), Vec3(0,0,0), Quat(), real_t(3.0), iron, false, true, false );
 
-   un->add(&sp1);
-   un->add(&sp2);
+   Sphere sp3( 182, 182, Vec3(0,real_t(6),0), Vec3(0,0,0), Quat(), real_t(3.0), iron, false, true, false );
+   unsub->add(&sp1);
+   unsub->add(&sp2);
+
+   //Create another union, and add sub union
+   Union<boost::tuple<Sphere, Union<boost::tuple<Sphere>>>> *un = new Union<boost::tuple<Sphere, Union<boost::tuple<Sphere>>>>(193, 193, Vec3(0, 0, 0), Vec3(0,0,0), Quat(), false, true, false);
+   un->add(&sp3);
+   un->add(unsub);
+
 
    PossibleContacts pcs;
-   pcs.push_back(std::pair<Union<boost::tuple<Sphere>>*, Box*>(un, &box));
+   pcs.push_back(std::pair<Union<boost::tuple<Sphere,Union<boost::tuple<Sphere>>>>*, Box*>(un, &box));
    Contacts& container = testFCD.generateContacts(pcs);
-
    WALBERLA_CHECK(container.size() == 2);
 
    Contact &c = container.back();
+   WALBERLA_LOG_DEVEL( c.getDistance() << " " << c.getNormal() << " " << c.getPosition() );
+   if(c.getBody1()->getID() == 181) {
+      checkContact( c, Contact(&sp2, &box,  Vec3(real_t(3), real_t(0.9), 0), Vec3(0, 1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else if (c.getBody1()->getID() == 179) {
+      checkContact( c, Contact(&box, &sp2,  Vec3(real_t(3), real_t(0.9), 0), Vec3(0, -1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else {
+      WALBERLA_ABORT("Unknown ID!");
+   }
+   container.pop_back();
 
-   checkContact( c, Contact(&sp2, &box,  Vec3(real_t(3), real_t(0.9), 0), Vec3(0, 1, 0), real_t(-0.2)), Vec3(0,0,0));
 
-   container.pop_back();
-   Contact &c2 = container.back();
-   checkContact( c2, Contact(&sp1, &box,  Vec3(real_t(-3), real_t(0.9), 0), Vec3(0, 1, 0), real_t(-0.2)), Vec3(0,0,0));
+   c = container.back();
+   WALBERLA_LOG_DEVEL( c.getDistance() << " " << c.getNormal() << " " << c.getPosition() );
+   if(c.getBody1()->getID() == 180) {
+      checkContact( c, Contact(&sp1, &box,  Vec3(real_t(-3), real_t(0.9), 0), Vec3(0, 1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else if (c.getBody1()->getID() == 179) {
+      checkContact( c, Contact(&box, &sp1,  Vec3(real_t(-3), real_t(0.9), 0), Vec3(0, -1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else {
+      WALBERLA_ABORT("Unknown ID!");
+   }
    pcs.clear();
 
    //Vice Versa
-   pcs.push_back(std::pair<Box*, Union<boost::tuple<Sphere>>* >(&box, un));
+   pcs.push_back(std::pair<Box*, Union<boost::tuple<Sphere, Union<boost::tuple<Sphere>>>>* >(&box, un));
    container = testFCD.generateContacts(pcs);
-
    WALBERLA_CHECK(container.size() == 2);
-   c = container.back();
-
-   checkContact( c, Contact(&box, &sp2,  Vec3(real_t(3), real_t(0.9), 0), Vec3(0, -1, 0), real_t(-0.2)), Vec3(0,0,0));
 
+   c = container.back();
+   WALBERLA_LOG_DEVEL( c.getDistance() << " " << c.getNormal() << " " << c.getPosition() );
+   if(c.getBody1()->getID() == 181) {
+      checkContact( c, Contact(&sp2, &box,  Vec3(real_t(3), real_t(0.9), 0), Vec3(0, 1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else if (c.getBody1()->getID() == 179) {
+      checkContact( c, Contact(&box, &sp2,  Vec3(real_t(3), real_t(0.9), 0), Vec3(0, -1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else {
+      WALBERLA_ABORT("Unknown ID!");
+   }
    container.pop_back();
-   c2 = container.back();
 
-   checkContact( c2, Contact(&box, &sp1, Vec3(real_t(-3), real_t(0.9), 0), Vec3(0, -1, 0), real_t(-0.2)), Vec3(0,0,0));
+   c = container.back();
+   WALBERLA_LOG_DEVEL( c.getDistance() << " " << c.getNormal() << " " << c.getPosition() );
+   if(c.getBody1()->getID() == 180) {
+      checkContact( c, Contact(&sp1, &box,  Vec3(real_t(-3), real_t(0.9), 0), Vec3(0, 1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else if (c.getBody1()->getID() == 179) {
+      checkContact( c, Contact(&box, &sp1,  Vec3(real_t(-3), real_t(0.9), 0), Vec3(0, -1, 0), real_t(-0.2)), Vec3(0,0,0));
+   } else {
+      WALBERLA_ABORT("Unknown ID!");
+   }
    pcs.clear();
 
 }
-- 
GitLab