00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
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   
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 
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   
00138   
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           
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 
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 
00193 
00194 
00195 
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   
00209   
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     
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; 
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   
00286   
00287   
00288   
00289   
00290   
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         
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         
00328         trans = camera->frame()->orientation().rotate(translationSensitivity()*trans);
00329         
00330         if (referenceFrame()) trans = referenceFrame()->transformOf(trans);
00331         translate(trans);
00332         break;
00333       }
00334 
00335     case QGLViewer::ZOOM:
00336       {
00337         
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         
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         
00387         trans = camera->frame()->orientation().rotate(translationSensitivity()*trans);
00388         
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         
00407         computeMouseSpeed(event);
00408         setSpinningQuaternion(rot);
00409         spin();
00410         break;
00411       }
00412 
00413     case QGLViewer::NO_MOUSE_ACTION:
00414       
00415       
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   
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       
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   
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   
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   
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   
00528   
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