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  * $Revision$ *
45  * $Author$ *
46  * $Date$ *
47  * *
48 \*===========================================================================*/
49 
50 
51 
52 
53 //=============================================================================
54 //
55 // CLASS ManipulatorNode - IMPLEMENTATION
56 //
57 //=============================================================================
58 
59 
60 //== INCLUDES =================================================================
61 
62 
63 #include "ManipulatorNode.hh"
64 
65 
66 //== NAMESPACES ===============================================================
67 
68 
69 namespace ACG {
70 namespace SceneGraph {
71 
72 
73 //== IMPLEMENTATION ==========================================================
74 
75 
76 const Vec4f cylinder_color (0.8f, 0.4f, 0.4f, 1.0f);
77 const Vec4f sphere_color (0.8f, 0.4f, 0.4f, 1.0f);
78 const Vec4f select_color (1.0f, 0.1f, 0.1f, 1.0f);
79 const float SCALE_CONST = 5.0f;
80 
81 
82 //----------------------------------------------------------------------------
83 
84 
86 ManipulatorNode( BaseNode* _parent, const std::string& _name )
87  : TransformNode(_parent, _name),
88  draw_cylinder_(false),
89  direction_(1.0,0.0,0.0),
90  cylinder_(0),
91  cylinder_radius_(1),
92  cylinder_height_(10),
93  cylinder_slices_(30),
94  cylinder_stacks_(10),
95  cylinder_clicked_(false),
96  sphere_clicked_(false),
97  touched_(false)
98 {
99 }
100 
101 
102 //----------------------------------------------------------------------------
103 
104 
107 {
108  if (cylinder_)
109  delete cylinder_;
110 }
111 
112 
113 //----------------------------------------------------------------------------
114 
115 
116 void
119 {
120  direction_ = rotation().transform_vector( direction_ );
122 }
123 
124 
125 //----------------------------------------------------------------------------
126 
127 
128 void
129 ManipulatorNode::
130 setup_cylinder_system(GLState& _state)
131 {
132  _state.translate(center()[0], center()[1], center()[2]);
133  _state.mult_matrix(inverse_scale (), scale ()); // Adapt scaling
134 
135  // rotation axis & angle
136  Vec3d z(0.0, 0.0, 1.0);
137  double scal_prod = (direction_ | z);
138  Vec3d axis = z % direction_;
139  double norm = axis.norm();
140  double angle;
141 
142  if (norm > FLT_MIN)
143  {
144  axis /= norm;
145  if (scal_prod > 1.0) scal_prod = 1.0;
146  if (scal_prod < -1.0) scal_prod = -1.0;
147  angle = 180.0 / M_PI * acos(scal_prod);
148  }
149  else
150  {
151  axis = Vec3d(1.0, 0.0, 0.0);
152  angle = (scal_prod > 0.0) ? 0.0 : 180.0;
153  }
154 
155  _state.rotate(angle, axis[0], axis[1], axis[2]);
156 }
157 
158 
159 void
160 ManipulatorNode::
161 setup_sphere_system(GLState& _state)
162 {
163  setup_cylinder_system(_state);
164 
165  _state.translate(0, 0, cylinder_height_+ 2*cylinder_radius_);
166 }
167 
168 
169 //----------------------------------------------------------------------------
170 
171 
172 void
173 ManipulatorNode::draw(GLState& _state, const DrawModes::DrawMode& /* _drawMode */ )
174 {
175  if (draw_cylinder_)
176  {
177  ACG::GLState::enable(GL_LIGHTING);
178  ACG::GLState::shadeModel(GL_FLAT);
179 
180 
181  // backup colors
182  Vec4f backup_diffuse = _state.diffuse_color();
183  Vec4f backup_specular = _state.specular_color();
184 
185 
186  // draw cylinder
187 
188  if (!cylinder_)
189  {
190  cylinder_ = new GLCylinder(cylinder_slices_, cylinder_stacks_, cylinder_radius_, false, false);
191  }
192 
193  _state.push_modelview_matrix();
194  setup_cylinder_system(_state);
195 
196  if( cylinder_clicked_)
197  {
198  _state.set_diffuse_color(select_color * 0.6f);
199  _state.set_specular_color(select_color * 0.8f);
200  }
201  else
202  {
203  _state.set_diffuse_color(cylinder_color * 0.6f);
204  _state.set_specular_color(cylinder_color * 0.8f);
205  }
206 
207  // Zylinder in die X-Achse
208  /*glPushMatrix();
209  glLoadIdentity();
210  glRotatef(-90, 0.0, 1.0, 0.0);*/
211 
212  ACG::GLState::shadeModel(GL_SMOOTH);
213  cylinder_->setBottomRadius(cylinder_radius_);
214  cylinder_->setTopRadius(cylinder_radius_);
215  cylinder_->draw(_state, cylinder_height_);
216 
217  //glPopMatrix();
218 
219 
220  // BIG wireframe sphere
221  if( sphere_clicked_)
222  {
223  _state.set_diffuse_color(select_color * 0.6f);
224  _state.set_specular_color(select_color * 0.0f);
225  ACG::GLState::shadeModel(GL_SMOOTH);
226  glutWireSphere(cylinder_height_+4*cylinder_radius_, 20, 20);
227  }
228 
229  _state.pop_modelview_matrix();
230 
231 
232 
233 
234  // draw sphere
235 
236  _state.push_modelview_matrix();
237  setup_sphere_system(_state);
238 
239  if( sphere_clicked_)
240  {
241  _state.set_diffuse_color(select_color * 0.6f);
242  _state.set_specular_color(select_color * 0.8f);
243  }
244  else
245  {
246  _state.set_diffuse_color(sphere_color * 0.6f);
247  _state.set_specular_color(sphere_color * 0.8f);
248  }
249 
250  ACG::GLState::shadeModel(GL_SMOOTH);
251  glutSolidSphere(2*cylinder_radius_, 20, 20);
252 
253  _state.pop_modelview_matrix();
254 
255 
256 
257 
258  // restore
259  _state.set_diffuse_color(backup_diffuse);
260  _state.set_specular_color(backup_specular);
261  }
262 }
263 
264 
265 //----------------------------------------------------------------------------
266 
267 
268 void
269 ManipulatorNode::mouseEvent(GLState& _state, QMouseEvent* _event)
270 {
271  Vec3d oldPoint3D;
272  Vec2i newPoint2D(_event->pos().x(), _event->pos().y());
273  Vec3d newPoint3D;
274  double new_axis_hit;
275 
276 
277  switch (_event->type())
278  {
279  case QEvent::MouseButtonPress:
280  {
281  // hit sphere ?
282  sphere_clicked_ = hitSphere(_state, newPoint2D);
283 
284  // hit cylinder ?
285  cylinder_clicked_ = mapToCylinder(_state, newPoint2D, new_axis_hit);
286 
287  // If the user clicked on the manipulator, remember it
288  if ( sphere_clicked_ || cylinder_clicked_)
289  touched_ = true;
290 
291  // select only sphere or cylinder
292  if(sphere_clicked_ && cylinder_clicked_)
293  cylinder_clicked_ = false;
294 
295  oldPoint2D_ = newPoint2D;
296  break;
297  }
298 
299 
300  case QEvent::MouseButtonRelease:
301  {
302  sphere_clicked_ = false;
303  cylinder_clicked_ = false;
304  break;
305  }
306 
307 
308  case QEvent::MouseButtonDblClick:
309  {
310  draw_cylinder_ = !draw_cylinder_;
311  break;
312  }
313 
314 
315  case QEvent::MouseMove:
316  {
317  // IF sphere clicked rotate or change direction
318  if(sphere_clicked_)
319  {
320  bool hit0 = mapToSphere(_state, newPoint2D, newPoint3D);
321  bool hit1 = mapToSphere(_state, oldPoint2D_, oldPoint3D);
322 
323 
324  if (hit0 && hit1)
325  {
326  // change direction
327  if(_event->modifiers() & Qt::ShiftModifier)
328  {
329 
330  // calculate new cylinder direction
331  direction_.normalize();
332  newPoint3D.normalize();
333  oldPoint3D.normalize();
334 
335  direction_ += newPoint3D - oldPoint3D;
336 
337  }
338  // rotate
339  else
340  {
341  Vec3d axis = oldPoint3D % newPoint3D;
342  double cos_angle = ( oldPoint3D | newPoint3D );
343 
344  if (fabs(cos_angle) < 1.0)
345  rotate(acos(cos_angle)*180.0/M_PI, axis);
346  }
347  }
348  else sphere_clicked_ = false;
349  }
350 
351 
352  // cylinder clicked change scaling or change translation
353  if(cylinder_clicked_)
354  {
355  double old_axis_hit;
356 
357  mapToCylinder(_state, oldPoint2D_, old_axis_hit);
358  mapToCylinder(_state, newPoint2D, new_axis_hit);
359 
360  // scale
361  if(_event->modifiers() & Qt::ShiftModifier)
362  {
363  scale(1.0 + (new_axis_hit - old_axis_hit) /
364  (cylinder_height_ * SCALE_CONST));
365  }
366 
367  // twist
368  else if(_event->modifiers() & (Qt::ControlModifier |
369  Qt::AltModifier))
370  {
371  rotate( 45.0 * (new_axis_hit-old_axis_hit) / cylinder_height_,
372  direction_);
373 
374  }
375 
376  // translate
377  else
378  {
379  translate(new_axis_hit - old_axis_hit);
380  }
381  }
382 
383  break;
384  }
385 
386  default: // avoid warning
387  break;
388  }
389 
390 
391  // save old Point
392  oldPoint2D_ = newPoint2D;
393 }
394 
395 
396 //----------------------------------------------------------------------------
397 
398 
399 bool ManipulatorNode::hitSphere( GLState& _state,
400  const Vec2i& _v2)
401 {
402  // Qt -> GL coordinate systems
403  unsigned int x = _v2[0];
404  unsigned int y = _state.context_height() - _v2[1];
405 
406 
407  // get ray from eye through pixel, in sphere coords
408  Vec3d origin, direction;
409 
410  _state.set_updateGL(false);
411  _state.push_modelview_matrix();
412 
413  setup_sphere_system(_state);
414  _state.scale(2*cylinder_radius_);
415 
416  _state.viewing_ray(x, y, origin, direction);
417 
418  _state.pop_modelview_matrix();
419  _state.set_updateGL(true);
420 
421 
422 
423  // calc sphere-ray intersection
424  // (sphere is centered at origin, has radius 1)
425  double a = direction.sqrnorm(),
426  b = 2.0 * (origin | direction),
427  c = origin.sqrnorm() - 1.0,
428  d = b*b - 4.0*a*c;
429 
430  return (d >= 0.0);
431 }
432 
433 
434 //----------------------------------------------------------------------------
435 
436 
437 bool
438 ManipulatorNode::mapToSphere( GLState& _state,
439  const Vec2i& _v2,
440  Vec3d& _v3 )
441 {
442  // Qt -> GL coordinate systems
443  unsigned int x = _v2[0];
444  unsigned int y = _state.context_height() - _v2[1];
445 
446 
447 
448  // get ray from eye through pixel (trackball coords)
449  // *no* rotation, points have to be in world coords
450  Vec3d origin, direction;
451 
452  _state.set_updateGL(false);
453  _state.push_modelview_matrix();
454 
455  _state.translate(center()[0], center()[1], center()[2]);
456  _state.mult_matrix(inverse_scale (), scale ());
457  _state.scale(cylinder_height_ + 4*cylinder_radius_);
458 
459  _state.viewing_ray(x, y, origin, direction);
460 
461  _state.pop_modelview_matrix();
462  _state.set_updateGL(true);
463 
464 
465 
466  // calc sphere-ray intersection
467  // (sphere is centered at origin, has radius 1)
468  double a = direction.sqrnorm(),
469  b = 2.0 * (origin | direction),
470  c = origin.sqrnorm() - 1.0,
471  d = b*b - 4.0*a*c,
472  t;
473 
474  if (d < 0.0) return false;
475  else if (d == 0.0) t = -b / (2.0*a);
476  else
477  {
478  a = 1.0 / (2.0*a);
479  d = sqrt(d);
480  double t1 = (-b - d) * a;
481  double t2 = (-b + d) * a;
482  t = (t1 < t2) ? t1 : t2;
483  }
484 
485 
486 
487  // map back to world coords
488  _v3 = origin + direction*t;
489 
490  return true;
491 }
492 
493 
494 //----------------------------------------------------------------------------
495 
496 
497 bool
498 ManipulatorNode::mapToCylinder(GLState& _state,
499  const Vec2i& _v1,
500  double& _axis_hit)
501 {
502  // Qt -> GL coordinate systems
503  unsigned int x = _v1[0];
504  unsigned int y = _state.context_height() - _v1[1];
505 
506 
507  // get ray from eye through pixel (cylinder coords)
508  Vec3d origin, direction;
509 
510  _state.set_updateGL(false);
511  _state.push_modelview_matrix();
512 
513  setup_cylinder_system(_state);
514 
515  _state.viewing_ray(x, y, origin, direction);
516 
517  _state.pop_modelview_matrix();
518  _state.set_updateGL(true);
519 
520 
521 
522  // get cylinder axis ray: it's in its own coord system!
523  const Vec3d origin2(0,0,0), direction2(0,0,1);
524 
525 
526  // compute pseude-intersection
527  Vec3d normal = (direction % direction2).normalize();
528  Vec3d vd = ((origin2 - origin) % direction);
529  _axis_hit = (normal | vd);
530 
531  double orthodistance = std::abs( ( origin2 - origin ) | normal);
532 
533 
534  // hit cylinder ?
535  return((orthodistance < cylinder_radius_) &&
536  (_axis_hit >= 0) &&
537  (_axis_hit <= cylinder_height_));
538 }
539 
540 
541 //----------------------------------------------------------------------------
542 
543 
544 void ManipulatorNode::pick(GLState& _state, PickTarget _target) {
545  if (_target == PICK_FACE || _target == PICK_ANYTHING) {
546  if (draw_cylinder_) {
547 
548  _state.pick_set_maximum(2);
549 
550  // cylinder
551  _state.push_modelview_matrix();
552  setup_cylinder_system(_state);
553  _state.pick_set_name(0);
554  cylinder_->setBottomRadius(cylinder_radius_);
555  cylinder_->setTopRadius(cylinder_radius_);
556  cylinder_->draw(_state, cylinder_height_);
557  _state.pop_modelview_matrix();
558 
559  // sphere
560  _state.push_modelview_matrix();
561  setup_sphere_system(_state);
562  _state.pick_set_name(1);
563  glutSolidSphere(2* cylinder_radius_ , 20, 20);
564  _state.pop_modelview_matrix();
565  }
566  }
567 }
568 
569 
570 //----------------------------------------------------------------------------
571 
572 
573 void
575 {
576  direction_ = inverse_rotation().transform_vector(_v.normalize());
577 }
578 
579 
580 //----------------------------------------------------------------------------
581 
582 
583 Vec3d
585 {
586  return rotation().transform_vector(direction_);
587 }
588 
589 
590 
591 //=============================================================================
592 } // namespace SceneGraph
593 } // namespace ACG
594 //=============================================================================
const GLMatrixd & inverse_scale() const
return inverse scale matrix
const GLMatrixd & rotation() const
return rotation matrix
void pick(GLState &_state, PickTarget _target)
picking
int context_height() const
get gl context height
Definition: GLState.hh:833
void viewing_ray(int _x, int _y, Vec3d &_origin, Vec3d &_direction) const
Definition: GLState.cc:926
void push_modelview_matrix()
push modelview matrix
Definition: GLState.cc:1006
const Vec4f & specular_color() const
get specular color
Definition: GLState.hh:944
PickTarget
What target to use for picking.
Definition: BaseNode.hh:99
ManipulatorNode(BaseNode *_parent=0, const std::string &_name="<ManipulatorNode>")
Default constructor.
void set_diffuse_color(const Vec4f &_col)
set diffuse color
Definition: GLState.cc:720
static void enable(GLenum _cap)
replaces glEnable, but supports locking
void translate(double _x, double _y, double _z, MultiplyFrom _mult_from=MULT_FROM_RIGHT)
translate by (_x, _y, _z)
Definition: GLState.cc:531
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:612
bool pick_set_maximum(unsigned int _idx)
Set the maximal number of primitives/components of your object.
Definition: GLState.cc:1047
void set_specular_color(const Vec4f &_col)
set specular color
Definition: GLState.cc:735
const Vec3d & center() const
get center
void draw(GLState &_state, const DrawModes::DrawMode &_drawMode)
draw the cylinder (if enabled)
const GLMatrixd & scale() const
return scale matrix
Namespace providing different geometric functions concerning angles.
Definition: DBSCANT.cc:51
bool touched_
if the manipulator is cklicked with a mouse, this flag will be set to true
void scale(double _s)
scale by (_s, _s, _s)
Definition: GLState.hh:753
void pop_modelview_matrix()
pop modelview matrix
Definition: GLState.cc:1022
void pick_set_name(unsigned int _idx)
sets the current name/color (like glLoadName(_idx))
Definition: GLState.cc:1057
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:562
auto normalize() -> decltype(*this/=std::declval< VectorT< S, DIM >>().norm())
Definition: Vector11T.hh:428
auto norm() const -> decltype(std::sqrt(std::declval< VectorT< S, DIM >>().sqrnorm()))
compute euclidean norm
Definition: Vector11T.hh:408
decltype(std::declval< S >()*std::declval< S >()) sqrnorm() const
compute squared euclidean norm
Definition: Vector11T.hh:396
void rotate(double _angle, const Vec3d &_axis)
virtual void mouseEvent(GLState &_state, QMouseEvent *_event)
get mouse events
const Vec4f & diffuse_color() const
get diffuse color
Definition: GLState.hh:939
const GLMatrixd & inverse_rotation() const
return inverse rotation matrix
pick any of the prior targets (should be implemented for all nodes)
Definition: BaseNode.hh:110
VectorT< double, 3 > Vec3d
Definition: VectorT.hh:127
void translate(double _s)
translate in cylinder direction
picks faces (should be implemented for all nodes)
Definition: BaseNode.hh:104
void set_updateGL(bool _b)
should GL matrices be updated after each matrix operation
Definition: GLState.hh:242
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)
Definition: Matrix4x4T.cc:225
static void shadeModel(GLenum _mode)
replaces glShadeModel, supports locking