From 18e9b2751eebf918bc2f689106793bce0294d0a7 Mon Sep 17 00:00:00 2001
From: "Dennis.Glaeser" <dennis.glaeser@iws.uni-stuttgart.de>
Date: Mon, 1 Apr 2019 09:56:46 +0200
Subject: [PATCH] [geometryisection] introduce policy classes for intersections

---
 dumux/common/geometry/geometryintersection.hh | 688 ++++++++++++------
 dumux/common/geometry/intersectingentities.hh |  25 +-
 .../facet/box/fluxvariablescache.hh           |  19 +-
 .../common/geometry/test_2d3d_intersection.cc |  37 +-
 4 files changed, 534 insertions(+), 235 deletions(-)

diff --git a/dumux/common/geometry/geometryintersection.hh b/dumux/common/geometry/geometryintersection.hh
index 4235e2037b..ab77faece4 100644
--- a/dumux/common/geometry/geometryintersection.hh
+++ b/dumux/common/geometry/geometryintersection.hh
@@ -23,6 +23,7 @@
 #ifndef DUMUX_GEOMETRY_INTERSECTION_HH
 #define DUMUX_GEOMETRY_INTERSECTION_HH
 
+#include <dune/common/deprecated.hh>
 #include <dune/common/exceptions.hh>
 #include <dune/common/promotiontraits.hh>
 #include <dune/geometry/referenceelements.hh>
@@ -33,6 +34,65 @@
 #include <dumux/common/geometry/grahamconvexhull.hh>
 
 namespace Dumux {
+namespace IntersectionPolicy {
+
+//! Policy structure for point-like intersections
+template<class ct, int dw>
+struct PointPolicy
+{
+    using ctype = ct;
+    using Point = Dune::FieldVector<ctype, dw>;
+
+    static constexpr int dimWorld = dw;
+    static constexpr int dimIntersection = 0;
+    using Intersection = Point;
+};
+
+//! Policy structure for segment-like intersections
+template<class ct, int dw>
+struct SegmentPolicy
+{
+    using ctype = ct;
+    using Point = Dune::FieldVector<ctype, dw>;
+    using Segment = std::array<Point, 2>;
+
+    static constexpr int dimWorld = dw;
+    static constexpr int dimIntersection = 1;
+    using Intersection = Segment;
+};
+
+//! Policy structure for polygon-like intersections
+template<class ct, int dw>
+struct PolygonPolicy
+{
+    using ctype = ct;
+    using Point = Dune::FieldVector<ctype, dw>;
+    using Polygon = std::vector<Point>;
+
+    static constexpr int dimWorld = dw;
+    static constexpr int dimIntersection = 2;
+    using Intersection = Polygon;
+};
+
+//! default policy chooser class
+template<class Geometry1, class Geometry2, int dim2 = Geometry2::mydimension>
+class DefaultPolicyChooser
+{
+    static constexpr int dimworld = Geometry1::coorddimension;
+    static_assert(dimworld == int(Geometry2::coorddimension), "Geometries must have the same coordinate dimension!");
+    static_assert(dim2 == 1 || dim2 == 2, "No chooser class specialization available for given dimensions");
+    using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType;
+public:
+    using type = std::conditional_t< dim2 == 2,
+                                     PolygonPolicy<ctype, Geometry1::coorddimension>,
+                                     SegmentPolicy<ctype, Geometry1::coorddimension> >;
+};
+
+//! Helper alias to define the default intersection policy
+template<class Geometry1, class Geometry2>
+using DefaultPolicy = typename DefaultPolicyChooser<Geometry1, Geometry2>::type;
+
+} // end namespace IntersectionPolicy
 
 /*!
  * \ingroup Geometry
@@ -42,16 +102,26 @@ namespace Dumux {
  */
 template
 <class Geometry1, class Geometry2,
-  int dimworld = Geometry1::coorddimension,
-  int dim1 = Geometry1::mydimension,
-  int dim2 = Geometry2::mydimension>
+ class Policy = IntersectionPolicy::DefaultPolicy<Geometry1, Geometry2>,
+ int dimworld = Geometry1::coorddimension,
+ int dim1 = Geometry1::mydimension,
+ int dim2 = Geometry2::mydimension>
 class GeometryIntersection
 {
+    static constexpr int dimWorld = Policy::dimWorld;
+
 public:
-    //! Determine if the two geometries intersect and compute the intersection corners
-    template<class IntersectionType>
-    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, IntersectionType& intersection)
+    using ctype = typename Policy::ctype;
+    using Point = typename Policy::Point;
+    using Intersection = typename Policy::Intersection;
+
+    //! Deprecated alias, will be removed after 3.1
+    using IntersectionType DUNE_DEPRECATED_MSG("Please use Intersection instead") = Intersection;
+
+    //! Determine if the two geometries intersect and compute the intersection geometry
+    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection)
     {
+        static_assert(int(dim1) >= int(dim2), "Geometries must be ordered such that dim1 >= dim2");
         static_assert(dimworld == Geometry2::coorddimension, "Can only intersect geometries of same coordinate dimension");
         DUNE_THROW(Dune::NotImplemented, "Geometry intersection detection with intersection computation not implemented for dimworld = "
                                           << dimworld << ", dim1 = " << dim1 << ", dim2 = " << dim2);
@@ -62,17 +132,20 @@ public:
  * \ingroup Geometry
  * \brief A class for polygon--segment intersection in 2d space
  */
-template <class Geometry1, class Geometry2>
-class GeometryIntersection<Geometry1, Geometry2, 2, 2, 1>
+template <class Geometry1, class Geometry2, class Policy>
+class GeometryIntersection<Geometry1, Geometry2, Policy, 2, 2, 1>
 {
     enum { dimworld = 2 };
     enum { dim1 = 2 };
     enum { dim2 = 1 };
 
 public:
-    using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType;
-    using Point = Dune::FieldVector<ctype, dimworld>;
-    using IntersectionType = std::array<std::vector<Point>, 1>;
+    using ctype = typename Policy::ctype;
+    using Point = typename Policy::Point;
+    using Intersection = typename Policy::Intersection;
+
+    //! Deprecated alias, will be removed after 3.1
+    using IntersectionType DUNE_DEPRECATED_MSG("Please use Intersection instead") = Intersection;
 
 private:
     static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons
@@ -84,110 +157,161 @@ public:
      * \param geo1/geo2 The geometries to intersect
      * \param intersection If the geometries collide intersection holds the
      *        corner points of the intersection object in global coordinates.
+     * \note This overload is used when segment-like intersections are seeked.
      */
-    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, IntersectionType& intersection)
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0>
+    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection)
     {
-        const auto a = geo2.corner(0);
-        const auto b = geo2.corner(1);
-        const auto d = b - a;
+        static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension");
 
-        const std::vector<std::array<int, 2>> edges = [&]()
+        ctype tfirst, tlast;
+        if (intersect_(geo1, geo2, tfirst, tlast))
         {
-            std::vector<std::array<int, 2>> edges;
-            switch (geo1.corners())
-            {
-                case 4: // quadrilateral
-                    edges = {{0, 2}, {3, 1}, {1, 0}, {2, 3}};
-                    break;
-                case 3: // triangle
-                    edges = {{1, 0}, {0, 2}, {2, 1}};
-                    break;
-                default:
-                    DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type "
-                                   << geo1.type() << " with "<< geo1.corners() << " corners.");
-            }
+            // the intersection exists. Export the intersections geometry now:
+            // s(t) = a + t(b-a) in [tfirst, tlast]
+            intersection = Intersection({geo2.global(tfirst), geo2.global(tlast)});
+            return true;
+        }
 
