manipulatedCameraFrame.cpp
1 /****************************************************************************
2 
3  Copyright (C) 2002-2013 Gilles Debunne. All rights reserved.
4 
5  This file is part of the QGLViewer library version 2.5.2.
6 
7  http://www.libqglviewer.com - contact@libqglviewer.com
8 
9  This file may be used under the terms of the GNU General Public License
10  versions 2.0 or 3.0 as published by the Free Software Foundation and
11  appearing in the LICENSE file included in the packaging of this file.
12  In addition, as a special exception, Gilles Debunne gives you certain
13  additional rights, described in the file GPL_EXCEPTION in this package.
14 
15  libQGLViewer uses dual licensing. Commercial/proprietary software must
16  purchase a libQGLViewer Commercial License.
17 
18  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
19  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 
21 *****************************************************************************/
22 
23 #include "domUtils.h"
24 #include "manipulatedCameraFrame.h"
25 #include "qglviewer.h"
26 
27 #include <QMouseEvent>
28 
29 using namespace qglviewer;
30 using namespace std;
31 
38  : driveSpeed_(0.0), sceneUpVector_(0.0, 1.0, 0.0), rotatesAroundUpVector_(false), zoomsOnPivotPoint_(false)
39 {
40  setFlySpeed(0.0);
42  connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate()));
43 }
44 
47 {
49 
50  setFlySpeed(mcf.flySpeed());
52  setRotatesAroundUpVector(mcf.rotatesAroundUpVector_);
53  setZoomsOnPivotPoint(mcf.zoomsOnPivotPoint_);
54 
55  return *this;
56 }
57 
60  : ManipulatedFrame(mcf)
61 {
63  connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate()));
64  (*this)=(mcf);
65 }
66 
68 
73 {
75 }
76 
77 #ifndef DOXYGEN
78 
80 void ManipulatedCameraFrame::flyUpdate()
81 {
82  static Vec flyDisp(0.0, 0.0, 0.0);
83  switch (action_)
84  {
85  case QGLViewer::MOVE_FORWARD:
86  flyDisp.z = -flySpeed();
88  break;
89  case QGLViewer::MOVE_BACKWARD:
90  flyDisp.z = flySpeed();
92  break;
93  case QGLViewer::DRIVE:
94  flyDisp.z = flySpeed() * driveSpeed_;
96  break;
97  default:
98  break;
99  }
100 
101  // Needs to be out of the switch since ZOOM/fastDraw()/wheelEvent use this callback to trigger a final draw().
102  // #CONNECTION# wheelEvent.
103  Q_EMIT manipulated();
104 }
105 
106 Vec ManipulatedCameraFrame::flyUpVector() const {
107  qWarning("flyUpVector() is deprecated. Use sceneUpVector() instead.");
108  return sceneUpVector();
109 }
110 
111 void ManipulatedCameraFrame::setFlyUpVector(const Vec& up) {
112  qWarning("setFlyUpVector() is deprecated. Use setSceneUpVector() instead.");
113  setSceneUpVector(up);
114 }
115 
116 #endif
117 
120 void ManipulatedCameraFrame::updateSceneUpVector()
121 {
122  sceneUpVector_ = inverseTransformOf(Vec(0.0, 1.0, 0.0));
123 }
124 
126 // S t a t e s a v i n g a n d r e s t o r i n g //
128 
142 QDomElement ManipulatedCameraFrame::domElement(const QString& name, QDomDocument& document) const
143 {
144  QDomElement e = ManipulatedFrame::domElement(name, document);
145  QDomElement mcp = document.createElement("ManipulatedCameraParameters");
146  mcp.setAttribute("flySpeed", QString::number(flySpeed()));
147  DomUtils::setBoolAttribute(mcp, "rotatesAroundUpVector", rotatesAroundUpVector());
148  DomUtils::setBoolAttribute(mcp, "zoomsOnPivotPoint", zoomsOnPivotPoint());
149  mcp.appendChild(sceneUpVector().domElement("sceneUpVector", document));
150  e.appendChild(mcp);
151  return e;
152 }
153 
158 void ManipulatedCameraFrame::initFromDOMElement(const QDomElement& element)
159 {
160  // No need to initialize, since default sceneUpVector and flySpeed are not meaningful.
161  // It's better to keep current ones. And it would destroy constraint() and referenceFrame().
162  // *this = ManipulatedCameraFrame();
164 
165  QDomElement child=element.firstChild().toElement();
166  while (!child.isNull())
167  {
168  if (child.tagName() == "ManipulatedCameraParameters")
169  {
170  setFlySpeed(DomUtils::floatFromDom(child, "flySpeed", flySpeed()));
171  setRotatesAroundUpVector(DomUtils::boolFromDom(child, "rotatesAroundUpVector", false));
172  setZoomsOnPivotPoint(DomUtils::boolFromDom(child, "zoomsOnPivotPoint", false));
173 
174  QDomElement schild=child.firstChild().toElement();
175  while (!schild.isNull())
176  {
177  if (schild.tagName() == "sceneUpVector")
178  setSceneUpVector(Vec(schild));
179 
180  schild = schild.nextSibling().toElement();
181  }
182  }
183  child = child.nextSibling().toElement();
184  }
185 }
186 
187 
189 // M o u s e h a n d l i n g //
191 
192 #ifndef DOXYGEN
193 
194 void ManipulatedCameraFrame::startAction(int ma, bool withConstraint)
195 {
196  ManipulatedFrame::startAction(ma, withConstraint);
197 
198  switch (action_)
199  {
200  case QGLViewer::MOVE_FORWARD:
201  case QGLViewer::MOVE_BACKWARD:
202  case QGLViewer::DRIVE:
203  flyTimer_.setSingleShot(false);
204  flyTimer_.start(10);
205  break;
206  case QGLViewer::ROTATE:
207  constrainedRotationIsReversed_ = transformOf(sceneUpVector_).y < 0.0;
208  break;
209  default:
210  break;
211  }
212 }
213 
214 void ManipulatedCameraFrame::zoom(float delta, const Camera * const camera) {
215  const float sceneRadius = camera->sceneRadius();
216  if (zoomsOnPivotPoint_) {
217  Vec direction = position() - camera->pivotPoint();
218  if (direction.norm() > 0.02f * sceneRadius || delta > 0.0f)
219  translate(delta * direction);
220  } else {
221  const float coef = qMax(fabsf((camera->frame()->coordinatesOf(camera->pivotPoint())).z), 0.2f * sceneRadius);
222  Vec trans(0.0, 0.0, -coef * delta);
224  }
225 }
226 
227 #endif
228 
233 void ManipulatedCameraFrame::mouseMoveEvent(QMouseEvent* const event, Camera* const camera)
234 {
235  // #CONNECTION# QGLViewer::mouseMoveEvent does the update().
236  switch (action_)
237  {
238  case QGLViewer::TRANSLATE:
239  {
240  const QPoint delta = prevPos_ - event->pos();
241  Vec trans(static_cast<float>(delta.x()), static_cast<float>(-delta.y()), 0.0);
242  // Scale to fit the screen mouse displacement
243  switch (camera->type())
244  {
245  case Camera::PERSPECTIVE :
246  trans *= 2.0 * tan(camera->fieldOfView()/2.0) *
247  fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / camera->screenHeight();
248  break;
249  case Camera::ORTHOGRAPHIC :
250  {
251  GLdouble w,h;
252  camera->getOrthoWidthHeight(w, h);
253  trans[0] *= 2.0 * w / camera->screenWidth();
254  trans[1] *= 2.0 * h / camera->screenHeight();
255  break;
256  }
257  }
259  break;
260  }
261 
262  case QGLViewer::MOVE_FORWARD:
263  {
264  Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
265  rotate(rot);
266  //#CONNECTION# wheelEvent MOVE_FORWARD case
267  // actual translation is made in flyUpdate().
268  //translate(inverseTransformOf(Vec(0.0, 0.0, -flySpeed())));
269  break;
270  }
271 
272  case QGLViewer::MOVE_BACKWARD:
273  {
274  Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
275  rotate(rot);
276  // actual translation is made in flyUpdate().
277  //translate(inverseTransformOf(Vec(0.0, 0.0, flySpeed())));
278  break;
279  }
280 
281  case QGLViewer::DRIVE:
282  {
283  Quaternion rot = turnQuaternion(event->x(), camera);
284  rotate(rot);
285  // actual translation is made in flyUpdate().
286  driveSpeed_ = 0.01 * (event->y() - pressPos_.y());
287  break;
288  }
289 
290  case QGLViewer::ZOOM:
291  {
292  zoom(deltaWithPrevPos(event, camera), camera);
293  break;
294  }
295 
296  case QGLViewer::LOOK_AROUND:
297  {
298  Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
299  rotate(rot);
300  break;
301  }
302 
303  case QGLViewer::ROTATE:
304  {
305  Quaternion rot;
306  if (rotatesAroundUpVector_) {
307  // Multiply by 2.0 to get on average about the same speed as with the deformed ball
308  float dx = 2.0f * rotationSensitivity() * (prevPos_.x() - event->x()) / camera->screenWidth();
309  float dy = 2.0f * rotationSensitivity() * (prevPos_.y() - event->y()) / camera->screenHeight();
310  if (constrainedRotationIsReversed_) dx = -dx;
311  Vec verticalAxis = transformOf(sceneUpVector_);
312  rot = Quaternion(verticalAxis, dx) * Quaternion(Vec(1.0, 0.0, 0.0), dy);
313  } else {
314  Vec trans = camera->projectedCoordinatesOf(pivotPoint());
315  rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], camera);
316  }
317  //#CONNECTION# These two methods should go together (spinning detection and activation)
318  computeMouseSpeed(event);
320  spin();
321  break;
322  }
323 
324  case QGLViewer::SCREEN_ROTATE:
325  {
326  Vec trans = camera->projectedCoordinatesOf(pivotPoint());
327 
328  const float angle = atan2(event->y() - trans[1], event->x() - trans[0]) - atan2(prevPos_.y()-trans[1], prevPos_.x()-trans[0]);
329 
330  Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
331  //#CONNECTION# These two methods should go together (spinning detection and activation)
332  computeMouseSpeed(event);
334  spin();
335  updateSceneUpVector();
336  break;
337  }
338 
339  case QGLViewer::ROLL:
340  {
341  const float angle = M_PI * (event->x() - prevPos_.x()) / camera->screenWidth();
342  Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
343  rotate(rot);
345  updateSceneUpVector();
346  break;
347  }
348 
349  case QGLViewer::SCREEN_TRANSLATE:
350  {
351  Vec trans;
352  int dir = mouseOriginalDirection(event);
353  if (dir == 1)
354  trans.setValue(static_cast<float>(prevPos_.x() - event->x()), 0.0, 0.0);
355  else if (dir == -1)
356  trans.setValue(0.0, static_cast<float>(event->y() - prevPos_.y()), 0.0);
357 
358  switch (camera->type())
359  {
360  case Camera::PERSPECTIVE :
361  trans *= 2.0 * tan(camera->fieldOfView()/2.0) *
362  fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / camera->screenHeight();
363  break;
364  case Camera::ORTHOGRAPHIC :
365  {
366  GLdouble w,h;
367  camera->getOrthoWidthHeight(w, h);
368  trans[0] *= 2.0 * w / camera->screenWidth();
369  trans[1] *= 2.0 * h / camera->screenHeight();
370  break;
371  }
372  }
373 
375  break;
376  }
377 
378  case QGLViewer::ZOOM_ON_REGION:
379  case QGLViewer::NO_MOUSE_ACTION:
380  break;
381  }
382 
383  if (action_ != QGLViewer::NO_MOUSE_ACTION)
384  {
385  prevPos_ = event->pos();
386  if (action_ != QGLViewer::ZOOM_ON_REGION)
387  // ZOOM_ON_REGION should not emit manipulated().
388  // prevPos_ is used to draw rectangle feedback.
389  Q_EMIT manipulated();
390  }
391 }
392 
393 
396 void ManipulatedCameraFrame::mouseReleaseEvent(QMouseEvent* const event, Camera* const camera)
397 {
398  if ((action_ == QGLViewer::MOVE_FORWARD) || (action_ == QGLViewer::MOVE_BACKWARD) || (action_ == QGLViewer::DRIVE))
399  flyTimer_.stop();
400 
401  if (action_ == QGLViewer::ZOOM_ON_REGION)
402  camera->fitScreenRegion(QRect(pressPos_, event->pos()));
403 
405 }
406 
413 void ManipulatedCameraFrame::wheelEvent(QWheelEvent* const event, Camera* const camera)
414 {
415  //#CONNECTION# QGLViewer::setWheelBinding, ManipulatedFrame::wheelEvent.
416  switch (action_)
417  {
418  case QGLViewer::ZOOM:
419  {
420  zoom(wheelDelta(event), camera);
421  Q_EMIT manipulated();
422  break;
423  }
424  case QGLViewer::MOVE_FORWARD:
425  case QGLViewer::MOVE_BACKWARD:
426  //#CONNECTION# mouseMoveEvent() MOVE_FORWARD case
427  translate(inverseTransformOf(Vec(0.0, 0.0, 0.2*flySpeed()*event->delta())));
428  Q_EMIT manipulated();
429  break;
430  default:
431  break;
432  }
433 
434  // #CONNECTION# startAction should always be called before
435  if (previousConstraint_)
436  setConstraint(previousConstraint_);
437 
438  // The wheel triggers a fastDraw. A final update() is needed after the last wheel event to
439  // polish the rendering using draw(). Since the last wheel event does not say its name, we use
440  // the flyTimer_ to trigger flyUpdate(), which emits manipulated. Two wheel events
441  // separated by more than this delay milliseconds will trigger a draw().
442  const int finalDrawAfterWheelEventDelay = 400;
443 
444  // Starts (or prolungates) the timer.
445  flyTimer_.setSingleShot(true);
446  flyTimer_.start(finalDrawAfterWheelEventDelay);
447 
448  // This could also be done *before* manipulated is emitted, so that isManipulated() returns false.
449  // But then fastDraw would not be used with wheel.
450  // Detecting the last wheel event and forcing a final draw() is done using the timer_.
451  action_ = QGLViewer::NO_MOUSE_ACTION;
452 }
453 
455 
457 Quaternion ManipulatedCameraFrame::turnQuaternion(int x, const Camera* const camera)
458 {
459  return Quaternion(Vec(0.0, 1.0, 0.0), rotationSensitivity()*(prevPos_.x()-x)/camera->screenWidth());
460 }
461 
464 Quaternion ManipulatedCameraFrame::pitchYawQuaternion(int x, int y, const Camera* const camera)
465 {
466  const Quaternion rotX(Vec(1.0, 0.0, 0.0), rotationSensitivity()*(prevPos_.y()-y)/camera->screenHeight());
467  const Quaternion rotY(transformOf(sceneUpVector()), rotationSensitivity()*(prevPos_.x()-x)/camera->screenWidth());
468  return rotY * rotX;
469 }
void fitScreenRegion(const QRect &rectangle)
Moves the Camera so that the rectangular screen region defined by rectangle (pixel units...
Definition: camera.cpp:1058
Vec projectedCoordinatesOf(const Vec &src, const Frame *frame=NULL) const
Returns the screen projected coordinates of a point src defined in the frame coordinate system...
Definition: camera.cpp:1584
ManipulatedCameraFrame & operator=(const ManipulatedCameraFrame &mcf)
Equal operator.
The ManipulatedCameraFrame class represents a ManipulatedFrame with Camera specific mouse bindings...
float deltaWithPrevPos(QMouseEvent *const event, Camera *const camera) const
Returns a screen scaled delta from event's position to prevPos_, along the X or Y direction...
virtual void initFromDOMElement(const QDomElement &element)
Restores the ManipulatedFrame state from a QDomElement created by domElement().
Vec pivotPoint() const
The point the Camera pivots around with the QGLViewer::ROTATE mouse binding.
Definition: camera.cpp:1275
A ManipulatedFrame is a Frame that can be rotated and translated using the mouse. ...
float flySpeed() const
Returns the fly speed, expressed in OpenGL units.
bool rotatesAroundUpVector() const
Returns true when the frame's rotation is constrained around the sceneUpVector(), and false otherwise...
void translate(Vec &t)
Same as translate(const Vec&) but t may be modified to satisfy the translation constraint().
Definition: frame.cpp:335
Quaternion spinningQuaternion() const
Returns the incremental rotation that is applied by spin() to the ManipulatedFrame orientation when i...
Vec pivotPoint() const
Returns the point the ManipulatedCameraFrame pivot point, around which the camera rotates...
double norm() const
Returns the norm of the vector.
Definition: vec.h:335
int screenWidth() const
Returns the width (in pixels) of the Camera screen.
Definition: camera.h:190
void rotateAroundPoint(Quaternion &rotation, const Vec &point)
Makes the Frame rotate() by rotation around point.
Definition: frame.cpp:414
void setRotatesAroundUpVector(bool constrained)
Sets the value of rotatesAroundUpVector().
ManipulatedCameraFrame * frame() const
Returns the ManipulatedCameraFrame attached to the Camera.
Definition: camera.h:334
float rotationSensitivity() const
Returns the influence of a mouse displacement on the ManipulatedFrame rotation.
int screenHeight() const
Returns the height (in pixels) of the Camera screen.
Definition: camera.h:195
Vec coordinatesOf(const Vec &src) const
Returns the Frame coordinates of a point src defined in the world coordinate system (converts from wo...
Definition: frame.cpp:702
float fieldOfView() const
Returns the vertical field of view of the Camera (in radians).
Definition: camera.h:170
float wheelDelta(const QWheelEvent *event) const
Returns a normalized wheel delta, proportionnal to wheelSensitivity().
The Vec class represents 3D positions and 3D vectors.
Definition: vec.h:65
void rotate(Quaternion &q)
Same as rotate(const Quaternion&) but q may be modified to satisfy the rotation constraint().
Definition: frame.cpp:376
virtual void getOrthoWidthHeight(GLdouble &halfWidth, GLdouble &halfHeight) const
Returns the halfWidth and halfHeight of the Camera orthographic frustum.
Definition: camera.cpp:324
virtual QDomElement domElement(const QString &name, QDomDocument &document) const
Returns an XML QDomElement that represents the ManipulatedCameraFrame.
void setFlySpeed(float speed)
Sets the flySpeed(), defined in OpenGL units.
virtual void startAction(int ma, bool withConstraint=true)
Protected internal method used to handle mouse events.
float translationSensitivity() const
Returns the influence of a mouse displacement on the ManipulatedFrame translation.
virtual void mouseReleaseEvent(QMouseEvent *const event, Camera *const camera)
Stops the ManipulatedFrame mouse manipulation.
void removeFromMouseGrabberPool()
Removes the MouseGrabber from the MouseGrabberPool().
void setValue(double X, double Y, double Z)
Set the current value.
Definition: vec.h:125
void setSpinningQuaternion(const Quaternion &spinningQuaternion)
Defines the spinningQuaternion().
A perspective or orthographic camera.
Definition: camera.h:84
Vec localInverseTransformOf(const Vec &src) const
Returns the referenceFrame() transform of a vector src defined in the Frame coordinate system (conver...
Definition: frame.cpp:881
The Quaternion class represents 3D rotations and orientations.
Definition: quaternion.h:66
void manipulated()
This signal is emitted when ever the ManipulatedFrame is manipulated (i.e.
Type type() const
Returns the Camera::Type of the Camera.
Definition: camera.h:158
void setConstraint(Constraint *const constraint)
Sets the constraint() attached to the Frame.
Definition: frame.h:361
virtual void wheelEvent(QWheelEvent *const event, Camera *const camera)
This is an overload of ManipulatedFrame::wheelEvent().
virtual QDomElement domElement(const QString &name, QDomDocument &document) const
Returns an XML QDomElement that represents the ManipulatedFrame.
Quaternion deformedBallQuaternion(int x, int y, float cx, float cy, const Camera *const camera)
Returns a quaternion computed according to the mouse motion.
int mouseOriginalDirection(const QMouseEvent *const e)
Return 1 if mouse motion was started horizontally and -1 if it was more vertical. ...
ManipulatedFrame & operator=(const ManipulatedFrame &mf)
Equal operator.
void setZoomsOnPivotPoint(bool enabled)
Sets the value of zoomsOnPivotPoint().
Vec transformOf(const Vec &src) const
Returns the Frame transform of a vector src defined in the world coordinate system (converts vectors ...
Definition: frame.cpp:843
Vec position() const
Returns the position of the Frame, defined in the world coordinate system.
Definition: frame.cpp:537
float sceneRadius() const
Returns the radius of the scene observed by the Camera.
Definition: camera.h:284
Vec inverseTransformOf(const Vec &src) const
Returns the world transform of the vector whose coordinates in the Frame coordinate system is src (co...
Definition: frame.cpp:856
void computeMouseSpeed(const QMouseEvent *const e)
Updates mouse speed, measured in pixels/milliseconds.
virtual void mouseReleaseEvent(QMouseEvent *const event, Camera *const camera)
This is an overload of ManipulatedFrame::mouseReleaseEvent().
virtual void startAction(int ma, bool withConstraint=true)
Protected internal method used to handle mouse events.
Vec sceneUpVector() const
Returns the up vector of the scene, expressed in the world coordinate system.
bool zoomsOnPivotPoint() const
Returns whether or not the QGLViewer::ZOOM action zooms on the pivot point.
virtual void mouseMoveEvent(QMouseEvent *const event, Camera *const camera)
Overloading of ManipulatedFrame::mouseMoveEvent().
virtual void initFromDOMElement(const QDomElement &element)
Restores the ManipulatedCameraFrame state from a QDomElement created by domElement().
void setSceneUpVector(const Vec &up)
Sets the sceneUpVector(), defined in the world coordinate system.
virtual void spin()
Overloading of ManipulatedFrame::spin().