| 
23 | 23 | 
 
  | 
24 | 24 | #include <geode/inspector/criterion/manifold/brep_meshes_manifold.h>  | 
25 | 25 | 
 
  | 
 | 26 | +#include <geode/basic/algorithm.h>  | 
26 | 27 | #include <geode/basic/logger.h>  | 
27 | 28 | #include <geode/basic/pimpl_impl.h>  | 
28 | 29 | 
 
  | 
 | 30 | +#include <geode/mesh/core/detail/vertex_cycle.h>  | 
29 | 31 | #include <geode/mesh/core/solid_mesh.h>  | 
 | 32 | +#include <geode/mesh/core/surface_mesh.h>  | 
30 | 33 | 
 
  | 
31 | 34 | #include <geode/model/mixin/core/block.h>  | 
 | 35 | +#include <geode/model/mixin/core/surface.h>  | 
32 | 36 | #include <geode/model/representation/core/brep.h>  | 
33 | 37 | 
 
  | 
34 | 38 | #include <geode/inspector/criterion/manifold/solid_edge_manifold.h>  | 
@@ -74,6 +78,18 @@ namespace geode  | 
74 | 78 |                     non_manifold_components.push_back( block.id() );  | 
75 | 79 |                 }  | 
76 | 80 |             }  | 
 | 81 | +            const auto& model_edges = model_non_manifold_edges();  | 
 | 82 | +            if( !model_edges.empty() )  | 
 | 83 | +            {  | 
 | 84 | +                for( const auto& model_edge : model_edges )  | 
 | 85 | +                {  | 
 | 86 | +                    for( const auto& component_id : model_edge.second )  | 
 | 87 | +                    {  | 
 | 88 | +                        non_manifold_components.push_back( component_id );  | 
 | 89 | +                    }  | 
 | 90 | +                }  | 
 | 91 | +            }  | 
 | 92 | +            sort_unique( non_manifold_components );  | 
77 | 93 |             return non_manifold_components;  | 
78 | 94 |         }  | 
79 | 95 | 
 
  | 
@@ -193,6 +209,54 @@ namespace geode  | 
193 | 209 |             }  | 
194 | 210 |             return components_non_manifold_facets;  | 
195 | 211 |         }  | 
 | 212 | + | 
 | 213 | +        absl::flat_hash_map< std::array< index_t, 2 >, std::vector< uuid > >  | 
 | 214 | +            model_non_manifold_edges() const  | 
 | 215 | +        {  | 
 | 216 | +            using Edge = detail::VertexCycle< std::array< index_t, 2 > >;  | 
 | 217 | +            absl::flat_hash_map< Edge, std::vector< uuid > > edges;  | 
 | 218 | +            for( const auto& surface : model().surfaces() )  | 
 | 219 | +            {  | 
 | 220 | +                const auto& mesh = surface.mesh();  | 
 | 221 | +                for( const auto p : Range{ mesh.nb_polygons() } )  | 
 | 222 | +                {  | 
 | 223 | +                    const auto vertices = mesh.polygon_vertices( p );  | 
 | 224 | +                    for( const auto e : LIndices{ vertices } )  | 
 | 225 | +                    {  | 
 | 226 | +                        const auto adj = mesh.polygon_adjacent( { p, e } );  | 
 | 227 | +                        if( !adj || adj.value() < p )  | 
 | 228 | +                        {  | 
 | 229 | +                            continue;  | 
 | 230 | +                        }  | 
 | 231 | +                        const auto v0 = model().unique_vertex(  | 
 | 232 | +                            { surface.component_id(), vertices[e] } );  | 
 | 233 | +                        const auto v1 =  | 
 | 234 | +                            model().unique_vertex( { surface.component_id(),  | 
 | 235 | +                                vertices[e == vertices.size() - 1 ? 0  | 
 | 236 | +                                                                  : e + 1] } );  | 
 | 237 | +                        const auto info = edges.try_emplace(  | 
 | 238 | +                            std::array< index_t, 2 >{ v0, v1 },  | 
 | 239 | +                            std::vector< uuid >{ surface.id() } );  | 
 | 240 | +                        if( !info.second )  | 
 | 241 | +                        {  | 
 | 242 | +                            info.first->second.push_back( surface.id() );  | 
 | 243 | +                        }  | 
 | 244 | +                    }  | 
 | 245 | +                }  | 
 | 246 | +            }  | 
 | 247 | +            absl::flat_hash_map< std::array< index_t, 2 >, std::vector< uuid > >  | 
 | 248 | +                result;  | 
 | 249 | +            for( auto& edge : edges )  | 
 | 250 | +            {  | 
 | 251 | +                sort_unique( edge.second );  | 
 | 252 | +                if( edge.second.size() > 1 )  | 
 | 253 | +                {  | 
 | 254 | +                    result.emplace( std::move( edge.first.vertices() ),  | 
 | 255 | +                        std::move( edge.second ) );  | 
 | 256 | +                }  | 
 | 257 | +            }  | 
 | 258 | +            return result;  | 
 | 259 | +        }  | 
196 | 260 |     };  | 
197 | 261 | 
 
  | 
198 | 262 |     BRepComponentMeshesManifold::BRepComponentMeshesManifold(  | 
@@ -255,4 +319,10 @@ namespace geode  | 
255 | 319 |     {  | 
256 | 320 |         return impl_->component_meshes_non_manifold_facets();  | 
257 | 321 |     }  | 
 | 322 | + | 
 | 323 | +    absl::flat_hash_map< std::array< index_t, 2 >, std::vector< uuid > >  | 
 | 324 | +        BRepComponentMeshesManifold::model_non_manifold_edges() const  | 
 | 325 | +    {  | 
 | 326 | +        return impl_->model_non_manifold_edges();  | 
 | 327 | +    }  | 
258 | 328 | } // namespace geode  | 
0 commit comments