-            return edges;
-        } ();
+        return false;
+    }
 
-        // The initial interval is the whole segment
-        // afterward we start clipping the interval
-        // by the lines decribed by the edges
-        ctype tfirst = 0.0;
-        ctype tlast = 1.0;
+    /*!
+     * \brief Colliding segment and convex polygon
+     * \param geo1/geo2 The geometries to intersect
+     * \param intersection If the geometries collide intersection holds the
+     *        corner points of the intersection object in global coordinates.
+     * \note This overload is used when point-like intersections are seeked.
+     */
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0>
+    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, typename P::Intersection& intersection)
+    {
+        static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension");
 
-        const auto center1 = geo1.center();
-        for (const auto& e : edges)
+        ctype tfirst, tlast;
+        if (!intersect_(geo1, geo2, tfirst, tlast))
         {
-            // compute outer normal vector of the edge
-            const auto c0 = geo1.corner(e[0]);
-            const auto c1 = geo1.corner(e[1]);
-            const auto edge = c1 - c0;
-            const auto eps = eps_*c0.two_norm();
-
-            Dune::FieldVector<ctype, dimworld> n({-1.0*edge[1], edge[0]});
-            n /= n.two_norm();
-
-            // orientation of the element is not known
-            // make sure normal points outwards of element
-            if ( n*(center1-c0) > 0.0 )
-                n *= -1.0;
-
-            const ctype denom = n*d;
-            const ctype dist = n*(a-c0);
-
-            // if denominator is zero the segment in parallel to the edge.
-            // If the distance is positive there is no intersection
+            // if start & end point are same, the intersection is a point
             using std::abs;
-            if (abs(denom) < eps)
-            {
-                if (dist > eps)
-                    return false;
-            }
-            else // not parallel: compute line-line intersection
+            if ( tfirst > tlast - eps_ )
             {
-                const ctype t = -dist / denom;
-                // if entering half space cut tfirst if t is larger
-                using std::signbit;
-                if (signbit(denom))
-                {
-                    if (t > tfirst)
-                        tfirst = t;
-                }
-                // if exiting half space cut tlast if t is smaller
-                else
-                {
-                    if (t < tlast)
-                        tlast = t;
-                }
-                // there is no intersection of the interval is empty
-                // use unscaled epsilon since t is in local coordinates
-                if (tfirst > tlast - eps_)
-                    return false;
+                intersection = Intersection(geo2.global(tfirst));
+                return true;
             }
         }
-        // If we made it until here an intersection exists. We also export
-        // the intersections geometry now s(t) = a + t(b-a) in [tfirst, tlast]
-        intersection = {std::vector<Point>({geo2.global(tfirst), geo2.global(tlast)})};
-        return true;
+
+        return false;
     }
