libMesh
mesh_triangle_interface.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 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  // Clean up.
374 
375  // Prepare the mesh for use before returning. This ensures (among
376  // other things) that it is partitioned and therefore users can
377  // iterate over local elements, etc.
379 }
380 
381 } // namespace libMesh
382 
383 
384 
385 
386 
387 
388 
389 #endif // LIBMESH_HAVE_TRIANGLE
virtual void triangulate() override
Internally, this calls Triangle&#39;s triangulate routine.
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 created (or read) mesh for use.
Definition: mesh_base.C:824
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
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true, const bool assert_valid=true) override
Other functions from MeshBase requiring re-definition.
UnstructuredMesh & _mesh
Reference to the mesh which is to be created by triangle.
The libMesh namespace provides an interface to certain functionality in the library.
virtual void smooth() override
Redefinition of the smooth function from the base class.
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:413
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