libMesh
Public Types | Public Member Functions | Public Attributes | Protected Member Functions | Protected Attributes | Private Attributes | List of all members
libMesh::Poly2TriTriangulator Class Reference

A C++ interface between LibMesh and the poly2tri library, with custom code for Steiner point insertion. More...

#include <poly2tri_triangulator.h>

Inheritance diagram for libMesh::Poly2TriTriangulator:
[legend]

Public Types

enum  TriangulationType { GENERATE_CONVEX_HULL = 0, PSLG = 1, INVALID_TRIANGULATION_TYPE }
 The TriangulationType is used with the general triangulate function defined below. More...
 

Public Member Functions

 Poly2TriTriangulator (UnstructuredMesh &mesh, dof_id_type n_boundary_nodes=DofObject::invalid_id)
 The constructor. More...
 
virtual ~Poly2TriTriangulator ()
 Empty destructor. More...
 
virtual void triangulate () override
 Internally, this calls the poly2tri triangulation code in a loop, inserting our owner Steiner points as necessary to promote mesh quality. More...
 
virtual void set_desired_area_function (FunctionBase< Real > *desired) override
 Set a function giving desired triangle area as a function of position. More...
 
virtual FunctionBase< Real > * get_desired_area_function () override
 Get the function giving desired triangle area as a function of position, or nullptr if no such function has been set. More...
 
virtual void set_refine_boundary_allowed (bool refine_bdy_allowed) override
 Set whether or not the triangulation is allowed to refine the mesh boundary when refining the interior. More...
 
virtual bool refine_boundary_allowed () const override
 Get whether or not the triangulation is allowed to refine the mesh boundary when refining the interior. More...
 
ElemTypeelem_type ()
 Sets and/or gets the desired element type. More...
 
Realdesired_area ()
 Sets and/or gets the desired triangle area. More...
 
Realminimum_angle ()
 Sets and/or gets the minimum desired angle. More...
 
TriangulationTypetriangulation_type ()
 Sets and/or gets the desired triangulation type. More...
 
bool & insert_extra_points ()
 Sets and/or gets the flag for inserting add'l points. More...
 
void set_interpolate_boundary_points (int n_points)
 Complicated setter, for compatibility with insert_extra_points() More...
 
int get_interpolate_boundary_points () const
 Complicated getter, for compatibility with insert_extra_points() More...
 
bool & smooth_after_generating ()
 Sets/gets flag which tells whether to do two steps of Laplace mesh smoothing after generating the grid. More...
 
bool & quiet ()
 Whether not to silence internal messages to stdout. More...
 
void attach_hole_list (const std::vector< Hole *> *holes)
 Attaches a vector of Hole* pointers which will be meshed around. More...
 
void set_verify_hole_boundaries (bool v)
 Verifying that hole boundaries don't cross the outer boundary or each other is something like O(N_bdys^2*N_points_per_bdy^2), so we only do it if requested. More...
 
bool get_verify_hole_boundaries () const
 
void attach_boundary_marker (const std::vector< int > *markers)
 Attaches boundary markers. More...
 
void attach_region_list (const std::vector< Region *> *regions)
 Attaches regions for using attribute to set subdomain IDs and better controlling the triangle sizes within the regions. More...
 
void set_auto_area_function (const Parallel::Communicator &comm, const unsigned int num_nearest_pts, const unsigned int power, const Real background_value, const Real background_eff_dist)
 Generate an auto area function based on spacing of boundary points. More...
 
bool has_auto_area_function ()
 Whether or not an auto area function has been set. More...
 
FunctionBase< Real > * get_auto_area_function ()
 Get the auto area function. More...
 
void calculate_auto_desired_area_samples (std::vector< Point > &function_points, std::vector< Real > &function_sizes, const Real &area_factor=1.5)
 The external boundary and all hole boundaries are collected. More...
 
void set_outer_boundary_ids (std::set< std::size_t > bdy_ids)
 A set of ids to allow on the outer boundary loop: interpreted as boundary ids of 2D elements and/or subdomain ids of 1D edges. More...
 
const std::set< std::size_t > & get_outer_boundary_ids () const
 

Public Attributes

std::vector< std::pair< unsigned int, unsigned int > > segments
 When constructing a PSLG, if the node numbers do not define the desired boundary segments implicitly through the ordering of the points, you can use the segments vector to specify the segments explicitly, Ex: unit square numbered counter-clockwise starting from origin segments[0] = (0,1) segments[1] = (1,2) segments[2] = (2,3) segments[3] = (3,0) (For the above case you could actually use the implicit ordering!) More...
 
std::vector< Pointsegment_midpoints
 When constructing a second-order triangulation from a second-order boundary, we may do the triangulation using first-order elements, in which case we need to save midpoint location data in order to reconstruct curvature along boundaries. More...
 

Protected Member Functions

bool is_refine_boundary_allowed (const BoundaryInfo &boundary_info, const Elem &elem, unsigned int side)
 Is refining this element's boundary side allowed? More...
 
void triangulate_current_points ()
 Triangulate the current mesh and hole points. More...
 
bool insert_refinement_points ()
 Add Steiner points as new mesh nodes, as necessary to refine an existing trangulation. More...
 
bool should_refine_elem (Elem &elem)
 Returns true if the given element ought to be refined according to current criteria. More...
 
void elems_to_segments ()
 Helper function to create PSLG segments from our other boundary-defining options (1D mesh edges, 2D mesh boundary sides), if no segments already exist. More...
 
void nodes_to_segments (dof_id_type max_node_id)
 Helper function to create PSLG segments from our node ordering, up to the maximum node id, if no segments already exist. More...
 
void insert_any_extra_boundary_points ()
 Helper function to add extra points (midpoints of initial segments) to a PSLG triangulation. More...
 
void increase_triangle_order ()
 Helper function to upconvert Tri3 to any higher order triangle type if requested via _elem_type. More...
 
void verify_holes (const Hole &outer_bdy)
 Helper function to check holes for intersections if requested. More...
 
unsigned int total_hole_points ()
 Helper function to count points in and verify holes. More...
 

Protected Attributes

UnstructuredMesh_mesh
 Reference to the mesh which is to be created by triangle. More...
 
const std::vector< Hole * > * _holes
 A pointer to a vector of Hole*s. More...
 
const std::vector< int > * _markers
 Boundary markers. More...
 
const std::vector< Region * > * _regions
 A pointer to a vector of Regions*s. More...
 
std::set< std::size_t > _bdy_ids
 A set of ids to allow on the outer boundary loop. More...
 
ElemType _elem_type
 The type of elements to generate. More...
 
Real _desired_area
 The desired area for the elements in the resulting mesh. More...
 
Real _minimum_angle
 Minimum angle in triangles. More...
 
TriangulationType _triangulation_type
 The type of triangulation to perform: choices are: convex hull PSLG. More...
 
bool _insert_extra_points
 Flag which tells whether or not to insert additional nodes before triangulation. More...
 
int _interpolate_boundary_points
 Flag which tells how many additional nodes should be inserted between each pair of original mesh points. More...
 
bool _smooth_after_generating
 Flag which tells whether we should smooth the mesh after it is generated. More...
 
bool _quiet
 Flag which tells if we want to suppress stdout outputs. More...
 
bool _verify_hole_boundaries
 Flag which tells if we want to check hole geometry. More...
 
std::unique_ptr< AutoAreaFunction_auto_area_function
 The auto area function based on the spacing of boundary points. More...
 

Private Attributes

std::map< const Hole *, std::unique_ptr< ArbitraryHole > > replaced_holes
 We might have to replace the user-provided holes with refined versions. More...
 
dof_id_type _n_boundary_nodes
 Keep track of how many mesh nodes are boundary nodes. More...
 
std::unique_ptr< FunctionBase< Real > > _desired_area_func
 Location-dependent area requirements. More...
 
bool _refine_bdy_allowed
 Whether to allow boundary refinement. More...
 

Detailed Description

A C++ interface between LibMesh and the poly2tri library, with custom code for Steiner point insertion.

Author
Roy H. Stogner
Date
2022

Definition at line 46 of file poly2tri_triangulator.h.

Member Enumeration Documentation

◆ TriangulationType

The TriangulationType is used with the general triangulate function defined below.

Enumerator
GENERATE_CONVEX_HULL 

First generate a convex hull from the set of points passed in, and then triangulate this set of points.

This is probably the most common type of usage.

PSLG 

Triangulate the interior of a Planar Straight Line Graph, which is defined implicitly by the order of the "points" vector: a straight line is assumed to lie between each successive pair of points, with an additional line joining the final and first points.

Explicitly telling the triangulator to add additional points may be important for this option.

INVALID_TRIANGULATION_TYPE 

Does nothing, used as a "null" value.

Definition at line 111 of file triangulator_interface.h.

112  {
119 
130  PSLG = 1,
131 
136  };
First generate a convex hull from the set of points passed in, and then triangulate this set of point...
Triangulate the interior of a Planar Straight Line Graph, which is defined implicitly by the order of...

Constructor & Destructor Documentation

◆ Poly2TriTriangulator()

libMesh::Poly2TriTriangulator::Poly2TriTriangulator ( UnstructuredMesh mesh,
dof_id_type  n_boundary_nodes = DofObject::invalid_id 
)
explicit

The constructor.

A reference to the mesh containing the points which are to be triangulated must be provided. The first n_boundary_nodes are expected to form a closed loop around the mesh domain; any subsequent nodes are expected to be interior nodes or in the middle of (internal hole or external) boundary segments.

If n_boundary_nodes is not supplied or is invalid_id then all mesh points are expected to be boundary polyline points.

Definition at line 354 of file poly2tri_triangulator.C.

357  _n_boundary_nodes(n_boundary_nodes),
358  _refine_bdy_allowed(true)
359 {
360 }
MeshBase & mesh
bool _refine_bdy_allowed
Whether to allow boundary refinement.
TriangulatorInterface(UnstructuredMesh &mesh)
The constructor.
dof_id_type _n_boundary_nodes
Keep track of how many mesh nodes are boundary nodes.

◆ ~Poly2TriTriangulator()

libMesh::Poly2TriTriangulator::~Poly2TriTriangulator ( )
virtualdefault

Empty destructor.

Defaulted in the .C so we can forward declare unique_ptr contents.

Member Function Documentation

◆ attach_boundary_marker()

void libMesh::TriangulatorInterface::attach_boundary_marker ( const std::vector< int > *  markers)
inlineinherited

Attaches boundary markers.

If segments is set, the number of markers must be equal to the size of segments, otherwise, it is equal to the number of points.

Definition at line 294 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_markers.

294 { _markers = markers; }
const std::vector< int > * _markers
Boundary markers.

◆ attach_hole_list()

void libMesh::TriangulatorInterface::attach_hole_list ( const std::vector< Hole *> *  holes)
inlineinherited

◆ attach_region_list()

void libMesh::TriangulatorInterface::attach_region_list ( const std::vector< Region *> *  regions)
inlineinherited

Attaches regions for using attribute to set subdomain IDs and better controlling the triangle sizes within the regions.

Definition at line 300 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_regions.

300 { _regions = regions; }
const std::vector< Region * > * _regions
A pointer to a vector of Regions*s.

◆ calculate_auto_desired_area_samples()

void libMesh::TriangulatorInterface::calculate_auto_desired_area_samples ( std::vector< Point > &  function_points,
std::vector< Real > &  function_sizes,
const Real area_factor = 1.5 
)
inherited

The external boundary and all hole boundaries are collected.

The centroid of each EDGE element is used as the point position and the desired area is calculated as the area of the equilateral triangle with the edge length as the length of the EDGE element times an area_factor (default is 1.5).

Definition at line 500 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_bdy_ids, libMesh::TriangulatorInterface::_holes, libMesh::TriangulatorInterface::_mesh, and libMesh::Real.

Referenced by libMesh::TriangulatorInterface::get_auto_area_function().

503 {
504  // Get the hole mesh of the outer boundary
505  // Holes should already be attached if applicable when this function is called
506  const TriangulatorInterface::MeshedHole bdry_mh { _mesh, this->_bdy_ids };
507  // Collect all the centroid points of the outer boundary segments
508  // and the corresponding element sizes
509  for (unsigned int i = 0; i < bdry_mh.n_points(); i++)
510  {
511  function_points.push_back((bdry_mh.point(i) + bdry_mh.point((i + 1) % bdry_mh.n_points())) /
512  Real(2.0));
513  function_sizes.push_back(
514  (bdry_mh.point(i) - bdry_mh.point((i + 1) % bdry_mh.n_points())).norm());
515  }
516  // If holes are present, do the same for the hole boundaries
517  if(_holes)
518  for (const Hole * hole : *_holes)
519  {
520  for (unsigned int i = 0; i < hole->n_points(); i++)
521  {
522  function_points.push_back(
523  (hole->point(i) + hole->point((i + 1) % hole->n_points())) / Real(2.0));
524  function_sizes.push_back(
525  (hole->point(i) - hole->point((i + 1) % hole->n_points())).norm());
526  }
527  }
528 
529  std::for_each(
530  function_sizes.begin(), function_sizes.end(), [&area_factor](Real & a) { a = a * a * area_factor * std::sqrt(3.0) / 4.0; });
531 
532 }
std::set< std::size_t > _bdy_ids
A set of ids to allow on the outer boundary loop.
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