+
+private:
+    /*!
+     * \brief Obtain local coordinates of start/end point of the intersecting segment.
+     */
+     static bool intersect_(const Geometry1& geo1, const Geometry2& geo2, ctype& tfirst, ctype& tlast)
+     {
+         const auto a = geo2.corner(0);
+         const auto b = geo2.corner(1);
+         const auto d = b - a;
+
+         const std::vector<std::array<int, 2>> edges = [&]()
+         {
+             std::vector<std::array<int, 2>> edges;
+             switch (geo1.corners())
+             {
+                 case 4: // quadrilateral
+                     edges = {{0, 2}, {3, 1}, {1, 0}, {2, 3}};
+                     break;
+                 case 3: // triangle
+                     edges = {{1, 0}, {0, 2}, {2, 1}};
+                     break;
+                 default:
+                     DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type "
+                                    << geo1.type() << " with "<< geo1.corners() << " corners.");
+             }
+
+             return edges;
+         } ();
+
+         // The initial interval is the whole segment
+         // afterward we start clipping the interval
+         // by the lines decribed by the edges
+         tfirst = 0.0;
+         tlast = 1.0;
+
+         const auto center1 = geo1.center();
+         for (const auto& e : edges)
+         {
+             // compute outer normal vector of the edge
+             const auto c0 = geo1.corner(e[0]);
+             const auto c1 = geo1.corner(e[1]);
+             const auto edge = c1 - c0;
+             const auto eps = eps_*c0.two_norm();
+
+             Dune::FieldVector<ctype, dimworld> n({-1.0*edge[1], edge[0]});
+             n /= n.two_norm();
+
+             // orientation of the element is not known
+             // make sure normal points outwards of element
+             if ( n*(center1-c0) > 0.0 )
+                 n *= -1.0;
+
+             const ctype denom = n*d;
+             const ctype dist = n*(a-c0);
+
+             // if denominator is zero the segment in parallel to the edge.
+             // If the distance is positive there is no intersection
+             using std::abs;
+             if (abs(denom) < eps)
+             {
+                 if (dist > eps)
+                     return false;
+             }
+             else // not parallel: compute line-line intersection
+             {
+                 const ctype t = -dist / denom;
+                 // if entering half space cut tfirst if t is larger
+                 using std::signbit;
+                 if (signbit(denom))
+                 {
+                     if (t > tfirst)
+                         tfirst = t;
+                 }
+                 // if exiting half space cut tlast if t is smaller
+                 else
+                 {
+                     if (t < tlast)
+                         tlast = t;
+                 }
+                 // there is no intersection of the interval is empty
+                 // use unscaled epsilon since t is in local coordinates
+                 if (tfirst > tlast - eps_)
+                     return false;
+             }
+         }
+
+         // If we made it until here an intersection exists.
+         return true;
+     }
 };
 
 /*!
  * \ingroup Geometry
  * \brief A class for polyhedron--segment intersection in 3d space
  */
-template <class Geometry1, class Geometry2>
-class GeometryIntersection<Geometry1, Geometry2, 3, 3, 1>
+template <class Geometry1, class Geometry2, class Policy>
+class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 3, 1>
 {
     enum { dimworld = 3 };
     enum { dim1 = 3 };
     enum { dim2 = 1 };
 
 public:
-    using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType;
-    using Point = Dune::FieldVector<ctype, dimworld>;
-    using IntersectionType = std::array<std::vector<Point>, 1>;
+    using ctype = typename Policy::ctype;
+    using Point = typename Policy::Point;
+    using Intersection = typename Policy::Intersection;
+
+    //! Deprecated alias, will be removed after 3.1
+    using IntersectionType DUNE_DEPRECATED_MSG("Please use Intersection instead") = Intersection;
 
 private:
     static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons
@@ -203,108 +327,161 @@ public:
      * \param geo1/geo2 The geometries to intersect
      * \param intersection If the geometries collide intersection holds the corner points of
      *        the intersection object in global coordinates.
+     * \note This overload is used when segment-like intersections are seeked.
      */
-    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, IntersectionType& intersection)
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 1, int> = 0>
+    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection)
     {
         static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension");
 
-        const auto a = geo2.corner(0);
-        const auto b = geo2.corner(1);
-        const auto d = b - a;
-
-        // The initial interval is the whole segment
-        // afterward we start clipping the interval
-        // by the planes decribed by the facet
-        ctype tfirst = 0.0;
-        ctype tlast = 1.0;
-
-        const std::vector<std::vector<int>> facets = [&]()
+        ctype tfirst, tlast;
+        if (intersect_(geo1, geo2, tfirst, tlast))
         {
-            std::vector<std::vector<int>> facets;
-            // sort facet corner so that normal n = (p1-p0)x(p2-p0) always points outwards
-            switch (geo1.corners())
-            {
-                // todo compute intersection geometries
-                case 8: // hexahedron
-                    facets = {{2, 0, 6, 4}, {1, 3, 5, 7}, {0, 1, 4, 5},
-                              {3, 2, 7, 6}, {1, 0, 3, 2}, {4, 5, 6, 7}};
-                    break;
-                case 4: // tetrahedron
-                    facets = {{1, 0, 2}, {0, 1, 3}, {0, 3, 2}, {1, 2, 3}};
-                    break;
-                default:
-                    DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type "
-                                   << geo1.type() << ", "<< geo1.corners() << " corners.");
-            }
-
-            return facets;
-        }();
-
-        for (const auto& f : facets)
-        {
-            // compute normal vector by cross product
-            const auto v0 = geo1.corner(f[1]) - geo1.corner(f[0]);
-            const auto v1 = geo1.corner(f[2]) - geo1.corner(f[0]);
-            const auto eps = eps_*v0.two_norm();
+            // Intersection exists. Export the intersections geometry now:
+            // s(t) = a + t(b-a) in [tfirst, tlast]
+            intersection = Intersection({geo2.global(tfirst), geo2.global(tlast)});
+            return true;
+        }
 
