Repository navigation
fix(Manifold): robustness and performance of manifold, adjacency and negative elements inspections - #202
fix(Manifold): robustness and performance of manifold, adjacency and negative elements inspections#202BotellaA wants to merge 20 commits into
Conversation
The number of polygons around an edge was stored in a local_index_t: an edge shared by 257 polygons wrapped to 1 and was seen as a border edge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hing The adjacent polygon/polyhedron index was used without bounds check (OpenGeode only asserts it in debug): a stale adjacency read out of bounds and crashed in release. It is now reported as a wrong adjacency. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Surface edges whose vertices are not linked to unique vertices all became the model edge (NO_ID, NO_ID), shared by every such surface, and were reported as non manifold. They are skipped: unlinked vertices are reported by the topology inspection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…onger crash several_cmvs_on_one_vertex called component_mesh_vertices(NO_ID), only guarded by a debug assertion: unlinked vertices of a single edge internal Line (or single polygon internal Surface) read out of bounds in release. Unlinked vertices are skipped, they are reported by the topology inspection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ad of throwing mesh_edges_are_manifold let the polyhedra_around_edge exception escape on broken adjacencies, while non_manifold_edges already reports them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… cache Each vertex called SolidMesh::polyhedra_around_vertex, which allocates a hash set and a stack, fills the mesh lazy cache (racy when other inspections run concurrently) and throws on broken adjacencies. The same propagation through facet adjacencies is now done locally, without allocation for usual vertices, in parallel over vertices. It starts from the first polyhedron containing the vertex instead of the mesh stored one. Solid vertex manifold: cva/brep_from_topo blocks 0.37s -> 0.15s, corbi_tet_mesh 0.11s -> 0.03s (identical results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- the edge to polyhedra map stored a heap vector per edge, did two hash lookups per edge occurrence and was built in the constructor, paid by every SolidMeshInspector even if edges were never inspected (and stale if the mesh changed); unique edges are now listed once per query with inline storage - the checked_edges hash set is replaced by the unique edges order - polyhedra around each edge are propagated in parallel Solid edge manifold: cva/brep_from_topo blocks 2.31s -> 0.57s, corbi_tet_mesh 0.32s -> 0.08s (identical results, same issues order). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sh map The facet counts used an absl::linked_hash_map (a list node per facet) and up to two lookups per facet occurrence. A reserved flat_hash_map with a single try_emplace is used, non manifold facets are sorted back in order of first appearance. Solid facet manifold: cva/brep_from_topo blocks 1.41s -> 0.96s; whole BRep manifold inspection 3.8s -> 1.87s with the previous commits (identical results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When polygons_around_vertex threw (broken adjacencies, or over 1000 polygons around the vertex), the vertex was silently skipped and counted as manifold. It is now reported as a vertex which could not be checked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x cache Each vertex called SurfaceMesh::polygons_around_vertex, which fills the mesh lazy cache: this raced with the topology inspection reading the same cache (is_vertex_on_border) in BRep/Section inspections. The same walk around the vertex is now done locally, bounded by the number of polygons containing the vertex, checking adjacency indices, and in parallel over vertices. Surface vertex manifold: cva/brep_from_topo surfaces 0.040s -> 0.013s (identical results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ying Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The vertices, edges, facets and model edges/facets manifold inspections of a BRep (vertices and edges for a Section) ran one after the other. They fill different results and no longer write to the mesh caches, so they now run concurrently. inspect_brep_manifold: cva/brep_from_topo 1.87s -> 1.08s, corbi_tet_mesh 0.29s -> 0.16s, fimiston 0.68s -> 0.48s (identical results). Overall with the previous commits: cva/brep_from_topo 3.8s -> 1.08s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each surface edge made two model.unique_vertex() calls (uuid hash lookup) and the edges map was an absl::linked_hash_map with a std::vector per edge. Surface unique vertices are fetched once per vertex, the map is a flat_hash_map with inline storage and non manifold edges are sorted back in order of first appearance. inspect_brep_manifold: fimiston 0.48s -> 0.32s, model_D 0.023s -> 0.011s (identical results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For every surface border edge, detail::line_component_mesh_edges built hash maps of component mesh vertex pairs and of line edges only to test whether the result was empty, after two unique_vertex() hash lookups; exceptions were used for unlinked vertices with a warning logged per edge. The line edge is now looked up directly from the component mesh vertices, unique vertices are fetched once per surface vertex, unlinked vertices are checked explicitly (same issue message) and surfaces are inspected in parallel. inspect_brep_meshes_adjacencies: fimiston 0.098s -> 0.034s, model_D 0.009s -> 0.003s (identical results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A block border facet whose vertices were not linked to unique vertices was silently skipped, while the same case is reported for surface border edges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For every block border facet, polygon_unique_vertices made one unique_vertex hash lookup per vertex and detail::surface_component_mesh_polygons built hash maps and called SurfaceMesh::polygons_around_vertex, which fills the mesh lazy cache (racy with the other inspections running concurrently). The sorted unique vertices of every surface polygon are now gathered once, in parallel, and each border facet is a single set lookup; blocks are inspected in parallel. inspect_brep_meshes_adjacencies: corbi_tet_mesh 0.29s -> 0.07s, mss 0.077s -> 0.034s (identical results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For each interior facet, an OrientedVertexCycle was built for every facet of the adjacent polyhedron before testing whether that facet points back. The back-link and the number of vertices are now tested first, so usually a single cycle comparison is made. inspect_brep_meshes_adjacencies: cva/brep_from_topo 0.83s -> 0.58s, corbi_tet_mesh 0.07s -> 0.05s (identical results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
polyhedron_facets_with_wrong_adjacency checked every polyhedron serially. Polyhedra are now checked in parallel and issues added in the same order. inspect_brep_meshes_adjacencies: cva/brep_from_topo 0.58s -> 0.15s, mss 0.028s -> 0.008s (identical results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mesh.polygon() builds a std::vector of points per polygon; triangles now use the exact triangle_area_sign on their three points directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cpp-Linter Report
|
Summary
Robustness and performance fixes for the manifold, adjacency and negative elements inspections, from a review of OpenGeode-Inspector. There is one commit per fix, and each fix is covered by a test.
Robustness
local_index_t, so an edge shared by 257 polygons wrapped around and was seen as a manifold border edge.(NO_ID, NO_ID)and were reported as non-manifold.several_cmvs_on_one_vertex(NO_ID): it read out of bounds in release, crashing the BRep manifold inspection when an internal Line has a single edge whose vertices have no unique vertex.SolidMeshEdgeManifold::mesh_edges_are_manifold: it threw on broken adjacencies, where it should return false.Performance (identical results on all benchmark models)
polygons_around_vertex/polyhedra_around_vertexof OpenGeode. The same walk is done locally, without allocation and in parallel. This also removes a data race: those caches were filled while the topology inspection read them concurrently.flat_hash_mapinstead of alinked_hash_map.flat_hash_mapwith inline storage. Issue order is preserved.polygons_around_vertexcache use;Polygon(a heap allocation each).inspect_brep_manifoldinspect_brep_manifoldinspect_brep_manifoldinspect_brep_meshes_adjacenciesinspect_brep_meshes_adjacenciesinspect_brep_meshes_adjacenciesThe large models come from the Geode-Explicit, Geode-Background and Geode-Conversion test data.
Not changed
polyhedron_volumealready returns 0 for it.flat_hash_map: measured with no gain, so it was not kept.Related
🤖 Generated with Claude Code