Developer Documentation
ManipulatorNode.cc
1 /*===========================================================================*\
2  * *
3  * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40 \*===========================================================================*/
41 
42 
43 
44 
45 
46 
47 //=============================================================================
48 //
49 // CLASS ManipulatorNode - IMPLEMENTATION
50 //
51 //=============================================================================
52 
53 
54 //== INCLUDES =================================================================
55 
56 
57 #include "ManipulatorNode.hh"
58 
59 //== NAMESPACES ===============================================================
60 
61 
62 namespace ACG {
63 namespace SceneGraph {
64 
65 
66 //== IMPLEMENTATION ==========================================================
67 
68 
69 const Vec4f cylinder_color (0.8f, 0.4f, 0.4f, 1.0f);
70 const Vec4f sphere_color (0.8f, 0.4f, 0.4f, 1.0f);
71 const Vec4f select_color (1.0f, 0.1f, 0.1f, 1.0f);
72 const float SCALE_CONST = 5.0f;
73 
74 
75 //----------------------------------------------------------------------------
76 
77 
79 ManipulatorNode( BaseNode* _parent, const std::string& _name )
80  : TransformNode(_parent, _name),
81  draw_cylinder_(false),
82  direction_(1.0,0.0,0.0),
83  cylinder_(0),
84  cylinder_radius_(1),
85  cylinder_height_(10),
86  cylinder_slices_(30),
87  cylinder_stacks_(10),
88  cylinder_clicked_(false),
89  sphere_clicked_(false),
90  touched_(false)
91 {
92 }
93 
94 
95 //----------------------------------------------------------------------------
96 
97 
100 {
101  if (cylinder_)
102  delete cylinder_;
103 }
104 
105 
106 //----------------------------------------------------------------------------
107 
108 
109 void
112 {
113  direction_ = rotation().transform_vector( direction_ );
115 }
116 
117 
118 //----------------------------------------------------------------------------
119 
120 
121 void
122 ManipulatorNode::
123 setup_cylinder_system(GLState& _state)
124 {
125  _state.translate(center()[0], center()[1], center()[2]);
126  _state.mult_matrix(inverse_scale (), scale ()); // Adapt scaling
127 
128  // rotation axis & angle
129  Vec3d z(0.0, 0.0, 1.0);
130  double scal_prod = (direction_ | z);
131  Vec3d axis = z % direction_;
132  double norm = axis.norm();
133  double angle;
134 
135  if (norm > FLT_MIN)
136  {
137  axis /= norm;
138  if (scal_prod > 1.0) scal_prod = 1.0;
139  if (scal_prod < -1.0) scal_prod = -1.0;
140  angle = 180.0 / M_PI * acos(scal_prod);
141  }
142  else
143  {
144  axis = Vec3d(1.0, 0.0, 0.0);
145  angle = (scal_prod > 0.0) ? 0.0 : 180.0;
146  }
147 
148  _state.rotate(angle, axis[0], axis[1], axis[2]);
149 }
150 
151 
152 void
153 ManipulatorNode::
154 setup_sphere_system(GLState& _state)
155 {
156  setup_cylinder_system(_state);
157 
158  _state.translate(0, 0, cylinder_height_+ 2*cylinder_radius_);
159 }
160 
161 
162 //----------------------------------------------------------------------------
163 
164 
165 void
166 ManipulatorNode::draw(GLState& _state, const DrawModes::DrawMode& /* _drawMode */ )
167 {
168  if (draw_cylinder_)
169  {
170  if(_state.compatibilityProfile())
171  {
172  ACG::GLState::enable(GL_LIGHTING);
173  ACG::GLState::shadeModel(GL_FLAT);
174  }
175 
176 
177  // backup colors
178  Vec4f backup_diffuse = _state.diffuse_color();
179  Vec4f backup_specular = _state.specular_color();
180 
181 
182  // draw cylinder
183 
184  if (!cylinder_)
185  {
186  cylinder_ = new GLCylinder(cylinder_slices_, cylinder_stacks_, cylinder_radius_, false, false);
187  }
188 
189  _state.push_modelview_matrix();
190  setup_cylinder_system(_state);
191 
192  if( cylinder_clicked_)
193  {
194  _state.set_diffuse_color(select_color * 0.6f);
195  _state.set_specular_color(select_color * 0.8f);
196  }
197  else
198  {
199  _state.set_diffuse_color(cylinder_color * 0.6f);
200  _state.set_specular_color(cylinder_color * 0.8f);
201  }
202 
203  // Zylinder in die X-Achse
204  /*glPushMatrix();
205  glLoadIdentity();
206  glRotatef(-90, 0.0, 1.0, 0.0);*/
207 
208  if(_state.compatibilityProfile())
209  ACG::GLState::shadeModel(GL_SMOOTH);
210  cylinder_->setBottomRadius(cylinder_radius_);
211  cylinder_->setTopRadius(cylinder_radius_);
212  cylinder_->draw(_state, cylinder_height_);
213 
214  //glPopMatrix();
215 
216 
217  // BIG wireframe sphere
218  if( sphere_clicked_ )
219  {
220  _state.set_diffuse_color(select_color * 0.6f);
221  _state.set_specular_color(select_color * 0.0f);
222  if(_state.compatibilityProfile())
223  {
224  ACG::GLState::shadeModel(GL_SMOOTH);
225  GLint previous[2];
226  glGetIntegerv(GL_POLYGON_MODE,previous);
227  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
228  _state.scale(cylinder_height_+4*cylinder_radius_,cylinder_height_+4*cylinder_radius_,cylinder_height_+4*cylinder_radius_);
229  ACG::GLSphere sphere(20, 20);
230  sphere.draw_primitive();
231  glPolygonMode(GL_FRONT,previous[0]);
232  glPolygonMode(GL_BACK,previous[1]);
233  }
234  }
235 
236  _state.pop_modelview_matrix();
237 
238 
239 
240 
241  // draw sphere
242 
243  _state.push_modelview_matrix();
244  setup_sphere_system(_state);
245 
246  if( sphere_clicked_)
247  {
248  _state.set_diffuse_color(select_color * 0.6f);
249  _state.set_specular_color(select_color * 0.8f);
250  }
251  else
252  {
253  _state.set_diffuse_color(sphere_color * 0.6f);
254  _state.set_specular_color(sphere_color * 0.8f);
255  }
256 
257  if(_state.compatibilityProfile())
258  {
259  ACG::GLState::shadeModel(GL_SMOOTH);
260  _state.scale(2*cylinder_radius_,2*cylinder_radius_,2*cylinder_radius_);
261  ACG::GLSphere sphere(20, 20);
262  sphere.draw_primitive();
263  }
264 
265  _state.pop_modelview_matrix();
266 
267 
268 
269 
270  // restore
271  _state.set_diffuse_color(backup_diffuse);
272  _state.set_specular_color(backup_specular);
273  }
274 }
275 
276 
277 //----------------------------------------------------------------------------
278 
279 
280 void
281 ManipulatorNode::mouseEvent(GLState& _state, QMouseEvent* _event)
282 {
283  Vec3d oldPoint3D;
284  Vec2i newPoint2D(_event->pos().x(), _event->pos().y());
285  Vec3d newPoint3D;
286  double new_axis_hit;
287 
288 
289  switch (_event->type())
290  {
291  case QEvent::MouseButtonPress:
292  {
293  // hit sphere ?
294  sphere_clicked_ = hitSphere(_state, newPoint2D);
295 
296  // hit cylinder ?
297  cylinder_clicked_ = mapToCylinder(_state, newPoint2D, new_axis_hit);
298 
299  // If the user clicked on the manipulator, remember it
300  if ( sphere_clicked_ || cylinder_clicked_)
301  touched_ = true;
302 
303  // select only sphere or cylinder
304  if(sphere_clicked_ && cylinder_clicked_)
305  cylinder_clicked_ = false;
306 
307  oldPoint2D_ = newPoint2D;
308  break;
309  }
310 
311 
312  case QEvent::MouseButtonRelease:
313  {
314  sphere_clicked_ = false;
315  cylinder_clicked_ = false;
316  break;
317  }
318 
319 
320  case QEvent::MouseButtonDblClick:
321  {
322  draw_cylinder_ = !draw_cylinder_;
323  break;
324  }
325 
326 
327  case QEvent::MouseMove:
328  {
329 
330  // IF sphere clicked rotate or change direction
331  if(sphere_clicked_)
332  {
333  bool hit0 = mapToSphere(_state, newPoint2D, newPoint3D);
334  bool hit1 = mapToSphere(_state, oldPoint2D_, oldPoint3D);
335 
336  if ( (newPoint2D != oldPoint2D_) && hit0 && hit1)
337  {
338  // change direction
339  if(_event->modifiers() & Qt::ShiftModifier)
340  {
341 
342  // calculate new cylinder direction
343  direction_.normalize();
344  newPoint3D.normalize();
345  oldPoint3D.normalize();
346 
347  direction_ += newPoint3D - oldPoint3D;
348 
349  }
350  // rotate
351  else
352  {
353 
354  Vec3d axis = oldPoint3D % newPoint3D;
355 
356  double cos_angle = ( oldPoint3D | newPoint3D );
357 
358  if (fabs(cos_angle) < 1.0)
359  rotate(acos(cos_angle)*180.0/M_PI, axis);
360  }
361  }
362  else sphere_clicked_ = false;
363  }
364 
365 
366  // cylinder clicked change scaling or change translation
367  if(cylinder_clicked_)
368  {
369  double old_axis_hit;
370 
371  mapToCylinder(_state, oldPoint2D_, old_axis_hit);
372  mapToCylinder(_state, newPoint2D, new_axis_hit);
373 
374  // scale
375  if(_event->modifiers() & Qt::ShiftModifier)
376  {
377  scale(1.0 + (new_axis_hit - old_axis_hit) /
378  (cylinder_height_ * SCALE_CONST));
379  }
380 
381  // twist
382  else if(_event->modifiers() & (Qt::ControlModifier |
383  Qt::AltModifier))
384  {
385  rotate( 45.0 * (new_axis_hit-old_axis_hit) / cylinder_height_,
386  direction_);
387 
388  }
389 
390  // translate
391  else
392  {
393  translate(new_axis_hit - old_axis_hit);
394  }
395  }
396 
397  break;
398  }
399 
400  default: // avoid warning
401  break;
402  }
403 
404 
405  // save old Point
406  oldPoint2D_ = newPoint2D;
407 }
408 
409 
410 //----------------------------------------------------------------------------
411 
412 
413 bool ManipulatorNode::hitSphere( GLState& _state,
414  const Vec2i& _v2)
415 {
416  // Qt -> GL coordinate systems
417  unsigned int x = _v2[0];
418  unsigned int y = _state.context_height() - _v2[1];
419 
420 
421  // get ray from eye through pixel, in sphere coords
422  Vec3d origin, direction;
423 
424  _state.set_updateGL(false);
425  _state.push_modelview_matrix();
426 
427  setup_sphere_system(_state);
428  _state.scale(2*cylinder_radius_);
429 
430  _state.viewing_ray(x, y, origin, direction);
431 
432  _state.pop_modelview_matrix();
433  _state.set_updateGL(true);
434 
435 
436 
437  // calc sphere-ray intersection
438  // (sphere is centered at origin, has radius 1)
439  double a = direction.sqrnorm(),
440  b = 2.0 * (origin | direction),
441  c = origin.sqrnorm() - 1.0,
442  d = b*b - 4.0*a*c;
443 
444  return (d >= 0.0);
445 }
446 
447 
448 //----------------------------------------------------------------------------
449 
450 
451 bool
452 ManipulatorNode::mapToSphere( GLState& _state,
453  const Vec2i& _v2,
454  Vec3d& _v3 )
455 {
456  // Qt -> GL coordinate systems
457  unsigned int x = _v2[0];
458  unsigned int y = _state.context_height() - _v2[1];
459 
460 
461 
462  // get ray from eye through pixel (trackball coords)
463  // *no* rotation, points have to be in world coords
464  Vec3d origin, direction;
465 
466  _state.set_updateGL(false);
467  _state.push_modelview_matrix();
468 
469  _state.translate(center()[0], center()[1], center()[2]);
470  _state.mult_matrix(inverse_scale (), scale ());
471  _state.scale(cylinder_height_ + 4*cylinder_radius_);
472 
473  _state.viewing_ray(x, y, origin, direction);
474 
475  _state.pop_modelview_matrix();
476  _state.set_updateGL(true);
477 
478 
479 
480  // calc sphere-ray intersection
481  // (sphere is centered at origin, has radius 1)
482  double a = direction.sqrnorm(),
483  b = 2.0 * (origin | direction),
484  c = origin.sqrnorm() - 1.0,
485  d = b*b - 4.0*a*c,
486  t;
487 
488  if (d < 0.0) return false;
489  else if (d == 0.0) t = -b / (2.0*a);
490  else
491  {
492  a = 1.0 / (2.0*a);
493  d = sqrt(d);
494  double t1 = (-b - d) * a;
495  double t2 = (-b + d) * a;
496  t = (t1 < t2) ? t1 : t2;
497  }
498 
499 
500 
501  // map back to world coords
502  _v3 = origin + direction*t;
503 
504  return true;
505 }
506 
507 
508 //----------------------------------------------------------------------------
509 
510 
511 bool
512 ManipulatorNode::mapToCylinder(GLState& _state,
513  const Vec2i& _v1,
514  double& _axis_hit)
515 {
516  // Qt -> GL coordinate systems
517  unsigned int x = _v1[0];
518  unsigned int y = _state.context_height() - _v1[1];
519 
520 
521  // get ray from eye through pixel (cylinder coords)
522  Vec3d origin, direction;
523 
524  _state.set_updateGL(false);
525  _state.push_modelview_matrix();
526 
527  setup_cylinder_system(_state);
528 
529  _state.viewing_ray(x, y, origin, direction);
530 
531  _state.pop_modelview_matrix();
532  _state.set_updateGL(true);
533 
534 
535 
536  // get cylinder axis ray: it's in its own coord system!
537  const Vec3d origin2(0,0,0), direction2(0,0,1);
538 
539 
540  // compute pseude-intersection
541  Vec3d normal = (direction % direction2).normalize();
542  Vec3d vd = ((origin2 - origin) % direction);
543  _axis_hit = (normal | vd);
544 
545  double orthodistance = std::abs( ( origin2 - origin ) | normal);
546 
547 
548  // hit cylinder ?
549  return((orthodistance < cylinder_radius_) &&
550  (_axis_hit >= 0) &&
551  (_axis_hit <= cylinder_height_));
552 }
553 
554 
555 //----------------------------------------------------------------------------
556 
557 
558 void ManipulatorNode::pick(GLState& _state, PickTarget _target) {
559  if (_target == PICK_FACE || _target == PICK_ANYTHING) {
560  if (draw_cylinder_) {
561 
562  _state.pick_set_maximum(2);
563 
564  // cylinder
565  _state.push_modelview_matrix();
566  setup_cylinder_system(_state);
567  _state.pick_set_name(0);
568  cylinder_->setBottomRadius(cylinder_radius_);
569  cylinder_->setTopRadius(cylinder_radius_);
570  cylinder_->draw(_state, cylinder_height_);
571  _state.pop_modelview_matrix();
572 
573  // sphere
574  _state.push_modelview_matrix();
575  setup_sphere_system(_state);
576  _state.pick_set_name(1);
577  if(_state.compatibilityProfile()) {
578  _state.scale(2*cylinder_radius_,2*cylinder_radius_,2*cylinder_radius_);
579  ACG::GLSphere sphere(20, 20);
580  sphere.draw_primitive();
581  }
582  _state.pop_modelview_matrix();
583  }
584  }
585 }
586 
587 
588 //----------------------------------------------------------------------------
589 
590 
591 void
593 {
594 
595  direction_ = inverse_rotation().transform_vector(_v.normalize());
596 }
597 
598 //----------------------------------------------------------------------------
599 
600 Vec3d
602 {
603 
604  return rotation().transform_vector(direction_);
605 }
606 
607 
608 
609 //=============================================================================
610 } // namespace SceneGraph
611 } // namespace ACG
612 //=============================================================================
static void enable(GLenum _cap, bool _warnRemoved=true)
replaces glEnable, but supports locking
Definition: GLState.cc:1507
virtual void mouseEvent(GLState &_state, QMouseEvent *_event) override
get mouse events
ManipulatorNode(BaseNode *_parent=0, const std::string &_name="<ManipulatorNode>")
Default constructor.
void pop_modelview_matrix()
pop modelview matrix
Definition: GLState.cc:1026
static void shadeModel(GLenum _mode)
replaces glShadeModel, supports locking
Definition: GLState.cc:1729
const Vec4f & specular_color() const
get specular color
Definition: GLState.hh:966
void set_diffuse_color(const Vec4f &_col)
set diffuse color
Definition: GLState.cc:722
bool touched_
if the manipulator is cklicked with a mouse, this flag will be set to true
const Vec3d & center() const
get center
picks faces (should be implemented for all nodes)
Definition: PickTarget.hh:78
Namespace providing different geometric functions concerning angles.
const GLMatrixd & inverse_scale() const
return inverse scale matrix
void scale(double _s)
scale by (_s, _s, _s)
Definition: GLState.hh:775
const Vec4f & diffuse_color() const
get diffuse color
Definition: GLState.hh:961
void mult_matrix(const GLMatrixd &_m, const GLMatrixd &_inv_m, MultiplyFrom _mult_from=MULT_FROM_RIGHT)
multiply by a given transformation matrix
Definition: GLState.cc:614
void set_updateGL(bool _b)
should GL matrices be updated after each matrix operation
Definition: GLState.hh:235
PickTarget
What target to use for picking.
Definition: PickTarget.hh:73
void push_modelview_matrix()
push modelview matrix
Definition: GLState.cc:1010
const GLMatrixd & inverse_rotation() const
return inverse rotation matrix
pick any of the prior targets (should be implemented for all nodes)
Definition: PickTarget.hh:84
bool pick_set_maximum(size_t _idx)
Set the maximal number of primitives/components of your object.
Definition: GLState.cc:1051
void draw(GLState &_state, const DrawModes::DrawMode &_drawMode) override
draw the cylinder (if enabled)
VectorT< T, 3 > transform_vector(const VectorT< T, 3 > &_v) const
transform vector (x&#39;,y&#39;,z&#39;,0) = A * (x,y,z,0)
void translate(double _s)
translate in cylinder direction
auto norm() const -> decltype(std::sqrt(std::declval< VectorT< S, DIM >>().sqrnorm()))
compute euclidean norm
Definition: Vector11T.hh:433
void set_specular_color(const Vec4f &_col)
set specular color
Definition: GLState.cc:737
void rotate(double _angle, double _x, double _y, double _z, MultiplyFrom _mult_from=MULT_FROM_RIGHT)
rotate around axis (_x, _y, _z) by _angle
Definition: GLState.cc:564
void pick(GLState &_state, PickTarget _target) override
picking
void viewing_ray(int _x, int _y, Vec3d &_origin, Vec3d &_direction) const
Definition: GLState.cc:930
void pick_set_name(size_t _idx)
sets the current name/color (like glLoadName(_idx))
Definition: GLState.cc:1061
decltype(std::declval< S >() *std::declval< S >()) sqrnorm() const
compute squared euclidean norm
Definition: Vector11T.hh:421
int context_height() const
get gl context height
Definition: GLState.hh:855
void rotate(double _angle, const Vec3d &_axis)
const GLMatrixd & rotation() const
return rotation matrix
auto normalize() -> decltype(*this/=std::declval< VectorT< S, DIM >>().norm())
Definition: Vector11T.hh:453
const GLMatrixd & scale() const
return scale matrix
virtual void setIdentity() override
void translate(double _x, double _y, double _z, MultiplyFrom _mult_from=MULT_FROM_RIGHT)
translate by (_x, _y, _z)
Definition: GLState.cc:533
VectorT< double, 3 > Vec3d
Definition: VectorT.hh:121