Skip to content

Commit 9f27f3d

Browse files
committed
Fix shared boundary donut: skip extract-largest-component for shared boundary domains, add empty mesh crash guards
1 parent 5fd591d commit 9f27f3d

6 files changed

Lines changed: 46 additions & 10 deletions

File tree

Libs/Groom/Groom.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,10 @@ bool Groom::mesh_pipeline(std::shared_ptr<Subject> subject, size_t domain) {
385385

386386
//---------------------------------------------------------------------------
387387
bool Groom::run_mesh_pipeline(Mesh& mesh, GroomParameters params, const std::string& filename) {
388-
// Repair mesh: triangulate, extract largest component, clean, fix non-manifold, remove zero-area triangles
389-
mesh = Mesh(MeshUtils::repair_mesh(mesh.getVTKMesh()));
388+
// Repair mesh: triangulate, clean, fix non-manifold, remove zero-area triangles
389+
// Skip extract-largest-component for shared boundary domains to avoid removing fragments at the cut surface
390+
bool extract_largest = !params.is_shared_boundary_domain();
391+
mesh = Mesh(MeshUtils::repair_mesh(mesh.getVTKMesh(), extract_largest));
390392

391393
if (params.get_fill_mesh_holes_tool()) {
392394
mesh.fillHoles();

Libs/Groom/GroomParameters.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,19 @@ bool GroomParameters::get_shared_boundaries_enabled() {
544544
//---------------------------------------------------------------------------
545545
void GroomParameters::set_shared_boundaries_enabled(bool enabled) { params_.set(Keys::SHARED_BOUNDARY, enabled); }
546546

547+
//---------------------------------------------------------------------------
548+
bool GroomParameters::is_shared_boundary_domain() {
549+
if (!get_shared_boundaries_enabled()) {
550+
return false;
551+
}
552+
for (const auto& boundary : get_shared_boundaries()) {
553+
if (boundary.first_domain == domain_name_ || boundary.second_domain == domain_name_) {
554+
return true;
555+
}
556+
}
557+
return false;
558+
}
559+
547560
//---------------------------------------------------------------------------
548561
std::vector<GroomParameters::SharedBoundary> GroomParameters::get_shared_boundaries() {
549562
std::vector<SharedBoundary> boundaries;

Libs/Groom/GroomParameters.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ class GroomParameters {
151151
bool get_shared_boundaries_enabled();
152152
void set_shared_boundaries_enabled(bool enabled);
153153

154+
//! Check if the current domain participates in any shared boundary
155+
bool is_shared_boundary_domain();
156+
154157
std::vector<SharedBoundary> get_shared_boundaries();
155158
void set_shared_boundaries(const std::vector<SharedBoundary>& boundaries);
156159
void add_shared_boundary(const std::string& first_domain, const std::string& second_domain, double tolerance);

Libs/Mesh/MeshUtils.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,12 @@ static std::tuple<Eigen::MatrixXd, Eigen::MatrixXi, Eigen::MatrixXd, Eigen::Matr
389389
Eigen::MatrixXi out_F;
390390
Eigen::MatrixXd rem_V;
391391
Eigen::MatrixXi rem_F;
392+
393+
// If either mesh is empty, there can be no shared surface
394+
if (is_empty(src_V, src_F) || is_empty(other_V, other_F)) {
395+
return std::make_tuple(out_V, out_F, src_V, src_F);
396+
}
397+
392398
igl::AABB<Eigen::MatrixXd, 3> tree;
393399
tree.init(other_V, other_F);
394400

@@ -482,6 +488,10 @@ std::array<Mesh, 3> MeshUtils::shared_boundary_extractor(const Mesh& mesh_l, con
482488
V_r = mesh_r.points();
483489
F_r = mesh_r.faces();
484490

491+
if (is_empty(V_l, F_l) || is_empty(V_r, F_r)) {
492+
throw std::runtime_error("Input mesh is empty. Cannot extract shared boundary from empty meshes");
493+
}
494+
485495
Eigen::MatrixXd shared_V_l, shared_V_r, rem_V_l, rem_V_r;
486496
Eigen::MatrixXi shared_F_l, shared_F_r, rem_F_l, rem_F_r;
487497
std::tie(shared_V_l, shared_F_l, rem_V_l, rem_F_l) = find_shared_surface(V_l, F_l, V_r, F_r, tol);
@@ -1073,18 +1083,23 @@ vtkSmartPointer<vtkPolyData> MeshUtils::recreate_mesh(vtkSmartPointer<vtkPolyDat
10731083
}
10741084

10751085
//---------------------------------------------------------------------------
1076-
vtkSmartPointer<vtkPolyData> MeshUtils::repair_mesh(vtkSmartPointer<vtkPolyData> mesh) {
1086+
vtkSmartPointer<vtkPolyData> MeshUtils::repair_mesh(vtkSmartPointer<vtkPolyData> mesh, bool extract_largest) {
10771087
auto triangle_filter = vtkSmartPointer<vtkTriangleFilter>::New();
10781088
triangle_filter->SetInputData(mesh);
10791089
triangle_filter->PassLinesOff();
10801090
triangle_filter->Update();
10811091

1082-
auto connectivity = vtkSmartPointer<vtkPolyDataConnectivityFilter>::New();
1083-
connectivity->SetInputConnection(triangle_filter->GetOutputPort());
1084-
connectivity->SetExtractionModeToLargestRegion();
1085-
connectivity->Update();
1092+
vtkSmartPointer<vtkPolyData> triangulated = triangle_filter->GetOutput();
1093+
1094+
if (extract_largest) {
1095+
auto connectivity = vtkSmartPointer<vtkPolyDataConnectivityFilter>::New();
1096+
connectivity->SetInputData(triangulated);
1097+
connectivity->SetExtractionModeToLargestRegion();
1098+
connectivity->Update();
1099+
triangulated = connectivity->GetOutput();
1100+
}
10861101

1087-
auto cleaned = MeshUtils::clean_mesh(connectivity->GetOutput());
1102+
auto cleaned = MeshUtils::clean_mesh(triangulated);
10881103

10891104
auto fixed = Mesh(cleaned).fixNonManifold();
10901105

Libs/Mesh/MeshUtils.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class MeshUtils {
8686
/// Recreate mesh, dropping deleted cells
8787
static vtkSmartPointer<vtkPolyData> recreate_mesh(vtkSmartPointer<vtkPolyData> mesh);
8888

89-
/// Repair mesh: triangulate, extract largest component, clean, fix non-manifold, remove zero-area triangles
90-
static vtkSmartPointer<vtkPolyData> repair_mesh(vtkSmartPointer<vtkPolyData> mesh);
89+
/// Repair mesh: triangulate, optionally extract largest component, clean, fix non-manifold, remove zero-area triangles
90+
static vtkSmartPointer<vtkPolyData> repair_mesh(vtkSmartPointer<vtkPolyData> mesh, bool extract_largest = true);
9191
};
9292
} // namespace shapeworks

Libs/Optimize/OptimizeParameters.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ std::vector<std::vector<itk::Point<double>>> OptimizeParameters::get_initial_poi
328328
for (auto s : subjects) {
329329
if (s->is_fixed()) {
330330
count++;
331+
if (d >= s->get_world_particle_filenames().size()) {
332+
throw std::runtime_error("Subject " + s->get_display_name() + " does not have enough world particle files");
333+
}
331334
// read the world points that are in the shared coordinate space
332335
auto filename = s->get_world_particle_filenames()[d];
333336
auto particles = read_particles_as_vector(filename);

0 commit comments

Comments
 (0)