-            auto n = crossProduct(v0, v1);
-            n /= n.two_norm();
+        return false;
+    }
 
-            const ctype denom = n*d;
-            const ctype dist = n*(a-geo1.corner(f[0]));
+    /*!
+     *  \brief Colliding segment and convex polyhedron
+     *  \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson,
+     *        published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc.
+     *        Basis is the theorem that for any two non-intersecting convex polyhedrons
+     *        a separating plane exists.
+     * \param geo1/geo2 The geometries to intersect
+     * \param intersection If the geometries collide intersection holds the corner points of
+     *        the intersection object in global coordinates.
+     * \note This overload is used when point-like intersections are seeked.
+     */
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0>
+    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection)
+    {
+        static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension");
 
-            // if denominator is zero the segment in parallel to
-            // the plane. If the distance is positive there is no intersection
+        ctype tfirst, tlast;
+        if (intersect_(geo1, geo2, tfirst, tlast))
+        {
+            // if start & end point are same, the intersection is a point
             using std::abs;
-            if (abs(denom) < eps)
-            {
-                if (dist > eps)
-                    return false;
-            }
-            else // not parallel: compute line-plane intersection
+            if ( abs(tfirst - tlast) < eps_ )
             {
-                const ctype t = -dist / denom;
-                // if entering half space cut tfirst if t is larger
-                using std::signbit;
-                if (signbit(denom))
-                {
-                    if (t > tfirst)
-                        tfirst = t;
-                }
-                // if exiting half space cut tlast if t is smaller
-                else
-                {
-                    if (t < tlast)
-                        tlast = t;
-                }
-                // there is no intersection of the interval is empty
-                // use unscaled epsilon since t is in local coordinates
-                if (tfirst > tlast - eps_)
-                    return false;
+                intersection = Intersection(geo2.global(tfirst));
+                return true;
             }
         }
-        // If we made it until here an intersection exists. We also export
-        // the intersections geometry now s(t) = a + t(b-a) in [tfirst, tlast]
-        intersection = {std::vector<Point>({geo2.global(tfirst), geo2.global(tlast)})};
-        return true;
+
+        return false;
     }
+
+private:
+    /*!
+     * \brief Obtain local coordinates of start/end point of the intersecting segment.
+     */
+     static bool intersect_(const Geometry1& geo1, const Geometry2& geo2, ctype& tfirst, ctype& tlast)
+     {
+         const auto a = geo2.corner(0);
+         const auto b = geo2.corner(1);
+         const auto d = b - a;
+
+         // The initial interval is the whole segment
+         // afterward we start clipping the interval
+         // by the planes decribed by the facet
+         tfirst = 0.0;
+         tlast = 1.0;
+
+         const std::vector<std::vector<int>> facets = [&]()
+         {
+             std::vector<std::vector<int>> facets;
+             // sort facet corner so that normal n = (p1-p0)x(p2-p0) always points outwards
+             switch (geo1.corners())
+             {
+                 // todo compute intersection geometries
+                 case 8: // hexahedron
+                     facets = {{2, 0, 6, 4}, {1, 3, 5, 7}, {0, 1, 4, 5},
+                               {3, 2, 7, 6}, {1, 0, 3, 2}, {4, 5, 6, 7}};
+                     break;
+                 case 4: // tetrahedron
+                     facets = {{1, 0, 2}, {0, 1, 3}, {0, 3, 2}, {1, 2, 3}};
+                     break;
+                 default:
+                     DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type "
+                                    << geo1.type() << ", "<< geo1.corners() << " corners.");
+             }
+
+             return facets;
+         }();
+
+         for (const auto& f : facets)
+         {
+             // compute normal vector by cross product
+             const auto v0 = geo1.corner(f[1]) - geo1.corner(f[0]);
+             const auto v1 = geo1.corner(f[2]) - geo1.corner(f[0]);
+             const auto eps = eps_*v0.two_norm();
+
+             auto n = crossProduct(v0, v1);
+             n /= n.two_norm();
+
+             const ctype denom = n*d;
+             const ctype dist = n*(a-geo1.corner(f[0]));
+
+             // if denominator is zero the segment in parallel to
+             // the plane. If the distance is positive there is no intersection
+             using std::abs;
+             if (abs(denom) < eps)
+             {
+                 if (dist > eps)
+                     return false;
+             }
+             else // not parallel: compute line-plane intersection
+             {
+                 const ctype t = -dist / denom;
+                 // if entering half space cut tfirst if t is larger
+                 using std::signbit;
+                 if (signbit(denom))
+                 {
+                     if (t > tfirst)
+                         tfirst = t;
+                 }
+                 // if exiting half space cut tlast if t is smaller
+                 else
+                 {
+                     if (t < tlast)
+                         tlast = t;
+                 }
+                 // there is no intersection of the interval is empty
+                 // use unscaled epsilon since t is in local coordinates
+                 if (tfirst > tlast - eps_)
+                     return false;
+             }
+         }
+
+         // If we made it until here an intersection exists.
+         return true;
+     }
 };
 
 /*!
  * \ingroup Geometry
  * \brief A class for polyhedron--polygon intersection in 3d space
  */
