Skip to content

Commit a8bc2bf

Browse files
committed
When igl::biharmonic fails, we retry again with remeshing.
1 parent 863a511 commit a8bc2bf

File tree

2 files changed

+75
-64
lines changed

2 files changed

+75
-64
lines changed

Libs/Mesh/MeshWarper.cpp

Lines changed: 73 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -371,12 +371,12 @@ void MeshWarper::split_cell_on_edge(int cell_id, int new_vertex, int v0, int v1,
371371

372372
//---------------------------------------------------------------------------
373373
vtkSmartPointer<vtkPolyData> MeshWarper::prep_mesh(vtkSmartPointer<vtkPolyData> mesh) {
374-
vtkSmartPointer<vtkTriangleFilter> triangle_filter = vtkSmartPointer<vtkTriangleFilter>::New();
374+
auto triangle_filter = vtkSmartPointer<vtkTriangleFilter>::New();
375375
triangle_filter->SetInputData(mesh);
376376
triangle_filter->PassLinesOff();
377377
triangle_filter->Update();
378378

379-
vtkSmartPointer<vtkPolyDataConnectivityFilter> connectivity = vtkSmartPointer<vtkPolyDataConnectivityFilter>::New();
379+
auto connectivity = vtkSmartPointer<vtkPolyDataConnectivityFilter>::New();
380380
connectivity->SetInputConnection(triangle_filter->GetOutputPort());
381381
connectivity->SetExtractionModeToLargestRegion();
382382
connectivity->Update();
@@ -388,7 +388,9 @@ vtkSmartPointer<vtkPolyData> MeshWarper::prep_mesh(vtkSmartPointer<vtkPolyData>
388388
// remove deleted triangles and clean up
389389
auto recreated = MeshWarper::recreate_mesh(fixed.getVTKMesh());
390390

391-
return MeshWarper::remove_zero_area_triangles(recreated);
391+
auto final = MeshWarper::remove_zero_area_triangles(recreated);
392+
393+
return final;
392394
}
393395

394396
//---------------------------------------------------------------------------
@@ -498,42 +500,57 @@ bool MeshWarper::generate_warp() {
498500
needs_warp_ = false;
499501
return true;
500502
}
501-
reference_mesh_ = MeshWarper::prep_mesh(incoming_reference_mesh_);
502503

503-
// prep points
504-
vertices_ = reference_particles_;
504+
const int MAX_ATTEMPTS = 2;
505505

506-
if (reference_mesh_->GetNumberOfPoints() == 0) {
507-
SW_ERROR("Unable to warp mesh, no points in surface mesh");
508-
update_progress(1.0);
509-
warp_available_ = false;
510-
return false;
511-
}
506+
for (int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
507+
// Prepare mesh (remesh on retry)
508+
if (attempt == 0) {
509+
reference_mesh_ = MeshWarper::prep_mesh(incoming_reference_mesh_);
510+
} else {
511+
SW_DEBUG("Initial igl::biharmonic failed, attempting again with remeshed reference mesh");
512+
Mesh mesh(reference_mesh_);
513+
mesh.remeshPercent(0.75);
514+
reference_mesh_ = MeshWarper::prep_mesh(mesh.getVTKMesh());
515+
}
516+
517+
// Prep points
518+
vertices_ = reference_particles_;
519+
if (reference_mesh_->GetNumberOfPoints() == 0) {
520+
SW_ERROR("Unable to warp mesh, no points in surface mesh");
521+
update_progress(1.0);
522+
warp_available_ = false;
523+
return false;
524+
}
512525

513-
add_particle_vertices(vertices_);
526+
add_particle_vertices(vertices_);
527+
if (landmarks_points_.size() > 0) {
528+
// to ensure that the landmark points sit on the ref mesh vertices
529+
add_particle_vertices(landmarks_points_);
530+
find_landmarks_vertices_on_ref_mesh();
531+
}
514532

515-
if (landmarks_points_.size() > 0) {
516-
// to ensure that the landmark points sit on the ref mesh vertices
517-
add_particle_vertices(landmarks_points_);
518-
find_landmarks_vertices_on_ref_mesh();
533+
find_good_particles();
534+
vertices_ = remove_bad_particles(vertices_);
535+
Mesh reference_mesh(reference_mesh_);
536+
Eigen::MatrixXd vertices = reference_mesh.points();
537+
faces_ = reference_mesh.faces();
538+
539+
// Perform warp
540+
if (MeshWarper::generate_warp_matrix(vertices, faces_, vertices_, warp_)) {
541+
// Success!
542+
update_progress(1.0);
543+
needs_warp_ = false;
544+
return true;
545+
}
519546
}
520547

521-
find_good_particles();
522-
vertices_ = remove_bad_particles(vertices_);
548+
SW_ERROR("Mesh Warp Error: igl:biharmonic_coordinates failed");
523549

524-
Mesh referenceMesh(reference_mesh_);
525-
Eigen::MatrixXd vertices = referenceMesh.points();
526-
faces_ = referenceMesh.faces();
527-
528-
// perform warp
529-
if (!MeshWarper::generate_warp_matrix(vertices, faces_, vertices_, warp_)) {
530-
update_progress(1.0);
531-
warp_available_ = false;
532-
return false;
533-
}
550+
// All attempts failed
534551
update_progress(1.0);
535-
needs_warp_ = false;
536-
return true;
552+
warp_available_ = false;
553+
return false;
537554
}
538555

539556
//---------------------------------------------------------------------------
@@ -554,7 +571,6 @@ bool MeshWarper::generate_warp_matrix(Eigen::MatrixXd target_vertices, Eigen::Ma
554571
// faster and looks OK
555572
const int k = 2;
556573
if (!igl::biharmonic_coordinates(target_vertices, target_faces, handle_sets, k, warp)) {
557-
SW_ERROR("Mesh Warp Error: igl:biharmonic_coordinates failed");
558574
diagnose_biharmonic_failure(target_vertices, target_faces, handle_sets, k);
559575
return false;
560576
}
@@ -609,23 +625,23 @@ vtkSmartPointer<vtkPolyData> MeshWarper::warp_mesh(const Eigen::MatrixXd& points
609625
return poly_data;
610626
}
611627

628+
//---------------------------------------------------------------------------
612629
//---------------------------------------------------------------------------
613630
void MeshWarper::diagnose_biharmonic_failure(const Eigen::MatrixXd& TV, const Eigen::MatrixXi& TF,
614631
const std::vector<std::vector<int>>& S, int k) {
615-
std::cerr << "\n=== Biharmonic Coordinates Failure Diagnostics ===\n";
616-
632+
SW_LOG_ONLY("=== Biharmonic Coordinates Failure Diagnostics ===");
617633
// 1. Check matrix dimensions and consistency
618-
std::cerr << "Vertices: " << TV.rows() << "x" << TV.cols() << std::endl;
619-
std::cerr << "Faces: " << TF.rows() << "x" << TF.cols() << std::endl;
620-
std::cerr << "Handles: " << S.size() << std::endl;
621-
std::cerr << "k parameter: " << k << std::endl;
634+
SW_LOG_ONLY("Vertices: {}x{}", TV.rows(), TV.cols());
635+
SW_LOG_ONLY("Faces: {}x{}", TF.rows(), TF.cols());
636+
SW_LOG_ONLY("Handles: {}", S.size());
637+
SW_LOG_ONLY("k parameter: {}", k);
622638

623639
// 2. Check handle indices validity
624640
for (size_t i = 0; i < S.size(); ++i) {
625641
for (int handle_idx : S[i]) {
626642
if (handle_idx < 0 || handle_idx >= TV.rows()) {
627-
std::cerr << "ERROR: Handle " << i << " contains invalid vertex index: " << handle_idx << " (valid range: 0-"
628-
<< (TV.rows() - 1) << ")\n";
643+
SW_LOG_ONLY("ERROR: Handle {} contains invalid vertex index: {} (valid range: 0-{})", i, handle_idx,
644+
TV.rows() - 1);
629645
}
630646
}
631647
}
@@ -634,67 +650,64 @@ void MeshWarper::diagnose_biharmonic_failure(const Eigen::MatrixXd& TV, const Ei
634650
int max_face_idx = TF.maxCoeff();
635651
int min_face_idx = TF.minCoeff();
636652
if (min_face_idx < 0 || max_face_idx >= TV.rows()) {
637-
std::cerr << "ERROR: Face indices out of range. Min: " << min_face_idx << ", Max: " << max_face_idx
638-
<< " (valid range: 0-" << (TV.rows() - 1) << ")\n";
653+
SW_LOG_ONLY("ERROR: Face indices out of range. Min: {}, Max: {} (valid range: 0-{})", min_face_idx, max_face_idx,
654+
TV.rows() - 1);
639655
}
640656

641657
// 4. Check mesh connectivity
642658
Eigen::VectorXi components;
643659
int num_components = igl::facet_components(TF, components);
644-
std::cerr << "Connected components: " << num_components << std::endl;
660+
SW_LOG_ONLY("Connected components: {}", num_components);
645661
if (num_components > 1) {
646-
std::cerr << "WARNING: Mesh has multiple disconnected components\n";
662+
SW_LOG_ONLY("WARNING: Mesh has multiple disconnected components");
647663
}
648664

649665
// 5. Check for boundary vertices (important for biharmonic coords)
650666
Eigen::VectorXi boundary;
651667
igl::boundary_loop(TF, boundary);
652-
std::cerr << "Boundary vertices: " << boundary.rows() << std::endl;
668+
SW_LOG_ONLY("Boundary vertices: {}", boundary.rows());
653669

654670
// 6. Check mesh genus/Euler characteristic
655671
int V = TV.rows();
656672
int F = TF.rows();
657673
Eigen::MatrixXi E;
658674
igl::edges(TF, E);
659675
int euler_char = V - E.rows() + F;
660-
std::cerr << "Euler characteristic: " << euler_char << " (genus ≈ " << (2 - euler_char) / 2 << ")\n";
676+
SW_LOG_ONLY("Euler characteristic: {} (genus ≈ {})", euler_char, (2 - euler_char) / 2);
661677

662678
// 7. Check for numerical issues
663679
double min_coord = TV.minCoeff();
664680
double max_coord = TV.maxCoeff();
665681
double coord_range = max_coord - min_coord;
666-
std::cerr << "Coordinate range: [" << min_coord << ", " << max_coord << "] (span: " << coord_range << ")\n";
667-
682+
SW_LOG_ONLY("Coordinate range: [{}, {}] (span: {})", min_coord, max_coord, coord_range);
668683
if (coord_range < 1e-10) {
669-
std::cerr << "ERROR: Coordinates too small - numerical precision issues likely\n";
684+
SW_LOG_ONLY("ERROR: Coordinates too small - numerical precision issues likely");
670685
}
671686
if (coord_range > 1e6) {
672-
std::cerr << "WARNING: Large coordinate range - consider normalizing mesh\n";
687+
SW_LOG_ONLY("WARNING: Large coordinate range - consider normalizing mesh");
673688
}
674689

675690
// 8. Check triangle quality
676691
Eigen::VectorXd areas;
677692
igl::doublearea(TV, TF, areas);
678693
areas *= 0.5;
679-
680694
double min_area = areas.minCoeff();
681695
double max_area = areas.maxCoeff();
682-
std::cerr << "Triangle areas - Min: " << min_area << ", Max: " << max_area;
683696
if (min_area > 0) {
684-
std::cerr << ", Ratio: " << (max_area / min_area);
697+
SW_LOG_ONLY("Triangle areas - Min: {}, Max: {}, Ratio: {}", min_area, max_area, max_area / min_area);
698+
} else {
699+
SW_LOG_ONLY("Triangle areas - Min: {}, Max: {}", min_area, max_area);
685700
}
686-
std::cerr << std::endl;
687701

688702
if (min_area < 1e-15) {
689-
std::cerr << "ERROR: Extremely small triangles detected - likely degenerate\n";
703+
SW_LOG_ONLY("ERROR: Extremely small triangles detected - likely degenerate");
690704
}
691705

692706
// 9. Test if we can build the Laplacian matrix (common failure point)
693707
Eigen::SparseMatrix<double> L;
694708
try {
695709
igl::cotmatrix(TV, TF, L);
696-
std::cerr << "Cotangent Laplacian: " << L.rows() << "x" << L.cols() << " (" << L.nonZeros() << " non-zeros)\n";
697-
710+
SW_LOG_ONLY("Cotangent Laplacian: {}x{} ({} non-zeros)", L.rows(), L.cols(), L.nonZeros());
698711
// Check for NaN or inf in Laplacian
699712
bool has_invalid = false;
700713
for (int k = 0; k < L.outerSize(); ++k) {
@@ -706,18 +719,14 @@ void MeshWarper::diagnose_biharmonic_failure(const Eigen::MatrixXd& TV, const Ei
706719
}
707720
if (has_invalid) break;
708721
}
709-
710722
if (has_invalid) {
711-
std::cerr << "ERROR: Laplacian matrix contains invalid values (NaN/inf)\n";
723+
SW_LOG_ONLY("ERROR: Laplacian matrix contains invalid values (NaN/inf)");
712724
}
713-
714725
} catch (const std::exception& e) {
715-
std::cerr << "ERROR: Failed to build Laplacian matrix: " << e.what() << std::endl;
726+
SW_LOG_ONLY("ERROR: Failed to build Laplacian matrix: {}", e.what());
716727
}
717-
718-
std::cerr << "=== End Diagnostics ===\n\n";
728+
SW_LOG_ONLY("=== End Diagnostics ===");
719729
}
720-
721730
//---------------------------------------------------------------------------
722731
Eigen::MatrixXd MeshWarper::extract_landmarks(vtkSmartPointer<vtkPolyData> warped_mesh) {
723732
Eigen::MatrixXd landmarks;

Libs/Mesh/MeshWarper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ class MeshWarper {
114114

115115
void diagnose_biharmonic_failure(const Eigen::MatrixXd& TV, const Eigen::MatrixXi& TF,
116116
const std::vector<std::vector<int>>& S, int k);
117+
118+
117119
// Members
118120
Eigen::MatrixXi faces_;
119121
Eigen::MatrixXd vertices_;

0 commit comments

Comments
 (0)