From 8a529d4a992ccf33643a54c13fd30bb824ab171a Mon Sep 17 00:00:00 2001 From: cflin Date: Fri, 14 Apr 2023 22:26:16 +0800 Subject: [PATCH] add extract interface --- src/static_sim/StaticSim.h | 20 +++ src/viewer/StaticSimGUI.cpp | 188 +++----------------------- src/viewer/StaticSimGUI.h | 26 +--- src/viewer/StaticSimGUIMenu.cpp | 233 ++++++++++++++++++++++++++++++++ src/viewer/Util.h | 116 ++++++++++++++++ 5 files changed, 393 insertions(+), 190 deletions(-) create mode 100644 src/viewer/StaticSimGUIMenu.cpp create mode 100644 src/viewer/Util.h diff --git a/src/static_sim/StaticSim.h b/src/static_sim/StaticSim.h index 441d185..2480935 100644 --- a/src/static_sim/StaticSim.h +++ b/src/static_sim/StaticSim.h @@ -31,6 +31,7 @@ namespace ssim { class StaticSimGUI; } namespace ssim { + struct Model { Eigen::MatrixX3d V; Eigen::MatrixX3i F; @@ -79,6 +80,25 @@ namespace ssim { void postprocess(Eigen::MatrixXd &Q, Eigen::VectorXd &QU, Eigen::MatrixXd &Qstress); + /// Extract data to save directory + /// \param save_dir absolute address, like "path/to/save_dir" + /// \param v_is_extract if (v_is_extract[i]){ extract the target i; } + /// \param extract_coord_flg + void ExtractTarget(std::string save_dir,std::vector v_is_extract,bool extract_coord_flg){ + if(extract_coord_flg){ + // TODO: add coordinate cols + spdlog::info("extract {}","coordinate"); + } + for(int i=0;iDirichletBCs.size(); ++i) { - // each boundary - // ImGui::Checkbox("受力", &m_state.m_solve_collisions); - auto &i_DBC = sp_StaticSim_->DirichletBCs[i]; - std::vector v_min_point = {(float) i_DBC.relMinBBox.x(), - (float) i_DBC.relMinBBox.y(), - (float) i_DBC.relMinBBox.z()}; - std::vector v_max_point = {(float) i_DBC.relMaxBBox.x(), - (float) i_DBC.relMaxBBox.y(), - (float) i_DBC.relMaxBBox.z()}; - if (ImGui::InputFloat3(("Dirichlet最小值 " + std::to_string(i + 1)).c_str(), - v_min_point.data()) || - ImGui::InputFloat3(("Dirichlet最大值 " + std::to_string(i + 1)).c_str(), - v_max_point.data())) { - gui_ctrl_.is_modified = true; - i_DBC.relMinBBox = {v_min_point[0], v_min_point[1], v_min_point[2]}; - i_DBC.relMaxBBox = {v_max_point[0], v_max_point[1], v_max_point[2]}; - } - } - if (gui_ctrl_.is_modified) { - sp_StaticSim_->updateBC(); - } - } - - if (ImGui::CollapsingHeader( - "Neumann边界", ImGuiTreeNodeFlags_Leaf)) { - // -------------------------------------------------------------------- - - for (int i = 0; i < sp_StaticSim_->NeumannBCs.size(); ++i) { - auto &i_NBC = sp_StaticSim_->NeumannBCs[i]; - std::vector v_min_point = {(float) i_NBC.relMinBBox.x(), - (float) i_NBC.relMinBBox.y(), - (float) i_NBC.relMinBBox.z()}; - 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(), - (float) i_NBC.force.z()}; - if ( - ImGui::InputFloat3(("Neumann最小值 " + std::to_string(i + 1)).c_str(), - v_min_point.data()) || - ImGui::InputFloat3(("Neumann最大值 " + std::to_string(i + 1)).c_str(), - v_max_point.data()) || - ImGui::InputFloat3(("Neumann力 " + std::to_string(i + 1)).c_str(), - v_force.data())) { - gui_ctrl_.is_modified = true; - i_NBC.relMinBBox = {v_min_point[0], v_min_point[1], v_min_point[2]}; - i_NBC.relMaxBBox = {v_max_point[0], v_max_point[1], v_max_point[2]}; - i_NBC.force = {v_force[0], v_force[1], v_force[2]}; - - } - } - if (gui_ctrl_.is_modified) { - sp_StaticSim_->updateBC(); - } - - } - - } - - // -------------------------------------------------------------------- - if (ImGui::CollapsingHeader("材料设置", ImGuiTreeNodeFlags_DefaultOpen)) { - - ImGui::InputFloat("杨氏模量", &sp_StaticSim_->get_material_property().Youngs_Modulus, 1.0f, 1e10f, - "%.3f"); - ImGui::InputFloat("泊松比", &sp_StaticSim_->get_material_property().Poisson_ratio, 0.01f, 1.0f, - "%.3f"); - ImGui::InputFloat("密度", &sp_StaticSim_->get_material_property().density, 1.0f, 1000.0f, "%.3f"); - } - - if (ImGui::Button(("原始模型"), ImVec2(-1, 0))) { - gui_ctrl_.is_single_color = true; - gui_ctrl_.is_modified = true; - } - - if (ImGui::Button("开始计算", ImVec2(-1, 0))) { - sp_StaticSim_->simulation(); - gui_ctrl_.is_modified = true; - gui_ctrl_.is_solved = true; - gui_ctrl_.is_single_color = false; - gui_ctrl_.is_visible_BC = false; - - } - }; - auto result_menu = [&]() { - ImGui::TextWrapped("柔顺度 (compliance): %.3e", - sp_StaticSim_->EvaluateTarget(ssim::SimTargetOption::COMPLIANCE)(0, 0)); - - if (ImGui::Button("位移范数", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::U_NORM); - } - if (ImGui::Button("位移-X", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::UX); - } - if (ImGui::Button("位移-Y", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::UY); - } - if (ImGui::Button("位移-Z", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::UZ); - } - - if (ImGui::Button("应力范数", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::S_NORM); - } - if (ImGui::Button("冯氏应力", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::S_VON_Mises); - } - - if (ImGui::Button("应力-X", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::SX); - } - if (ImGui::Button("应力-Y", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::SY); - } - if (ImGui::Button("应力-Z", ImVec2(-1, 0))) { - gui_ctrl_.set_target(ssim::SimTargetOption::Target::SZ); - } - }; - - - if (ImGui::CollapsingHeader("载入模型", ImGuiTreeNodeFlags_DefaultOpen)) { - io_menu(); + void StaticSimGUI::init_mesh() { + // clear all data + // size remain 1 + viewer_.data_list.resize(1); + viewer_.data_list.front().clear(); + viewer_.data_list.front().show_lines= true; + // add boundary box + for (int i = 1; i <=sp_StaticSim_->DirichletBCs.size() + sp_StaticSim_->NeumannBCs.size(); ++i) { + viewer_.append_mesh(); + viewer_.data_list.back().show_lines = false; } - if (!gui_ctrl_.is_initialized && gui_ctrl_.is_loaded_json) { - if (ImGui::CollapsingHeader("模型设置", ImGuiTreeNodeFlags_DefaultOpen)) { - model_menu(); - } + } - if (gui_ctrl_.is_solved) { - if (ImGui::CollapsingHeader("仿真结果", ImGuiTreeNodeFlags_DefaultOpen)) { - result_menu(); - } - } - } + void StaticSimGUI::reload_json(std::string f_json) { + sp_StaticSim_ = f_json == "" ? nullptr : std::make_shared(ssim::SimTargetOption(), f_json); + gui_ctrl_ = GUICtrl(); + init_mesh(); + init_screen(); + } + StaticSimGUI::StaticSimGUI() { + viewer_.plugins.push_back(&plugin_); + plugin_.widgets.push_back(&menu_); + init_screen(); } } \ No newline at end of file diff --git a/src/viewer/StaticSimGUI.h b/src/viewer/StaticSimGUI.h index 74e15ed..8b6510d 100644 --- a/src/viewer/StaticSimGUI.h +++ b/src/viewer/StaticSimGUI.h @@ -42,39 +42,19 @@ namespace ssim { class StaticSimGUI { public: - StaticSimGUI() { - viewer_.plugins.push_back(&plugin_); - plugin_.widgets.push_back(&menu_); - init_screen(); - } + StaticSimGUI(); void viewer_launch(); protected: - void reload_json(std::string f_json="") { - sp_StaticSim_ = f_json == "" ? nullptr : std::make_shared(ssim::SimTargetOption(), f_json); - gui_ctrl_ = GUICtrl(); - init_mesh(); - init_screen(); - } + void reload_json(std::string f_json=""); void init_screen() { viewer_.core().background_color << 0.9f, 0.9f, 0.9f, 0.4f; } - void init_mesh() { - // clear all data - // size remain 1 - viewer_.data_list.resize(1); - viewer_.data_list.front().clear(); - viewer_.data_list.front().show_lines= true; - // add boundary box - for (int i = 1; i <=sp_StaticSim_->DirichletBCs.size() + sp_StaticSim_->NeumannBCs.size(); ++i) { - viewer_.append_mesh(); - viewer_.data_list.back().show_lines = false; - } - } + void init_mesh(); void menu_draw_viewer_menu(); diff --git a/src/viewer/StaticSimGUIMenu.cpp b/src/viewer/StaticSimGUIMenu.cpp new file mode 100644 index 0000000..a85d057 --- /dev/null +++ b/src/viewer/StaticSimGUIMenu.cpp @@ -0,0 +1,233 @@ +// +// Created by cflin on 4/14/23. +// +#include +#include "StaticSimGUI.h" +#include "Util.h" +namespace ssim{ + + void StaticSimGUI::menu_draw_viewer_menu() { + // Draw parent menu content +// menu_.draw_viewer_menu(); + + auto io_menu = [&]() { + if (ImGui::Button("加载文件##IO", ImVec2(-1, 0))) { +#ifndef DEBUG_SSIM + std::string f_json = igl::file_dialog_open(); + if (f_json != "" ) { + reload_json(f_json); + gui_ctrl_.is_initialized = true; + gui_ctrl_.is_loaded_json = true; + } +#else + load(CMAKE_SOURCE_DIR "/sim-test/rigid-test/rocker-arm/config.json"); +#endif + } + + + + + }; + auto model_menu = [&]() { + + if (ImGui::Checkbox("网格可视化", &gui_ctrl_.is_visible_mesh)) { + gui_ctrl_.is_modified = true; + } + + + // -------------------------------------------------------------------- + if (ImGui::CollapsingHeader("边界条件", ImGuiTreeNodeFlags_DefaultOpen)) { + + if (ImGui::Checkbox("边界可视化", &gui_ctrl_.is_visible_BC)) { + gui_ctrl_.is_modified = true; + } + + if (ImGui::CollapsingHeader( + "Dirichlet边界", ImGuiTreeNodeFlags_Leaf)) { + // -------------------------------------------------------------------- + + for (int i = 0; i < sp_StaticSim_->DirichletBCs.size(); ++i) { + // each boundary + // ImGui::Checkbox("受力", &m_state.m_solve_collisions); + auto &i_DBC = sp_StaticSim_->DirichletBCs[i]; + std::vector v_min_point = {(float) i_DBC.relMinBBox.x(), + (float) i_DBC.relMinBBox.y(), + (float) i_DBC.relMinBBox.z()}; + std::vector v_max_point = {(float) i_DBC.relMaxBBox.x(), + (float) i_DBC.relMaxBBox.y(), + (float) i_DBC.relMaxBBox.z()}; + if (ImGui::InputFloat3(("Dirichlet最小值 " + std::to_string(i + 1)).c_str(), + v_min_point.data()) || + ImGui::InputFloat3(("Dirichlet最大值 " + std::to_string(i + 1)).c_str(), + v_max_point.data())) { + gui_ctrl_.is_modified = true; + i_DBC.relMinBBox = {v_min_point[0], v_min_point[1], v_min_point[2]}; + i_DBC.relMaxBBox = {v_max_point[0], v_max_point[1], v_max_point[2]}; + } + } + if (gui_ctrl_.is_modified) { + sp_StaticSim_->updateBC(); + } + } + + if (ImGui::CollapsingHeader( + "Neumann边界", ImGuiTreeNodeFlags_Leaf)) { + // -------------------------------------------------------------------- + + for (int i = 0; i < sp_StaticSim_->NeumannBCs.size(); ++i) { + auto &i_NBC = sp_StaticSim_->NeumannBCs[i]; + std::vector v_min_point = {(float) i_NBC.relMinBBox.x(), + (float) i_NBC.relMinBBox.y(), + (float) i_NBC.relMinBBox.z()}; + 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(), + (float) i_NBC.force.z()}; + if ( + ImGui::InputFloat3(("Neumann最小值 " + std::to_string(i + 1)).c_str(), + v_min_point.data()) || + ImGui::InputFloat3(("Neumann最大值 " + std::to_string(i + 1)).c_str(), + v_max_point.data()) || + ImGui::InputFloat3(("Neumann力 " + std::to_string(i + 1)).c_str(), + v_force.data())) { + gui_ctrl_.is_modified = true; + i_NBC.relMinBBox = {v_min_point[0], v_min_point[1], v_min_point[2]}; + i_NBC.relMaxBBox = {v_max_point[0], v_max_point[1], v_max_point[2]}; + i_NBC.force = {v_force[0], v_force[1], v_force[2]}; + + } + } + if (gui_ctrl_.is_modified) { + sp_StaticSim_->updateBC(); + } + + } + + } + + // -------------------------------------------------------------------- + if (ImGui::CollapsingHeader("材料设置", ImGuiTreeNodeFlags_DefaultOpen)) { + + ImGui::InputFloat("杨氏模量", &sp_StaticSim_->get_material_property().Youngs_Modulus, 1.0f, 1e10f, + "%.3f"); + ImGui::InputFloat("泊松比", &sp_StaticSim_->get_material_property().Poisson_ratio, 0.01f, 1.0f, + "%.3f"); + ImGui::InputFloat("密度", &sp_StaticSim_->get_material_property().density, 1.0f, 1000.0f, "%.3f"); + } + + if (ImGui::Button(("原始模型"), ImVec2(-1, 0))) { + gui_ctrl_.is_single_color = true; + gui_ctrl_.is_modified = true; + } + + if (ImGui::Button("开始计算", ImVec2(-1, 0))) { + sp_StaticSim_->simulation(); + gui_ctrl_.is_modified = true; + gui_ctrl_.is_solved = true; + gui_ctrl_.is_single_color = false; + gui_ctrl_.is_visible_BC = false; + + } + }; + auto result_menu = [&]() { + ImGui::TextWrapped("柔顺度 (compliance): %.3e", + 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; + if (ImGui::Button("位移范数",button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::U_NORM); + } + ImGui::SameLine(); + ImGui::Checkbox("导出位移范数",arr_extract+idx); + ++idx; + + if (ImGui::Button("位移-X", button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::UX); + } + ImGui::SameLine(); + ImGui::Checkbox("导出位移-X",arr_extract+idx); + ++idx; + + if (ImGui::Button("位移-Y", button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::UY); + } + ImGui::SameLine(); + ImGui::Checkbox("导出位移-Y",arr_extract+idx); + ++idx; + + if (ImGui::Button("位移-Z", button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::UZ); + } + ImGui::SameLine(); + ImGui::Checkbox("导出位移-Z",arr_extract+idx); + ++idx; + + if (ImGui::Button("应力范数", button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::S_NORM); + } + ImGui::SameLine(); + ImGui::Checkbox("导出应力范数",arr_extract+idx); + ++idx; + + if (ImGui::Button("冯氏应力", button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::S_VON_Mises); + } + ImGui::SameLine(); + ImGui::Checkbox("导出冯氏应力",arr_extract+idx); + ++idx; + + if (ImGui::Button("应力-X", button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::SX); + } + ImGui::SameLine(); + ImGui::Checkbox("导出应力-X",arr_extract+idx); + ++idx; + + if (ImGui::Button("应力-Y", button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::SY); + } + ImGui::SameLine(); + ImGui::Checkbox("导出应力-Y",arr_extract+idx); + ++idx; + + if (ImGui::Button("应力-Z", button_size)) { + gui_ctrl_.set_target(ssim::SimTargetOption::Target::SZ); + } + ImGui::SameLine(); + ImGui::Checkbox("导出应力-Z",arr_extract+idx); + ++idx; + + static bool is_extract_coord= false; + 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); + sp_StaticSim_->ExtractTarget(extract_dir,v_is_extract,is_extract_coord); + } + ImGui::SameLine(); + ImGui::Checkbox("导出坐标",&is_extract_coord); + + }; + + + if (ImGui::CollapsingHeader("载入模型", ImGuiTreeNodeFlags_DefaultOpen)) { + io_menu(); + } + if (!gui_ctrl_.is_initialized && gui_ctrl_.is_loaded_json) { + if (ImGui::CollapsingHeader("模型设置", ImGuiTreeNodeFlags_DefaultOpen)) { + model_menu(); + } + + if (gui_ctrl_.is_solved) { + if (ImGui::CollapsingHeader("仿真结果", ImGuiTreeNodeFlags_DefaultOpen)) { + result_menu(); + } + } + } + + } + + +} \ No newline at end of file diff --git a/src/viewer/Util.h b/src/viewer/Util.h new file mode 100644 index 0000000..08226e8 --- /dev/null +++ b/src/viewer/Util.h @@ -0,0 +1,116 @@ +// +// Created by cflin on 4/14/23. +// + +#ifndef EXAMPLE_UTIL_H +#define EXAMPLE_UTIL_H +#include +#include +#include + +#ifdef _WIN32 +#include + #include +#endif +namespace ssim { + + inline std::string directory_dialog_save(){ + const int FILE_DIALOG_MAX_BUFFER = 1024; + char buffer[FILE_DIALOG_MAX_BUFFER]; + buffer[0] = '\0'; + buffer[FILE_DIALOG_MAX_BUFFER - 1] = 'x'; // Initialize last character with a char != '\0' + +#ifdef __APPLE__ + // For apple use applescript hack + // There is currently a bug in Applescript that strips extensions off + // of chosen existing files in the "choose file name" dialog + // I'm assuming that will be fixed soon + FILE * output = popen( + "osascript -e \"" + " tell application \\\"System Events\\\"\n" + " activate\n" + " set existing_file to choose file name\n" + " end tell\n" + " set existing_file_path to (POSIX path of (existing_file))\n" + "\" 2>/dev/null | tr -d '\n' ","r"); + if (output) + { + auto ret = fgets(buffer, FILE_DIALOG_MAX_BUFFER, output); + if (ret == NULL || ferror(output)) + { + // I/O error + buffer[0] = '\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'; + } + } +#elif defined _WIN32 + + // Use native windows file dialog box + // (code contributed by Tino Weinkauf) + + OPENFILENAME ofn; // common dialog box structure + char szFile[260]; // buffer for file name + + // Initialize OPENFILENAME + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = NULL;//hwnd; + ofn.lpstrFile = szDir; + // Set lpstrFile[0] to '\0' so that GetOpenFileName does not + // use the contents of szFile to initialize itself. + ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = sizeof(szDir); + ofn.lpstrFilter = ""; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST; + + // Display the Open dialog box. + int pos = 0; + if (GetSaveFileName(&ofn)==TRUE) + { + while(ofn.lpstrFile[pos] != '\0') + { + buffer[pos] = (char)ofn.lpstrFile[pos]; + pos++; + } + buffer[pos] = 0; + } + +#else + // For every other machine type use zenity + 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)) + { + // I/O error + buffer[0] = '\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'; + } + +#endif + return std::string(buffer); + } + +} // ssim + +#endif //EXAMPLE_UTIL_H