diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bad251..702f977 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ option(AUTO_DOWNLOAD "Auto download dependencies" ON) # enable GPU for libmat option(LIBMAT_IS_USE_GPU "Enable GPU for RPD" ON) option(LIBMAT_IS_BUILD_TEST "Build tests, either CXX or CUDA" OFF) +option(MATSTRUCT_WITH_VISUALIZATION "Enable polyscope visualization (requires display)" OFF) include(matstruct_dependencies) add_subdirectory(extern/BGAL) add_subdirectory(src/knn) @@ -73,6 +74,9 @@ target_compile_options(${PROJECT_NAME} PRIVATE $<$: >) # target_compile_features(${PROJECT_NAME} PUBLIC cuda_std_17) target_compile_definitions(${PROJECT_NAME} PUBLIC -Dgeogram_EXPORTS) +if(MATSTRUCT_WITH_VISUALIZATION) + target_compile_definitions(${PROJECT_NAME} PUBLIC -DMATSTRUCT_WITH_VISUALIZATION) +endif() # add common_headers include target_include_directories(${PROJECT_NAME} PUBLIC @@ -100,7 +104,7 @@ target_link_libraries(${PROJECT_NAME} #### extern lib general cuda general cublas - optimized polyscope + $<$:polyscope> optimized geogram optimized ZLIB::ZLIB optimized CGAL::CGAL diff --git a/cmake/DownloadProject.cmake b/cmake/DownloadProject.cmake index e300f42..4833683 100644 --- a/cmake/DownloadProject.cmake +++ b/cmake/DownloadProject.cmake @@ -162,6 +162,7 @@ function(download_project) "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt") execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}" + -D "CMAKE_POLICY_VERSION_MINIMUM=3.5" . RESULT_VARIABLE result ${OUTPUT_QUIET} diff --git a/cmake/matstruct_dependencies.cmake b/cmake/matstruct_dependencies.cmake index f15a66c..76c9f04 100644 --- a/cmake/matstruct_dependencies.cmake +++ b/cmake/matstruct_dependencies.cmake @@ -51,10 +51,12 @@ if(NOT TARGET CLI11::CLI11) endif() # polyscope -if(NOT TARGET polyscope) - rpd_download_polyscope() - add_subdirectory(${EXTERNAL_DIR}/polyscope) -endif() +#if(MATSTRUCT_WITH_VISUALIZATION) +# if(NOT TARGET polyscope) +# rpd_download_polyscope() +# add_subdirectory(${EXTERNAL_DIR}/polyscope) +# endif() +#endif() # json if(NOT TARGET nlohmann_json::nlohmann_json) diff --git a/cmake/matstruct_downloadExternal.cmake b/cmake/matstruct_downloadExternal.cmake index a6af062..95145bf 100644 --- a/cmake/matstruct_downloadExternal.cmake +++ b/cmake/matstruct_downloadExternal.cmake @@ -89,8 +89,8 @@ endfunction() # libmat function(rpd_download_libmat) rpd_download_project(libmat - # GIT_REPOSITORY https://github.com/ningnawang/libmat.git - GIT_REPOSITORY git@github.com:ningnawang/libmat.git + GIT_REPOSITORY https://github.com/ningnawang/libmat.git + #GIT_REPOSITORY git@github.com:ningnawang/libmat.git GIT_TAG main ) endfunction() diff --git a/src/io_wrapper.cpp b/src/io_wrapper.cpp index c7480d6..95f6eda 100644 --- a/src/io_wrapper.cpp +++ b/src/io_wrapper.cpp @@ -1,5 +1,13 @@ #include "io_wrapper.h" +std::string g_output_dir = "../out"; + +void set_output_dir(const std::string& dir) { + g_output_dir = dir; + while (!g_output_dir.empty() && g_output_dir.back() == '/') + g_output_dir.pop_back(); +} + // helper function void get_mat_clean_and_map(const MedialMesh& mat, std::map& map_matv, std::map& map_mate, @@ -223,7 +231,7 @@ bool export_mesh_obj(const std::string obj_file_path, // 2. T is either Vector3 or Vector4 // 3. if face_ids is given, then assign color based on the id template -bool export_mesh_obj(const std::string folder_name, const std::string& filename, +void export_mesh_obj(const std::string folder_name, const std::string& filename, const std::vector& vertices, const std::vector& edges, const std::vector& faces, @@ -324,7 +332,7 @@ bool export_mat_feature_edge_obj(const std::string& folder_name, // // conains some deleted vertices, edges, faces // // not cleaned ma // void export_ma_and_struct_all(const std::string& maname, MedialMesh& mat) { -// std::string folder_name = "../out/" + maname + "/mat"; +// std::string folder_name = g_output_dir + "/" + maname + "/mat"; // create_dir(folder_name); // std::string ma_name_full = // folder_name + "/mat_" + maname + "_" + get_timestamp() + @@ -501,7 +509,7 @@ void compute_and_save_shrink_mstructures(const std::string& maname, const double shrinkFactor) { compute_shrink_mstructures(mmesh, shrinkVertices, shrinkFaces, faceStructIds, shrinkFactor); - std::string folder_name = "../out/" + maname + "/mat/"; + std::string folder_name = g_output_dir + "/" + maname + "/mat/"; create_dir(folder_name); std::string ma_name_full = folder_name + "mat_shrink_" + maname + "_" + get_timestamp() + ".obj"; @@ -516,7 +524,7 @@ bool export_convex_cells_obj( std::string folder_name, std::string rpd_name, const int max_sf_fid, const bool is_boundary_only) { if (folder_name.empty()) { - folder_name = "../out/" + rpd_name + "/rpd/"; + folder_name = g_output_dir + "/" + rpd_name + "/rpd/"; } create_dir(folder_name); @@ -548,7 +556,7 @@ bool export_convex_cells_obj( // each tet has its own vertices copy void export_input_tet_obj(const std::string& tet_name, const TetMesh& tet_mesh) { - std::string folder_name = "../out/" + tet_name + "/input/"; + std::string folder_name = g_output_dir + "/" + tet_name + "/input/"; create_dir(folder_name); static const int tet_face_indices[4][3] = { @@ -593,7 +601,7 @@ void export_ma_junctions(const std::string& folder_name, } void export_ma_obj(const std::string& maname, const MedialMesh& mat) { - std::string folder_name = "../out/" + maname + "/mat/"; + std::string folder_name = g_output_dir + "/" + maname + "/mat/"; create_dir(folder_name); std::map map_matv, map_mate, map_matf; @@ -617,7 +625,7 @@ void export_ma_obj(const std::string& maname, const MedialMesh& mat) { void export_ma_obj_itr(std::string& folder_name, const std::string& maname, const MedialMesh& mat) { if (folder_name.empty()) { - folder_name = "../out/" + maname + "/mmesh_itr/"; + folder_name = g_output_dir + "/" + maname + "/mmesh_itr/"; } create_dir(folder_name); @@ -635,7 +643,7 @@ void export_ma_obj_itr(std::string& folder_name, const std::string& maname, } void export_ma_obj_only(const std::string& maname, const MedialMesh& mat) { - std::string folder_name = "../out/" + maname + "/mat/"; + std::string folder_name = g_output_dir + "/" + maname + "/mat/"; create_dir(folder_name); std::map map_matv, map_mate, map_matf; @@ -665,7 +673,7 @@ void export_ma_obj_only(const std::string& maname, const MedialMesh& mat) { * f1/e1/v1 f2/e2/v2 f3/e3/v3 f4/e4/v3 ... fn/en/ */ void export_ma_and_struct_clean(const std::string& maname, MedialMesh& mat) { - std::string folder_name = "../out/" + maname + "/mat/"; + std::string folder_name = g_output_dir + "/" + maname + "/mat/"; create_dir(folder_name); std::map map_matv, map_mate, map_matf; @@ -794,7 +802,7 @@ void save_mstruct_houdini_split_sheet_only_helper( std::map>& map_vid_old_to_new_copies /*global*/) { if (mmesh.vertices == nullptr) return; if (mmesh.mstructure.empty()) return; - std::string folder_name = "../out/" + mstruct_name + "/mstruct/"; + std::string folder_name = g_output_dir + "/" + mstruct_name + "/mstruct/"; create_dir(folder_name); std::string mstruct_sheet_path = folder_name + "mstruct_sheet_" + mstruct_name + "_" + get_timestamp(); @@ -914,7 +922,7 @@ bool load_mstruct_jun_seam_sheet_helper( // 2. save all types of mstructs, for each vertex, save its copies in step1. bool save_mstruct_houdini(const MedialMesh& mmesh, std::string mstruct_name, const bool is_store_sheets_split, bool is_debug) { - std::string folder_name = "../out/" + mstruct_name + "/mstruct/"; + std::string folder_name = g_output_dir + "/" + mstruct_name + "/mstruct/"; create_dir(folder_name); std::string mstruct_all_path = folder_name + "mstruct_all_" + mstruct_name + "_" + get_timestamp(); @@ -995,7 +1003,7 @@ void export_spheres_normals(const MedialMesh& mmesh, if (mmesh.vertices == nullptr) return; const auto& all_medial_spheres = *mmesh.vertices; - std::string folder_name = "../out/" + maname + "/normals/"; + std::string folder_name = g_output_dir + "/" + maname + "/normals/"; create_dir(folder_name); std::string normals_path = folder_name + "normals_" + maname + "_" + get_timestamp() + ".normal"; @@ -1051,7 +1059,7 @@ bool export_centers_xyz(const std::string& folder_name, } void export_MSER(const std::string& filename, const double mser) { - std::string out = "../out/MSER.txt"; + std::string out = g_output_dir + "/MSER.txt"; std::ofstream outFile; outFile.open(out, std::ios::app); // append outFile << filename << ": " << mser << std::endl; @@ -1059,7 +1067,7 @@ void export_MSER(const std::string& filename, const double mser) { } void export_TQ(const std::string& filename, const double tq) { - std::string out = "../out/TQ.txt"; + std::string out = g_output_dir + "/TQ.txt"; std::ofstream outFile; outFile.open(out, std::ios::app); // append outFile << filename << ": " << tq << std::endl; diff --git a/src/io_wrapper.h b/src/io_wrapper.h index 8002dc9..9e6e5fb 100644 --- a/src/io_wrapper.h +++ b/src/io_wrapper.h @@ -1,7 +1,13 @@ +#include + #include "io.h" #include "io_cuda.h" #include "io_utils.hpp" +// Global output directory (default "../out"). Set via set_output_dir(). +extern std::string g_output_dir; +void set_output_dir(const std::string& dir); + // // for exporting more info // void export_ma_and_struct_all(const std::string& maname, MedialMesh& mat); diff --git a/src/main_gui.cxx b/src/main_gui.cxx index 32b0fdf..5f03289 100644 --- a/src/main_gui.cxx +++ b/src/main_gui.cxx @@ -1,11 +1,13 @@ #include "main_gui.h" +#ifdef MATSTRUCT_WITH_VISUALIZATION #include #include #include #include #include +#endif // MATSTRUCT_WITH_VISUALIZATION #include "eval/mesh_eval.h" #include "fix_extf.h" @@ -48,6 +50,18 @@ MainGuiWindow::~MainGuiWindow() { this->instance_ = nullptr; } +void MainGuiWindow::release_pointers() { + params = nullptr; + tet_mesh = nullptr; + sf_mesh = nullptr; + all_medial_spheres = nullptr; + mmesh = nullptr; + spheres_to_fix = nullptr; + rt = nullptr; + rpd3d = nullptr; + opt_rpd = nullptr; +} + void MainGuiWindow::set_params(Parameter& _params) { params = &_params; } void MainGuiWindow::set_tet_mesh(TetMesh& _tet_mesh) { tet_mesh = &_tet_mesh; } void MainGuiWindow::set_sf_mesh(const SurfaceMesh& _sf_mesh) { @@ -85,7 +99,9 @@ int MainGuiWindow::run_topo_fix_itr( is_debug /*is_debug*/); if (spheres_to_fix.empty()) { +#ifdef MATSTRUCT_WITH_VISUALIZATION polyscope::info("Topology check has passed!"); +#endif return 0; } @@ -95,8 +111,10 @@ int MainGuiWindow::run_topo_fix_itr( spheres_to_fix, all_medial_spheres, is_debug /*is_debug*/); // int num_added = all_medial_spheres.size() - old_size; +#ifdef MATSTRUCT_WITH_VISUALIZATION polyscope::info("Topo check change " + std::to_string(num_sphere_change) + " spheres"); +#endif num_topo_itr++; return num_sphere_change; } @@ -126,7 +144,9 @@ void MainGuiWindow::run_mmesh_generator( // compute medial mesh quality double tri_quality = eval_triangle_quality(mmesh); +#ifdef MATSTRUCT_WITH_VISUALIZATION polyscope::info("Done calculating medial mesh"); +#endif } int MainGuiWindow::run_fix_geo_itr( @@ -141,7 +161,9 @@ int MainGuiWindow::run_fix_geo_itr( this->fix_geo_samples_clostprim, is_debug /*is_debug*/); int num_added = all_medial_spheres.size() - old_size; +#ifdef MATSTRUCT_WITH_VISUALIZATION polyscope::info("Geo check added " + std::to_string(num_added) + " spheres"); +#endif num_geo_itr++; return num_added; } @@ -159,8 +181,10 @@ int MainGuiWindow::run_extf_fix_itr( int num_corners = init_corner_spheres(instance_->num_itr_global, tet_mesh, all_medial_spheres); mmesh.is_corner_spheres_created = true; +#ifdef MATSTRUCT_WITH_VISUALIZATION polyscope::info("EXTF check add " + std::to_string(num_corners) + " corners"); +#endif num_extf_itr++; return num_corners; } @@ -171,8 +195,10 @@ int MainGuiWindow::run_extf_fix_itr( sf_mesh, tet_mesh.tet_vs_lfs2tvs_map, tet_mesh.fl2corner_sphere, all_medial_spheres, is_debug /*is_debug*/); int num_added = all_medial_spheres.size() - old_size; +#ifdef MATSTRUCT_WITH_VISUALIZATION polyscope::info("EXTF check changed " + std::to_string(num_changed) + " spheres"); +#endif num_extf_itr++; return num_changed; } @@ -192,8 +218,10 @@ int MainGuiWindow::run_intf_fix_itr( // update T2 by TN int num_updated = 0; +#ifdef MATSTRUCT_WITH_VISUALIZATION polyscope::info("INTF check added " + std::to_string(num_added) + ", updated " + std::to_string(num_updated) + " spheres"); +#endif num_intf_itr++; return num_added + num_updated; } @@ -455,6 +483,7 @@ void MainGuiWindow::update_rpd_after_partial() { } void MainGuiWindow::show(bool is_compute_rpd) { +#ifdef MATSTRUCT_WITH_VISUALIZATION // a few camera options polyscope::view::upDir = polyscope::UpDir::ZUp; @@ -487,6 +516,7 @@ void MainGuiWindow::show(bool is_compute_rpd) { // Show the GUI polyscope::show(); +#endif // MATSTRUCT_WITH_VISUALIZATION } void MainGuiWindow::auto_eval_add_medge_len(const int itr_print) { @@ -764,6 +794,7 @@ void MainGuiWindow::proj_sphere_SQEM_clean_extf() { ///////////////////////////////////////////////////////////////////////////////////////////////////////// void MainGuiWindow::show_result_convex_cells( std::vector& cells_to_show, bool is_slice_plane) { +#ifdef MATSTRUCT_WITH_VISUALIZATION std::vector voro_points; std::vector voro_points_num_adjs; std::vector> voro_tets; // tet @@ -804,9 +835,11 @@ void MainGuiWindow::show_result_convex_cells( my_mesh->addCellColorQuantity("site_color", voro_colors)->setEnabled(true); my_mesh->addCellScalarQuantity("euler", voro_tets_euler); my_mesh->addCellScalarQuantity("cell_id", voro_tets_cell_ids); +#endif // MATSTRUCT_WITH_VISUALIZATION } void MainGuiWindow::show_tet_mesh(const TetMesh& tet_mesh, bool is_shown_meta) { +#ifdef MATSTRUCT_WITH_VISUALIZATION std::vector> tet_vertices_new; std::vector> tet_indices_new; std::vector tet_ids; @@ -868,10 +901,12 @@ void MainGuiWindow::show_tet_mesh(const TetMesh& tet_mesh, bool is_shown_meta) { my_se_edges->addEdgeScalarQuantity("fl_id", se_fl_ids)->setEnabled(true); my_se_edges->addEdgeScalarQuantity("fe_id", se_fe_ids)->setEnabled(true); my_se_edges->setEnabled(false); +#endif // MATSTRUCT_WITH_VISUALIZATION } void MainGuiWindow::show_medial_mesh(const MedialMesh& mmesh, std::string mmname) { +#ifdef MATSTRUCT_WITH_VISUALIZATION if (mmesh.vertices == nullptr) return; const auto& mspheres = *(mmesh.vertices); const auto& mfaces = mmesh.faces; @@ -904,4 +939,5 @@ void MainGuiWindow::show_medial_mesh(const MedialMesh& mmesh, medial_mesh->setBackFacePolicy(polyscope::BackFacePolicy::Identical); medial_mesh->addVertexScalarQuantity("radius", mat_radius, polyscope::DataType::MAGNITUDE); +#endif // MATSTRUCT_WITH_VISUALIZATION } diff --git a/src/main_gui.h b/src/main_gui.h index 3d38563..c46a627 100644 --- a/src/main_gui.h +++ b/src/main_gui.h @@ -83,6 +83,7 @@ class MainGuiWindow { void update_rpd_after_partial(); void show(bool is_compute_rpd = true); static void callbacks(); + void release_pointers(); // main iteration functions int num_itr_global = 0; diff --git a/src/main_voronoi.cpp b/src/main_voronoi.cpp index 804e5e2..43adf0e 100644 --- a/src/main_voronoi.cpp +++ b/src/main_voronoi.cpp @@ -51,6 +51,7 @@ int main(int argc, char** argv) { setup_signal_handlers(); std::srand(RAN_SEED); // set random seed std::string tet_mesh_file; + std::string output_dir = "../out"; int poisson_diag = 40; Parameter params; @@ -72,6 +73,8 @@ int main(int argc, char** argv) { app.add_option("--ce,--concave", params.thres_concave, "Concave angle threshold in degrees, bigger is more " "sensitive. (double, optional, default=60.)"); + app.add_option("-d,--output-dir", output_dir, + "Output directory (default: ../out)"); try { app.parse(argc, argv); @@ -79,11 +82,14 @@ int main(int argc, char** argv) { return app.exit(e); } + set_output_dir(output_dir); + params.is_run_cad = !params.is_run_organic; if (params.is_run_organic) { params.thres_concave = 70.; params.is_sample_rpd = false; } + printf("output_dir: %s\n", g_output_dir.c_str()); printf("setting poisson_diag to %d\n", poisson_diag); printf("is_run_cad: %d, is_run_organic: %d\n", params.is_run_cad, params.is_run_organic); @@ -243,7 +249,16 @@ int main(int argc, char** argv) { } main_gui.auto_fix_topo_and_clean(); main_gui.auto_opt_rpd_only_particle(); +#ifdef MATSTRUCT_WITH_VISUALIZATION main_gui.show(true /*is_compute_rpd*/); +#endif + + // Release pointers before stack unwinds to prevent destructors from + // calling delete on stack-allocated objects passed via set_*() / init(). + opt_rpd.get_rpd3d()->release_pointers(); + opt_rpd.release_pointers(); + mmesh.clear(); + main_gui.release_pointers(); cudaDeviceReset(); if (initptr != nullptr) cudaFree(initptr); diff --git a/src/opt/opt_rpd.cxx b/src/opt/opt_rpd.cxx index 155b584..14ac51a 100644 --- a/src/opt/opt_rpd.cxx +++ b/src/opt/opt_rpd.cxx @@ -14,6 +14,14 @@ OPT_RPD::~OPT_RPD() { delete _mmesh; } +void OPT_RPD::release_pointers() { + _rpd3d.release_pointers(); + _tet_mesh = nullptr; + _sf_mesh = nullptr; + _all_medial_spheres = nullptr; + _mmesh = nullptr; +} + void OPT_RPD::init(const TetMesh* _tet_mesh, const SurfaceMesh* _sf_mesh, const Parameter* _params, std::vector* _all_medial_spheres, diff --git a/src/opt/opt_rpd.h b/src/opt/opt_rpd.h index aa72c5c..27b1490 100644 --- a/src/opt/opt_rpd.h +++ b/src/opt/opt_rpd.h @@ -56,6 +56,7 @@ class OPT_RPD { const Parameter* _params, std::vector* _all_medial_spheres, MedialMesh* _mmesh); void set_file_name_no_ext(std::string _name_no_ext); + void release_pointers(); // for debug std::vector _sphere_centers_old; // 3D (x,y,z)