45 numeric_like_vertex_attribute_names_(),
46 vertex_attribute_dimensions_(),
47 numeric_like_cell_attribute_names_(),
48 cell_attribute_dimensions_(),
51 vertex_exported_id_(),
55 void load(
const std::string& filename, GeoModel3D& geomodel )
final 57 std::ifstream input( filename.c_str() );
63 builder.build_geomodel();
65 void save(
const GeoModel3D& geomodel,
const std::string& filename )
final 68 out_.open( filename.c_str() );
72 fill_top_header( geomodel );
74 const GeoModelMesh3D& mesh = geomodel.mesh;
77 fill_vertex_attribute_header( geomodel );
78 fill_cell_attribute_header( geomodel );
80 vertex_exported_.resize( mesh.vertices.nb(), false );
81 atom_exported_.resize( mesh.cells.nb_duplicated_vertices(), false );
82 vertex_exported_id_.resize( mesh.vertices.nb(), NO_ID );
83 atom_exported_id_.resize( mesh.cells.nb_duplicated_vertices(), NO_ID );
84 for( index_t r = 0; r < geomodel.nb_regions(); r++ ) {
85 const auto& region = geomodel.region( r );
86 export_one_region( region );
89 export_model( geomodel );
90 export_model_region( geomodel );
92 out_ <<
"END" << std::endl;
95 const GeoModelMesh3D& geomodel_mesh(
const Region3D& region )
const 97 return region.geomodel().mesh;
99 void fill_top_header(
const GeoModel3D& geomodel )
103 out_ <<
"GOCAD TSolid 1" <<
EOL <<
"HEADER {" <<
EOL <<
"name:" 104 << geomodel.name() <<
EOL <<
"}" <<
EOL;
106 out_ <<
"GOCAD_ORIGINAL_COORDINATE_SYSTEM" << EOL <<
"NAME Default" 107 << EOL <<
"AXIS_NAME \"X\" \"Y\" \"Z\"" << EOL
108 <<
"AXIS_UNIT \"m\" \"m\" \"m\"" << EOL <<
"ZPOSITIVE Elevation" 109 << EOL <<
"END_ORIGINAL_COORDINATE_SYSTEM" <<
EOL;
111 void fill_vertex_attribute_header(
const GeoModel3D& geomodel )
114 std::vector< bool > is_integer_like_attribute;
115 for( index_t reg_i = 0; reg_i < geomodel.nb_regions(); ++reg_i ) {
116 const Region3D& cur_reg = geomodel.region( reg_i );
117 GEO::AttributesManager& reg_vertex_attr_mgr =
118 cur_reg.vertex_attribute_manager();
119 GEO::vector< std::string > att_v_names;
120 reg_vertex_attr_mgr.list_attribute_names( att_v_names );
122 for(
const std::string& cur_att_v_name : att_v_names ) {
124 if( cur_att_v_name ==
"point" ) {
128 if(
contains( numeric_like_vertex_attribute_names_,
133 const GEO::AttributeStore* attr_store =
134 reg_vertex_attr_mgr.find_attribute_store( cur_att_v_name );
137 if( !GEO::ReadOnlyScalarAttributeAdapter::can_be_bound_to(
142 numeric_like_vertex_attribute_names_.push_back( cur_att_v_name );
143 index_t cur_dim = attr_store->dimension();
144 vertex_attribute_dimensions_.push_back( cur_dim );
146 const GEO::ReadOnlyScalarAttributeAdapter adapter(
147 reg_vertex_attr_mgr, cur_att_v_name );
149 adapter.element_type()
150 != GEO::ReadOnlyScalarAttributeAdapter::ET_NONE );
151 is_integer_like_attribute.push_back(
152 adapter.element_type()
153 < GEO::ReadOnlyScalarAttributeAdapter::ET_FLOAT32 );
157 const auto nb_numeric_like_vertex_attribute_names =
158 static_cast< index_t
>( numeric_like_vertex_attribute_names_.size() );
159 if( !numeric_like_vertex_attribute_names_.empty() ) {
161 out_ <<
"PROPERTIES";
162 for(
const std::string& cur_num_like_v_att_name : numeric_like_vertex_attribute_names_ ) {
163 out_ <<
" " << cur_num_like_v_att_name;
166 out_ <<
"PROP_LEGAL_RANGES";
167 for(
auto i :
range( nb_numeric_like_vertex_attribute_names ) ) {
169 out_ <<
" **none** **none**";
172 out_ <<
"NO_DATA_VALUES";
173 write_no_data_value( nb_numeric_like_vertex_attribute_names );
176 for(
auto i :
range( nb_numeric_like_vertex_attribute_names ) ) {
181 out_ <<
"PROPERTY_CLASSES";
182 for(
const std::string& cur_num_like_v_att_name : numeric_like_vertex_attribute_names_ ) {
183 out_ <<
" " << cur_num_like_v_att_name;
186 out_ <<
"PROPERTY_KINDS";
187 for(
auto i :
range( nb_numeric_like_vertex_attribute_names ) ) {
188 if( is_integer_like_attribute[i] ) {
189 out_ <<
" \"Number\"";
191 out_ <<
" \"Real Number\"";
195 out_ <<
"PROPERTY_SUBCLASSES";
196 for(
auto i :
range( nb_numeric_like_vertex_attribute_names ) ) {
198 out_ <<
" QUANTITY Float";
202 for(
const auto& cur_v_att_dim : vertex_attribute_dimensions_ ) {
203 out_ <<
" " << GEO::String::to_string( cur_v_att_dim );
207 for(
auto i :
range( nb_numeric_like_vertex_attribute_names ) ) {
212 for(
auto i :
range( nb_numeric_like_vertex_attribute_names ) ) {
213 out_ <<
"PROPERTY_CLASS_HEADER " 214 << numeric_like_vertex_attribute_names_[i] <<
" {" <<
EOL;
215 if( is_integer_like_attribute[i] ) {
216 out_ <<
"kind: Number" <<
EOL;
218 out_ <<
"kind: Real Number" <<
EOL;
220 out_ <<
"unit: unitless" <<
EOL;
226 void fill_cell_attribute_header(
const GeoModel3D& geomodel )
229 std::vector< bool > is_integer_like_attribute;
231 auto& reg_cell_attr_mgr =
232 cur_reg.cell_attribute_manager();
233 GEO::vector< std::string > att_c_names;
234 reg_cell_attr_mgr.list_attribute_names( att_c_names );
236 for(
const auto& cur_att_c_name : att_c_names ) {
238 if(
contains( numeric_like_cell_attribute_names_,
243 const auto* attr_store =
244 reg_cell_attr_mgr.find_attribute_store( cur_att_c_name );
247 if( !GEO::ReadOnlyScalarAttributeAdapter::can_be_bound_to(
252 numeric_like_cell_attribute_names_.push_back( cur_att_c_name );
253 auto cur_dim = attr_store->dimension();
254 cell_attribute_dimensions_.push_back( cur_dim );
256 const GEO::ReadOnlyScalarAttributeAdapter adapter(
257 reg_cell_attr_mgr, cur_att_c_name );
259 adapter.element_type()
260 != GEO::ReadOnlyScalarAttributeAdapter::ET_NONE );
261 is_integer_like_attribute.push_back(
262 adapter.element_type()
263 < GEO::ReadOnlyScalarAttributeAdapter::ET_FLOAT32 );
267 const auto nb_numeric_like_cell_attribute_names =
268 static_cast< index_t
> ( numeric_like_cell_attribute_names_.size() );
269 if( !numeric_like_cell_attribute_names_.empty() ) {
270 out_ <<
"TETRA_PROPERTIES";
271 for(
const auto& cur_num_like_c_att_name : numeric_like_cell_attribute_names_ ) {
272 out_ <<
" " << cur_num_like_c_att_name;
275 out_ <<
"TETRA_PROP_LEGAL_RANGES";
276 for(
auto i :
range( nb_numeric_like_cell_attribute_names ) ) {
278 out_ <<
" **none** **none**";
281 out_ <<
"TETRA_NO_DATA_VALUES";
282 write_no_data_value( nb_numeric_like_cell_attribute_names );
285 for(
auto i :
range( nb_numeric_like_cell_attribute_names ) ) {
290 out_ <<
"TETRA_PROPERTY_CLASSES";
291 for(
const auto& cur_num_like_c_att_name : numeric_like_cell_attribute_names_ ) {
292 out_ <<
" " << cur_num_like_c_att_name;
295 out_ <<
"TETRA_PROPERTY_KINDS";
296 for(
auto i :
range( nb_numeric_like_cell_attribute_names ) ) {
297 if( is_integer_like_attribute[i] ) {
298 out_ <<
" \"Number\"";
300 out_ <<
" \"Real Number\"";
304 out_ <<
"TETRA_PROPERTY_SUBCLASSES";
305 for(
auto i :
range( nb_numeric_like_cell_attribute_names ) ) {
307 out_ <<
" QUANTITY Float";
310 out_ <<
"TETRA_ESIZES";
311 for(
const auto& cur_cell_attr_dim : cell_attribute_dimensions_ ) {
312 out_ <<
" " << std::to_string( cur_cell_attr_dim );
315 out_ <<
"TETRA_UNITS";
316 for(
auto i :
range( nb_numeric_like_cell_attribute_names ) ) {
321 for(
auto i :
range( nb_numeric_like_cell_attribute_names ) ) {
322 out_ <<
"TETRA_PROPERTY_CLASS_HEADER " 323 << numeric_like_cell_attribute_names_[i] <<
" {" <<
EOL;
324 if( is_integer_like_attribute[i] ) {
325 out_ <<
"kind: Number" <<
EOL;
327 out_ <<
"kind: Real Number" <<
EOL;
329 out_ <<
"unit: unitless" <<
EOL;
335 void export_one_region(
const Region3D& region )
338 out_ <<
"TVOLUME " << region.name() <<
EOL;
339 export_region_vertices( region );
340 export_tetrahedra( region );
343 void export_region_vertices(
const Region3D& region )
346 for(
auto c :
range( region.nb_mesh_elements() ) ) {
347 export_region_cell_vertices( region, c );
351 void export_region_cell_vertices(
const Region3D& region, index_t c )
353 const auto& mesh = geomodel_mesh( region );
354 auto cell = mesh.cells.cell( region.gmme().index(), c );
355 auto cell_center = mesh.cells.barycenter( cell );
356 for(
auto v :
range( mesh.cells.nb_vertices( cell ) ) ) {
357 export_region_cell_vertex( region, cell, v, cell_center );
361 void export_region_cell_vertex(
362 const Region3D& region,
365 const vec3& cell_center )
368 const auto& mesh = geomodel_mesh( region );
369 auto atom_id = mesh.cells.duplicated_corner_index( { cell, v } );
370 if( atom_id == NO_ID ) {
371 auto vertex_id = mesh.cells.vertex( { cell, v } );
372 if( vertex_exported_[vertex_id] )
return;
373 vertex_exported_[vertex_id] =
true;
374 vertex_exported_id_[vertex_id] = nb_vertices_exported_;
377 out_ <<
"PVRTX " << nb_vertices_exported_++ <<
" " 378 << mesh.vertices.vertex( vertex_id );
381 export_region_cell_vertex_attributes( region, vertex_id,
387 void export_region_cell_vertex_attributes(
388 const Region3D& region,
390 const vec3& cell_center )
393 auto& reg_vertex_attr_mgr =
394 region.vertex_attribute_manager();
395 auto vertex_id_in_reg = find_gmm_cell_in_gm_region( region, vertex_id,
399 for(
auto attr_dbl_itr :
range(
400 static_cast< index_t >( numeric_like_vertex_attribute_names_.size() ) ) ) {
401 if( reg_vertex_attr_mgr.is_defined(
402 numeric_like_vertex_attribute_names_[attr_dbl_itr] ) ) {
404 reg_vertex_attr_mgr.find_attribute_store(
405 numeric_like_vertex_attribute_names_[attr_dbl_itr] )
408 reg_vertex_attr_mgr.find_attribute_store(
409 numeric_like_vertex_attribute_names_[attr_dbl_itr] )->dimension()
410 == vertex_attribute_dimensions_[attr_dbl_itr] );
412 GEO::ReadOnlyScalarAttributeAdapter::can_be_bound_to(
413 reg_vertex_attr_mgr.find_attribute_store(
414 numeric_like_vertex_attribute_names_[attr_dbl_itr] ) ) );
415 GEO::ReadOnlyScalarAttributeAdapter cur_attr(
417 numeric_like_vertex_attribute_names_[attr_dbl_itr] );
418 for(
auto dim_itr :
range(vertex_attribute_dimensions_[attr_dbl_itr] ) ) {
420 << cur_attr[vertex_id_in_reg
421 * vertex_attribute_dimensions_[attr_dbl_itr]
426 vertex_attribute_dimensions_[attr_dbl_itr] );
431 index_t find_gmm_cell_in_gm_region(
432 const Region3D& region,
434 const vec3& cell_center )
const 436 const auto& mesh = geomodel_mesh( region );
442 const auto& gme_vertices =
443 mesh.vertices.gme_vertices( vertex_id );
444 for(
const auto& cur_gme_vertex : gme_vertices ) {
445 if( cur_gme_vertex.gmme != region.gmme() ) {
448 auto cells_around_vertex =
449 region.cells_around_vertex( cur_gme_vertex.v_index, NO_ID );
453 for(
const auto& cur_cell_around_vertex : cells_around_vertex ) {
454 auto center = region.mesh_element_barycenter(
455 cur_cell_around_vertex );
456 if( ( center - cell_center ).length()
457 < region.geomodel().epsilon() ) {
458 return cur_gme_vertex.v_index;
466 void export_tetrahedra(
const Region3D& region )
469 export_duplicated_vertices();
472 mark_boundary_ending_in_region( region );
474 const auto& mesh = geomodel_mesh( region );
475 for(
auto c :
range(region.nb_mesh_elements() ) ) {
477 auto cell = mesh.cells.cell( region.gmme().index(), c );
478 export_tetra( region, cell );
480 out_ <<
"# CTETRA " << region.name();
481 export_ctetra( region, c );
486 void export_duplicated_vertices()
const 506 void mark_boundary_ending_in_region(
const Region3D& region )
508 std::map< index_t, index_t > sides;
509 for(
auto s :
range( region.nb_boundaries() ) ) {
510 if( sides.count( region.boundary_gmme( s ).index() ) > 0 ) {
512 sides[region.boundary_gmme( s ).index()] = 2;
514 sides[region.boundary_gmme( s ).index()] = region.side( s );
519 void export_tetra(
const Region3D& region, index_t cell )
521 export_tetra_coordinates( region, cell );
523 export_tetra_attributes( region, cell );
526 void export_tetra_coordinates(
const Region3D& region, index_t cell )
529 const auto& mesh = geomodel_mesh( region );
531 region.geomodel().mesh.cells.nb_vertices( cell ) ) ) {
532 auto atom_id = mesh.cells.duplicated_corner_index(
534 if( atom_id == NO_ID ) {
535 auto vertex_id = mesh.cells.vertex(
537 out_ <<
" " << vertex_exported_id_[vertex_id];
539 out_ <<
" " << atom_exported_id_[atom_id];
544 void export_tetra_attributes(
const Region3D& region, index_t cell )
547 auto& reg_cell_attr_mgr =
548 region.cell_attribute_manager();
549 const auto& mesh = geomodel_mesh( region );
550 auto center = mesh.cells.barycenter( cell );
551 const auto c_in_reg =
552 region.cell_nn_search().get_neighbors( center,
553 region.geomodel().epsilon() );
555 for(
auto attr_dbl_itr :
range(
556 numeric_like_cell_attribute_names_.size() ) ) {
557 if( reg_cell_attr_mgr.is_defined(
558 numeric_like_cell_attribute_names_[attr_dbl_itr] ) ) {
560 reg_cell_attr_mgr.find_attribute_store(
561 numeric_like_cell_attribute_names_[attr_dbl_itr] )
564 reg_cell_attr_mgr.find_attribute_store(
565 numeric_like_cell_attribute_names_[attr_dbl_itr] )->dimension()
566 == cell_attribute_dimensions_[attr_dbl_itr] );
568 GEO::ReadOnlyScalarAttributeAdapter::can_be_bound_to(
569 reg_cell_attr_mgr.find_attribute_store(
570 numeric_like_cell_attribute_names_[attr_dbl_itr] ) ) );
571 GEO::ReadOnlyScalarAttributeAdapter cur_attr( reg_cell_attr_mgr,
572 numeric_like_cell_attribute_names_[attr_dbl_itr] );
573 for(
auto dim_itr :
range(
574 cell_attribute_dimensions_[attr_dbl_itr] ) ) {
576 << cur_attr[c_in_reg[0]
577 * cell_attribute_dimensions_[attr_dbl_itr] + dim_itr];
580 write_no_data_value( cell_attribute_dimensions_[attr_dbl_itr] );
585 void export_ctetra(
const Region3D& region, index_t c )
588 const auto& mesh = geomodel_mesh( region );
589 for(
auto f :
range( mesh.cells.nb_facets( c ) ) ) {
591 index_t polygon { NO_ID };
593 if( mesh.cells.is_cell_facet_on_surface( c, f, polygon, side ) ) {
594 index_t surface_id { mesh.polygons.surface( polygon ) };
595 side ? out_ <<
"+" : out_ <<
"-";
597 << region.geomodel().surface( surface_id ).parent( 0 ).name();
604 void export_model(
const GeoModel3D& geomodel )
607 out_ <<
"MODEL" <<
EOL;
610 const auto& polygons = geomodel.mesh.polygons;
611 for(
auto& cur_interface : geomodel.geol_entities(
612 Interface3D::type_name_static() ) ) {
613 out_ <<
"SURFACE " << cur_interface.name() <<
EOL;
614 for(
auto s :
range( cur_interface.nb_children() ) ) {
615 out_ <<
"TFACE " << tface_count++ <<
EOL;
616 auto surface_id = cur_interface.child_gmme( s ).index();
617 out_ <<
"KEYVERTICES";
618 auto key_polygon_id = polygons.polygon( surface_id, 0 );
619 for(
auto v :
range( polygons.nb_vertices( key_polygon_id ) ) ) {
621 << vertex_exported_id_[polygons.vertex(
622 { key_polygon_id, v } )];
625 for(
auto p :
range( polygons.nb_polygons( surface_id ) ) ) {
626 auto polygon_id = polygons.polygon( surface_id, p );
628 for(
auto v :
range( polygons.nb_vertices( polygon_id ) ) ) {
630 << vertex_exported_id_[polygons.vertex(
631 { polygon_id, v } )];
638 void export_model_region(
const GeoModel3D& geomodel )
641 for(
auto& region : geomodel.regions() ) {
642 out_ <<
"MODEL_REGION " << region.name() <<
" ";
643 region.side( 0 ) ? out_ <<
"+" : out_ <<
"-";
644 out_ << region.boundary_gmme( 0 ).index() + 1 <<
EOL;
648 void write_no_data_value( index_t nb )
650 for(
auto i :
range( nb ) ) {
652 out_ <<
" " << GEO::String::to_string( gocad_no_data_value_ );
667 std::vector< std::string > numeric_like_vertex_attribute_names_;
668 std::vector< index_t > vertex_attribute_dimensions_;
669 std::vector< std::string > numeric_like_cell_attribute_names_;
670 std::vector< index_t > cell_attribute_dimensions_;
671 std::vector< bool > vertex_exported_;
672 std::vector< bool > atom_exported_;
673 std::vector< index_t > vertex_exported_id_;
674 std::vector< index_t > atom_exported_id_;
675 index_t nb_vertices_exported_ { 1 };
678 const double gocad_no_data_value_ { -99999 };
void ringmesh_unused(const T &)
Builds a meshed GeoModel from a Gocad TSolid (file.so)
#define ringmesh_assert(x)
bool contains(const container &in, const T &value)
Classes to build GeoModel from various inputs.
#define ringmesh_assert_not_reached