mesh_triangle_interface.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2018 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 // C/C++ includes
24 #include <sstream>
25 
28 #include "libmesh/face_tri3.h"
29 #include "libmesh/face_tri6.h"
32 #include "libmesh/boundary_info.h"
35 #include "libmesh/enum_elem_type.h"
36 
37 namespace libMesh
38 {
39 //
40 // Function definitions for the TriangleInterface class
41 //
42 
43 // Constructor
45  : _mesh(mesh),
46  _holes(nullptr),
47  _elem_type(TRI3),
48  _desired_area(0.1),
49  _minimum_angle(20.0),
50  _extra_flags(""),
51  _triangulation_type(GENERATE_CONVEX_HULL),
52  _insert_extra_points(false),
53  _smooth_after_generating(true),
54  _serializer(_mesh)
55 {}
56 
57 
58 
59 // Primary function responsible for performing the triangulation
61 {
62  // Will the triangulation have holes?
63  const bool have_holes = ((_holes != nullptr) && (!_holes->empty()));
64 
65  // If the initial PSLG is really simple, e.g. an L-shaped domain or
66  // a square/rectangle, the resulting triangulation may be very
67  // "structured" looking. Sometimes this is a problem if your
68  // intention is to work with an "unstructured" looking grid. We can
69  // attempt to work around this limitation by inserting midpoints
70  // into the original PSLG. Inserting additional points into a
71  // set of points meant to be a convex hull usually makes less sense.
72 
73  // May or may not need to insert new points ...
75  {
76  // Make a copy of the original points from the Mesh
77  std::vector<Point> original_points;
78  original_points.reserve (_mesh.n_nodes());
79  for (auto & node : _mesh.node_ptr_range())
80  original_points.push_back(*node);
81 
82  // Clear out the mesh
83  _mesh.clear();
84 
85  // Make sure the new Mesh will be 2D
87 
88  // Insert a new point on each PSLG at some random location
89  // np=index into new points vector
90  // n =index into original points vector
91  for (std::size_t np=0, n=0; np<2*original_points.size(); ++np)
92  {
93  // the even entries are the original points
94  if (np%2==0)
95  _mesh.add_point(original_points[n++]);
96 
97  else // the odd entries are the midpoints of the original PSLG segments
98  _mesh.add_point ((original_points[n] + original_points[n-1])/2);
99  }
100  }
101 
102  // Regardless of whether we added additional points, the set of points to
103  // triangulate is now sitting in the mesh.
104 
105  // If the holes vector is non-nullptr (and non-empty) we need to determine
106  // the number of additional points which the holes will add to the
107  // triangulation.
108  unsigned int n_hole_points = 0;
109 
110  if (have_holes)
111  {
112  for (std::size_t i=0; i<_holes->size(); ++i)
113  n_hole_points += (*_holes)[i]->n_points();
114  }
115 
116  // Triangle data structure for the mesh
117  TriangleWrapper::triangulateio initial;
118  TriangleWrapper::triangulateio final;
119 
120  // Pseudo-Constructor for the triangle io structs
121  TriangleWrapper::init(initial);
122  TriangleWrapper::init(final);
123 
124  initial.numberofpoints = _mesh.n_nodes() + n_hole_points;
125  initial.pointlist = static_cast<REAL*>(std::malloc(initial.numberofpoints * 2 * sizeof(REAL)));
126 
128  {
129  // Implicit segment ordering: One segment per point, including hole points
130  if (this->segments.empty())
131  initial.numberofsegments = initial.numberofpoints;
132 
133  // User-defined segment ordering: One segment per entry in the segments vector
134  else
135  initial.numberofsegments = this->segments.size();
136  }
137 
139  initial.numberofsegments = n_hole_points; // One segment for each hole point
140 
141  // Debugging
142  // libMesh::out << "Number of segments set to: " << initial.numberofsegments << std::endl;
143 
144  // Allocate space for the segments (2 int per segment)
145  if (initial.numberofsegments > 0)
146  {
147  initial.segmentlist = static_cast<int *> (std::malloc(initial.numberofsegments * 2 * sizeof(int)));
148  }
149 
150 
151  // Copy all the holes' points and segments into the triangle struct.
152 
153  // The hole_offset is a constant offset into the points vector which points
154  // past the end of the last hole point added.
155  unsigned int hole_offset=0;
156 
157  if (have_holes)
158  for (std::size_t i=0; i<_holes->size(); ++i)
159  {
160  for (unsigned int ctr=0, h=0; h<(*_holes)[i]->n_points(); ctr+=2, ++h)
161  {
162  Point p = (*_holes)[i]->point(h);
163 
164  const unsigned int index0 = 2*hole_offset+ctr;
165  const unsigned int index1 = 2*hole_offset+ctr+1;
166 
167  // Save the x,y locations in the triangle struct.
168  initial.pointlist[index0] = p(0);
169  initial.pointlist[index1] = p(1);
170 
171  // Set the points which define the segments
172  initial.segmentlist[index0] = hole_offset+h;
173  initial.segmentlist[index1] = (h==(*_holes)[i]->n_points()-1) ? hole_offset : hole_offset+h+1; // wrap around
174  }
175 
176  // Update the hole_offset for the next hole
177  hole_offset += (*_holes)[i]->n_points();
178  }
179 
180 
181  // Copy all the non-hole points and segments into the triangle struct.
182  {
183  dof_id_type ctr=0;
184  for (auto & node : _mesh.node_ptr_range())
185  {
186  dof_id_type index = 2*hole_offset + ctr;
187 
188  // Set x,y values in pointlist
189  initial.pointlist[index] = (*node)(0);
190  initial.pointlist[index+1] = (*node)(1);
191 
192  // If the user requested a PSLG, the non-hole points are also segments
194  {
195  // Use implicit ordering to define segments
196  if (this->segments.empty())
197  {
198  dof_id_type n = ctr/2; // ctr is always even
199  initial.segmentlist[index] = hole_offset+n;
200  initial.segmentlist[index+1] = (n==_mesh.n_nodes()-1) ? hole_offset : hole_offset+n+1; // wrap around
201  }
202  }
203 
204  ctr +=2;
205  }
206  }
207 
208 
209  // If the user provided it, use his ordering to define the segments
210  for (std::size_t ctr=0, s=0; s<this->segments.size(); ctr+=2, ++s)
211  {
212  const unsigned int index0 = 2*hole_offset+ctr;
213  const unsigned int index1 = 2*hole_offset+ctr+1;
214 
215  initial.segmentlist[index0] = hole_offset + this->segments[s].first;
216  initial.segmentlist[index1] = hole_offset + this->segments[s].second;
217  }
218 
219 
220 
221  // Tell the input struct about the holes
222  if (have_holes)
223  {
224  initial.numberofholes = _holes->size();
225  initial.holelist = static_cast<REAL*>(std::malloc(initial.numberofholes * 2 * sizeof(REAL)));
226  for (std::size_t i=0, ctr=0; i<_holes->size(); ++i, ctr+=2)
227  {
228  Point inside_point = (*_holes)[i]->inside();
229  initial.holelist[ctr] = inside_point(0);
230  initial.holelist[ctr+1] = inside_point(1);
231  }
232  }
233 
234  // Set the triangulation flags.
235  // c ~ enclose convex hull with segments
236  // z ~ use zero indexing
237  // B ~ Suppresses boundary markers in the output
238  // Q ~ run in "quiet" mode
239  // p ~ Triangulates a Planar Straight Line Graph
240  // If the `p' switch is used, `segmentlist' must point to a list of
241  // segments, `numberofsegments' must be properly set, and
242  // `segmentmarkerlist' must either be set to nullptr (in which case all
243  // markers default to zero), or must point to a list of markers.
244  // D ~ Conforming Delaunay: use this switch if you want all triangles
245  // in the mesh to be Delaunay, and not just constrained Delaunay
246  // q ~ Quality mesh generation with no angles smaller than 20 degrees.
247  // An alternate minimum angle may be specified after the q
248  // a ~ Imposes a maximum triangle area constraint.
249  // -P Suppresses the output .poly file. Saves disk space, but you lose the ability to maintain
250  // constraining segments on later refinements of the mesh.
251  // Create the flag strings, depends on element type
252  std::ostringstream flags;
253 
254  // Default flags always used
255  flags << "zBPQ";
256 
257  // Flags which are specific to the type of triangulation
258  switch (_triangulation_type)
259  {
261  {
262  flags << "c";
263  break;
264  }
265 
266  case PSLG:
267  {
268  flags << "p";
269  break;
270  }
271 
273  libmesh_error_msg("ERROR: INVALID_TRIANGULATION_TYPE selected!");
274 
275  default:
276  libmesh_error_msg("Unrecognized _triangulation_type");
277  }
278 
279 
280  // Flags specific to the type of element
281  switch (_elem_type)
282  {
283  case TRI3:
284  {
285  // do nothing.
286  break;
287  }
288 
289  case TRI6:
290  {
291  flags << "o2";
292  break;
293  }
294 
295  default:
296  libmesh_error_msg("ERROR: Unrecognized triangular element type.");
297  }
298 
299 
300  // If we do have holes and the user asked to GENERATE_CONVEX_HULL,
301  // need to add the p flag so the triangulation respects those segments.
302  if ((_triangulation_type==GENERATE_CONVEX_HULL) && (have_holes))
303  flags << "p";
304 
305  // Finally, add the area constraint
306  if (_desired_area > TOLERANCE)
307  flags << "a" << std::fixed << _desired_area;
308 
309  // add minimum angle constraint
311  flags << "q" << std::fixed << _minimum_angle;
312 
313  // add user provided extra flags
314  if (_extra_flags.size() > 0)
315  flags << _extra_flags;
316 
317  // Refine the initial output to conform to the area constraint
318  TriangleWrapper::triangulate(const_cast<char *>(flags.str().c_str()),
319  &initial,
320  &final,
321  nullptr); // voronoi ouput -- not used
322 
323 
324  // Send the information computed by Triangle to the Mesh.
326  _mesh,
327  _elem_type);
328 
329  // To the naked eye, a few smoothing iterations usually looks better,
330  // so we do this by default unless the user says not to.
331  if (this->_smooth_after_generating)
333 
334 
335  // Clean up.
338 
339  // Prepare the mesh for use before returning. This ensures (among
340  // other things) that it is partitioned and therefore users can
341  // iterate over local elements, etc.
343 }
344 
345 } // namespace libMesh
346 
347 
348 
349 
350 
351 
352 
353 #endif // LIBMESH_HAVE_TRIANGLE
virtual void smooth() override
MeshBase & mesh
static const Real TOLERANCE
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
const std::vector< Hole * > * _holes
Base class for Replicated and Distributed meshes.
void copy_tri_to_mesh(const triangulateio &triangle_data_input, UnstructuredMesh &mesh_output, const ElemType type)
virtual SimpleRange< node_iterator > node_ptr_range()=0
void init(triangulateio &t)
void prepare_for_use(const bool skip_renumber_nodes_and_elements=false, const bool skip_find_neighbors=false)
Definition: mesh_base.C:152
void set_mesh_dimension(unsigned char d)
Definition: mesh_base.h:213
std::vector< std::pair< unsigned int, unsigned int > > segments
virtual void clear()
Definition: mesh_base.C:260
void destroy(triangulateio &t, IO_Type)
A geometric point in (x,y,z) space.
Definition: point.h:38
virtual dof_id_type n_nodes() const =0
TriangleInterface(UnstructuredMesh &mesh)
uint8_t dof_id_type
Definition: id_types.h:64