40 #include <geogram/basic/file_system.h> 41 #include <geogram/basic/line_stream.h> 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> 56 std::vector< std::string > bounded_attribute_names(
57 GEO::AttributesManager& manager )
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() );
65 for( std::string name : names )
67 if( manager.find_attribute_store( name )->has_observers() )
69 bounded_names.push_back( name );
77 const std::string& output_location )
81 Logger::err(
"Attributes",
"Attributes still bounded on ",
82 output_location,
":" );
83 for( std::string name : names )
91 GEO::AttributesManager& manager,
const std::string& output_location )
93 std::vector< std::string > names = bounded_attribute_names( manager );
100 class TSurfMeshIOHandler final :
public GEO::MeshIOHandler
104 : starting_index_( 1 ),
105 mesh_dimension_( 3 ),
122 bool load(
const std::string& filename,
124 const GEO::MeshIOFlags& flag = GEO::MeshIOFlags() ) final
127 filename_ = filename;
128 if( !is_file_valid() )
134 read_number_of_vertices_and_triangles();
135 read_vertex_property_names();
136 read_vertex_property_sizes();
138 allocate_triangles();
139 allocate_vertex_properties();
140 read_vertices_and_triangles();
141 assign_and_repair_mesh( mesh );
149 bool save(
const GEO::Mesh& mesh,
150 const std::string& filename,
151 const GEO::MeshIOFlags& flag = GEO::MeshIOFlags() ) final
154 if( !mesh.facets.are_simplices() )
157 "Cannot save a non triangulated mesh into TSurf format" );
160 std::ofstream out( filename.c_str() );
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 );
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;
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 )
182 for(
auto v :
range( mesh.vertices.nb() ) )
186 out <<
"PVRTX " << v + starting_index_ <<
" " 187 << mesh.vertices.point( v );
188 for(
auto attr_dbl_itr :
range( att_v_double_names.size() ) )
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 ) )
196 out <<
" " << cur_attr[v * nb_dimensions + dim_itr];
202 void save_triangles( std::ofstream& out,
const GEO::Mesh& mesh )
204 for(
auto t :
range( mesh.facets.nb() ) )
207 for(
auto v :
range( 3 ) )
209 out <<
" " << mesh.facets.vertex( t, v ) + starting_index_;
214 void save_header( std::ofstream& out,
const std::string& mesh_name )
216 out <<
"GOCAD TSurf" << std::endl;
217 out <<
"HEADER {" << std::endl;
218 out <<
"name: " << mesh_name << std::endl;
219 out <<
"}" << std::endl;
221 void fill_vertex_attribute_header(
const GEO::Mesh& mesh,
223 std::vector< std::string >& att_v_double_names,
224 std::vector< index_t >& vertex_attr_dims )
const 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() ) )
232 if( att_v_names[att_v] ==
"point" )
237 if( !GEO::Attribute< double >::is_defined(
238 mesh_vertex_mgr, att_v_names[att_v] ) )
242 att_v_double_names.push_back( att_v_names[att_v] );
244 mesh_vertex_mgr.find_attribute_store( att_v_names[att_v] )
246 vertex_attr_dims.push_back( cur_dim );
249 if( !att_v_double_names.empty() )
251 index_t nb_attributes =
252 static_cast< index_t
>( att_v_double_names.size() );
254 for(
auto attr_dbl_itr :
range( nb_attributes ) )
256 out <<
" " << att_v_double_names[attr_dbl_itr];
259 out <<
"PROP_LEGAL_RANGES";
260 for(
auto attr_dbl_itr :
range( nb_attributes ) )
263 out <<
" **none** **none**";
266 out <<
"NO_DATA_VALUES";
267 for(
auto attr_dbl_itr :
range( nb_attributes ) )
274 for(
auto attr_dbl_itr :
range( nb_attributes ) )
280 out <<
"PROPERTY_CLASSES";
281 for(
auto attr_dbl_itr :
range( nb_attributes ) )
283 out <<
" " << att_v_double_names[attr_dbl_itr];
286 out <<
"PROPERTY_KINDS";
287 for(
auto attr_dbl_itr :
range( nb_attributes ) )
290 out <<
" \"Real Number\"";
293 out <<
"PROPERTY_SUBCLASSES";
294 for(
auto attr_dbl_itr :
range( nb_attributes ) )
297 out <<
" QUANTITY Float";
301 for(
auto attr_dbl_itr :
range( nb_attributes ) )
304 << std::to_string( vertex_attr_dims[attr_dbl_itr] );
308 for(
auto attr_dbl_itr :
range( nb_attributes ) )
314 for(
auto attr_dbl_itr :
range( nb_attributes ) )
316 out <<
"PROPERTY_CLASS_HEADER " 317 << att_v_double_names[attr_dbl_itr] <<
" {" 319 out <<
"kind: Real Number" << std::endl;
320 out <<
"unit: unitless" << std::endl;
321 out <<
"}" << std::endl;
326 void read_number_of_vertices_and_triangles()
328 GEO::LineInput in( filename_ );
329 while( !in.eof() && in.get_line() )
332 if( in.nb_fields() > 0 )
334 if( in.field_matches( 0,
"ZPOSITIVE" ) )
336 if( in.field_matches( 1,
"Elevation" ) )
340 else if( in.field_matches( 1,
"Depth" ) )
345 else if( in.field_matches( 0,
"VRTX" )
346 || in.field_matches( 0,
"PVRTX" ) )
350 else if( in.field_matches( 0,
"PATOM" )
351 || in.field_matches( 0,
"ATOM" ) )
355 else if( in.field_matches( 0,
"TRGL" ) )
363 void read_vertices_and_triangles()
365 GEO::LineInput in( filename_ );
368 while( !in.eof() && in.get_line() )
371 if( in.nb_fields() > 0 )
373 if( in.field_matches( 0,
"VRTX" )
374 || in.field_matches( 0,
"PVRTX" ) )
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" ) )
385 for(
auto prop_name_itr :
386 range( vertex_property_names_.size() ) )
388 for(
auto v_attr_dim_itr :
389 range( vertex_attribute_dims_
392 vertex_attributes_[prop_name_itr][v]
402 else if( in.field_matches( 0,
"PATOM" )
403 || in.field_matches( 0,
"ATOM" ) )
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];
414 else if( in.field_matches( 0,
"TRGL" ) )
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_ );
428 void read_vertex_property_names()
430 GEO::LineInput in( filename_ );
431 while( !in.eof() && in.get_line() )
434 if( in.nb_fields() > 0 )
436 if( !in.field_matches( 0,
"PROPERTIES" ) )
440 vertex_property_names_.reserve( in.nb_fields() - 1 );
441 for(
auto prop_name_itr :
range( 1, in.nb_fields() ) )
443 vertex_property_names_.push_back(
444 in.field( prop_name_itr ) );
451 void read_vertex_property_sizes()
453 GEO::LineInput in( filename_ );
454 while( !in.eof() && in.get_line() )
457 if( in.nb_fields() > 0 )
459 if( !in.field_matches( 0,
"ESIZES" ) )
463 vertex_property_names_.reserve( in.nb_fields() - 1 );
464 for(
auto prop_size_itr :
range( 1, in.nb_fields() ) )
466 vertex_attribute_dims_.push_back(
467 in.field_as_uint( prop_size_itr ) );
474 void assign_and_repair_mesh( GEO::Mesh& mesh )
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 );
481 assign_tsurf_properties_to_geogram_mesh( mesh );
485 GEO::mesh_repair( mesh, GEO::MESH_REPAIR_DUP_F );
488 void assign_tsurf_properties_to_geogram_mesh( GEO::Mesh& mesh )
490 for(
auto prop_name_itr :
range( vertex_property_names_.size() ) )
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_ ) )
498 for(
auto prop_dim_itr :
range( nb_dimensions ) )
500 attr[v_itr * nb_dimensions + prop_dim_itr] =
501 vertex_attributes_[prop_name_itr][v_itr]
510 GEO::LineInput in( filename_ );
521 void allocate_vertices()
523 vertices_.resize( mesh_dimension_ * nb_vertices_ );
526 void allocate_triangles()
528 triangles_.resize( 3 * nb_triangles_ );
530 void allocate_vertex_properties()
532 vertex_attributes_.resize( vertex_property_names_.size() );
533 for(
auto vertex_attributes_itr :
534 range( vertex_attributes_.size() ) )
536 vertex_attributes_[vertex_attributes_itr].resize(
538 for(
auto vertex_itr :
range( nb_vertices_ ) )
540 vertex_attributes_[vertex_attributes_itr][vertex_itr]
542 vertex_attribute_dims_[vertex_attributes_itr], 0 );
548 index_t starting_index_;
549 index_t mesh_dimension_;
550 index_t nb_vertices_;
551 index_t nb_triangles_;
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_;
561 class LINMeshIOHandler final :
public GEO::MeshIOHandler
564 bool load(
const std::string& filename,
566 const GEO::MeshIOFlags& flag = GEO::MeshIOFlags() ) final
569 GEO::LineInput file( filename );
571 while( !file.eof() && file.get_line() )
574 if( file.nb_fields() > 0 )
576 if( file.field_matches( 0,
"v" ) )
578 vec3 vertex = load_vertex( file, 1 );
579 mesh.vertices.create_vertex( vertex.data() );
581 else if( file.field_matches( 0,
"s" ) )
583 mesh.edges.create_edge( file.field_as_uint( 1 ) - 1,
584 file.field_as_uint( 2 ) - 1 );
590 bool save(
const GEO::Mesh& M,
591 const std::string& filename,
592 const GEO::MeshIOFlags& ioflags = GEO::MeshIOFlags() ) final
598 "I/O",
"Saving a Mesh into .lin format not implemented yet" );
603 vec3 load_vertex( GEO::LineInput& file, index_t field )
const 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 );
621 geo_register_MeshIOHandler_creator( TSurfMeshIOHandler,
"ts" );
622 geo_register_MeshIOHandler_creator( LINMeshIOHandler,
"lin" );
628 switch( M.cells.type( c ) )
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 ) ) );
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 ) ) );
649 case GEO::MESH_PRISM:
653 for(
auto f :
range( M.cells.nb_facets( c ) ) )
655 switch( M.cells.facet_nb_vertices( c, f ) )
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 ) ),
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 ) ),
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 ) ),
704 const GEO::Mesh& M, index_t cell, index_t f )
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 ) )
711 GEO::Geom::mesh_vertex( M, M.cells.facet_vertex( cell, f, v ) );
715 return result /
static_cast< double >( nb_vertices );
720 vec3 result( 0.0, 0.0, 0.0 );
721 for(
auto v :
range( M.cells.nb_vertices( cell ) ) )
723 result += GEO::Geom::mesh_vertex( M, M.cells.vertex( cell, v ) );
725 return ( 1.0 / M.cells.nb_vertices( cell ) ) * result;
730 std::vector< std::string > names =
731 bounded_attribute_names( M.vertices.attributes() );
732 names.erase(
std::find( names.begin(), names.end(),
"point" ) );
737 M.facet_corners.attributes(),
"facet_corners" );
740 M.cell_corners.attributes(),
"cell_corners" );
vec3 RINGMESH_API mesh_cell_barycenter(const GEO::Mesh &M, index_t cell)
void ringmesh_unused(const T &)
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.
static void err(const std::string &feature, const Args &... args)
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.
void RINGMESH_API ringmesh_mesh_io_initialize()
complement the available MeshIOHandler
#define ringmesh_assert_not_reached