https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
17namespace geom_utils
18{
19
20bool
21isPointZero(const Point & pt)
22{
23 const Point zero(0.0, 0.0, 0.0);
24 return pt.absolute_fuzzy_equals(zero);
25}
26
27Point
28unitVector(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
36Real
37minDistanceToPoints(const Point & pt,
38 const std::vector<Point> & candidates,
39 const unsigned int axis)
40{
41 const auto idx = projectedIndices(axis);
42
43 Real distance = std::numeric_limits<Real>::max();
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);
49 distance = std::min(d, distance);
50 }
51
52 return distance;
53}
54
55Point
56projectPoint(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
67Real
68projectedLineHalfSpace(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
81Real
82signedArea2D(const Point & pt1, const Point & pt2, const Point & pt3)
83{
84 // The shoelace determinant of the three corners is the half space of the line through the second
85 // and the third that the first lies in, taken in the plane perpendicular to z
86 return projectedLineHalfSpace(pt2, pt3, pt1, 2);
87}
88
89Real
90signedArea2D(const std::vector<Point> & polygon)
91{
92 Real twice_area = 0.0;
93 for (const auto i : index_range(polygon))
94 twice_area += signedArea2D(Point(), polygon[i], polygon[(i + 1) % polygon.size()]);
95
96 return twice_area;
97}
98
99bool
100pointInPolygon(const Point & point, const std::vector<Point> & corners, const unsigned int axis)
101{
102 const auto n_pts = corners.size();
103
104 std::vector<bool> negative_half_space;
105 std::vector<bool> positive_half_space;
106 for (const auto i : index_range(corners))
107 {
108 const int next = (i == n_pts - 1) ? 0 : i + 1;
109 const auto half = projectedLineHalfSpace(point, corners[i], corners[next], axis);
110 negative_half_space.push_back(half < 0);
111 positive_half_space.push_back(half > 0);
112 }
113
114 const bool negative = std::find(negative_half_space.begin(), negative_half_space.end(), true) !=
115 negative_half_space.end();
116 const bool positive = std::find(positive_half_space.begin(), positive_half_space.end(), true) !=
117 positive_half_space.end();
118
119 const bool in_polygon = !(negative && positive);
120 if (in_polygon)
121 return true;
122
123 if (pointOnEdge(point, corners, axis))
124 return true;
125
126 return false;
127}
128
129bool
130pointOnEdge(const Point & point, const std::vector<Point> & corners, const unsigned int axis)
131{
132 const auto n_pts = corners.size();
133 const auto idx = projectedIndices(axis);
134
135 constexpr Real tol = 1e-8;
136 for (const auto i : index_range(corners))
137 {
138 const int next = (i == n_pts - 1) ? 0 : i + 1;
139 const auto & pt1 = corners[i];
140 const auto & pt2 = corners[next];
141 const bool close_to_line = projectedDistanceFromLine(point, pt1, pt2, axis) < tol;
142
143 // we can stop early if we know we're not close to the line
144 if (!close_to_line)
145 continue;
146
147 // check that the point is "between" the two points; TODO: first pass
148 // we can just compare x and y coordinates
149 const bool between_points = (point(idx.first) >= std::min(pt1(idx.first), pt2(idx.first))) &&
150 (point(idx.first) <= std::max(pt1(idx.first), pt2(idx.first))) &&
151 (point(idx.second) >= std::min(pt1(idx.second), pt2(idx.second))) &&
152 (point(idx.second) <= std::max(pt1(idx.second), pt2(idx.second)));
153
154 // point needs to be close to the line AND "between" the two points
155 if (close_to_line && between_points)
156 return true;
157 }
158
159 return false;
160}
161
162std::pair<unsigned int, unsigned int>
163projectedIndices(const unsigned int axis)
164{
165 std::pair<unsigned int, unsigned int> indices;
166
167 if (axis == 0)
168 {
169 indices.first = 1;
170 indices.second = 2;
171 }
172 else if (axis == 1)
173 {
174 indices.first = 0;
175 indices.second = 2;
176 }
177 else
178 {
179 indices.first = 0;
180 indices.second = 1;
181 }
182
183 return indices;
184}
185
186Point
187projectedUnitNormal(Point pt1, Point pt2, const unsigned int axis)
188{
189 // project the points to the plane perpendicular to the axis
190 pt1(axis) = 0.0;
191 pt2(axis) = 0.0;
192
193 const auto i = projectedIndices(axis);
194
195 const Real dx = pt2(i.first) - pt1(i.first);
196 const Real dy = pt2(i.second) - pt1(i.second);
197
198 const Point normal = projectPoint(dy, -dx, axis);
199 const Point gap_line = pt2 - pt1;
200
201 const auto cross_product = gap_line.cross(normal);
202
203 if (cross_product(axis) > 0)
204 return normal.unit();
205 else
206 return projectPoint(-dy, dx, axis).unit();
207}
208
209Real
210distanceFromLine(const Point & pt, const Point & line0, const Point & line1)
211{
212 const Point a = pt - line0;
213 const Point b = pt - line1;
214 const Point c = line1 - line0;
215
216 return (a.cross(b).norm()) / c.norm();
217}
218
219Real
220projectedDistanceFromLine(Point pt, Point line0, Point line1, const unsigned int axis)
221{
222 // project all the points to the plane perpendicular to the axis
223 pt(axis) = 0.0;
224 line0(axis) = 0.0;
225 line1(axis) = 0.0;
226
227 return distanceFromLine(pt, line0, line1);
228}
229
230std::vector<Point>
231polygonCorners(const unsigned int num_sides, const Real radius, const unsigned int axis)
232{
233 std::vector<Point> corners;
234 const Real theta = 2.0 * M_PI / num_sides;
235 const Real first_angle = M_PI / 2.0 - theta / 2.0;
236
237 for (const auto i : make_range(num_sides))
238 {
239 const Real angle = first_angle + i * theta;
240 const Real x = radius * cos(angle);
241 const Real y = radius * sin(angle);
242
243 corners.push_back(projectPoint(x, y, axis));
244 }
245
246 return corners;
247}
248
249Point
250rotatePointAboutAxis(const Point & p, const Real angle, const Point & axis)
251{
252 const Real cos_theta = cos(angle);
253 const Real sin_theta = sin(angle);
254
255 Point pt;
256 const Real xy = axis(0) * axis(1);
257 const Real xz = axis(0) * axis(2);
258 const Real yz = axis(1) * axis(2);
259
260 const Point x_op(cos_theta + axis(0) * axis(0) * (1.0 - cos_theta),
261 xy * (1.0 - cos_theta) - axis(2) * sin_theta,
262 xz * (1.0 - cos_theta) + axis(1) * sin_theta);
263
264 const Point y_op(xy * (1.0 - cos_theta) + axis(2) * sin_theta,
265 cos_theta + axis(1) * axis(1) * (1.0 - cos_theta),
266 yz * (1.0 - cos_theta) - axis(0) * sin_theta);
267
268 const Point z_op(xz * (1.0 - cos_theta) - axis(1) * sin_theta,
269 yz * (1.0 - cos_theta) + axis(0) * sin_theta,
270 cos_theta + axis(2) * axis(2) * (1.0 - cos_theta));
271
272 pt(0) = x_op * p;
273 pt(1) = y_op * p;
274 pt(2) = z_op * p;
275 return pt;
276}
277
278std::vector<Point>
279boxCorners(const libMesh::BoundingBox & box, const Real factor)
280{
281 Point diff = (box.max() - box.min()) / 2.0;
282 const Point origin = box.min() + diff;
283
284 // Rescale side length of box by specified factor
285 diff *= factor;
286
287 // Vectors for sides of box
288 const Point dx(2.0 * diff(0), 0.0, 0.0);
289 const Point dy(0.0, 2.0 * diff(1), 0.0);
290 const Point dz(0.0, 0.0, 2.0 * diff(2));
291
292 std::vector<Point> verts(8, origin - diff);
293 const unsigned int pts_per_dim = 2;
294 for (const auto z : make_range(pts_per_dim))
295 for (const auto y : make_range(pts_per_dim))
296 for (const auto x : make_range(pts_per_dim))
297 verts[pts_per_dim * pts_per_dim * z + pts_per_dim * y + x] += x * dx + y * dy + z * dz;
298
299 return verts;
300}
301
302bool
303arePointsColinear(const Point & p1, const Point & p2, const Point & p3)
304{
305 const Point v1 = p2 - p1;
306 const Point v2 = p3 - p1;
307 const Point cross_prod = v1.cross(v2);
308
309 return MooseUtils::absoluteFuzzyEqual(cross_prod.norm(), 0.0);
310}
311
312bool
313segmentsIntersect(const Point & p1, const Point & p2, const Point & p3, const Point & p4)
314{
315 mooseAssert(
316 MooseUtils::absoluteFuzzyEqual(p1(2), 0.0) && MooseUtils::absoluteFuzzyEqual(p2(2), 0.0) &&
317 MooseUtils::absoluteFuzzyEqual(p3(2), 0.0) && MooseUtils::absoluteFuzzyEqual(p4(2), 0.0),
318 "segmentsIntersect only works in 2D (x-y plane)");
319
320 mooseAssert(MooseUtils::absoluteFuzzyGreaterThan((p1 - p2).norm(), 0.0) &&
321 MooseUtils::absoluteFuzzyGreaterThan((p3 - p4).norm(), 0.0),
322 "Zero length segments are not allowed in segmentsIntersect");
323
324 const Real a1 = p2(1) - p1(1);
325 const Real b1 = p1(0) - p2(0);
326 const Real c1 = p2(0) * p1(1) - p1(0) * p2(1);
327
328 const Real a2 = p4(1) - p3(1);
329 const Real b2 = p3(0) - p4(0);
330 const Real c2 = p4(0) * p3(1) - p3(0) * p4(1);
331
332 const Real denom = a1 * b2 - a2 * b1;
333 Point intersection_pt;
334 // Parallel case
335 if (MooseUtils::absoluteFuzzyEqual(denom, 0.0))
336 {
337 if (arePointsColinear(p1, p2, p3))
338 {
339 // for colinear segments, we construct a "virtual" intersection point using weighted average
340 // In that case, the virtual point will always lie outside of both segments unless they
341 // overlap
342 const Point p12 = (p1 + p2) / 2.0;
343 const Point p34 = (p3 + p4) / 2.0;
344 const Real dist12 = (p1 - p2).norm();
345 const Real dist34 = (p3 - p4).norm();
346 intersection_pt = p12 + (p34 - p12) * (dist12 / (dist12 + dist34));
347 }
348 else
349 return false;
350 }
351 else
352 {
353 intersection_pt = Point((b1 * c2 - b2 * c1) / denom, (a2 * c1 - a1 * c2) / denom, 0.0);
354 }
355
356 const Real ratio_p1p2 = (intersection_pt - p1) * (p2 - p1) / ((p2 - p1).norm_sq());
357 const Real ratio_p3p4 = (intersection_pt - p3) * (p4 - p3) / ((p4 - p3).norm_sq());
358
359 if (MooseUtils::absoluteFuzzyGreaterEqual(ratio_p1p2, 0.0) &&
360 MooseUtils::absoluteFuzzyLessEqual(ratio_p1p2, 1.0) &&
361 MooseUtils::absoluteFuzzyGreaterEqual(ratio_p3p4, 0.0) &&
362 MooseUtils::absoluteFuzzyLessEqual(ratio_p3p4, 1.0))
363 return true;
364 else
365 return false;
366}
367
368Real
369pointSegmentDistanceSq(const Point & point, const Point & a, const Point & b)
370{
371 const Point ab = b - a;
372 const auto length_sq = ab.norm_sq();
373 if (length_sq <= std::numeric_limits<Real>::epsilon())
374 return (point - a).norm_sq();
375
376 const auto t = std::clamp(((point - a) * ab) / length_sq, 0.0, 1.0);
377 const Point projection = a + t * ab;
378 return (point - projection).norm_sq();
379}
380
381Real
382pointTriangleDistanceSq(const Point & point, const Point & v0, const Point & v1, const Point & v2)
383{
384 const Point ab = v1 - v0;
385 const Point ac = v2 - v0;
386 const Point ap = point - v0;
387 const Real d1 = ab * ap;
388 const Real d2 = ac * ap;
389 if (d1 <= 0.0 && d2 <= 0.0)
390 return (point - v0).norm_sq();
391
392 const Point bp = point - v1;
393 const Real d3 = ab * bp;
394 const Real d4 = ac * bp;
395 if (d3 >= 0.0 && d4 <= d3)
396 return (point - v1).norm_sq();
397
398 const Real vc = d1 * d4 - d3 * d2;
399 if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0)
400 {
401 const Real v = d1 / (d1 - d3);
402 const Point projection = v0 + v * ab;
403 return (point - projection).norm_sq();
404 }
405
406 const Point cp = point - v2;
407 const Real d5 = ab * cp;
408 const Real d6 = ac * cp;
409 if (d6 >= 0.0 && d5 <= d6)
410 return (point - v2).norm_sq();
411
412 const Real vb = d5 * d2 - d1 * d6;
413 if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0)
414 {
415 const Real w = d2 / (d2 - d6);
416 const Point projection = v0 + w * ac;
417 return (point - projection).norm_sq();
418 }
419
420 const Real va = d3 * d6 - d5 * d4;
421 if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0)
422 {
423 const Point bc = v2 - v1;
424 const Real w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
425 const Point projection = v1 + w * bc;
426 return (point - projection).norm_sq();
427 }
428
429 const Real denom = 1.0 / (va + vb + vc);
430 const Real v = vb * denom;
431 const Real w = vc * denom;
432 const Point projection = v0 + ab * v + ac * w;
433 return (point - projection).norm_sq();
434}
435
436Real
437solidAngle(const Point & point, const Point & v0, const Point & v1, const Point & v2)
438{
439 const Point a = v0 - point;
440 const Point b = v1 - point;
441 const Point c = v2 - point;
442
443 const Real la = a.norm();
444 const Real lb = b.norm();
445 const Real lc = c.norm();
446
447 const Real numerator = a * (b.cross(c));
448 const Real denominator = la * lb * lc + (a * b) * lc + (b * c) * la + (c * a) * lb;
449
450 return 2.0 * std::atan2(numerator, denominator);
451}
452} // end namespace geom_utils
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
const Point & max() const
const Point & min() const
TypeVector< Real > unit() const
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.
Real pointSegmentDistanceSq(const Point &point, const Point &a, const Point &b)
Compute the squared distance from a point to a 3-D line segment.
std::pair< unsigned int, unsigned int > projectedIndices(const unsigned int axis)
Get the indices of the plane perpendicular to the specified axis.
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,...
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.
bool isPointZero(const libMesh::Point &pt)
Check whether a point is equal to zero.
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 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...
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.
libMesh::Point unitVector(const libMesh::Point &pt, const std::string &name)
Get the unit vector for a point parameter.
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).
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...
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,...
libMesh::Real signedArea2D(const libMesh::Point &pt1, const libMesh::Point &pt2, const libMesh::Point &pt3)
Twice the signed area of a triangle in the xy plane, which is positive when its corners are ordered c...
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::Point rotatePointAboutAxis(const libMesh::Point &p, const libMesh::Real angle, const libMesh::Point &axis)
Rotate point about an axis.
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.
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...
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.
bool arePointsColinear(const Point &p1, const Point &p2, const Point &p3)
Check if three points are colinear.
const Real radius
Real distance(const Point &p)