@@ -371,12 +371,12 @@ void MeshWarper::split_cell_on_edge(int cell_id, int new_vertex, int v0, int v1,
371371
372372// ---------------------------------------------------------------------------
373373vtkSmartPointer<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// ---------------------------------------------------------------------------
613630void 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// ---------------------------------------------------------------------------
722731Eigen::MatrixXd MeshWarper::extract_landmarks (vtkSmartPointer<vtkPolyData> warped_mesh) {
723732 Eigen::MatrixXd landmarks;
0 commit comments