-template <class Geometry1, class Geometry2>
-class GeometryIntersection<Geometry1, Geometry2, 3, 3, 2>
+template <class Geometry1, class Geometry2, class Policy>
+class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 3, 2>
 {
     enum { dimworld = 3 };
     enum { dim1 = 3 };
     enum { dim2 = 2 };
 
 public:
-    using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType;
-    using Point = Dune::FieldVector<ctype, dimworld>;
-    using IntersectionType = std::vector<std::array<Point, 3>>;
+    using ctype = typename Policy::ctype;
+    using Point = typename Policy::Point;
+    using Intersection = typename Policy::Intersection;
+
+    //! Deprecated alias, will be removed after 3.1
+    using IntersectionType DUNE_DEPRECATED_MSG("Please use Intersection instead") = Intersection;
 
 private:
     static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons
@@ -324,11 +501,12 @@ public:
      *       Return a triangulation of that polygon as intersection
      * \param geo1/geo2 The geometries to intersect
      * \param intersection A triangulation of the intersection polygon
+     * \note This overload is used when polygon like intersections are seeked
      */
-    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, IntersectionType& intersection)
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 2, int> = 0>
+    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection)
     {
-        static_assert(int(dimworld) == int(Geometry2::coorddimension),
-                      "Can only collide geometries of same coordinate dimension");
+        static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension");
 
         // the candidate intersection points
         std::vector<Point> points; points.reserve(10);
@@ -350,6 +528,9 @@ public:
         const auto referenceElement1 = ReferenceElementsGeo1::general(geo1.type());
         const auto referenceElement2 = ReferenceElementsGeo2::general(geo2.type());
 
+        // intersection policy for point-like intersections (used later)
+        using PointPolicy = IntersectionPolicy::PointPolicy<ctype, dimworld>;
+
         // add intersection points of all polyhedron edges (codim dim-1) with the polygon
         for (int i = 0; i < referenceElement1.size(dim1-1); ++i)
         {
@@ -358,10 +539,10 @@ public:
             const auto q = geo1.global(localEdgeGeom.corner(1));
             const auto segGeo = SegGeometry(Dune::GeometryTypes::line, std::vector<Point>{p, q});
 
-            using PolySegTest = GeometryIntersection<Geometry2, SegGeometry>;
-            typename PolySegTest::IntersectionType polySegIntersection;
-            if (PolySegTest::template intersection<2>(geo2, segGeo, polySegIntersection))
-                points.emplace_back(polySegIntersection[0]);
+            using PolySegTest = GeometryIntersection<Geometry2, SegGeometry, PointPolicy>;
+            typename PolySegTest::Intersection polySegIntersection;
+            if (PolySegTest::intersection(geo2, segGeo, polySegIntersection))
+                points.emplace_back(polySegIntersection);
         }
 
         // add intersection points of all polygon faces (codim 1) with the polyhedron faces
@@ -397,10 +578,10 @@ public:
 
                 const auto segGeo = SegGeometry(Dune::GeometryTypes::line, std::vector<Point>{p, q});
 
-                using PolySegTest = GeometryIntersection<PolyhedronFaceGeometry, SegGeometry>;
-                typename PolySegTest::IntersectionType polySegIntersection;
-                if (PolySegTest::template intersection<2>(faceGeo, segGeo, polySegIntersection))
-                    points.emplace_back(polySegIntersection[0]);
+                using PolySegTest = GeometryIntersection<PolyhedronFaceGeometry, SegGeometry, PointPolicy>;
+                typename PolySegTest::Intersection polySegIntersection;
+                if (PolySegTest::intersection(faceGeo, segGeo, polySegIntersection))
+                    points.emplace_back(polySegIntersection);
             }
         }
 
@@ -425,31 +606,53 @@ public:
         // return false if we don't have more than three unique points
         if (points.size() < 3) return false;
 
-        // compute convex hull
-        const auto convexHull = grahamConvexHull2d3d(points);
-        assert(!convexHull.empty());
+        // intersection polygon is convex hull of above points
+        intersection = grahamConvexHull2d3d(points);
+        assert(!intersection.empty());
 
-        // the intersections are the triangulation of the convex hull polygon
-        intersection = triangulateConvexHull(convexHull);
         return true;
     }
+
+    /*!
+     * \brief Colliding segment and convex polyhedron
+     * \note First we find the vertex candidates for the intersection region as follows:
+     *       Add triangle vertices that are inside the tetrahedron
+     *       Add tetrahedron vertices that are inside the triangle
+     *       Add all intersection points of tetrahedron edges (codim 2) with the triangle (codim 0) (6*1 tests)
+     *       Add all intersection points of triangle edges (codim 1) with tetrahedron faces (codim 1) (3*4 tests)
+     *       Remove duplicate points from the list
+     *       Compute the convex hull polygon
+     *       Return a triangulation of that polygon as intersection
+     * \param geo1/geo2 The geometries to intersect
+     * \param intersection A triangulation of the intersection polygon
+     * \todo implement overloads for segment or point-like intersections
+     */
+    template<class P = Policy, std::enable_if_t<P::dimIntersection != 2, int> = 0>
+    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& intersection)
+    {
+        static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension");
+        DUNE_THROW(Dune::NotImplemented, "Polyhedron-polygon intersection detection only implemented for polygon-like intersections");
+    }
 };
 
 /*!
  * \ingroup Geometry
  * \brief A class for polygon--segment intersection in 3d space
  */
