diff --git a/README.md b/README.md index d7aefdf..c21f636 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,10 @@ To run the program, simply execute the binary: ## Usage The load files are in `sim-test/rigid-test/*/json`, generated by `sim-test/rigid-test/*/*.json.in`. + +--- +## Detailed steps +1. 点击加载文件,选择.json配置文件(选择错误后缀会有提醒). +2. 设置边界条件和材料,点击开始计算. +3. 查看仿真结果,并可选择导出. +4. 可选择结果对比(需要预先准备文件),选择对应例子的`/ref_res`文件夹(错误文件夹名会有提醒),显示对比结果. \ No newline at end of file diff --git a/src/viewer/StaticSimGUIMenu.cpp b/src/viewer/StaticSimGUIMenu.cpp index 7d318ad..589cee0 100644 --- a/src/viewer/StaticSimGUIMenu.cpp +++ b/src/viewer/StaticSimGUIMenu.cpp @@ -15,20 +15,51 @@ namespace ssim { auto io_menu = [&]() { static std::string f_json; + static bool show_popup = false; + if (ImGui::Button("加载文件##IO", ImVec2(-1, 0))) { #ifndef DEBUG_SSIM f_json = igl::file_dialog_open(); - if (f_json == "") return; - reload_json(f_json); + + if (!JudgeEndWith(f_json, ".json")) { + // wrong file suffix, tip to reload + show_popup = true; + } else { + show_popup = false; + reload_json(f_json); #else - f_json ="/debug/abspath/to/sim-test/rigid-test/cube/config.json/"; - reload_json(CMAKE_SOURCE_DIR "/sim-test/rigid-test/cube/config.json"); + f_json ="/debug/abspath/to/sim-test/rigid-test/cube/config.json/"; + reload_json(CMAKE_SOURCE_DIR "/sim-test/rigid-test/cube/config.json"); #endif - gui_ctrl_.is_initialized = true; - gui_ctrl_.is_loaded_json = true; - gui_ctrl_.has_load_cmp_file= false; + gui_ctrl_.is_initialized = true; + gui_ctrl_.is_loaded_json = true; + gui_ctrl_.has_load_cmp_file = false; + } + } + + // tip window + if (show_popup) { + static float popup_time = 0.0f; + const float popup_duration = 7.0f; // 显示时间 + + ImGui::SetNextWindowPos(ImVec2(600, 500)); // 窗口位置 + ImGui::SetNextWindowBgAlpha(0.5f); // 背景透明度 + ImGui::Begin("提示", &show_popup, ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings); + ImGui::Text("文件类型错误!\n请选择.json配置文件."); + ImGui::End(); + + if (popup_time == 0.0f) { + popup_time = ImGui::GetTime(); + } else if (ImGui::GetTime() - popup_time > popup_duration) { + show_popup = false; + popup_time = 0.0f; + } } + if (gui_ctrl_.is_loaded_json) { ImGui::BeginChild("##filename", ImVec2(-1, ImGui::GetFontSize() * 3), false, ImGuiWindowFlags_HorizontalScrollbar); @@ -72,10 +103,12 @@ namespace ssim { static bool is_drag[2]; ImGui::PushItemWidth(-80); - is_drag[0] = ImGui::DragFloat3(("最小值 " + std::to_string(i + 1) + "##Dir").c_str(), - v_min_point.data(), 0.01, 0.0, 1.0, "%.3f"); - is_drag[1] = ImGui::DragFloat3(("最大值 " + std::to_string(i + 1) + "##Dir").c_str(), - v_max_point.data(), 0.01, 0.0, 1.0, "%.3f"); + is_drag[0] = ImGui::DragFloat3( + ("最小值 " + std::to_string(i + 1) + "##Dir").c_str(), + v_min_point.data(), 0.01, 0.0, 1.0, "%.3f"); + is_drag[1] = ImGui::DragFloat3( + ("最大值 " + std::to_string(i + 1) + "##Dir").c_str(), + v_max_point.data(), 0.01, 0.0, 1.0, "%.3f"); ImGui::PopItemWidth(); if (is_drag[0] || is_drag[1]) { gui_ctrl_.is_modified = true; @@ -100,16 +133,20 @@ namespace ssim { std::vector v_max_point = {(float) i_NBC.relMaxBBox.x(), (float) i_NBC.relMaxBBox.y(), (float) i_NBC.relMaxBBox.z()}; - std::vector v_force = {(float) i_NBC.force.x(), (float) i_NBC.force.y(), + std::vector v_force = {(float) i_NBC.force.x(), + (float) i_NBC.force.y(), (float) i_NBC.force.z()}; static bool is_drag[3]; ImGui::PushItemWidth(-80); - is_drag[0] = ImGui::DragFloat3(("最小值 " + std::to_string(i + 1) + "##Neu").c_str(), - v_min_point.data(), 0.01, 0.0, 1.0, "%.3f"); - is_drag[1] = ImGui::DragFloat3(("最大值 " + std::to_string(i + 1) + "##Neu").c_str(), - v_max_point.data(), 0.01, 0.0, 1.0, "%.3f"); - is_drag[2] = ImGui::DragFloat3(("力" + std::to_string(i + 1) + " (N)" + "##Neu").c_str(), - v_force.data(), 1, -1e10, 1e10, "%.3f"); + is_drag[0] = ImGui::DragFloat3( + ("最小值 " + std::to_string(i + 1) + "##Neu").c_str(), + v_min_point.data(), 0.01, 0.0, 1.0, "%.3f"); + is_drag[1] = ImGui::DragFloat3( + ("最大值 " + std::to_string(i + 1) + "##Neu").c_str(), + v_max_point.data(), 0.01, 0.0, 1.0, "%.3f"); + is_drag[2] = ImGui::DragFloat3( + ("力" + std::to_string(i + 1) + " (N)" + "##Neu").c_str(), + v_force.data(), 1, -1e10, 1e10, "%.3f"); ImGui::PopItemWidth(); if (is_drag[0] || is_drag[1] || is_drag[2]) { gui_ctrl_.is_modified = true; @@ -130,15 +167,19 @@ namespace ssim { if (ImGui::CollapsingHeader("材料设置", ImGuiTreeNodeFlags_DefaultOpen)) { static const int item_width = 200; ImGui::SetNextItemWidth(item_width); - ImGui::InputFloat("杨氏模量 (Pa)", &sp_StaticSim_->get_material_property().Youngs_Modulus, 1.0f, 2e11f, + ImGui::InputFloat("杨氏模量 (Pa)", + &sp_StaticSim_->get_material_property().Youngs_Modulus, 1.0f, + 2e11f, "%.2e"); ImGui::SetNextItemWidth(item_width); - ImGui::InputFloat("泊松比", &sp_StaticSim_->get_material_property().Poisson_ratio, 0.01f, 1.0f, + ImGui::InputFloat("泊松比", &sp_StaticSim_->get_material_property().Poisson_ratio, + 0.01f, 1.0f, "%.3f"); ImGui::SetNextItemWidth(item_width); - ImGui::InputFloat("密度 (kg/m^3)", &sp_StaticSim_->get_material_property().density, 1.0f, 1000.0f, + ImGui::InputFloat("密度 (kg/m^3)", &sp_StaticSim_->get_material_property().density, + 1.0f, 1000.0f, "%.3f"); } @@ -158,7 +199,8 @@ namespace ssim { }; auto result_menu = [&]() { ImGui::TextWrapped("柔顺度 (compliance): %.3e", - sp_StaticSim_->EvaluateTarget(ssim::SimTargetOption::COMPLIANCE)(0, 0)); + sp_StaticSim_->EvaluateTarget(ssim::SimTargetOption::COMPLIANCE)(0, + 0)); static const ImVec2 button_size(200, 40); static bool arr_extract[SimTargetOption::Target::ENUM_SIZE] = {false}; int idx = 0; @@ -229,7 +271,8 @@ namespace ssim { if (ImGui::Button("导出结果文件", button_size)) { std::string extract_dir = ssim::directory_dialog_save(); spdlog::info("extract to: {}", extract_dir); - std::vector v_is_extract(arr_extract, arr_extract + SimTargetOption::Target::ENUM_SIZE); + std::vector v_is_extract(arr_extract, + arr_extract + SimTargetOption::Target::ENUM_SIZE); sp_StaticSim_->ExtractTarget(extract_dir, v_is_extract, is_extract_coord); } ImGui::SameLine(); @@ -241,35 +284,65 @@ namespace ssim { static std::vector v_error(SimTargetOption::Target::ENUM_SIZE, EMPTY); + static bool show_popup = false; if (ImGui::Button("导入Ansys文件", ImVec2(-1, 0))) { std::string input_dir = ssim::directory_dialog_save(); - spdlog::info("input from: {}", input_dir); - - for (int i = 0; i < ssim::SimTargetOption::Target::ENUM_SIZE; ++i) { - std::string target_name = SimTargetOption::GetTargetName(i); - std::ifstream iif(input_dir + "/" + target_name + ".txt"); - Eigen::VectorXd cpt_cur_target = sp_StaticSim_->EvaluateTarget( - static_cast(i)); - Eigen::VectorXd ref_cur_target = cpt_cur_target; - int cnt = 0; - if (!iif.is_open()) { - continue; - } - double t; - while (iif >> t) { - ref_cur_target[cnt++] = t; - } - double cur_error = (cpt_cur_target - ref_cur_target).norm() / ref_cur_target.norm(); - v_error[i] = cur_error; + if (!JudgeEndWith(input_dir, "/ref_res")) { + show_popup = true; + } else { + show_popup = false; + spdlog::info("input from: {}", input_dir); + + for (int i = 0; i < ssim::SimTargetOption::Target::ENUM_SIZE; ++i) { + std::string target_name = SimTargetOption::GetTargetName(i); + std::ifstream iif(input_dir + "/" + target_name + ".txt"); + Eigen::VectorXd cpt_cur_target = sp_StaticSim_->EvaluateTarget( + static_cast(i)); + Eigen::VectorXd ref_cur_target = cpt_cur_target; + int cnt = 0; + if (!iif.is_open()) { + continue; + } + double t; + while (iif >> t) { + ref_cur_target[cnt++] = t; + } + + double cur_error = + (cpt_cur_target - ref_cur_target).norm() / ref_cur_target.norm(); + v_error[i] = cur_error; + } + gui_ctrl_.has_load_cmp_file = true; + } + } + if(show_popup) { + // wrong input file tip + static float popup_time = 0.0f; + const float popup_duration = 7.0f; // 显示时间为5秒 + + ImGui::SetNextWindowPos(ImVec2(600, 500)); // 窗口位置 + ImGui::SetNextWindowBgAlpha(0.5f); // 背景透明度 + ImGui::Begin("提示", &show_popup, ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings); + ImGui::Text("文件夹类型错误!\n请选择ref_res文件夹."); + ImGui::End(); + + if (popup_time == 0.0f) { + popup_time = ImGui::GetTime(); + } else if (ImGui::GetTime() - popup_time > popup_duration) { + show_popup = false; + popup_time = 0.0f; } - gui_ctrl_.has_load_cmp_file = true; } if (gui_ctrl_.has_load_cmp_file) { for (int i = 0; i < SimTargetOption::Target::ENUM_SIZE; ++i) { if (v_error[i] != EMPTY) { - ImGui::TextWrapped((SimTargetOption::GetTargetName(i) + std::string("误差: %.5f %%")).c_str(), + ImGui::TextWrapped((SimTargetOption::GetTargetName(i) + + std::string("误差: %.5f %%")).c_str(), v_error[i]); } } diff --git a/src/viewer/Util.h b/src/viewer/Util.h index 5f849a6..5928db7 100644 --- a/src/viewer/Util.h +++ b/src/viewer/Util.h @@ -4,17 +4,19 @@ #ifndef EXAMPLE_UTIL_H #define EXAMPLE_UTIL_H + #include #include #include #include + #ifdef _WIN32 #include - #include +#include #endif namespace ssim { - inline std::string directory_dialog_save(){ + inline std::string directory_dialog_save() { const int FILE_DIALOG_MAX_BUFFER = 1024; char buffer[FILE_DIALOG_MAX_BUFFER]; buffer[0] = '\0'; @@ -85,34 +87,29 @@ namespace ssim { #else // For every other machine type use zenity - FILE * output = popen("/usr/bin/zenity --directory --file-selection --save","r"); - if (output) - { + FILE *output = popen("/usr/bin/zenity --directory --file-selection --save", "r"); + if (output) { auto ret = fgets(buffer, FILE_DIALOG_MAX_BUFFER, output); - if (ret == NULL || ferror(output)) - { + if (ret == NULL || ferror(output)) { // I/O error buffer[0] = '\0'; } - if (buffer[FILE_DIALOG_MAX_BUFFER - 1] == '\0') - { + if (buffer[FILE_DIALOG_MAX_BUFFER - 1] == '\0') { // File name too long, buffer has been filled, so we return empty string instead buffer[0] = '\0'; } } // Replace last '\n' by '\0' - if(strlen(buffer) > 0) - { - buffer[strlen(buffer)-1] = '\0'; + if (strlen(buffer) > 0) { + buffer[strlen(buffer) - 1] = '\0'; } #endif return std::string(buffer); } - void texture_from_colormap(const Eigen::MatrixXd &rgb, GLuint & id) - { + void texture_from_colormap(const Eigen::MatrixXd &rgb, GLuint &id) { int width = 1; int height = (int) rgb.rows(); @@ -141,6 +138,10 @@ namespace ssim { glBindTexture(GL_TEXTURE_2D, 0); } + inline bool JudgeEndWith(const std::string &str, const std::string &suffix) { + if (suffix.size() > str.size()) return false; + return str.rfind(suffix) == str.size() - suffix.size(); + }