MouseController.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public License
  6. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. // obtain one at http://mozilla.org/MPL/2.0/.
  8. #ifndef IGL_OPENGL2_MOUSECONTROLLER_H
  9. #define IGL_OPENGL2_MOUSECONTROLLER_H
  10. // Needs to be included before others
  11. #include <Eigen/StdVector>
  12. #include "RotateWidget.h"
  13. #include <Eigen/Core>
  14. #include <Eigen/Geometry>
  15. #include <vector>
  16. // Class for control a skeletal FK rig with the mouse.
  17. namespace igl
  18. {
  19. namespace opengl2
  20. {
  21. class MouseController
  22. {
  23. public:
  24. typedef Eigen::VectorXi VectorXb;
  25. // Propogate selection to descendants so that selected bones and their
  26. // subtrees are all selected.
  27. //
  28. // Input:
  29. // S #S list of whether selected
  30. // P #S list of bone parents
  31. // Output:
  32. // T #S list of whether selected
  33. static inline void propogate_to_descendants_if(
  34. const VectorXb & S,
  35. const Eigen::VectorXi & P,
  36. VectorXb & T);
  37. // Create a matrix of colors for the selection and their descendants.
  38. //
  39. // Inputs:
  40. // selection #S list of whether a bone is selected
  41. // selected_color color for selected bones
  42. // unselected_color color for unselected bones
  43. // Outputs:
  44. // C #P by 4 list of colors
  45. static inline void color_if(
  46. const VectorXb & S,
  47. const Eigen::Vector4f & selected_color,
  48. const Eigen::Vector4f & unselected_color,
  49. Eigen::MatrixXf & C);
  50. private:
  51. // m_is_selecting whether currently selecting
  52. // m_selection #m_rotations list of whether a bone is selected
  53. // m_down_x x-coordinate of mouse location at down
  54. // m_down_y y-coordinate 〃
  55. // m_drag_x x-coordinate of mouse location at drag
  56. // m_drag_y y-coordinate 〃
  57. // m_widget rotation widget for selected bone
  58. // m_width width of containing window
  59. // m_height height 〃
  60. // m_rotations list of rotations for each bone
  61. // m_rotations_at_selection list of rotations for each bone at time of
  62. // selection
  63. // m_fk_rotations_at_selection list of rotations for each bone at time of
  64. // selection
  65. // m_root_enabled Whether root is enabled
  66. bool m_is_selecting;
  67. VectorXb m_selection;
  68. int m_down_x,m_down_y,m_drag_x,m_drag_y;
  69. int m_width,m_height;
  70. igl::opengl2::RotateWidget m_widget;
  71. Eigen::Quaterniond m_widget_rot_at_selection;
  72. typedef std::vector<
  73. Eigen::Quaterniond,
  74. Eigen::aligned_allocator<Eigen::Quaterniond> > RotationList;
  75. RotationList
  76. m_rotations,m_rotations_at_selection,m_fk_rotations_at_selection;
  77. bool m_root_enabled;
  78. public:
  79. MouseController();
  80. // Returns const reference to m_selection
  81. inline const VectorXb & selection() const{return m_selection;};
  82. // 〃 m_is_selecting
  83. inline const bool & is_selecting() const{return m_is_selecting;}
  84. inline bool is_widget_down() const{return m_widget.is_down();}
  85. // 〃 m_rotations
  86. inline const RotationList & rotations() const{return m_rotations;}
  87. // Returns non-const reference to m_root_enabled
  88. inline bool & root_enabled(){ return m_root_enabled;}
  89. inline void reshape(const int w, const int h);
  90. // Process down, drag, up mouse events
  91. //
  92. // Inputs:
  93. // x x-coordinate of mouse click with respect to container
  94. // y y-coordinate 〃
  95. // Returns true if accepted (action taken).
  96. inline bool down(const int x, const int y);
  97. inline bool drag(const int x, const int y);
  98. inline bool up(const int x, const int y);
  99. // Draw selection box and widget
  100. inline void draw() const;
  101. // Set `m_selection` based on the last drag selection and initialize
  102. // widget.
  103. //
  104. // Inputs:
  105. // C #C by dim list of joint positions at rest
  106. // BE #BE by 2 list of bone indices at rest
  107. // P #P list of bone parents
  108. inline void set_selection_from_last_drag(
  109. const Eigen::MatrixXd & C,
  110. const Eigen::MatrixXi & BE,
  111. const Eigen::VectorXi & P,
  112. const Eigen::VectorXi & RP);
  113. // Set from explicit selection
  114. inline void set_selection(
  115. const Eigen::VectorXi & S,
  116. const Eigen::MatrixXd & C,
  117. const Eigen::MatrixXi & BE,
  118. const Eigen::VectorXi & P,
  119. const Eigen::VectorXi & RP);
  120. // Set size of skeleton
  121. //
  122. // Inputs:
  123. // n number of bones
  124. inline void set_size(const int n);
  125. // Resets m_rotation elements to identity
  126. inline void reset_rotations();
  127. inline void reset_selected_rotations();
  128. inline bool set_rotations(const RotationList & vQ);
  129. // Sets all entries in m_selection to false
  130. inline void clear_selection();
  131. // Returns true iff some element in m_selection is true
  132. inline bool any_selection() const;
  133. public:
  134. EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  135. };
  136. }
  137. }
  138. // Implementation
  139. #include "../line_segment_in_rectangle.h"
  140. #include "draw_rectangular_marquee.h"
  141. #include "project.h"
  142. #include "../forward_kinematics.h"
  143. #include "../matlab_format.h"
  144. #include "../any_of.h"
  145. #include <iostream>
  146. #include <algorithm>
  147. #include <functional>
  148. inline void igl::opengl2::MouseController::propogate_to_descendants_if(
  149. const VectorXb & S,
  150. const Eigen::VectorXi & P,
  151. VectorXb & T)
  152. {
  153. using namespace std;
  154. const int n = S.rows();
  155. assert(P.rows() == n);
  156. // dynamic programming
  157. T = S;
  158. vector<bool> seen(n,false);
  159. // Recursively look up chain and see if ancestor is selected
  160. const function<bool(int)> look_up = [&](int e) -> bool
  161. {
  162. if(e==-1)
  163. {
  164. return false;
  165. }
  166. if(!seen[e])
  167. {
  168. seen[e] = true;
  169. T(e) |= look_up(P(e));
  170. }
  171. return T(e);
  172. };
  173. for(int e = 0;e<n;e++)
  174. {
  175. if(!seen[e])
  176. {
  177. T(e) = look_up(e);
  178. }
  179. }
  180. }
  181. inline void igl::opengl2::MouseController::color_if(
  182. const VectorXb & S,
  183. const Eigen::Vector4f & selected_color,
  184. const Eigen::Vector4f & unselected_color,
  185. Eigen::MatrixXf & C)
  186. {
  187. C.resize(S.rows(),4);
  188. for(int e=0;e<S.rows();e++)
  189. {
  190. C.row(e) = S(e)?selected_color:unselected_color;
  191. }
  192. }
  193. inline igl::opengl2::MouseController::MouseController():
  194. m_is_selecting(false),
  195. m_selection(),
  196. m_down_x(-1),m_down_y(-1),m_drag_x(-1),m_drag_y(-1),
  197. m_width(-1),m_height(-1),
  198. m_widget(),
  199. m_widget_rot_at_selection(),
  200. m_rotations(),
  201. m_rotations_at_selection(),
  202. m_root_enabled(true)
  203. {
  204. }
  205. inline void igl::opengl2::MouseController::reshape(const int w, const int h)
  206. {
  207. m_width = w;
  208. m_height = h;
  209. }
  210. inline bool igl::opengl2::MouseController::down(const int x, const int y)
  211. {
  212. using namespace std;
  213. m_down_x = m_drag_x =x;
  214. m_down_y = m_drag_y =y;
  215. const bool widget_down = any_selection() && m_widget.down(x,m_height-y);
  216. if(!widget_down)
  217. {
  218. m_is_selecting = true;
  219. }
  220. return m_is_selecting || widget_down;
  221. }
  222. inline bool igl::opengl2::MouseController::drag(const int x, const int y)
  223. {
  224. using namespace std;
  225. using namespace Eigen;
  226. m_drag_x = x;
  227. m_drag_y = y;
  228. if(m_is_selecting)
  229. {
  230. return m_is_selecting;
  231. }else
  232. {
  233. if(!m_widget.drag(x,m_height-y))
  234. {
  235. return false;
  236. }
  237. assert(any_selection());
  238. assert(m_selection.size() == (int)m_rotations.size());
  239. for(int e = 0;e<m_selection.size();e++)
  240. {
  241. if(m_selection(e))
  242. {
  243. // Let:
  244. // w.θr = w.θ ⋅ w.θ₀*
  245. // w.θr takes (absolute) frame of w.θ₀ to w.θ:
  246. // w.θ = w.θr ⋅ w.θ₀
  247. // Define:
  248. // w.θ₀ = θfk ⋅ θx,
  249. // the absolute rotation of the x axis to the deformed bone at
  250. // selection. Likewise,
  251. // w.θ = θfk' ⋅ θx,
  252. // the current absolute rotation of the x axis to the deformed bone.
  253. // Define recursively:
  254. // θfk = θfk(p) ⋅ Θr,
  255. // then because we're only changeing this relative rotation
  256. // θfk' = θfk(p) ⋅ Θr ⋅ θr* ⋅ θr'
  257. // θfk' = θfk ⋅ θr* ⋅ θr'
  258. // w.θ ⋅ θx* = θfk ⋅ θr* ⋅ θr'
  259. // θr ⋅ θfk* ⋅ w.θ ⋅ θx* = θr'
  260. // θr ⋅ θfk* ⋅ w.θr ⋅ w.θ₀ ⋅ θx* = θr'
  261. // θr ⋅ θfk* ⋅ w.θr ⋅ θfk ⋅θx ⋅ θx* = θr'
  262. // θr ⋅ θfk* ⋅ w.θr ⋅ θfk = θr'
  263. // which I guess is the right multiply change after being changed to
  264. // the bases of θfk, the rotation of the bone relative to its rest
  265. // frame.
  266. //
  267. const Quaterniond & frame = m_fk_rotations_at_selection[e];
  268. m_rotations[e] =
  269. m_rotations_at_selection[e] *
  270. frame.conjugate() *
  271. (m_widget.rot*m_widget_rot_at_selection.conjugate()) *
  272. frame;
  273. }
  274. }
  275. return true;
  276. }
  277. }
  278. inline bool igl::opengl2::MouseController::up(const int x, const int y)
  279. {
  280. m_is_selecting = false;
  281. m_widget.up(x,m_height-y);
  282. return false;
  283. }
  284. inline void igl::opengl2::MouseController::draw() const
  285. {
  286. using namespace igl;
  287. if(any_selection())
  288. {
  289. m_widget.draw();
  290. }
  291. if(m_is_selecting)
  292. {
  293. // Remember settings
  294. GLboolean dt;
  295. glGetBooleanv(GL_DEPTH_TEST,&dt);
  296. int old_vp[4];
  297. glGetIntegerv(GL_VIEWPORT,old_vp);
  298. // True screen space
  299. glViewport(0,0,m_width,m_height);
  300. glMatrixMode(GL_PROJECTION);
  301. glPushMatrix();
  302. glLoadIdentity();
  303. gluOrtho2D(0,m_width,0,m_height);
  304. glMatrixMode(GL_MODELVIEW);
  305. glPushMatrix();
  306. glLoadIdentity();
  307. glDisable(GL_DEPTH_TEST);
  308. draw_rectangular_marquee(
  309. m_down_x,
  310. m_height-m_down_y,
  311. m_drag_x,
  312. m_height-m_drag_y);
  313. // Restore settings
  314. glMatrixMode(GL_PROJECTION);
  315. glPopMatrix();
  316. glMatrixMode(GL_MODELVIEW);
  317. glPopMatrix();
  318. glViewport(old_vp[0],old_vp[1],old_vp[2],old_vp[3]);
  319. dt?glEnable(GL_DEPTH_TEST):glDisable(GL_DEPTH_TEST);
  320. }
  321. }
  322. inline void igl::opengl2::MouseController::set_selection_from_last_drag(
  323. const Eigen::MatrixXd & C,
  324. const Eigen::MatrixXi & BE,
  325. const Eigen::VectorXi & P,
  326. const Eigen::VectorXi & RP)
  327. {
  328. using namespace Eigen;
  329. using namespace std;
  330. using namespace igl;
  331. m_rotations_at_selection = m_rotations;
  332. assert(BE.rows() == P.rows());
  333. m_selection = VectorXb::Zero(BE.rows());
  334. // m_rotation[e] is the relative rotation stored at bone e (as seen by the
  335. // joint traveling with its parent)
  336. // vQ[e] is the absolute rotation of a bone at rest to its current position:
  337. // vQ[e] = vQ[p(e)] * m_rotation[e]
  338. vector<Quaterniond,aligned_allocator<Quaterniond> > vQ;
  339. vector<Vector3d> vT;
  340. forward_kinematics(C,BE,P,m_rotations,vQ,vT);
  341. // Loop over deformed bones
  342. for(int e = 0;e<BE.rows();e++)
  343. {
  344. Affine3d a = Affine3d::Identity();
  345. a.translate(vT[e]);
  346. a.rotate(vQ[e]);
  347. Vector3d s = a * (Vector3d)C.row(BE(e,0));
  348. Vector3d d = a * (Vector3d)C.row(BE(e,1));
  349. Vector3d projs = project(s);
  350. Vector3d projd = project(d);
  351. m_selection(e) = line_segment_in_rectangle(
  352. projs.head(2),projd.head(2),
  353. Vector2d(m_down_x,m_height-m_down_y),
  354. Vector2d(m_drag_x,m_height-m_drag_y));
  355. }
  356. return set_selection(m_selection,C,BE,P,RP);
  357. }
  358. inline void igl::opengl2::MouseController::set_selection(
  359. const Eigen::VectorXi & S,
  360. const Eigen::MatrixXd & C,
  361. const Eigen::MatrixXi & BE,
  362. const Eigen::VectorXi & P,
  363. const Eigen::VectorXi & RP)
  364. {
  365. using namespace igl;
  366. using namespace Eigen;
  367. using namespace std;
  368. vector<Quaterniond,aligned_allocator<Quaterniond> > & vQ =
  369. m_fk_rotations_at_selection;
  370. vector<Vector3d> vT;
  371. forward_kinematics(C,BE,P,m_rotations,vQ,vT);
  372. if(&m_selection != &S)
  373. {
  374. m_selection = S;
  375. }
  376. assert(m_selection.rows() == BE.rows());
  377. assert(BE.rows() == P.rows());
  378. assert(BE.rows() == RP.rows());
  379. // Zero-out S up a path of ones from e
  380. auto propagate = [&](const int e, const VectorXb & S, VectorXb & N)
  381. {
  382. if(S(e))
  383. {
  384. int f = e;
  385. while(true)
  386. {
  387. int p = P(f);
  388. if(p==-1||!S(p))
  389. {
  390. break;
  391. }
  392. N(f) = false;
  393. f = p;
  394. }
  395. }
  396. };
  397. VectorXb prev_selection = m_selection;
  398. // Combine upward, group rigid parts, repeat
  399. while(true)
  400. {
  401. // Spread selection accross rigid pieces
  402. VectorXb SRP(VectorXb::Zero(RP.maxCoeff()+1));
  403. for(int e = 0;e<BE.rows();e++)
  404. {
  405. SRP(RP(e)) |= m_selection(e);
  406. }
  407. for(int e = 0;e<BE.rows();e++)
  408. {
  409. m_selection(e) = SRP(RP(e));
  410. }
  411. // Clear selections below m_selection ancestors
  412. VectorXb new_selection = m_selection;
  413. for(int e = 0;e<P.rows();e++)
  414. {
  415. propagate(e,m_selection,new_selection);
  416. }
  417. m_selection = new_selection;
  418. if(m_selection==prev_selection)
  419. {
  420. break;
  421. }
  422. prev_selection = m_selection;
  423. }
  424. // Now selection should contain just bone roots of m_selection subtrees
  425. if(any_of(m_selection))
  426. {
  427. // Taking average
  428. m_widget.pos.setConstant(0);
  429. m_widget_rot_at_selection.coeffs().setConstant(0);
  430. m_widget.rot.coeffs().array().setConstant(0);
  431. Quaterniond cur_rot(0,0,0,0);
  432. int num_selection = 0;
  433. // Compute average widget for selection
  434. for(int e = 0;e<BE.rows();e++)
  435. {
  436. if(m_selection(e))
  437. {
  438. Vector3d s = C.row(BE(e,0));
  439. Vector3d d = C.row(BE(e,1));
  440. auto b = (d-s).transpose().eval();
  441. {
  442. Affine3d a = Affine3d::Identity();
  443. a.translate(vT[e]);
  444. a.rotate(vQ[e]);
  445. m_widget.pos += a*s;
  446. }
  447. // Rotation of x axis to this bone
  448. Quaterniond rot_at_bind;
  449. rot_at_bind.setFromTwoVectors(Vector3d(1,0,0),b);
  450. const Quaterniond abs_rot = vQ[e] * rot_at_bind;
  451. m_widget_rot_at_selection.coeffs() += abs_rot.coeffs();
  452. num_selection++;
  453. }
  454. }
  455. // Take average
  456. m_widget.pos.array() /= (double)num_selection;
  457. m_widget_rot_at_selection.coeffs().array() /= (double)num_selection;
  458. m_widget_rot_at_selection.normalize();
  459. m_widget.rot = m_widget_rot_at_selection;
  460. }
  461. m_widget.m_is_enabled = true;
  462. for(int s = 0;s<m_selection.rows();s++)
  463. {
  464. // a root is selected then disable.
  465. if(!m_root_enabled && m_selection(s) && P(s) == -1)
  466. {
  467. m_widget.m_is_enabled = false;
  468. break;
  469. }
  470. }
  471. }
  472. inline void igl::opengl2::MouseController::set_size(const int n)
  473. {
  474. using namespace Eigen;
  475. clear_selection();
  476. m_rotations.clear();
  477. m_rotations.resize(n,Quaterniond::Identity());
  478. m_selection = VectorXb::Zero(n);
  479. }
  480. inline void igl::opengl2::MouseController::reset_rotations()
  481. {
  482. using namespace Eigen;
  483. using namespace std;
  484. fill(m_rotations.begin(),m_rotations.end(),Quaterniond::Identity());
  485. // cop out. just clear selection
  486. clear_selection();
  487. }
  488. inline void igl::opengl2::MouseController::reset_selected_rotations()
  489. {
  490. using namespace Eigen;
  491. for(int e = 0;e<m_selection.size();e++)
  492. {
  493. if(m_selection(e))
  494. {
  495. m_rotations[e] = Quaterniond::Identity();
  496. }
  497. }
  498. }
  499. inline bool igl::opengl2::MouseController::set_rotations(const RotationList & vQ)
  500. {
  501. if(vQ.size() != m_rotations.size())
  502. {
  503. return false;
  504. }
  505. assert(!any_selection());
  506. m_rotations = vQ;
  507. return true;
  508. }
  509. inline void igl::opengl2::MouseController::clear_selection()
  510. {
  511. m_selection.setConstant(false);
  512. }
  513. inline bool igl::opengl2::MouseController::any_selection() const
  514. {
  515. return igl::any_of(m_selection);
  516. }
  517. #endif