manipulatedFrame.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 "manipulatedFrame.h"
00025 #include "qglviewer.h"
00026 #include "camera.h"
00027 
00028 #include <cstdlib>
00029 
00030 #if QT_VERSION >= 0x040000
00031 # include <QMouseEvent>
00032 #endif
00033 
00034 using namespace qglviewer;
00035 using namespace std;
00036 
00044 ManipulatedFrame::ManipulatedFrame()
00045   : action_(QGLViewer::NO_MOUSE_ACTION), keepsGrabbingMouse_(false)
00046 {
00047   // #CONNECTION# initFromDOMElement and accessor docs
00048   setRotationSensitivity(1.0f);
00049   setTranslationSensitivity(1.0f);
00050   setSpinningSensitivity(0.3f);
00051   setWheelSensitivity(1.0f);
00052 
00053   isSpinning_ = false;
00054   previousConstraint_ = NULL;
00055 
00056   connect(&spinningTimer_, SIGNAL(timeout()), SLOT(spinUpdate()));
00057 }
00058 
00060 ManipulatedFrame& ManipulatedFrame::operator=(const ManipulatedFrame& mf)
00061 {
00062   Frame::operator=(mf);
00063 
00064   setRotationSensitivity(mf.rotationSensitivity());
00065   setTranslationSensitivity(mf.translationSensitivity());
00066   setSpinningSensitivity(mf.spinningSensitivity());
00067   setWheelSensitivity(mf.wheelSensitivity());
00068 
00069   mouseSpeed_ = 0.0;
00070   dirIsFixed_ = false;
00071   keepsGrabbingMouse_ = false;
00072 
00073   return *this;
00074 }
00075 
00077 ManipulatedFrame::ManipulatedFrame(const ManipulatedFrame& mf)
00078   : Frame(mf), MouseGrabber()
00079 {
00080   (*this)=mf;
00081 }
00082 
00084 
00091 void ManipulatedFrame::checkIfGrabsMouse(int x, int y, const Camera* const camera)
00092 {
00093   const int thresold = 10;
00094   const Vec proj = camera->projectedCoordinatesOf(position());
00095   setGrabsMouse(keepsGrabbingMouse_ || ((fabs(x-proj.x) < thresold) && (fabs(y-proj.y) < thresold)));
00096 }
00097 
00099 //          S t a t e   s a v i n g   a n d   r e s t o r i n g               //
00101 
00114 QDomElement ManipulatedFrame::domElement(const QString& name, QDomDocument& document) const
00115 {
00116   QDomElement e = Frame::domElement(name, document);
00117   QDomElement mp = document.createElement("ManipulatedParameters");
00118   mp.setAttribute("rotSens", QString::number(rotationSensitivity()));
00119   mp.setAttribute("transSens", QString::number(translationSensitivity()));
00120   mp.setAttribute("spinSens", QString::number(spinningSensitivity()));
00121   mp.setAttribute("wheelSens", QString::number(wheelSensitivity()));
00122   e.appendChild(mp);
00123   return e;
00124 }
00125 
00135 void ManipulatedFrame::initFromDOMElement(const QDomElement& element)
00136 {
00137   // Not called since it would set constraint() and referenceFrame() to NULL.
00138   // *this = ManipulatedFrame();
00139   Frame::initFromDOMElement(element);
00140 
00141   stopSpinning();
00142 
00143   QDomElement child=element.firstChild().toElement();
00144   while (!child.isNull())
00145     {
00146       if (child.tagName() == "ManipulatedParameters")
00147         {
00148           // #CONNECTION# constructor default values and accessor docs
00149           setRotationSensitivity   (DomUtils::floatFromDom(child, "rotSens",   1.0f));
00150           setTranslationSensitivity(DomUtils::floatFromDom(child, "transSens", 1.0f));
00151           setSpinningSensitivity   (DomUtils::floatFromDom(child, "spinSens",  0.3f));
00152           setWheelSensitivity      (DomUtils::floatFromDom(child, "wheelSens", 1.0f));
00153         }
00154       child = child.nextSibling().toElement();
00155     }
00156 }
00157 
00158 
00160 //                 M o u s e    h a n d l i n g                               //
00162 
00170 bool ManipulatedFrame::isManipulated() const
00171 {
00172   return action_ != QGLViewer::NO_MOUSE_ACTION;
00173 }
00174 
00179 void ManipulatedFrame::startSpinning(int updateInterval)
00180 {
00181   isSpinning_ = true;
00182   spinningTimer_.start(updateInterval);
00183 }
00184 
00187 void ManipulatedFrame::spin()
00188 {
00189   rotate(spinningQuaternion());
00190 }
00191 
00192 /* spin() and spinUpdate() differ since spin can be used by itself (for instance by
00193    QGLViewer::SCREEN_ROTATE) without a spun emission. Much nicer to use the spinningQuaternion() and
00194    hence spin() for these incremental updates. Nothing special to be done for continuous spinning
00195    with this design. */
00196 void ManipulatedFrame::spinUpdate()
00197 {
00198   spin();
00199   Q_EMIT spun();
00200 }
00201 
00202 #ifndef DOXYGEN
00203 
00204 void ManipulatedFrame::startAction(int ma, bool withConstraint)
00205 {
00206   action_ = (QGLViewer::MouseAction)(ma);
00207 
00208   // #CONNECTION# manipulatedFrame::wheelEvent, manipulatedCameraFrame::wheelEvent and mouseReleaseEvent()
00209   // restore previous constraint
00210   if (withConstraint)
00211     previousConstraint_ = NULL;
00212   else
00213     {
00214       previousConstraint_ = constraint();
00215       setConstraint(NULL);
00216     }
00217 
00218   switch (action_)
00219     {
00220     case QGLViewer::ROTATE:
00221     case QGLViewer::SCREEN_ROTATE:
00222       mouseSpeed_ = 0.0;
00223       stopSpinning();
00224       break;
00225 
00226     case QGLViewer::SCREEN_TRANSLATE:
00227       dirIsFixed_ = false;
00228       break;
00229 
00230     default:
00231       break;
00232     }
00233 }
00234 
00237 void ManipulatedFrame::computeMouseSpeed(const QMouseEvent* const e)
00238 {
00239   const QPoint delta = (e->pos() - prevPos_);
00240   const float dist = sqrt(static_cast<float>(delta.x()*delta.x() + delta.y()*delta.y()));
00241   delay_ = last_move_time.restart();
00242   if (delay_ == 0)
00243     // Less than a millisecond: assume delay = 1ms
00244     mouseSpeed_ = dist;
00245   else
00246     mouseSpeed_ = dist/delay_;
00247 }
00248 
00251 int ManipulatedFrame::mouseOriginalDirection(const QMouseEvent* const e)
00252 {
00253   static bool horiz = true; // Two simultaneous manipulatedFrame require two mice !
00254 
00255   if (!dirIsFixed_)
00256     {
00257       const QPoint delta = e->pos() - pressPos_;
00258       dirIsFixed_ = abs(delta.x()) != abs(delta.y());
00259       horiz = abs(delta.x()) > abs(delta.y());
00260     }
00261 
00262   if (dirIsFixed_)
00263     if (horiz)
00264       return 1;
00265     else
00266       return -1;
00267   else
00268     return 0;
00269 }
00270 #endif // DOXYGEN
00271 
00278 void ManipulatedFrame::mousePressEvent(QMouseEvent* const event, Camera* const camera)
00279 {
00280   Q_UNUSED(camera);
00281 
00282   if (grabsMouse())
00283     keepsGrabbingMouse_ = true;
00284 
00285   // #CONNECTION setMouseBinding
00286   // action_ should no longer possibly be NO_MOUSE_ACTION since this value is not inserted in mouseBinding_
00287   //#if QT_VERSION >= 0x030000
00288   //if (action_ == QGLViewer::NO_MOUSE_ACTION)
00289   //event->ignore();
00290   //#endif
00291 
00292   prevPos_ = pressPos_ = event->pos();
00293 }
00294 
00304 void ManipulatedFrame::mouseMoveEvent(QMouseEvent* const event, Camera* const camera)
00305 {
00306   switch (action_)
00307     {
00308     case QGLViewer::TRANSLATE:
00309       {
00310         const QPoint delta = event->pos() - prevPos_;
00311         Vec trans(static_cast<float>(delta.x()), static_cast<float>(-delta.y()), 0.0);
00312         // Scale to fit the screen mouse displacement
00313         switch (camera->type())
00314           {
00315           case Camera::PERSPECTIVE :
00316             trans *= 2.0 * tan(camera->fieldOfView()/2.0) * fabs((camera->frame()->coordinatesOf(position())).z) / camera->screenHeight();
00317             break;
00318           case Camera::ORTHOGRAPHIC :
00319             {
00320               GLdouble w,h;
00321               camera->getOrthoWidthHeight(w, h);
00322               trans[0] *= 2.0 * w / camera->screenWidth();
00323               trans[1] *= 2.0 * h / camera->screenHeight();
00324               break;
00325             }
00326           }
00327         // Transform to world coordinate system.
00328         trans = camera->frame()->orientation().rotate(translationSensitivity()*trans);
00329         // And then down to frame
00330         if (referenceFrame()) trans = referenceFrame()->transformOf(trans);
00331         translate(trans);
00332         break;
00333       }
00334 
00335     case QGLViewer::ZOOM:
00336       {
00337         //#CONNECTION# wheelEvent ZOOM case
00338         Vec trans(0.0, 0.0, (camera->position()-position()).norm() * (event->y() - prevPos_.y()) / camera->screenHeight());
00339 
00340         trans = camera->frame()->orientation().rotate(trans);
00341         if (referenceFrame())
00342           trans = referenceFrame()->transformOf(trans);
00343         translate(trans);
00344         break;
00345       }
00346 
00347     case QGLViewer::SCREEN_ROTATE:
00348       {
00349         Vec trans = camera->projectedCoordinatesOf(position());
00350 
00351         const double prev_angle = atan2(prevPos_.y()-trans[1], prevPos_.x()-trans[0]);
00352         const double      angle = atan2(event->y()-trans[1], event->x()-trans[0]);
00353 
00354         const Vec axis = transformOf(camera->frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0)));
00355         Quaternion rot(axis, angle-prev_angle);
00356         //#CONNECTION# These two methods should go together (spinning detection and activation)
00357         computeMouseSpeed(event);
00358         setSpinningQuaternion(rot);
00359         spin();
00360         break;
00361       }
00362 
00363     case QGLViewer::SCREEN_TRANSLATE:
00364       {
00365         Vec trans;
00366         int dir = mouseOriginalDirection(event);
00367         if (dir == 1)
00368           trans.setValue(static_cast<float>(event->x() - prevPos_.x()), 0.0, 0.0);
00369         else if (dir == -1)
00370           trans.setValue(0.0, static_cast<float>(prevPos_.y() - event->y()), 0.0);
00371 
00372         switch (camera->type())
00373           {
00374           case Camera::PERSPECTIVE :
00375             trans *= 2.0 * tan(camera->fieldOfView()/2.0) * fabs((camera->frame()->coordinatesOf(position())).z) / camera->screenHeight();
00376             break;
00377           case Camera::ORTHOGRAPHIC :
00378             {
00379               GLdouble w,h;
00380               camera->getOrthoWidthHeight(w, h);
00381               trans[0] *= 2.0 * w / camera->screenWidth();
00382               trans[1] *= 2.0 * h / camera->screenHeight();
00383               break;
00384             }
00385           }
00386         // Transform to world coordinate system.
00387         trans = camera->frame()->orientation().rotate(translationSensitivity()*trans);
00388         // And then down to frame
00389         if (referenceFrame())
00390           trans = referenceFrame()->transformOf(trans);
00391 
00392         translate(trans);
00393         break;
00394       }
00395 
00396     case QGLViewer::ROTATE:
00397       {
00398         Vec trans = camera->projectedCoordinatesOf(position());
00399         Quaternion rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], camera);
00400         trans = Vec(-rot[0], -rot[1], -rot[2]);
00401         trans = camera->frame()->orientation().rotate(trans);
00402         trans = transformOf(trans);
00403         rot[0] = trans[0];
00404         rot[1] = trans[1];
00405         rot[2] = trans[2];
00406         //#CONNECTION# These two methods should go together (spinning detection and activation)
00407         computeMouseSpeed(event);
00408         setSpinningQuaternion(rot);
00409         spin();
00410         break;
00411       }
00412 
00413     case QGLViewer::NO_MOUSE_ACTION:
00414       // Possible when the ManipulatedFrame is a MouseGrabber. This method is then called without startAction
00415       // because of mouseTracking.
00416       break;
00417     }
00418 
00419   if (action_ != QGLViewer::NO_MOUSE_ACTION)
00420     {
00421       prevPos_ = event->pos();
00422       Q_EMIT manipulated();
00423     }
00424 }
00425 
00433 void ManipulatedFrame::mouseReleaseEvent(QMouseEvent* const event, Camera* const camera)
00434 {
00435   Q_UNUSED(event);
00436   Q_UNUSED(camera);
00437 
00438   keepsGrabbingMouse_ = false;
00439 
00440   if (previousConstraint_)
00441     setConstraint(previousConstraint_);
00442 
00443   if (((action_ == QGLViewer::ROTATE) || (action_ == QGLViewer::SCREEN_ROTATE)) && (mouseSpeed_ >= spinningSensitivity()))
00444     startSpinning(delay_);
00445 
00446   action_ = QGLViewer::NO_MOUSE_ACTION;
00447 }
00448 
00454 void ManipulatedFrame::mouseDoubleClickEvent(QMouseEvent* const event, Camera* const camera)
00455 {
00456 #if QT_VERSION >= 0x040000
00457   if (event->modifiers() == Qt::NoModifier)
00458 #else
00459   if (event->state() == Qt::NoButton)
00460 #endif
00461     switch (event->button())
00462       {
00463       case Qt::LeftButton:  alignWithFrame(camera->frame()); break;
00464       case Qt::RightButton: projectOnLine(camera->position(), camera->viewDirection()); break;
00465       default: break;
00466       }
00467 }
00468 
00473 void ManipulatedFrame::wheelEvent(QWheelEvent* const event, Camera* const camera)
00474 {
00475   //#CONNECTION# QGLViewer::setWheelBinding
00476   if (action_ == QGLViewer::ZOOM)
00477     {
00478       const float wheelSensitivityCoef = 8E-4f;
00479       Vec trans(0.0, 0.0, -event->delta()*wheelSensitivity()*wheelSensitivityCoef*(camera->position()-position()).norm());
00480 
00481       //#CONNECTION# Cut-pasted from the mouseMoveEvent ZOOM case
00482       trans = camera->frame()->orientation().rotate(trans);
00483       if (referenceFrame())
00484         trans = referenceFrame()->transformOf(trans);
00485       translate(trans);
00486       Q_EMIT manipulated();
00487     }
00488 
00489   // #CONNECTION# startAction should always be called before
00490   if (previousConstraint_)
00491     setConstraint(previousConstraint_);
00492 
00493   action_ = QGLViewer::NO_MOUSE_ACTION;
00494 }
00495 
00496 
00498 
00503 static float projectOnBall(float x, float y)
00504 {
00505   // If you change the size value, change angle computation in deformedBallQuaternion().
00506   const float size       = 1.0f;
00507   const float size2      = size*size;
00508   const float size_limit = size2*0.5;
00509 
00510   const float d = x*x + y*y;
00511   return d < size_limit ? sqrt(size2 - d) : size_limit/sqrt(d);
00512 }
00513 
00514 #ifndef DOXYGEN
00515 
00517 Quaternion ManipulatedFrame::deformedBallQuaternion(int x, int y, float cx, float cy, const Camera* const camera)
00518 {
00519   // Points on the deformed ball
00520   float px = rotationSensitivity() * (prevPos_.x()  - cx) / camera->screenWidth();
00521   float py = rotationSensitivity() * (cy - prevPos_.y())  / camera->screenHeight();
00522   float dx = rotationSensitivity() * (x - cx)       / camera->screenWidth();
00523   float dy = rotationSensitivity() * (cy - y)       / camera->screenHeight();
00524 
00525   const Vec p1(px, py, projectOnBall(px, py));
00526   const Vec p2(dx, dy, projectOnBall(dx, dy));
00527   // Approximation of rotation angle
00528   // Should be divided by the projectOnBall size, but it is 1.0
00529   const Vec axis = cross(p2,p1);
00530   const float angle = 2.0 * asin(sqrt(axis.squaredNorm() / p1.squaredNorm() / p2.squaredNorm()));
00531   return Quaternion(axis, angle);
00532 }
00533 #endif // DOXYGEN


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