Developer Documentation
QtExaminerViewer.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 // CLASS QtExaminerViewer - IMPLEMENTATION
49 //
50 //=============================================================================
51 
52 
53 //== INCLUDES =================================================================
54 #include <ACG/GL/acg_glew.hh>
55 #include "QtExaminerViewer.hh"
56 
57 #include <QTimer>
58 #include <QApplication>
59 #include <QInputDialog>
60 #include <QStatusBar>
61 
62 #include "../GL/GLState.hh"
63 
64 #ifdef max
65 # undef max
66 #endif
67 
68 
69 //== NAMESPACES ===============================================================
70 
71 
72 namespace ACG {
73 namespace QtWidgets {
74 
75 
76 //== IMPLEMENTATION ==========================================================
77 
78 
80  const char* _name,
81  QStatusBar *_statusBar,
82  const QGLFormat* _format,
83  const QtBaseViewer* _share,
84  Options _options ) :
85  QtBaseViewer(_parent, _name, _statusBar, _format, _share, _options)
86 {
87 
88  // timer for animation
89  timer_ = new QTimer( this );
90  connect( timer_, SIGNAL(timeout()), this, SLOT( slotAnimation()) );
91 
92  allowRotation_ = true;
93 
94  //default wheel zoom factors
95  wZoomFactor_ = 1.0;
96  wZoomFactorShift_ = 0.2;
97 }
98 
99 
100 //-----------------------------------------------------------------------------
101 
102 
103 void
105 {
106  switch (_event->type())
107  {
108  case QEvent::MouseButtonPress:
109  {
110  // shift key -> set rotation center
111  if (_event->modifiers() & Qt::ShiftModifier)
112  {
113  Vec3d c;
114  if (fast_pick(_event->pos(), c))
115  {
116  trackball_center_ = c;
117  trackball_radius_ = std::max(scene_radius_, (c-glState().eye()).norm()*0.9f);
118  }
119  }
120 
121  lastPoint_hitSphere_ = mapToSphere( lastPoint2D_=_event->pos(),
122  lastPoint3D_ );
123  isRotating_ = true;
124  timer_->stop();
125 
126 
127  break;
128  }
129 
130 
131  case QEvent::MouseButtonDblClick:
132  {
133  if (allowRotation_)
134  flyTo(_event->pos(), _event->button()==Qt::MidButton);
135  break;
136  }
137 
138 
139  case QEvent::MouseMove:
140  {
141  double factor = 1.0;
142 
143  if (_event->modifiers() == Qt::ShiftModifier)
144  factor = wZoomFactorShift_;
145 
146  // mouse button should be pressed
147  if (_event->buttons() & (Qt::LeftButton | Qt::MidButton))
148  {
149  QPoint newPoint2D = _event->pos();
150 
151  if ( (newPoint2D.x()<0) || (newPoint2D.x() > (int)glWidth()) ||
152  (newPoint2D.y()<0) || (newPoint2D.y() > (int)glHeight()) )
153  return;
154 
155  double value_y;
156  Vec3d newPoint3D;
157  bool newPoint_hitSphere = mapToSphere( newPoint2D, newPoint3D );
158 
159  makeCurrent();
160 
161  // move in z direction
162  if ( (_event->buttons() & Qt::LeftButton) &&
163  (_event->buttons() & Qt::MidButton))
164  {
165  switch (projectionMode())
166  {
168  {
169  value_y = scene_radius_ * ((newPoint2D.y() - lastPoint2D_.y())) * 3.0 / (double) glHeight();
170  translate( Vec3d(0.0, 0.0, value_y * factor ) );
171  updateGL();
172  break;
173  }
174 
176  {
177  value_y = ((newPoint2D.y() - lastPoint2D_.y())) * orthoWidth_ / (double) glHeight();
178  orthoWidth_ -= value_y * factor;
180  updateGL();
181  break;
182  }
183  }
184  }
185 
186  // move in x,y direction
187  else if (_event->buttons() & Qt::MidButton)
188  {
189  double value_x;
190  value_x = scene_radius_ * ((newPoint2D.x() - lastPoint2D_.x())) * 2.0 / (double) glWidth();
191  value_y = scene_radius_ * ((newPoint2D.y() - lastPoint2D_.y())) * 2.0 / (double) glHeight();
192  translate( Vec3d(value_x * factor , -value_y * factor , 0.0) );
193  }
194 
195  // rotate
196  else if (allowRotation_ && (_event->buttons() & Qt::LeftButton) )
197  {
198  Vec3d axis(1.0,0.0,0.0);
199  double angle(0.0);
200 
201  if ( lastPoint_hitSphere_ ) {
202 
203  if ( ( newPoint_hitSphere = mapToSphere( newPoint2D,
204  newPoint3D ) ) ) {
205  axis = lastPoint3D_ % newPoint3D;
206  double cos_angle = ( lastPoint3D_ | newPoint3D );
207  if ( fabs(cos_angle) < 1.0 ) {
208  angle = acos( cos_angle ) * 180.0 / M_PI * factor ;
209  angle *= 2.0; // inventor rotation
210  }
211  }
212 
213  rotate(axis, angle);
214 
215  }
216 
217  lastRotationAxis_ = axis;
218  lastRotationAngle_ = angle;
219  }
220 
221  lastPoint2D_ = newPoint2D;
222  lastPoint3D_ = newPoint3D;
223  lastPoint_hitSphere_ = newPoint_hitSphere;
224 
225  updateGL();
226  lastMoveTime_.restart();
227  }
228  break;
229  }
230 
231 
232 
233  case QEvent::MouseButtonRelease:
234  {
235  lastPoint_hitSphere_ = false;
236 
237  // continue rotation ?
238  if ( isRotating_ &&
239  (_event->button() == Qt::LeftButton) &&
240  (!(_event->buttons() & Qt::MidButton)) &&
241  (lastMoveTime_.elapsed() < 10) &&
242  animation() )
243  timer_->start(0);
244  break;
245  }
246 
247  default: // avoid warning
248  break;
249  }
250 
251 
252  // sync views
253  emit(signalSetView(glstate_->modelview(), glstate_->inverse_modelview()));
254 }
255 
256 
257 //-----------------------------------------------------------------------------
258 
259 
260 void
262 {
263  switch (_event->type()) {
264  case QEvent::MouseButtonPress: {
265  lastPoint_hitSphere_ = mapToSphere(lastPoint2D_ = _event->pos(), lastPoint3D_);
266  isRotating_ = true;
267  timer_->stop();
268  break;
269  }
270 
271  case QEvent::MouseMove: {
272 
273  // rotate lights
274  if (_event->buttons() & Qt::LeftButton) {
275  QPoint newPoint2D = _event->pos();
276 
277  if ((newPoint2D.x() < 0) || (newPoint2D.x() > (int) glWidth()) || (newPoint2D.y() < 0)
278  || (newPoint2D.y() > (int) glHeight()))
279  return;
280 
281  Vec3d newPoint3D;
282  bool newPoint_hitSphere = mapToSphere(newPoint2D, newPoint3D);
283 
284  makeCurrent();
285 
286  if (lastPoint_hitSphere_) {
287  Vec3d axis(1.0, 0.0, 0.0);
288  double angle(0.0);
289 
290  if ((newPoint_hitSphere = mapToSphere(newPoint2D, newPoint3D))) {
291  axis = lastPoint3D_ % newPoint3D;
292  double cos_angle = (lastPoint3D_ | newPoint3D);
293  if (fabs(cos_angle) < 1.0) {
294  angle = acos(cos_angle) * 180.0 / M_PI;
295  angle *= 2.0;
296  }
297  }
298  rotate_lights(axis, angle);
299  }
300 
301  lastPoint2D_ = newPoint2D;
302  lastPoint3D_ = newPoint3D;
303  lastPoint_hitSphere_ = newPoint_hitSphere;
304 
305  updateGL();
306  }
307  break;
308  }
309 
310  default: // avoid warning
311  break;
312  }
313 }
314 
315 
316 //-----------------------------------------------------------------------------
317 
319  return wZoomFactor_;
320 }
321 
322 //-----------------------------------------------------------------------------
323 
324 double QtExaminerViewer::wheelZoomFactorShift(){
325  return wZoomFactorShift_;
326 }
327 
328 //-----------------------------------------------------------------------------
329 
330 void QtExaminerViewer::setWheelZoomFactor(double _factor){
331  wZoomFactor_ = _factor;
332 }
333 
334 //-----------------------------------------------------------------------------
335 
336 void QtExaminerViewer::setWheelZoomFactorShift(double _factor){
337  wZoomFactorShift_ = _factor;
338 }
339 
340 //-----------------------------------------------------------------------------
341 
342 void QtExaminerViewer::viewWheelEvent( QWheelEvent* _event)
343 {
344  double factor = wZoomFactor_;
345 
346  if (_event->modifiers() == Qt::ShiftModifier)
347  factor = wZoomFactorShift_;
348 
349  switch (projectionMode())
350  {
352  {
353  double d = -(double)_event->delta() / 120.0 * 0.2 * factor * trackball_radius_/3;
354  translate( Vec3d(0.0, 0.0, d) );
355  updateGL();
356  break;
357  }
358 
360  {
361  double d = (double)_event->delta() / 120.0 * 0.2 * factor * orthoWidth_;
362  orthoWidth_ += d;
364  updateGL();
365  break;
366  }
367  }
368 
369 
370  // sync views
371  emit(signalSetView(glstate_->modelview(), glstate_->inverse_modelview()));
372 }
373 
374 
375 //-----------------------------------------------------------------------------
376 
377 
378 bool QtExaminerViewer::mapToSphere(const QPoint& _v2D, Vec3d& _v3D) const
379 {
380  if ( (_v2D.x() >= 0) && (_v2D.x() < (int)glWidth()) &&
381  (_v2D.y() >= 0) && (_v2D.y() < (int)glHeight()) )
382  {
383  double x = (double)(_v2D.x() - ((double)glWidth() / 2.0)) / (double)glWidth();
384  double y = (double)(((double)glHeight() / 2.0) - _v2D.y()) / (double)glHeight();
385  double sinx = sin(M_PI * x * 0.5);
386  double siny = sin(M_PI * y * 0.5);
387  double sinx2siny2 = sinx * sinx + siny * siny;
388 
389  _v3D[0] = sinx;
390  _v3D[1] = siny;
391  _v3D[2] = sinx2siny2 < 1.0 ? sqrt(1.0 - sinx2siny2) : 0.0;
392 
393  return true;
394  }
395  else return false;
396 }
397 
398 
399 //-----------------------------------------------------------------------------
400 
401 
402 void QtExaminerViewer::slotAnimation()
403 {
404 
405  QTime t;
406  t.start();
407  makeCurrent();
408  rotate( lastRotationAxis_, lastRotationAngle_ );
409  updateGL();
410 
411  if (!isUpdateLocked()) {
412 
413  static int msecs=0, count=0;
414 
415  msecs += t.elapsed();
416  if (count==10) {
417  assert(statusbar_!=0);
418  char s[100];
419  sprintf( s, "%.3f fps", (10000.0 / (float)msecs) );
420  statusbar_->showMessage(s,2000);
421  count = msecs = 0;
422  }
423  else
424  ++count;
425  }
426 }
427 
428 
429 //=============================================================================
430 } // namespace QtWidgets
431 } // namespace ACG
432 //=============================================================================
bool fast_pick(const QPoint &_mousePos, Vec3d &_hitPoint)
bool mapToSphere(const QPoint &_p, Vec3d &_result) const
virtual trackball: map 2D screen point to unit sphere
void signalSetView(const GLMatrixd &_modelview, const GLMatrixd &_inverse_modelview)
set view, used for synchronizing (cf. slotSetView())
void updateProjectionMatrix()
updates projection matrix
ProjectionMode projectionMode() const
get current projection mode
Namespace providing different geometric functions concerning angles.
GLState & glState()
get OpenGL state
virtual void makeCurrent()
Makes this widget the current widget for OpenGL operations.
bool animation() const
Is animation enabled?
void rotate(const Vec3d &axis, double angle)
rotate the scene (around its center) and update modelview matrix
unsigned int glWidth() const
get width of QGLWidget
const GLMatrixd & inverse_modelview() const
get inverse modelview matrix
Definition: GLState.hh:811
virtual void flyTo(const QPoint &_pos, bool _move_back)
Fly to. Get closer if _move_back=false, get more distant else.
virtual void updateGL()
Redraw scene. Triggers paint event for updating the view (cf. drawNow()).
virtual void viewMouseEvent(QMouseEvent *_event) override
handle mouse events
void rotate_lights(Vec3d &_axis, double _angle)
rotete light sources
virtual void viewWheelEvent(QWheelEvent *_event) override
handle wheel events
double wheelZoomFactor()
Factors for zooming with the wheel.
virtual void lightMouseEvent(QMouseEvent *_event) override
handle mouse events
const GLMatrixd & modelview() const
get modelview matrix
Definition: GLState.hh:791
void translate(const Vec3d &trans)
translate the scene and update modelview matrix
QtExaminerViewer(QWidget *_parent=0, const char *_name=0, QStatusBar *_statusBar=0, const QGLFormat *_format=0, const QtBaseViewer *_share=0, Options _options=DefaultOptions)
Constructor.
unsigned int glHeight() const
get height of QGLWidget
VectorT< double, 3 > Vec3d
Definition: VectorT.hh:121