◆ desired_area()

Real& libMesh::TriangulatorInterface::desired_area ( )
inlineinherited

Sets and/or gets the desired triangle area.

Set to zero to disable area constraint.

If a desired_area_function is set, then desired_area() should be used to set a minimum desired area; this will reduce "false negatives" by suggesting how finely to sample desired_area_function inside large triangles, where ideally the desired_area_function will be satisfied in the triangle interior and not just at the triangle vertices.

Definition at line 177 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_desired_area.

Referenced by libMesh::MeshTools::Generation::build_delaunay_square(), MeshTriangulationTest::commonSettings(), insert_refinement_points(), main(), should_refine_elem(), MeshTriangulationTest::testPoly2TriRefinementBase(), MeshTriangulationTest::testTriangulatorInterp(), and triangulate_domain().

177 {return _desired_area;}
Real _desired_area
The desired area for the elements in the resulting mesh.

◆ elem_type()

ElemType& libMesh::TriangulatorInterface::elem_type ( )
inlineinherited

Sets and/or gets the desired element type.

Definition at line 164 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_elem_type.

Referenced by libMesh::MeshTools::Generation::build_delaunay_square(), and MeshTriangulationTest::testTriangulatorRoundHole().

164 {return _elem_type;}
ElemType _elem_type
The type of elements to generate.

◆ elems_to_segments()

void libMesh::TriangulatorInterface::elems_to_segments ( )
protectedinherited

Helper function to create PSLG segments from our other boundary-defining options (1D mesh edges, 2D mesh boundary sides), if no segments already exist.

Definition at line 149 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_bdy_ids, libMesh::TriangulatorInterface::_holes, libMesh::TriangulatorInterface::_mesh, libMesh::TriangulatorInterface::_verify_hole_boundaries, libMesh::MeshBase::clear_elems(), libMesh::MeshBase::delete_node(), libMesh::make_range(), libMesh::MeshBase::n_elem(), libMesh::MeshBase::node_ptr(), libMesh::TriangulatorInterface::segment_midpoints, libMesh::TriangulatorInterface::segments, and libMesh::TriangulatorInterface::verify_holes().

Referenced by libMesh::TriangleInterface::triangulate(), and triangulate().

150 {
151  // Don't try to override manually specified segments
152  if (!this->segments.empty())
153  return;
154 
155  // If we have edges, they should form the polyline with the ordering
156  // we want. Let's turn them into segments for later use, because
157  // we're going to delete the original elements to replace with our
158  // triangulation.
159  if (_mesh.n_elem())
160  {
161  // Mapping from points to node ids, to back those out from
162  // MeshedHole results later
163  std::map<Point, dof_id_type> point_id_map;
164 
165  for (Node * node : _mesh.node_ptr_range())
166  {
167  // We're not going to support overlapping nodes on the boundary
168  libmesh_error_msg_if
169  (point_id_map.count(*node),
170  "TriangulatorInterface does not support overlapping nodes found at "
171  << static_cast<Point&>(*node));
172 
173  point_id_map.emplace(*node, node->id());
174  }
175 
176  // We don't support directly generating Tri6, so for
177  // compatibility with future stitching we need to be working
178  // with first-order elements. Let's get rid of any non-vertex
179  // nodes we just added.
180  for (Elem * elem : _mesh.element_ptr_range())
181  for (auto n : make_range(elem->n_vertices(), elem->n_nodes()))
182  point_id_map.erase(elem->point(n));
183 
184  // We'll steal the ordering calculation from
185  // the MeshedHole code
186  const TriangulatorInterface::MeshedHole mh { _mesh, this->_bdy_ids };
187 
188  // If we've specified only a subset of the mesh as our outer
189  // boundary, then we may have nodes that don't actually fall
190  // inside that boundary. Triangulator code doesn't like Steiner
191  // points that aren't inside the triangulation domain, so we
192  // need to get rid of them.
193  //
194  // Also, if we're using Edge3 elements to define our outer
195  // boundary, we're only dealing with their 2 end nodes and we'll
196  // need to get rid of their central nodes.
197  std::unordered_set<Node *> nodes_to_delete;
198 
199  for (Elem * elem : _mesh.element_ptr_range())
200  for (auto n : make_range(elem->n_vertices(), elem->n_nodes()))
201  nodes_to_delete.insert(elem->node_ptr(n));
202 
203  if (!this->_bdy_ids.empty())
204  {
205  for (auto & node : _mesh.node_ptr_range())
206  if (!mh.contains(*node))
207  nodes_to_delete.insert(node);
208  }
209 
210  // And now we're done with elements. Delete them lest they have
211  // dangling pointers to nodes we'll be deleting.
212  _mesh.clear_elems();
213 
214  // Make segments from boundary nodes; also make sure we don't
215  // delete them.
216  const std::size_t np = mh.n_points();
217  for (auto i : make_range(np))
218  {
219  const Point pt = mh.point(i);
220  const dof_id_type id0 = libmesh_map_find(point_id_map, pt);
221  nodes_to_delete.erase(_mesh.node_ptr(id0));
222  const Point next_pt = mh.point((np+i+1)%np);
223  const dof_id_type id1 = libmesh_map_find(point_id_map, next_pt);
224  this->segments.emplace_back(id0, id1);
225  for (auto m : make_range(mh.n_midpoints()))
226  this->segment_midpoints.emplace_back(mh.midpoint(m, i));
227  }
228 
229  for (Node * node : nodes_to_delete)
230  _mesh.delete_node(node);
231 
232  if (this->_verify_hole_boundaries && _holes)
233  this->verify_holes(mh);
234  }
235 }
bool _verify_hole_boundaries
Flag which tells if we want to check hole geometry.
std::set< std::size_t > _bdy_ids
A set of ids to allow on the outer boundary loop.
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
virtual void delete_node(Node *n)=0
Removes the Node n from the mesh.
std::vector< Point > segment_midpoints
When constructing a second-order triangulation from a second-order boundary, we may do the triangulat...
virtual void clear_elems()=0
Deletes all the element data that is currently stored.
std::vector< std::pair< unsigned int, unsigned int > > segments
When constructing a PSLG, if the node numbers do not define the desired boundary segments implicitly ...
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
void verify_holes(const Hole &outer_bdy)
Helper function to check holes for intersections if requested.
virtual dof_id_type n_elem() const =0
virtual const Node * node_ptr(const dof_id_type i) const =0
uint8_t dof_id_type
Definition: id_types.h:67

◆ get_auto_area_function()

FunctionBase< Real > * libMesh::TriangulatorInterface::get_auto_area_function ( )
inherited

Get the auto area function.

Definition at line 487 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_auto_area_function, and libMesh::TriangulatorInterface::calculate_auto_desired_area_samples().

Referenced by should_refine_elem().

488 {
489  if (!_auto_area_function->initialized())
490  {
491  // Points and target element sizes for the interpolation
492  std::vector<Point> function_points;
493  std::vector<Real> function_sizes;
494  calculate_auto_desired_area_samples(function_points, function_sizes);
495  _auto_area_function->init_mfi(function_points, function_sizes);
496  }
497  return _auto_area_function.get();
498 }
void calculate_auto_desired_area_samples(std::vector< Point > &function_points, std::vector< Real > &function_sizes, const Real &area_factor=1.5)
The external boundary and all hole boundaries are collected.
std::unique_ptr< AutoAreaFunction > _auto_area_function
The auto area function based on the spacing of boundary points.

◆ get_desired_area_function()

FunctionBase< Real > * libMesh::Poly2TriTriangulator::get_desired_area_function ( )
overridevirtual

Get the function giving desired triangle area as a function of position, or nullptr if no such function has been set.

Reimplemented from libMesh::TriangulatorInterface.

Definition at line 456 of file poly2tri_triangulator.C.

References _desired_area_func.

Referenced by insert_refinement_points(), and should_refine_elem().

457 {
458  return _desired_area_func.get();
459 }
std::unique_ptr< FunctionBase< Real > > _desired_area_func
Location-dependent area requirements.

◆ get_interpolate_boundary_points()

int libMesh::TriangulatorInterface::get_interpolate_boundary_points ( ) const
inherited

Complicated getter, for compatibility with insert_extra_points()

Definition at line 137 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_insert_extra_points, and libMesh::TriangulatorInterface::_interpolate_boundary_points.

Referenced by libMesh::TriangulatorInterface::insert_any_extra_boundary_points().

138 {
139  // backwards compatibility - someone might have turned this off via
140  // the old API
142  return 0;
143 
145 }
bool _insert_extra_points
Flag which tells whether or not to insert additional nodes before triangulation.
int _interpolate_boundary_points
Flag which tells how many additional nodes should be inserted between each pair of original mesh poin...

◆ get_outer_boundary_ids()

const std::set<std::size_t>& libMesh::TriangulatorInterface::get_outer_boundary_ids ( ) const
inlineinherited

Definition at line 352 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_bdy_ids.

352 { return _bdy_ids; }
std::set< std::size_t > _bdy_ids
A set of ids to allow on the outer boundary loop.

◆ get_verify_hole_boundaries()

bool libMesh::TriangulatorInterface::get_verify_hole_boundaries ( ) const
inlineinherited

Definition at line 264 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_verify_hole_boundaries.

264 {return _verify_hole_boundaries;}
bool _verify_hole_boundaries
Flag which tells if we want to check hole geometry.

◆ has_auto_area_function()

bool libMesh::TriangulatorInterface::has_auto_area_function ( )
inlineinherited

Whether or not an auto area function has been set.

Definition at line 329 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_auto_area_function.

Referenced by insert_refinement_points(), and should_refine_elem().

329 {return _auto_area_function != nullptr;}
std::unique_ptr< AutoAreaFunction > _auto_area_function
The auto area function based on the spacing of boundary points.

◆ increase_triangle_order()

void libMesh::TriangulatorInterface::increase_triangle_order ( )
protectedinherited

Helper function to upconvert Tri3 to any higher order triangle type if requested via _elem_type.

Should be called at the end of triangulate()

Definition at line 332 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_elem_type, libMesh::TriangulatorInterface::_holes, libMesh::TriangulatorInterface::_mesh, libMesh::MeshBase::all_complete_order(), libMesh::MeshBase::all_second_order(), libMesh::FIRST, libMesh::libmesh_assert(), libMesh::make_range(), libMesh::MeshBase::point(), libMesh::TriangulatorInterface::segment_midpoints, libMesh::TriangulatorInterface::segments, libMesh::TRI3, libMesh::TRI6, and libMesh::TRI7.

Referenced by libMesh::TriangleInterface::triangulate(), and triangulate().

