// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2013 Alec Jacobson // // This Source Code Form is subject to the terms of the Mozilla Public License // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at http://mozilla.org/MPL/2.0/. #ifndef IGL_OPENGL2_MOUSECONTROLLER_H #define IGL_OPENGL2_MOUSECONTROLLER_H // Needs to be included before others #include #include "RotateWidget.h" #include "TranslateWidget.h" #include #include #include // Class for control a skeletal FK rig with the mouse. namespace igl { namespace opengl2 { class MouseController { public: typedef Eigen::VectorXi VectorXb; // Propagate selection to descendants so that selected bones and their // subtrees are all selected. // // Input: // S #S list of whether selected // P #S list of bone parents // Output: // T #S list of whether selected static inline void propogate_to_descendants_if( const VectorXb & S, const Eigen::VectorXi & P, VectorXb & T); // Create a matrix of colors for the selection and their descendants. // // Inputs: // selection #S list of whether a bone is selected // selected_color color for selected bones // unselected_color color for unselected bones // Outputs: // C #P by 4 list of colors static inline void color_if( const VectorXb & S, const Eigen::Vector4f & selected_color, const Eigen::Vector4f & unselected_color, Eigen::MatrixXf & C); enum WidgetMode { WIDGET_MODE_ROTATE = 0, WIDGET_MODE_TRANSLATE = 1, NUM_WIDGET_MODES = 2, }; private: // m_is_selecting whether currently selecting // m_selection #m_rotations list of whether a bone is selected // m_down_x x-coordinate of mouse location at down // m_down_y y-coordinate 〃 // m_drag_x x-coordinate of mouse location at drag // m_drag_y y-coordinate 〃 // m_widget rotation widget for selected bone // m_width width of containing window // m_height height 〃 // m_rotations list of rotations for each bone // m_rotations_at_selection list of rotations for each bone at time of // selection // m_translations list of translations for each bone // m_fk_rotations_at_selection list of rotations for each bone at time of // selection // m_root_enabled Whether root is enabled bool m_is_selecting; VectorXb m_selection; int m_down_x,m_down_y,m_drag_x,m_drag_y; int m_width,m_height; igl::opengl2::RotateWidget m_widget; igl::opengl2::TranslateWidget m_trans_widget; Eigen::Quaterniond m_widget_rot_at_selection; //Eigen::Vector3d m_trans_widget_trans_at_selection; typedef std::vector< Eigen::Quaterniond, Eigen::aligned_allocator > RotationList; typedef std::vector< Eigen::Vector3d > TranslationList; RotationList m_rotations, m_rotations_at_selection, m_fk_rotations_at_selection, m_parent_rotations_at_selection; TranslationList m_translations, m_translations_at_selection, m_fk_translations_at_selection; bool m_root_enabled; WidgetMode m_widget_mode; public: MouseController(); // Returns const reference to m_selection inline const VectorXb & selection() const{return m_selection;}; // 〃 m_is_selecting inline const bool & is_selecting() const{return m_is_selecting;} inline bool is_widget_down() const{return m_widget.is_down();} inline bool is_trans_widget_down() const{return m_trans_widget.is_down();} // 〃 m_rotations inline const RotationList & rotations() const{return m_rotations;} inline const TranslationList & translations() const{return m_translations;} // Returns non-const reference to m_root_enabled inline bool & root_enabled(){ return m_root_enabled;} inline void reshape(const int w, const int h); // Process down, drag, up mouse events // // Inputs: // x x-coordinate of mouse click with respect to container // y y-coordinate 〃 // Returns true if accepted (action taken). inline bool down(const int x, const int y); inline bool drag(const int x, const int y); inline bool up(const int x, const int y); // Draw selection box and widget inline void draw() const; // Set `m_selection` based on the last drag selection and initialize // widget. // // Inputs: // C #C by dim list of joint positions at rest // BE #BE by 2 list of bone indices at rest // P #P list of bone parents inline void set_selection_from_last_drag( const Eigen::MatrixXd & C, const Eigen::MatrixXi & BE, const Eigen::VectorXi & P, const Eigen::VectorXi & RP); // Set from explicit selection inline void set_selection( const Eigen::VectorXi & S, const Eigen::MatrixXd & C, const Eigen::MatrixXi & BE, const Eigen::VectorXi & P, const Eigen::VectorXi & RP); // Set size of skeleton // // Inputs: // n number of bones inline void set_size(const int n); // Resets m_rotation elements to identity inline void reset(); inline void reset_selected(); inline void reset_rotations(); inline void reset_selected_rotations(); inline void reset_translations(); inline void reset_selected_translations(); inline bool set_rotations(const RotationList & vQ); inline bool set_translations(const TranslationList & vT); // Sets all entries in m_selection to false inline void clear_selection(); // Returns true iff some element in m_selection is true inline bool any_selection() const; inline void set_widget_mode(const WidgetMode & mode); public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; } } // Implementation #include "../line_segment_in_rectangle.h" #include "draw_rectangular_marquee.h" #include "project.h" #include "../forward_kinematics.h" #include #include #include inline void igl::opengl2::MouseController::propogate_to_descendants_if( const VectorXb & S, const Eigen::VectorXi & P, VectorXb & T) { using namespace std; const int n = S.rows(); assert(P.rows() == n); // dynamic programming T = S; vector seen(n,false); // Recursively look up chain and see if ancestor is selected const function look_up = [&](int e) -> bool { if(e==-1) { return false; } if(!seen[e]) { seen[e] = true; T(e) |= look_up(P(e)); } return T(e); }; for(int e = 0;e > vQ; vector vT; forward_kinematics(C,BE,P,m_rotations,m_translations,vQ,vT); // Loop over deformed bones for(int e = 0;e > & vQ = m_fk_rotations_at_selection; vector & vT = m_fk_translations_at_selection; forward_kinematics(C,BE,P,m_rotations,m_translations,vQ,vT); m_parent_rotations_at_selection.resize( m_rotations.size(),Quaterniond::Identity()); for(size_t r = 0;r=0) { m_parent_rotations_at_selection[r] = vQ[P(r)]; } } if(&m_selection != &S) { m_selection = S; } assert(m_selection.rows() == BE.rows()); assert(BE.rows() == P.rows()); assert(BE.rows() == RP.rows()); // Zero-out S up a path of ones from e auto propagate = [&](const int e, const VectorXb & S, VectorXb & N) { if(S(e)) { int f = e; while(true) { int p = P(f); if(p==-1||!S(p)) { break; } N(f) = false; f = p; } } }; VectorXb prev_selection = m_selection; // Combine upward, group rigid parts, repeat while(true) { // Spread selection across rigid pieces VectorXb SRP(VectorXb::Zero(RP.maxCoeff()+1)); for(int e = 0;e