diff --git a/Libs/Mesh/Mesh.cpp b/Libs/Mesh/Mesh.cpp index 57de5c7877..68398645a4 100644 --- a/Libs/Mesh/Mesh.cpp +++ b/Libs/Mesh/Mesh.cpp @@ -490,7 +490,24 @@ Mesh& Mesh::remesh(int numVertices, double adaptivity) { numVertices = std::max(numVertices, 1); - remesh->SetForceManifold(true); + // ACVD's ForceManifold treats clusters on an open boundary as non-manifold and keeps splitting + // them, piling extra vertices and sliver triangles along the boundary (e.g. a mesh clipped by a + // cutting plane ends up with more vertices than requested). Only force manifold on closed meshes. + // geometry only: vtkFeatureEdges copies point data, which reads out of bounds on a field that + // doesn't have one value per point + auto geometry = vtkSmartPointer::New(); + geometry->SetPoints(this->poly_data_->GetPoints()); + geometry->SetPolys(this->poly_data_->GetPolys()); + auto boundary_edges = vtkSmartPointer::New(); + boundary_edges->SetInputData(geometry); + boundary_edges->BoundaryEdgesOn(); + boundary_edges->FeatureEdgesOff(); + boundary_edges->NonManifoldEdgesOff(); + boundary_edges->ManifoldEdgesOff(); + boundary_edges->Update(); + bool is_closed = boundary_edges->GetOutput()->GetNumberOfCells() == 0; + + remesh->SetForceManifold(is_closed); remesh->SetInput(surf); remesh->SetFileLoadSaveOption(0); remesh->SetConsoleOutput(0); diff --git a/Testing/MeshTests/MeshTests.cpp b/Testing/MeshTests/MeshTests.cpp index 2ab85547e5..0b3b8b95a5 100644 --- a/Testing/MeshTests/MeshTests.cpp +++ b/Testing/MeshTests/MeshTests.cpp @@ -359,6 +359,16 @@ TEST(MeshTests, remeshPercentTest2) { ASSERT_TRUE(femur == ground_truth); } +// Remeshing an open (clipped) mesh should hit the requested vertex count instead of piling extra +// vertices along the open boundary (#2672) +TEST(MeshTests, remeshPercentOpenMeshTest) { + Mesh femur(std::string(TEST_DATA_DIR) + "/femur.vtk"); + femur.clip(makePlane(Point3({-91.0, 0.0, 1230.0}), makeVector({0.0, 0.0, 1.0}))); + auto num_points = femur.numPoints(); + femur.remeshPercent(0.40, 1.0); + ASSERT_NEAR(femur.numPoints(), num_points * 0.40, num_points * 0.02); +} + TEST(MeshTests, fieldGradientFloatTest) { // fields arrive on meshes as floats as often as doubles, and the gradient has to be computed for // either: a missing gradient array used to be read as a null pointer diff --git a/Testing/data/remeshPercent1.vtk b/Testing/data/remeshPercent1.vtk index e93b2c5d2c..5a97443feb 100644 --- a/Testing/data/remeshPercent1.vtk +++ b/Testing/data/remeshPercent1.vtk @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:780e4de73c8b478a83d6a443be5358b600378618fb811b7be791a89e7887e5ca -size 231379 +oid sha256:8fdd1416339b792e7e6e4c1d9b95f31d19108a9c7e8e56bb6097f8be3fad0ebc +size 253904 diff --git a/Testing/data/remeshPercent2.vtk b/Testing/data/remeshPercent2.vtk index 6632855727..b055bdaf0c 100644 --- a/Testing/data/remeshPercent2.vtk +++ b/Testing/data/remeshPercent2.vtk @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f05141126a75d16e30c9281fdcc66d2dd6988fb9d4cede9b257681bdd285737 -size 95218 +oid sha256:4841cc2611eafbb6ec7445636b5f6725ef0d6b86a5208dabf372300b8fdf6780 +size 98795