333 {
334  switch (_elem_type)
335  {
336  case TRI3:
337  // Nothing to do if we're not requested to increase order
338  return;
339  case TRI6:
341  break;
342  case TRI7:
344  break;
345  default:
346  libmesh_not_implemented();
347  }
348 
349  // If we have any midpoint location data, we'll want to look it up
350  // by point. all_midpoints[{p, m}] will be the mth midpoint
351  // location following after point p (when traversing a triangle
352  // counter-clockwise)
353  std::map<std::pair<Point, unsigned int>, Point> all_midpoints;
354  unsigned int n_midpoints =
355  this->segment_midpoints.size() / this->segments.size();
356  libmesh_assert_equal_to(this->segments.size() * n_midpoints,
357  this->segment_midpoints.size());
358  for (auto m : make_range(n_midpoints))
359  for (auto i : make_range(this->segments.size()))
360  {
361  const Point & p = _mesh.point(this->segments[i].first);
362  all_midpoints[{p,m}] =
363  this->segment_midpoints[i*n_midpoints+m];
364  }
365 
366  if (_holes)
367  for (const Hole * hole : *_holes)
368  {
369  if (!hole->n_midpoints())
370  continue;
371  if (!n_midpoints)
372  n_midpoints = hole->n_midpoints();
373  else if (hole->n_midpoints() != n_midpoints)
374  libmesh_not_implemented_msg
375  ("Differing boundary midpoint counts " <<
376  hole->n_midpoints() << " and " << n_midpoints);
377 
378  // Our inner holes are expected to have points in
379  // counter-clockwise order, which is backwards from how we
380  // want to traverse them when iterating in counter-clockwise
381  // order over a triangle, so we'll need to reverse our maps
382  // carefully here.
383  const auto n_hole_points = hole->n_points();
384  libmesh_assert(n_hole_points);
385  for (auto m : make_range(n_midpoints))
386  {
387  for (auto i : make_range(n_hole_points-1))
388  {
389  const Point & p = hole->point(i+1);
390  all_midpoints[{p,m}] = hole->midpoint(n_midpoints-m-1, i);
391  }
392  const Point & p = hole->point(0);
393  all_midpoints[{p,m}] = hole->midpoint(n_midpoints-m-1, n_hole_points-1);
394  }
395  }
396 
397  // The n_midpoints > 1 case is for future proofing, but in the
398  // present we have EDGE4 and no TRI10 yet.
399  if (n_midpoints > 1)
400  libmesh_not_implemented_msg
401  ("Cannot construct triangles with more than 1 midpoint per edge");
402 
403  if (!n_midpoints)
404  return;
405 
406  for (Elem * elem : _mesh.element_ptr_range())
407  {
408  // This should only be called right after we've finished
409  // converting a triangulation to higher order
410  libmesh_assert_equal_to(elem->n_vertices(), 3);
411  libmesh_assert_not_equal_to(elem->default_order(), FIRST);
412 
413  for (auto n : make_range(3))
414  {
415  // Only hole/outer boundary segments need adjusted midpoints
416  if (elem->neighbor_ptr(n))
417  continue;
418 
419  const Point & p = elem->point(n);
420 
421  if (const auto it = all_midpoints.find({p,0});
422  it != all_midpoints.end())
423  elem->point(n+3) = it->second;
424  }
425  }
426 }
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
ElemType _elem_type
The type of elements to generate.
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
libmesh_assert(ctx)
std::vector< Point > segment_midpoints
When constructing a second-order triangulation from a second-order boundary, we may do the triangulat...
std::vector< std::pair< unsigned int, unsigned int > > segments
When constructing a PSLG, if the node numbers do not define the desired boundary segments implicitly ...
virtual void all_complete_order()
Calls the range-based version of this function with a range consisting of all elements in the mesh...
Definition: mesh_base.C:1613
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
void all_second_order(const bool full_ordered=true)
Calls the range-based version of this function with a range consisting of all elements in the mesh...
Definition: mesh_base.C:1608
virtual const Point & point(const dof_id_type i) const =0

◆ insert_any_extra_boundary_points()

void libMesh::TriangulatorInterface::insert_any_extra_boundary_points ( )
protectedinherited

Helper function to add extra points (midpoints of initial segments) to a PSLG triangulation.

Definition at line 278 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_mesh, libMesh::TriangulatorInterface::_triangulation_type, libMesh::MeshBase::add_point(), libMesh::TriangulatorInterface::get_interpolate_boundary_points(), libMesh::DofObject::id(), libMesh::libmesh_assert(), libMesh::make_range(), libMesh::MeshBase::max_node_id(), libMesh::MeshBase::node_ptr(), libMesh::TriangulatorInterface::PSLG, and libMesh::TriangulatorInterface::segments.

Referenced by libMesh::TriangleInterface::triangulate(), and triangulate().

279 {
280  // If the initial PSLG is really simple, e.g. an L-shaped domain or
281  // a square/rectangle, the resulting triangulation may be very
282  // "structured" looking. Sometimes this is a problem if your
283  // intention is to work with an "unstructured" looking grid. We can
284  // attempt to work around this limitation by inserting midpoints
285  // into the original PSLG. Inserting additional points into a
286  // set of points meant to be a convex hull usually makes less sense.
287 
288  const int n_interpolated = this->get_interpolate_boundary_points();
289  if ((_triangulation_type==PSLG) && n_interpolated)
290  {
291  // If we were lucky enough to start with contiguous node ids,
292  // let's keep them that way.
294 
295  std::vector<std::pair<unsigned int, unsigned int>> old_segments =
296  std::move(this->segments);
297 
298  // We expect to have converted any elems and/or nodes into
299  // segments by now.
300  libmesh_assert(!old_segments.empty());
301 
302  this->segments.clear();
303 
304  // Insert a new point on each segment at evenly spaced locations
305  // between existing boundary points.
306  // np=index into new points vector
307  // n =index into original points vector
308  for (auto old_segment : old_segments)
309  {
310  Node * begin_node = _mesh.node_ptr(old_segment.first);
311  Node * end_node = _mesh.node_ptr(old_segment.second);
312  dof_id_type current_id = begin_node->id();
313  for (auto i : make_range(n_interpolated))
314  {
315  // new points are equispaced along the original segments
316  const Point new_point =
317  ((n_interpolated-i) * *(Point *)(begin_node) +
318  (i+1) * *(Point *)(end_node)) /
319  (n_interpolated + 1);
320  Node * next_node = _mesh.add_point(new_point, nn++);
321  this->segments.emplace_back(current_id,
322  next_node->id());
323  current_id = next_node->id();
324  }
325  this->segments.emplace_back(current_id,
326  end_node->id());
327  }
328  }
329 }
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
libmesh_assert(ctx)
Triangulate the interior of a Planar Straight Line Graph, which is defined implicitly by the order of...
std::vector< std::pair< unsigned int, unsigned int > > segments
When constructing a PSLG, if the node numbers do not define the desired boundary segments implicitly ...
int get_interpolate_boundary_points() const
Complicated getter, for compatibility with insert_extra_points()
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 dof_id_type max_node_id() const =0
virtual const Node * node_ptr(const dof_id_type i) const =0
TriangulationType _triangulation_type
The type of triangulation to perform: choices are: convex hull PSLG.
uint8_t dof_id_type
Definition: id_types.h:67

◆ insert_extra_points()

bool& libMesh::TriangulatorInterface::insert_extra_points ( )
inlineinherited

Sets and/or gets the flag for inserting add'l points.

Definition at line 210 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_insert_extra_points.

210 {return _insert_extra_points;}
bool _insert_extra_points
Flag which tells whether or not to insert additional nodes before triangulation.

◆ insert_refinement_points()

bool libMesh::Poly2TriTriangulator::insert_refinement_points ( )
protected

Add Steiner points as new mesh nodes, as necessary to refine an existing trangulation.

Returns true iff new points were added.

Definition at line 782 of file poly2tri_triangulator.C.

References libMesh::TriangulatorInterface::_holes, libMesh::TriangulatorInterface::_mesh, _n_boundary_nodes, libMesh::MeshBase::add_elem(), libMesh::MeshBase::add_point(), libMesh::BoundaryInfo::add_side(), libMesh::BoundaryInfo::boundary_ids(), libMesh::Elem::build_with_id(), libMesh::MeshBase::delete_elem(), libMesh::TriangulatorInterface::desired_area(), libMesh::UnstructuredMesh::find_neighbors(), libMesh::MeshBase::get_boundary_info(), get_desired_area_function(), libMesh::TriangulatorInterface::ArbitraryHole::get_points(), libMesh::TriangulatorInterface::has_auto_area_function(), libMesh::DofObject::id(), libMesh::DofObject::invalid_id, libMesh::BoundaryInfo::invalid_id, libMesh::invalid_uint, is_refine_boundary_allowed(), libMesh::Elem::level(), libMesh::libmesh_assert(), libMesh::libmesh_ignore(), libMesh::libmesh_merge_move(), libMesh::make_range(), libMesh::MeshBase::max_elem_id(), libMesh::MeshBase::max_node_id(), mesh, libMesh::TriangulatorInterface::minimum_angle(), libMesh::Elem::neighbor_ptr(), libMesh::MeshBase::node_ptr(), libMesh::Elem::quasicircumcenter(), libMesh::Real, libMesh::BoundaryInfo::remove(), replaced_holes, libMesh::TriangulatorInterface::segments, libMesh::Elem::set_neighbor(), libMesh::TriangulatorInterface::ArbitraryHole::set_points(), should_refine_elem(), libMesh::TOLERANCE, libMesh::TRI3, libMesh::Elem::type(), libMesh::DofObject::valid_id(), libMesh::Elem::vertex_average(), and libMesh::Elem::which_neighbor_am_i().

Referenced by triangulate().

