manipulatedCameraFrame.cpp
Go to the documentation of this file.
00001 /****************************************************************************
00002 
00003  Copyright (C) 2002-2013 Gilles Debunne. All rights reserved.
00004 
00005  This file is part of the QGLViewer library version 2.4.0.
00006 
00007  http://www.libqglviewer.com - contact@libqglviewer.com
00008 
00009  This file may be used under the terms of the GNU General Public License 
00010  versions 2.0 or 3.0 as published by the Free Software Foundation and
00011  appearing in the LICENSE file included in the packaging of this file.
00012  In addition, as a special exception, Gilles Debunne gives you certain 
00013  additional rights, described in the file GPL_EXCEPTION in this package.
00014 
00015  libQGLViewer uses dual licensing. Commercial/proprietary software must
00016  purchase a libQGLViewer Commercial License.
00017 
00018  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00019  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00020 
00021 *****************************************************************************/
00022 
00023 #include "domUtils.h"
00024 #include "manipulatedCameraFrame.h"
00025 #include "qglviewer.h"
00026 
00027 #if QT_VERSION >= 0x040000
00028 # include <QMouseEvent>
00029 #endif
00030 
00031 using namespace qglviewer;
00032 using namespace std;
00033 
00039 ManipulatedCameraFrame::ManipulatedCameraFrame()
00040   : driveSpeed_(0.0), flyUpVector_(0.0, 1.0, 0.0)
00041 {
00042   setFlySpeed(0.0);
00043   removeFromMouseGrabberPool();
00044 
00045   connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate()));
00046 }
00047 
00049 ManipulatedCameraFrame& ManipulatedCameraFrame::operator=(const ManipulatedCameraFrame& mcf)
00050 {
00051   ManipulatedFrame::operator=(mcf);
00052 
00053   setFlySpeed(mcf.flySpeed());
00054   setFlyUpVector(mcf.flyUpVector());
00055 
00056   return *this;
00057 }
00058 
00060 ManipulatedCameraFrame::ManipulatedCameraFrame(const ManipulatedCameraFrame& mcf)
00061   : ManipulatedFrame(mcf)
00062 {
00063   removeFromMouseGrabberPool();
00064   connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate()));
00065   (*this)=(mcf);
00066 }
00067 
00069 
00073 void ManipulatedCameraFrame::spin()
00074 {
00075   rotateAroundPoint(spinningQuaternion(), revolveAroundPoint());
00076 }
00077 
00078 #ifndef DOXYGEN
00079 
00081 void ManipulatedCameraFrame::flyUpdate()
00082 {
00083   static Vec flyDisp(0.0, 0.0, 0.0);
00084   switch (action_)
00085     {
00086     case QGLViewer::MOVE_FORWARD:
00087       flyDisp.z = -flySpeed();
00088       translate(localInverseTransformOf(flyDisp));
00089       break;
00090     case QGLViewer::MOVE_BACKWARD:
00091       flyDisp.z = flySpeed();
00092       translate(localInverseTransformOf(flyDisp));
00093       break;
00094     case QGLViewer::DRIVE:
00095       flyDisp.z = flySpeed() * driveSpeed_;
00096       translate(localInverseTransformOf(flyDisp));
00097       break;
00098     default:
00099       break;
00100     }
00101 
00102   // Needs to be out of the switch since ZOOM/fastDraw()/wheelEvent use this callback to trigger a final draw().
00103   // #CONNECTION# wheelEvent.
00104   Q_EMIT manipulated();
00105 }
00106 #endif
00107 
00110 void ManipulatedCameraFrame::updateFlyUpVector()
00111 {
00112   flyUpVector_ = inverseTransformOf(Vec(0.0, 1.0, 0.0));
00113 }
00114 
00116 //          S t a t e   s a v i n g   a n d   r e s t o r i n g               //
00118 
00132 QDomElement ManipulatedCameraFrame::domElement(const QString& name, QDomDocument& document) const
00133 {
00134   QDomElement e = ManipulatedFrame::domElement(name, document);
00135   QDomElement mcp = document.createElement("ManipulatedCameraParameters");
00136   mcp.setAttribute("flySpeed", QString::number(flySpeed()));
00137   mcp.appendChild(flyUpVector().domElement("flyUpVector", document));
00138   e.appendChild(mcp);
00139   return e;
00140 }
00141 
00146 void ManipulatedCameraFrame::initFromDOMElement(const QDomElement& element)
00147 {
00148   // No need to initialize, since default flyUpVector and flySpeed are not meaningful.
00149   // It's better to keep current ones. And it would destroy constraint() and referenceFrame().
00150   // *this = ManipulatedCameraFrame();
00151   ManipulatedFrame::initFromDOMElement(element);
00152 
00153   QDomElement child=element.firstChild().toElement();
00154   while (!child.isNull())
00155     {
00156       if (child.tagName() == "ManipulatedCameraParameters")
00157         {
00158           setFlySpeed(DomUtils::floatFromDom(child, "flySpeed", flySpeed()));
00159 
00160           QDomElement schild=child.firstChild().toElement();
00161           while (!schild.isNull())
00162             {
00163               if (schild.tagName() == "flyUpVector")
00164                 setFlyUpVector(Vec(schild));
00165 
00166               schild = schild.nextSibling().toElement();
00167             }
00168         }
00169       child = child.nextSibling().toElement();
00170     }
00171 }
00172 
00173 
00175 //                 M o u s e    h a n d l i n g                               //
00177 
00178 #ifndef DOXYGEN
00179 
00180 void ManipulatedCameraFrame::startAction(int ma, bool withConstraint)
00181 {
00182   ManipulatedFrame::startAction(ma, withConstraint);
00183 
00184   switch (action_)
00185     {
00186     case QGLViewer::MOVE_FORWARD:
00187     case QGLViewer::MOVE_BACKWARD:
00188     case QGLViewer::DRIVE:
00189 #if QT_VERSION >= 0x040000
00190       flyTimer_.setSingleShot(false);
00191 #endif
00192       flyTimer_.start(10);
00193       break;
00194     default:
00195       break;
00196     }
00197 }
00198 #endif
00199 
00204 void ManipulatedCameraFrame::mouseMoveEvent(QMouseEvent* const event, Camera* const camera)
00205 {
00206   // #CONNECTION# QGLViewer::mouseMoveEvent does the updateGL.
00207   switch (action_)
00208     {
00209     case QGLViewer::TRANSLATE:
00210       {
00211         const QPoint delta = prevPos_ - event->pos();
00212         Vec trans(static_cast<float>(delta.x()), static_cast<float>(-delta.y()), 0.0);
00213         // Scale to fit the screen mouse displacement
00214         switch (camera->type())
00215           {
00216           case Camera::PERSPECTIVE :
00217             trans *= 2.0 * tan(camera->fieldOfView()/2.0) *
00218               fabs((camera->frame()->coordinatesOf(revolveAroundPoint())).z) / camera->screenHeight();
00219             break;
00220           case Camera::ORTHOGRAPHIC :
00221             {
00222               GLdouble w,h;
00223               camera->getOrthoWidthHeight(w, h);
00224               trans[0] *= 2.0 * w / camera->screenWidth();
00225               trans[1] *= 2.0 * h / camera->screenHeight();
00226               break;
00227             }
00228           }
00229         translate(inverseTransformOf(translationSensitivity()*trans));
00230         break;
00231       }
00232 
00233     case QGLViewer::MOVE_FORWARD:
00234       {
00235         Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
00236         rotate(rot);
00237         //#CONNECTION# wheelEvent MOVE_FORWARD case
00238         // actual translation is made in flyUpdate().
00239         //translate(inverseTransformOf(Vec(0.0, 0.0, -flySpeed())));
00240         break;
00241       }
00242 
00243     case QGLViewer::MOVE_BACKWARD:
00244       {
00245         Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
00246         rotate(rot);
00247         // actual translation is made in flyUpdate().
00248         //translate(inverseTransformOf(Vec(0.0, 0.0, flySpeed())));
00249         break;
00250       }
00251 
00252     case QGLViewer::DRIVE:
00253       {
00254         Quaternion rot = turnQuaternion(event->x(), camera);
00255         rotate(rot);
00256         // actual translation is made in flyUpdate().
00257         driveSpeed_ = 0.01 * (event->y() - pressPos_.y());
00258         break;
00259       }
00260 
00261     case QGLViewer::ZOOM:
00262       {
00263         //#CONNECTION# wheelEvent() ZOOM case
00264         const float coef = qMax(fabsf((camera->frame()->coordinatesOf(camera->revolveAroundPoint())).z), 0.2f*camera->sceneRadius());
00265         Vec trans(0.0, 0.0, -coef * (event->y() - prevPos_.y()) / camera->screenHeight());
00266         translate(inverseTransformOf(trans));
00267         break;
00268       }
00269 
00270     case QGLViewer::LOOK_AROUND:
00271       {
00272         Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
00273         rotate(rot);
00274         break;
00275       }
00276 
00277     case QGLViewer::ROTATE:
00278       {
00279         Vec trans = camera->projectedCoordinatesOf(revolveAroundPoint());
00280         Quaternion rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], camera);
00281         //#CONNECTION# These two methods should go together (spinning detection and activation)
00282         computeMouseSpeed(event);
00283         setSpinningQuaternion(rot);
00284         spin();
00285         break;
00286       }
00287 
00288     case QGLViewer::SCREEN_ROTATE:
00289       {
00290         Vec trans = camera->projectedCoordinatesOf(revolveAroundPoint());
00291 
00292         const float angle = atan2(event->y() - trans[1], event->x() - trans[0]) - atan2(prevPos_.y()-trans[1], prevPos_.x()-trans[0]);
00293 
00294         Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
00295         //#CONNECTION# These two methods should go together (spinning detection and activation)
00296         computeMouseSpeed(event);
00297         setSpinningQuaternion(rot);
00298         spin();
00299         updateFlyUpVector();
00300         break;
00301       }
00302 
00303     case QGLViewer::ROLL:
00304       {
00305         const float angle = M_PI * (event->x() - prevPos_.x()) / camera->screenWidth();
00306         Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
00307         rotate(rot);
00308         setSpinningQuaternion(rot);
00309         updateFlyUpVector();
00310         break;
00311       }
00312 
00313     case QGLViewer::SCREEN_TRANSLATE:
00314       {
00315         Vec trans;
00316         int dir = mouseOriginalDirection(event);
00317         if (dir == 1)
00318           trans.setValue(static_cast<float>(prevPos_.x() - event->x()), 0.0, 0.0);
00319         else if (dir == -1)
00320           trans.setValue(0.0, static_cast<float>(event->y() - prevPos_.y()), 0.0);
00321 
00322         switch (camera->type())
00323           {
00324           case Camera::PERSPECTIVE :
00325             trans *= 2.0 * tan(camera->fieldOfView()/2.0) *
00326               fabs((camera->frame()->coordinatesOf(revolveAroundPoint())).z) / camera->screenHeight();
00327             break;
00328           case Camera::ORTHOGRAPHIC :
00329             {
00330               GLdouble w,h;
00331               camera->getOrthoWidthHeight(w, h);
00332               trans[0] *= 2.0 * w / camera->screenWidth();
00333               trans[1] *= 2.0 * h / camera->screenHeight();
00334               break;
00335             }
00336           }
00337 
00338         translate(inverseTransformOf(translationSensitivity()*trans));
00339         break;
00340       }
00341 
00342     case QGLViewer::ZOOM_ON_REGION:
00343     case QGLViewer::NO_MOUSE_ACTION:
00344       break;
00345     }
00346 
00347   if (action_ != QGLViewer::NO_MOUSE_ACTION)
00348     {
00349       prevPos_ = event->pos();
00350       if (action_ != QGLViewer::ZOOM_ON_REGION)
00351         // ZOOM_ON_REGION should not emit manipulated().
00352         // prevPos_ is used to draw rectangle feedback.
00353         Q_EMIT manipulated();
00354     }
00355 }
00356 
00357 
00360 void ManipulatedCameraFrame::mouseReleaseEvent(QMouseEvent* const event, Camera* const camera)
00361 {
00362   if ((action_ == QGLViewer::MOVE_FORWARD) || (action_ == QGLViewer::MOVE_BACKWARD) || (action_ == QGLViewer::DRIVE))
00363     flyTimer_.stop();
00364 
00365   if (action_ == QGLViewer::ZOOM_ON_REGION)
00366     camera->fitScreenRegion(QRect(pressPos_, event->pos()));
00367 
00368   ManipulatedFrame::mouseReleaseEvent(event, camera);
00369 }
00370 
00377 void ManipulatedCameraFrame::wheelEvent(QWheelEvent* const event, Camera* const camera)
00378 {
00379   //#CONNECTION# QGLViewer::setWheelBinding, ManipulatedFrame::wheelEvent.
00380   switch (action_)
00381     {
00382     case QGLViewer::ZOOM:
00383       {
00384         const float wheelSensitivityCoef = 8E-4f;
00385         //#CONNECTION# mouseMoveEvent() ZOOM case
00386         const float coef = qMax(fabsf((camera->frame()->coordinatesOf(camera->revolveAroundPoint())).z), 0.2f*camera->sceneRadius());
00387         Vec trans(0.0, 0.0, coef * event->delta() * wheelSensitivity() * wheelSensitivityCoef);
00388         translate(inverseTransformOf(trans));
00389         Q_EMIT manipulated();
00390         break;
00391       }
00392     case QGLViewer::MOVE_FORWARD:
00393     case QGLViewer::MOVE_BACKWARD:
00394       //#CONNECTION# mouseMoveEvent() MOVE_FORWARD case
00395       translate(inverseTransformOf(Vec(0.0, 0.0, 0.2*flySpeed()*event->delta())));
00396       Q_EMIT manipulated();
00397       break;
00398     default:
00399       break;
00400     }
00401 
00402   // #CONNECTION# startAction should always be called before
00403   if (previousConstraint_)
00404     setConstraint(previousConstraint_);
00405 
00406   // The wheel triggers a fastDraw. A final updateGL is needed after the last wheel event to
00407   // polish the rendering using draw(). Since the last wheel event does not say its name, we use
00408   // the flyTimer_ to trigger flyUpdate(), which emits manipulated. Two wheel events
00409   // separated by more than this delay milliseconds will trigger a draw().
00410   const int finalDrawAfterWheelEventDelay = 400;
00411 
00412   // Starts (or prolungates) the timer.
00413 #if QT_VERSION >= 0x040000
00414   flyTimer_.setSingleShot(true);
00415   flyTimer_.start(finalDrawAfterWheelEventDelay);
00416 #else
00417   flyTimer_.start(finalDrawAfterWheelEventDelay, true);
00418 #endif
00419 
00420   // This could also be done *before* manipulated is emitted, so that isManipulated() returns false.
00421   // But then fastDraw would not be used with wheel.
00422   // Detecting the last wheel event and forcing a final draw() is done using the timer_.
00423   action_ = QGLViewer::NO_MOUSE_ACTION;
00424 }
00425 
00427 
00429 Quaternion ManipulatedCameraFrame::turnQuaternion(int x, const Camera* const camera)
00430 {
00431   return Quaternion(Vec(0.0, 1.0, 0.0), rotationSensitivity()*(prevPos_.x()-x)/camera->screenWidth());
00432 }
00433 
00436 Quaternion ManipulatedCameraFrame::pitchYawQuaternion(int x, int y, const Camera* const camera)
00437 {
00438   const Quaternion rotX(Vec(1.0, 0.0, 0.0), rotationSensitivity()*(prevPos_.y()-y)/camera->screenHeight());
00439   const Quaternion rotY(transformOf(flyUpVector()), rotationSensitivity()*(prevPos_.x()-x)/camera->screenWidth());
00440   return rotY * rotX;
00441 }


octovis
Author(s): Kai M. Wurm , Armin Hornung
autogenerated on Thu Aug 27 2015 14:13:26