diff --git a/dumux/common/geometry/boundingboxtreeintersection.hh b/dumux/common/geometry/boundingboxtreeintersection.hh index d3f32aa2be58187c4f8ad5bf1356641d125ff464..b927070751f520fa1db6a48e0cd27a819952b3e2 100644 --- a/dumux/common/geometry/boundingboxtreeintersection.hh +++ b/dumux/common/geometry/boundingboxtreeintersection.hh @@ -45,12 +45,13 @@ class BoundingBoxTreeIntersection using GlobalPosition = Dune::FieldVector<ctype, dimworld>; public: + template<class Corners> explicit BoundingBoxTreeIntersection(std::size_t a, std::size_t b, - std::vector<GlobalPosition>&& c) + Corners&& c) : a_(a) , b_(b) - , corners_(std::move(c)) + , corners_(c.begin(), c.end()) { static_assert(int(EntitySet0::dimensionworld) == int(EntitySet1::dimensionworld), "Can only store intersections of entity sets with the same world dimension"); diff --git a/dumux/common/geometry/geometryintersection.hh b/dumux/common/geometry/geometryintersection.hh index f541a177583e18e29e613ef79de8b069ad4fb975..5e3afd3b41b191122e58fbad92f65004ab4b8bd3 100644 --- a/dumux/common/geometry/geometryintersection.hh +++ b/dumux/common/geometry/geometryintersection.hh @@ -23,14 +23,16 @@ #ifndef DUMUX_GEOMETRY_INTERSECTION_HH #define DUMUX_GEOMETRY_INTERSECTION_HH +#include <dune/common/version.hh> #include <dune/common/exceptions.hh> #include <dune/common/promotiontraits.hh> #include <dune/geometry/referenceelements.hh> #include <dumux/common/math.hh> +#include <dumux/common/geometry/intersectspointgeometry.hh> +#include <dumux/common/geometry/grahamconvexhull.hh> -namespace Dumux -{ +namespace Dumux { /*! * \ingroup Common @@ -46,11 +48,8 @@ template class GeometryIntersection { public: - using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType; - using GlobalPosition = Dune::FieldVector<ctype, dimworld>; - using IntersectionType = std::vector<GlobalPosition>; - //! 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) { static_assert(dimworld == Geometry2::coorddimension, "Can only intersect geometries of same coordinate dimension"); @@ -59,19 +58,18 @@ public: } }; -//! Geometry collision detection with 3d and 1d geometry in 3d space +//! polyhedron--segment intersection in 3d space template <class Geometry1, class Geometry2> class GeometryIntersection<Geometry1, Geometry2, 3, 3, 1> { enum { dimworld = 3 }; enum { dim1 = 3 }; enum { dim2 = 1 }; - enum { dimis = dim2 }; // the intersection dimension public: using ctype = typename Dune::PromotionTraits<typename Geometry1::ctype, typename Geometry2::ctype>::PromotedType; - using GlobalPosition = Dune::FieldVector<ctype, dimworld>; - using IntersectionType = std::vector<GlobalPosition>; + using Point = Dune::FieldVector<ctype, dimworld>; + using IntersectionType = std::array<std::vector<Point>, 1>; private: static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons @@ -80,7 +78,8 @@ private: public: /*! * \brief Colliding segment and convex polyhedron - * \note Algorithm from "Real-Time Collision Detection" by Christer Ericson + * \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 @@ -116,7 +115,8 @@ public: facets = {{1, 2, 0}, {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."); + DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " + << geo1.type() << ", "<< geo1.corners() << " corners."); } return facets; @@ -168,7 +168,258 @@ public: } // 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 = {geo2.global(tfirst), geo2.global(tlast)}; + intersection = {std::vector<Point>({geo2.global(tfirst), geo2.global(tlast)})}; + return true; + } +}; + +//! polyhedron--polygon intersection in 3d space +template <class Geometry1, class Geometry2> +class GeometryIntersection<Geometry1, Geometry2, 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>>; + +private: + static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons + using ReferenceElementsGeo1 = typename Dune::ReferenceElements<ctype, dim1>; + using ReferenceElementsGeo2 = typename Dune::ReferenceElements<ctype, dim2>; + +public: + /*! + * \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 + */ + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, IntersectionType& intersection) + { + 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); + + // add 3d geometry corners that are inside the 2d geometry + for (int i = 0; i < geo1.corners(); ++i) + if (intersectsPointGeometry(geo1.corner(i), geo2)) + points.emplace_back(geo1.corner(i)); + + // add 2d geometry corners that are inside the 3d geometry + for (int i = 0; i < geo2.corners(); ++i) + if (intersectsPointGeometry(geo2.corner(i), geo1)) + points.emplace_back(geo2.corner(i)); + + // get some geometry types + using PolyhedronFaceGeometry = Dune::MultiLinearGeometry<ctype, 2, dimworld>; + using SegGeometry = Dune::MultiLinearGeometry<ctype, 1, dimworld>; + +#if DUNE_VERSION_NEWER(DUNE_COMMON,2,6) + const auto referenceElement1 = ReferenceElementsGeo1::general(geo1.type()); + const auto referenceElement2 = ReferenceElementsGeo2::general(geo2.type()); +#else + const auto& referenceElement1 = ReferenceElementsGeo1::general(geo1.type()); + const auto& referenceElement2 = ReferenceElementsGeo2::general(geo2.type()); +#endif + + // add intersection points of all polyhedron edges (codim dim-1) with the polygon + for (int i = 0; i < referenceElement1.size(dim1-1); ++i) + { + const auto localEdgeGeom = referenceElement1.template geometry<dim1-1>(i); + const auto p = geo1.global(localEdgeGeom.corner(0)); + 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 intersection; + if (PolySegTest::template intersection<2>(geo2, segGeo, intersection)) + points.emplace_back(intersection[0]); + } + + // add intersection points of all polygon faces (codim 1) with the polyhedron faces + for (int i = 0; i < referenceElement1.size(1); ++i) + { + const auto faceGeo = [&]() + { + const auto localFaceGeo = referenceElement1.template geometry<1>(i); + if (localFaceGeo.corners() == 4) + { + const auto a = geo1.global(localFaceGeo.corner(0)); + const auto b = geo1.global(localFaceGeo.corner(1)); + const auto c = geo1.global(localFaceGeo.corner(2)); + const auto d = geo1.global(localFaceGeo.corner(3)); + return PolyhedronFaceGeometry(Dune::GeometryTypes::cube(2), std::vector<Point>{a, b, c, d}); + } + else + { + const auto a = geo1.global(localFaceGeo.corner(0)); + const auto b = geo1.global(localFaceGeo.corner(1)); + const auto c = geo1.global(localFaceGeo.corner(2)); + return PolyhedronFaceGeometry(Dune::GeometryTypes::simplex(2), std::vector<Point>{a, b, c}); + } + }(); + + for (int j = 0; j < referenceElement2.size(1); ++j) + { + const auto localEdgeGeom = referenceElement2.template geometry<1>(j); + const auto p = geo2.global(localEdgeGeom.corner(0)); + const auto q = geo2.global(localEdgeGeom.corner(1)); + const auto segGeo = SegGeometry(Dune::GeometryTypes::line, std::vector<Point>{p, q}); + + using PolySegTest = GeometryIntersection<PolyhedronFaceGeometry, SegGeometry>; + typename PolySegTest::IntersectionType intersection; + if (PolySegTest::template intersection<2>(faceGeo, segGeo, intersection)) + points.emplace_back(intersection[0]); + } + } + + // return if no intersection points were found + if (points.empty()) return false; + + // remove duplicates + const auto eps = (geo1.corner(0) - geo1.corner(1)).two_norm()*eps_; + std::sort(points.begin(), points.end(), [&eps](const auto& a, const auto& b) -> bool + { + return (abs(a[0]-b[0]) > eps ? a[0] < b[0] : (abs(a[1]-b[1]) > eps ? a[1] < b[1] : (a[2] < b[2]))); + }); + + auto removeIt = std::unique(points.begin(), points.end(), [&eps](const auto& a, const auto&b) + { + return (b-a).two_norm() < eps; + }); + + points.erase(removeIt, points.end()); + + // 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); + + // the intersections are the triangulation of the convex hull polygon + intersection = triangulateConvexHull(convexHull); + return true; + } +}; + +//! polygon--segment intersection in 3d space +template <class Geometry1, class Geometry2> +class GeometryIntersection<Geometry1, Geometry2, 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>; + +private: + static constexpr ctype eps_ = 1.5e-7; // base epsilon for floating point comparisons + using ReferenceElements = typename Dune::ReferenceElements<ctype, dim1>; + +public: + /*! + * \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 intersection If the geometries collide intersection holds the corner points of + * the intersection object in global coordinates. + */ + template<int dimIntersection> + static bool intersection(const Geometry1& geo1, const Geometry2& geo2, IntersectionType& 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); + const auto q = geo2.corner(1); + + const auto a = geo1.corner(0); + const auto b = geo1.corner(1); + const auto c = geo1.corner(2); + + if (geo1.corners() == 3) + return intersection<dimIntersection>(a, b, c, p, q, is); + + else if (geo1.corners() == 4) + { + 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; + } + + else + DUNE_THROW(Dune::NotImplemented, "Collision of segment and geometry of type " + << geo1.type() << ", "<< geo1.corners() << " corners."); + } + + // triangle--segment intersection with points as input + template<int dimIntersection> + static bool intersection(const Point& a, const Point& b, const Point& c, + const Point& p, const Point& q, + IntersectionType& is) + { + if (dimIntersection != 2) + DUNE_THROW(Dune::NotImplemented, "Only simplex intersections are currently implemented!"); + + const auto ab = b - a; + const auto ac = c - a; + const auto qp = p - q; + + // compute the triangle normal that defines the triangle plane + const auto normal = crossProduct(ab, ac); + + // 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(); + using std::abs; + if (abs(denom) < eps) + return false; + + // compute intersection t value of pq with plane of triangle. + // a segment intersects if and only if 0 <= t <= 1. + const auto ap = p - a; + const auto t = (ap*normal)/denom; + if (t < 0.0 - eps_) return false; + if (t > 1.0 + eps_) return false; + + // compute the barycentric coordinates and check if the intersection point + // is inside the bounds of the triangle + const auto e = crossProduct(qp, ap); + const auto v = (ac*e)/denom; + if (v < -eps_ || v > 1.0 + eps_) return false; + const auto w = -(ab*e)/denom; + if (w < -eps_ || v + w > 1.0 + eps_) return false; + + // Now we are sure there is an intersection points + // Perform delayed division compute the last barycentric coordinate component + const auto u = 1.0 - v - w; + + Point ip(0.0); + ip.axpy(u, a); + ip.axpy(v, b); + ip.axpy(w, c); + is = {ip}; return true; } }; diff --git a/dumux/common/geometry/intersectingentities.hh b/dumux/common/geometry/intersectingentities.hh index 132e61f3b41ec2c70020e5ceb1131ac895521d93..d3bb24badb96672486da06f0338d0c68acbff435 100644 --- a/dumux/common/geometry/intersectingentities.hh +++ b/dumux/common/geometry/intersectingentities.hh @@ -145,7 +145,10 @@ void intersectingEntities(const BoundingBoxTree<EntitySet0>& treeA, std::decay_t<decltype(geometryB)> >; typename IntersectionAlgorithm::IntersectionType intersection; if (IntersectionAlgorithm::intersection(geometryA, geometryB, intersection)) - intersections.emplace_back(eIdxA, eIdxB, std::move(intersection)); + { + for (int i = 0; i < intersection.size(); ++i) + intersections.emplace_back(eIdxA, eIdxB, std::move(intersection[i])); + } } // if we reached the leaf in treeA, just continue in treeB