https://mooseframework.inl.gov
GeometryUtils.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "GeometryUtils.h"
11 #include "MooseUtils.h"
12 
13 #include <algorithm>
14 #include <cmath>
15 #include <limits>
16 
17 namespace geom_utils
18 {
19 
20 bool
21 isPointZero(const Point & pt)
22 {
23  const Point zero(0.0, 0.0, 0.0);
24  return pt.absolute_fuzzy_equals(zero);
25 }
26 
27 Point
28 unitVector(const Point & pt, const std::string & name)
29 {
30  if (isPointZero(pt))
31  mooseError("'" + name + "' cannot have zero norm!");
32 
33  return pt.unit();
34 }
35 
36 Real
37 minDistanceToPoints(const Point & pt,
38  const std::vector<Point> & candidates,
39  const unsigned int axis)
40 {
41  const auto idx = projectedIndices(axis);
42 
44  for (const auto & c : candidates)
45  {
46  const Real dx = c(idx.first) - pt(idx.first);
47  const Real dy = c(idx.second) - pt(idx.second);
48  const Real d = std::sqrt(dx * dx + dy * dy);
50  }
51 
52  return distance;
53 }
54 
55 Point
56 projectPoint(const Real x0, const Real x1, const unsigned int axis)
57 {
58  const auto i = projectedIndices(axis);
59  Point point;
60  point(i.first) = x0;
61  point(i.second) = x1;
62  point(axis) = 0.0;
63 
64  return point;
65 }
66 
67 Real
68 projectedLineHalfSpace(Point pt1, Point pt2, Point pt3, const unsigned int axis)
69 {
70  // project points onto plane perpendicular to axis
71  pt1(axis) = 0.0;
72  pt2(axis) = 0.0;
73  pt3(axis) = 0.0;
74 
75  const auto i = projectedIndices(axis);
76 
77  return (pt1(i.first) - pt3(i.first)) * (pt2(i.second) - pt3(i.second)) -
78  (pt2(i.first) - pt3(i.first)) * (pt1(i.second) - pt3(i.second));
79 }
80 
81 bool
82 pointInPolygon(const Point & point, const std::vector<Point> & corners, const unsigned int axis)
83 {
84  const auto n_pts = corners.size();
85 
86  std::vector<bool> negative_half_space;
87  std::vector<bool> positive_half_space;
88  for (const auto i : index_range(corners))
89  {
90  const int next = (i == n_pts - 1) ? 0 : i + 1;
91  const auto half = projectedLineHalfSpace(point, corners[i], corners[next], axis);
92  negative_half_space.push_back(half < 0);
93  positive_half_space.push_back(half > 0);
94  }
95 
96  const bool negative = std::find(negative_half_space.begin(), negative_half_space.end(), true) !=
97  negative_half_space.end();
98  const bool positive = std::find(positive_half_space.begin(), positive_half_space.end(), true) !=
99  positive_half_space.end();
100 
101  const bool in_polygon = !(negative && positive);
102  if (in_polygon)
103  return true;
104 
105  if (pointOnEdge(point, corners, axis))
106  return true;
107 
108  return false;
109 }
110 
111 bool
112 pointOnEdge(const Point & point, const std::vector<Point> & corners, const unsigned int axis)
113 {
114  const auto n_pts = corners.size();
115  const auto idx = projectedIndices(axis);
116 
117  constexpr Real tol = 1e-8;
118  for (const auto i : index_range(corners))
119  {
120  const int next = (i == n_pts - 1) ? 0 : i + 1;
121  const auto & pt1 = corners[i];
122  const auto & pt2 = corners[next];
123  const bool close_to_line = projectedDistanceFromLine(point, pt1, pt2, axis) < tol;
124 
125  // we can stop early if we know we're not close to the line
126  if (!close_to_line)
127  continue;
128 
129  // check that the point is "between" the two points; TODO: first pass
130  // we can just compare x and y coordinates
131  const bool between_points = (point(idx.first) >= std::min(pt1(idx.first), pt2(idx.first))) &&
132  (point(idx.first) <= std::max(pt1(idx.first), pt2(idx.first))) &&
133  (point(idx.second) >= std::min(pt1(idx.second), pt2(idx.second))) &&
134  (point(idx.second) <= std::max(pt1(idx.second), pt2(idx.second)));
135 
136  // point needs to be close to the line AND "between" the two points
137  if (close_to_line && between_points)
138  return true;
139  }
140 
141  return false;
142 }
143 
144 std::pair<unsigned int, unsigned int>
145 projectedIndices(const unsigned int axis)
146 {
147  std::pair<unsigned int, unsigned int> indices;
148 
149  if (axis == 0)
150  {
151  indices.first = 1;
152  indices.second = 2;
153  }
154  else if (axis == 1)
155  {
156  indices.first = 0;
157  indices.second = 2;
158  }
159  else
160  {
161  indices.first = 0;
162  indices.second = 1;
163  }
164 
165  return indices;
166 }
167 
168 Point
169 projectedUnitNormal(Point pt1, Point pt2, const unsigned int axis)
170 {
171  // project the points to the plane perpendicular to the axis
172  pt1(axis) = 0.0;
173  pt2(axis) = 0.0;
174 
175  const auto i = projectedIndices(axis);
176 
177  const Real dx = pt2(i.first) - pt1(i.first);
178  const Real dy = pt2(i.second) - pt1(i.second);
179 
180  const Point normal = projectPoint(dy, -dx, axis);
181  const Point gap_line = pt2 - pt1;
182 
183  const auto cross_product = gap_line.cross(normal);
184 
185  if (cross_product(axis) > 0)
186  return normal.unit();
187  else
188  return projectPoint(-dy, dx, axis).unit();
189 }
190 
191 Real
192 distanceFromLine(const Point & pt, const Point & line0, const Point & line1)
193 {
194  const Point a = pt - line0;
195  const Point b = pt - line1;
196  const Point c = line1 - line0;
197 
198  return (a.cross(b).norm()) / c.norm();
199 }
200 
201 Real
202 projectedDistanceFromLine(Point pt, Point line0, Point line1, const unsigned int axis)
203 {
204  // project all the points to the plane perpendicular to the axis
205  pt(axis) = 0.0;
206  line0(axis) = 0.0;
207  line1(axis) = 0.0;
208 
209  return distanceFromLine(pt, line0, line1);
210 }
211 
212 std::vector<Point>
213 polygonCorners(const unsigned int num_sides, const Real radius, const unsigned int axis)
214 {
215  std::vector<Point> corners;
216  const Real theta = 2.0 * M_PI / num_sides;
217  const Real first_angle = M_PI / 2.0 - theta / 2.0;
218 
219  for (const auto i : make_range(num_sides))
220  {
221  const Real angle = first_angle + i * theta;
222  const Real x = radius * cos(angle);
223  const Real y = radius * sin(angle);
224 
225  corners.push_back(projectPoint(x, y, axis));
226  }
227 
228  return corners;
229 }
230 
231 Point
232 rotatePointAboutAxis(const Point & p, const Real angle, const Point & axis)
233 {
234  const Real cos_theta = cos(angle);
235  const Real sin_theta = sin(angle);
236 
237  Point pt;
238  const Real xy = axis(0) * axis(1);
239  const Real xz = axis(0) * axis(2);
240  const Real yz = axis(1) * axis(2);
241 
242  const Point x_op(cos_theta + axis(0) * axis(0) * (1.0 - cos_theta),
243  xy * (1.0 - cos_theta) - axis(2) * sin_theta,
244  xz * (1.0 - cos_theta) + axis(1) * sin_theta);
245 
246  const Point y_op(xy * (1.0 - cos_theta) + axis(2) * sin_theta,
247  cos_theta + axis(1) * axis(1) * (1.0 - cos_theta),
248  yz * (1.0 - cos_theta) - axis(0) * sin_theta);
249 
250  const Point z_op(xz * (1.0 - cos_theta) - axis(1) * sin_theta,
251  yz * (1.0 - cos_theta) + axis(0) * sin_theta,
252  cos_theta + axis(2) * axis(2) * (1.0 - cos_theta));
253 
254  pt(0) = x_op * p;
255  pt(1) = y_op * p;
256  pt(2) = z_op * p;
257  return pt;
258 }
259 
260 std::vector<Point>
261 boxCorners(const libMesh::BoundingBox & box, const Real factor)
262 {
263  Point diff = (box.max() - box.min()) / 2.0;
264  const Point origin = box.min() + diff;
265 
266  // Rescale side length of box by specified factor
267  diff *= factor;
268 
269  // Vectors for sides of box
270  const Point dx(2.0 * diff(0), 0.0, 0.0);
271  const Point dy(0.0, 2.0 * diff(1), 0.0);
272  const Point dz(0.0, 0.0, 2.0 * diff(2));
273 
274  std::vector<Point> verts(8, origin - diff);
275  const unsigned int pts_per_dim = 2;
276  for (const auto z : make_range(pts_per_dim))
277  for (const auto y : make_range(pts_per_dim))
278  for (const auto x : make_range(pts_per_dim))
279  verts[pts_per_dim * pts_per_dim * z + pts_per_dim * y + x] += x * dx + y * dy + z * dz;
280 
281  return verts;
282 }
283 
284 bool
285 arePointsColinear(const Point & p1, const Point & p2, const Point & p3)
286 {
287  const Point v1 = p2 - p1;
288  const Point v2 = p3 - p1;
289  const Point cross_prod = v1.cross(v2);
290 
291  return MooseUtils::absoluteFuzzyEqual(cross_prod.norm(), 0.0);
292 }
293 
294 bool
295 segmentsIntersect(const Point & p1, const Point & p2, const Point & p3, const Point & p4)
296 {
297  mooseAssert(
298  MooseUtils::absoluteFuzzyEqual(p1(2), 0.0) && MooseUtils::absoluteFuzzyEqual(p2(2), 0.0) &&
299  MooseUtils::absoluteFuzzyEqual(p3(2), 0.0) && MooseUtils::absoluteFuzzyEqual(p4(2), 0.0),
300  "segmentsIntersect only works in 2D (x-y plane)");
301 
302  mooseAssert(MooseUtils::absoluteFuzzyGreaterThan((p1 - p2).norm(), 0.0) &&
303  MooseUtils::absoluteFuzzyGreaterThan((p3 - p4).norm(), 0.0),
304  "Zero length segments are not allowed in segmentsIntersect");
305 
306  const Real a1 = p2(1) - p1(1);
307  const Real b1 = p1(0) - p2(0);
308  const Real c1 = p2(0) * p1(1) - p1(0) * p2(1);
309 
310  const Real a2 = p4(1) - p3(1);
311  const Real b2 = p3(0) - p4(0);
312  const Real c2 = p4(0) * p3(1) - p3(0) * p4(1);
313 
314  const Real denom = a1 * b2 - a2 * b1;
315  Point intersection_pt;
316  // Parallel case
317  if (MooseUtils::absoluteFuzzyEqual(denom, 0.0))
318  {
319  if (arePointsColinear(p1, p2, p3))
320  {
321  // for colinear segments, we construct a "virtual" intersection point using weighted average
322  // In that case, the virtual point will always lie outside of both segments unless they
323  // overlap
324  const Point p12 = (p1 + p2) / 2.0;
325  const Point p34 = (p3 + p4) / 2.0;
326  const Real dist12 = (p1 - p2).norm();
327  const Real dist34 = (p3 - p4).norm();
328  intersection_pt = p12 + (p34 - p12) * (dist12 / (dist12 + dist34));
329  }
330  else
331  return false;
332  }
333  else
334  {
335  intersection_pt = Point((b1 * c2 - b2 * c1) / denom, (a2 * c1 - a1 * c2) / denom, 0.0);
336  }
337 
338  const Real ratio_p1p2 = (intersection_pt - p1) * (p2 - p1) / ((p2 - p1).norm_sq());
339  const Real ratio_p3p4 = (intersection_pt - p3) * (p4 - p3) / ((p4 - p3).norm_sq());
340 
341  if (MooseUtils::absoluteFuzzyGreaterEqual(ratio_p1p2, 0.0) &&
342  MooseUtils::absoluteFuzzyLessEqual(ratio_p1p2, 1.0) &&
343  MooseUtils::absoluteFuzzyGreaterEqual(ratio_p3p4, 0.0) &&
344  MooseUtils::absoluteFuzzyLessEqual(ratio_p3p4, 1.0))
345  return true;
346  else
347  return false;
348 }
349 
350 Real
351 pointSegmentDistanceSq(const Point & point, const Point & a, const Point & b)
352 {
353  const Point ab = b - a;
354  const auto length_sq = ab.norm_sq();
355  if (length_sq <= std::numeric_limits<Real>::epsilon())
356  return (point - a).norm_sq();
357 
358  const auto t = std::clamp(((point - a) * ab) / length_sq, 0.0, 1.0);
359  const Point projection = a + t * ab;
360  return (point - projection).norm_sq();
361 }
362 
363 Real
364 pointTriangleDistanceSq(const Point & point, const Point & v0, const Point & v1, const Point & v2)
365 {
366  const Point ab = v1 - v0;
367  const Point ac = v2 - v0;
368  const Point ap = point - v0;
369  const Real d1 = ab * ap;
370  const Real d2 = ac * ap;
371  if (d1 <= 0.0 && d2 <= 0.0)
372  return (point - v0).norm_sq();
373 
374  const Point bp = point - v1;
375  const Real d3 = ab * bp;
376  const Real d4 = ac * bp;
377  if (d3 >= 0.0 && d4 <= d3)
378  return (point - v1).norm_sq();
379 
380  const Real vc = d1 * d4 - d3 * d2;
381  if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0)
382  {
383  const Real v = d1 / (d1 - d3);
384  const Point projection = v0 + v * ab;
385  return (point - projection).norm_sq();
386  }
387 
388  const Point cp = point - v2;
389  const Real d5 = ab * cp;
390  const Real d6 = ac * cp;
391  if (d6 >= 0.0 && d5 <= d6)
392  return (point - v2).norm_sq();
393 
394  const Real vb = d5 * d2 - d1 * d6;
395  if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0)
396  {
397  const Real w = d2 / (d2 - d6);
398  const Point projection = v0 + w * ac;
399  return (point - projection).norm_sq();
400  }
401 
402  const Real va = d3 * d6 - d5 * d4;
403  if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0)
404  {
405  const Point bc = v2 - v1;
406  const Real w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
407  const Point projection = v1 + w * bc;
408  return (point - projection).norm_sq();
409  }
410 
411  const Real denom = 1.0 / (va + vb + vc);
412  const Real v = vb * denom;
413  const Real w = vc * denom;
414  const Point projection = v0 + ab * v + ac * w;
415  return (point - projection).norm_sq();
416 }
417 
418 Real
419 solidAngle(const Point & point, const Point & v0, const Point & v1, const Point & v2)
420 {
421  const Point a = v0 - point;
422  const Point b = v1 - point;
423  const Point c = v2 - point;
424 
425  const Real la = a.norm();
426  const Real lb = b.norm();
427  const Real lc = c.norm();
428 
429  const Real numerator = a * (b.cross(c));
430  const Real denominator = la * lb * lc + (a * b) * lc + (b * c) * la + (c * a) * lb;
431 
432  return 2.0 * std::atan2(numerator, denominator);
433 }
434 } // end namespace geom_utils
std::string name(const ElemQuality q)
Real pointTriangleDistanceSq(const Point &point, const Point &v0, const Point &v1, const Point &v2)
Compute the squared distance from a point to a 3-D triangle.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:40
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sin(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tan
const Real radius
libMesh::Point projectPoint(const libMesh::Real x0, const libMesh::Real x1, const unsigned int axis)
Given two coordinates, construct a point in the 2-D plane perpendicular to the specified axis...
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
auto norm_sq(const T &a)
libMesh::Real projectedDistanceFromLine(libMesh::Point pt, libMesh::Point line0, libMesh::Point line1, const unsigned int axis)
Compute the distance from a 3-D line, provided in terms of two points on the line.
Real solidAngle(const Point &point, const Point &v0, const Point &v1, const Point &v2)
Compute the signed solid angle subtended by one oriented triangle at the query point.
libMesh::Real projectedLineHalfSpace(libMesh::Point pt1, libMesh::Point pt2, libMesh::Point pt3, const unsigned int axis)
If positive, point is on the positive side of the half space (and vice versa).
libMesh::Point projectedUnitNormal(libMesh::Point pt1, libMesh::Point pt2, const unsigned int axis)
Get the unit normal vector between two points (which are first projected onto the plane perpendicular...
libMesh::Point rotatePointAboutAxis(const libMesh::Point &p, const libMesh::Real angle, const libMesh::Point &axis)
Rotate point about an axis.
const Number zero
std::vector< libMesh::Point > polygonCorners(const unsigned int num_sides, const libMesh::Real radius, const unsigned int axis)
Get the corner coordinates of a regular 2-D polygon, assuming a face of the polygon is parallel to th...
Real distance(const Point &p)
auto max(const L &left, const R &right)
TypeVector< Real > unit() const
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template cos(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(cos
bool pointOnEdge(const libMesh::Point &point, const std::vector< libMesh::Point > &corners, const unsigned int axis)
Whether a point is on the edge of a 2-D polygon in the plane perpendicular to the specified axis...
const Point & min() const
libMesh::Point unitVector(const libMesh::Point &pt, const std::string &name)
Get the unit vector for a point parameter.
bool isPointZero(const libMesh::Point &pt)
Check whether a point is equal to zero.
std::pair< unsigned int, unsigned int > projectedIndices(const unsigned int axis)
Get the indices of the plane perpendicular to the specified axis.
bool segmentsIntersect(const Point &p1, const Point &p2, const Point &p3, const Point &p4)
Check if the line segment p1-p2 intersects with line segment p3-p4 (only working in 2D (x-y plane))...
libMesh::Real distanceFromLine(const libMesh::Point &pt, const libMesh::Point &line0, const libMesh::Point &line1)
Compute the distance from a 3-D line, provided in terms of two points on the line.
libMesh::Real minDistanceToPoints(const libMesh::Point &pt, const std::vector< libMesh::Point > &candidates, const unsigned int axis)
Get the minimum distance from a point to another set of points, in the plane perpendicular to the spe...
std::vector< libMesh::Point > boxCorners(const libMesh::BoundingBox &box, const libMesh::Real factor)
Get corner points of a bounding box, with side length re-scaled.
Real pointSegmentDistanceSq(const Point &point, const Point &a, const Point &b)
Compute the squared distance from a point to a 3-D line segment.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
bool pointInPolygon(const libMesh::Point &point, const std::vector< libMesh::Point > &corners, const unsigned int axis)
Whether a point is in 2-D a polygon in the plane perpendicular to the specified axis, given by corner points.
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
auto norm(const T &a)
IntRange< T > make_range(T beg, T end)
bool arePointsColinear(const Point &p1, const Point &p2, const Point &p3)
Check if three points are colinear.
T clamp(const T &x, T2 lowerlimit, T2 upperlimit)
Definition: MathUtils.h:314
const Point & max() const
auto min(const L &left, const R &right)
auto index_range(const T &sizable)
unsigned int idx(const ElemType type, const unsigned int nx, const unsigned int i, const unsigned int j)