libMesh
mesh_triangle_interface.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 #include "libmesh/libmesh_config.h"
20 
21 #ifdef LIBMESH_HAVE_TRIANGLE
22 
23 // libmesh includes
24 #include "libmesh/mesh_triangle_interface.h"
25 #include "libmesh/unstructured_mesh.h"
26 #include "libmesh/face_tri3.h"
27 #include "libmesh/face_tri6.h"
28 #include "libmesh/mesh_generation.h"
29 #include "libmesh/mesh_smoother_laplace.h"
30 #include "libmesh/boundary_info.h"
31 #include "libmesh/mesh_triangle_holes.h"
32 #include "libmesh/mesh_triangle_wrapper.h"
33 #include "libmesh/enum_elem_type.h"
34 #include "libmesh/enum_to_string.h"
35 
36 // C/C++ includes
37 #include <sstream>
38 
39 
40 namespace libMesh
41 {
42 
43 //
44 // Function definitions for the TrianguleInterface class
45 //
46 
47 // Constructor
50  _extra_flags(""),
51  _serializer(_mesh)
52 {}
53 
54 
55 
56 
57 // Primary function responsible for performing the triangulation
59 {
60  // Will the triangulation have holes?
61  const bool have_holes = ((_holes != nullptr) && (!_holes->empty()));
62 
63  unsigned int n_hole_points = this->total_hole_points();
64 
65  // If we have no explicit segments defined, we may get them from
66  // mesh elements
67  this->elems_to_segments();
68 
69  // If we're doing PSLG without segments, construct them from all our
70  // mesh nodes
72 
73  // Insert additional new points in between existing boundary points,
74  // if that is requested and reasonable
76 
77  // Regardless of whether we added additional points, the set of points to
78  // triangulate is now sitting in the mesh.
79 
80  // Triangle data structure for the mesh
81  TriangleWrapper::triangulateio initial;
82  TriangleWrapper::triangulateio final;
83  TriangleWrapper::triangulateio voronoi;
84 
85  // Pseudo-Constructor for the triangle io structs
86  TriangleWrapper::init(initial);
87  TriangleWrapper::init(final);
88  TriangleWrapper::init(voronoi);
89 
90  initial.numberofpoints = _mesh.n_nodes() + n_hole_points;
91  initial.pointlist = static_cast<REAL*>(std::malloc(initial.numberofpoints * 2 * sizeof(REAL)));
92 
94  {
95  // Implicit segment ordering: One segment per point, including hole points
96  if (this->segments.empty())
97  initial.numberofsegments = initial.numberofpoints;
98 
99  // User-defined segment ordering: One segment per entry in the segments vector
100  else
101  initial.numberofsegments = this->segments.size() + n_hole_points;
102  }
103 
105  initial.numberofsegments = n_hole_points; // One segment for each hole point
106 
107  // Allocate space for the segments (2 int per segment)
108  if (initial.numberofsegments > 0)
109  {
110  initial.segmentlist = static_cast<int *> (std::malloc(initial.numberofsegments * 2 * sizeof(int)));
111  if (_markers)
112  initial.segmentmarkerlist = static_cast<int *> (std::malloc(initial.numberofsegments * sizeof(int)));
113  }
114 
115 
116  // Copy all the holes' points and segments into the triangle struct.
117 
118  // The hole_offset is a constant offset into the points vector which points
119  // past the end of the last hole point added.
120  unsigned int hole_offset=0;
121 
122  if (have_holes)
123  for (const auto & hole : *_holes)
124  {
125  for (unsigned int ctr=0, h=0, i=0, hsism=hole->segment_indices().size()-1; i<hsism; ++i)
126  {
127  unsigned int begp = hole_offset + hole->segment_indices()[i];
128  unsigned int endp = hole->segment_indices()[i+1];
129 
130  for (; h<endp; ctr+=2, ++h)
131  {
132  Point p = hole->point(h);
133 
134  const unsigned int index0 = 2*hole_offset+ctr;
135  const unsigned int index1 = 2*hole_offset+ctr+1;
136 
137  // Save the x,y locations in the triangle struct.
138  initial.pointlist[index0] = p(0);
139  initial.pointlist[index1] = p(1);
140 
141  // Set the points which define the segments
142  initial.segmentlist[index0] = hole_offset+h;
143  initial.segmentlist[index1] = (h == endp - 1) ? begp : hole_offset + h + 1; // wrap around
144  if (_markers)
145  // 1 is reserved for boundaries of holes
146  initial.segmentmarkerlist[hole_offset+h] = 1;
147  }
148  }
149 
150  // Update the hole_offset for the next hole
151  hole_offset += hole->n_points();
152  }
153 
154 
155  // Copy all the non-hole points and segments into the triangle struct.
156  std::vector<unsigned int> libmesh_id_to_pointlist_index(_mesh.max_node_id());
157  {
158  dof_id_type ctr=0;
159  for (auto & node : _mesh.node_ptr_range())
160  {
161  dof_id_type index = 2*hole_offset + ctr;
162 
163  // Set x,y values in pointlist
164  initial.pointlist[index] = (*node)(0);
165  initial.pointlist[index+1] = (*node)(1);
166  libmesh_id_to_pointlist_index[node->id()] = ctr/2;
167 
168  // If the user requested a PSLG, the non-hole points are also segments
170  {
171  // Use implicit ordering to define segments
172  if (this->segments.empty())
173  {
174  dof_id_type n = ctr/2; // ctr is always even
175  initial.segmentlist[index] = hole_offset+n;
176  initial.segmentlist[index+1] = (n==_mesh.n_nodes()-1) ? hole_offset : hole_offset+n+1; // wrap around
177  if (_markers)
178  initial.segmentmarkerlist[hole_offset + n] = (*_markers)[n];
179  }
180  }
181 
182  ctr +=2;
183  }
184  }
185 
186 
187  // If the user provided it, use his ordering to define the segments
188  for (std::size_t ctr=0, s=0, ss=this->segments.size(); s<ss; ctr+=2, ++s)
189  {
190  const unsigned int index0 = 2*hole_offset+ctr;
191  const unsigned int index1 = 2*hole_offset+ctr+1;
192 
193  initial.segmentlist[index0] = hole_offset +
194  libmesh_id_to_pointlist_index[this->segments[s].first];
195  initial.segmentlist[index1] = hole_offset +
196  libmesh_id_to_pointlist_index[this->segments[s].second];
197  if (_markers)
198  initial.segmentmarkerlist[hole_offset + s] = (*_markers)[s];
199  }
200 
201 
202 
203  // Tell the input struct about the holes
204  if (have_holes)
205  {
206  initial.numberofholes = _holes->size();
207  initial.holelist = static_cast<REAL*>(std::malloc(initial.numberofholes * 2 * sizeof(REAL)));
208  for (std::size_t i=0, ctr=0, hs=_holes->size(); i<hs; ++i, ctr+=2)
209  {
210  Point inside_point = (*_holes)[i]->inside();
211  initial.holelist[ctr] = inside_point(0);
212  initial.holelist[ctr+1] = inside_point(1);
213  }
214  }
215 
216  if (_regions)
217  {
218  initial.numberofregions = _regions->size();
219  initial.regionlist = static_cast<REAL*>(std::malloc(initial.numberofregions * 4 * sizeof(REAL)));
220  for (std::size_t i=0, ctr=0, rs=_regions->size(); i<rs; ++i, ctr+=4)
221  {
222  Point inside_point = (*_regions)[i]->inside();
223  initial.regionlist[ctr] = inside_point(0);
224  initial.regionlist[ctr+1] = inside_point(1);
225  initial.regionlist[ctr+2] = (*_regions)[i]->attribute();
226  initial.regionlist[ctr+3] = (*_regions)[i]->max_area();
227  }
228  }
229 
230  // Set the triangulation flags.
231  // c ~ enclose convex hull with segments
232  // z ~ use zero indexing
233  // B ~ Suppresses boundary markers in the output
234  // Q ~ run in "quiet" mode
235  // p ~ Triangulates a Planar Straight Line Graph
236  // If the `p' switch is used, `segmentlist' must point to a list of
237  // segments, `numberofsegments' must be properly set, and
238  // `segmentmarkerlist' must either be set to nullptr (in which case all
239  // markers default to zero), or must point to a list of markers.
240  // D ~ Conforming Delaunay: use this switch if you want all triangles
241  // in the mesh to be Delaunay, and not just constrained Delaunay
242  // q ~ Quality mesh generation with no angles smaller than 20 degrees.
243  // An alternate minimum angle may be specified after the q
244  // a ~ Imposes a maximum triangle area constraint.
245  // -P Suppresses the output .poly file. Saves disk space, but you lose the ability to maintain
246  // constraining segments on later refinements of the mesh.
247  // -e Outputs (to an .edge file) a list of edges of the triangulation.
248  // -v Outputs the Voronoi diagram associated with the triangulation.
249  // Create the flag strings, depends on element type
250  std::ostringstream flags;
251 
252  // Default flags always used
253  flags << "z";
254 
255  if (_quiet)
256  flags << "QP";
257  else
258  flags << "V";
259 
260  if (_markers)
261  flags << "ev";
262 
263  // Flags which are specific to the type of triangulation
264  switch (_triangulation_type)
265  {
267  {
268  flags << "c";
269  break;
270  }
271 
272  case PSLG:
273  {
274  flags << "p";
275  break;
276  }
277 
279  libmesh_error_msg("ERROR: INVALID_TRIANGULATION_TYPE selected!");
280 
281  default:
282  libmesh_error_msg("Unrecognized _triangulation_type");
283  }
284 
285 
286  // Flags specific to the type of element
287  switch (_elem_type)
288  {
289  case TRI3:
290  {
291  // do nothing.
292  break;
293  }
294 
295  case TRI6:
296  {
297  flags << "o2";
298  break;
299  }
300 
301  default:
302  libmesh_error_msg("ERROR: Unrecognized triangular element type == " << Utility::enum_to_string(_elem_type));
303  }
304 
305 
306  // If we do have holes and the user asked to GENERATE_CONVEX_HULL,
307  // need to add the p flag so the triangulation respects those segments.
308  if ((_triangulation_type==GENERATE_CONVEX_HULL) && (have_holes))
309  flags << "p";
310 
311  // Finally, add the area constraint
312  if (_desired_area > TOLERANCE)
313  flags << "a" << std::fixed << _desired_area;
314 
315  // add minimum angle constraint
317  flags << "q" << std::fixed << _minimum_angle;
318 
319  if (_regions)
320  flags << "Aa";
321 
322  // add user provided extra flags
323  if (_extra_flags.size() > 0)
324  flags << _extra_flags;
325 
326  // Refine the initial output to conform to the area constraint
327  if (_markers)
328  {
329  // need Voronoi to generate boundary information
330  TriangleWrapper::triangulate(const_cast<char *>(flags.str().c_str()),
331  &initial,
332  &final,
333  &voronoi);
334 
335  // Send the information computed by Triangle to the Mesh.
337  _mesh,
338  _elem_type,
339  &voronoi);
340  }
341  else
342  {
343  TriangleWrapper::triangulate(const_cast<char *>(flags.str().c_str()),
344  &initial,
345  &final,
346  nullptr);
347  // Send the information computed by Triangle to the Mesh.
349  _mesh,
350  _elem_type);
351  }
352 
353 
355 
356  // The user might have requested TRI6 or higher instead of TRI3. If
357  // so then we'll need to get our neighbor pointers in order ahead of
358  // time for increase_triangle_order() to use.
359  if (_elem_type != TRI3)
360  {
362  this->increase_triangle_order();
363  }
364 
365  // To the naked eye, a few smoothing iterations usually looks better,
366  // so we do this by default unless the user says not to.
367  if (this->_smooth_after_generating)
369 
370 
371  // Clean up.
375 
376  // Prepare the mesh for use before returning. This ensures (among
377  // other things) that it is partitioned and therefore users can
378  // iterate over local elements, etc.
380 }
381 
382 } // namespace libMesh
383 
384 
385 
386 
387 
388 
389 
390 #endif // LIBMESH_HAVE_TRIANGLE
virtual void triangulate() override
Internally, this calls Triangle&#39;s triangulate routine.
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.
static constexpr Real TOLERANCE
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
Real _minimum_angle
Minimum angle in triangles.
const std::vector< Region * > * _regions
A pointer to a vector of Regions*s.
std::string _extra_flags
Additional flags to be passed to triangle.
MeshBase & mesh
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.
The libMesh namespace provides an interface to certain functionality in the library.
Real _desired_area
The desired area for the elements in the resulting mesh.
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.
bool _quiet
Flag which tells if we want to suppress stdout outputs.
The UnstructuredMesh class is derived from the MeshBase class.
void copy_tri_to_mesh(const triangulateio &triangle_data_input, UnstructuredMesh &mesh_output, const ElemType type, const triangulateio *voronoi=nullptr)
Copies triangulation data computed by triangle from a triangulateio object to a LibMesh mesh...
const std::vector< Hole * > * _holes
A pointer to a vector of Hole*s.
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
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...
First generate a convex hull from the set of points passed in, and then triangulate this set of point...
unsigned int total_hole_points()
Helper function to count points in and verify holes.
std::string enum_to_string(const T e)
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 ...
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...
bool _smooth_after_generating
Flag which tells whether we should smooth the mesh after it is generated.
void destroy(triangulateio &t, IO_Type)
Frees any memory which has been dynamically allocated by Triangle.
virtual dof_id_type max_node_id() const =0
TriangulationType _triangulation_type
The type of triangulation to perform: choices are: convex hull PSLG.
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
virtual dof_id_type n_nodes() const =0
TriangleInterface(UnstructuredMesh &mesh)
The constructor.
uint8_t dof_id_type
Definition: id_types.h:67