783 {
784  LOG_SCOPE("insert_refinement_points()", "Poly2TriTriangulator");
785 
786  if (this->minimum_angle() != 0)
787  libmesh_not_implemented();
788 
789  // We need neighbor pointers for ray casting and cavity finding
790  UnstructuredMesh & mesh = dynamic_cast<UnstructuredMesh &>(this->_mesh);
791  mesh.find_neighbors();
792 
793  if (this->desired_area() == 0 &&
794  this->get_desired_area_function() == nullptr &&
795  !this->has_auto_area_function())
796  return false;
797 
798  BoundaryInfo & boundary_info = _mesh.get_boundary_info();
799 
800  // We won't immediately add these, lest we invalidate iterators on a
801  // ReplicatedMesh. They'll still be in the mesh neighbor topology
802  // for the purpose of doing Delaunay cavity stuff, so we need to
803  // manage memory here, but there's no point in adding them to the
804  // Mesh just to remove them again afterward when we hit up poly2tri.
805 
806  // We'll need to be able to remove new elems from new_elems, in
807  // cases where a later refinement insertion has a not-yet-added
808  // element in its cavity, so we'll use a map here to make searching
809  // possible.
810  //
811  // For parallel consistency, we can't order a container we plan to
812  // iterate through based on Elem * or a hash of it. We'll be doing
813  // Delaunay swaps so we can't iterate based on geometry. These are
814  // not-yet-added elements so we can't iterate based on proper
815  // element ids ... but we can set a temporary element id to use for
816  // the purpose.
817  struct cmp {
818  bool operator()(Elem * a, Elem * b) const {
819  libmesh_assert(a == b || a->id() != b->id());
820  return (a->id() < b->id());
821  }
822  } comp;
823 
824  std::map<Elem *, std::unique_ptr<Elem>, decltype(comp)> new_elems(comp);
825 
826  // We should already be Delaunay when we get here, otherwise we
827  // won't be able to stay Delaunay later. But we're *not* always
828  // Delaunay when we get here? What the hell, poly2tri? Fixing this
829  // is expensive!
830  {
831  // restore_delaunay should get to the same Delaunay triangulation up to
832  // isomorphism regardless of ordering ... but we actually care
833  // about the isomorphisms! If a triangle's nodes are permuted on
834  // one processor vs another that's an issue. So sort our input
835  // carefully.
836  std::set<Elem *, decltype(comp)> all_elems
837  { mesh.elements_begin(), mesh.elements_end(), comp };
838 
839  restore_delaunay(all_elems, boundary_info);
840 
841  libmesh_assert_delaunay(mesh, new_elems);
842  }
843 
844  // Map of which points follow which in the boundary polylines. If
845  // we have to add new boundary points, we'll use this to construct
846  // an updated this->segments to retriangulate with. If we have to
847  // add new hole points, we'll use this to insert points into an
848  // ArbitraryHole.
849  std::unordered_map<Point, Node *> next_boundary_node;
850 
851  // In cases where we've been working with contiguous node id ranges;
852  // let's keep it that way.
855 
856  // We can't handle duplicated nodes. We shouldn't ever create one,
857  // but let's make sure of that.
858 #ifdef DEBUG
859  std::unordered_set<Point> mesh_points;
860  for (const Node * node : mesh.node_ptr_range())
861  {
862  libmesh_assert(!mesh_points.count(*node));
863  mesh_points.insert(*node);
864  }
865 #endif
866 
867  auto add_point = [&mesh,
868 #ifdef DEBUG
869  &mesh_points,
870 #endif
871  &nn](const Point & p)
872  {
873 #ifdef DEBUG
874  libmesh_assert(!mesh_points.count(p));
875  mesh_points.insert(p);
876 #endif
877  return mesh.add_point(p, nn++);
878  };
879 
880  for (auto & elem : mesh.element_ptr_range())
881  {
882  // element_ptr_range skips deleted elements ... right?
883  libmesh_assert(elem);
884  libmesh_assert(elem->valid_id());
885 
886  // We only handle triangles in our triangulation
887  libmesh_assert_equal_to(elem->level(), 0u);
888  libmesh_assert_equal_to(elem->type(), TRI3);
889 
890  // If this triangle is as small as we desire, move along
891  if (!should_refine_elem(*elem))
892  continue;
893 
894  // Otherwise add a Steiner point. We'd like to add the
895  // circumcenter ...
896  Point new_pt = elem->quasicircumcenter();
897 
898  // And to give it a node;
899  Node * new_node = nullptr;
900 
901  // But that might be outside our triangle, or even outside the
902  // boundary. We'll find a triangle that should contain our new
903  // point
904  Elem * cavity_elem = elem; // Start looking at elem anyway
905 
906  // We'll refine a boundary later if necessary.
907  auto boundary_refine = [this, &next_boundary_node,
908  &cavity_elem, &new_node]
909  (unsigned int side)
910  {
911  libmesh_ignore(this); // Only used in dbg/devel
912  libmesh_assert(new_node);
913  libmesh_assert(new_node->valid_id());
914 
915  Node * old_segment_start = cavity_elem->node_ptr(side),
916  * old_segment_end = cavity_elem->node_ptr((side+1)%3);
917  libmesh_assert(old_segment_start);
918  libmesh_assert(old_segment_start->valid_id());
919  libmesh_assert(old_segment_end);
920  libmesh_assert(old_segment_end->valid_id());
921 
922  if (auto it = next_boundary_node.find(*old_segment_start);
923  it != next_boundary_node.end())
924  {
925  libmesh_assert(it->second == old_segment_end);
926  it->second = new_node;
927  }
928  else
929  {
930  // This would be an O(N) sanity check if we already
931  // have a segments vector or any holes. :-P
932  libmesh_assert(!this->segments.empty() ||
933  (_holes && !_holes->empty()) ||
934  (old_segment_end->id() ==
935  old_segment_start->id() + 1));
936  next_boundary_node[*old_segment_start] = new_node;
937  }
938 
939  next_boundary_node[*new_node] = old_segment_end;
940  };
941 
942  // Let's find a triangle containing our new point, or at least
943  // containing the end of a ray leading from our current triangle
944  // to the new point.
945  Point ray_start = elem->vertex_average();
946 
947  // What side are we coming from, and what side are we going to?
948  unsigned int source_side = invalid_uint;
949  unsigned int side = invalid_uint;
950 
951  while (!cavity_elem->contains_point(new_pt))
952  {
953  side = segment_intersection(*cavity_elem, ray_start, new_pt, source_side);
954 
955  libmesh_assert_not_equal_to (side, invalid_uint);
956 
957  Elem * neigh = cavity_elem->neighbor_ptr(side);
958  // If we're on a boundary, stop there. Refine the boundary
959  // if we're allowed, the boundary element otherwise.
960  if (!neigh)
961  {
962  if (this->is_refine_boundary_allowed(boundary_info,
963  *cavity_elem,
964  side))
965  {
966  new_pt = ray_start;
967  new_node = add_point(new_pt);
968  boundary_refine(side);
969  }
970  else
971  {
972  // Should we just add the vertex average of the
973  // boundary element, to minimize the number of
974  // slivers created?
975  //
976  // new_pt = cavity_elem->vertex_average();
977  //
978  // That works for a while, but it
979  // seems to be able to "run away" and leave us with
980  // crazy slivers on boundaries if we push interior
981  // refinement too far while disabling boundary
982  // refinement.
983  //
984  // Let's go back to refining our original problem
985  // element.
986  cavity_elem = elem;
987  new_pt = cavity_elem->vertex_average();
988  new_node = add_point(new_pt);
989 
990  // This was going to be a side refinement but it's
991  // now an internal refinement
992  side = invalid_uint;
993  }
994 
995  break;
996  }
997 
998  source_side = neigh->which_neighbor_am_i(cavity_elem);
999  cavity_elem = neigh;
1000  side = invalid_uint;
1001  }
1002 
1003  // If we're ready to create a new node and we're not on a
1004  // boundary ... should we be? We don't want to create any
1005  // sliver elements or confuse poly2tri or anything.
1006  if (side == invalid_uint && !new_node)
1007  {
1008  unsigned int worst_side = libMesh::invalid_uint;
1009  Real worst_cos = 1;
1010  for (auto s : make_range(3u))
1011  {
1012  // We never snap to a non-domain-boundary
1013  if (cavity_elem->neighbor_ptr(s))
1014  continue;
1015 
1016  Real ax = cavity_elem->point(s)(0) - new_pt(0),
1017  ay = cavity_elem->point(s)(1) - new_pt(1),
1018  bx = cavity_elem->point((s+1)%3)(0) - new_pt(0),
1019  by = cavity_elem->point((s+1)%3)(1) - new_pt(1);
1020  const Real my_cos = (ax*bx+ay*by) /
1021  std::sqrt(ax*ax+ay*ay) /
1022  std::sqrt(bx*bx+by*by);
1023 
1024  if (my_cos < worst_cos)
1025  {
1026  worst_side = s;
1027  worst_cos = my_cos;
1028  }
1029  }
1030 
1031  // If we'd create a sliver element on the side, let's just
1032  // refine the side instead, if we're allowed.
1033  if (worst_cos < -0.6) // -0.5 is the best we could enforce?
1034  {
1035  side = worst_side;
1036 
1037  if (this->is_refine_boundary_allowed(boundary_info,
1038  *cavity_elem,
1039  side))
1040  {
1041  // Let's just try bisecting for now
1042  new_pt = (cavity_elem->point(side) +
1043  cavity_elem->point((side+1)%3)) / 2;
1044  new_node = add_point(new_pt);
1045  boundary_refine(side);
1046  }
1047  else // Do the best we can under these restrictions
1048  {
1049  new_pt = cavity_elem->vertex_average();
1050  new_node = add_point(new_pt);
1051 
1052  // This was going to be a side refinement but it's
1053  // now an internal refinement
1054  side = invalid_uint;
1055  }
1056  }
1057  else
1058  new_node = add_point(new_pt);
1059  }
1060  else
1061  libmesh_assert(new_node);
1062 
1063  // Find the Delaunay cavity around the new point.
1064  std::set<Elem *, decltype(comp)> cavity(comp);
1065 
1066  std::set<Elem *, decltype(comp)> unchecked_cavity ({cavity_elem}, comp);
1067  while (!unchecked_cavity.empty())
1068  {
1069  std::set<Elem *, decltype(comp)> checking_cavity(comp);
1070  checking_cavity.swap(unchecked_cavity);
1071  for (Elem * checking_elem : checking_cavity)
1072  {
1073  for (auto s : make_range(3u))
1074  {
1075  Elem * neigh = checking_elem->neighbor_ptr(s);
1076  if (!neigh || checking_cavity.count(neigh) || cavity.count(neigh))
1077  continue;
1078 
1079  if (in_circumcircle(*neigh, new_pt, TOLERANCE*TOLERANCE))
1080  unchecked_cavity.insert(neigh);
1081  }
1082  }
1083 
1084  libmesh_merge_move(cavity, checking_cavity);
1085  }
1086 
1087  // Retriangulate the Delaunay cavity.
1088  // Each of our cavity triangle edges that are exterior to the
1089  // cavity will be a source of one new triangle.
1090 
1091  // Set of elements that might need Delaunay swaps
1092  std::set<Elem *, decltype(comp)> check_delaunay_on(comp);
1093 
1094  // Keep maps for doing neighbor pointer assignment. Not going
1095  // to iterate through these so hashing pointers is fine.
1096  std::unordered_map<Node *, std::pair<Elem *, boundary_id_type>>
1097  neighbors_CCW, neighbors_CW;
1098 
1099  for (Elem * old_elem : cavity)
1100  {
1101  for (auto s : make_range(3u))
1102  {
1103  Elem * neigh = old_elem->neighbor_ptr(s);
1104  if (!neigh || !cavity.count(neigh))
1105  {
1106  Node * node_CW = old_elem->node_ptr(s),
1107  * node_CCW = old_elem->node_ptr((s+1)%3);
1108 
1109  auto set_neighbors =
1110  [&neighbors_CW, &neighbors_CCW, &node_CW,
1111  &node_CCW, &boundary_info]
1112  (Elem * new_neigh, boundary_id_type bcid)
1113  {
1114  // Set clockwise neighbor and vice-versa if possible
1115  if (const auto CW_it = neighbors_CW.find(node_CW);
1116  CW_it == neighbors_CW.end())
1117  {
1118  libmesh_assert(!neighbors_CCW.count(node_CW));
1119  neighbors_CCW[node_CW] = std::make_pair(new_neigh, bcid);
1120  }
1121  else
1122  {
1123  Elem * neigh_CW = CW_it->second.first;
1124  if (new_neigh)
1125  {
1126  new_neigh->set_neighbor(0, neigh_CW);
1127  boundary_id_type bcid_CW = CW_it->second.second;
1128  if (bcid_CW != BoundaryInfo::invalid_id)
1129  boundary_info.add_side(new_neigh, 0, bcid_CW);
1130 
1131  }
1132  if (neigh_CW)
1133  {
1134  neigh_CW->set_neighbor(2, new_neigh);
1135  if (bcid != BoundaryInfo::invalid_id)
1136  boundary_info.add_side(neigh_CW, 2, bcid);
1137  }
1138  neighbors_CW.erase(CW_it);
1139  }
1140 
1141  // Set counter-CW neighbor and vice-versa if possible
1142  if (const auto CCW_it = neighbors_CCW.find(node_CCW);
1143  CCW_it == neighbors_CCW.end())
1144  {
1145  libmesh_assert(!neighbors_CW.count(node_CCW));
1146  neighbors_CW[node_CCW] = std::make_pair(new_neigh, bcid);
1147  }
1148  else
1149  {
1150  Elem * neigh_CCW = CCW_it->second.first;
1151  if (new_neigh)
1152  {
1153  boundary_id_type bcid_CCW = CCW_it->second.second;
1154  new_neigh->set_neighbor(2, neigh_CCW);
1155  if (bcid_CCW != BoundaryInfo::invalid_id)
1156  boundary_info.add_side(new_neigh, 2, bcid_CCW);
1157  }
1158  if (neigh_CCW)
1159  {
1160  neigh_CCW->set_neighbor(0, new_neigh);
1161  if (bcid != BoundaryInfo::invalid_id)
1162  boundary_info.add_side(neigh_CCW, 0, bcid);
1163  }
1164  neighbors_CCW.erase(CCW_it);
1165  }
1166  };
1167 
1168  // We aren't going to try to add a sliver element if we
1169  // have a new boundary node here. We do need to
1170  // keep track of other elements' neighbors, though.
1171  if (old_elem == cavity_elem &&
1172  s == side)
1173  {
1174  std::vector<boundary_id_type> bcids;
1175  boundary_info.boundary_ids(old_elem, s, bcids);
1176  libmesh_assert_equal_to(bcids.size(), 1);
1177  set_neighbors(nullptr, bcids[0]);
1178  continue;
1179  }
1180 
1181  auto new_elem = Elem::build_with_id(TRI3, ne++);
1182  new_elem->set_node(0, new_node);
1183  new_elem->set_node(1, node_CW);
1184  new_elem->set_node(2, node_CCW);
1185  libmesh_assert(!new_elem->is_flipped());
1186 
1187  // Set in-and-out-of-cavity neighbor pointers
1188  new_elem->set_neighbor(1, neigh);
1189  if (neigh)
1190  {
1191  const unsigned int neigh_s =
1192  neigh->which_neighbor_am_i(old_elem);
1193  neigh->set_neighbor(neigh_s, new_elem.get());
1194  }
1195  else
1196  {
1197  std::vector<boundary_id_type> bcids;
1198  boundary_info.boundary_ids(old_elem, s, bcids);
1199  boundary_info.add_side(new_elem.get(), 1, bcids);
1200  }
1201 
1202  // Set in-cavity neighbors' neighbor pointers
1203  set_neighbors(new_elem.get(), BoundaryInfo::invalid_id);
1204 
1205  // C++ allows function argument evaluation in any
1206  // order, but we need get() to precede move
1207  Elem * new_elem_ptr = new_elem.get();
1208  new_elems.emplace(new_elem_ptr, std::move(new_elem));
1209 
1210  check_delaunay_on.insert(new_elem_ptr);
1211  }
1212  }
1213 
1214  boundary_info.remove(old_elem);
1215  }
1216 
1217  // Now that we're done using our cavity elems (including with a
1218  // cavity.find() that used a comparator that dereferences the
1219  // elements!) it's safe to delete them.
1220  for (Elem * old_elem : cavity)
1221  {
1222  if (const auto it = new_elems.find(old_elem);
1223  it == new_elems.end())
1224  mesh.delete_elem(old_elem);
1225  else
1226  new_elems.erase(it);
1227  }
1228 
1229  // Everybody found their match?
1230  libmesh_assert(neighbors_CW.empty());
1231  libmesh_assert(neighbors_CCW.empty());
1232 
1233  // Because we're preserving boundaries here, our naive cavity
1234  // triangulation might not be a Delaunay triangulation. Let's
1235  // check and if necessary fix that; we depend on it when doing
1236  // future point insertions.
1237  restore_delaunay(check_delaunay_on, boundary_info);
1238 
1239  // This is too expensive to do on every cavity in devel mode
1240 #ifdef DEBUG
1241  libmesh_assert_delaunay(mesh, new_elems);
1242 #endif
1243  }
1244 
1245  // If we added any new boundary nodes, we're going to need to keep
1246  // track of the changes they made to the outer polyline and/or to
1247  // any holes.
1248  if (!next_boundary_node.empty())
1249  {
1250  auto checked_emplace = [this](dof_id_type new_first,
1251  dof_id_type new_second)
1252  {
1253 #ifdef DEBUG
1254  for (auto [first, second] : this->segments)
1255  {
1256  libmesh_assert_not_equal_to(first, new_first);
1257  libmesh_assert_not_equal_to(second, new_second);
1258  }
1259  if (!this->segments.empty())
1260  libmesh_assert_equal_to(this->segments.back().second, new_first);
1261 #endif
1262  libmesh_assert_not_equal_to(new_first, new_second);
1263 
1264  this->segments.emplace_back (new_first, new_second);
1265  };
1266 
1267  // Keep track of the outer polyline
1268  if (this->segments.empty())
1269  {
1271 
1272  // Custom loop because we increment node_it 1+ times inside
1273  for (auto node_it = _mesh.nodes_begin(),
1274  node_end = _mesh.nodes_end();
1275  node_it != node_end;)
1276  {
1277  Node & node = **node_it;
1278  ++node_it;
1279 
1280  const dof_id_type node_id = node.id();
1281 
1282  // Don't add Steiner points
1283  if (node_id >= _n_boundary_nodes)
1284  break;
1285 
1286  // Connect up the previous node, if we didn't already
1287  // connect it after some newly inserted nodes
1288  if (!this->segments.empty())
1289  last_id = this->segments.back().second;
1290 
1291  if (last_id != DofObject::invalid_id &&
1292  last_id != node_id)
1293  checked_emplace(last_id, node_id);
1294 
1295  last_id = node_id;
1296 
1297  // Connect to any newly inserted nodes
1298  Node * this_node = &node;
1299  auto it = next_boundary_node.find(*this_node);
1300  while (it != next_boundary_node.end())
1301  {
1302  libmesh_assert(this_node->valid_id());
1303  Node * next_node = it->second;
1304  libmesh_assert(next_node->valid_id());
1305 
1306  if (node_it != node_end &&
1307  next_node == *node_it)
1308  ++node_it;
1309 
1310  checked_emplace(this_node->id(), next_node->id());
1311 
1312  this_node = next_node;
1313  if (this_node->id() == this->segments.front().first)
1314  break;
1315 
1316  it = next_boundary_node.find(*this_node);
1317  }
1318  }
1319 
1320  // We expect a closed loop here
1321  if (this->segments.back().second != this->segments.front().first)
1322  checked_emplace(this->segments.back().second,
1323  this->segments.front().first);
1324  }
1325  else
1326  {
1327  std::vector<std::pair<unsigned int, unsigned int>> old_segments;
1328  old_segments.swap(this->segments);
1329 
1330  auto old_it = old_segments.begin();
1331 
1332  const Node * node = _mesh.node_ptr(old_it->first);
1333  const Node * const first_node = node;
1334 
1335  do
1336  {
1337  const dof_id_type node_id = node->id();
1338  if (const auto it = next_boundary_node.find(*node);
1339  it == next_boundary_node.end())
1340  {
1341  while (node_id != old_it->first)
1342  {
1343  ++old_it;
1344  libmesh_assert(old_it != old_segments.end());
1345  }
1346  node = mesh.node_ptr(old_it->second);
1347  }
1348  else
1349  {
1350  node = it->second;
1351  }
1352 
1353  checked_emplace(node_id, node->id());
1354  }
1355  while (node != first_node);
1356  }
1357 
1358  // Keep track of any holes
1359  if (this->_holes)
1360  {
1361  // Do we have any holes that need to be newly replaced?
1362  for (const Hole * hole : *this->_holes)
1363  {
1364  if (this->replaced_holes.count(hole))
1365  continue;
1366 
1367  bool hole_point_insertion = false;
1368  for (auto p : make_range(hole->n_points()))
1369  if (next_boundary_node.count(hole->point(p)))
1370  {
1371  hole_point_insertion = true;
1372  break;
1373  }
1374  if (hole_point_insertion)
1375  this->replaced_holes.emplace
1376  (hole, std::make_unique<ArbitraryHole>(*hole));
1377  }
1378 
1379  // If we have any holes that are being replaced, make sure
1380  // their replacements are up to date.
1381  for (const Hole * hole : *this->_holes)
1382  {
1383  auto hole_it = replaced_holes.find(hole);
1384  if (hole_it == replaced_holes.end())
1385  continue;
1386 
1387  ArbitraryHole & arb = *hole_it->second;
1388 
1389  // We only need to update a replacement that's just had
1390  // new points inserted
1391  bool point_inserted = false;
1392  for (const Point & point : arb.get_points())
1393  if (next_boundary_node.count(point))
1394  {
1395  point_inserted = true;
1396  break;
1397  }
1398 
1399  if (!point_inserted)
1400  continue;
1401 
1402  // Find all points in the replacement hole
1403  std::vector<Point> new_points;
1404 
1405  // Our outer polyline is expected to have points in
1406  // counter-clockwise order, so it proceeds "to the left"
1407  // from the point of view of rays inside the domain
1408  // pointing outward, and our next_boundary_node ordering
1409  // was filled accordingly.
1410  //
1411  // Our inner holes are expected to have points in
1412  // counter-clockwise order, but for holes "to the left
1413  // as viewed from the hole interior is the *opposite* of
1414  // "to the left as viewed from the domain interior". We
1415  // need to build the updated hole ordering "backwards".
1416 
1417  // We should never see duplicate points when we add one
1418  // to a hole; if we do then we did something wrong.
1419  auto push_back_new_point = [&new_points](const Point & p) {
1420  // O(1) assert in devel
1421  libmesh_assert(new_points.empty() ||
1422  new_points.back() != p);
1423 #ifdef DEBUG
1424  // O(N) asserts in dbg
1425  for (auto old_p : new_points)
1426  libmesh_assert_not_equal_to(old_p, p);
1427 #endif
1428  new_points.push_back(p);
1429  };
1430 
1431  for (auto point_it = arb.get_points().rbegin(),
1432  point_end = arb.get_points().rend();
1433  point_it != point_end;)
1434  {
1435  Point point = *point_it;
1436  ++point_it;
1437 
1438  if (new_points.empty() ||
1439  (point != new_points.back() &&
1440  point != new_points.front()))
1441  push_back_new_point(point);
1442 
1443  auto it = next_boundary_node.find(point);
1444  while (it != next_boundary_node.end())
1445  {
1446  point = *it->second;
1447  if (point == new_points.front())
1448  break;
1449  if (point_it != point_end &&
1450  point == *point_it)
1451  ++point_it;
1452  push_back_new_point(point);
1453  it = next_boundary_node.find(point);
1454  }
1455  }
1456 
1457  std::reverse(new_points.begin(), new_points.end());
1458 
1459  arb.set_points(std::move(new_points));
1460  }
1461  }
1462  }
1463 
1464  // Okay, *now* we can add the new elements.
1465  for (auto & [raw_elem, unique_elem] : new_elems)
1466  {
1467  libmesh_assert_equal_to(raw_elem, unique_elem.get());
1468  libmesh_assert(!raw_elem->is_flipped());
1469  libmesh_ignore(raw_elem); // Old gcc warns "unused variable"
1470  mesh.add_elem(std::move(unique_elem));
1471  }
1472 
1473  // Did we add anything?
1474  return !new_elems.empty();
1475 }
A Node is like a Point, but with more information.
Definition: node.h:52
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition: libmesh.h:310
static constexpr Real TOLERANCE
void libmesh_merge_move(T &target, T &source)
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
MeshBase & mesh
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.
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true) override
Other functions from MeshBase requiring re-definition.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:165
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
virtual FunctionBase< Real > * get_desired_area_function() override
Get the function giving desired triangle area as a function of position, or nullptr if no such functi...
void libmesh_ignore(const Args &...)
int8_t boundary_id_type
Definition: id_types.h:51
static const boundary_id_type invalid_id
Number used for internal use.
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
dof_id_type id() const
Definition: dof_object.h:828
The UnstructuredMesh class is derived from the MeshBase class.
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition: elem.h:2919
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
Real & minimum_angle()
Sets and/or gets the minimum desired angle.
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
bool has_auto_area_function()
Whether or not an auto area function has been set.
std::map< const Hole *, std::unique_ptr< ArbitraryHole > > replaced_holes
We might have to replace the user-provided holes with refined versions.
virtual dof_id_type max_elem_id() const =0
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
libmesh_assert(ctx)
static const dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:482
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition: elem.h:2618
Real & desired_area()
Sets and/or gets the desired triangle area.
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2598
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< std::pair< unsigned int, unsigned int > > segments
When constructing a PSLG, if the node numbers do not define the desired boundary segments implicitly ...
bool valid_id() const
Definition: dof_object.h:885
bool is_refine_boundary_allowed(const BoundaryInfo &boundary_info, const Elem &elem, unsigned int side)
Is refining this element&#39;s boundary side allowed?
static std::unique_ptr< Elem > build_with_id(const ElemType type, dof_id_type id)
Calls the build() method above with a nullptr parent, and additionally sets the newly-created Elem&#39;s ...
Definition: elem.C:558
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure...
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
dof_id_type _n_boundary_nodes
Keep track of how many mesh nodes are boundary nodes.
bool should_refine_elem(Elem &elem)
Returns true if the given element ought to be refined according to current criteria.
virtual dof_id_type max_node_id() const =0
virtual const Node * node_ptr(const dof_id_type i) const =0
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
uint8_t dof_id_type
Definition: id_types.h:67