-template <class Geometry1, class Geometry2>
-class GeometryIntersection<Geometry1, Geometry2, 3, 2, 1>
+template <class Geometry1, class Geometry2, class Policy>
+class GeometryIntersection<Geometry1, Geometry2, Policy, 3, 2, 1>
 {
     enum { dimworld = 3 };
     enum { dim1 = 2 };
     enum { dim2 = 1 };
 
 public:
-    using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType;
-    using Point = Dune::FieldVector<ctype, dimworld>;
-    using IntersectionType = std::vector<Point>;
+    using ctype = typename Policy::ctype;
+    using Point = typename Policy::Point;
+    using Intersection = typename Policy::Intersection;
+
+    //! Deprecated alias, will be removed after 3.1
+    using IntersectionType DUNE_DEPRECATED_MSG("Please use Intersection instead") = Intersection;
 
 private:
     static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons
@@ -463,13 +666,11 @@ public:
      * \param geo1/geo2 The geometries to intersect
      * \param is If the geometries collide, is holds the corner points of
      *        the intersection object in global coordinates.
+     * \note This overload is used when point-like intersections are seeked
      */
-    template<int dimIntersection>
-    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, IntersectionType& is)
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0>
+    static bool intersection(const Geometry1& geo1, const Geometry2& geo2, Intersection& is)
     {
-        if (dimIntersection != 2)
-            DUNE_THROW(Dune::NotImplemented, "Only simplex intersections are currently implemented!");
-
         static_assert(int(dimworld) == int(Geometry2::coorddimension), "Can only collide geometries of same coordinate dimension");
 
         const auto p = geo2.corner(0);
@@ -480,14 +681,24 @@ public:
         const auto c = geo1.corner(2);
 
         if (geo1.corners() == 3)
-            return intersection<dimIntersection>(a, b, c, p, q, is);
+            return intersect_<Policy>(a, b, c, p, q, is);
 
         else if (geo1.corners() == 4)
         {
+            Intersection is1, is2;
+            bool hasSegment1, hasSegment2;
+
             const auto d = geo1.corner(3);
-            if (intersection<dimIntersection>(a, b, d, p, q, is)) return true;
-            else if (intersection<dimIntersection>(a, d, c, p, q, is)) return true;
-            else return false;
+            const bool intersects1 = intersect_<Policy>(a, b, d, p, q, is1, hasSegment1);
+            const bool intersects2 = intersect_<Policy>(a, d, c, p, q, is2, hasSegment2);
+
+            if (hasSegment1 || hasSegment2)
+                return false;
+
+            if (intersects1) { is = is1; return true; }
+            if (intersects2) { is = is2; return true; }
+
+            return false;
         }
 
         else
@@ -495,14 +706,45 @@ public:
                           << geo1.type() << ", "<< geo1.corners() << " corners.");
     }
 
-    // triangle--segment intersection with points as input
-    template<int dimIntersection>
+    /*!
+     *  \brief Colliding segment and convex polyhedron
+     *  \note Algorithm based on the one from "Real-Time Collision Detection" by Christer Ericson,
+     *        published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. (Chapter 5.3.6)
+     * \param geo1/geo2 The geometries to intersect
+     * \param is If the geometries collide, is holds the corner points of
+     *        the intersection object in global coordinates.
+     * \note This overload is used when point-like intersections are seeked
+     */
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0>
+    DUNE_DEPRECATED_MSG("Please use intersection(triangle, segment, ...) instead")
     static bool intersection(const Point& a, const Point& b, const Point& c,
                              const Point& p, const Point& q,
-                             IntersectionType& is)
+                             Intersection& is)
     {
-        if (dimIntersection != 2)
-            DUNE_THROW(Dune::NotImplemented, "Only simplex intersections are currently implemented!");
+        return intersect_<Policy>(a, b, c, p, q, is);
+    }
+
+private:
+    // triangle--segment intersection with points as input
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0>
+    static bool intersect_(const Point& a, const Point& b, const Point& c,
+                           const Point& p, const Point& q,
+                           Intersection& is)
+    {
+        bool hasSegment;
+        return intersect_(a, b, c, p, q, is, hasSegment);
+    }
+
+    /*!
+     * \brief triangle--segment point-like intersection with points as input. Also
+     *        stores if a segment-like intersection was found in the provided boolean.
+     */
+    template<class P = Policy, std::enable_if_t<P::dimIntersection == 0, int> = 0>
+    static bool intersect_(const Point& a, const Point& b, const Point& c,
+                           const Point& p, const Point& q,
+                           Intersection& is, bool& hasSegmentIs)
+    {
+        hasSegmentIs = false;
 
         const auto ab = b - a;
         const auto ac = c - a;
@@ -514,10 +756,48 @@ public:
         // compute the denominator
         // if denom is 0 the segment is parallel and we can return
         const auto denom = normal*qp;
-        const auto eps = eps_*ab.two_norm2()*qp.two_norm();
+        const auto abnorm2 = ab.two_norm2();
+        const auto eps = eps_*abnorm2*qp.two_norm();
         using std::abs;
         if (abs(denom) < eps)
+        {
+            const auto pa = a - p;
+            const auto denom2 = normal*pa;
+            if (abs(denom2) > eps_*pa.two_norm()*abnorm2)
+                return false;
+
+            // if we get here, we are in-plane. Check if a
+            // segment-like intersection with geometry 1 exists.
+            using SegmentPolicy = typename IntersectionPolicy::SegmentPolicy<ctype, dimworld>;
+            using Triangle = Dune::AffineGeometry<ctype, 2, dimworld>;
+            using Segment = Dune::AffineGeometry<ctype, 1, dimworld>;
+            using SegmentIntersectionAlgorithm = GeometryIntersection<Triangle, Segment, SegmentPolicy>;
+            using SegmentIntersectionType = typename SegmentIntersectionAlgorithm::Intersection;
+            SegmentIntersectionType segmentIs;
+
+            Triangle triangle(Dune::GeometryTypes::simplex(2), std::array<Point, 3>({a, b, c}));
+            Segment segment(Dune::GeometryTypes::simplex(1), std::array<Point, 2>({p, q}));
+            if (SegmentIntersectionAlgorithm::intersection(triangle, segment, segmentIs))
+            {
+                hasSegmentIs = true;
+                return false;
+            }
+
+            // We are in-plane. A point-like
+            // intersection can only be on the edges
+            for (const auto& ip : {p, q})
+            {
+                if ( intersectsPointSimplex(ip, a, b)
+                     || intersectsPointSimplex(ip, b, c)
+                     || intersectsPointSimplex(ip, c, a) )
+                {
+                    is = ip;
+                    return true;
+                }
+            }
+
             return false;
+        }
 
         // compute intersection t value of pq with plane of triangle.
         // a segment intersects if and only if 0 <= t <= 1.
@@ -542,7 +822,7 @@ public:
         ip.axpy(u, a);
         ip.axpy(v, b);
         ip.axpy(w, c);
-        is = {ip};
+        is = ip;
         return true;
     }
 };
