RINGMesh  Version 5.0.0
A programming library for geological model meshes
tetgen_mesher.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012-2017, Association Scientifique pour la Geologie et ses
3  * Applications (ASGA). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of ASGA nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ASGA BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * http://www.ring-team.org
28  *
29  * RING Project
30  * Ecole Nationale Superieure de Geologie - GeoRessources
31  * 2 Rue du Doyen Marcel Roubault - TSA 70605
32  * 54518 VANDOEUVRE-LES-NANCY
33  * FRANCE
34  */
35 
37 
38 #include <cstring>
39 
40 #include <geogram/mesh/mesh.h>
41 #include <ringmesh/mesh/mesh.h>
43 
44 #ifdef RINGMESH_WITH_TETGEN
45 
50 namespace
51 {
52  using namespace RINGMesh;
53 
54  bool is_mesh_tetrahedralizable( const GEO::Mesh& M )
55  {
56  if( M.facets.nb() == 0 )
57  {
58  Logger::err( "RING", "Mesh to tetrahedralize has no polygons " );
59  return false;
60  }
61  if( !M.facets.are_simplices() )
62  {
63  Logger::err( "RING", "Mesh to tetrahedralize is not triangulated" );
64  return false;
65  }
66  if( M.cells.nb() != 0 )
67  {
68  Logger::warn( "RING", "Mesh to tetrahedralize already have cells" );
69  }
70  return true;
71  }
72 } // namespace
73 
74 namespace RINGMesh
75 {
76  TetgenMesher::~TetgenMesher()
77  {
78  // Take over polygon deletion of tetgen that does not set to
79  // nullptr pointers to polygonlist or holelist in polygon
80  delete[] tetgen_in_.facetlist;
81  tetgen_in_.facetlist = nullptr;
82  tetgen_in_.numberoffacets = 0;
83  }
84 
85  void TetgenMesher::tetrahedralize(
86  const GEO::Mesh& input_mesh, VolumeMeshBuilder3D& output_mesh_builder )
87  {
88  initialize();
89  copy_mesh_to_tetgen_input( input_mesh );
90  tetrahedralize();
91  assign_result_tetmesh_to_mesh( output_mesh_builder );
92  }
93 
94  void TetgenMesher::initialize()
95  {
96  initialize_tetgen_args();
97  tetgen_in_.initialize();
98  tetgen_out_.initialize();
99  }
100 
101  void TetgenMesher::tetrahedralize()
102  {
103  try
104  {
105  GEO_3rdParty::tetrahedralize(
106  &tetgen_args_, &tetgen_in_, &tetgen_out_ );
107  }
108  catch( int code )
109  {
110  Logger::err( "Tetgen", "Encountered a problem: " );
111  switch( code )
112  {
113  case 1:
114  Logger::err( "Tetgen", "Out of memory" );
115  break;
116  case 2:
117  Logger::err( "Tetgen", "Please report this bug to "
118  "Hang.Si@wias-berlin.de. Include" );
119  Logger::err( "Tetgen",
120  " the message above, your input data set, and the exact" );
121  Logger::err( "Tetgen",
122  " command line you used to run this program, thank you" );
123  ;
124  break;
125  case 3:
126  Logger::err( "Tetgen",
127  "A self-intersection was detected. Program stopped" );
128  Logger::err( "Tetgen",
129  "Hint: use -d option to detect all self-intersections" );
130  break;
131  case 4:
132  Logger::err( "Tetgen", "A very small input feature size was "
133  "detected. Program stopped." );
134  Logger::err( "Tetgen",
135  "Hint: use -T option to set a smaller tolerance." );
136  break;
137  case 5:
138  Logger::err( "Tetgen", "Two very close input polygons were "
139  "detected. Program stopped." );
140  Logger::err( "Tetgen", "Hint: use -Y option to avoid adding "
141  "Steiner points in boundary." );
142  break;
143  case 10:
144  Logger::err(
145  "Tetgen", "An input error was detected. Program stopped." );
146  break;
147  }
148  }
149  }
150 
151  void TetgenMesher::copy_mesh_to_tetgen_input( const GEO::Mesh& M )
152  {
153  if( M.vertices.nb() != 0 )
154  {
155  copy_vertices_to_tetgen_input( M );
156  }
157  if( M.edges.nb() != 0 )
158  {
159  copy_edges_to_tetgen_input( M );
160  }
161  if( M.facets.nb() != 0 )
162  {
163  copy_polygons_to_tetgen_input( M );
164  }
165  }
166 
167  void TetgenMesher::copy_vertices_to_tetgen_input( const GEO::Mesh& M )
168  {
169  tetgen_in_.numberofpoints = static_cast< int >( M.vertices.nb() );
170  tetgen_in_.pointlist = new double[3 * tetgen_in_.numberofpoints];
171  GEO::Memory::copy( tetgen_in_.pointlist, M.vertices.point_ptr( 0 ),
172  M.vertices.nb() * 3 * sizeof( double ) );
173  }
174 
175  void TetgenMesher::copy_edges_to_tetgen_input( const GEO::Mesh& M )
176  {
177  tetgen_in_.numberofedges = static_cast< int >( M.edges.nb() );
178  tetgen_in_.edgelist = new int[2 * tetgen_in_.numberofedges];
179  GEO::Memory::copy( tetgen_in_.edgelist, M.edges.vertex_index_ptr( 0 ),
180  M.edges.nb() * 2 * sizeof( int ) );
181  }
182 
183  void TetgenMesher::copy_polygons_to_tetgen_input( const GEO::Mesh& M )
184  {
185  polygons_.reset( new GEO_3rdParty::tetgenio::polygon[M.facets.nb()] );
186 
187  tetgen_in_.numberoffacets = static_cast< int >( M.facets.nb() );
188  tetgen_in_.facetlist =
189  new GEO_3rdParty::tetgenio::facet[tetgen_in_.numberoffacets];
190 
191  polygon_corners_.reset( new int[M.facet_corners.nb()] );
192  GEO::Memory::copy( polygon_corners_.get(),
193  M.facet_corners.vertex_index_ptr( 0 ),
194  M.facet_corners.nb() * sizeof( int ) );
195 
196  for( auto f : range( M.facets.nb() ) )
197  {
198  GEO_3rdParty::tetgenio::facet& F = tetgen_in_.facetlist[f];
199  GEO_3rdParty::tetgenio::init( &F );
200  F.numberofpolygons = 1;
201  F.polygonlist = &polygons_[f];
202 
203  GEO_3rdParty::tetgenio::polygon& P = F.polygonlist[0];
204  GEO_3rdParty::tetgenio::init( &P );
205  P.numberofvertices = static_cast< int >( M.facets.nb_corners( f ) );
206  P.vertexlist = &polygon_corners_[M.facets.corners_begin( f )];
207  }
208  }
209 
210  void TetgenMesher::set_regions(
211  const std::vector< vec3 >& one_point_in_each_region )
212  {
213  index_t nb_regions = one_point_in_each_region.size();
214  tetgen_in_.numberofregions = static_cast< int >( nb_regions );
215  tetgen_in_.regionlist = new double[5 * nb_regions];
216 
217  for( auto i : range( nb_regions ) )
218  {
219  tetgen_in_.regionlist[5 * i] = one_point_in_each_region[i].x;
220  tetgen_in_.regionlist[5 * i + 1] = one_point_in_each_region[i].y;
221  tetgen_in_.regionlist[5 * i + 2] = one_point_in_each_region[i].z;
222  tetgen_in_.regionlist[5 * i + 3] = i;
223  tetgen_in_.regionlist[5 * i + 4] =
224  DBL_MAX; // Used only with the a switch
225  }
226  }
227 
228  void TetgenMesher::initialize_tetgen_args()
229  {
230  std::vector< char > copy(
231  tetgen_command_line_.begin(), tetgen_command_line_.end() );
232  copy.push_back( '\0' );
233  tetgen_args_.parse_commandline( copy.data() );
234  }
235 
236  void TetgenMesher::assign_result_tetmesh_to_mesh(
237  VolumeMeshBuilder3D& output_mesh_builder ) const
238  {
239  output_mesh_builder.assign_vertices( get_result_tetmesh_points() );
240  output_mesh_builder.assign_cell_tet_mesh( get_result_tetmesh_tets() );
241  output_mesh_builder.remove_isolated_vertices();
242  output_mesh_builder.connect_cells();
243  }
244 
245  std::vector< double > TetgenMesher::get_result_tetmesh_points() const
246  {
247  auto nb_points = static_cast< index_t >( tetgen_out_.numberofpoints );
248  std::vector< double > points( 3 * nb_points );
249  double* points_ptr = tetgen_out_.pointlist;
250  parallel_for( 3 * nb_points, [&points, &points_ptr]( index_t i ) {
251  points[i] = points_ptr[i];
252  } );
253  return points;
254  }
255 
256  std::vector< index_t > TetgenMesher::get_result_tetmesh_tets() const
257  {
258  std::vector< index_t > tets_to_keep = determine_tets_to_keep();
259 
260  auto nb_tets = static_cast< index_t >( tets_to_keep.size() );
261  std::vector< index_t > tets( 4 * nb_tets );
262  int* tets_ptr = tetgen_out_.tetrahedronlist;
263  parallel_for( nb_tets, [&tets_to_keep, &tets_ptr, &tets]( index_t i ) {
264  index_t tetra = tets_to_keep[i];
265  for( auto v : range( 4 ) )
266  {
267  tets[4 * i + v] =
268  static_cast< index_t >( tets_ptr[4 * tetra + v] );
269  }
270  } );
271  return tets;
272  }
273 
274  std::set< double > TetgenMesher::determine_tet_regions_to_keep() const
275  {
276  // Determine which regions are incident to
277  // the 'exterior' (neighbor = -1).
278  // The region Id of tet t is determined by:
279  // tetgen_out_.tetrahedronattributelist[t]
280  std::set< double > regions_to_keep;
281  auto nb_tets = static_cast< index_t >( tetgen_out_.numberoftetrahedra );
282  for( auto t : range( nb_tets ) )
283  {
284  for( auto f : range( 4 ) )
285  {
286  signed_index_t n = tetgen_out_.neighborlist[t * 4 + f];
287  if( n == -1 )
288  {
289  regions_to_keep.insert(
290  tetgen_out_.tetrahedronattributelist[t] );
291  break;
292  }
293  }
294  }
295  return regions_to_keep;
296  }
297 
298  std::vector< index_t > TetgenMesher::determine_tets_to_keep() const
299  {
300  std::vector< index_t > tets_to_keep;
301  std::set< double > regions_to_keep = determine_tet_regions_to_keep();
302 
303  auto nb_tets = static_cast< index_t >( tetgen_out_.numberoftetrahedra );
304  tets_to_keep.reserve( nb_tets );
305  for( auto t : range( nb_tets ) )
306  {
307  if( regions_to_keep.find( tetgen_out_.tetrahedronattributelist[t] )
308  != regions_to_keep.end() )
309  {
310  tets_to_keep.push_back( t );
311  }
312  }
313  return tets_to_keep;
314  }
315 
316  void tetrahedralize_mesh_tetgen( VolumeMeshBuilder3D& out_tet_mesh,
317  const GEO::Mesh& in_mesh,
318  bool refine,
319  double quality )
320  {
321  if( !is_mesh_tetrahedralizable( in_mesh ) )
322  {
323  throw RINGMeshException(
324  "TetGen", "Mesh cannot be tetrahedralized" );
325  }
326  TetgenMesher mesher;
327  if( refine )
328  {
329  mesher.add_points_to_match_quality( quality );
330  }
331  mesher.tetrahedralize( in_mesh, out_tet_mesh );
332  }
333 
334  void TetgenMesher::add_points_to_match_quality( double quality )
335  {
336  tetgen_command_line_ += "q" + std::to_string( quality );
337  }
338 } // namespace RINGMesh
339 
340 #endif
static void warn(const std::string &feature, const Args &... args)
Definition: logger.h:75
static void err(const std::string &feature, const Args &... args)
Definition: logger.h:68
virtual void connect_cells()=0
Retrieve the adjacencies.
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
void assign_cell_tet_mesh(const std::vector< index_t > &tets)
Definition: mesh_builder.h:927
void assign_vertices(const std::vector< double > &point_coordinates)
set vertex coordinates from a std::vector of coordinates
Definition: mesh_builder.h:159
void parallel_for(index_t size, const ACTION &action)
Definition: common.h:244