◆ is_refine_boundary_allowed()

bool libMesh::Poly2TriTriangulator::is_refine_boundary_allowed ( const BoundaryInfo boundary_info,
const Elem elem,
unsigned int  side 
)
protected

Is refining this element's boundary side allowed?

Definition at line 463 of file poly2tri_triangulator.C.

References libMesh::BoundaryInfo::boundary_ids(), libMesh::libmesh_assert(), libMesh::Elem::neighbor_ptr(), and libMesh::TriangulatorInterface::Hole::refine_boundary_allowed().

Referenced by insert_refinement_points().

466 {
467  // We should only be calling this on a boundary side
468  libmesh_assert(!elem.neighbor_ptr(side));
469 
470  std::vector<boundary_id_type> bcids;
471  boundary_info.boundary_ids(&elem, side, bcids);
472 
473  // We should have one bcid on every boundary side.
474  libmesh_assert_equal_to(bcids.size(), 1);
475 
476  if (bcids[0] == 0)
477  return this->refine_boundary_allowed();
478 
479  // If we're not on an outer boundary side we'd better be on a hole
480  // side
481  libmesh_assert(this->_holes);
482 
483  const boundary_id_type hole_num = bcids[0]-1;
484  libmesh_assert_less(hole_num, this->_holes->size());
485  const Hole * hole = (*this->_holes)[hole_num];
486  return hole->refine_boundary_allowed();
487 }
virtual bool refine_boundary_allowed() const override
Get whether or not the triangulation is allowed to refine the mesh boundary when refining the interio...
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.
int8_t boundary_id_type
Definition: id_types.h:51
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
libmesh_assert(ctx)
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2598

◆ minimum_angle()

Real& libMesh::TriangulatorInterface::minimum_angle ( )
inlineinherited

Sets and/or gets the minimum desired angle.

Set to zero to disable angle constraint.

Definition at line 200 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_minimum_angle.

Referenced by MeshTriangulationTest::commonSettings(), insert_refinement_points(), and main().

200 {return _minimum_angle;}
Real _minimum_angle
Minimum angle in triangles.

◆ nodes_to_segments()

void libMesh::TriangulatorInterface::nodes_to_segments ( dof_id_type  max_node_id)
protectedinherited

Helper function to create PSLG segments from our node ordering, up to the maximum node id, if no segments already exist.

