libMesh
mesh_triangle_holes.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 #include "libmesh/libmesh_config.h"
20 
21 // Local includes
22 #include "libmesh/mesh_triangle_holes.h"
23 
24 #include "libmesh/boundary_info.h"
25 #include "libmesh/elem.h"
26 #include "libmesh/int_range.h"
27 #include "libmesh/mesh_base.h"
28 #include "libmesh/mesh_serializer.h"
29 #include "libmesh/node.h"
30 #include "libmesh/parallel_algebra.h" // Packing<Point>
31 #include "libmesh/simple_range.h"
32 
33 // TIMPI includes
34 #include "timpi/parallel_implementation.h" // broadcast
35 
36 // C++ includes
37 #include <algorithm>
38 
39 namespace
40 {
41  using namespace libMesh;
42 
43  int signof(Real val) {
44  return (0 < val) - (val < 0);
45  }
46 
47  // Return 1 iff counter-clockwise turn
48  // Return -1 iff clockwise turn
49  // Return 0 iff collinear
50  int orientation(const Point & p0,
51  const Point & p1,
52  const Point & p2)
53  {
54  const Real detleft = (p0(0)-p2(0))*(p1(1)-p2(1));
55  const Real detright = (p0(1)-p2(1))*(p1(0)-p2(0));
56 
57  return signof(detleft - detright);
58  }
59 
60  // Same, but for the ray target as it goes to infinity
61  int ray_orientation(const Point & p0,
62  const Point & p1,
63  const Point & source,
64  const Point & ray_target)
65  {
66  const Point rayvec = ray_target - source;
67  const Point edgevec = p1 - p0;
68  const Real det = edgevec(0)*rayvec(1)-edgevec(1)*rayvec(0);
69 
70  return signof(det);
71  }
72 
73  bool is_intersection(const Point & source,
74  const Point & ray_target,
75  const Point & edge_pt0,
76  const Point & edge_pt1)
77  {
78  int orient_st0 = orientation(source, ray_target, edge_pt0);
79  int orient_st1 = orientation(source, ray_target, edge_pt1);
80  int orient_edge_s = orientation(edge_pt0, edge_pt1, source);
81  int orient_edge_t = ray_orientation(edge_pt0, edge_pt1, source, ray_target);
82 
83  // Intersection on interior
84  if ((orient_st0 == -orient_st1) &&
85  (orient_edge_s != orient_edge_t))
86  return true;
87 
88  // Ray intersects edge_pt1
89  if (orient_st1 == 0)
90  return true;
91 
92  // Source is on line; we don't count that
93  // if (orient_edge_s == 0)
94  // Ray is parallel to edge; no intersection;
95  // if (orient_edge_t == 0)
96  // Ray intersects edge_pt0; we don't count that
97  // if (orient_st0 == 0)
98 
99  return false;
100  }
101 
102  // Returns a positive distance iff the ray from source in the
103  // direction of ray_target intersects the edge from pt0
104  // (non-inclusive) to pt1 (inclusive), -1 otherwise.
105  //
106  // If the intersection is a "glancing" one at a corner, return -1.
107  Real find_intersection(const Point & source,
108  const Point & ray_target,
109  const Point & edge_pt0,
110  const Point & edge_pt1,
111  const Point & edge_pt2)
112  {
113  // Quick and more numerically stable check
114  if (!is_intersection(source, ray_target, edge_pt0, edge_pt1))
115  return -1;
116 
117  // Calculate intersection parameters (fractions of the distance
118  // along each segment)
119  const Real raydx = ray_target(0)-source(0),
120  raydy = ray_target(1)-source(1),
121  edgedx = edge_pt1(0)-edge_pt0(0),
122  edgedy = edge_pt1(1)-edge_pt0(1);
123  const Real denom = edgedx * raydy - edgedy * raydx;
124 
125  // divide-by-zero means the segments are parallel
126  if (denom == 0)
127  return -1;
128 
129  const Real one_over_denom = 1 / denom;
130 
131  const Real targetsdx = edge_pt1(0)-ray_target(0),
132  targetsdy = edge_pt1(1)-ray_target(1);
133 
134  const Real t_num = targetsdx * raydy -
135  targetsdy * raydx;
136  const Real t = t_num * one_over_denom;
137 
138  // There's an intersection between the ray line and the edge?
139  if (t >= 0 && t < 1)
140  {
141  // There's an intersection right on a vertex? We'll count it
142  // if and only if it isn't a "double-intersection", if the
143  // *next* edge in line is on the other side of our ray.
144  if (!t)
145  {
146  const Real prevdx = edge_pt0(0)-ray_target(0),
147  prevdy = edge_pt0(1)-ray_target(1);
148  const Real p_num = prevdx * raydy -
149  prevdy * raydx;
150 
151  const Real nextdx = edge_pt2(0)-ray_target(0),
152  nextdy = edge_pt2(1)-ray_target(1);
153  const Real n_num = nextdx * raydy -
154  nextdy * raydx;
155 
156  if (signof(p_num) != -signof(n_num))
157  return -1;
158  }
159 
160  const Real u_num = targetsdx * edgedy - targetsdy * edgedx;
161  const Real u = u_num * one_over_denom;
162  const Real ray_fraction = (1-u);
163 
164  // Intersection is in the other direction!?
165  if (ray_fraction < 0)
166  return -1;
167 
168  const Real distance =
169  ray_fraction * std::sqrt(raydx*raydx + raydy*raydy);
170  return distance;
171  }
172 
173  return -1;
174  }
175 }
176 
177 
178 namespace libMesh
179 {
180 
181 //
182 // Hole member functions
183 //
185 {
186  return this->areavec().norm() / 2;
187 }
188 
189 
191 {
192  const unsigned int np = this->n_points();
193 
194  if (np < 3)
195  return 0;
196 
197  const Point p0 = this->point(0);
198 
199  // Every segment (p_{i-1},p_i) from i=2 on defines a triangle w.r.t.
200  // p_0. Add up the cross products of those triangles. We'll save
201  // the division by 2 and the norm for the end.
202  //
203  // Your hole points had best be coplanar, but this should work
204  // regardless of which plane they're in. If you're in the XY plane,
205  // then the standard counter-clockwise hole point ordering gives you
206  // a positive areavec(2);
207 
208  RealGradient areavec = 0;
209 
210  for (unsigned int i=2; i != np; ++i)
211  {
212  const Point e_0im = this->point(i-1) - p0,
213  e_0i = this->point(i) - p0;
214 
215  areavec += e_0i.cross(e_0im);
216  }
217 
218  return areavec;
219 }
220 
221 
222 
223 std::vector<Real>
225  Point ray_target) const
226 {
227  const auto np = this->n_points();
228 
229  std::vector<Real> intersection_distances;
230 
231  for (auto i : make_range(np))
232  {
233  const Point & p0 = this->point(i),
234  & p1 = this->point((i+1)%np),
235  & p2 = this->point((i+2)%np);
236  const Real intersection_distance =
237  find_intersection(ray_start, ray_target, p0, p1, p2);
238  if (intersection_distance >= 0)
239  intersection_distances.push_back
240  (intersection_distance);
241  }
242 
243  return intersection_distances;
244 }
245 
246 
247 
249 {
250  // Start with the vertex average
251 
252  // Turns out "I'm a fully compliant C++17 compiler!" doesn't
253  // mean "I have a full C++17 standard library!"
254  // inside = std::reduce(points.begin(), points.end());
255  Point inside = 0;
256  for (auto i : make_range(this->n_points()))
257  inside += this->point(i);
258 
259  inside /= this->n_points();
260 
261  // Count the number of intersections with a ray to the right,
262  // keep track of how far they are
263  Point ray_target = inside + Point(1);
264  std::vector<Real> intersection_distances =
265  this->find_ray_intersections(inside, ray_target);
266 
267  // The vertex average isn't on the interior, and we found no
268  // intersections to the right? Try looking to the left.
269  if (!intersection_distances.size())
270  {
271  ray_target = inside - Point(1);
272  intersection_distances =
273  this->find_ray_intersections(inside, ray_target);
274  }
275 
276  // I'd make this an assert, but I'm not 100% confident we can't
277  // get here via some kind of FP error on a weird hole shape.
278  libmesh_error_msg_if
279  (!intersection_distances.size(),
280  "Can't find a center for a MeshedHole!");
281 
282  if (intersection_distances.size() % 2)
283  return inside;
284 
285  // The vertex average is outside. So go from the vertex average to
286  // the closest edge intersection, then halfway to the next-closest.
287 
288  // Find the nearest first.
289  Real min_distance = std::numeric_limits<Real>::max(),
290  second_distance = std::numeric_limits<Real>::max();
291  for (Real d : intersection_distances)
292  if (d < min_distance)
293  {
294  second_distance = min_distance;
295  min_distance = d;
296  }
297 
298  const Point ray = ray_target - inside;
299  inside += ray * (min_distance + second_distance)/2;
300 
301  return inside;
302 }
303 
304 
306 {
307  // Count the number of intersections with a ray to the right,
308  // keep track of how far they are
309  Point ray_target = p + Point(1);
310  std::vector<Real> intersection_distances =
311  this->find_ray_intersections(p, ray_target);
312 
313  // Odd number of intersections == we're inside
314  // Even number == we're outside
315  return intersection_distances.size() % 2;
316 }
317 
318 
319 
320 //
321 // PolygonHole member functions
322 //
324  Real radius,
325  unsigned int n_points_in) :
326  _center(center),
327  _radius(radius),
328  _n_points(n_points_in)
329 {}
330 
331 
333 {
334  return _n_points;
335 }
336 
337 
339 {
340  // The nth point lies at the angle theta = 2 * pi * n / _n_points
341  const Real theta = static_cast<Real>(n) * 2.0 * libMesh::pi / static_cast<Real>(_n_points);
342 
343  return Point(_center(0) + _radius*std::cos(theta), // x=r*cos(theta)
344  _center(1) + _radius*std::sin(theta), // y=r*sin(theta)
345  0.);
346 }
347 
348 
350 {
351  // The center of the hole is definitely inside.
352  return _center;
353 }
354 
355 
356 //
357 // AffineHole member functions
358 //
360 {
361  return this->transform(_underlying.point(n));
362 }
363 
364 
366 {
367  return this->transform(_underlying.inside());
368 }
369 
370 
372 {
373  const Real cos_a = std::cos(_angle);
374  const Real sin_a = std::sin(_angle);
375  return Point(p(0)*cos_a-p(1)*sin_a + _shift(0),
376  p(1)*cos_a+p(1)*sin_a + _shift(1));
377 }
378 
379 
380 //
381 // ArbitraryHole member functions
382 //
384  std::vector<Point> points)
385  : _center(center),
386  _points(std::move(points))
387 {
388  _segment_indices.push_back(0);
389  _segment_indices.push_back(_points.size());
390 }
391 
392 
394  std::vector<Point> points,
395  std::vector<unsigned int> segment_indices)
396  : _center(center),
397  _points(std::move(points)),
398  _segment_indices(std::move(segment_indices))
399 {}
400 
401 
403  : _points(std::move(points))
404 {
405  _segment_indices.push_back(0);
406  _segment_indices.push_back(_points.size());
408 }
409 
410 
412  : _center(orig.inside())
413 {
414  const unsigned int np = orig.n_points();
415  _points.reserve(np);
416  for (auto i : make_range(np))
417  _points.push_back(orig.point(i));
418 }
419 
420 
422 {
423  return _points.size();
424 }
425 
426 
428 {
429  libmesh_assert_less (n, _points.size());
430  return _points[n];
431 }
432 
433 
435 {
436  return _center;
437 }
438 
439 
441 {
442  return _segment_indices;
443 }
444 
445 
446 //
447 // MeshedHole member functions
448 //
449 
450 
452  std::set<std::size_t> ids)
453  : _center(std::numeric_limits<Real>::max())
454 {
455  // We'll want to do this on one processor and broadcast to the rest;
456  // otherwise we can get out of sync by doing things like using
457  // pointers as keys.
458  libmesh_parallel_only(mesh.comm());
459 
460  MeshSerializer serial(const_cast<MeshBase &>(mesh),
461  /* serial */ true, /* only proc 0 */ true);
462 
463  // Try to keep in sync even if we throw an error on proc 0, so we
464  // can examine errors in our unit tests in parallel too.
465  std::string error_reported;
466 
467  auto report_error = [&mesh, &error_reported](std::string er) {
468  error_reported = std::move(er);
469  mesh.comm().broadcast(error_reported);
470  libmesh_error_msg(error_reported);
471  };
472 
473  if (mesh.processor_id() != 0)
474  {
475  // Make sure proc 0 didn't just fail
476  mesh.comm().broadcast(error_reported);
477  libmesh_error_msg_if(!error_reported.empty(), error_reported);
478 
479  // Receive the points proc 0 will send later
482  return;
483  }
484 
485  // We'll find all the line segments first, then stitch them together
486  // afterward. If the line segments come from 2D element sides then
487  // we'll label their edge_type as "1" for clockwise orientation
488  // around the element or "2" for CCW, to make it easier to detect
489  // and scream about cases where we have a disconnected outer
490  // boundary.
491  std::multimap<const Node *,
492  std::pair<const Node *, int>> hole_edge_map;
493 
494  // If we're looking at higher-order elements, we have mid-edge edge
495  // nodes to worry about. hole_midpoint_map[{m,n}][i] should give us
496  // the ith mid-edge node traveling from vertex m to vertex n
497  std::map<std::pair<const Node *, const Node *>,
498  std::vector<const Node *>> hole_midpoint_map;
499 
500  std::vector<boundary_id_type> bcids;
501 
502  const BoundaryInfo & boundary_info = mesh.get_boundary_info();
503 
504  for (const auto & elem : mesh.active_element_ptr_range())
505  {
506  if (elem->dim() == 1)
507  {
508  if (ids.empty() || ids.count(elem->subdomain_id()))
509  {
510  hole_edge_map.emplace(elem->node_ptr(0),
511  std::make_pair(elem->node_ptr(1),
512  /*edge*/ 0));
513  hole_edge_map.emplace(elem->node_ptr(1),
514  std::make_pair(elem->node_ptr(0),
515  /*edge*/ 0));
516  if (elem->type() == EDGE3)
517  {
518  hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(0),
519  elem->node_ptr(1)),
520  std::vector<const Node *>{elem->node_ptr(2)});
521  hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(1),
522  elem->node_ptr(0)),
523  std::vector<const Node *>{elem->node_ptr(2)});
524  }
525  else if (elem->type() == EDGE4)
526  {
527  hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(0),
528  elem->node_ptr(1)),
529  std::vector<const Node *>{elem->node_ptr(2),
530  elem->node_ptr(3)});
531  hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(1),
532  elem->node_ptr(0)),
533  std::vector<const Node *>{elem->node_ptr(3),
534  elem->node_ptr(2)});
535  }
536  else
537  libmesh_assert_equal_to(elem->default_side_order(), 1);
538  }
539  continue;
540  }
541 
542  if (elem->dim() == 2)
543  {
544  const auto ns = elem->n_sides();
545  for (auto s : make_range(ns))
546  {
547  boundary_info.boundary_ids(elem, s, bcids);
548 
549  bool add_edge = false;
550  if (!elem->neighbor_ptr(s) && ids.empty())
551  add_edge = true;
552 
553  if (!add_edge)
554  for (auto b : bcids)
555  if (ids.count(b))
556  add_edge = true;
557 
558  if (add_edge)
559  {
560  hole_edge_map.emplace(elem->node_ptr(s),
561  std::make_pair(elem->node_ptr((s+1)%ns),
562  /*counter-CW*/ 2));
563  // Do we really need to support flipped 2D elements?
564  hole_edge_map.emplace(elem->node_ptr((s+1)%ns),
565  std::make_pair(elem->node_ptr(s),
566  /*clockwise*/ 1));
567 
568  if (elem->default_side_order() == 2)
569  {
570  hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(s),
571  elem->node_ptr((s+1)%ns)),
572  std::vector<const Node *>{elem->node_ptr(s+ns)});
573  hole_midpoint_map.emplace(std::make_pair(elem->node_ptr((s+1)%ns),
574  elem->node_ptr(s)),
575  std::vector<const Node *>{elem->node_ptr(s+ns)});
576  }
577  else
578  libmesh_assert_equal_to(elem->default_side_order(), 1);
579 
580  continue;
581  }
582  }
583  }
584  }
585 
586  if (hole_edge_map.empty())
587  report_error("No valid hole edges found in mesh!");
588 
589  // Function to pull a vector of points out of the map; a loop of
590  // edges connecting these points defines a hole boundary. If the
591  // mesh has multiple boundaries (e.g. because it had holes itself),
592  // then a random vector will be extracted; this function will be
593  // called multiple times so that the various options can be
594  // compared. We choose the largest option.
595  auto extract_edge_vector =
596  [&report_error, &hole_edge_map, &hole_midpoint_map]() {
597  std::tuple<std::vector<const Node *>, std::vector<const Node *>, int>
598  hole_points_and_edge_type
599  {{hole_edge_map.begin()->first, hole_edge_map.begin()->second.first},
600  {}, hole_edge_map.begin()->second.second};
601 
602  auto & hole_points = std::get<0>(hole_points_and_edge_type);
603  auto & midpoint_points = std::get<1>(hole_points_and_edge_type);
604  int & edge_type = std::get<2>(hole_points_and_edge_type);
605 
606  // We won't be needing to search for this edge
607  hole_edge_map.erase(hole_points.front());
608 
609  // Sort the remaining edges into a connected order
610  for (const Node * last = hole_points.front(),
611  * n = hole_points.back();
612  n != hole_points.front();
613  last = n,
614  n = hole_points.back())
615  {
616  auto [next_it_begin, next_it_end] = hole_edge_map.equal_range(n);
617 
618  if (std::distance(next_it_begin, next_it_end) != 2)
619  report_error("Bad edge topology found by MeshedHole");
620 
621  const Node * next = nullptr;
622  for (const auto & [key, val] : as_range(next_it_begin, next_it_end))
623  {
624  libmesh_assert_equal_to(key, n);
625  libmesh_ignore(key);
626  libmesh_assert_not_equal_to(val.first, n);
627 
628  // Don't go backwards on the edge we just traversed
629  if (val.first == last)
630  continue;
631 
632  // We can support mixes of Edge and Tri-side edges, but we
633  // can't do proper error detection on flipped triangles.
634  if (val.second != edge_type &&
635  val.second != 0)
636  {
637  if (!edge_type)
638  edge_type = val.second;
639  else
640  report_error("MeshedHole sees inconsistent triangle orientations on boundary");
641  }
642  next = val.first;
643  }
644 
645  // We should never hit the same n twice!
646  hole_edge_map.erase(next_it_begin, next_it_end);
647 
648  hole_points.push_back(next);
649  }
650 
651  for (auto i : make_range(hole_points.size()-1))
652  {
653  const auto & midpoints = hole_midpoint_map[{hole_points[i],hole_points[i+1]}];
654  midpoint_points.insert(midpoint_points.end(),
655  midpoints.begin(), midpoints.end());
656  }
657 
658  hole_points.pop_back();
659 
660  return hole_points_and_edge_type;
661  };
662 
663  /*
664  * If it's not obvious which loop we find is really the loop we
665  * want, then we should die with a nice error message.
666  */
667  int n_negative_areas = 0,
668  n_positive_areas = 0,
669  n_edgeelem_loops = 0;
670 
671  std::vector<const Node *> outer_hole_points, outer_mid_points;
672  int outer_edge_type = -1;
673  Real twice_outer_area = 0,
674  abs_twice_outer_area = 0;
675 
676 #ifdef DEBUG
677  // Area and edge type, for error reporting
678  std::vector<std::pair<Real, int>> areas;
679 #endif
680 
681  while (!hole_edge_map.empty()) {
682  auto [hole_points, mid_points, edge_type] = extract_edge_vector();
683 
684  if (edge_type == 0)
685  {
686  ++n_edgeelem_loops;
687  if (n_edgeelem_loops > 1)
688  report_error("MeshedHole is confused by multiple loops of Edge elements");
689  if (n_positive_areas || n_negative_areas)
690  report_error("MeshedHole is confused by meshes with both Edge and 2D-side boundaries");
691  }
692 
693  const std::size_t n_hole_points = hole_points.size();
694  if (n_hole_points < 3)
695  report_error("Loop with only " + std::to_string(n_hole_points) +
696  " hole edges found in mesh!");
697 
698  Real twice_this_area = 0;
699  const Point p0 = *hole_points[0];
700  for (unsigned int i=2; i != n_hole_points; ++i)
701  {
702  const Point e_0im = *hole_points[i-1] - p0,
703  e_0i = *hole_points[i] - p0;
704 
705  twice_this_area += e_0i.cross(e_0im)(2);
706  }
707 
708  auto abs_twice_this_area = std::abs(twice_this_area);
709 
710  if (((twice_this_area > 0) && edge_type == 2) ||
711  ((twice_this_area < 0) && edge_type == 1))
712  ++n_positive_areas;
713  else if (edge_type != 0)
714  ++n_negative_areas;
715 
716 #ifdef DEBUG
717  areas.push_back({twice_this_area/2,edge_type});
718 #endif
719 
720  if (abs_twice_this_area > abs_twice_outer_area)
721  {
722  twice_outer_area = twice_this_area;
723  abs_twice_outer_area = abs_twice_this_area;
724  outer_hole_points = std::move(hole_points);
725  outer_mid_points = std::move(mid_points);
726  outer_edge_type = edge_type;
727  }
728  }
729 
730  _points.resize(outer_hole_points.size());
731  std::transform(outer_hole_points.begin(),
732  outer_hole_points.end(),
733  _points.begin(),
734  [](const Node * n){ return Point(*n); });
735  _midpoints.resize(outer_mid_points.size());
736  std::transform(outer_mid_points.begin(),
737  outer_mid_points.end(),
738  _midpoints.begin(),
739  [](const Node * n){ return Point(*n); });
740 
741  if (!twice_outer_area)
742  report_error("Zero-area MeshedHoles are not currently supported");
743 
744  // We ordered ourselves counter-clockwise? But a hole is expected
745  // to be clockwise, so use the reverse order.
746  if (twice_outer_area > 0)
747  {
748  std::reverse(_points.begin(), _points.end());
749 
750  // Our midpoints are numbered e.g.
751  // (01a)(01b)(12a)(12b)(23a)(23b)(30a)(30b) for points 0123, but
752  // if we reverse to get 3210 then we want our midpoints to be
753  // (23b)(23a)(12b)(12a)(01b)(01a)(30b)(30a)
754  const unsigned int n_midpoints = _midpoints.size() / _points.size();
755  auto split_it = _midpoints.end() - n_midpoints;
756  std::reverse(_midpoints.begin(), split_it);
757  std::reverse(split_it, _midpoints.end());
758  }
759 
760 #ifdef DEBUG
761  auto print_areas = [areas](){
762  libMesh::out << "Found boundary areas:\n";
763  static const std::vector<std::string> edgenames {"E","CW","CCW"};
764  for (auto area : areas)
765  libMesh::out << '(' << edgenames[area.second] << ' ' <<
766  area.first << ')';
767  libMesh::out << std::endl;
768  };
769 #else
770  auto print_areas = [](){};
771 #endif
772 
773  if (((twice_outer_area > 0) && outer_edge_type == 2) ||
774  ((twice_outer_area < 0) && outer_edge_type == 1))
775  {
776  if (n_positive_areas > 1)
777  {
778  print_areas();
779  report_error("MeshedHole found " +
780  std::to_string(n_positive_areas) +
781  " counter-clockwise boundaries and cannot choose one!");
782  }
783 
784  }
785  else if (outer_edge_type != 0)
786  {
787  if (n_negative_areas > 1)
788  {
789  print_areas();
790  report_error("MeshedHole found " +
791  std::to_string(n_negative_areas) +
792  " clockwise boundaries and cannot choose one!");
793  }
794 
795  }
796 
797  // Hey, no errors! Broadcast that empty string.
798  mesh.comm().broadcast(error_reported);
801 }
802 
803 
805 {
806  return _points.size();
807 }
808 
809 
811 {
812  libmesh_assert (!(_midpoints.size() % _points.size()));
813  return _midpoints.size() / _points.size();
814 }
815 
816 
818 {
819  libmesh_assert_less (n, _points.size());
820  return _points[n];
821 }
822 
823 
825  const unsigned int n) const
826 {
827  const unsigned int n_mid = this->n_midpoints();
828  libmesh_assert_less (m, n_mid);
829  libmesh_assert_less (n, _points.size());
830  return _midpoints[n*n_mid+m];
831 }
832 
833 
835 {
836  // This is expensive to compute, so only do it when we first need it
837  if (_center(0) == std::numeric_limits<Real>::max())
838  _center = this->calculate_inside_point();
839 
840  return _center;
841 }
842 
843 
844 
845 } // namespace libMesh
virtual Point point(const unsigned int n) const override
Return the nth point defining the hole.
std::vector< Point > _points
The sorted vector of points which makes up the hole.
A Node is like a Point, but with more information.
Definition: node.h:52
const Real radius
auto norm() const -> decltype(std::norm(T()))
Definition: type_vector.h:907
virtual unsigned int n_points() const override
The number of geometric points which define the hole.
An abstract class for defining a 2-dimensional hole.
std::vector< Point > _points
Reference to the vector of points which makes up the hole.
ArbitraryHole(const Point &center, std::vector< Point > points)
The fastest constructor requires a point which lies in the interior of the hole and a reference to a ...
std::vector< Real > find_ray_intersections(Point ray_start, Point ray_target) const
Helper function for contains(), also useful for MeshedHole::inside()
Real area() const
Return the area of the hole.
MeshBase & mesh
const Parallel::Communicator & comm() const
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
The libMesh namespace provides an interface to certain functionality in the library.
virtual Point inside() const override
Return an (arbitrary) point which lies inside the hole.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:165
MeshedHole(const MeshBase &mesh, std::set< std::size_t > ids={})
The constructor requires a mesh defining the hole, and optionally boundary+subdomain ids restricting ...
Real distance(const Point &p)
This is the MeshBase class.
Definition: mesh_base.h:75
virtual Point inside() const override
Return an (arbitrary) point which lies inside the hole.
void libmesh_ignore(const Args &...)
Point transform(const Point &p) const
Rotate-and-shift equations.
void report_error(const char *file, int line, const char *date, const char *time, std::ostream &os=libMesh::err)
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
libmesh_assert(ctx)
virtual unsigned int n_points() const override
The number of geometric points which define the hole.
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
Definition: type_vector.h:884
virtual unsigned int n_midpoints() const override
The number of geometric midpoints along each of the sides defining the hole.
virtual unsigned int n_points() const override
The number of geometric points which define the hole.
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
Point calculate_inside_point() const
Calculate an inside point based on our boundary.
virtual std::vector< unsigned int > segment_indices() const override
Starting indices of points for a hole with multiple disconnected boundaries.
static const unsigned int next[3]
A lookup table for the increment modulo 3 operation, for iterating through the three nodes per elemen...
bool contains(Point p) const
Return true iff p lies inside the hole.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual Point point(const unsigned int n) const override
Return the nth point defining the hole.
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
virtual Point inside() const override
Return an (arbitrary) point which lies inside the hole.
OStreamProxy out
std::vector< Point > _midpoints
The sorted vector of midpoints in between points along the edges of the hole.
virtual Point point(const unsigned int n) const override
Return the nth point defining the hole.
virtual Point inside() const override
Return an (arbitrary) point which lies inside the hole.
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
virtual Point point(const unsigned int n) const override
Return the nth point defining the hole.
RealGradient areavec() const
Return a vector with right-hand-rule orientation and length of twice area() squared.
virtual Point midpoint(const unsigned int m, const unsigned int n) const override
Return the midpoint m along the side n defining the hole.
Point _center
arbitrary (x,y) location inside the hole
virtual Point point(const unsigned int n) const =0
Return the nth point defining the hole.
processor_id_type processor_id() const
PolygonHole(const Point &center, Real radius, unsigned int n_points)
Constructor specifying the center, radius, and number of points which comprise the hole...
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
virtual unsigned int n_points() const =0
The number of geometric points which define the hole.
const Real pi
.
Definition: libmesh.h:299