#include #include #include #include #include #include #include #include #include namespace { void ShowHelpMarker(const char* desc) { ImGui::SameLine(); ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(450.0f); ImGui::TextUnformatted(desc); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } } } int main() { Eigen::MatrixXd V1, OrigV; Eigen::MatrixXi F1, OrigF; igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off", OrigV, OrigF); std::cout << "1 View original mesh\n"; std::cout << "2 Switch to deformed mesh\n"; V1 = OrigV; F1 = OrigF; igl::opengl::glfw::Viewer viewer; igl::opengl::glfw::imgui::ImGuiPlugin plugin; viewer.plugins.push_back(&plugin); igl::opengl::glfw::imgui::ImGuiMenu menu; plugin.widgets.push_back(&menu); auto brushRadius = 1.; auto brushType = igl::BrushType::GRAB; auto scale = 1; menu.callback_draw_custom_window = [&]() { ImGui::SetNextWindowPos(ImVec2(180.f * menu.menu_scaling(), 10), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(200, 160), ImGuiCond_FirstUseEver); ImGui::Begin( "Kelvinlet Brushes", nullptr, ImGuiWindowFlags_NoSavedSettings); ImGui::InputDouble("Brush Radius", &brushRadius, 0, 0, "%.4f"); ImGui::Combo("Brush type", reinterpret_cast(&brushType), "Grab\0Scale\0Twist\0Pinch\0\0"); ImGui::InputInt("Falloff", &scale); ShowHelpMarker("Defines how localized the stroke is {1,2,3}"); ImGui::End(); }; Eigen::Vector3d posStart(0, 0, 0); Eigen::Vector3d posEnd; decltype(OrigV) result; auto min_point = V1.colwise().minCoeff(); auto max_point = V1.colwise().maxCoeff(); // to multiply brush force proportional to size of mesh auto brush_strength = (max_point - min_point).norm(); Eigen::Matrix3d twist, pinch; twist << 0, 1, -1, -1, 0, 1, 1, -1, 0; // skew-symmetric pinch << 0, 1, 1, 1, 0, 1, 1, 1, 0; // symmetric viewer.callback_key_down = [&](igl::opengl::glfw::Viewer& viewer, unsigned char key, int) { if (key == '1') { viewer.data().clear(); viewer.data().set_mesh(OrigV, OrigF); viewer.core().align_camera_center(OrigV, OrigF); } else if (key == '2') { viewer.data().clear(); viewer.data().set_mesh(V1, F1); viewer.core().align_camera_center(V1, F1); } return false; }; viewer.callback_mouse_down = [&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool { Eigen::Vector3f bc; int fid; auto x = viewer.current_mouse_x; auto y = viewer.core().viewport(3) - static_cast(viewer.current_mouse_y); if (igl::unproject_onto_mesh(Eigen::Vector2f(x, y), viewer.core().view, viewer.core().proj, viewer.core().viewport, V1, F1, fid, bc)) { posStart = igl::unproject(Eigen::Vector3f(x, y, viewer.down_mouse_z), viewer.core().view, viewer.core().proj, viewer.core().viewport) .template cast(); return true; } return false; }; viewer.callback_mouse_move = [&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool { if (!posStart.isZero() && !posStart.hasNaN()) { posEnd = igl::unproject( Eigen::Vector3f(viewer.current_mouse_x, viewer.core().viewport[3] - static_cast(viewer.current_mouse_y), viewer.down_mouse_z), viewer.core().view, viewer.core().proj, viewer.core().viewport) .template cast(); // exaggerate the force by a little bit Eigen::Vector3d forceVec = (posEnd - posStart) * brush_strength; int scaleFactor = forceVec.norm(); if (posEnd.x() < posStart.x()) { // probably not the best way to determine direction. scaleFactor = -scaleFactor; } Eigen::Matrix3d mat; switch (brushType) { case igl::BrushType::GRAB: mat.setZero(); break; case igl::BrushType::SCALE: mat = Eigen::Matrix3d::Identity() * scaleFactor; break; case igl::BrushType::TWIST: mat = twist * scaleFactor; break; case igl::BrushType::PINCH: mat = pinch * scaleFactor; break; } igl::kelvinlets( V1, posStart, forceVec, mat, igl::KelvinletParams(brushRadius, scale, brushType), result); viewer.data().set_vertices(result); viewer.data().compute_normals(); return true; } return false; }; viewer.callback_mouse_up = [&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool { if (!posStart.isZero()) { V1 = result; posStart.setZero(); return true; } return false; }; viewer.data().set_mesh(V1, F1); viewer.core().align_camera_center(V1, F1); viewer.launch(); }