Definition at line 239 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_holes, libMesh::TriangulatorInterface::_mesh, libMesh::TriangulatorInterface::_triangulation_type, libMesh::TriangulatorInterface::_verify_hole_boundaries, libMesh::DofObject::id(), libMesh::MeshBase::point(), libMesh::TriangulatorInterface::PSLG, libMesh::TriangulatorInterface::segments, and libMesh::TriangulatorInterface::verify_holes().

Referenced by libMesh::TriangleInterface::triangulate(), and triangulate().

240 {
241  // Don't try to override manually specified segments, or try to add
242  // segments if we're doing a convex hull
243  if (!this->segments.empty() || _triangulation_type != PSLG)
244  return;
245 
246  for (auto node_it = _mesh.nodes_begin(),
247  node_end = _mesh.nodes_end();
248  node_it != node_end;)
249  {
250  Node * node = *node_it;
251 
252  // If we're out of boundary nodes, the rest are going to be
253  // Steiner points or hole points
254  if (node->id() >= max_node_id)
255  break;
256 
257  ++node_it;
258 
259  Node * next_node = (node_it == node_end) ?
260  *_mesh.nodes_begin() : *node_it;
261 
262  this->segments.emplace_back(node->id(), next_node->id());
263  }
264 
265  if (this->_verify_hole_boundaries && _holes)
266  {
267  std::vector<Point> outer_pts;
268  for (auto segment : this->segments)
269  outer_pts.push_back(_mesh.point(segment.first));
270 
271  ArbitraryHole ah(outer_pts);
272  this->verify_holes(ah);
273  }
274 }
bool _verify_hole_boundaries
Flag which tells if we want to check hole geometry.
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
Triangulate the interior of a Planar Straight Line Graph, which is defined implicitly by the order of...
std::vector< std::pair< unsigned int, unsigned int > > segments
When constructing a PSLG, if the node numbers do not define the desired boundary segments implicitly ...
virtual const Point & point(const dof_id_type i) const =0
void verify_holes(const Hole &outer_bdy)
Helper function to check holes for intersections if requested.
TriangulationType _triangulation_type
The type of triangulation to perform: choices are: convex hull PSLG.

◆ quiet()

bool& libMesh::TriangulatorInterface::quiet ( )
inlineinherited

Whether not to silence internal messages to stdout.

Definition at line 249 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_quiet.

249 {return _quiet;}
bool _quiet
Flag which tells if we want to suppress stdout outputs.

◆ refine_boundary_allowed()

virtual bool libMesh::Poly2TriTriangulator::refine_boundary_allowed ( ) const
inlineoverridevirtual

Get whether or not the triangulation is allowed to refine the mesh boundary when refining the interior.

True by default.

Reimplemented from libMesh::TriangulatorInterface.

Definition at line 104 of file poly2tri_triangulator.h.

References _refine_bdy_allowed.

105  { return _refine_bdy_allowed; }
bool _refine_bdy_allowed
Whether to allow boundary refinement.

◆ set_auto_area_function()

void libMesh::TriangulatorInterface::set_auto_area_function ( const Parallel::Communicator comm,
const unsigned int  num_nearest_pts,
const unsigned int  power,
const Real  background_value,
const Real  background_eff_dist 
)
inherited

Generate an auto area function based on spacing of boundary points.

The external boundary as well as the hole boundaries are taken into consideration to generate the auto area function based on inverse distance interpolation. For each EDGE element on these boundaries, its centroid (midpoint) is used as the point position and the desired area is calculated as 1.5 times of the area of the equilateral triangle with the edge length as the length of the EDGE element. For a given position, the inverse distance interpolation only considers a number of nearest points (set by num_nearest_pts) to calculate the desired area. The weight of the value at each point is calculated as 1/distance^power.

In addition to these conventional inverse distance interpolation features, a concept of "background value" and "background effective distance" is introduced. The background value belongs to a virtual point located at a constant distance (background effective distance) from the given position. The weight of the value at this virtual point is calculated as 1/background_effective_distance^power. Effectively, the background value is the value when the given position is far away from the boundary points.

Definition at line 478 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_auto_area_function.

483 {
484  _auto_area_function = std::make_unique<AutoAreaFunction>(comm, num_nearest_pts, power, background_value, background_eff_dist);
485 }
std::unique_ptr< AutoAreaFunction > _auto_area_function
The auto area function based on the spacing of boundary points.

◆ set_desired_area_function()

void libMesh::Poly2TriTriangulator::set_desired_area_function ( FunctionBase< Real > *  desired)
overridevirtual

Set a function giving desired triangle area as a function of position.

Set this to nullptr to disable position-dependent area constraint (falling back on desired_area()).

Reimplemented from libMesh::TriangulatorInterface.

Definition at line 447 of file poly2tri_triangulator.C.

References libMesh::FunctionBase< Output >::clone().

Referenced by MeshTriangulationTest::testPoly2TriRefinementBase().

448 {
449  if (desired)
450  _desired_area_func = desired->clone();
451  else
452  _desired_area_func.reset();
453 }
std::unique_ptr< FunctionBase< Real > > _desired_area_func
Location-dependent area requirements.
virtual std::unique_ptr< FunctionBase< Output > > clone() const =0

◆ set_interpolate_boundary_points()

void libMesh::TriangulatorInterface::set_interpolate_boundary_points ( int  n_points)
inherited

Complicated setter, for compatibility with insert_extra_points()

Definition at line 123 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_insert_extra_points, libMesh::TriangulatorInterface::_interpolate_boundary_points, and libMesh::libmesh_assert().

Referenced by MeshTriangulationTest::testTriangulatorInterp().

124 {
125  // Maybe we'll reserve a meaning for negatives later?
126  libmesh_assert(n_points >= 0);
127 
128  _interpolate_boundary_points = n_points;
129 
130  // backwards compatibility - someone (including us) might want to
131  // query this via the old API.
132  _insert_extra_points = n_points;
133 }
bool _insert_extra_points
Flag which tells whether or not to insert additional nodes before triangulation.
int _interpolate_boundary_points
Flag which tells how many additional nodes should be inserted between each pair of original mesh poin...
libmesh_assert(ctx)

◆ set_outer_boundary_ids()

void libMesh::TriangulatorInterface::set_outer_boundary_ids ( std::set< std::size_t >  bdy_ids)
inlineinherited

A set of ids to allow on the outer boundary loop: interpreted as boundary ids of 2D elements and/or subdomain ids of 1D edges.

If this is empty, then the outer boundary may be constructed from boundary edges of any id!

Definition at line 351 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_bdy_ids.

Referenced by MeshTriangulationTest::testHalfDomain().

351 { _bdy_ids = std::move(bdy_ids); }
std::set< std::size_t > _bdy_ids
A set of ids to allow on the outer boundary loop.

◆ set_refine_boundary_allowed()

virtual void libMesh::Poly2TriTriangulator::set_refine_boundary_allowed ( bool  refine_bdy_allowed)
inlineoverridevirtual

Set whether or not the triangulation is allowed to refine the mesh boundary when refining the interior.

This is true by default, but may be set to false to make the mesh boundary more predictable (and so easier to stitch to other meshes) later.

Reimplemented from libMesh::TriangulatorInterface.

Definition at line 97 of file poly2tri_triangulator.h.

References _refine_bdy_allowed.

Referenced by MeshTriangulationTest::testPoly2TriHolesInterpRefined().

98  { _refine_bdy_allowed = refine_bdy_allowed; }
bool _refine_bdy_allowed
Whether to allow boundary refinement.

◆ set_verify_hole_boundaries()

void libMesh::TriangulatorInterface::set_verify_hole_boundaries ( bool  v)
inlineinherited

Verifying that hole boundaries don't cross the outer boundary or each other is something like O(N_bdys^2*N_points_per_bdy^2), so we only do it if requested.

Definition at line 262 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_verify_hole_boundaries.

Referenced by MeshTriangulationTest::commonSettings().

bool _verify_hole_boundaries
Flag which tells if we want to check hole geometry.

◆ should_refine_elem()

bool libMesh::Poly2TriTriangulator::should_refine_elem ( Elem elem)
protected

Returns true if the given element ought to be refined according to current criteria.

Definition at line 1478 of file poly2tri_triangulator.C.

References libMesh::TriangulatorInterface::desired_area(), libMesh::TriangulatorInterface::get_auto_area_function(), get_desired_area_function(), libMesh::TriangulatorInterface::has_auto_area_function(), libMesh::libmesh_assert(), libMesh::make_range(), libMesh::Elem::n_vertices(), libMesh::Elem::point(), libMesh::Real, and libMesh::Elem::volume().

Referenced by insert_refinement_points().

1479 {
1480  const Real min_area_target = this->desired_area();
1482 
1483  // If this isn't a question, why are we here?
1484  libmesh_assert(min_area_target > 0 ||
1485  area_func != nullptr ||
1486  this->has_auto_area_function());
1487 
1488  const Real area = elem.volume();
1489 
1490  // If we don't have position-dependent area targets we can make a
1491  // decision quickly
1492  if (!area_func && !this->has_auto_area_function())
1493  return (area > min_area_target);
1494  else if(area_func && this->has_auto_area_function())
1495  libmesh_warning("WARNING: both desired are function and automatic area function are set. Using automatic area function.");
1496 
1497  // If we do?
1498  //
1499  // See if we're meeting the local area target at all the elem
1500  // vertices first
1501  for (auto v : make_range(elem.n_vertices()))
1502  {
1503  // If we have an auto area function, we'll use it and override other area options
1504  const Real local_area_target = (*area_func)(elem.point(v));
1505  libmesh_error_msg_if
1506  (local_area_target <= 0,
1507  "Non-positive desired element areas are unachievable");
1508  if (area > local_area_target)
1509  return true;
1510  }
1511 
1512  // If our vertices are happy, it's still possible that our interior
1513  // isn't. Are we allowed not to bother checking it?
1514  if (!min_area_target)
1515  return false;
1516 
1517  libmesh_not_implemented_msg
1518  ("Combining a minimum desired_area with an area function isn't yet supported.");
1519 }
FunctionBase< Real > * get_auto_area_function()
Get the auto area function.
virtual FunctionBase< Real > * get_desired_area_function() override
Get the function giving desired triangle area as a function of position, or nullptr if no such functi...
bool has_auto_area_function()
Whether or not an auto area function has been set.
libmesh_assert(ctx)
Real & desired_area()
Sets and/or gets the desired triangle area.
virtual unsigned int n_vertices() const =0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual Real volume() const
Definition: elem.C:3429
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
const Point & point(const unsigned int i) const
Definition: elem.h:2453

◆ smooth_after_generating()

bool& libMesh::TriangulatorInterface::smooth_after_generating ( )
inlineinherited

Sets/gets flag which tells whether to do two steps of Laplace mesh smoothing after generating the grid.

Definition at line 244 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_smooth_after_generating.

Referenced by MeshTriangulationTest::commonSettings(), and triangulate_domain().

244 {return _smooth_after_generating;}
bool _smooth_after_generating
Flag which tells whether we should smooth the mesh after it is generated.

◆ total_hole_points()

unsigned int libMesh::TriangulatorInterface::total_hole_points ( )
protectedinherited

Helper function to count points in and verify holes.

Definition at line 454 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_holes.

Referenced by libMesh::TriangleInterface::triangulate().

455 {
456  // If the holes vector is non-nullptr (and non-empty) we need to determine
457  // the number of additional points which the holes will add to the
458  // triangulation.
459  // Note that the number of points is always equal to the number of segments
460  // that form the holes.
461  unsigned int n_hole_points = 0;
462 
463  if (_holes)
464  for (const auto & hole : *_holes)
465  {
466  n_hole_points += hole->n_points();
467  // A hole at least has one enclosure.
468  // Points on enclosures are ordered so that we can add segments implicitly.
469  // Elements in segment_indices() indicates the starting points of all enclosures.
470  // The last element in segment_indices() is the number of total points.
471  libmesh_assert_greater(hole->segment_indices().size(), 1);
472  libmesh_assert_equal_to(hole->segment_indices().back(), hole->n_points());
473  }
474 
475  return n_hole_points;
476 }
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.

◆ triangulate()

void libMesh::Poly2TriTriangulator::triangulate ( )
overridevirtual

Internally, this calls the poly2tri triangulation code in a loop, inserting our owner Steiner points as necessary to promote mesh quality.

Implements libMesh::TriangulatorInterface.

Definition at line 367 of file poly2tri_triangulator.C.

