40 #include <geogram/basic/file_system.h> 42 #include <geogram/mesh/triangle_intersection.h> 62 template < index_t DIMENSION >
70 const auto& vertices = geomodel.
mesh.vertices;
71 const auto& p1 = vertices.vertex( polygons.
vertex( { triangle1, 0 } ) );
72 const auto& p2 = vertices.vertex( polygons.
vertex( { triangle1, 1 } ) );
73 const auto& p3 = vertices.vertex( polygons.
vertex( { triangle1, 2 } ) );
75 const auto& q1 = vertices.vertex( polygons.
vertex( { triangle2, 0 } ) );
76 const auto& q2 = vertices.vertex( polygons.
vertex( { triangle2, 1 } ) );
77 const auto& q3 = vertices.vertex( polygons.
vertex( { triangle2, 2 } ) );
78 GEO::vector< GEO::TriangleIsect > sym;
79 return triangles_intersections( p1, p2, p3, q1, q2, q3, sym );
82 template < index_t DIMENSION >
90 const auto& vertices = geomodel.
mesh.vertices;
91 const auto& p1 = vertices.vertex( polygons.
vertex( { triangle, 0 } ) );
92 const auto& p2 = vertices.vertex( polygons.
vertex( { triangle, 1 } ) );
93 const auto& p3 = vertices.vertex( polygons.
vertex( { triangle, 2 } ) );
95 const auto& q1 = vertices.vertex( polygons.
vertex( { quad, 0 } ) );
96 const auto& q2 = vertices.vertex( polygons.
vertex( { quad, 1 } ) );
97 const auto& q3 = vertices.vertex( polygons.
vertex( { quad, 2 } ) );
98 const auto& q4 = vertices.vertex( polygons.
vertex( { quad, 3 } ) );
99 GEO::vector< GEO::TriangleIsect > sym;
100 if( triangles_intersections( p1, p2, p3, q1, q2, q3, sym ) )
104 if( triangles_intersections( p1, p2, p3, q1, q3, q4, sym ) )
111 template < index_t DIMENSION >
119 const auto& vertices = geomodel.
mesh.vertices;
120 const auto& p1 = vertices.vertex( polygons.
vertex( { quad1, 0 } ) );
121 const auto& p2 = vertices.vertex( polygons.
vertex( { quad1, 1 } ) );
122 const auto& p3 = vertices.vertex( polygons.
vertex( { quad1, 2 } ) );
123 const auto& p4 = vertices.vertex( polygons.
vertex( { quad1, 3 } ) );
125 const auto& q1 = vertices.vertex( polygons.
vertex( { quad2, 0 } ) );
126 const auto& q2 = vertices.vertex( polygons.
vertex( { quad2, 1 } ) );
127 const auto& q3 = vertices.vertex( polygons.
vertex( { quad2, 2 } ) );
128 const auto& q4 = vertices.vertex( polygons.
vertex( { quad2, 3 } ) );
129 GEO::vector< GEO::TriangleIsect > sym;
130 if( triangles_intersections( p1, p2, p3, q1, q2, q3, sym ) )
134 if( triangles_intersections( p1, p2, p3, q1, q3, q4, sym ) )
138 if( triangles_intersections( p1, p3, p4, q1, q2, q3, sym ) )
142 if( triangles_intersections( p1, p3, p4, q1, q3, q4, sym ) )
149 template < index_t DIMENSION >
150 bool is_edge_on_line(
157 index_t delta_i{ v1 - v0 };
181 template < index_t DIMENSION >
182 bool is_edge_on_line(
187 geomodel.
mesh.vertices.gme_type_vertices( line_type, v0 );
188 if( v0_line_bme.empty() )
193 geomodel.
mesh.vertices.gme_type_vertices( line_type, v1 );
194 if( v1_line_bme.empty() )
199 bool found_line{
false };
200 for(
const auto& vertex0 : v0_line_bme )
202 index_t line0_id{ vertex0.gmme.index() };
203 for(
const auto& vertex1 : v1_line_bme )
205 if( line0_id == vertex1.gmme.index() )
207 if( !is_edge_on_line( geomodel.
line( line0_id ),
208 vertex0.v_index, vertex1.v_index ) )
226 template < index_t DIMENSION >
235 if( polygons.
adjacent( { p1, v1 } ) != NO_ID )
239 index_t v10{ polygons.
vertex( { p1, v1 } ) };
240 index_t v11{ polygons.
vertex(
241 { p1, ( v1 + 1 ) % polygons.
nb_vertices( p1 ) } ) };
244 if( polygons.
adjacent( { p2, v2 } ) != NO_ID )
248 index_t v20{ polygons.
vertex( { p2, v2 } ) };
249 index_t v21{ polygons.
vertex(
250 { p2, ( v2 + 1 ) % polygons.
nb_vertices( p2 ) } ) };
252 if( ( v10 == v20 && v11 == v21 )
253 || ( v10 == v21 && v11 == v20 ) )
255 if( is_edge_on_line( geomodel, v20, v21 ) )
266 template < index_t DIMENSION >
267 bool polygons_are_adjacent(
278 if( polygons.
adjacent( { p1, v } ) == p2 )
290 template < index_t DIMENSION >
291 class StoreIntersections
301 std::vector< bool >& has_isect )
302 : geomodel_( geomodel ),
303 polygons_( geomodel.mesh.polygons ),
304 has_intersection_( has_isect )
306 has_intersection_.assign( polygons_.nb(), 0 );
315 void operator()( index_t p1, index_t p2 )
317 if( p1 == p2 || polygons_are_adjacent( polygons_, p1, p2 )
318 || polygons_share_line_edge( geomodel_, polygons_, p1, p2 ) )
323 if( is_triangle( p1 ) )
325 if( is_triangle( p2 ) )
327 if( triangles_intersect( geomodel_, polygons_, p1, p2 ) )
329 has_intersection_[p1] = 1;
330 has_intersection_[p2] = 1;
333 else if( is_quad( p2 ) )
335 if( triangle_quad_intersect(
336 geomodel_, polygons_, p1, p2 ) )
338 has_intersection_[p1] = 1;
339 has_intersection_[p2] = 1;
347 else if( is_quad( p1 ) )
349 if( is_triangle( p2 ) )
351 if( triangle_quad_intersect(
352 geomodel_, polygons_, p2, p1 ) )
354 has_intersection_[p1] = 1;
355 has_intersection_[p2] = 1;
358 else if( is_quad( p2 ) )
360 if( quad_quad_intersect( geomodel_, polygons_, p1, p2 ) )
362 has_intersection_[p1] = 1;
363 has_intersection_[p2] = 1;
377 bool is_triangle( index_t p )
const 380 std::tie( type, std::ignore ) = polygons_.type( p );
383 bool is_quad( index_t p )
const 386 std::tie( type, std::ignore ) = polygons_.type( p );
393 std::vector< bool >& has_intersection_;
396 void save_mesh_locating_geomodel_inconsistencies(
397 const GEO::Mesh& mesh,
const std::ostringstream& file )
399 if( GEO::CmdLine::get_arg_bool(
"validity_save" ) )
401 GEO::mesh_save( mesh, file.str() );
411 template < index_t DIMENSION >
417 for(
auto i :
range( E.nb_boundaries() ) )
419 if( E.boundary_gmme( i ) == boundary )
427 template < index_t DIMENSION >
428 void save_invalid_points(
const std::ostringstream& file,
430 const std::vector< bool >& valid )
432 GEO::Mesh point_mesh;
433 for(
auto i :
range( valid.size() ) )
437 const auto& V = geomodel.
mesh.vertices.vertex( i );
438 point_mesh.vertices.create_vertex( V.data() );
441 save_mesh_locating_geomodel_inconsistencies( point_mesh, file );
444 template < index_t DIMENSION >
445 std::map< MeshEntityType, std::vector< index_t > > get_entities(
448 std::map< MeshEntityType, std::vector< index_t > > entities;
450 .mesh_entity_manager.mesh_entity_types();
451 for(
const auto& type : types )
456 const auto& bmes = geomodel.
mesh.vertices.gme_vertices( i );
457 for(
const auto& vertex : bmes )
459 const auto& T = vertex.gmme.type();
460 index_t
id{ vertex.gmme.index() };
461 entities[T].push_back(
id );
467 const std::vector< index_t >& entities,
const std::string& entity_name )
469 std::ostringstream oss;
470 oss <<
" Vertex is in " << entities.size() <<
" " << entity_name
472 for(
auto entity : entities )
474 oss << entity <<
" ; ";
479 template <
template < index_t >
class ENTITY, index_t DIMENSION >
481 const std::map<
MeshEntityType, std::vector< index_t > >& entities )
483 auto type = ENTITY< DIMENSION >::type_name_static();
486 .mesh_entity_manager.boundary_entity_type( type );
487 const auto& type_entities = entities.find( type )->second;
488 if( entities.find( boundary_type )->second.empty() )
490 if( !type_entities.empty() )
492 if( type_entities.size() != 1 )
494 print_error( type_entities, type.string() +
"s" );
503 if( type_entities.empty() )
505 Logger::warn(
"GeoModel",
" Vertex is in a ", boundary_type,
506 " but in no ", type );
509 const auto& boundary_entities = entities.find( boundary_type )->second;
511 for(
auto entity : type_entities )
513 auto nb = std::count(
514 type_entities.begin(), type_entities.end(), entity );
517 Logger::warn(
"GeoModel",
" Vertex is ", nb,
" times in ",
525 bool internal_boundary{
false };
526 for(
auto line : boundary_entities )
532 internal_boundary =
true;
536 if( !internal_boundary )
548 template < index_t DIMENSION >
550 const std::map<
MeshEntityType, std::vector< index_t > >& entities )
552 if( geomodel.nb_regions() > 0 && geomodel.region( 0 ).is_meshed() )
554 return is_vertex_valid< Region >( geomodel, entities );
559 template < index_t DIMENSION >
561 const std::map<
MeshEntityType, std::vector< index_t > >& entities )
563 return is_vertex_valid< Surface >( geomodel, entities );
566 template < index_t DIMENSION >
568 const std::map<
MeshEntityType, std::vector< index_t > >& entities );
571 bool is_line_vertex_valid(
const GeoModel3D& geomodel,
572 const std::map<
MeshEntityType, std::vector< index_t > >& entities )
574 const auto& lines = entities.find( Line3D::type_name_static() )->second;
575 if( entities.find( Corner3D::type_name_static() )->second.empty() )
579 if( lines.size() != 1 )
581 print_error( lines,
"Lines" );
582 Logger::warn(
"GeoModel",
"It should be in only one Line" );
589 if( lines.size() < 2 )
591 print_error( lines,
"Line" );
592 Logger::warn(
"GeoModel",
"It should be in at least 2 Lines" );
595 for(
const auto line : lines )
597 auto nb = std::count( lines.begin(), lines.end(), line );
600 if( !geomodel.line( line ).is_closed() )
603 " is twice in Line ",
611 " times in Line ", line );
616 gmme_id corner_id( Corner3D::type_name_static(),
617 entities.find( Corner3D::type_name_static() )->second.front() );
618 for(
const auto line : lines )
620 gmme_id line_id( Line3D::type_name_static(), line );
621 if( !is_boundary_entity( geomodel, line_id, corner_id ) )
624 " Inconsistent Line-Corner connectivity ",
625 " vertex shows that ", line_id,
626 " must be in the boundary of ", corner_id );
634 bool is_line_vertex_valid(
const GeoModel2D& geomodel,
635 const std::map<
MeshEntityType, std::vector< index_t > >& entities )
637 const auto& lines = entities.find( Line2D::type_name_static() )->second;
638 if( entities.find( Corner2D::type_name_static() )->second.empty() )
642 if( lines.size() != 1 )
644 print_error( lines,
"Lines" );
645 Logger::warn(
"GeoModel",
"It should be in only one Line" );
654 print_error( lines,
"Lines" );
655 Logger::warn(
"GeoModel",
"It should be in at least one Line" );
658 for(
auto line : lines )
660 auto nb = std::count( lines.begin(), lines.end(), line );
663 if( !geomodel.line( line ).is_closed() )
666 " is twice in Line ",
674 " times in Line ", line );
679 gmme_id corner_id( Corner2D::type_name_static(),
680 entities.find( Corner2D::type_name_static() )->second.front() );
681 for(
auto line : lines )
683 gmme_id line_id( Line2D::type_name_static(), line );
684 if( !is_boundary_entity( geomodel, line_id, corner_id ) )
687 " Inconsistent Line-Corner connectivity ",
688 " vertex shows that ", line_id,
689 " must be in the boundary of ", corner_id );
696 template < index_t DIMENSION >
697 bool is_corner_valid(
698 const std::map<
MeshEntityType, std::vector< index_t > >& entities )
700 const auto& corners =
702 if( corners.size() > 1 )
704 print_error( corners,
"Corners" );
705 Logger::warn(
"GeoModel",
"It should be in only one Corner" );
711 template < index_t DIMENSION >
715 if( !is_corner_valid< DIMENSION >( entities ) )
719 if( !is_line_vertex_valid< DIMENSION >( geomodel, entities ) )
723 if( !is_surface_vertex_valid< DIMENSION >( geomodel, entities ) )
731 template < index_t DIMENSION >
732 bool is_geomodel_vertex_valid(
736 bool is_geomodel_vertex_valid(
const GeoModel3D& geomodel, index_t i )
739 std::map< MeshEntityType, std::vector< index_t > > entities =
740 get_entities( geomodel, i );
742 if( !is_geomodel_vertex_valid_base( geomodel, entities ) )
746 if( !is_region_vertex_valid< 3 >( geomodel, entities ) )
755 bool is_geomodel_vertex_valid(
const GeoModel2D& geomodel, index_t i )
758 std::map< MeshEntityType, std::vector< index_t > > entities =
759 get_entities( geomodel, i );
761 return is_geomodel_vertex_valid_base( geomodel, entities );
775 template < index_t DIMENSION >
781 std::vector< bool > valid( geomodel.
mesh.vertices.nb(), true );
782 for(
auto i :
range( geomodel.
mesh.vertices.nb() ) )
784 valid[i] = is_geomodel_vertex_valid( geomodel, i );
787 Logger::warn(
"GeoModel",
" Vertex ", i,
" is not valid" );
790 auto nb_invalid = std::count( valid.begin(), valid.end(), false );
794 std::ostringstream file;
796 <<
"/invalid_global_vertices.geogram";
797 save_invalid_points( file, geomodel, valid );
799 if( GEO::CmdLine::get_arg_bool(
"validity_save" ) )
801 Logger::warn(
"GeoModel", nb_invalid,
" invalid vertices" );
802 Logger::warn(
"GeoModel",
"Saved in file: ", file.str() );
810 template < index_t DIMENSION >
811 void save_edges(
const std::ostringstream& file,
813 const std::vector< index_t >& e )
816 index_t previous_vertex_id{ NO_ID };
817 for(
auto i :
range( e.size() ) )
819 index_t cur_vertex_id{ edge_mesh.vertices.create_vertex(
820 geomodel.
mesh.vertices.vertex( e[i] ).data() ) };
824 previous_vertex_id = cur_vertex_id;
828 edge_mesh.edges.create_edge( previous_vertex_id, cur_vertex_id );
829 previous_vertex_id = NO_ID;
831 save_mesh_locating_geomodel_inconsistencies( edge_mesh, file );
834 template < index_t DIMENSION >
835 void save_polygons(
const std::string& file,
837 const std::vector< index_t >& polygons )
840 for(
auto cur_polygon : polygons )
844 GEO::vector< index_t > vertices;
845 vertices.reserve( nb_vertices_in_polygon );
846 for(
auto v :
range( nb_vertices_in_polygon ) )
848 index_t new_vertex{ mesh.vertices.create_vertex(
851 vertices.push_back( new_vertex );
853 mesh.facets.create_polygon( vertices );
855 GEO::mesh_save( mesh, file );
864 template < index_t DIMENSION >
867 const auto& geomodel_vertices = surface.
geomodel().mesh.vertices;
868 std::vector< index_t > invalid_corners;
869 auto S_id = surface.
gmme();
875 && !is_edge_on_line( surface.
geomodel(),
876 geomodel_vertices.geomodel_vertex_id(
878 geomodel_vertices.geomodel_vertex_id(
879 S_id, surface.
mesh().next_polygon_vertex(
882 invalid_corners.push_back(
883 geomodel_vertices.geomodel_vertex_id(
885 invalid_corners.push_back(
886 geomodel_vertices.geomodel_vertex_id( S_id,
887 surface.
mesh().next_polygon_vertex( { p, v } ) ) );
891 if( !invalid_corners.empty() )
893 std::ostringstream file;
895 <<
"/invalid_boundary_surface_" << surface.
index()
897 save_edges( file, surface.
geomodel(), invalid_corners );
899 if( GEO::CmdLine::get_arg_bool(
"validity_save" ) )
901 Logger::warn(
"GeoModel",
" Invalid surface boundary: ",
902 invalid_corners.size() / 2,
" boundary edges of ", S_id,
903 " are in no line of the geomodel " );
904 Logger::warn(
"GeoModel",
" Saved in file: ", file.str() );
914 template < index_t DIMENSION >
916 const std::vector< index_t >& edge_indices,
917 const std::vector< index_t >& non_manifold_edges )
921 index_t nb_edges{
static_cast< index_t
>( non_manifold_edges.size() ) };
922 builder.create_vertices( 2 * nb_edges );
923 builder.create_edges( nb_edges );
924 const auto& vertices = geomodel.
mesh.vertices;
925 for(
auto e :
range( non_manifold_edges.size() ) )
927 index_t edge_id{ non_manifold_edges[e] };
928 const auto& v0 = vertices.vertex( edge_indices[edge_id] );
929 const auto& v1 = vertices.vertex( edge_indices[edge_id + 1] );
930 builder.set_vertex( 2 * e, v0 );
931 builder.set_vertex( 2 * e + 1, v1 );
939 template < index_t DIMENSION >
943 std::vector< index_t > unconformal_polygons;
948 center, surface.
geomodel().epsilon() );
951 unconformal_polygons.push_back( p );
954 if( !unconformal_polygons.empty() )
956 std::ostringstream file;
958 << surface.
index() <<
".geogram";
959 save_polygons( file.str(), surface, unconformal_polygons );
961 if( GEO::CmdLine::get_arg_bool(
"validity_save" ) )
964 unconformal_polygons.size(),
" polygons of ",
966 " are unconformal with the geomodel cells " );
967 Logger::warn(
"GeoModel",
" Saved in file: ", file.str() );
975 template < index_t DIMENSION >
976 std::vector< index_t > compute_border_edges(
979 std::vector< index_t > edge_indices;
980 const auto& polygons = geomodel.
mesh.polygons;
981 for(
const auto& surface : geomodel.
surfaces() )
983 for(
auto p :
range( polygons.nb_polygons( surface.
index() ) ) )
985 index_t polygon_id{ polygons.polygon( surface.
index(), p ) };
986 for(
auto v :
range( polygons.nb_vertices( polygon_id ) ) )
988 index_t adj{ polygons.adjacent( { polygon_id, v } ) };
991 edge_indices.push_back(
992 polygons.vertex( { polygon_id, v } ) );
993 index_t next_v{ ( v + 1 )
994 % polygons.nb_vertices( polygon_id ) };
995 edge_indices.push_back(
996 polygons.vertex( { polygon_id, next_v } ) );
1001 return edge_indices;
1004 template < index_t DIMENSION >
1005 std::vector< vecn< DIMENSION > > compute_border_edge_barycenters(
1007 const std::vector< index_t >& edge_indices )
1009 const auto& vertices = geomodel.
mesh.vertices;
1010 std::vector< vecn< DIMENSION > > edge_barycenters;
1011 edge_barycenters.reserve( edge_indices.size() / 2 );
1012 for( index_t e = 0; e < edge_indices.size(); e += 2 )
1014 const auto& v0 = vertices.vertex( edge_indices[e] );
1015 const auto& v1 = vertices.vertex( edge_indices[e + 1] );
1016 edge_barycenters.push_back( ( v0 + v1 ) * 0.5 );
1018 return edge_barycenters;
1021 template < index_t DIMENSION >
1022 std::vector< bool > are_border_edges_on_line(
1026 const auto& line_abbb = geomodel.
mesh.edges.aabb();
1027 std::vector< bool > border_edges_on_line( barycenters.size(), true );
1028 double distance{ 0 };
1030 for(
auto border_edge :
range( barycenters.size() ) )
1032 const auto& barycenter = barycenters[border_edge];
1033 std::tie( std::ignore, std::ignore, distance ) =
1034 line_abbb.closest_edge( barycenter );
1035 if( distance > geomodel.
epsilon() )
1037 border_edges_on_line[border_edge] =
false;
1040 return border_edges_on_line;
1043 std::vector< index_t > compute_non_manifold_edges(
1044 const std::vector< bool >& edge_on_lines )
1046 std::vector< index_t > non_manifold_edges;
1047 for(
auto e :
range( edge_on_lines.size() ) )
1049 if( !edge_on_lines[e] )
1051 non_manifold_edges.push_back( e );
1054 return non_manifold_edges;
1060 template < index_t DIMENSION >
1061 class GeoModelValidityCheck
1066 : geomodel_( geomodel ),
1068 mode_( validity_check_mode )
1072 geomodel_.mesh.vertices.test_and_initialize();
1073 geomodel_.mesh.polygons.test_and_initialize();
1082 do_check_validity();
1087 void add_base_checks( std::vector< std::future< void > >& tasks )
1092 tasks.emplace_back( std::async( std::launch::async,
1093 &GeoModelValidityCheck::test_geomodel_connectivity_validity,
1098 tasks.emplace_back( std::async( std::launch::async,
1099 &GeoModelValidityCheck::test_geomodel_geological_validity,
1105 tasks.emplace_back( std::async( std::launch::async,
1106 &GeoModelValidityCheck::test_surface_line_mesh_conformity,
1111 tasks.emplace_back( std::async( std::launch::async,
1112 &GeoModelValidityCheck::
1113 test_geomodel_mesh_entities_validity,
1122 void add_checks( std::vector< std::future< void > >& tasks )
1124 add_base_checks( tasks );
1127 void do_check_validity()
1129 std::vector< std::future< void > > tasks;
1131 add_checks( tasks );
1133 for(
auto& task : tasks )
1142 void test_geomodel_mesh_entities_validity()
1146 set_invalid_model();
1153 void test_geomodel_connectivity_validity()
1157 set_invalid_model();
1161 set_invalid_model();
1168 void test_geomodel_geological_validity()
1172 set_invalid_model();
1176 set_invalid_model();
1185 void test_surface_line_mesh_conformity()
1189 if( !check_model_points_validity( geomodel_ ) )
1191 set_invalid_model();
1194 for(
const auto& surface : geomodel_.surfaces() )
1196 if( !surface_boundary_valid( surface ) )
1198 set_invalid_model();
1203 void test_region_surface_mesh_conformity()
1205 if( geomodel_.mesh.cells.nb() > 0 )
1209 const auto& nn_search =
1210 geomodel_.mesh.cells.cell_facet_nn_search();
1211 for(
const auto& surface : geomodel_.surfaces() )
1213 if( !is_surface_conformal_to_volume( surface, nn_search ) )
1215 set_invalid_model();
1221 void test_non_free_line_at_two_interfaces_intersection()
1223 if( !geomodel_.entity_type_manager()
1224 .geological_entity_manager.is_valid_type(
1229 for(
const auto& line : geomodel_.lines() )
1236 const index_t first_interface_id{
1243 bool at_least_two_different_interfaces{
false };
1244 for(
auto in_boundary_i :
1247 const index_t cur_interface_id{
1254 if( cur_interface_id != first_interface_id )
1256 at_least_two_different_interfaces =
true;
1261 if( !at_least_two_different_interfaces )
1264 "All in boundaries (surfaces) of line ", line.
index(),
1265 " are children of a same interface." );
1266 set_invalid_model();
1277 void test_non_manifold_edges()
1279 auto edge_indices = compute_border_edges( geomodel_ );
1281 compute_border_edge_barycenters( geomodel_, edge_indices );
1282 auto border_edges_on_line =
1283 are_border_edges_on_line( geomodel_, barycenters );
1284 auto non_manifold_edges =
1285 compute_non_manifold_edges( border_edges_on_line );
1287 if( !non_manifold_edges.empty() )
1290 " non-manifold edges " );
1291 debug_save_non_manifold_edges(
1292 geomodel_, edge_indices, non_manifold_edges );
1294 set_invalid_model();
1303 void test_polygon_intersections()
1305 if( geomodel_.mesh.polygons.nb()
1306 == geomodel_.mesh.polygons.nb_triangle()
1307 + geomodel_.mesh.polygons.nb_quad() )
1309 std::vector< bool > has_intersection;
1310 StoreIntersections< DIMENSION > action(
1311 geomodel_, has_intersection );
1312 const auto& AABB = geomodel_.mesh.polygons.aabb();
1313 AABB.compute_self_element_bbox_intersections( action );
1315 auto nb_intersections = std::count(
1316 has_intersection.begin(), has_intersection.end(), 1 );
1318 if( nb_intersections > 0 )
1321 for(
auto p :
range( has_intersection.size() ) )
1323 if( !has_intersection[p] )
1327 GEO::vector< index_t > vertices;
1329 geomodel_.mesh.polygons.nb_vertices( p ) );
1331 range( geomodel_.mesh.polygons.nb_vertices( p ) ) )
1333 index_t
id = mesh.vertices.create_vertex(
1334 geomodel_.mesh.vertices
1335 .vertex( geomodel_.mesh.polygons.vertex(
1338 vertices.push_back(
id );
1340 mesh.facets.create_polygon( vertices );
1342 std::ostringstream file;
1344 <<
"/intersected_polygons.geogram";
1345 save_mesh_locating_geomodel_inconsistencies( mesh, file );
1348 " polygon intersections " );
1349 set_invalid_model();
1355 "Polygonal intersection check not implemented yet" );
1359 void set_invalid_model()
1370 void GeoModelValidityCheck< 3 >::add_checks(
1371 std::vector< std::future< void > >& tasks )
1375 tasks.push_back( std::async( std::launch::async,
1376 &GeoModelValidityCheck::test_polygon_intersections,
this ) );
1381 tasks.emplace_back( std::async( std::launch::async,
1382 &GeoModelValidityCheck::test_region_surface_mesh_conformity,
1387 tasks.emplace_back( std::async( std::launch::async,
1388 &GeoModelValidityCheck::test_non_manifold_edges,
this ) );
1390 add_base_checks( tasks );
1400 std::string copy( directory );
1401 if( *copy.rbegin() ==
'/' || *copy.rbegin() ==
'\\' )
1403 copy.erase( copy.end() - 1 );
1405 if( GEO::FileSystem::is_directory( copy ) )
1407 GEO::CmdLine::set_arg(
"validity_directory", copy +
'/' );
1413 return GEO::CmdLine::get_arg(
"validity_directory" );
1416 template < index_t DIMENSION >
1421 .mesh_entity_manager.mesh_entity_types();
1422 index_t count_invalid{ 0 };
1423 for(
const auto& type : meshed_types )
1426 for(
auto i :
range( nb_entities ) )
1428 const auto& entity = geomodel.
mesh_entity( type, i );
1429 if( !entity.is_valid() )
1435 if( count_invalid != 0 )
1438 " mesh entities of the geomodel have an invalid mesh." );
1440 return count_invalid == 0;
1443 template < index_t DIMENSION >
1448 .mesh_entity_manager.mesh_entity_types();
1449 index_t count_invalid{ 0 };
1450 for(
const auto& type : meshed_types )
1453 for(
auto i :
range( nb_entities ) )
1455 const auto& entity = geomodel.
mesh_entity( type, i );
1456 if( !entity.is_connectivity_valid() )
1462 if( count_invalid != 0 )
1464 Logger::warn(
"GeoModel", count_invalid,
" mesh entities of the " 1465 "geomodel have an invalid " 1468 return count_invalid == 0;
1471 template < index_t DIMENSION >
1475 const auto& geological_types =
1477 .geological_entity_manager.geological_entity_types();
1478 index_t count_invalid{ 0 };
1479 for(
const auto& type : geological_types )
1483 if( !geol_entity.is_valid() )
1489 if( count_invalid != 0 )
1492 " geological entities of the geomodel are invalid " );
1494 return count_invalid == 0;
1497 template < index_t DIMENSION >
1502 .mesh_entity_manager.mesh_entity_types();
1503 index_t count_invalid{ 0 };
1504 for(
const auto& type : meshed_types )
1507 for(
auto i :
range( nb_entities ) )
1509 const auto& geol_entity = geomodel.
mesh_entity( type, i );
1510 if( !geol_entity.is_parent_connectivity_valid() )
1516 if( count_invalid != 0 )
1519 " mesh entities of the geomodel have an invalid ",
1520 "parent connectivity (geological relationships)." );
1522 return count_invalid == 0;
1525 template < index_t DIMENSION >
1529 if( !GEO::CmdLine::get_arg_bool(
"in:intersection_check" ) )
1531 validity_check_mode =
1535 GeoModelValidityCheck< DIMENSION > validity_checker(
1536 geomodel, validity_check_mode );
1538 bool valid{ validity_checker.is_geomodel_valid() };
1547 "GeoModel",
"Model ", geomodel.
name(),
" is invalid " );
1548 if( !GEO::CmdLine::get_arg_bool(
"validity_save" ) )
1550 Logger::out(
"Info",
"To save geomodel invalidities in files ",
1551 "(.geogram) set \"validity_save\" to true in the command " 1564 template < index_t DIMENSION >
1569 const GeoModel2D& geomodel )
1571 const auto& geomodelmesh = geomodel.
mesh;
1572 const auto& geomodelmesh_vertices = geomodelmesh.vertices;
1574 std::vector< vec2 > all_points;
1575 all_points.reserve( geomodelmesh_vertices.nb() );
1576 for(
auto v_i :
range( geomodelmesh_vertices.nb() ) )
1578 all_points.push_back( geomodelmesh_vertices.vertex( v_i ) );
1581 auto line = LineMesh2D::create_mesh();
1583 auto builder = LineMeshBuilder2D::create_builder( *line );
1585 auto start = builder->create_vertices( geomodelmesh_vertices.nb() );
1588 for(
auto v_i :
range( geomodelmesh_vertices.nb() ) )
1590 builder->set_vertex( v_i, all_points[v_i] );
1593 const auto voi_lines = geomodel.voi_lines();
1594 const auto& geomodelmesh_edges = geomodelmesh.edges;
1595 for(
auto edge_i :
range( geomodelmesh_edges.nb() ) )
1597 const auto cur_line_id = geomodelmesh_edges.
line( edge_i );
1598 if(
contains( voi_lines.lines_, cur_line_id ) )
1600 auto v0 = geomodelmesh_edges.vertex( { edge_i, 0 } );
1601 auto v1 = geomodelmesh_edges.vertex( { edge_i, 1 } );
1602 builder->create_edge( v0, v1 );
1611 builder->repair( GEO::MESH_REPAIR_TOPOLOGY, global_epsilon );
1612 index_t nb_connected_components;
1613 std::tie( nb_connected_components, std::ignore ) =
1614 line->connected_components();
1616 return nb_connected_components == 1;
1621 const GeoModel3D& geomodel )
1623 const auto& geomodelmesh = geomodel.
mesh;
1624 const auto& geomodelmesh_vertices = geomodelmesh.vertices;
1626 std::vector< vec3 > all_points;
1627 all_points.reserve( geomodelmesh_vertices.nb() );
1628 for(
auto v_i :
range( geomodelmesh_vertices.nb() ) )
1630 all_points.push_back( geomodelmesh_vertices.vertex( v_i ) );
1633 auto surface = SurfaceMesh3D::create_mesh();
1635 auto builder = SurfaceMeshBuilder3D::create_builder( *surface );
1637 auto start = builder->create_vertices( geomodelmesh_vertices.nb() );
1640 for(
auto v_i :
range( geomodelmesh_vertices.nb() ) )
1642 builder->set_vertex( v_i, all_points[v_i] );
1645 const auto voi_surfaces = geomodel.voi_surfaces();
1646 const auto& geomodelmesh_polygons = geomodelmesh.polygons;
1647 for(
auto polygon_i :
range( geomodelmesh_polygons.nb() ) )
1649 const auto cur_surface_id =
1650 geomodelmesh_polygons.
surface( polygon_i );
1651 if(
contains( voi_surfaces.surfaces_, cur_surface_id ) )
1653 std::vector< index_t > polygon_vertices(
1654 geomodelmesh_polygons.nb_vertices( polygon_i ) );
1656 range( geomodelmesh_polygons.nb_vertices( polygon_i ) ) )
1658 polygon_vertices[v_i] =
1659 geomodelmesh_polygons.vertex( { polygon_i, v_i } );
1661 builder->create_polygon( polygon_vertices );
1665 for(
auto p :
range( surface->nb_polygons() ) )
1667 for(
auto v :
range( surface->nb_polygon_vertices( p ) ) )
1669 builder->set_polygon_adjacent( { p, v }, NO_ID );
1673 builder->connect_polygons();
1679 builder->repair( GEO::MESH_REPAIR_TOPOLOGY, global_epsilon );
1680 index_t nb_connected_components;
1681 std::tie( nb_connected_components, std::ignore ) =
1682 surface->connected_components();
1684 return nb_connected_components == 1;
1690 const GeoModel2D& );
1692 const GeoModel2D& );
1694 const GeoModel2D& );
1696 const GeoModel2D& );
1701 const GeoModel3D& );
1703 const GeoModel3D& );
1705 const GeoModel3D& );
1707 const GeoModel3D& );
index_t nb_incident_entities() const
const GeoModel< DIMENSION > & geomodel() const
template bool RINGMESH_API is_geomodel_valid< 2 >(const GeoModel2D &, ValidityCheckMode)
GeoModelMesh< DIMENSION > mesh
bool has_geomodel_finite_extension(const GeoModel< DIMENSION > &geomodel)
Check that the geomodel has a finite extension.
GEO::vecng< DIMENSION, double > vecn
std::string RINGMESH_API get_validity_errors_directory()
Get the directory where debugging information on invalid entities shall be stored.
virtual void save_mesh(const std::string &filename) const =0
bool is_geomodel_valid(const GeoModel< DIMENSION > &geomodel, ValidityCheckMode validity_check_mode=ValidityCheckMode::ALL)
Check global geomodel validity.
const EntityTypeManager< DIMENSION > & entity_type_manager() const
Gets the EntityTypeManager associated to the GeoModel.
index_t nb_mesh_element_vertices(index_t polygon_index) const final
virtual const GeoModelMeshEntity< DIMENSION > & mesh_entity(const gmme_id &id) const
Generic access to a meshed entity.
void ringmesh_unused(const T &)
index_t adjacent(const PolygonLocalEdge &polygon_local_edge) const
bool RINGMESH_API has_geomodel_finite_extension< 3 >(const GeoModel3D &geomodel)
A GeoModelEntity of type CORNER.
static void warn(const std::string &feature, const Args &... args)
bool is_closed() const
A Line is closed if its two extremities are identitical.
bool are_geomodel_mesh_entities_connectivity_valid(const GeoModel< DIMENSION > &geomodel)
Check the connectivity of mesh entities.
const std::string & name() const
Gets the name of the GeoModel.
const SurfaceMesh< DIMENSION > & mesh() const
Get the low level mesh data structure.
vecn< DIMENSION > mesh_element_barycenter(index_t polygon_index) const final
static MeshEntityType type_name_static()
geol_entity_range< DIMENSION > geol_entities(const GeologicalEntityType &geol_type) const
bool RINGMESH_API has_geomodel_finite_extension< 2 >(const GeoModel2D &geomodel)
bool are_geomodel_mesh_entities_parent_valid(const GeoModel< DIMENSION > &geomodel)
index_t nb_mesh_elements() const final
bool enum_contains(Enum lhs, Enum rhs)
template bool RINGMESH_API is_geomodel_valid< 3 >(const GeoModel3D &, ValidityCheckMode)
static void out(const std::string &feature, const Args &... args)
const Line< DIMENSION > & line(index_t index) const
const Surface< DIMENSION > & surface(index_t index) const
bool are_geomodel_mesh_entities_mesh_valid(const GeoModel< DIMENSION > &geomodel)
Check the validity of all individual entity meshes.
const Surface< DIMENSION > & incident_entity(index_t x) const
ValidityCheckMode
Option to select what are checked.
bool are_geomodel_geological_entities_valid(const GeoModel< DIMENSION > &geomodel)
index_t vertex(const ElementLocalVertex &polygon_local_vertex) const
#define ringmesh_assert(x)
bool contains(const container &in, const T &value)
The MeshEntityType described the type of the meshed entities There are 4 MeshEntityTypes correspondin...
void RINGMESH_API set_validity_errors_directory(const std::string &directory)
Set the directory where debugging information on invalid entities shall be stored.
Classes to build GeoModel from various inputs.
index_t polygon_adjacent_index(const PolygonLocalEdge &polygon_local_edge) const
Gets the polygon adjacent along an edge of a polygon.
surface_range< DIMENSION > surfaces() const
A GeoModelEntity of type LINE.
const vecn< DIMENSION > & mesh_element_vertex(const ElementLocalVertex &element_local_vertex) const
Coordinates of a vertex of a mesh element.
std::vector< index_t > get_neighbors(const vecn< DIMENSION > &v, double threshold_distance) const
virtual index_t nb_mesh_entities(const MeshEntityType &type) const
Returns the number of mesh entities of the given type.
index_t nb_vertices(index_t polygon) const
This template is a specialization of a gme_id to the GeoModelMeshEntity.
index_t nb_vertices() const
#define ringmesh_assert_not_reached