RINGMesh  Version 5.0.0
A programming library for geological model meshes
geogram_extension.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 <algorithm>
39 
40 #include <geogram/basic/file_system.h>
41 #include <geogram/basic/line_stream.h>
42 
43 #include <geogram/mesh/mesh.h>
44 #include <geogram/mesh/mesh_geometry.h>
45 #include <geogram/mesh/mesh_io.h>
46 #include <geogram/mesh/mesh_repair.h>
47 
52 namespace
53 {
54  using namespace RINGMesh;
55 
56  std::vector< std::string > bounded_attribute_names(
57  GEO::AttributesManager& manager )
58  {
59  GEO::vector< std::string > names;
60  manager.list_attribute_names( names );
61  std::vector< std::string > bounded_names;
62  bounded_names.reserve( names.size() );
63  if( !names.empty() )
64  {
65  for( std::string name : names )
66  {
67  if( manager.find_attribute_store( name )->has_observers() )
68  {
69  bounded_names.push_back( name );
70  }
71  }
72  }
73  return bounded_names;
74  }
75 
76  void print_bounded_attributes( const std::vector< std::string >& names,
77  const std::string& output_location )
78  {
79  if( !names.empty() )
80  {
81  Logger::err( "Attributes", "Attributes still bounded on ",
82  output_location, ":" );
83  for( std::string name : names )
84  {
85  Logger::err( "Attributes", " ", name );
86  }
87  }
88  }
89 
91  GEO::AttributesManager& manager, const std::string& output_location )
92  {
93  std::vector< std::string > names = bounded_attribute_names( manager );
94  print_bounded_attributes( names, output_location );
95  }
96 
100  class TSurfMeshIOHandler final : public GEO::MeshIOHandler
101  {
102  public:
103  TSurfMeshIOHandler()
104  : starting_index_( 1 ),
105  mesh_dimension_( 3 ),
106  nb_vertices_( 0 ),
107  nb_triangles_( 0 ),
108  z_sign_( 1 )
109  {
110  }
111 
122  bool load( const std::string& filename,
123  GEO::Mesh& mesh,
124  const GEO::MeshIOFlags& flag = GEO::MeshIOFlags() ) final
125  {
126  ringmesh_unused( flag );
127  filename_ = filename;
128  if( !is_file_valid() )
129  {
130  return false;
131  }
132  else
133  {
134  read_number_of_vertices_and_triangles();
135  read_vertex_property_names();
136  read_vertex_property_sizes();
137  allocate_vertices();
138  allocate_triangles();
139  allocate_vertex_properties();
140  read_vertices_and_triangles();
141  assign_and_repair_mesh( mesh );
142  return true;
143  }
144  }
145 
149  bool save( const GEO::Mesh& mesh,
150  const std::string& filename,
151  const GEO::MeshIOFlags& flag = GEO::MeshIOFlags() ) final
152  {
153  ringmesh_unused( flag );
154  if( !mesh.facets.are_simplices() )
155  {
156  throw RINGMeshException( "I/O",
157  "Cannot save a non triangulated mesh into TSurf format" );
158  }
159 
160  std::ofstream out( filename.c_str() );
161  out.precision( 16 );
162  save_header( out, GEO::FileSystem::base_name( filename ) );
163  std::vector< std::string > att_v_double_names;
164  std::vector< index_t > vertex_attr_dims;
165  fill_vertex_attribute_header(
166  mesh, out, att_v_double_names, vertex_attr_dims );
167 
168  out << "TFACE" << std::endl;
169  save_vertices( out, mesh, att_v_double_names, vertex_attr_dims );
170  save_triangles( out, mesh );
171  out << "END" << std::endl;
172 
173  return true;
174  }
175 
176  private:
177  void save_vertices( std::ofstream& out,
178  const GEO::Mesh& mesh,
179  const std::vector< std::string >& att_v_double_names,
180  const std::vector< index_t >& vertex_attr_dims )
181  {
182  for( auto v : range( mesh.vertices.nb() ) )
183  {
184  // PVRTX must be used instead of VRTX because
185  // properties are not read by Gocad if it is VRTX.
186  out << "PVRTX " << v + starting_index_ << " "
187  << mesh.vertices.point( v );
188  for( auto attr_dbl_itr : range( att_v_double_names.size() ) )
189  {
190  GEO::Attribute< double > cur_attr(
191  mesh.vertices.attributes(),
192  att_v_double_names[attr_dbl_itr] );
193  index_t nb_dimensions = vertex_attr_dims[attr_dbl_itr];
194  for( auto dim_itr : range( nb_dimensions ) )
195  {
196  out << " " << cur_attr[v * nb_dimensions + dim_itr];
197  }
198  }
199  out << std::endl;
200  }
201  }
202  void save_triangles( std::ofstream& out, const GEO::Mesh& mesh )
203  {
204  for( auto t : range( mesh.facets.nb() ) )
205  {
206  out << "TRGL";
207  for( auto v : range( 3 ) )
208  {
209  out << " " << mesh.facets.vertex( t, v ) + starting_index_;
210  }
211  out << std::endl;
212  }
213  }
214  void save_header( std::ofstream& out, const std::string& mesh_name )
215  {
216  out << "GOCAD TSurf" << std::endl;
217  out << "HEADER {" << std::endl;
218  out << "name: " << mesh_name << std::endl;
219  out << "}" << std::endl;
220  }
221  void fill_vertex_attribute_header( const GEO::Mesh& mesh,
222  std::ofstream& out,
223  std::vector< std::string >& att_v_double_names,
224  std::vector< index_t >& vertex_attr_dims ) const
225  {
226  GEO::vector< std::string > att_v_names;
227  GEO::AttributesManager& mesh_vertex_mgr =
228  mesh.vertices.attributes();
229  mesh_vertex_mgr.list_attribute_names( att_v_names );
230  for( auto att_v : range( mesh_vertex_mgr.nb() ) )
231  {
232  if( att_v_names[att_v] == "point" )
233  {
234  continue;
235  }
236 
237  if( !GEO::Attribute< double >::is_defined(
238  mesh_vertex_mgr, att_v_names[att_v] ) )
239  {
240  continue;
241  }
242  att_v_double_names.push_back( att_v_names[att_v] );
243  index_t cur_dim =
244  mesh_vertex_mgr.find_attribute_store( att_v_names[att_v] )
245  ->dimension();
246  vertex_attr_dims.push_back( cur_dim );
247  }
248 
249  if( !att_v_double_names.empty() )
250  {
251  index_t nb_attributes =
252  static_cast< index_t >( att_v_double_names.size() );
253  out << "PROPERTIES";
254  for( auto attr_dbl_itr : range( nb_attributes ) )
255  {
256  out << " " << att_v_double_names[attr_dbl_itr];
257  }
258  out << std::endl;
259  out << "PROP_LEGAL_RANGES";
260  for( auto attr_dbl_itr : range( nb_attributes ) )
261  {
262  ringmesh_unused( attr_dbl_itr );
263  out << " **none** **none**";
264  }
265  out << std::endl;
266  out << "NO_DATA_VALUES";
267  for( auto attr_dbl_itr : range( nb_attributes ) )
268  {
269  ringmesh_unused( attr_dbl_itr );
270  out << " -99999";
271  }
272  out << std::endl;
273  out << "READ_ONLY";
274  for( auto attr_dbl_itr : range( nb_attributes ) )
275  {
276  ringmesh_unused( attr_dbl_itr );
277  out << " 1";
278  }
279  out << std::endl;
280  out << "PROPERTY_CLASSES";
281  for( auto attr_dbl_itr : range( nb_attributes ) )
282  {
283  out << " " << att_v_double_names[attr_dbl_itr];
284  }
285  out << std::endl;
286  out << "PROPERTY_KINDS";
287  for( auto attr_dbl_itr : range( nb_attributes ) )
288  {
289  ringmesh_unused( attr_dbl_itr );
290  out << " \"Real Number\"";
291  }
292  out << std::endl;
293  out << "PROPERTY_SUBCLASSES";
294  for( auto attr_dbl_itr : range( nb_attributes ) )
295  {
296  ringmesh_unused( attr_dbl_itr );
297  out << " QUANTITY Float";
298  }
299  out << std::endl;
300  out << "ESIZES";
301  for( auto attr_dbl_itr : range( nb_attributes ) )
302  {
303  out << " "
304  << std::to_string( vertex_attr_dims[attr_dbl_itr] );
305  }
306  out << std::endl;
307  out << "UNITS";
308  for( auto attr_dbl_itr : range( nb_attributes ) )
309  {
310  ringmesh_unused( attr_dbl_itr );
311  out << " unitless";
312  }
313  out << std::endl;
314  for( auto attr_dbl_itr : range( nb_attributes ) )
315  {
316  out << "PROPERTY_CLASS_HEADER "
317  << att_v_double_names[attr_dbl_itr] << " {"
318  << std::endl;
319  out << "kind: Real Number" << std::endl;
320  out << "unit: unitless" << std::endl;
321  out << "}" << std::endl;
322  }
323  }
324  }
325  // This function read the z_sign too [PA]
326  void read_number_of_vertices_and_triangles()
327  {
328  GEO::LineInput in( filename_ );
329  while( !in.eof() && in.get_line() )
330  {
331  in.get_fields();
332  if( in.nb_fields() > 0 )
333  {
334  if( in.field_matches( 0, "ZPOSITIVE" ) )
335  {
336  if( in.field_matches( 1, "Elevation" ) )
337  {
338  z_sign_ = 1;
339  }
340  else if( in.field_matches( 1, "Depth" ) )
341  {
342  z_sign_ = -1;
343  }
344  }
345  else if( in.field_matches( 0, "VRTX" )
346  || in.field_matches( 0, "PVRTX" ) )
347  {
348  nb_vertices_++;
349  }
350  else if( in.field_matches( 0, "PATOM" )
351  || in.field_matches( 0, "ATOM" ) )
352  {
353  nb_vertices_++;
354  }
355  else if( in.field_matches( 0, "TRGL" ) )
356  {
357  nb_triangles_++;
358  }
359  }
360  }
361  }
362 
363  void read_vertices_and_triangles()
364  {
365  GEO::LineInput in( filename_ );
366  index_t v = 0;
367  index_t t = 0;
368  while( !in.eof() && in.get_line() )
369  {
370  in.get_fields();
371  if( in.nb_fields() > 0 )
372  {
373  if( in.field_matches( 0, "VRTX" )
374  || in.field_matches( 0, "PVRTX" ) )
375  {
376  vertices_[mesh_dimension_ * v] =
377  in.field_as_double( 2 );
378  vertices_[mesh_dimension_ * v + 1] =
379  in.field_as_double( 3 );
380  vertices_[mesh_dimension_ * v + 2] =
381  in.field_as_double( 4 ) * z_sign_;
382  if( in.field_matches( 0, "PVRTX" ) )
383  {
384  index_t offset = 5;
385  for( auto prop_name_itr :
386  range( vertex_property_names_.size() ) )
387  {
388  for( auto v_attr_dim_itr :
389  range( vertex_attribute_dims_
390  [prop_name_itr] ) )
391  {
392  vertex_attributes_[prop_name_itr][v]
393  [v_attr_dim_itr] =
394  in.field_as_double(
395  offset );
396  ++offset;
397  }
398  }
399  }
400  ++v;
401  }
402  else if( in.field_matches( 0, "PATOM" )
403  || in.field_matches( 0, "ATOM" ) )
404  {
405  index_t v0 = in.field_as_uint( 2 ) - starting_index_;
406  vertices_[mesh_dimension_ * v] =
407  vertices_[mesh_dimension_ * v0];
408  vertices_[mesh_dimension_ * v + 1] =
409  vertices_[mesh_dimension_ * v0 + 1];
410  vertices_[mesh_dimension_ * v + 2] =
411  vertices_[mesh_dimension_ * v0 + 2];
412  ++v;
413  }
414  else if( in.field_matches( 0, "TRGL" ) )
415  {
416  triangles_[3 * t] =
417  index_t( in.field_as_uint( 1 ) - starting_index_ );
418  triangles_[3 * t + 1] =
419  index_t( in.field_as_uint( 2 ) - starting_index_ );
420  triangles_[3 * t + 2] =
421  index_t( in.field_as_uint( 3 ) - starting_index_ );
422  t++;
423  }
424  }
425  }
426  }
427 
428  void read_vertex_property_names()
429  {
430  GEO::LineInput in( filename_ );
431  while( !in.eof() && in.get_line() )
432  {
433  in.get_fields();
434  if( in.nb_fields() > 0 )
435  {
436  if( !in.field_matches( 0, "PROPERTIES" ) )
437  {
438  continue;
439  }
440  vertex_property_names_.reserve( in.nb_fields() - 1 );
441  for( auto prop_name_itr : range( 1, in.nb_fields() ) )
442  {
443  vertex_property_names_.push_back(
444  in.field( prop_name_itr ) );
445  }
446  return; // No need to continue.
447  }
448  }
449  }
450 
451  void read_vertex_property_sizes()
452  {
453  GEO::LineInput in( filename_ );
454  while( !in.eof() && in.get_line() )
455  {
456  in.get_fields();
457  if( in.nb_fields() > 0 )
458  {
459  if( !in.field_matches( 0, "ESIZES" ) )
460  {
461  continue;
462  }
463  vertex_property_names_.reserve( in.nb_fields() - 1 );
464  for( auto prop_size_itr : range( 1, in.nb_fields() ) )
465  {
466  vertex_attribute_dims_.push_back(
467  in.field_as_uint( prop_size_itr ) );
468  }
469  return; // No need to continue.
470  }
471  }
472  }
473 
474  void assign_and_repair_mesh( GEO::Mesh& mesh )
475  {
476  GEO::coord_index_t dimension =
477  static_cast< GEO::coord_index_t >( mesh_dimension_ );
478  mesh.facets.assign_triangle_mesh(
479  dimension, vertices_, triangles_, true );
480 
481  assign_tsurf_properties_to_geogram_mesh( mesh );
482 
483  // Do not use GEO::MESH_REPAIR_DEFAULT because it glues the
484  // disconnected edges along internal boundaries
485  GEO::mesh_repair( mesh, GEO::MESH_REPAIR_DUP_F );
486  }
487 
488  void assign_tsurf_properties_to_geogram_mesh( GEO::Mesh& mesh )
489  {
490  for( auto prop_name_itr : range( vertex_property_names_.size() ) )
491  {
492  index_t nb_dimensions = vertex_attribute_dims_[prop_name_itr];
493  GEO::Attribute< double > attr;
494  attr.create_vector_attribute( mesh.vertices.attributes(),
495  vertex_property_names_[prop_name_itr], nb_dimensions );
496  for( auto v_itr : range( nb_vertices_ ) )
497  {
498  for( auto prop_dim_itr : range( nb_dimensions ) )
499  {
500  attr[v_itr * nb_dimensions + prop_dim_itr] =
501  vertex_attributes_[prop_name_itr][v_itr]
502  [prop_dim_itr];
503  }
504  }
505  }
506  }
507 
508  bool is_file_valid()
509  {
510  GEO::LineInput in( filename_ );
511  if( !in.OK() )
512  {
513  return false;
514  }
515  else
516  {
517  return true;
518  }
519  }
520 
521  void allocate_vertices()
522  {
523  vertices_.resize( mesh_dimension_ * nb_vertices_ );
524  }
525 
526  void allocate_triangles()
527  {
528  triangles_.resize( 3 * nb_triangles_ );
529  }
530  void allocate_vertex_properties()
531  {
532  vertex_attributes_.resize( vertex_property_names_.size() );
533  for( auto vertex_attributes_itr :
534  range( vertex_attributes_.size() ) )
535  {
536  vertex_attributes_[vertex_attributes_itr].resize(
537  nb_vertices_ );
538  for( auto vertex_itr : range( nb_vertices_ ) )
539  {
540  vertex_attributes_[vertex_attributes_itr][vertex_itr]
541  .resize(
542  vertex_attribute_dims_[vertex_attributes_itr], 0 );
543  }
544  }
545  }
546 
547  private:
548  index_t starting_index_;
549  index_t mesh_dimension_;
550  index_t nb_vertices_;
551  index_t nb_triangles_;
552  int z_sign_;
553  std::string filename_;
554  GEO::vector< double > vertices_;
555  GEO::vector< index_t > triangles_;
556  GEO::vector< std::string > vertex_property_names_;
557  GEO::vector< index_t > vertex_attribute_dims_;
558  GEO::vector< GEO::vector< GEO::vector< double > > > vertex_attributes_;
559  };
560 
561  class LINMeshIOHandler final : public GEO::MeshIOHandler
562  {
563  public:
564  bool load( const std::string& filename,
565  GEO::Mesh& mesh,
566  const GEO::MeshIOFlags& flag = GEO::MeshIOFlags() ) final
567  {
568  ringmesh_unused( flag );
569  GEO::LineInput file( filename );
570 
571  while( !file.eof() && file.get_line() )
572  {
573  file.get_fields();
574  if( file.nb_fields() > 0 )
575  {
576  if( file.field_matches( 0, "v" ) )
577  {
578  vec3 vertex = load_vertex( file, 1 );
579  mesh.vertices.create_vertex( vertex.data() );
580  }
581  else if( file.field_matches( 0, "s" ) )
582  {
583  mesh.edges.create_edge( file.field_as_uint( 1 ) - 1,
584  file.field_as_uint( 2 ) - 1 );
585  }
586  }
587  }
588  return true;
589  }
590  bool save( const GEO::Mesh& M,
591  const std::string& filename,
592  const GEO::MeshIOFlags& ioflags = GEO::MeshIOFlags() ) final
593  {
594  ringmesh_unused( M );
595  ringmesh_unused( filename );
596  ringmesh_unused( ioflags );
597  throw RINGMeshException(
598  "I/O", "Saving a Mesh into .lin format not implemented yet" );
599  return false;
600  }
601 
602  private:
603  vec3 load_vertex( GEO::LineInput& file, index_t field ) const
604  {
605  double x = file.field_as_double( field++ );
606  double y = file.field_as_double( field++ );
607  double z = file.field_as_double( field++ );
608  return vec3( x, y, z );
609  }
610  };
611 
612 } // namespace
613 
614 namespace RINGMesh
615 {
616  /***********************************************************************/
617  /* Loading and saving a GEO::Mesh */
618 
620  {
621  geo_register_MeshIOHandler_creator( TSurfMeshIOHandler, "ts" );
622  geo_register_MeshIOHandler_creator( LINMeshIOHandler, "lin" );
623  }
624 
625  double mesh_cell_signed_volume( const GEO::Mesh& M, index_t c )
626  {
627  double volume = 0;
628  switch( M.cells.type( c ) )
629  {
630  case GEO::MESH_TET:
631  volume = GEO::Geom::tetra_signed_volume(
632  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 0 ) ),
633  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 1 ) ),
634  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 2 ) ),
635  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 3 ) ) );
636  break;
637  case GEO::MESH_PYRAMID:
638  volume = GEO::Geom::tetra_signed_volume(
639  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 0 ) ),
640  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 1 ) ),
641  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 2 ) ),
642  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 4 ) ) );
643  volume += GEO::Geom::tetra_signed_volume(
644  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 0 ) ),
645  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 2 ) ),
646  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 3 ) ),
647  GEO::Geom::mesh_vertex( M, M.cells.vertex( c, 4 ) ) );
648  break;
649  case GEO::MESH_PRISM:
650  case GEO::MESH_HEX:
651  {
652  vec3 ori( 0, 0, 0 );
653  for( auto f : range( M.cells.nb_facets( c ) ) )
654  {
655  switch( M.cells.facet_nb_vertices( c, f ) )
656  {
657  case 3:
658  volume += GEO::Geom::tetra_signed_volume(
659  GEO::Geom::mesh_vertex(
660  M, M.cells.facet_vertex( c, f, 0 ) ),
661  GEO::Geom::mesh_vertex(
662  M, M.cells.facet_vertex( c, f, 1 ) ),
663  GEO::Geom::mesh_vertex(
664  M, M.cells.facet_vertex( c, f, 2 ) ),
665  ori );
666  break;
667  case 4:
668  volume += GEO::Geom::tetra_signed_volume(
669  GEO::Geom::mesh_vertex(
670  M, M.cells.facet_vertex( c, f, 0 ) ),
671  GEO::Geom::mesh_vertex(
672  M, M.cells.facet_vertex( c, f, 1 ) ),
673  GEO::Geom::mesh_vertex(
674  M, M.cells.facet_vertex( c, f, 2 ) ),
675  ori );
676  volume += GEO::Geom::tetra_signed_volume(
677  GEO::Geom::mesh_vertex(
678  M, M.cells.facet_vertex( c, f, 0 ) ),
679  GEO::Geom::mesh_vertex(
680  M, M.cells.facet_vertex( c, f, 2 ) ),
681  GEO::Geom::mesh_vertex(
682  M, M.cells.facet_vertex( c, f, 3 ) ),
683  ori );
684  break;
685  default:
687  return 0;
688  }
689  }
690  break;
691  }
692  default:
693  return 0;
694  }
695  return volume;
696  }
697 
698  double mesh_cell_volume( const GEO::Mesh& M, index_t c )
699  {
700  return std::fabs( mesh_cell_signed_volume( M, c ) );
701  }
702 
704  const GEO::Mesh& M, index_t cell, index_t f )
705  {
706  vec3 result( 0., 0., 0. );
707  index_t nb_vertices = M.cells.facet_nb_vertices( cell, f );
708  for( auto v : range( nb_vertices ) )
709  {
710  result +=
711  GEO::Geom::mesh_vertex( M, M.cells.facet_vertex( cell, f, v ) );
712  }
713  ringmesh_assert( nb_vertices > 0 );
714 
715  return result / static_cast< double >( nb_vertices );
716  }
717 
718  vec3 mesh_cell_barycenter( const GEO::Mesh& M, index_t cell )
719  {
720  vec3 result( 0.0, 0.0, 0.0 );
721  for( auto v : range( M.cells.nb_vertices( cell ) ) )
722  {
723  result += GEO::Geom::mesh_vertex( M, M.cells.vertex( cell, v ) );
724  }
725  return ( 1.0 / M.cells.nb_vertices( cell ) ) * result;
726  }
727 
728  void print_bounded_attributes( const GEO::Mesh& M )
729  {
730  std::vector< std::string > names =
731  bounded_attribute_names( M.vertices.attributes() );
732  names.erase( std::find( names.begin(), names.end(), "point" ) );
733  ::print_bounded_attributes( names, "vertices" );
734  ::print_bounded_attributes( M.edges.attributes(), "edges" );
735  ::print_bounded_attributes( M.facets.attributes(), "facets" );
737  M.facet_corners.attributes(), "facet_corners" );
738  ::print_bounded_attributes( M.cells.attributes(), "cells" );
740  M.cell_corners.attributes(), "cell_corners" );
741  ::print_bounded_attributes( M.cell_facets.attributes(), "cell_facets" );
742  }
743 
744 } // namespace RINGMesh
vec3 RINGMESH_API mesh_cell_barycenter(const GEO::Mesh &M, index_t cell)
vecn< 3 > vec3
Definition: types.h:76
void ringmesh_unused(const T &)
Definition: common.h:105
double RINGMESH_API mesh_cell_signed_volume(const GEO::Mesh &M, index_t c)
void RINGMESH_API print_bounded_attributes(const GEO::Mesh &M)
index_t find(const container &in, const T &value)
Returns the position of the first entity matching.
Definition: algorithm.h:55
static void err(const std::string &feature, const Args &... args)
Definition: logger.h:68
double RINGMESH_API mesh_cell_volume(const GEO::Mesh &M, index_t c)
#define ringmesh_assert(x)
vec3 RINGMESH_API mesh_cell_facet_barycenter(const GEO::Mesh &M, index_t cell, index_t f)
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
void RINGMESH_API ringmesh_mesh_io_initialize()
complement the available MeshIOHandler
#define ringmesh_assert_not_reached