version 3.10-dev
intersectspointsimplex.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_GEOMETRY_INTERSECTS_POINT_SIMPLEX_HH
13#define DUMUX_GEOMETRY_INTERSECTS_POINT_SIMPLEX_HH
14
15#include <cmath>
16#include <dune/common/fvector.hh>
17#include <dumux/common/math.hh>
18
19namespace Dumux {
20
25template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3), int> = 0>
26bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point,
27 const Dune::FieldVector<ctype, dimworld>& p0,
28 const Dune::FieldVector<ctype, dimworld>& p1,
29 const Dune::FieldVector<ctype, dimworld>& p2,
30 const Dune::FieldVector<ctype, dimworld>& p3)
31{
32 // Algorithm from http://www.blackpawn.com/texts/pointinpoly/
33 // See also "Real-Time Collision Detection" by Christer Ericson.
34 using GlobalPosition = Dune::FieldVector<ctype, dimworld>;
35 static constexpr ctype eps_ = 1.0e-7;
36
37 // put the tetrahedron points in an array
38 const GlobalPosition *p[4] = {&p0, &p1, &p2, &p3};
39
40 // iterate over all faces
41 for (int i = 0; i < 4; ++i)
42 {
43 // compute all the vectors from vertex (local index 0) to the other points
44 const GlobalPosition v1 = *p[(i + 1)%4] - *p[i];
45 const GlobalPosition v2 = *p[(i + 2)%4] - *p[i];
46 const GlobalPosition v3 = *p[(i + 3)%4] - *p[i];
47 const GlobalPosition v = point - *p[i];
48 // compute the normal to the facet (cross product)
49 GlobalPosition n1 = crossProduct(v1, v2);
50 n1 /= n1.two_norm();
51 // find out on which side of the plane v and v3 are
52 const auto t1 = n1.dot(v);
53 const auto t2 = n1.dot(v3);
54 // If the point is not exactly on the plane the
55 // points have to be on the same side
56 const auto eps = eps_ * v1.two_norm();
57 if ((t1 > eps || t1 < -eps) && std::signbit(t1) != std::signbit(t2))
58 return false;
59 }
60 return true;
61}
62
67template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3), int> = 0>
68bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point,
69 const Dune::FieldVector<ctype, dimworld>& p0,
70 const Dune::FieldVector<ctype, dimworld>& p1,
71 const Dune::FieldVector<ctype, dimworld>& p2)
72{
73 // adapted from the algorithm from from "Real-Time Collision Detection" by Christer Ericson,
74 // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc. (Chapter 5.4.2)
75 constexpr ctype eps_ = 1.0e-7;
76
77 // compute the normal of the triangle
78 const auto v1 = p0 - p2;
79 auto n = crossProduct(v1, p1 - p0);
80 const ctype nnorm = n.two_norm();
81 const ctype eps4 = eps_*nnorm*nnorm; // compute an epsilon for later
82 n /= nnorm; // normalize
83
84 // first check if we are in the plane of the triangle
85 // if not we can return early
86 using std::abs;
87 auto x = p0 - point;
88 x /= x.two_norm(); // normalize
89
90 if (abs(x*n) > eps_)
91 return false;
92
93 // translate the triangle so that 'point' is the origin
94 const auto a = p0 - point;
95 const auto b = p1 - point;
96 const auto c = p2 - point;
97
98 // compute the normal vectors for triangles P->A->B and P->B->C
99 const auto u = crossProduct(b, c);
100 const auto v = crossProduct(c, a);
101
102 // they have to point in the same direction or be orthogonal
103 if (u*v < 0.0 - eps4)
104 return false;
105
106 // compute the normal vector for triangle P->C->A
107 const auto w = crossProduct(a, b);
108
109 // it also has to point in the same direction or be orthogonal
110 if (u*w < 0.0 - eps4)
111 return false;
112
113 // check if point is on the line of one of the edges
114 if (u.two_norm2() < eps4)
115 return b*c < 0.0 + eps_*nnorm;
116 if (v.two_norm2() < eps4)
117 return a*c < 0.0 + eps_*nnorm;
118
119 // now the point must be in the triangle (or on the faces)
120 return true;
121}
122
127template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 2), int> = 0>
128bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point,
129 const Dune::FieldVector<ctype, dimworld>& p0,
130 const Dune::FieldVector<ctype, dimworld>& p1,
131 const Dune::FieldVector<ctype, dimworld>& p2)
132{
133 static constexpr ctype eps_ = 1.0e-7;
134
135 // Use barycentric coordinates
136 const ctype A = 0.5*(-p1[1]*p2[0] + p0[1]*(p2[0] - p1[0])
137 +p1[0]*p2[1] + p0[0]*(p1[1] - p2[1]));
138 const ctype sign = std::copysign(1.0, A);
139 const ctype s = sign*(p0[1]*p2[0] + point[0]*(p2[1]-p0[1])
140 -p0[0]*p2[1] + point[1]*(p0[0]-p2[0]));
141 const ctype t = sign*(p0[0]*p1[1] + point[0]*(p0[1]-p1[1])
142 -p0[1]*p1[0] + point[1]*(p1[0]-p0[0]));
143 const ctype eps = sign*A*eps_;
144
145 return (s > -eps
146 && t > -eps
147 && (s + t) < 2*A*sign + eps);
148}
149
155template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 3 || dimworld == 2), int> = 0>
156bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point,
157 const Dune::FieldVector<ctype, dimworld>& p0,
158 const Dune::FieldVector<ctype, dimworld>& p1)
159{
160 using GlobalPosition = Dune::FieldVector<ctype, dimworld>;
161 static constexpr ctype eps_ = 1.0e-7;
162
163 // compute the vectors between p0 and the other points
164 const GlobalPosition v1 = p1 - p0;
165 const GlobalPosition v2 = point - p0;
166
167 const ctype v1norm = v1.two_norm();
168 const ctype v2norm = v2.two_norm();
169
170 // early exit if point and p0 are the same
171 if (v2norm < v1norm*eps_)
172 return true;
173
174 // early exit if the point is outside the segment (no epsilon in the
175 // first statement because we already did the above equality check)
176 if (v1.dot(v2) < 0.0 || v2norm > v1norm*(1.0 + eps_))
177 return false;
178
179 // If the area spanned by the 2 vectors is zero, the points are colinear.
180 // If that is the case, the given point is on the segment.
181 const auto n = crossProduct(v1, v2);
182 const auto eps2 = v1norm*v1norm*eps_;
183 if constexpr (dimworld == 3)
184 return n.two_norm2() < eps2*eps2;
185 else
186 {
187 using std::abs;
188 return abs(n) < eps2;
189 }
190}
191
196template<class ctype, int dimworld, typename std::enable_if_t<(dimworld == 1), int> = 0>
197bool intersectsPointSimplex(const Dune::FieldVector<ctype, dimworld>& point,
198 const Dune::FieldVector<ctype, dimworld>& p0,
199 const Dune::FieldVector<ctype, dimworld>& p1)
200{
201 static constexpr ctype eps_ = 1.0e-7;
202
203 // sort the interval so interval[1] is the end and interval[0] the start
204 const ctype *interval[2] = {&p0[0], &p1[0]};
205 if (*interval[0] > *interval[1])
206 std::swap(interval[0], interval[1]);
207
208 const ctype v1 = point[0] - *interval[0];
209 const ctype v2 = *interval[1] - *interval[0]; // always positive
210
211 // the coordinates are the same
212 using std::abs;
213 if (abs(v1) < v2*eps_)
214 return true;
215
216 // the point doesn't coincide with p0
217 // so if p0 and p1 are equal it's not inside
218 if (v2 < 1.0e-30)
219 return false;
220
221 // the point is inside if the length is
222 // smaller than the interval length and the
223 // sign of v1 & v2 are the same
224 using std::signbit;
225 return (!signbit(v1) && abs(v1) < v2*(1.0 + eps_));
226}
227
228} // end namespace Dumux
229
230#endif
Dune::FieldVector< Scalar, 3 > crossProduct(const Dune::FieldVector< Scalar, 3 > &vec1, const Dune::FieldVector< Scalar, 3 > &vec2)
Cross product of two vectors in three-dimensional Euclidean space.
Definition: math.hh:671
constexpr int sign(const ValueType &value) noexcept
Sign or signum function.
Definition: math.hh:658
bool intersectsPointSimplex(const Dune::FieldVector< ctype, dimworld > &point, const Dune::FieldVector< ctype, dimworld > &p0, const Dune::FieldVector< ctype, dimworld > &p1, const Dune::FieldVector< ctype, dimworld > &p2, const Dune::FieldVector< ctype, dimworld > &p3)
Find out whether a point is inside the tetrahedron (p0, p1, p2, p3) (dimworld is 3)
Definition: intersectspointsimplex.hh:26
Define some often used mathematical functions.
Definition: adapt.hh:17