worldsim/3rdParts/qglviewer/manipulatedCameraFrame.cpp

00001 /****************************************************************************
00002 
00003  Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
00004 
00005  This file is part of the QGLViewer library version 2.3.10.
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 }