41     index_t nb_polygons( 
const GeoModel3D& geomodel )
    44         for( 
const auto& surface : surface_range < 3 > ( geomodel ) ) {
    45             result += surface.nb_mesh_elements();
    58     void save_region( index_t count, 
const Region3D& region, std::ostream& out )
    60         out << 
"REGION " << count << 
"  " << region.name() << 
" " << 
EOL;
    63         for( 
auto i : range( region.nb_boundaries() ) ) {
    65             if( region.side( i ) ) {
    70             out << region.boundary( i ).index() + 1;
    82         const GeoModel3D& geomodel,
    85         const SurfaceSide surface_region_sides = geomodel.voi_surfaces();
    87         out << 
"REGION " << count << 
"  Universe "    91         for( 
auto i : range( surface_region_sides.surfaces_.size() ) ) {
    93             if( surface_region_sides.sides_[ i ] ) {
    98             out << surface_region_sides.surfaces_[ i ] + 1;
   119         const GeoModelGeologicalEntity3D& layer,
   122         out << 
"LAYER " << layer.name() << 
" " << 
EOL;
   125         for( 
auto i : range( layer.nb_children() ) ) {
   126             out << 
"  " << layer.child_gmme( i ).index() + offset + 1;
   140     void save_coordinate_system( std::ostream& out )
   142         out << 
"GOCAD_ORIGINAL_COORDINATE_SYSTEM" << EOL << 
"NAME Default"   143             << EOL << 
"AXIS_NAME \"X\" \"Y\" \"Z\"" << EOL
   144             << 
"AXIS_UNIT \"m\" \"m\" \"m\"" << EOL << 
"ZPOSITIVE Elevation"   145             << EOL << 
"END_ORIGINAL_COORDINATE_SYSTEM" << 
EOL;
   156     bool check_gocad_validity( 
const GeoModel3D& geomodel )
   158         auto nb_interfaces = geomodel.nb_geological_entities(
   159             Interface3D::type_name_static() );
   160         if( nb_interfaces == 0 ) {
   161             Logger::err( 
"", 
" The GeoModel ", geomodel.name(),
   162                 " has no Interface" );
   165         for( 
auto& cur_interface : geomodel.geol_entities(
   166             Interface3D::type_name_static() ) ) {
   167             if( !cur_interface.has_geological_feature() ) {
   168                 Logger::err( 
"", cur_interface.gmge(), 
" has no geological feature" );
   172         for( 
const auto& surface : surface_range < 3 > ( geomodel ) ) {
   173             if( !surface.has_parent() ) {
   174                 Logger::err( 
"", surface.gmme(),
   175                     " does not belong to any Interface of the geomodel" );
   178             if( !surface.is_simplicial() ) {
   179                 Logger::err( 
"", surface.gmme(), 
" is not triangulated " );
   187     bool has_surface_edge(
   188         const Surface3D& surface,
   192         for( 
auto i : range( surface.nb_mesh_elements() ) ) {
   193             for( 
auto j : range( surface.nb_mesh_element_vertices( i ) ) ) {
   194                 auto v0 = surface.mesh_element_vertex_index(
   196                 auto v1 = surface.mesh_element_vertex_index(
   197                     surface.mesh().next_polygon_vertex(
   199                 if( ( v0 == v0_in && v1 == v1_in )
   200                     || ( v0 == v1_in && v1 == v0_in ) ) {
   213     void save_gocad_model3d( 
const GeoModel3D& geomodel, std::ostream& out )
   215         if( !check_gocad_validity( geomodel ) ) {
   216             throw RINGMeshException( 
"I/O", 
" The GeoModel ", geomodel.name(),
   217                 " cannot be saved in .ml format" );
   222         out << 
"GOCAD Model3d 1" << EOL << 
"HEADER {" << EOL << 
"name: "   223             << geomodel.name() << EOL << 
"}" << 
EOL;
   225         save_coordinate_system( out );
   228         for( 
auto& tsurf : geomodel.geol_entities(
   229             Interface3D::type_name_static() ) ) {
   230             out << 
"TSURF " << tsurf.name() << 
EOL;
   236         for( 
const auto& surface : geomodel.surfaces() ) {
   237             const gmge_id& parent_interface = surface.parent_gmge(
   238                 Interface3D::type_name_static() );
   239             if( !parent_interface.is_defined() ) {
   240                 throw RINGMeshException( 
"I/O", 
"Failed to save GeoModel",
   241                     " in .ml Gocad format because Surface ", surface.index(),
   242                     " has no Interface parent)" );
   244             const auto& cur_geol_feature =
   245                 geomodel.geological_entity( parent_interface ).geological_feature();
   247             out << 
"TFACE " << count << 
"  ";
   248             out << GeoModelGeologicalEntity3D::geol_name( cur_geol_feature );
   249             out << 
" " << surface.parent( Interface3D::type_name_static() ).name()
   255                 << surface.mesh_element_vertex( { 0, 0 } )
   258                 << surface.mesh_element_vertex( { 0, 1 } )
   261                 << surface.mesh_element_vertex( { 0, 2 } )
   267         auto offset_layer = count;
   268         save_universe( count, geomodel, out );
   271         for( 
const auto& region : geomodel.regions() ) {
   272             save_region( count, region, out );
   276         if( geomodel.entity_type_manager().geological_entity_manager.is_valid_type(
   277             Layer3D::type_name_static() ) ) {
   278             for( 
auto& layer : geomodel.geol_entities( Layer3D::type_name_static() ) ) {
   279                 save_layer( offset_layer, layer, out );
   285         const auto& geomodel_vertices = geomodel.mesh.vertices;
   287         for( 
auto& tsurf : geomodel.geol_entities( Interface3D::type_name_static() ) ) {
   289             out << 
"GOCAD TSurf 1" << EOL << 
"HEADER {" << EOL << 
"name:"   290                 << tsurf.name() << EOL << 
"name_in_model_list:" << tsurf.name()
   291                 << EOL << 
"}" << 
EOL;
   292             save_coordinate_system( out );
   294             out << 
"GEOLOGICAL_FEATURE " << tsurf.name() << EOL
   295                 << 
"GEOLOGICAL_TYPE ";
   296             out << GeoModelGeologicalEntity < 3
   297                 > ::geol_name( tsurf.geological_feature() );
   299             out << 
"PROPERTY_CLASS_HEADER Z {" << EOL << 
"is_z:on" << EOL
   302             index_t vertex_count { 1 };
   304             auto offset = vertex_count;
   308             std::set< index_t > corners;
   309             std::set< std::pair< index_t, index_t > > lineindices;
   310             for( 
auto j : range( tsurf.nb_children() ) ) {
   311                 offset = vertex_count;
   312                 const auto& surface =
   313                     dynamic_cast< const Surface3D& 
>( tsurf.child( j ) );
   315                 out << 
"TFACE" << 
EOL;
   316                 for( 
auto k : range( surface.nb_vertices() ) ) {
   317                     out << 
"VRTX " << vertex_count << 
" " << surface.vertex( k )
   321                 for( 
auto k : range( surface.nb_mesh_elements() ) ) {
   323                         << surface.mesh_element_vertex_index(
   324                             { k, 0 } ) + offset << 
" "   325                         << surface.mesh_element_vertex_index(
   326                             { k, 1 } ) + offset << 
" "   327                         << surface.mesh_element_vertex_index(
   328                             { k, 2 } ) + offset << EOL;
   330                 for( 
auto k : range( surface.nb_boundaries() ) ) {
   331                     const auto& line = surface.boundary( k );
   332                     auto v0_model_id = geomodel_vertices.geomodel_vertex_id(
   334                     auto v1_model_id = geomodel_vertices.geomodel_vertex_id(
   337                     auto v0_surface_ids =
   338                         geomodel_vertices.mesh_entity_vertex_id( surface.gmme(),
   340                     auto v1_surface_ids =
   341                         geomodel_vertices.mesh_entity_vertex_id( surface.gmme(),
   344                     if( !surface.has_inside_border() ) {
   345                         auto v0 = v0_surface_ids[0];
   346                         auto v1 = v1_surface_ids[0];
   351                             std::pair< index_t, index_t >( v0, v1 ) );
   352                         corners.insert( v0 );
   357                         bool to_break = 
false;
   358                         for( 
auto v0 : v0_surface_ids ) {
   359                             for( 
auto v1 : v1_surface_ids ) {
   360                                 if( has_surface_edge( surface, v0, v1 ) ) {
   362                                         std::pair< index_t, index_t >( v0 + offset,
   366                                 if( !line.is_inside_border( surface )
   370                                 } 
else if( count == 2 ) {
   376                                 corners.insert( v0 + offset );
   383                     const auto& c1_id = line.boundary_gmme( 1 );
   385                         geomodel_vertices.mesh_entity_vertex_id( surface.gmme(),
   386                             geomodel_vertices.geomodel_vertex_id( c1_id ) );
   387                     corners.insert( gme_vertices.front() + offset );
   391             for( 
auto it( corners.begin() ); it != corners.end(); ++it ) {
   392                 out << 
"BSTONE " << *it << 
EOL;
   394             for( 
auto it( lineindices.begin() ); it != lineindices.end(); ++it ) {
   395                 out << 
"BORDER " << vertex_count << 
" " << it->first << 
" "   396                     << it->second << 
EOL;
   403     class MLIOHandler final: 
public GeoModelIOHandler< 3 > {
   408         void load( 
const std::string& filename, GeoModel3D& geomodel ) 
final   410             std::ifstream input( filename.c_str() );
   412                 throw RINGMeshException( 
"I/O", 
"Failed to open file ", filename );
   414             GeoModelBuilderML builder( geomodel, filename );
   415             builder.build_geomodel();
   418         void save( 
const GeoModel< 3 >& geomodel, 
const std::string& filename ) 
final   420             std::ofstream out( filename.c_str() );
   421             save_gocad_model3d( geomodel, out );