Developer Documentation
Loading...
Searching...
No Matches
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
62namespace ACG {
63namespace SceneGraph {
64
65
66//== IMPLEMENTATION ==========================================================
67
68
69const Vec4f cylinder_color (0.8f, 0.4f, 0.4f, 1.0f);
70const Vec4f sphere_color (0.8f, 0.4f, 0.4f, 1.0f);
71const Vec4f select_color (1.0f, 0.1f, 0.1f, 1.0f);
72const float SCALE_CONST = 5.0f;
73
74
75//----------------------------------------------------------------------------
76
77
79ManipulatorNode( 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
109void
112{
113 direction_ = rotation().transform_vector( direction_ );
115}
116
117
118//----------------------------------------------------------------------------
119
120
121void
122ManipulatorNode::
123setup_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 > NumLimitsT<float>::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
152void
153ManipulatorNode::
154setup_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
165void
166ManipulatorNode::draw(GLState& _state, const DrawModes::DrawMode& /* _drawMode */ )
167{
168 if (draw_cylinder_)
169 {
170 if(_state.compatibilityProfile())
171 {
172 ACG::GLState::enable(GL_LIGHTING);
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
280void
281ManipulatorNode::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
413bool 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
451bool
452ManipulatorNode::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
511bool
512ManipulatorNode::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
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
591void
597
598//----------------------------------------------------------------------------
599
600Vec3d
602{
603
604 return rotation().transform_vector(direction_);
605}
606
607
608
609//=============================================================================
610} // namespace SceneGraph
611} // namespace ACG
612//=============================================================================
void set_specular_color(const Vec4f &_col)
set specular color
Definition GLState.cc:737
void pop_modelview_matrix()
pop modelview matrix
Definition GLState.cc:1026
static void enable(GLenum _cap, bool _warnRemoved=true)
replaces glEnable, but supports locking
Definition GLState.cc:1507
const Vec4f & specular_color() const
get specular color
Definition GLState.hh:966
int context_height() const
get gl context height
Definition GLState.hh:855
void pick_set_name(size_t _idx)
sets the current name/color (like glLoadName(_idx))
Definition GLState.cc:1061
bool pick_set_maximum(size_t _idx)
Set the maximal number of primitives/components of your object.
Definition GLState.cc:1051
void set_updateGL(bool _b)
should GL matrices be updated after each matrix operation
Definition GLState.hh:235
void scale(double _s)
scale by (_s, _s, _s)
Definition GLState.hh:775
void viewing_ray(int _x, int _y, Vec3d &_origin, Vec3d &_direction) const
Definition GLState.cc:930
void translate(double _x, double _y, double _z, MultiplyFrom _mult_from=MULT_FROM_RIGHT)
translate by (_x, _y, _z)
Definition GLState.cc:533
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 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
static void shadeModel(GLenum _mode)
replaces glShadeModel, supports locking
Definition GLState.cc:1729
const Vec4f & diffuse_color() const
get diffuse color
Definition GLState.hh:961
void push_modelview_matrix()
push modelview matrix
Definition GLState.cc:1010
void set_diffuse_color(const Vec4f &_col)
set diffuse color
Definition GLState.cc:722
VectorT< T, 3 > transform_vector(const VectorT< T, 3 > &_v) const
transform vector (x',y',z',0) = A * (x,y,z,0)
ManipulatorNode(BaseNode *_parent=0, const std::string &_name="<ManipulatorNode>")
Default constructor.
bool touched_
if the manipulator is cklicked with a mouse, this flag will be set to true
void draw(GLState &_state, const DrawModes::DrawMode &_drawMode) override
draw the cylinder (if enabled)
virtual void mouseEvent(GLState &_state, QMouseEvent *_event) override
get mouse events
void pick(GLState &_state, PickTarget _target) override
picking
virtual void setIdentity() override
void translate(double _s)
translate in cylinder direction
const Vec3d & center() const
get center
const GLMatrixd & scale() const
return scale matrix
const GLMatrixd & rotation() const
return rotation matrix
void rotate(double _angle, const Vec3d &_axis)
const GLMatrixd & inverse_scale() const
return inverse scale matrix
const GLMatrixd & inverse_rotation() const
return inverse rotation matrix
decltype(std::declval< S >() *std::declval< S >()) sqrnorm() const
compute squared euclidean norm
Definition Vector11T.hh:422
auto normalize() -> decltype(*this/=std::declval< VectorT< S, DIM > >().norm())
Definition Vector11T.hh:454
auto norm() const -> decltype(std::sqrt(std::declval< VectorT< S, DIM > >().sqrnorm()))
compute euclidean norm
Definition Vector11T.hh:434
PickTarget
What target to use for picking.
Definition PickTarget.hh:74
@ PICK_ANYTHING
pick any of the prior targets (should be implemented for all nodes)
Definition PickTarget.hh:84
@ PICK_FACE
picks faces (should be implemented for all nodes)
Definition PickTarget.hh:78
Namespace providing different geometric functions concerning angles.
VectorT< signed int, 2 > Vec2i
Definition VectorT.hh:98
VectorT< double, 3 > Vec3d
Definition VectorT.hh:121