You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
404 lines
18 KiB
404 lines
18 KiB
//
|
|
// Created by cflin on 4/7/23.
|
|
//
|
|
|
|
#ifndef STATICSIMGUI_H
|
|
#define STATICSIMGUI_H
|
|
|
|
#include <memory> // shared_ptr
|
|
|
|
#include <spdlog/spdlog.h>
|
|
|
|
#include <igl/Timer.h>
|
|
#include <igl/opengl/glfw/Viewer.h>
|
|
#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
|
|
#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
|
|
#include <igl/opengl/glfw/imgui/ImGuiHelpers.h>
|
|
#include <iostream>
|
|
#include <igl/jet.h>
|
|
#include <igl/colormap.h>
|
|
#include "../static_sim/StaticSim.h"
|
|
#include "StaticMenu.h"
|
|
|
|
namespace ssim {
|
|
|
|
struct GUICtrl {
|
|
bool is_initialized = false;
|
|
bool is_loaded_json = false;
|
|
bool is_modified = false;
|
|
bool is_solved = false;
|
|
bool is_visible_BC = false;
|
|
bool mesh_visible = true;
|
|
bool single_color = true;
|
|
ssim::SimTargetOption::Target target_to_evaluate = ssim::SimTargetOption::U_NORM;
|
|
|
|
void set_target(ssim::SimTargetOption::Target target_to_set) {
|
|
target_to_evaluate = target_to_set;
|
|
is_modified = true;
|
|
single_color = false;
|
|
}
|
|
|
|
};
|
|
|
|
class StaticSimGUI {
|
|
public:
|
|
StaticSimGUI() : gui_ctrl_(), sp_StaticSim_(nullptr) {
|
|
plugin_.widgets.push_back(&menu_);
|
|
viewer_.plugins.push_back(&plugin_);
|
|
init_screen();
|
|
}
|
|
|
|
void viewer_launch();
|
|
|
|
protected:
|
|
|
|
void init_screen() {
|
|
viewer_.core().background_color << 0.9f, 0.9f, 0.9f, 0.4f;
|
|
}
|
|
|
|
void init_mesh() {
|
|
// erase mesh to 1
|
|
while (viewer_.erase_mesh(viewer_.selected_data_index)) { ;
|
|
}
|
|
// clear last mesh
|
|
viewer_.data(0).clear();
|
|
// add mesh to visible boundary
|
|
for (int i = 0; i < sp_StaticSim_->DirichletBCs.size() + sp_StaticSim_->NeumannBCs.size(); ++i) {
|
|
viewer_.append_mesh();
|
|
if (i >= 1)
|
|
viewer_.data(i).show_lines = false;
|
|
}
|
|
}
|
|
|
|
void 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 fname = igl::file_dialog_open();
|
|
if (fname != "") {
|
|
sp_StaticSim_ = std::make_shared<ssim::StaticSim>(ssim::SimTargetOption(), fname);
|
|
init_screen();
|
|
// reset gui_ctrl_
|
|
gui_ctrl_ = GUICtrl();
|
|
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::Button("开始计算")) {
|
|
sp_StaticSim_->simulation();
|
|
gui_ctrl_.is_modified = true;
|
|
gui_ctrl_.is_solved = true;
|
|
gui_ctrl_.single_color = false;
|
|
gui_ctrl_.is_visible_BC = false;
|
|
}
|
|
if (ImGui::Button(("原始模型"))) {
|
|
gui_ctrl_.single_color = true;
|
|
gui_ctrl_.is_modified = true;
|
|
}
|
|
|
|
if (ImGui::Checkbox("网格可视化", &gui_ctrl_.mesh_visible)) {
|
|
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_DefaultOpen)) {
|
|
// --------------------------------------------------------------------
|
|
|
|
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<float> v_min_point = {(float) i_DBC.relMinBBox.x(),
|
|
(float) i_DBC.relMinBBox.y(),
|
|
(float) i_DBC.relMinBBox.z()};
|
|
std::vector<float> 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_DefaultOpen)) {
|
|
// --------------------------------------------------------------------
|
|
|
|
for (int i = 0; i < sp_StaticSim_->NeumannBCs.size(); ++i) {
|
|
auto &i_NBC = sp_StaticSim_->NeumannBCs[i];
|
|
std::vector<float> v_min_point = {(float) i_NBC.relMinBBox.x(),
|
|
(float) i_NBC.relMinBBox.y(),
|
|
(float) i_NBC.relMinBBox.z()};
|
|
std::vector<float> v_max_point = {(float) i_NBC.relMaxBBox.x(),
|
|
(float) i_NBC.relMaxBBox.y(),
|
|
(float) i_NBC.relMaxBBox.z()};
|
|
std::vector<float> 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();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ImGui::HelpMarker("yes - stop playing if step had a collision.");
|
|
|
|
// --------------------------------------------------------------------
|
|
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");
|
|
}
|
|
};
|
|
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();
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
bool pre_draw_loop() {
|
|
auto load_first_buffer = [&]() {
|
|
// get V, F
|
|
ssim::Model model = sp_StaticSim_->get_mesh();
|
|
const Eigen::MatrixXd &V = model.V;
|
|
const Eigen::MatrixXi &F = model.F;
|
|
|
|
// plot
|
|
viewer_.data(0).clear();
|
|
viewer_.data(0).is_visible = true;
|
|
viewer_.data(0).set_mesh(V, F);
|
|
|
|
std::cerr << "load first buffer?" << std::endl;
|
|
int dim = V.cols();
|
|
viewer_.core().trackball_angle.setIdentity();
|
|
viewer_.core().set_rotation_type(
|
|
dim == 2 ? igl::opengl::ViewerCore::ROTATION_TYPE_NO_ROTATION
|
|
: igl::opengl::ViewerCore::
|
|
ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP);
|
|
viewer_.core().align_camera_center(
|
|
V, F);
|
|
};
|
|
auto redraw_scene = [&]() {
|
|
// get V, F
|
|
ssim::Model model = sp_StaticSim_->get_mesh();
|
|
const Eigen::MatrixXd &V = model.V;
|
|
const Eigen::MatrixXi &F = model.F;
|
|
Eigen::VectorXi DBC_vertex_idx = sp_StaticSim_->DBC_vertexIdx_;
|
|
Eigen::VectorXi NBC_vertex_idx = sp_StaticSim_->NBC_vertexIdx_;
|
|
|
|
|
|
// plot
|
|
viewer_.data(0).set_mesh(V, F);
|
|
|
|
|
|
viewer_.data(0).show_lines = gui_ctrl_.mesh_visible;
|
|
|
|
|
|
if (!gui_ctrl_.single_color) {
|
|
// colormap
|
|
Eigen::MatrixXd target = sp_StaticSim_->EvaluateTarget(gui_ctrl_.target_to_evaluate);
|
|
Eigen::MatrixXd C;
|
|
igl::jet(target, true, C);
|
|
if (0 && gui_ctrl_.is_visible_BC) {
|
|
for (int i_ = 0; i_ < DBC_vertex_idx.size(); ++i_) {
|
|
C.row(DBC_vertex_idx(i_)) = Eigen::RowVector3d(0, 0, 1);
|
|
}
|
|
for (int i_ = 0; i_ < NBC_vertex_idx.size(); ++i_) {
|
|
C.row(NBC_vertex_idx(i_)) = Eigen::RowVector3d(1, 0, 0);
|
|
}
|
|
}
|
|
viewer_.data(0).set_colors(C);
|
|
} else {
|
|
Eigen::MatrixXd color(V.rows(), 3);
|
|
for (int i_ = 0; i_ < V.rows(); ++i_) {
|
|
color.row(i_) = Eigen::RowVector3d(1, 1, 0);
|
|
}
|
|
if (gui_ctrl_.is_visible_BC) {
|
|
for (int i_ = 0; i_ < DBC_vertex_idx.size(); ++i_) {
|
|
color.row(DBC_vertex_idx(i_)) = Eigen::RowVector3d(0, 0, 1);
|
|
}
|
|
for (int i_ = 0; i_ < NBC_vertex_idx.size(); ++i_) {
|
|
color.row(NBC_vertex_idx(i_)) = Eigen::RowVector3d(1, 0, 0);
|
|
}
|
|
}
|
|
// single color
|
|
viewer_.data(0).set_colors(color);
|
|
// colorbar_plugin_.draw_colorbar(igl::ColorMapType::COLOR_MAP_TYPE_JET,0,1,/*background_color=*/Eigen::Vector4f(1,1,1,0));
|
|
}
|
|
};
|
|
auto redraw_BC_box = [&]() {
|
|
auto get_V_F_from_bd_box = [](const Eigen::Vector3d &v_min,
|
|
const Eigen::Vector3d &v_max) -> std::tuple<Eigen::MatrixXd, Eigen::MatrixXi> {
|
|
Eigen::MatrixXd V(8, 3);
|
|
V << -1, -1, -1,
|
|
1, -1, -1,
|
|
1, 1, -1,
|
|
-1, 1, -1,
|
|
-1, -1, 1,
|
|
1, -1, 1,
|
|
1, 1, 1,
|
|
-1, 1, 1;
|
|
// Define the faces of the cube
|
|
static Eigen::MatrixXi F(12, 3);
|
|
F << 0, 2, 1,
|
|
0, 3, 2,
|
|
4, 5, 6,
|
|
4, 6, 7,
|
|
3, 6, 2,
|
|
3, 7, 6,
|
|
0, 1, 5,
|
|
0, 5, 4,
|
|
5, 1, 2,
|
|
5, 2, 6,
|
|
4, 7, 3,
|
|
4, 3, 0;
|
|
Eigen::Vector3d transform = (v_max + v_min) / 2.0;
|
|
Eigen::Vector3d scaled_factor = (v_max - v_min) / 2.0;
|
|
V = V.array().rowwise() * scaled_factor.transpose().array();
|
|
V = V.array().rowwise() + transform.transpose().array();
|
|
return {V, F};
|
|
|
|
};
|
|
for (int i = 0; i < sp_StaticSim_->DirichletBCs.size(); ++i) {
|
|
auto [V, F] = get_V_F_from_bd_box(sp_StaticSim_->DirichletBCs[i].absMinBBox,
|
|
sp_StaticSim_->DirichletBCs[i].absMaxBBox);
|
|
viewer_.data(i + 1).clear();
|
|
viewer_.data(i + 1).show_lines = false;
|
|
viewer_.data(i + 1).set_mesh(V, F);
|
|
viewer_.data(i + 1).set_colors(Eigen::RowVector4d(0, 0, 0, 0.1));
|
|
viewer_.data(i + 1).is_visible = gui_ctrl_.is_visible_BC;
|
|
|
|
}
|
|
for (int i = 0; i < sp_StaticSim_->NeumannBCs.size(); ++i) {
|
|
auto [V, F] = get_V_F_from_bd_box(sp_StaticSim_->NeumannBCs[i].absMinBBox,
|
|
sp_StaticSim_->NeumannBCs[i].absMaxBBox);
|
|
viewer_.data(i + 1 + sp_StaticSim_->DirichletBCs.size()).clear();
|
|
viewer_.data(i + 1 + sp_StaticSim_->DirichletBCs.size()).show_lines = false;
|
|
viewer_.data(i + 1 + sp_StaticSim_->DirichletBCs.size()).set_mesh(V, F);
|
|
viewer_.data(i + 1 + sp_StaticSim_->DirichletBCs.size()).set_colors(
|
|
Eigen::RowVector4d(0, 0, 0, 0.1));
|
|
viewer_.data(i + 1 + sp_StaticSim_->DirichletBCs.size()).is_visible = gui_ctrl_.is_visible_BC;
|
|
|
|
}
|
|
};
|
|
|
|
if (gui_ctrl_.is_initialized && gui_ctrl_.is_loaded_json) {
|
|
init_mesh();
|
|
load_first_buffer();
|
|
gui_ctrl_.is_initialized = false;
|
|
}
|
|
|
|
if (gui_ctrl_.is_modified) {
|
|
redraw_BC_box();
|
|
redraw_scene();
|
|
gui_ctrl_.is_modified = false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool post_draw_loop() {
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
igl::opengl::glfw::Viewer viewer_;
|
|
igl::opengl::glfw::imgui::ImGuiPlugin plugin_;
|
|
StaticMenu menu_;
|
|
GUICtrl gui_ctrl_;
|
|
std::shared_ptr<ssim::StaticSim> sp_StaticSim_;
|
|
|
|
};
|
|
|
|
} // ssim
|
|
|
|
|
|
#endif //STATICSIMGUI_H
|
|
|