diff --git a/dumux/common/geometry/intersectingentities.hh b/dumux/common/geometry/intersectingentities.hh
index f4990f46eb..eb7189e52e 100644
--- a/dumux/common/geometry/intersectingentities.hh
+++ b/dumux/common/geometry/intersectingentities.hh
@@ -24,12 +24,15 @@
 
 #include <cmath>
 #include <type_traits>
+
 #include <dune/common/fvector.hh>
+
 #include <dumux/common/math.hh>
 #include <dumux/common/geometry/boundingboxtree.hh>
 #include <dumux/common/geometry/intersectspointgeometry.hh>
 #include <dumux/common/geometry/geometryintersection.hh>
 #include <dumux/common/geometry/boundingboxtreeintersection.hh>
+#include <dumux/common/geometry/triangulation.hh>
 
 namespace Dumux {
 
@@ -149,13 +152,25 @@ void intersectingEntities(const BoundingBoxTree<EntitySet0>& treeA,
         const auto geometryA = treeA.entitySet().entity(eIdxA).geometry();
         const auto geometryB = treeB.entitySet().entity(eIdxB).geometry();
 
-        using IntersectionAlgorithm = GeometryIntersection< std::decay_t<decltype(geometryA)>,
-                                                            std::decay_t<decltype(geometryB)> >;
-        typename IntersectionAlgorithm::IntersectionType intersection;
+        using GeometryA = std::decay_t<decltype(geometryA)>;
+        using GeometryB = std::decay_t<decltype(geometryB)>;
+        using Policy = IntersectionPolicy::DefaultPolicy<GeometryA, GeometryB>;
+        using IntersectionAlgorithm = GeometryIntersection<GeometryA, GeometryB, Policy>;
+        using Intersection = typename IntersectionAlgorithm::Intersection;
+        Intersection intersection;
+
         if (IntersectionAlgorithm::intersection(geometryA, geometryB, intersection))
         {
-            for (int i = 0; i < intersection.size(); ++i)
-                intersections.emplace_back(eIdxA, eIdxB, std::move(intersection[i]));
+            static constexpr int dimIntersection = Policy::dimIntersection;
+
+            if (dimIntersection >= 2)
+            {
+                const auto triangulation = triangulate<2, dimworld>(intersection);
+                for (unsigned int i = 0; i < triangulation.size(); ++i)
+                    intersections.emplace_back(eIdxA, eIdxB, std::move(triangulation[i]));
+            }
+            else
+                intersections.emplace_back(eIdxA, eIdxB, intersection);
         }
     }
 
diff --git a/dumux/multidomain/facet/box/fluxvariablescache.hh b/dumux/multidomain/facet/box/fluxvariablescache.hh
index 23d2d78bcb..4074e68bc5 100644
--- a/dumux/multidomain/facet/box/fluxvariablescache.hh
+++ b/dumux/multidomain/facet/box/fluxvariablescache.hh
@@ -88,27 +88,26 @@ public:
             const auto& geometry = element.geometry();
             auto distVec = scvf.unitOuterNormal();
             distVec *= -1.0;
-            distVec *= diameter(geometry)*5.0; // make sure to be long enough
+            distVec *= diameter(geometry)*5.0; // make sure segment will be long enough
 
-            auto p1 = scvf.ipGlobal();
+            const auto p1 = scvf.ipGlobal();
             auto p2 = scvf.ipGlobal();
             p2 += distVec;
 
             static constexpr int dimWorld = GridView::dimensionworld;
             using Segment = Dune::MultiLinearGeometry<Scalar, 1, dimWorld>;
 
-            std::vector<GlobalPosition> corners({p1, p2});
-            Segment segment(Dune::GeometryTypes::line, corners);
+            using Policy = IntersectionPolicy::SegmentPolicy<typename GridView::ctype, dimWorld>;
+            using IntersectionAlgorithm = GeometryIntersection<typename Element::Geometry, Segment, Policy>;
+            typename IntersectionAlgorithm::Intersection intersection;
 
-            using Intersection = GeometryIntersection<typename Element::Geometry, Segment>;
-            typename Intersection::IntersectionType intersection;
-
-            if (!Intersection::intersection(geometry, segment, intersection))
+            Segment segment(Dune::GeometryTypes::line, std::vector<GlobalPosition>({p1, p2}));
+            if (!IntersectionAlgorithm::intersection(geometry, segment, intersection))
                 DUNE_THROW(Dune::InvalidStateException, "Could not compute interior integration point");
 
             // use center of intersection as integration point
-            ipGlobalInside_ = intersection[0][0];
-            ipGlobalInside_ += intersection[0][1];
+            ipGlobalInside_ = intersection[0];
+            ipGlobalInside_ += intersection[1];
             ipGlobalInside_ /= 2.0;
 
             fvGeometry.feLocalBasis().evaluateFunction(geometry.local(ipGlobalInside_), shapeValuesInside_);
diff --git a/test/common/geometry/test_2d3d_intersection.cc b/test/common/geometry/test_2d3d_intersection.cc
index a8a681af49..3b2af1dad0 100644
--- a/test/common/geometry/test_2d3d_intersection.cc
+++ b/test/common/geometry/test_2d3d_intersection.cc
@@ -11,6 +11,7 @@
 #include <dune/geometry/multilineargeometry.hh>
 
 #include <dumux/common/geometry/geometryintersection.hh>
+#include <dumux/common/geometry/triangulation.hh>
 #include <test/common/geometry/writetriangulation.hh>
 
 template<int dimworld = 3>
@@ -21,20 +22,22 @@ void testSegTriangle(const Dune::FieldVector<double, dimworld>& a,
                      const Dune::FieldVector<double, dimworld>& p,
                      bool expectIntersection = true)
 {
+    using GlobalPosition = Dune::FieldVector<double, dimworld>;
+    using CornerStorage = std::vector<GlobalPosition>;
     using TriGeometry = Dune::MultiLinearGeometry<double, 2, dimworld>;
     using SegGeometry = Dune::MultiLinearGeometry<double, 1, dimworld>;
-    using Test = Dumux::GeometryIntersection<TriGeometry, SegGeometry>;
+    using Policy = Dumux::IntersectionPolicy::PointPolicy<double, dimworld>;
+    using Test = Dumux::GeometryIntersection<TriGeometry, SegGeometry, Policy>;
     typename Test::IntersectionType intersection;
 
-    if (Test::template intersection<2>(a, b, c, p, q, intersection))
+    const auto tria = TriGeometry(Dune::GeometryTypes::triangle, CornerStorage({a, b, c}));
+    const auto seg = SegGeometry(Dune::GeometryTypes::line, CornerStorage({q, p}));
+    if (Test::intersection(tria, seg, intersection))
     {
-        if (intersection.size() != 1)
-            DUNE_THROW(Dune::InvalidStateException, "Found more than one intersection poin!");
-
         if (expectIntersection)
-            std::cout << "Found intersection point: " << intersection[0] << std::endl;
+            std::cout << "Found intersection point: " << intersection << std::endl;
         else
-            DUNE_THROW(Dune::InvalidStateException, "Found false positive: " << intersection[0]);
+            DUNE_THROW(Dune::InvalidStateException, "Found false positive: " << intersection);
     }
     else
     {
@@ -74,7 +77,7 @@ int main(int argc, char* argv[]) try
     using Geometry3D = Dune::MultiLinearGeometry<double, 3, dimworld>;
     using Geometry2D = Dune::MultiLinearGeometry<double, 2, dimworld>;
     using Test = Dumux::GeometryIntersection<Geometry3D, Geometry2D>;
-    typename Test::IntersectionType intersections;
+    typename Test::Intersection intersectionPolygon;
 
     std::vector<Dune::FieldVector<double, dimworld>> cubeCorners({
         {0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 1.0, 0.0},
@@ -93,20 +96,22 @@ int main(int argc, char* argv[]) try
     Geometry2D quad(Dune::GeometryTypes::cube(dimworld-1), quadCorners);
     Geometry2D tri(Dune::GeometryTypes::simplex(dimworld-1), triCorners);
 
-    if (Test::intersection(cube, quad, intersections))
+    if (Test::intersection(cube, quad, intersectionPolygon))
     {
-        Dumux::writeVTKPolyDataTriangle(intersections, "quad_intersections");
-        if (intersections.size() != 4)
-            DUNE_THROW(Dune::InvalidStateException, "Should be 4 intersections!");
+        const auto triangulation = Dumux::triangulate<2, dimworld>(intersectionPolygon);
+        Dumux::writeVTKPolyDataTriangle(triangulation, "quad_intersections");
+        if (triangulation.size() != 4)
+            DUNE_THROW(Dune::InvalidStateException, "Found " << triangulation.size() << " instead of 4 intersections!");
     }
     else
         DUNE_THROW(Dune::InvalidStateException, "No intersections found!");
 
-    if (Test::intersection(cube, tri, intersections))
+    if (Test::intersection(cube, tri, intersectionPolygon))
     {
-        Dumux::writeVTKPolyDataTriangle(intersections, "tri_intersections");
-        if (intersections.size() != 6)
-            DUNE_THROW(Dune::InvalidStateException, "Should be 4 intersections!");
+        const auto triangulation = Dumux::triangulate<2, dimworld>(intersectionPolygon);
+        Dumux::writeVTKPolyDataTriangle(triangulation, "tri_intersections");
+        if (triangulation.size() != 6)
+            DUNE_THROW(Dune::InvalidStateException, "Found " << triangulation.size() << " instead of 6 intersections!");
     }
     else
         DUNE_THROW(Dune::InvalidStateException, "No intersections found!");
-- 
GitLab