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