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  // IF sphere clicked rotate or change direction
330  if(sphere_clicked_)
331  {
332  bool hit0 = mapToSphere(_state, newPoint2D, newPoint3D);
333  bool hit1 = mapToSphere(_state, oldPoint2D_, oldPoint3D);
334 
335 
336  if (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  Vec3d axis = oldPoint3D % newPoint3D;
354  double cos_angle = ( oldPoint3D | newPoint3D );
355 
356  if (fabs(cos_angle) < 1.0)
357  rotate(acos(cos_angle)*180.0/M_PI, axis);
358  }
359  }
360  else sphere_clicked_ = false;
361  }
362 
363 
364  // cylinder clicked change scaling or change translation
365  if(cylinder_clicked_)
366  {
367  double old_axis_hit;
368 
369  mapToCylinder(_state, oldPoint2D_, old_axis_hit);
370  mapToCylinder(_state, newPoint2D, new_axis_hit);
371 
372  // scale
373  if(_event->modifiers() & Qt::ShiftModifier)
374  {
375  scale(1.0 + (new_axis_hit - old_axis_hit) /
376  (cylinder_height_ * SCALE_CONST));
377  }
378 
379  // twist
380  else if(_event->modifiers() & (Qt::ControlModifier |
381  Qt::AltModifier))
382  {
383  rotate( 45.0 * (new_axis_hit-old_axis_hit) / cylinder_height_,
384  direction_);
385 
386  }
387 
388  // translate
389  else
390  {
391  translate(new_axis_hit - old_axis_hit);
392  }
393  }
394 
395  break;
396  }
397 
398  default: // avoid warning
399  break;
400  }
401 
402 
403  // save old Point
404  oldPoint2D_ = newPoint2D;
405 }
406 
407 
408 //----------------------------------------------------------------------------
409 
410 
411 bool ManipulatorNode::hitSphere( GLState& _state,
412  const Vec2i& _v2)
413 {
414  // Qt -> GL coordinate systems
415  unsigned int x = _v2[0];
416  unsigned int y = _state.context_height() - _v2[1];
417 
418 
419  // get ray from eye through pixel, in sphere coords
420  Vec3d origin, direction;
421 
422  _state.set_updateGL(false);
423  _state.push_modelview_matrix();
424 
425  setup_sphere_system(_state);
426  _state.scale(2*cylinder_radius_);
427 
428  _state.viewing_ray(x, y, origin, direction);
429 
430  _state.pop_modelview_matrix();
431  _state.set_updateGL(true);
432 
433 
434 
435  // calc sphere-ray intersection
436  // (sphere is centered at origin, has radius 1)
437  double a = direction.sqrnorm(),
438  b = 2.0 * (origin | direction),
439  c = origin.sqrnorm() - 1.0,
440  d = b*b - 4.0*a*c;
441 
442  return (d >= 0.0);
443 }
444 
445 
446 //----------------------------------------------------------------------------
447 
448 
449 bool
450 ManipulatorNode::mapToSphere( GLState& _state,
451  const Vec2i& _v2,
452  Vec3d& _v3 )
453 {
454  // Qt -> GL coordinate systems
455  unsigned int x = _v2[0];
456  unsigned int y = _state.context_height() - _v2[1];
457 
458 
459 
460  // get ray from eye through pixel (trackball coords)
461  // *no* rotation, points have to be in world coords
462  Vec3d origin, direction;
463 
464  _state.set_updateGL(false);
465  _state.push_modelview_matrix();
466 
467  _state.translate(center()[0], center()[1], center()[2]);
468  _state.mult_matrix(inverse_scale (), scale ());
469  _state.scale(cylinder_height_ + 4*cylinder_radius_);
470 
471  _state.viewing_ray(x, y, origin, direction);
472 
473  _state.pop_modelview_matrix();
474  _state.set_updateGL(true);
475 
476 
477 
478  // calc sphere-ray intersection
479  // (sphere is centered at origin, has radius 1)
480  double a = direction.sqrnorm(),
481  b = 2.0 * (origin | direction),
482  c = origin.sqrnorm() - 1.0,
483  d = b*b - 4.0*a*c,
484  t;
485 
486  if (d < 0.0) return false;
487  else if (d == 0.0) t = -b / (2.0*a);
488  else
489  {
490  a = 1.0 / (2.0*a);
491  d = sqrt(d);
492  double t1 = (-b - d) * a;
493  double t2 = (-b + d) * a;
494  t = (t1 < t2) ? t1 : t2;
495  }
496 
497 
498 
499  // map back to world coords
500  _v3 = origin + direction*t;
501 
502  return true;
503 }
504 
505 
506 //----------------------------------------------------------------------------
507 
508 
509 bool
510 ManipulatorNode::mapToCylinder(GLState& _state,
511  const Vec2i& _v1,
512  double& _axis_hit)
513 {
514  // Qt -> GL coordinate systems
515  unsigned int x = _v1[0];
516  unsigned int y = _state.context_height() - _v1[1];
517 
518 
519  // get ray from eye through pixel (cylinder coords)
520  Vec3d origin, direction;
521 
522  _state.set_updateGL(false);
523  _state.push_modelview_matrix();
524 
525  setup_cylinder_system(_state);
526 
527  _state.viewing_ray(x, y, origin, direction);
528 
529  _state.pop_modelview_matrix();
530  _state.set_updateGL(true);
531 
532 
533 
534  // get cylinder axis ray: it's in its own coord system!
535  const Vec3d origin2(0,0,0), direction2(0,0,1);
536 
537 
538  // compute pseude-intersection
539  Vec3d normal = (direction % direction2).normalize();
540  Vec3d vd = ((origin2 - origin) % direction);
541  _axis_hit = (normal | vd);
542 
543  double orthodistance = std::abs( ( origin2 - origin ) | normal);
544 
545 
546  // hit cylinder ?
547  return((orthodistance < cylinder_radius_) &&
548  (_axis_hit >= 0) &&
549  (_axis_hit <= cylinder_height_));
550 }
551 
552 
553 //----------------------------------------------------------------------------
554 
555 
556 void ManipulatorNode::pick(GLState& _state, PickTarget _target) {
557  if (_target == PICK_FACE || _target == PICK_ANYTHING) {
558  if (draw_cylinder_) {
559 
560  _state.pick_set_maximum(2);
561 
562  // cylinder
563  _state.push_modelview_matrix();
564  setup_cylinder_system(_state);
565  _state.pick_set_name(0);
566  cylinder_->setBottomRadius(cylinder_radius_);
567  cylinder_->setTopRadius(cylinder_radius_);
568  cylinder_->draw(_state, cylinder_height_);
569  _state.pop_modelview_matrix();
570 
571  // sphere
572  _state.push_modelview_matrix();
573  setup_sphere_system(_state);
574  _state.pick_set_name(1);
575  if(_state.compatibilityProfile()) {
576  _state.scale(2*cylinder_radius_,2*cylinder_radius_,2*cylinder_radius_);
577  ACG::GLSphere sphere(20, 20);
578  sphere.draw_primitive();
579  }
580  _state.pop_modelview_matrix();
581  }
582  }
583 }
584 
585 
586 //----------------------------------------------------------------------------
587 
588 
589 void
591 {
592  direction_ = inverse_rotation().transform_vector(_v.normalize());
593 }
594 
595 //----------------------------------------------------------------------------
596 
597 Vec3d
599 {
600  return rotation().transform_vector(direction_);
601 }
602 
603 
604 
605 //=============================================================================
606 } // namespace SceneGraph
607 } // namespace ACG
608 //=============================================================================
const Vec4f & specular_color() const
get specular color
Definition: GLState.hh:941
Namespace providing different geometric functions concerning angles.
const GLMatrixd & inverse_scale() const
return inverse scale matrix
void set_specular_color(const Vec4f &_col)
set specular color
Definition: GLState.cc:737
pick any of the prior targets (should be implemented for all nodes)
Definition: PickTarget.hh:84
auto norm() const -> decltype(std::sqrt(std::declval< VectorT< S, DIM >>().sqrnorm()))
compute euclidean norm
Definition: Vector11T.hh:409
void set_diffuse_color(const Vec4f &_col)
set diffuse color
Definition: GLState.cc:722
decltype(std::declval< S >() *std::declval< S >()) sqrnorm() const
compute squared euclidean norm
Definition: Vector11T.hh:397
void push_modelview_matrix()
push modelview matrix
Definition: GLState.cc:1010
void set_updateGL(bool _b)
should GL matrices be updated after each matrix operation
Definition: GLState.hh:235
static void enable(GLenum _cap, bool _warnRemoved=true)
replaces glEnable, but supports locking
int context_height() const
get gl context height
Definition: GLState.hh:830
const GLMatrixd & scale() const
return scale matrix
void translate(double _x, double _y, double _z, MultiplyFrom _mult_from=MULT_FROM_RIGHT)
translate by (_x, _y, _z)
Definition: GLState.cc:533
auto normalize() -> decltype(*this/=std::declval< VectorT< S, DIM >>().norm())
Definition: Vector11T.hh:429
void translate(double _s)
translate in cylinder direction
void rotate(double _angle, const Vec3d &_axis)
bool touched_
if the manipulator is cklicked with a mouse, this flag will be set to true
const GLMatrixd & inverse_rotation() const
return inverse rotation matrix
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
VectorT< double, 3 > Vec3d
Definition: VectorT.hh:121
static void shadeModel(GLenum _mode)
replaces glShadeModel, supports locking
void pick(GLState &_state, PickTarget _target)
picking
ManipulatorNode(BaseNode *_parent=0, const std::string &_name="<ManipulatorNode>")
Default constructor.
void viewing_ray(int _x, int _y, Vec3d &_origin, Vec3d &_direction) const
Definition: GLState.cc:930
const GLMatrixd & rotation() const
return rotation matrix
PickTarget
What target to use for picking.
Definition: PickTarget.hh:73
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 pop_modelview_matrix()
pop modelview matrix
Definition: GLState.cc:1026
void scale(double _s)
scale by (_s, _s, _s)
Definition: GLState.hh:750
virtual void mouseEvent(GLState &_state, QMouseEvent *_event)
get mouse events
picks faces (should be implemented for all nodes)
Definition: PickTarget.hh:78
void draw(GLState &_state, const DrawModes::DrawMode &_drawMode)
draw the cylinder (if enabled)
const Vec4f & diffuse_color() const
get diffuse color
Definition: GLState.hh:936
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)
const Vec3d & center() const
get center
bool pick_set_maximum(size_t _idx)
Set the maximal number of primitives/components of your object.
Definition: GLState.cc:1051
void pick_set_name(size_t _idx)
sets the current name/color (like glLoadName(_idx))
Definition: GLState.cc:1061