References libMesh::TriangulatorInterface::_elem_type, libMesh::TriangulatorInterface::_markers, libMesh::TriangulatorInterface::_mesh, _n_boundary_nodes, libMesh::TriangulatorInterface::_regions, libMesh::TriangulatorInterface::_smooth_after_generating, libMesh::TriangulatorInterface::_triangulation_type, libMesh::ParallelObject::comm(), libMesh::TriangulatorInterface::elems_to_segments(), libMesh::TriangulatorInterface::increase_triangle_order(), libMesh::TriangulatorInterface::insert_any_extra_boundary_points(), insert_refinement_points(), libMesh::TriangulatorInterface::nodes_to_segments(), libMesh::MeshBase::prepare_for_use(), libMesh::TriangulatorInterface::PSLG, libMesh::MeshBase::set_mesh_dimension(), libMesh::LaplaceMeshSmoother::smooth(), libMesh::TRI3, libMesh::TRI6, libMesh::TRI7, and triangulate_current_points().

Referenced by main(), and MeshTriangulationTest::testPoly2TriRefinementBase().

368 {
369  LOG_SCOPE("triangulate()", "Poly2TriTriangulator");
370 
371  // We only operate on serialized meshes. And it's not safe to
372  // serialize earlier, because it would then be possible for the user
373  // to re-parallelize the mesh in between there and here.
374  MeshSerializer serializer(_mesh);
375 
376  // We don't yet support every set of Triangulator options in the
377  // poly2tri implementation
378 
379  // We don't support convex hull triangulation, only triangulation of
380  // (implicitly defined, by node ordering) polygons (with holes if
381  // requested)
382  if (_triangulation_type != PSLG)
383  libmesh_not_implemented();
384 
385  // We currently don't handle region specifications
386  if (_regions)
387  libmesh_not_implemented();
388 
389  // We won't support quads any time soon, or 1D/3D in this interface
390  // ever.
391  if (_elem_type != TRI3 &&
392  _elem_type != TRI6 &&
393  _elem_type != TRI7)
394  libmesh_not_implemented();
395 
396  // If we have no explicit segments defined, we may get them from
397  // mesh elements
398  this->elems_to_segments();
399 
400  // If we *still* have no explicit segments defined, we get them from
401  // the order of nodes.
403 
404  // Insert additional new points in between existing boundary points,
405  // if that is requested and reasonable
407 
408  // Triangulate the points we have, then see if we need to add more;
409  // repeat until we don't need to add more.
410  //
411  // This is currently done redundantly in parallel; make sure no
412  // processor quits before the others.
413  do
414  {
415  libmesh_parallel_only(_mesh.comm());
417  }
418  while (this->insert_refinement_points());
419 
420  libmesh_parallel_only(_mesh.comm());
421 
422  // Okay, we really do need to support boundary ids soon, but we
423  // don't yet
424  if (_markers)
425  libmesh_not_implemented();
426 
428 
429  // To the naked eye, a few smoothing iterations usually looks better,
430  // so we do this by default unless the user says not to.
431  if (this->_smooth_after_generating)
433 
434  // The user might have requested TRI6 or higher instead of TRI3. We
435  // can do this before prepare_for_use() because all we need for it
436  // is find_neighbors(), which we did in insert_refinement_points()
437  this->increase_triangle_order();
438 
439  // Prepare the mesh for use before returning. This ensures (among
440  // other things) that it is partitioned and therefore users can
441  // iterate over local elements, etc.
443 }
virtual void smooth() override
Redefinition of the smooth function from the base class.
void insert_any_extra_boundary_points()
Helper function to add extra points (midpoints of initial segments) to a PSLG triangulation.
const std::vector< int > * _markers
Boundary markers.
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly ecreated (or read) mesh for use.
Definition: mesh_base.C:759
const std::vector< Region * > * _regions
A pointer to a vector of Regions*s.
const Parallel::Communicator & comm() const
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
void elems_to_segments()
Helper function to create PSLG segments from our other boundary-defining options (1D mesh edges...
This class defines the data structures necessary for Laplace smoothing.
ElemType _elem_type
The type of elements to generate.
void triangulate_current_points()
Triangulate the current mesh and hole points.
bool insert_refinement_points()
Add Steiner points as new mesh nodes, as necessary to refine an existing trangulation.
void set_mesh_dimension(unsigned char d)
Resets the logical dimension of the mesh.
Definition: mesh_base.h:275
void increase_triangle_order()
Helper function to upconvert Tri3 to any higher order triangle type if requested via _elem_type...
Triangulate the interior of a Planar Straight Line Graph, which is defined implicitly by the order of...
void nodes_to_segments(dof_id_type max_node_id)
Helper function to create PSLG segments from our node ordering, up to the maximum node id...
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
bool _smooth_after_generating
Flag which tells whether we should smooth the mesh after it is generated.
dof_id_type _n_boundary_nodes
Keep track of how many mesh nodes are boundary nodes.
TriangulationType _triangulation_type
The type of triangulation to perform: choices are: convex hull PSLG.

◆ triangulate_current_points()

void libMesh::Poly2TriTriangulator::triangulate_current_points ( )
protected

Triangulate the current mesh and hole points.

Definition at line 490 of file poly2tri_triangulator.C.

References libMesh::TriangulatorInterface::_holes, libMesh::TriangulatorInterface::_mesh, _n_boundary_nodes, libMesh::MeshBase::add_elem(), libMesh::MeshBase::add_point(), libMesh::BoundaryInfo::add_side(), libMesh::Elem::build_with_id(), libMesh::BoundaryInfo::clear(), libMesh::MeshBase::clear_elems(), distance(), libMesh::MeshBase::get_boundary_info(), libMesh::DofObject::id(), libMesh::DofObject::invalid_id, libMesh::Elem::is_flipped(), libMesh::libmesh_assert(), libMesh::make_range(), libMesh::MeshBase::max_node_id(), libMesh::MeshBase::n_nodes(), libMesh::TriangulatorInterface::Hole::n_points(), libMesh::TriangulatorInterface::Hole::point(), libMesh::MeshBase::query_node_ptr(), replaced_holes, libMesh::TriangulatorInterface::segments, libMesh::Elem::set_node(), and libMesh::TRI3.

Referenced by triangulate().

491 {
492  LOG_SCOPE("triangulate_current_points()", "Poly2TriTriangulator");
493 
494  // Will the triangulation have holes?
495  const std::size_t n_holes = _holes != nullptr ? _holes->size() : 0;
496 
497  // Mapping from Poly2Tri points to libMesh nodes, so we can get the
498  // connectivity translated back later.
499  std::map<const p2t::Point, Node *, P2TPointCompare> point_node_map;
500 
501  // Poly2Tri data structures
502  // Poly2Tri takes vectors of pointers-to-Point for some reason, but
503  // we'll just make those shims to vectors of Point rather than
504  // individually/manually heap allocating everything.
505  std::vector<p2t::Point> outer_boundary_points;
506  std::vector<std::vector<p2t::Point>> inner_hole_points(n_holes);
507 
509  libmesh_error_msg_if
510  (!nn, "Poly2TriTriangulator cannot triangulate an empty mesh!");
511 
512  // Unless we're using an explicit segments list, we assume node ids
513  // are contiguous here.
514  if (this->segments.empty())
515  libmesh_error_msg_if
516  (_mesh.n_nodes() != nn,
517  "Poly2TriTriangulator needs contiguous node ids or explicit segments!");
518 
519  // And if we have more nodes than outer boundary points, the rest
520  // may be interior "Steiner points". We use a set here so we can
521  // cheaply search and erase from it later, when we're identifying
522  // hole points.
523  std::set<p2t::Point, P2TPointCompare> steiner_points;
524 
525  // If we were asked to use all mesh nodes as boundary nodes, now's
526  // the time to see how many that is.
528  {
530  libmesh_assert_equal_to(std::ptrdiff_t(_n_boundary_nodes),
531  std::distance(_mesh.nodes_begin(),
532  _mesh.nodes_end()));
533 
534  }
535  else
536  libmesh_assert_less_equal(_n_boundary_nodes,
537  _mesh.n_nodes());
538 
539  // Prepare poly2tri points for our nodes, sorted into outer boundary
540  // points and interior Steiner points.
541 
542  if (this->segments.empty())
543  {
544  // If we have no segments even after taking elems into account,
545  // the nodal id ordering defines our outer polyline ordering
546  for (auto & node : _mesh.node_ptr_range())
547  {
548  const p2t::Point pt = to_p2t(*node);
549 
550  // If we're out of boundary nodes, the rest are going to be
551  // Steiner points or hole points
552  if (node->id() < _n_boundary_nodes)
553  outer_boundary_points.push_back(pt);
554  else
555  steiner_points.insert(pt);
556 
557  // We're not going to support overlapping nodes on the boundary
558  if (point_node_map.count(pt))
559  libmesh_not_implemented();
560 
561  point_node_map.emplace(pt, node);
562  }
563  }
564  // If we have explicit segments defined, that's our outer polyline
565  // ordering:
566  else
567  {
568  // Let's make sure our segments are in order
570 
571  // Add nodes from every segment, in order, to the outer polyline
572  for (auto [segment_start, segment_end] : this->segments)
573  {
574  if (last_id != DofObject::invalid_id)
575  libmesh_error_msg_if(segment_start != last_id,
576  "Disconnected triangulator segments");
577  last_id = segment_end;
578 
579  Node * node = _mesh.query_node_ptr(segment_start);
580 
581  libmesh_error_msg_if(!node,
582  "Triangulator segments reference nonexistent node id " <<
583  segment_start);
584 
585  outer_boundary_points.emplace_back(double((*node)(0)), double((*node)(1)));
586  p2t::Point * pt = &outer_boundary_points.back();
587 
588  // We're not going to support overlapping nodes on the boundary
589  if (point_node_map.count(*pt))
590  libmesh_not_implemented_msg
591  ("Triangulating overlapping boundary nodes is unsupported");
592 
593  point_node_map.emplace(*pt, node);
594  }
595 
596  libmesh_error_msg_if(last_id != this->segments[0].first,
597  "Non-closed-loop triangulator segments");
598 
599  // If we have points that aren't in any segments, those will be
600  // Steiner points
601  for (auto & node : _mesh.node_ptr_range())
602  {
603  const p2t::Point pt = to_p2t(*node);
604  if (const auto it = point_node_map.find(pt);
605  it == point_node_map.end())
606  {
607  steiner_points.insert(pt);
608  point_node_map.emplace(pt, node);
609  }
610  else
611  libmesh_assert_equal_to(it->second, node);
612  }
613  }
614 
615  // If we have any elements from a previous triangulation, we're
616  // going to replace them with our own. If we have any elements that
617  // were used to create our segments, we're done creating and we no
618  // longer need them.
619  _mesh.clear_elems();
620 
621  // Keep track of what boundary ids we want to assign to each new
622  // triangle. We'll give the outer boundary BC 0, and give holes ids
623  // starting from 1. We've already got the point_node_map to find
624  // nodes, so we can just key on pairs of node ids to identify a side.
625  std::unordered_map<std::pair<dof_id_type,dof_id_type>,
626  boundary_id_type, libMesh::hash> side_boundary_id;
627 
628  const boundary_id_type outer_bcid = 0;
629  const std::size_t n_outer = outer_boundary_points.size();
630 
631  for (auto i : make_range(n_outer))
632  {
633  const Node * node1 =
634  libmesh_map_find(point_node_map, outer_boundary_points[i]),
635  * node2 =
636  libmesh_map_find(point_node_map, outer_boundary_points[(i+1)%n_outer]);
637 
638  side_boundary_id.emplace(std::make_pair(node1->id(),
639  node2->id()),
640  outer_bcid);
641  }
642 
643  // Create poly2tri triangulator with our mesh points
644  std::vector<p2t::Point *> outer_boundary_pointers(n_outer);
645  std::transform(outer_boundary_points.begin(),
646  outer_boundary_points.end(),
647  outer_boundary_pointers.begin(),
648  [](p2t::Point & p) { return &p; });
649 
650 
651  // Make sure shims for holes last as long as the CDT does; the
652  // poly2tri headers don't make clear whether or not they're hanging
653  // on to references to these vectors, and it would be reasonable for
654  // them to do so.
655  std::vector<std::vector<p2t::Point *>> inner_hole_pointers(n_holes);
656 
657  p2t::CDT cdt{outer_boundary_pointers};
658 
659  // Add any holes
660  for (auto h : make_range(n_holes))
661  {
662  const Hole * initial_hole = (*_holes)[h];
663  auto it = replaced_holes.find(initial_hole);
664  const Hole & our_hole =
665  (it == replaced_holes.end()) ?
666  *initial_hole : *it->second;
667  auto & poly2tri_hole = inner_hole_points[h];
668 
669  for (auto i : make_range(our_hole.n_points()))
670  {
671  Point p = our_hole.point(i);
672  poly2tri_hole.emplace_back(to_p2t(p));
673 
674  const auto & pt = poly2tri_hole.back();
675 
676  // This won't be a steiner point.
677  steiner_points.erase(pt);
678 
679  // If we see a hole point already in the mesh, we'll share
680  // that node. This might be a problem if it's a boundary
681  // node, but it might just be the same hole point already
682  // added during a previous triangulation refinement step.
683  if (point_node_map.count(pt))
684  {
685  libmesh_assert_equal_to
686  (point_node_map[pt],
687  _mesh.query_node_ptr(point_node_map[pt]->id()));
688  }
689  else
690  {
691  Node * node = _mesh.add_point(p, nn++);
692  point_node_map[pt] = node;
693  }
694  }
695 
696  const boundary_id_type inner_bcid = h+1;
697  const std::size_t n_inner = poly2tri_hole.size();
698 
699  for (auto i : make_range(n_inner))
700  {
701  const Node * node1 =
702  libmesh_map_find(point_node_map, poly2tri_hole[i]),
703  * node2 =
704  libmesh_map_find(point_node_map, poly2tri_hole[(i+1)%n_inner]);
705 
706  side_boundary_id.emplace(std::make_pair(node1->id(),
707  node2->id()),
708  inner_bcid);
709  }
710 
711  auto & poly2tri_ptrs = inner_hole_pointers[h];
712  poly2tri_ptrs.resize(n_inner);
713 
714  std::transform(poly2tri_hole.begin(),
715  poly2tri_hole.end(),
716  poly2tri_ptrs.begin(),
717  [](p2t::Point & p) { return &p; });
718 
719  cdt.AddHole(poly2tri_ptrs);
720  }
721 
722  // Add any steiner points. We had them in a set, but post-C++11
723  // that won't give us non-const element access (even if we
724  // pinky-promise not to change the elements in any way that affects
725  // our Comparator), and Poly2Tri wants non-const elements (to store
726  // edge data?), so we need to move them here.
727  std::vector<p2t::Point> steiner_vector(steiner_points.begin(), steiner_points.end());
728  steiner_points.clear();
729  for (auto & p : steiner_vector)
730  cdt.AddPoint(&p);
731 
732  // Triangulate!
733  cdt.Triangulate();
734 
735  // Get poly2tri triangles, turn them into libMesh triangles
736  std::vector<p2t::Triangle *> triangles = cdt.GetTriangles();
737 
738  // Do our own numbering, even on DistributedMesh
739  dof_id_type next_id = 0;
740 
741  BoundaryInfo & boundary_info = _mesh.get_boundary_info();
742  boundary_info.clear();
743 
744  // Add the triangles to our Mesh data structure.
745  for (auto ptri_ptr : triangles)
746  {
747  p2t::Triangle & ptri = *ptri_ptr;
748 
749  // We always use TRI3 here, since that's what we have nodes for;
750  // if we need a higher order we can convert at the end.
751  auto elem = Elem::build_with_id(TRI3, next_id++);
752  for (auto v : make_range(3))
753  {
754  const p2t::Point & vertex = *ptri.GetPoint(v);
755 
756  Node * node = libmesh_map_find(point_node_map, vertex);
757  libmesh_assert(node);
758  elem->set_node(v, node);
759  }
760 
761  // We expect a consistent triangle orientation
762  libmesh_assert(!elem->is_flipped());
763 
764  Elem * added_elem = _mesh.add_elem(std::move(elem));
765 
766  for (auto v : make_range(3))
767  {
768  const Node & node1 = added_elem->node_ref(v),
769  & node2 = added_elem->node_ref((v+1)%3);
770 
771  auto it = side_boundary_id.find(std::make_pair(node1.id(), node2.id()));
772  if (it == side_boundary_id.end())
773  it = side_boundary_id.find(std::make_pair(node2.id(), node1.id()));
774  if (it != side_boundary_id.end())
775  boundary_info.add_side(added_elem, v, it->second);
776  }
777  }
778 }
A Node is like a Point, but with more information.
Definition: node.h:52
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:165
Real distance(const Point &p)
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
int8_t boundary_id_type
Definition: id_types.h:51
dof_id_type id() const
Definition: dof_object.h:828
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
virtual const Node * query_node_ptr(const dof_id_type i) const =0
std::map< const Hole *, std::unique_ptr< ArbitraryHole > > replaced_holes
We might have to replace the user-provided holes with refined versions.
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
libmesh_assert(ctx)
static const dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:482
void clear()
Clears the underlying data structures and restores the object to a pristine state with no data stored...
virtual void clear_elems()=0
Deletes all the element data that is currently stored.
std::vector< std::pair< unsigned int, unsigned int > > segments
When constructing a PSLG, if the node numbers do not define the desired boundary segments implicitly ...
static std::unique_ptr< Elem > build_with_id(const ElemType type, dof_id_type id)
Calls the build() method above with a nullptr parent, and additionally sets the newly-created Elem&#39;s ...
Definition: elem.C:558
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure...
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
dof_id_type _n_boundary_nodes
Keep track of how many mesh nodes are boundary nodes.
virtual dof_id_type max_node_id() const =0
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
virtual dof_id_type n_nodes() const =0
uint8_t dof_id_type
Definition: id_types.h:67

◆ triangulation_type()

TriangulationType& libMesh::TriangulatorInterface::triangulation_type ( )
inlineinherited

Sets and/or gets the desired triangulation type.

Definition at line 205 of file triangulator_interface.h.

References libMesh::TriangulatorInterface::_triangulation_type.

Referenced by libMesh::MeshTools::Generation::build_delaunay_square(), MeshTriangulationTest::commonSettings(), main(), and triangulate_domain().

205 {return _triangulation_type;}
TriangulationType _triangulation_type
The type of triangulation to perform: choices are: convex hull PSLG.

◆ verify_holes()

void libMesh::TriangulatorInterface::verify_holes ( const Hole outer_bdy)
protectedinherited

Helper function to check holes for intersections if requested.

Definition at line 429 of file triangulator_interface.C.

References libMesh::TriangulatorInterface::_holes, libMesh::TriangulatorInterface::Hole::contains(), and libMesh::make_range().

Referenced by libMesh::TriangulatorInterface::elems_to_segments(), and libMesh::TriangulatorInterface::nodes_to_segments().

430 {
431  for (const Hole * hole : *_holes)
432  {
433  for (const Hole * hole2 : *_holes)
434  {
435  if (hole == hole2)
436  continue;
437 
438  for (auto i : make_range(hole2->n_points()))
439  if (hole->contains(hole2->point(i)))
440  libmesh_error_msg
441  ("Found point " << hole2->point(i) <<
442  " on one hole boundary and another's interior");
443  }
444 
445  for (auto i : make_range(hole->n_points()))
446  if (!outer_bdy.contains(hole->point(i)))
447  libmesh_error_msg
448  ("Found point " << hole->point(i) <<
449  " on hole boundary but outside outer boundary");
450  }
451 }
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
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

Member Data Documentation

◆ _auto_area_function

std::unique_ptr<AutoAreaFunction> libMesh::TriangulatorInterface::_auto_area_function
protectedinherited

◆ _bdy_ids

std::set<std::size_t> libMesh::TriangulatorInterface::_bdy_ids
protectedinherited

◆ _desired_area

Real libMesh::TriangulatorInterface::_desired_area
protectedinherited

The desired area for the elements in the resulting mesh.

Definition at line 427 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::desired_area(), and libMesh::TriangleInterface::triangulate().

◆ _desired_area_func

std::unique_ptr<FunctionBase<Real> > libMesh::Poly2TriTriangulator::_desired_area_func
private

Location-dependent area requirements.

Definition at line 149 of file poly2tri_triangulator.h.

Referenced by get_desired_area_function().

◆ _elem_type

ElemType libMesh::TriangulatorInterface::_elem_type
protectedinherited

◆ _holes

const std::vector<Hole*>* libMesh::TriangulatorInterface::_holes
protectedinherited

◆ _insert_extra_points

bool libMesh::TriangulatorInterface::_insert_extra_points
protectedinherited

Flag which tells whether or not to insert additional nodes before triangulation.

This can sometimes be used to "de-regularize" the resulting triangulation.

This flag is supported for backwards compatibility; setting _interpolate_boundary_points = 1 is equivalent.

Definition at line 449 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::get_interpolate_boundary_points(), libMesh::TriangulatorInterface::insert_extra_points(), and libMesh::TriangulatorInterface::set_interpolate_boundary_points().

◆ _interpolate_boundary_points

int libMesh::TriangulatorInterface::_interpolate_boundary_points
protectedinherited

Flag which tells how many additional nodes should be inserted between each pair of original mesh points.

Definition at line 455 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::get_interpolate_boundary_points(), and libMesh::TriangulatorInterface::set_interpolate_boundary_points().

◆ _markers

const std::vector<int>* libMesh::TriangulatorInterface::_markers
protectedinherited

◆ _mesh

UnstructuredMesh& libMesh::TriangulatorInterface::_mesh
protectedinherited

◆ _minimum_angle

Real libMesh::TriangulatorInterface::_minimum_angle
protectedinherited

Minimum angle in triangles.

Definition at line 432 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::minimum_angle(), and libMesh::TriangleInterface::triangulate().

◆ _n_boundary_nodes

dof_id_type libMesh::Poly2TriTriangulator::_n_boundary_nodes
private

Keep track of how many mesh nodes are boundary nodes.

Definition at line 144 of file poly2tri_triangulator.h.

Referenced by insert_refinement_points(), triangulate(), and triangulate_current_points().

◆ _quiet

bool libMesh::TriangulatorInterface::_quiet
protectedinherited

Flag which tells if we want to suppress stdout outputs.

Definition at line 466 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::quiet(), and libMesh::TriangleInterface::triangulate().

◆ _refine_bdy_allowed

bool libMesh::Poly2TriTriangulator::_refine_bdy_allowed
private

Whether to allow boundary refinement.

Definition at line 154 of file poly2tri_triangulator.h.

Referenced by refine_boundary_allowed(), and set_refine_boundary_allowed().

◆ _regions

const std::vector<Region*>* libMesh::TriangulatorInterface::_regions
protectedinherited

A pointer to a vector of Regions*s.

If this is nullptr, there are no regions!

Definition at line 411 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::attach_region_list(), libMesh::TriangleInterface::triangulate(), and triangulate().

◆ _smooth_after_generating

bool libMesh::TriangulatorInterface::_smooth_after_generating
protectedinherited

Flag which tells whether we should smooth the mesh after it is generated.

True by default.

Definition at line 461 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::smooth_after_generating(), libMesh::TriangleInterface::triangulate(), and triangulate().

◆ _triangulation_type

TriangulationType libMesh::TriangulatorInterface::_triangulation_type
protectedinherited

◆ _verify_hole_boundaries

bool libMesh::TriangulatorInterface::_verify_hole_boundaries
protectedinherited

◆ replaced_holes

std::map<const Hole *, std::unique_ptr<ArbitraryHole> > libMesh::Poly2TriTriangulator::replaced_holes
private

We might have to replace the user-provided holes with refined versions.

Definition at line 139 of file poly2tri_triangulator.h.

Referenced by insert_refinement_points(), and triangulate_current_points().

◆ segment_midpoints

std::vector<Point> libMesh::TriangulatorInterface::segment_midpoints
inherited

When constructing a second-order triangulation from a second-order boundary, we may do the triangulation using first-order elements, in which case we need to save midpoint location data in order to reconstruct curvature along boundaries.

Definition at line 287 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::elems_to_segments(), and libMesh::TriangulatorInterface::increase_triangle_order().

◆ segments

std::vector<std::pair<unsigned int, unsigned int> > libMesh::TriangulatorInterface::segments
inherited

When constructing a PSLG, if the node numbers do not define the desired boundary segments implicitly through the ordering of the points, you can use the segments vector to specify the segments explicitly, Ex: unit square numbered counter-clockwise starting from origin segments[0] = (0,1) segments[1] = (1,2) segments[2] = (2,3) segments[3] = (3,0) (For the above case you could actually use the implicit ordering!)

Definition at line 279 of file triangulator_interface.h.

Referenced by libMesh::TriangulatorInterface::elems_to_segments(), libMesh::TriangulatorInterface::increase_triangle_order(), libMesh::TriangulatorInterface::insert_any_extra_boundary_points(), insert_refinement_points(), libMesh::TriangulatorInterface::nodes_to_segments(), MeshTriangulationTest::testTriangulatorSegments(), libMesh::TriangleInterface::triangulate(), and triangulate_current_points().


The documentation for this class was generated from the following files: