From f354581a18b6a61c714299d14ed8710741d8057f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 15:50:46 +0000 Subject: [PATCH 1/2] Apply jafranc review comments on PR#251 (euler surface) - Add SetRegionIdAssignmentMode(CELL_COUNT_DESCENDING) after SetExtractionModeToAllRegions() in __countConnectedComponents so regions are ordered largest-first (suggestion on line 154). - Add comment that GetConnectivityArray/GetOffsetsArray require VTK 9+ (comment on line 233). - Store non-manifold edge point-id pairs in SurfaceComponent and display them (up to 10) in the results output so users know exactly where non-manifold edges are located (question on line 249). https://claude.ai/code/session_01GwpNNFQRPhb26XMCFHJtts --- mesh-doctor/src/geos/mesh_doctor/actions/euler.py | 9 +++++++-- mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/euler.py b/mesh-doctor/src/geos/mesh_doctor/actions/euler.py index b72504ab..92d5a129 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/euler.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/euler.py @@ -52,6 +52,7 @@ class SurfaceComponent: numBoundaryEdges: int numNonManifoldEdges: int interpretation: str + nonManifoldEdgeEndpoints: tuple[ tuple[ int, int ], ...] = field( default_factory=tuple ) @dataclass( frozen=True ) @@ -152,6 +153,7 @@ def __countConnectedComponents( mesh: vtk.vtkUnstructuredGrid ) -> int: cf = vtk.vtkConnectivityFilter() cf.SetInputData( mesh ) cf.SetExtractionModeToAllRegions() + cf.SetRegionIdAssignmentMode( vtk.vtkConnectivityFilter.CELL_COUNT_DESCENDING ) cf.ColorRegionsOn() cf.Update() return cf.GetNumberOfExtractedRegions() @@ -229,6 +231,7 @@ def __surfaceComponentsFromColored( colored: vtk.vtkUnstructuredGrid ) -> list[ """Compute (V, E, F, chi, boundary, non-manifold) per RegionId of a colored 2D mesh.""" rid = vtk_to_numpy( colored.GetCellData().GetArray( "RegionId" ) ).astype( np.int64 ) cells = colored.GetCells() + # GetConnectivityArray / GetOffsetsArray require VTK 9+ conn = vtk_to_numpy( cells.GetConnectivityArray() ).astype( np.int64, copy=False ) off = vtk_to_numpy( cells.GetOffsetsArray() ).astype( np.int64, copy=False ) @@ -251,7 +254,8 @@ def __surfaceComponentsFromColored( colored: vtk.vtkUnstructuredGrid ) -> list[ E = len( edgeCount ) F = int( len( sel ) ) bE = sum( 1 for c in edgeCount.values() if c == 1 ) - nm = sum( 1 for c in edgeCount.values() if c > 2 ) + nmEdges = tuple( ek for ek, c in edgeCount.items() if c > 2 ) + nm = len( nmEdges ) chi = V - E + F components.append( SurfaceComponent( componentId=int( regionId ), @@ -262,7 +266,8 @@ def __surfaceComponentsFromColored( colored: vtk.vtkUnstructuredGrid ) -> list[ eulerCharacteristic=chi, numBoundaryEdges=bE, numNonManifoldEdges=nm, - interpretation=__interpretSurface( chi, bE, nm ) ) ) + interpretation=__interpretSurface( chi, bE, nm ), + nonManifoldEdgeEndpoints=nmEdges ) ) return components diff --git a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py index 1ad7dcfa..ba51d96e 100644 --- a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py +++ b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py @@ -131,6 +131,13 @@ def __displaySurfaceGroup( g: SurfaceGroup ) -> None: setupLogger.results( " " + f"{c.componentId:>5} {c.numCells:>9,} {c.numVertices:>8,} {c.numEdges:>8,} " f"{c.numFaces:>8,} {c.eulerCharacteristic:>5} {c.numBoundaryEdges:>6,} " f"{c.numNonManifoldEdges:>4,} {c.interpretation}" ) + if c.nonManifoldEdgeEndpoints: + _MAX_DISPLAY = 10 + shown = c.nonManifoldEdgeEndpoints[ :_MAX_DISPLAY ] + setupLogger.results( f" non-manifold edge endpoints (point id pairs): " + + ", ".join( f"({a},{b})" for a, b in shown ) + + ( f" … (+{len(c.nonManifoldEdgeEndpoints)-_MAX_DISPLAY} more)" + if len( c.nonManifoldEdgeEndpoints ) > _MAX_DISPLAY else "" ) ) if len( g.components ) > 1: setupLogger.results( f" WARNING: {len(g.components)} components — verify isolated cells" ) From 50386f52587c993da88f67288e1f8e084e2c3e61 Mon Sep 17 00:00:00 2001 From: Jacques Franc <49998870+jafranc@users.noreply.github.com> Date: Fri, 29 May 2026 19:13:01 +0200 Subject: [PATCH 2/2] Update eulerParsing.py --- mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py index ba51d96e..7af5061e 100644 --- a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py +++ b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py @@ -134,7 +134,7 @@ def __displaySurfaceGroup( g: SurfaceGroup ) -> None: if c.nonManifoldEdgeEndpoints: _MAX_DISPLAY = 10 shown = c.nonManifoldEdgeEndpoints[ :_MAX_DISPLAY ] - setupLogger.results( f" non-manifold edge endpoints (point id pairs): " + setupLogger.results( " non-manifold edge endpoints (point id pairs): " + ", ".join( f"({a},{b})" for a, b in shown ) + ( f" … (+{len(c.nonManifoldEdgeEndpoints)-_MAX_DISPLAY} more)" if len( c.nonManifoldEdgeEndpoints ) > _MAX_DISPLAY else "" ) )