From 0c74a689522ed527cf50f2261a5e3f778fe8fd9e Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Wed, 28 Mar 2018 15:49:20 +0200 Subject: [PATCH] WIP Header_only --- AABB_tree/demo/AABB_tree/CMakeLists.txt | 4 +- AABB_tree/demo/AABB_tree/MainWindow.cpp | 6 +- AABB_tree/demo/AABB_tree/Refiner.h | 2 +- .../include/CGAL/Qt/AlphaShapeGraphicsItem.h | 359 -- GraphicsView/include/CGAL/Qt/camera.h | 36 +- GraphicsView/include/CGAL/Qt/camera_impl.h | 2487 ++++++++++ GraphicsView/include/CGAL/Qt/config.h | 10 +- GraphicsView/include/CGAL/Qt/constraint.h | 9 +- .../include/CGAL/Qt/constraint_impl.h | 301 ++ GraphicsView/include/CGAL/Qt/frame.h | 12 +- GraphicsView/include/CGAL/Qt/frame_impl.h | 1186 +++++ .../include/CGAL/Qt/keyFrameInterpolator.h | 9 +- .../CGAL/Qt/keyFrameInterpolator_impl.h | 587 +++ .../include/CGAL/Qt/manipulatedCameraFrame.h | 24 +- .../CGAL/Qt/manipulatedCameraFrame_impl.h | 500 ++ .../include/CGAL/Qt/manipulatedFrame.h | 15 +- .../include/CGAL/Qt/manipulatedFrame_impl.h | 592 +++ GraphicsView/include/CGAL/Qt/mouseGrabber.h | 17 +- .../include/CGAL/Qt/mouseGrabber_impl.h | 90 + GraphicsView/include/CGAL/Qt/qglviewer.h | 199 +- GraphicsView/include/CGAL/Qt/qglviewer_impl.h | 4321 +++++++++++++++++ .../include/CGAL/Qt/qglviewer_impl_list.h | 13 + GraphicsView/include/CGAL/Qt/quaternion.h | 2 +- .../include/CGAL/Qt/quaternion_impl.h | 566 +++ GraphicsView/include/CGAL/Qt/vec_impl.h | 181 + GraphicsView/include/CGAL/Qt/viewer_actions.h | 75 + GraphicsView/src/CGAL_Qt5/camera.cpp | 2495 +--------- GraphicsView/src/CGAL_Qt5/constraint.cpp | 259 +- GraphicsView/src/CGAL_Qt5/frame.cpp | 1080 +--- .../src/CGAL_Qt5/keyFrameInterpolator.cpp | 692 +-- .../src/CGAL_Qt5/manipulatedCameraFrame.cpp | 453 +- .../src/CGAL_Qt5/manipulatedFrame.cpp | 540 +- GraphicsView/src/CGAL_Qt5/mouseGrabber.cpp | 52 +- GraphicsView/src/CGAL_Qt5/qglviewer-icon.xpm | 359 -- GraphicsView/src/CGAL_Qt5/qglviewer.cpp | 4118 +--------------- GraphicsView/src/CGAL_Qt5/quaternion.cpp | 504 +- GraphicsView/src/CGAL_Qt5/vec.cpp | 143 +- .../CGAL_Qt5_moc_and_resource_files.cmake | 27 +- .../CGAL_SetupCGAL_Qt5Dependencies.cmake | 18 +- .../Polyhedron/Plugins/Mesh_3/Volume_plane.h | 8 +- .../Plugins/PCA/Scene_edit_box_item.cpp | 10 +- Polyhedron/demo/Polyhedron/Polyhedron_3.cpp | 2 +- Polyhedron/demo/Polyhedron/Viewer.cpp | 9 +- Polyhedron/demo/Polyhedron/Viewer.h | 6 +- 44 files changed, 11105 insertions(+), 11273 deletions(-) create mode 100644 GraphicsView/include/CGAL/Qt/camera_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/constraint_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/frame_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/keyFrameInterpolator_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/manipulatedCameraFrame_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/manipulatedFrame_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/mouseGrabber_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/qglviewer_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/qglviewer_impl_list.h create mode 100644 GraphicsView/include/CGAL/Qt/quaternion_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/vec_impl.h create mode 100644 GraphicsView/include/CGAL/Qt/viewer_actions.h delete mode 100644 GraphicsView/src/CGAL_Qt5/qglviewer-icon.xpm diff --git a/AABB_tree/demo/AABB_tree/CMakeLists.txt b/AABB_tree/demo/AABB_tree/CMakeLists.txt index 29f1957b80f..67d82685b54 100644 --- a/AABB_tree/demo/AABB_tree/CMakeLists.txt +++ b/AABB_tree/demo/AABB_tree/CMakeLists.txt @@ -49,7 +49,9 @@ if(CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND) "${CMAKE_CURRENT_BINARY_DIR}/Viewer_moc.cpp" "${CMAKE_CURRENT_BINARY_DIR}/Scene_moc.cpp" ) - add_executable ( AABB_demo AABB_demo.cpp ${UI_FILES} ${CGAL_Qt5_RESOURCE_FILES} ${CGAL_Qt5_MOC_FILES}) + add_executable ( AABB_demo AABB_demo.cpp ${UI_FILES} ${CGAL_Qt5_RESOURCE_FILES} + #${CGAL_Qt5_MOC_FILES} + ) # Link with Qt libraries target_link_libraries( AABB_demo PRIVATE Qt5::OpenGL Qt5::Gui Qt5::Xml diff --git a/AABB_tree/demo/AABB_tree/MainWindow.cpp b/AABB_tree/demo/AABB_tree/MainWindow.cpp index 57377068fec..5fd4e50a30c 100644 --- a/AABB_tree/demo/AABB_tree/MainWindow.cpp +++ b/AABB_tree/demo/AABB_tree/MainWindow.cpp @@ -411,11 +411,7 @@ void MainWindow::on_actionRefine_loop_triggered() void MainWindow::on_actionSave_snapshot_triggered() { - // save snapshot to file - QApplication::setOverrideCursor(Qt::WaitCursor); - QString filename = QFileDialog::getSaveFileName(this,tr("Save snapshot to file..."),"snapshot00.png","*.png"); - m_pViewer->saveSnapshot(filename); - QApplication::restoreOverrideCursor(); + return; } void MainWindow::on_actionCopy_snapshot_triggered() { diff --git a/AABB_tree/demo/AABB_tree/Refiner.h b/AABB_tree/demo/AABB_tree/Refiner.h index 74d9f86aa79..f03c0ef4216 100644 --- a/AABB_tree/demo/AABB_tree/Refiner.h +++ b/AABB_tree/demo/AABB_tree/Refiner.h @@ -68,7 +68,7 @@ class Refiner typedef typename Polyhedron::Edge_iterator Edge_iterator; typedef std::priority_queue, - less > PQueue; + ::less > PQueue; // data PQueue m_queue; Polyhedron* m_pMesh; diff --git a/GraphicsView/include/CGAL/Qt/AlphaShapeGraphicsItem.h b/GraphicsView/include/CGAL/Qt/AlphaShapeGraphicsItem.h index 8a0fa3d212e..60622c4b604 100644 --- a/GraphicsView/include/CGAL/Qt/AlphaShapeGraphicsItem.h +++ b/GraphicsView/include/CGAL/Qt/AlphaShapeGraphicsItem.h @@ -1,360 +1 @@ // Copyright (c) 2008 GeometryFactory Sarl (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// You can redistribute it and/or modify it under the terms of the GNU -// General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Licensees holding a valid commercial license may use this file in -// accordance with the commercial license agreement provided with the software. -// -// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0+ -// -// -// Author(s) : Andreas Fabri -// Laurent Rineau - -#ifndef CGAL_QT_ALPHA_SHAPE_GRAPHICS_ITEM_H -#define CGAL_QT_ALPHA_SHAPE_GRAPHICS_ITEM_H - -#include - - -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace CGAL { -namespace Qt { - -template -class AlphaShapeGraphicsItem : public GraphicsItem -{ - typedef typename T::Geom_traits Geom_traits; -public: - AlphaShapeGraphicsItem(T* t_); - - void modelChanged(); - -public: - - QRectF boundingRect() const; - - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); - - virtual void operator()(typename T::Face_handle fh); - - const QPen& verticesPen() const - { - return vertices_pen; - } - - const QPen& edgesPen() const - { - return edges_pen; - } - - const QPen& regularEdgesPen() const - { - return regular_edges_pen; - } - - const QPen& singularEdgesPen() const - { - return singular_edges_pen; - } - - const QBrush& regularFacesBrush() const - { - return regular_faces_brush; - } - - void setVerticesPen(const QPen& pen) - { - vertices_pen = pen; - } - - void setEdgesPen(const QPen& pen) - { - edges_pen = pen; - } - - void setRegularEdgesPen(const QPen& pen) - { - regular_edges_pen = pen; - } - - void setSingularEdgesPen(const QPen& pen) - { - singular_edges_pen = pen; - } - - void setRegularFacesBrush(const QBrush& b) - { - regular_faces_brush = b; - } - - bool visibleVertices() const - { - return visible_vertices; - } - - void setVisibleVertices(const bool b) - { - visible_vertices = b; - update(); - } - - bool visibleEdges() const - { - return visible_edges; - } - - void setVisibleEdges(const bool b) - { - visible_edges = b; - update(); - } - -protected: - virtual void drawAll(QPainter *painter); - void paintVertices(QPainter *painter); - void paintOneVertex(const typename T::Point& point); - virtual void paintVertex(typename T::Vertex_handle vh); - void updateBoundingBox(); - - T * t; - QPainter* m_painter; - PainterOstream painterostream; - - typename T::Vertex_handle vh; - typename T::Point p; - CGAL::Bbox_2 bb; - bool bb_initialized; - QRectF bounding_rect; - - QPen vertices_pen; - QPen edges_pen; - QPen regular_edges_pen; - QPen singular_edges_pen; - QBrush regular_faces_brush; - bool visible_edges; - bool visible_vertices; -}; - - -template -AlphaShapeGraphicsItem::AlphaShapeGraphicsItem(T * t_) - : t(t_), painterostream(0), - bb(0,0,0,0), bb_initialized(false), - visible_edges(true), visible_vertices(true) -{ - setVerticesPen(QPen(::Qt::red, 3.)); - if(t->number_of_vertices() == 0){ - this->hide(); - } - updateBoundingBox(); - setZValue(3); -} - -template -QRectF -AlphaShapeGraphicsItem::boundingRect() const -{ - return bounding_rect; -} - - -template -void -AlphaShapeGraphicsItem::operator()(typename T::Face_handle fh) -{ - if(visible_edges) { - for (int i=0; i<3; i++) { - if (fh < fh->neighbor(i) || t->is_infinite(fh->neighbor(i))){ - m_painter->setPen(this->edgesPen()); - painterostream << t->segment(fh,i); - } - } - } - if(visible_vertices) { - for (int i=0; i<3; i++) { - paintVertex(fh->vertex(i)); - } - } -} - -template -void -AlphaShapeGraphicsItem::drawAll(QPainter *painter) -{ - painterostream = PainterOstream(painter); - - painter->setBrush(regularFacesBrush()); - typedef typename T::Gt::Triangle_2 Triangle_2; - for(typename T::Finite_faces_iterator fit = t->finite_faces_begin(); - fit != t->finite_faces_end(); - ++fit){ - - if(t->classify(fit) == T::INTERIOR){ - Triangle_2 triangle = t->triangle(fit); - painterostream << triangle; - } - } - - if(visibleEdges()) { - for(typename T::Finite_edges_iterator eit = t->finite_edges_begin(); - eit != t->finite_edges_end(); - ++eit){ - switch (t->classify(*eit)) { - case T::REGULAR : - painter->setPen(regularEdgesPen()); - break; - case T::SINGULAR : - painter->setPen(singularEdgesPen()); - break; - default: - painter->setPen(edgesPen()); - } - painterostream << t->segment(*eit); - } - } - paintVertices(painter); -} - -template -void -AlphaShapeGraphicsItem::paintVertices(QPainter *painter) -{ - if(visibleVertices()) { - Converter convert; - - painter->setPen(verticesPen()); - QMatrix matrix = painter->matrix(); - painter->resetMatrix(); - for(typename T::Finite_vertices_iterator it = t->finite_vertices_begin(); - it != t->finite_vertices_end(); - it++){ - QPointF point = matrix.map(convert(it->point())); - painter->drawPoint(point); - } - } -} - -template -void -AlphaShapeGraphicsItem::paintOneVertex(const typename T::Point& point) -{ - Converter convert; - - m_painter->setPen(this->verticesPen()); - QMatrix matrix = m_painter->matrix(); - m_painter->resetMatrix(); - m_painter->drawPoint(matrix.map(convert(point))); - m_painter->setMatrix(matrix); -} - -template -void -AlphaShapeGraphicsItem::paintVertex(typename T::Vertex_handle vh) -{ - Converter convert; - - m_painter->setPen(this->verticesPen()); - QMatrix matrix = m_painter->matrix(); - m_painter->resetMatrix(); - m_painter->drawPoint(matrix.map(convert(vh->point()))); - m_painter->setMatrix(matrix); -} - -template -void -AlphaShapeGraphicsItem::paint(QPainter *painter, - const QStyleOptionGraphicsItem * /*option*/, - QWidget * /*widget*/) -{ - painter->setPen(this->edgesPen()); -// painter->drawRect(boundingRect()); - // if ( t->dimension()<2 || option->exposedRect.contains(boundingRect()) ) { - drawAll(painter); - /* - } else { - m_painter = painter; - painterostream = PainterOstream(painter); - CGAL::apply_to_range (*t, - typename T::Point(option->exposedRect.left(), - option->exposedRect.bottom()), - typename T::Point(option->exposedRect.right(), - option->exposedRect.top()), - *this); - } - */ -} - -// We let the bounding box only grow, so that when vertices get removed -// the maximal bbox gets refreshed in the GraphicsView -template -void -AlphaShapeGraphicsItem::updateBoundingBox() -{ - prepareGeometryChange(); - if(t->number_of_vertices() == 0){ - bb = Bbox_2(0,0,0,0); - bb_initialized = false; - return; - } else if(! bb_initialized){ - bb = t->finite_vertices_begin()->point().bbox(); - bb_initialized = true; - } - - if(t->dimension() <2){ - for(typename T::Finite_vertices_iterator it = t->finite_vertices_begin(); - it != t->finite_vertices_end(); - ++it){ - bb = bb + it->point().bbox(); - } - } else { - typename T::Vertex_handle inf = t->infinite_vertex(); - typename T::Vertex_circulator vc = t->incident_vertices(inf), done(vc); - do { - bb = bb + vc->point().bbox(); - ++vc; - } while(vc != done); - } - bounding_rect = QRectF(bb.xmin(), - bb.ymin(), - bb.xmax()-bb.xmin(), - bb.ymax()-bb.ymin()); -} - - -template -void -AlphaShapeGraphicsItem::modelChanged() -{ - if((t->number_of_vertices() == 0) ){ - this->hide(); - } else if((t->number_of_vertices() > 0) && (! this->isVisible())){ - this->show(); - } - updateBoundingBox(); - update(); -} - - -} // namespace Qt -} // namespace CGAL - -#endif // CGAL_QT_ALPHA_SHAPE_GRAPHICS_ITEM_H diff --git a/GraphicsView/include/CGAL/Qt/camera.h b/GraphicsView/include/CGAL/Qt/camera.h index a0cbb2669fb..7c2d401317e 100644 --- a/GraphicsView/include/CGAL/Qt/camera.h +++ b/GraphicsView/include/CGAL/Qt/camera.h @@ -22,15 +22,21 @@ #ifndef QGLVIEWER_CAMERA_H #define QGLVIEWER_CAMERA_H - #include -#include +#include +#include +#include +#include +#include class QGLViewer; namespace qglviewer { +class KeyFrameInterpolator; +class Frame; class ManipulatedCameraFrame; + /*! \brief A perspective or orthographic camera. \class Camera camera.h QGLViewer/camera.h @@ -98,7 +104,7 @@ class QGLVIEWER_EXPORT Camera : public QObject { Q_OBJECT public: - Camera(); + Camera(QObject *parent); virtual ~Camera(); Camera(const Camera &camera); @@ -182,9 +188,7 @@ public: Value is set using setHorizontalFieldOfView() or setFieldOfView(). These values are always linked by: \code horizontalFieldOfView() = 2.0 * atan ( tan(fieldOfView()/2.0) * aspectRatio() ). \endcode */ - qreal horizontalFieldOfView() const { - return 2.0 * atan(tan(fieldOfView() / 2.0) * aspectRatio()); - } + qreal horizontalFieldOfView() const; /*! Returns the Camera aspect ratio defined by screenWidth() / screenHeight(). @@ -265,9 +269,7 @@ public Q_SLOTS: This method actually calls setFieldOfView(( 2.0 * atan (tan(hfov / 2.0) / aspectRatio()) )) so that a call to horizontalFieldOfView() returns the expected value. */ - void setHorizontalFieldOfView(qreal hfov) { - setFieldOfView(2.0 * atan(tan(hfov / 2.0) / aspectRatio())); - } + void setHorizontalFieldOfView(qreal hfov); void setFOVToFitScene(); @@ -384,7 +386,6 @@ public Q_SLOTS: virtual void playPath(unsigned int i); virtual void deletePath(unsigned int i); virtual void resetPath(unsigned int i); - virtual void drawAllPaths(); //@} /*! @name OpenGL matrices */ @@ -408,15 +409,6 @@ public: void getModelViewProjectionMatrix(GLdouble m[16]) const; //@} -/*! @name Drawing */ -//@{ -#ifndef DOXYGEN - static void drawCamera(qreal scale = 1.0, qreal aspectRatio = 1.33, - qreal fieldOfView = qreal(M_PI) / 4.0); -#endif - virtual void draw(bool drawFarPlane = true, qreal scale = 1.0) const; - //@} - /*! @name World to Camera coordinate systems conversions */ //@{ public: @@ -466,9 +458,7 @@ public: This is a helper function. It simply returns physicalScreenWidth() / 2.0 / tan(horizontalFieldOfView() / 2.0); */ - qreal physicalDistanceToScreen() const { - return physicalScreenWidth() / 2.0 / tan(horizontalFieldOfView() / 2.0); - } + qreal physicalDistanceToScreen() const; /*! Returns the physical screen width, in meters. Default value is 0.5m (average monitor width). @@ -525,6 +515,7 @@ private Q_SLOTS: void onFrameModified(); private: + QOpenGLFunctions_2_1* gl() const{ return dynamic_cast(parent()); } // F r a m e ManipulatedCameraFrame *frame_; @@ -553,5 +544,4 @@ private: }; } // namespace qglviewer - #endif // QGLVIEWER_CAMERA_H diff --git a/GraphicsView/include/CGAL/Qt/camera_impl.h b/GraphicsView/include/CGAL/Qt/camera_impl.h new file mode 100644 index 00000000000..f6eca10564b --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/camera_impl.h @@ -0,0 +1,2487 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include +#include +#include +#include + +using namespace std; +using namespace qglviewer; + +/*! Default constructor. + + sceneCenter() is set to (0,0,0) and sceneRadius() is set to 1.0. type() is + Camera::PERSPECTIVE, with a \c M_PI/4 fieldOfView(). + + See IODistance(), physicalDistanceToScreen(), physicalScreenWidth() and + focusDistance() documentations for default stereo parameter values. */ +CGAL_INLINE_FUNCTION +Camera::Camera(QObject *parent) + : frame_(NULL), fieldOfView_(M_PI / 4.0), modelViewMatrixIsUpToDate_(false), + projectionMatrixIsUpToDate_(false) { + setParent(parent); + // #CONNECTION# Camera copy constructor + interpolationKfi_ = new KeyFrameInterpolator; + // Requires the interpolationKfi_ + setFrame(new ManipulatedCameraFrame()); + + // #CONNECTION# All these default values identical in initFromDOMElement. + + // Requires fieldOfView() to define focusDistance() + setSceneRadius(1.0); + + // Initial value (only scaled after this) + orthoCoef_ = tan(fieldOfView() / 2.0); + + // Also defines the pivotPoint(), which changes orthoCoef_. Requires a + // frame(). + setSceneCenter(Vec(0.0, 0.0, 0.0)); + + // Requires fieldOfView() when called with ORTHOGRAPHIC. Attention to + // projectionMatrix_ below. + setType(PERSPECTIVE); + + // #CONNECTION# initFromDOMElement default values + setZNearCoefficient(0.005); + setZClippingCoefficient(sqrt(3.0)); + + // Dummy values + setScreenWidthAndHeight(600, 400); + + // Stereo parameters + setIODistance(0.062); + setPhysicalScreenWidth(0.5); + // focusDistance is set from setFieldOfView() + + // #CONNECTION# Camera copy constructor + for (unsigned short j = 0; j < 16; ++j) { + modelViewMatrix_[j] = ((j % 5 == 0) ? 1.0 : 0.0); + // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost + // everywhere. + projectionMatrix_[j] = 0.0; + } + computeProjectionMatrix(); +} + +/*! Virtual destructor. + + The frame() is deleted, but the different keyFrameInterpolator() are \e not + deleted (in case they are shared). */ + +CGAL_INLINE_FUNCTION +Camera::~Camera() { + delete frame_; + delete interpolationKfi_; +} + +/*! Copy constructor. Performs a deep copy using operator=(). */ +CGAL_INLINE_FUNCTION +Camera::Camera(const Camera &camera) : QObject(), frame_(NULL) { + // #CONNECTION# Camera constructor + interpolationKfi_ = new KeyFrameInterpolator; + // Requires the interpolationKfi_ + setFrame(new ManipulatedCameraFrame(*camera.frame())); + + for (unsigned short j = 0; j < 16; ++j) { + modelViewMatrix_[j] = ((j % 5 == 0) ? 1.0 : 0.0); + // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost + // everywhere. + projectionMatrix_[j] = 0.0; + } + + (*this) = camera; +} + +/*! Equal operator. + + All the parameters of \p camera are copied. The frame() pointer is not + modified, but its Frame::position() and Frame::orientation() are set to those + of \p camera. + + \attention The Camera screenWidth() and screenHeight() are set to those of \p + camera. If your Camera is associated with a QGLViewer, you should update these + value after the call to this method: \code + *(camera()) = otherCamera; + camera()->setScreenWidthAndHeight(width(), height()); + \endcode + The same applies to sceneCenter() and sceneRadius(), if needed. */ +CGAL_INLINE_FUNCTION +Camera &Camera::operator=(const Camera &camera) { + setScreenWidthAndHeight(camera.screenWidth(), camera.screenHeight()); + setFieldOfView(camera.fieldOfView()); + setSceneRadius(camera.sceneRadius()); + setSceneCenter(camera.sceneCenter()); + setZNearCoefficient(camera.zNearCoefficient()); + setZClippingCoefficient(camera.zClippingCoefficient()); + setType(camera.type()); + + // Stereo parameters + setIODistance(camera.IODistance()); + setFocusDistance(camera.focusDistance()); + setPhysicalScreenWidth(camera.physicalScreenWidth()); + + orthoCoef_ = camera.orthoCoef_; + projectionMatrixIsUpToDate_ = false; + + // frame_ and interpolationKfi_ pointers are not shared. + frame_->setReferenceFrame(NULL); + frame_->setPosition(camera.position()); + frame_->setOrientation(camera.orientation()); + + interpolationKfi_->resetInterpolation(); + + kfi_ = camera.kfi_; + + computeProjectionMatrix(); + computeModelViewMatrix(); + + return *this; +} + +/*! Sets Camera screenWidth() and screenHeight() (expressed in pixels). + +You should not call this method when the Camera is associated with a QGLViewer, +since the latter automatically updates these values when it is resized (hence +overwritting your values). + +Non-positive dimension are silently replaced by a 1 pixel value to ensure +frustrum coherence. + +If your Camera is used without a QGLViewer (offscreen rendering, shadow maps), +use setAspectRatio() instead to define the projection matrix. */ +CGAL_INLINE_FUNCTION +void Camera::setScreenWidthAndHeight(int width, int height) { + // Prevent negative and zero dimensions that would cause divisions by zero. + screenWidth_ = width > 0 ? width : 1; + screenHeight_ = height > 0 ? height : 1; + projectionMatrixIsUpToDate_ = false; +} + +/*! Returns the near clipping plane distance used by the Camera projection + matrix. + + The clipping planes' positions depend on the sceneRadius() and sceneCenter() + rather than being fixed small-enough and large-enough values. A good scene + dimension approximation will hence result in an optimal precision of the + z-buffer. + + The near clipping plane is positioned at a distance equal to + zClippingCoefficient() * sceneRadius() in front of the sceneCenter(): \code + zNear = distanceToSceneCenter() - zClippingCoefficient()*sceneRadius(); + \endcode + + In order to prevent negative or too small zNear() values (which would degrade + the z precision), zNearCoefficient() is used when the Camera is inside the + sceneRadius() sphere: \code const qreal zMin = zNearCoefficient() * + zClippingCoefficient() * sceneRadius(); if (zNear < zMin) zNear = zMin; + // With an ORTHOGRAPHIC type, the value is simply clamped to 0.0 + \endcode + + See also the zFar(), zClippingCoefficient() and zNearCoefficient() + documentations. + + If you need a completely different zNear computation, overload the zNear() and + zFar() methods in a new class that publicly inherits from Camera and use + QGLViewer::setCamera(): \code class myCamera :: public qglviewer::Camera + { + virtual qreal Camera::zNear() const { return 0.001; }; + virtual qreal Camera::zFar() const { return 100.0; }; + } + \endcode + + See the standardCamera example + for an application. + + \attention The value is always positive although the clipping plane is + positioned at a negative z value in the Camera coordinate system. This follows + the \c gluPerspective standard. */ +CGAL_INLINE_FUNCTION +qreal Camera::zNear() const { + const qreal zNearScene = zClippingCoefficient() * sceneRadius(); + qreal z = distanceToSceneCenter() - zNearScene; + + // Prevents negative or null zNear values. + const qreal zMin = zNearCoefficient() * zNearScene; + if (z < zMin) + switch (type()) { + case Camera::PERSPECTIVE: + z = zMin; + break; + case Camera::ORTHOGRAPHIC: + z = 0.0; + break; + } + return z; +} + +/*! Returns the far clipping plane distance used by the Camera projection +matrix. + +The far clipping plane is positioned at a distance equal to +zClippingCoefficient() * sceneRadius() behind the sceneCenter(): \code zFar = +distanceToSceneCenter() + zClippingCoefficient()*sceneRadius(); \endcode + +See the zNear() documentation for details. */ +CGAL_INLINE_FUNCTION +qreal Camera::zFar() const { + return distanceToSceneCenter() + zClippingCoefficient() * sceneRadius(); +} + +/*! Sets the vertical fieldOfView() of the Camera (in radians). + +Note that focusDistance() is set to sceneRadius() / tan(fieldOfView()/2) by this +method. */ +CGAL_INLINE_FUNCTION +void Camera::setFieldOfView(qreal fov) { + fieldOfView_ = fov; + setFocusDistance(sceneRadius() / tan(fov / 2.0)); + projectionMatrixIsUpToDate_ = false; +} + +/*! Defines the Camera type(). + +Changing the camera Type alters the viewport and the objects' sizes can be +changed. This method garantees that the two frustum match in a plane normal to +viewDirection(), passing through the pivotPoint(). + +Prefix the type with \c Camera if needed, as in: +\code +camera()->setType(Camera::ORTHOGRAPHIC); +// or even qglviewer::Camera::ORTHOGRAPHIC if you do not use namespace +\endcode */ +CGAL_INLINE_FUNCTION +void Camera::setType(Type type) { + // make ORTHOGRAPHIC frustum fit PERSPECTIVE (at least in plane normal to + // viewDirection(), passing through RAP). Done only when CHANGING type since + // orthoCoef_ may have been changed with a setPivotPoint() in the meantime. + if ((type == Camera::ORTHOGRAPHIC) && (type_ == Camera::PERSPECTIVE)) + orthoCoef_ = tan(fieldOfView() / 2.0); + type_ = type; + projectionMatrixIsUpToDate_ = false; +} + +/*! Sets the Camera frame(). + +If you want to move the Camera, use setPosition() and setOrientation() or one of +the Camera positioning methods (lookAt(), fitSphere(), showEntireScene()...) +instead. + +If you want to save the Camera position(), there's no need to call this method +either. Use addKeyFrameToPath() and playPath() instead. + +This method is actually mainly useful if you derive the ManipulatedCameraFrame +class and want to use an instance of your new class to move the Camera. + +A \c NULL \p mcf pointer will silently be ignored. The calling method is +responsible for deleting the previous frame() pointer if needed in order to +prevent memory leaks. */ +CGAL_INLINE_FUNCTION +void Camera::setFrame(ManipulatedCameraFrame *const mcf) { + if (!mcf) + return; + + if (frame_) { + disconnect(frame_, SIGNAL(modified()), this, SLOT(onFrameModified())); + } + + frame_ = mcf; + interpolationKfi_->setFrame(frame()); + + connect(frame_, SIGNAL(modified()), this, SLOT(onFrameModified())); + onFrameModified(); +} + +/*! Returns the distance from the Camera center to sceneCenter(), projected + along the Camera Z axis. Used by zNear() and zFar() to optimize the Z range. +*/ +CGAL_INLINE_FUNCTION +qreal Camera::distanceToSceneCenter() const { + return fabs((frame()->coordinatesOf(sceneCenter())).z); +} + +/*! Returns the \p halfWidth and \p halfHeight of the Camera orthographic + frustum. + + These values are only valid and used when the Camera is of type() + Camera::ORTHOGRAPHIC. They are expressed in OpenGL units and are used by + loadProjectionMatrix() to define the projection matrix using: \code glOrtho( + -halfWidth, halfWidth, -halfHeight, halfHeight, zNear(), zFar() ) \endcode + + These values are proportional to the Camera (z projected) distance to the + pivotPoint(). When zooming on the object, the Camera is translated forward \e + and its frustum is narrowed, making the object appear bigger on screen, as + intuitively expected. + + Overload this method to change this behavior if desired, as is done in the + standardCamera example. */ +CGAL_INLINE_FUNCTION +void Camera::getOrthoWidthHeight(GLdouble &halfWidth, + GLdouble &halfHeight) const { + const qreal dist = orthoCoef_ * fabs(cameraCoordinatesOf(pivotPoint()).z); + //#CONNECTION# fitScreenRegion + halfWidth = dist * ((aspectRatio() < 1.0) ? 1.0 : aspectRatio()); + halfHeight = dist * ((aspectRatio() < 1.0) ? 1.0 / aspectRatio() : 1.0); +} + +/*! Computes the projection matrix associated with the Camera. + + If type() is Camera::PERSPECTIVE, defines a \c GL_PROJECTION matrix similar to + what would \c gluPerspective() do using the fieldOfView(), window + aspectRatio(), zNear() and zFar() parameters. + + If type() is Camera::ORTHOGRAPHIC, the projection matrix is as what \c + glOrtho() would do. Frustum's width and height are set using + getOrthoWidthHeight(). + + Both types use zNear() and zFar() to place clipping planes. These values are + determined from sceneRadius() and sceneCenter() so that they best fit the scene + size. + + Use getProjectionMatrix() to retrieve this matrix. Overload + loadProjectionMatrix() if you want your Camera to use an exotic projection + matrix. + + \note You must call this method if your Camera is not associated with a + QGLViewer and is used for offscreen computations (using + (un)projectedCoordinatesOf() for instance). loadProjectionMatrix() does it + otherwise. */ +CGAL_INLINE_FUNCTION +void Camera::computeProjectionMatrix() const { + if (projectionMatrixIsUpToDate_) + return; + + const qreal ZNear = zNear(); + const qreal ZFar = zFar(); + + switch (type()) { + case Camera::PERSPECTIVE: { + // #CONNECTION# all non null coefficients were set to 0.0 in constructor. + const qreal f = 1.0 / tan(fieldOfView() / 2.0); + projectionMatrix_[0] = f / aspectRatio(); + projectionMatrix_[5] = f; + projectionMatrix_[10] = (ZNear + ZFar) / (ZNear - ZFar); + projectionMatrix_[11] = -1.0; + projectionMatrix_[14] = 2.0 * ZNear * ZFar / (ZNear - ZFar); + projectionMatrix_[15] = 0.0; + // same as gluPerspective( 180.0*fieldOfView()/M_PI, aspectRatio(), zNear(), + // zFar() ); + break; + } + case Camera::ORTHOGRAPHIC: { + GLdouble w, h; + getOrthoWidthHeight(w, h); + projectionMatrix_[0] = 1.0 / w; + projectionMatrix_[5] = 1.0 / h; + projectionMatrix_[10] = -2.0 / (ZFar - ZNear); + projectionMatrix_[11] = 0.0; + projectionMatrix_[14] = -(ZFar + ZNear) / (ZFar - ZNear); + projectionMatrix_[15] = 1.0; + // same as glOrtho( -w, w, -h, h, zNear(), zFar() ); + break; + } + } + + projectionMatrixIsUpToDate_ = true; +} + +/*! Computes the modelView matrix associated with the Camera's position() and + orientation(). + + This matrix converts from the world coordinates system to the Camera + coordinates system, so that coordinates can then be projected on screen using + the projection matrix (see computeProjectionMatrix()). + + Use getModelViewMatrix() to retrieve this matrix. + + \note You must call this method if your Camera is not associated with a + QGLViewer and is used for offscreen computations (using + (un)projectedCoordinatesOf() for instance). loadModelViewMatrix() does it + otherwise. */ +CGAL_INLINE_FUNCTION +void Camera::computeModelViewMatrix() const { + if (modelViewMatrixIsUpToDate_) + return; + + const Quaternion q = frame()->orientation(); + + const qreal q00 = 2.0 * q[0] * q[0]; + const qreal q11 = 2.0 * q[1] * q[1]; + const qreal q22 = 2.0 * q[2] * q[2]; + + const qreal q01 = 2.0 * q[0] * q[1]; + const qreal q02 = 2.0 * q[0] * q[2]; + const qreal q03 = 2.0 * q[0] * q[3]; + + const qreal q12 = 2.0 * q[1] * q[2]; + const qreal q13 = 2.0 * q[1] * q[3]; + + const qreal q23 = 2.0 * q[2] * q[3]; + + modelViewMatrix_[0] = 1.0 - q11 - q22; + modelViewMatrix_[1] = q01 - q23; + modelViewMatrix_[2] = q02 + q13; + modelViewMatrix_[3] = 0.0; + + modelViewMatrix_[4] = q01 + q23; + modelViewMatrix_[5] = 1.0 - q22 - q00; + modelViewMatrix_[6] = q12 - q03; + modelViewMatrix_[7] = 0.0; + + modelViewMatrix_[8] = q02 - q13; + modelViewMatrix_[9] = q12 + q03; + modelViewMatrix_[10] = 1.0 - q11 - q00; + modelViewMatrix_[11] = 0.0; + + const Vec t = q.inverseRotate(frame()->position()); + + modelViewMatrix_[12] = -t.x; + modelViewMatrix_[13] = -t.y; + modelViewMatrix_[14] = -t.z; + modelViewMatrix_[15] = 1.0; + + modelViewMatrixIsUpToDate_ = true; +} + +/*! Loads the OpenGL \c GL_PROJECTION matrix with the Camera projection matrix. + + The Camera projection matrix is computed using computeProjectionMatrix(). + + When \p reset is \c true (default), the method clears the previous projection + matrix by calling \c glLoadIdentity before setting the matrix. Setting \p reset + to \c false is useful for \c GL_SELECT mode, to combine the pushed matrix with + a picking matrix. See QGLViewer::beginSelection() for details. + + This method is used by QGLViewer::preDraw() (called before user's + QGLViewer::draw() method) to set the \c GL_PROJECTION matrix according to the + viewer's QGLViewer::camera() settings. + + Use getProjectionMatrix() to retrieve this matrix. Overload this method if you + want your Camera to use an exotic projection matrix. See also + loadModelViewMatrix(). + + \attention \c glMatrixMode is set to \c GL_PROJECTION. + + \attention If you use several OpenGL contexts and bypass the Qt main refresh + loop, you should call QOpenGLWidget::makeCurrent() before this method in order + to activate the right OpenGL context. */ +CGAL_INLINE_FUNCTION +void Camera::loadProjectionMatrix(bool reset) const { + // WARNING: makeCurrent must be called by every calling method + gl()->glMatrixMode(GL_PROJECTION); + + if (reset) + gl()->glLoadIdentity(); + + computeProjectionMatrix(); + + gl()->glMultMatrixd(projectionMatrix_); +} + +/*! Loads the OpenGL \c GL_MODELVIEW matrix with the modelView matrix + corresponding to the Camera. + + Calls computeModelViewMatrix() to compute the Camera's modelView matrix. + + This method is used by QGLViewer::preDraw() (called before user's + QGLViewer::draw() method) to set the \c GL_MODELVIEW matrix according to the + viewer's QGLViewer::camera() position() and orientation(). + + As a result, the vertices used in QGLViewer::draw() can be defined in the so + called world coordinate system. They are multiplied by this matrix to get + converted to the Camera coordinate system, before getting projected using the + \c GL_PROJECTION matrix (see loadProjectionMatrix()). + + When \p reset is \c true (default), the method loads (overwrites) the \c + GL_MODELVIEW matrix. Setting \p reset to \c false simply calls \c glMultMatrixd + (might be useful for some applications). + + Overload this method or simply call glLoadMatrixd() at the beginning of + QGLViewer::draw() if you want your Camera to use an exotic modelView matrix. + See also loadProjectionMatrix(). + + getModelViewMatrix() returns the 4x4 modelView matrix. + + \attention glMatrixMode is set to \c GL_MODELVIEW + + \attention If you use several OpenGL contexts and bypass the Qt main refresh + loop, you should call QOpenGLWidget::makeCurrent() before this method in order + to activate the right OpenGL context. */ +CGAL_INLINE_FUNCTION +void Camera::loadModelViewMatrix(bool reset) const { + // WARNING: makeCurrent must be called by every calling method + gl()->glMatrixMode(GL_MODELVIEW); + computeModelViewMatrix(); + if (reset) + gl()->glLoadMatrixd(modelViewMatrix_); + else + gl()->glMultMatrixd(modelViewMatrix_); +} + +/*! Same as loadProjectionMatrix() but for a stereo setup. + + Only the Camera::PERSPECTIVE type() is supported for stereo mode. See + QGLViewer::setStereoDisplay(). + + Uses focusDistance(), IODistance(), and physicalScreenWidth() to compute + cameras offset and asymmetric frustums. + + When \p leftBuffer is \c true, computes the projection matrix associated to the + left eye (right eye otherwise). See also loadModelViewMatrixStereo(). + + See the stereoViewer and the anaglyph examples for an + illustration. + + To retrieve this matrix, use a code like: + \code + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + loadProjectionMatrixStereo(left_or_right); + glGetDoublev(GL_PROJECTION_MATRIX, m); + glPopMatrix(); + \endcode + Note that getProjectionMatrix() always returns the mono-vision matrix. + + \attention glMatrixMode is set to \c GL_PROJECTION. */ +CGAL_INLINE_FUNCTION +void Camera::loadProjectionMatrixStereo(bool leftBuffer) const { + qreal left, right, bottom, top; + qreal screenHalfWidth, halfWidth, side, shift, delta; + + gl()->glMatrixMode(GL_PROJECTION); + gl()->glLoadIdentity(); + + switch (type()) { + case Camera::PERSPECTIVE: + // compute half width of screen, + // corresponding to zero parallax plane to deduce decay of cameras + screenHalfWidth = focusDistance() * tan(horizontalFieldOfView() / 2.0); + shift = screenHalfWidth * IODistance() / physicalScreenWidth(); + // should be * current y / y total + // to take into account that the window doesn't cover the entire screen + + // compute half width of "view" at znear and the delta corresponding to + // the shifted camera to deduce what to set for asymmetric frustums + halfWidth = zNear() * tan(horizontalFieldOfView() / 2.0); + delta = shift * zNear() / focusDistance(); + side = leftBuffer ? -1.0 : 1.0; + + left = -halfWidth + side * delta; + right = halfWidth + side * delta; + top = halfWidth / aspectRatio(); + bottom = -top; + gl()->glFrustum(left, right, bottom, top, zNear(), zFar()); + break; + + case Camera::ORTHOGRAPHIC: + qWarning("Camera::setProjectionMatrixStereo: Stereo not available with " + "Ortho mode"); + break; + } +} + +/*! Same as loadModelViewMatrix() but for a stereo setup. + + Only the Camera::PERSPECTIVE type() is supported for stereo mode. See + QGLViewer::setStereoDisplay(). + + The modelView matrix is almost identical to the mono-vision one. It is simply + translated along its horizontal axis by a value that depends on stereo + parameters (see focusDistance(), IODistance(), and physicalScreenWidth()). + + When \p leftBuffer is \c true, computes the modelView matrix associated to the + left eye (right eye otherwise). + + loadProjectionMatrixStereo() explains how to retrieve to resulting matrix. + + See the stereoViewer and the anaglyph examples for an + illustration. + + \attention glMatrixMode is set to \c GL_MODELVIEW. */ +CGAL_INLINE_FUNCTION +void Camera::loadModelViewMatrixStereo(bool leftBuffer) const { + // WARNING: makeCurrent must be called by every calling method + gl()->glMatrixMode(GL_MODELVIEW); + + qreal halfWidth = focusDistance() * tan(horizontalFieldOfView() / 2.0); + qreal shift = + halfWidth * IODistance() / + physicalScreenWidth(); // * current window width / full screen width + + computeModelViewMatrix(); + if (leftBuffer) + modelViewMatrix_[12] -= shift; + else + modelViewMatrix_[12] += shift; + gl()->glLoadMatrixd(modelViewMatrix_); +} + +/*! Fills \p m with the Camera projection matrix values. + + Based on computeProjectionMatrix() to make sure the Camera projection matrix is + up to date. + + This matrix only reflects the Camera's internal parameters and it may differ + from the \c GL_PROJECTION matrix retrieved using \c + glGetDoublev(GL_PROJECTION_MATRIX, m). It actually represents the state of the + \c GL_PROJECTION after QGLViewer::preDraw(), at the beginning of + QGLViewer::draw(). If you modified the \c GL_PROJECTION matrix (for instance + using QGLViewer::startScreenCoordinatesSystem()), the two results differ. + + The result is an OpenGL 4x4 matrix, which is given in \e column-major order + (see \c glMultMatrix man page for details). + + See also getModelViewMatrix() and setFromProjectionMatrix(). */ +CGAL_INLINE_FUNCTION +void Camera::getProjectionMatrix(GLdouble m[16]) const { + computeProjectionMatrix(); + for (unsigned short i = 0; i < 16; ++i) + m[i] = projectionMatrix_[i]; +} + +/*! Overloaded getProjectionMatrix(GLdouble m[16]) method using a \c GLfloat + * array instead. */ +CGAL_INLINE_FUNCTION +void Camera::getProjectionMatrix(GLfloat m[16]) const { + static GLdouble mat[16]; + getProjectionMatrix(mat); + for (unsigned short i = 0; i < 16; ++i) + m[i] = float(mat[i]); +} + +/*! Fills \p m with the Camera modelView matrix values. + + First calls computeModelViewMatrix() to define the Camera modelView matrix. + + Note that this matrix may \e not be the one you would get from a \c + glGetDoublev(GL_MODELVIEW_MATRIX, m). It actually represents the state of the + \c GL_MODELVIEW after QGLViewer::preDraw(), at the \e beginning of + QGLViewer::draw(). It converts from the world to the Camera coordinate system. + As soon as you modify the \c GL_MODELVIEW in your QGLViewer::draw() method + (using glTranslate, glRotate... or similar methods), the two matrices differ. + + The result is an OpenGL 4x4 matrix, which is given in \e column-major order + (see \c glMultMatrix man page for details). + + See also getProjectionMatrix() and setFromModelViewMatrix(). */ +CGAL_INLINE_FUNCTION +void Camera::getModelViewMatrix(GLdouble m[16]) const { + // May not be needed, but easier like this. + // Prevents from retrieving matrix in stereo mode -> overwrites shifted value. + computeModelViewMatrix(); + for (unsigned short i = 0; i < 16; ++i) + m[i] = modelViewMatrix_[i]; +} + +/*! Overloaded getModelViewMatrix(GLdouble m[16]) method using a \c GLfloat + * array instead. */ +CGAL_INLINE_FUNCTION +void Camera::getModelViewMatrix(GLfloat m[16]) const { + static GLdouble mat[16]; + getModelViewMatrix(mat); + for (unsigned short i = 0; i < 16; ++i) + m[i] = float(mat[i]); +} + +/*! Fills \p m with the product of the ModelView and Projection matrices. + + Calls getModelViewMatrix() and getProjectionMatrix() and then fills \p m with + the product of these two matrices. */ +CGAL_INLINE_FUNCTION +void Camera::getModelViewProjectionMatrix(GLdouble m[16]) const { + GLdouble mv[16]; + GLdouble proj[16]; + getModelViewMatrix(mv); + getProjectionMatrix(proj); + + for (unsigned short i = 0; i < 4; ++i) { + for (unsigned short j = 0; j < 4; ++j) { + qreal sum = 0.0; + for (unsigned short k = 0; k < 4; ++k) + sum += proj[i + 4 * k] * mv[k + 4 * j]; + m[i + 4 * j] = sum; + } + } +} + +/*! Overloaded getModelViewProjectionMatrix(GLdouble m[16]) method using a \c + * GLfloat array instead. */ +CGAL_INLINE_FUNCTION +void Camera::getModelViewProjectionMatrix(GLfloat m[16]) const { + static GLdouble mat[16]; + getModelViewProjectionMatrix(mat); + for (unsigned short i = 0; i < 16; ++i) + m[i] = float(mat[i]); +} + +/*! Sets the sceneRadius() value. Negative values are ignored. + +\attention This methods also sets focusDistance() to sceneRadius() / +tan(fieldOfView()/2) and flySpeed() to 1% of sceneRadius(). */ +CGAL_INLINE_FUNCTION +void Camera::setSceneRadius(qreal radius) { + if (radius <= 0.0) { + qWarning("Scene radius must be positive - Ignoring value"); + return; + } + + sceneRadius_ = radius; + projectionMatrixIsUpToDate_ = false; + + setFocusDistance(sceneRadius() / tan(fieldOfView() / 2.0)); + + frame()->setFlySpeed(0.01 * sceneRadius()); +} + +/*! Similar to setSceneRadius() and setSceneCenter(), but the scene limits are + defined by a (world axis aligned) bounding box. */ +CGAL_INLINE_FUNCTION +void Camera::setSceneBoundingBox(const Vec &min, const Vec &max) { + setSceneCenter((min + max) / 2.0); + setSceneRadius(0.5 * (max - min).norm()); +} + +/*! Sets the sceneCenter(). + + \attention This method also sets the pivotPoint() to sceneCenter(). */ +CGAL_INLINE_FUNCTION +void Camera::setSceneCenter(const Vec ¢er) { + sceneCenter_ = center; + setPivotPoint(sceneCenter()); + projectionMatrixIsUpToDate_ = false; +} + +/*! setSceneCenter() to the result of pointUnderPixel(\p pixel). + + Returns \c true if a pointUnderPixel() was found and sceneCenter() was + actually changed. + + See also setPivotPointFromPixel(). See the pointUnderPixel() documentation. */ +CGAL_INLINE_FUNCTION +bool Camera::setSceneCenterFromPixel(const QPoint &pixel) { + bool found; + Vec point = pointUnderPixel(pixel, found); + if (found) + setSceneCenter(point); + return found; +} + +#ifndef DOXYGEN +CGAL_INLINE_FUNCTION +void Camera::setRevolveAroundPoint(const Vec &point) { + qWarning( + "setRevolveAroundPoint() is deprecated, use setPivotPoint() instead"); + setPivotPoint(point); +} +CGAL_INLINE_FUNCTION +bool Camera::setRevolveAroundPointFromPixel(const QPoint &pixel) { + qWarning("setRevolveAroundPointFromPixel() is deprecated, use " + "setPivotPointFromPixel() instead"); + return setPivotPointFromPixel(pixel); +} +CGAL_INLINE_FUNCTION +Vec Camera::revolveAroundPoint() const { + qWarning("revolveAroundPoint() is deprecated, use pivotPoint() instead"); + return pivotPoint(); +} +#endif + +/*! Changes the pivotPoint() to \p point (defined in the world coordinate + * system). */ +CGAL_INLINE_FUNCTION +void Camera::setPivotPoint(const Vec &point) { + const qreal prevDist = fabs(cameraCoordinatesOf(pivotPoint()).z); + + // If frame's RAP is set directly, projectionMatrixIsUpToDate_ should also be + // set to false to ensure proper recomputation of the ORTHO projection matrix. + frame()->setPivotPoint(point); + + // orthoCoef_ is used to compensate for changes of the pivotPoint, so that the + // image does not change when the pivotPoint is changed in ORTHOGRAPHIC mode. + const qreal newDist = fabs(cameraCoordinatesOf(pivotPoint()).z); + // Prevents division by zero when rap is set to camera position + if ((prevDist > 1E-9) && (newDist > 1E-9)) + orthoCoef_ *= prevDist / newDist; + projectionMatrixIsUpToDate_ = false; +} + +/*! The pivotPoint() is set to the point located under \p pixel on screen. + +Returns \c true if a pointUnderPixel() was found. If no point was found under \p +pixel, the pivotPoint() is left unchanged. + +\p pixel is expressed in Qt format (origin in the upper left corner of the +window). See pointUnderPixel(). + +See also setSceneCenterFromPixel(). */ +CGAL_INLINE_FUNCTION +bool Camera::setPivotPointFromPixel(const QPoint &pixel) { + bool found; + Vec point = pointUnderPixel(pixel, found); + if (found) + setPivotPoint(point); + return found; +} + +/*! Returns the ratio between pixel and OpenGL units at \p position. + + A line of \c n * pixelGLRatio() OpenGL units, located at \p position in the + world coordinates system, will be projected with a length of \c n pixels on + screen. + + Use this method to scale objects so that they have a constant pixel size on + screen. The following code will draw a 20 pixel line, starting at sceneCenter() + and always directed along the screen vertical direction: \code + glBegin(GL_LINES); + glVertex3fv(sceneCenter()); + glVertex3fv(sceneCenter() + 20 * pixelGLRatio(sceneCenter()) * + camera()->upVector()); glEnd(); \endcode */ +CGAL_INLINE_FUNCTION +qreal Camera::pixelGLRatio(const Vec &position) const { + switch (type()) { + case Camera::PERSPECTIVE: + return 2.0 * fabs((frame()->coordinatesOf(position)).z) * + tan(fieldOfView() / 2.0) / screenHeight(); + case Camera::ORTHOGRAPHIC: { + GLdouble w, h; + getOrthoWidthHeight(w, h); + return 2.0 * h / screenHeight(); + } + } + // Bad compilers complain + return 1.0; +} + +/*! Changes the Camera fieldOfView() so that the entire scene (defined by + QGLViewer::sceneCenter() and QGLViewer::sceneRadius()) is visible from the + Camera position(). + + The position() and orientation() of the Camera are not modified and you first + have to orientate the Camera in order to actually see the scene (see lookAt(), + showEntireScene() or fitSphere()). + + This method is especially useful for \e shadow \e maps computation. Use the + Camera positioning tools (setPosition(), lookAt()) to position a Camera at the + light position. Then use this method to define the fieldOfView() so that the + shadow map resolution is optimally used: \code + // The light camera needs size hints in order to optimize its fieldOfView + lightCamera->setSceneRadius(sceneRadius()); + lightCamera->setSceneCenter(sceneCenter()); + + // Place the light camera. + lightCamera->setPosition(lightFrame->position()); + lightCamera->lookAt(sceneCenter()); + lightCamera->setFOVToFitScene(); + \endcode + + See the (soon available) shadowMap contribution example for a practical + implementation. + + \attention The fieldOfView() is clamped to M_PI/2.0. This happens when the + Camera is at a distance lower than sqrt(2.0) * sceneRadius() from the + sceneCenter(). It optimizes the shadow map resolution, although it may miss + some parts of the scene. */ +CGAL_INLINE_FUNCTION +void Camera::setFOVToFitScene() { + if (distanceToSceneCenter() > sqrt(2.0) * sceneRadius()) + setFieldOfView(2.0 * asin(sceneRadius() / distanceToSceneCenter())); + else + setFieldOfView(M_PI / 2.0); +} + +/*! Makes the Camera smoothly zoom on the pointUnderPixel() \p pixel. + + Nothing happens if no pointUnderPixel() is found. Otherwise a + KeyFrameInterpolator is created that animates the Camera on a one second path + that brings the Camera closer to the point under \p pixel. + + See also interpolateToFitScene(). */ +CGAL_INLINE_FUNCTION +void Camera::interpolateToZoomOnPixel(const QPoint &pixel) { + const qreal coef = 0.1; + + bool found; + Vec target = pointUnderPixel(pixel, found); + + if (!found) + return; + + if (interpolationKfi_->interpolationIsStarted()) + interpolationKfi_->stopInterpolation(); + + interpolationKfi_->deletePath(); + interpolationKfi_->addKeyFrame(*(frame())); + + interpolationKfi_->addKeyFrame( + Frame(0.3 * frame()->position() + 0.7 * target, frame()->orientation()), + 0.4); + + // Small hack: attach a temporary frame to take advantage of lookAt without + // modifying frame + static ManipulatedCameraFrame *tempFrame = new ManipulatedCameraFrame(); + ManipulatedCameraFrame *const originalFrame = frame(); + tempFrame->setPosition(coef * frame()->position() + (1.0 - coef) * target); + tempFrame->setOrientation(frame()->orientation()); + setFrame(tempFrame); + lookAt(target); + setFrame(originalFrame); + + interpolationKfi_->addKeyFrame(*(tempFrame), 1.0); + + interpolationKfi_->startInterpolation(); +} + +/*! Interpolates the Camera on a one second KeyFrameInterpolator path so that + the entire scene fits the screen at the end. + + The scene is defined by its sceneCenter() and its sceneRadius(). See + showEntireScene(). + + The orientation() of the Camera is not modified. See also + interpolateToZoomOnPixel(). */ +CGAL_INLINE_FUNCTION +void Camera::interpolateToFitScene() { + if (interpolationKfi_->interpolationIsStarted()) + interpolationKfi_->stopInterpolation(); + + interpolationKfi_->deletePath(); + interpolationKfi_->addKeyFrame(*(frame())); + + // Small hack: attach a temporary frame to take advantage of lookAt without + // modifying frame + static ManipulatedCameraFrame *tempFrame = new ManipulatedCameraFrame(); + ManipulatedCameraFrame *const originalFrame = frame(); + tempFrame->setPosition(frame()->position()); + tempFrame->setOrientation(frame()->orientation()); + setFrame(tempFrame); + showEntireScene(); + setFrame(originalFrame); + + interpolationKfi_->addKeyFrame(*(tempFrame)); + + interpolationKfi_->startInterpolation(); +} + +/*! Smoothly interpolates the Camera on a KeyFrameInterpolator path so that it + goes to \p fr. + + \p fr is expressed in world coordinates. \p duration tunes the interpolation + speed (default is 1 second). + + See also interpolateToFitScene() and interpolateToZoomOnPixel(). */ +CGAL_INLINE_FUNCTION +void Camera::interpolateTo(const Frame &fr, qreal duration) { + if (interpolationKfi_->interpolationIsStarted()) + interpolationKfi_->stopInterpolation(); + + interpolationKfi_->deletePath(); + interpolationKfi_->addKeyFrame(*(frame())); + interpolationKfi_->addKeyFrame(fr, duration); + + interpolationKfi_->startInterpolation(); +} + +/*! Returns the coordinates of the 3D point located at pixel (x,y) on screen. + + Calls a \c glReadPixel to get the pixel depth and applies an + unprojectedCoordinatesOf() to the result. \p found indicates whether a point + was found or not (i.e. background pixel, result's depth is zFar() in that + case). + + \p x and \p y are expressed in pixel units with an origin in the upper left + corner. Use screenHeight() - y to convert to OpenGL standard. + + \attention This method assumes that a GL context is available, and that its + content was drawn using the Camera (i.e. using its projection and modelview + matrices). This method hence cannot be used for offscreen Camera computations. + Use cameraCoordinatesOf() and worldCoordinatesOf() to perform similar + operations in that case. + + \note The precision of the z-Buffer highly depends on how the zNear() and + zFar() values are fitted to your scene. Loose boundaries will result in + imprecision along the viewing direction. */ +CGAL_INLINE_FUNCTION +Vec Camera::pointUnderPixel(const QPoint &pixel, bool &found) const { + float depth; + // Qt uses upper corner for its origin while GL uses the lower corner. + dynamic_cast(parent())->glReadPixels(pixel.x(), screenHeight() - 1 - pixel.y(), 1, 1, + GL_DEPTH_COMPONENT, GL_FLOAT, &depth); + found = depth < 1.0; + Vec point(pixel.x(), pixel.y(), depth); + point = unprojectedCoordinatesOf(point); + return point; +} + +/*! Moves the Camera so that the entire scene is visible. + + Simply calls fitSphere() on a sphere defined by sceneCenter() and + sceneRadius(). + + You will typically use this method in QGLViewer::init() after you defined a new + sceneRadius(). */ +CGAL_INLINE_FUNCTION +void Camera::showEntireScene() { fitSphere(sceneCenter(), sceneRadius()); } + +/*! Moves the Camera so that its sceneCenter() is projected on the center of the + window. The orientation() and fieldOfView() are unchanged. + + Simply projects the current position on a line passing through sceneCenter(). + See also showEntireScene().*/ +CGAL_INLINE_FUNCTION +void Camera::centerScene() { + frame()->projectOnLine(sceneCenter(), viewDirection()); +} + +/*! Sets the Camera orientation(), so that it looks at point \p target (defined + in the world coordinate system). + + The Camera position() is not modified. Simply setViewDirection(). + + See also setUpVector(), setOrientation(), showEntireScene(), fitSphere() and + fitBoundingBox(). */ +CGAL_INLINE_FUNCTION +void Camera::lookAt(const Vec &target) { + setViewDirection(target - position()); +} + +/*! Moves the Camera so that the sphere defined by (\p center, \p radius) is + visible and fits in the frustum. + + The Camera is simply translated to center the sphere in the screen and make it + fit the frustum. Its orientation() and its fieldOfView() are unchanged. + + You should therefore orientate the Camera before you call this method. See + lookAt(), setOrientation() and setUpVector(). */ +CGAL_INLINE_FUNCTION +void Camera::fitSphere(const Vec ¢er, qreal radius) { + qreal distance = 0.0; + switch (type()) { + case Camera::PERSPECTIVE: { + const qreal yview = radius / sin(fieldOfView() / 2.0); + const qreal xview = radius / sin(horizontalFieldOfView() / 2.0); + distance = qMax(xview, yview); + break; + } + case Camera::ORTHOGRAPHIC: { + distance = + ((center - pivotPoint()) * viewDirection()) + (radius / orthoCoef_); + break; + } + } + Vec newPos(center - distance * viewDirection()); + frame()->setPositionWithConstraint(newPos); +} + +/*! Moves the Camera so that the (world axis aligned) bounding box (\p min, \p + max) is entirely visible, using fitSphere(). */ +CGAL_INLINE_FUNCTION +void Camera::fitBoundingBox(const Vec &min, const Vec &max) { + qreal diameter = qMax(fabs(max[1] - min[1]), fabs(max[0] - min[0])); + diameter = qMax(fabs(max[2] - min[2]), diameter); + fitSphere(0.5 * (min + max), 0.5 * diameter); +} + +/*! Moves the Camera so that the rectangular screen region defined by \p + rectangle (pixel units, with origin in the upper left corner) fits the screen. + + The Camera is translated (its orientation() is unchanged) so that \p rectangle + is entirely visible. Since the pixel coordinates only define a \e frustum in + 3D, it's the intersection of this frustum with a plane (orthogonal to the + viewDirection() and passing through the sceneCenter()) that is used to define + the 3D rectangle that is eventually fitted. */ +CGAL_INLINE_FUNCTION +void Camera::fitScreenRegion(const QRect &rectangle) { + const Vec vd = viewDirection(); + const qreal distToPlane = distanceToSceneCenter(); + const QPoint center = rectangle.center(); + + Vec orig, dir; + convertClickToLine(center, orig, dir); + Vec newCenter = orig + distToPlane / (dir * vd) * dir; + + convertClickToLine(QPoint(rectangle.x(), center.y()), orig, dir); + const Vec pointX = orig + distToPlane / (dir * vd) * dir; + + convertClickToLine(QPoint(center.x(), rectangle.y()), orig, dir); + const Vec pointY = orig + distToPlane / (dir * vd) * dir; + + qreal distance = 0.0; + switch (type()) { + case Camera::PERSPECTIVE: { + const qreal distX = + (pointX - newCenter).norm() / sin(horizontalFieldOfView() / 2.0); + const qreal distY = (pointY - newCenter).norm() / sin(fieldOfView() / 2.0); + distance = qMax(distX, distY); + break; + } + case Camera::ORTHOGRAPHIC: { + const qreal dist = ((newCenter - pivotPoint()) * vd); + //#CONNECTION# getOrthoWidthHeight + const qreal distX = (pointX - newCenter).norm() / orthoCoef_ / + ((aspectRatio() < 1.0) ? 1.0 : aspectRatio()); + const qreal distY = (pointY - newCenter).norm() / orthoCoef_ / + ((aspectRatio() < 1.0) ? 1.0 / aspectRatio() : 1.0); + distance = dist + qMax(distX, distY); + break; + } + } + + Vec newPos(newCenter - distance * vd); + frame()->setPositionWithConstraint(newPos); +} + +/*! Rotates the Camera so that its upVector() becomes \p up (defined in the + world coordinate system). + + The Camera is rotated around an axis orthogonal to \p up and to the current + upVector() direction. Use this method in order to define the Camera horizontal + plane. + + When \p noMove is set to \c false, the orientation modification is compensated + by a translation, so that the pivotPoint() stays projected at the same position + on screen. This is especially useful when the Camera is used as an observer of + the scene (default mouse binding). + + When \p noMove is \c true (default), the Camera position() is left unchanged, + which is an intuitive behavior when the Camera is in a walkthrough fly mode + (see the QGLViewer::MOVE_FORWARD and QGLViewer::MOVE_BACKWARD + QGLViewer::MouseAction). + + The frame()'s ManipulatedCameraFrame::sceneUpVector() is set accordingly. + + See also setViewDirection(), lookAt() and setOrientation(). */ +CGAL_INLINE_FUNCTION +void Camera::setUpVector(const Vec &up, bool noMove) { + Quaternion q(Vec(0.0, 1.0, 0.0), frame()->transformOf(up)); + + if (!noMove) + frame()->setPosition(pivotPoint() - + (frame()->orientation() * q) + .rotate(frame()->coordinatesOf(pivotPoint()))); + + frame()->rotate(q); + + // Useful in fly mode to keep the horizontal direction. + frame()->updateSceneUpVector(); +} + +/*! Sets the orientation() of the Camera using polar coordinates. + + \p theta rotates the Camera around its Y axis, and \e then \p phi rotates it + around its X axis. The polar coordinates are defined in the world coordinates + system: \p theta = \p phi = 0 means that the Camera is directed towards the + world Z axis. Both angles are expressed in radians. + + See also setUpVector(). The position() of the Camera is unchanged, you may want + to call showEntireScene() after this method to move the Camera. + + This method can be useful to create Quicktime VR panoramic sequences, see the + QGLViewer::saveSnapshot() documentation for details. */ +CGAL_INLINE_FUNCTION +void Camera::setOrientation(qreal theta, qreal phi) { + Vec axis(0.0, 1.0, 0.0); + const Quaternion rot1(axis, theta); + axis = Vec(-cos(theta), 0.0, sin(theta)); + const Quaternion rot2(axis, phi); + setOrientation(rot1 * rot2); +} + +/*! Sets the Camera orientation(), defined in the world coordinate system. */ +CGAL_INLINE_FUNCTION +void Camera::setOrientation(const Quaternion &q) { + frame()->setOrientation(q); + frame()->updateSceneUpVector(); +} + +/*! Rotates the Camera so that its viewDirection() is \p direction (defined in + the world coordinate system). + + The Camera position() is not modified. The Camera is rotated so that the + horizon (defined by its upVector()) is preserved. See also lookAt() and + setUpVector(). */ +CGAL_INLINE_FUNCTION +void Camera::setViewDirection(const Vec &direction) { + if (direction.squaredNorm() < 1E-10) + return; + + Vec xAxis = direction ^ upVector(); + if (xAxis.squaredNorm() < 1E-10) { + // target is aligned with upVector, this means a rotation around X axis + // X axis is then unchanged, let's keep it ! + xAxis = frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0)); + } + + Quaternion q; + q.setFromRotatedBasis(xAxis, xAxis ^ direction, -direction); + frame()->setOrientationWithConstraint(q); +} + +// Compute a 3 by 3 determinant. +static qreal det(qreal m00, qreal m01, qreal m02, qreal m10, qreal m11, + qreal m12, qreal m20, qreal m21, qreal m22) { + return m00 * m11 * m22 + m01 * m12 * m20 + m02 * m10 * m21 - m20 * m11 * m02 - + m10 * m01 * m22 - m00 * m21 * m12; +} + +// Computes the index of element [i][j] in a \c qreal matrix[3][4]. +static inline unsigned int ind(unsigned int i, unsigned int j) { + return (i * 4 + j); +} + +/*! Returns the Camera position (the eye), defined in the world coordinate +system. + +Use setPosition() to set the Camera position. Other convenient methods are +showEntireScene() or fitSphere(). Actually returns \c frame()->position(). + +This position corresponds to the projection center of a Camera::PERSPECTIVE +Camera. It is not located in the image plane, which is at a zNear() distance +ahead. */ +CGAL_INLINE_FUNCTION +Vec Camera::position() const { return frame()->position(); } + +/*! Returns the normalized up vector of the Camera, defined in the world +coordinate system. + +Set using setUpVector() or setOrientation(). It is orthogonal to viewDirection() +and to rightVector(). + +It corresponds to the Y axis of the associated frame() (actually returns +frame()->inverseTransformOf(Vec(0.0, 1.0, 0.0)) ). */ +CGAL_INLINE_FUNCTION +Vec Camera::upVector() const { + return frame()->inverseTransformOf(Vec(0.0, 1.0, 0.0)); +} +/*! Returns the normalized view direction of the Camera, defined in the world +coordinate system. + +Change this value using setViewDirection(), lookAt() or setOrientation(). It is +orthogonal to upVector() and to rightVector(). + +This corresponds to the negative Z axis of the frame() ( +frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0)) ). */ +CGAL_INLINE_FUNCTION +Vec Camera::viewDirection() const { + return frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0)); +} + +/*! Returns the normalized right vector of the Camera, defined in the world +coordinate system. + +This vector lies in the Camera horizontal plane, directed along the X axis +(orthogonal to upVector() and to viewDirection()). Set using setUpVector(), +lookAt() or setOrientation(). + +Simply returns frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0)). */ +CGAL_INLINE_FUNCTION +Vec Camera::rightVector() const { + return frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0)); +} + +/*! Returns the Camera orientation, defined in the world coordinate system. + +Actually returns \c frame()->orientation(). Use setOrientation(), setUpVector() +or lookAt() to set the Camera orientation. */ +CGAL_INLINE_FUNCTION +Quaternion Camera::orientation() const { return frame()->orientation(); } + +/*! Sets the Camera position() (the eye), defined in the world coordinate + * system. */ +CGAL_INLINE_FUNCTION +void Camera::setPosition(const Vec &pos) { frame()->setPosition(pos); } + +/*! Returns the Camera frame coordinates of a point \p src defined in world +coordinates. + +worldCoordinatesOf() performs the inverse transformation. + +Note that the point coordinates are simply converted in a different coordinate +system. They are not projected on screen. Use projectedCoordinatesOf() for that. +*/ +CGAL_INLINE_FUNCTION +Vec Camera::cameraCoordinatesOf(const Vec &src) const { + return frame()->coordinatesOf(src); +} + +/*! Returns the world coordinates of the point whose position \p src is defined +in the Camera coordinate system. + +cameraCoordinatesOf() performs the inverse transformation. */ +CGAL_INLINE_FUNCTION +Vec Camera::worldCoordinatesOf(const Vec &src) const { + return frame()->inverseCoordinatesOf(src); +} + +/*! Returns the fly speed of the Camera. + +Simply returns frame()->flySpeed(). See the ManipulatedCameraFrame::flySpeed() +documentation. This value is only meaningful when the MouseAction bindings is +QGLViewer::MOVE_FORWARD or QGLViewer::MOVE_BACKWARD. + +Set to 1% of the sceneRadius() by setSceneRadius(). See also setFlySpeed(). */ +CGAL_INLINE_FUNCTION +qreal Camera::flySpeed() const { return frame()->flySpeed(); } + +/*! Sets the Camera flySpeed(). + +\attention This value is modified by setSceneRadius(). */ +CGAL_INLINE_FUNCTION +void Camera::setFlySpeed(qreal speed) { frame()->setFlySpeed(speed); } + +/*! The point the Camera pivots around with the QGLViewer::ROTATE mouse binding. +Defined in world coordinate system. + +Default value is the sceneCenter(). + +\attention setSceneCenter() changes this value. */ +CGAL_INLINE_FUNCTION +Vec Camera::pivotPoint() const { return frame()->pivotPoint(); } + +/*! Sets the Camera's position() and orientation() from an OpenGL ModelView +matrix. + +This enables a Camera initialisation from an other OpenGL application. \p +modelView is a 16 GLdouble vector representing a valid OpenGL ModelView matrix, +such as one can get using: \code GLdouble mvm[16]; +glGetDoublev(GL_MODELVIEW_MATRIX, mvm); +myCamera->setFromModelViewMatrix(mvm); +\endcode + +After this method has been called, getModelViewMatrix() returns a matrix +equivalent to \p modelView. + +Only the orientation() and position() of the Camera are modified. + +\note If you defined your matrix as \c GLdouble \c mvm[4][4], pass \c +&(mvm[0][0]) as a parameter. */ +CGAL_INLINE_FUNCTION +void Camera::setFromModelViewMatrix(const GLdouble *const modelViewMatrix) { + // Get upper left (rotation) matrix + qreal upperLeft[3][3]; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + upperLeft[i][j] = modelViewMatrix[i * 4 + j]; + + // Transform upperLeft into the associated Quaternion + Quaternion q; + q.setFromRotationMatrix(upperLeft); + + setOrientation(q); + setPosition(-q.rotate( + Vec(modelViewMatrix[12], modelViewMatrix[13], modelViewMatrix[14]))); +} + +/*! Defines the Camera position(), orientation() and fieldOfView() from a + projection matrix. + + \p matrix has to be given in the format used by vision algorithm. It has 3 + lines and 4 columns. It transforms a point from the world homogeneous + coordinate system (4 coordinates: \c sx, \c sy, \c sz and \c s) into a point in + the screen homogeneous coordinate system (3 coordinates: \c sx, \c sy, and \c + s, where \c x and \c y are the pixel coordinates on the screen). + + Its three lines correspond to the homogeneous coordinates of the normals to the + planes x=0, y=0 and z=0, defined in the Camera coordinate system. + + The elements of the matrix are ordered in line major order: you can call \c + setFromProjectionMatrix(&(matrix[0][0])) if you defined your matrix as a \c + qreal \c matrix[3][4]. + + \attention Passing the result of getProjectionMatrix() or getModelViewMatrix() + to this method is not possible (purposefully incompatible matrix dimensions). + \p matrix is more likely to be the product of these two matrices, without the + last line. + + Use setFromModelViewMatrix() to set position() and orientation() from a \c + GL_MODELVIEW matrix. fieldOfView() can also be retrieved from a \e perspective + \c GL_PROJECTION matrix using 2.0 * atan(1.0/projectionMatrix[5]). + + This code was written by Sylvain Paris. */ +CGAL_INLINE_FUNCTION +void Camera::setFromProjectionMatrix(const qreal matrix[12]) { + // The 3 lines of the matrix are the normals to the planes x=0, y=0, z=0 + // in the camera CS. As we normalize them, we do not need the 4th coordinate. + Vec line_0(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 2)]); + Vec line_1(matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 2)]); + Vec line_2(matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 2)]); + + line_0.normalize(); + line_1.normalize(); + line_2.normalize(); + + // The camera position is at (0,0,0) in the camera CS so it is the + // intersection of the 3 planes. It can be seen as the kernel + // of the 3x4 projection matrix. We calculate it through 4 dimensional + // vectorial product. We go directly into 3D that is to say we directly + // divide the first 3 coordinates by the 4th one. + + // We derive the 4 dimensional vectorial product formula from the + // computation of a 4x4 determinant that is developped according to + // its 4th column. This implies some 3x3 determinants. + const Vec cam_pos = + Vec(det(matrix[ind(0, 1)], matrix[ind(0, 2)], matrix[ind(0, 3)], + matrix[ind(1, 1)], matrix[ind(1, 2)], matrix[ind(1, 3)], + matrix[ind(2, 1)], matrix[ind(2, 2)], matrix[ind(2, 3)]), + + -det(matrix[ind(0, 0)], matrix[ind(0, 2)], matrix[ind(0, 3)], + matrix[ind(1, 0)], matrix[ind(1, 2)], matrix[ind(1, 3)], + matrix[ind(2, 0)], matrix[ind(2, 2)], matrix[ind(2, 3)]), + + det(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 3)], + matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 3)], + matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 3)])) / + + (-det(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 2)], + matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 2)], + matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 2)])); + + // We compute the rotation matrix column by column. + + // GL Z axis is front facing. + Vec column_2 = -line_2; + + // X-axis is almost like line_0 but should be orthogonal to the Z axis. + Vec column_0 = ((column_2 ^ line_0) ^ column_2); + column_0.normalize(); + + // Y-axis is almost like line_1 but should be orthogonal to the Z axis. + // Moreover line_1 is downward oriented as the screen CS. + Vec column_1 = -((column_2 ^ line_1) ^ column_2); + column_1.normalize(); + + qreal rot[3][3]; + rot[0][0] = column_0[0]; + rot[1][0] = column_0[1]; + rot[2][0] = column_0[2]; + + rot[0][1] = column_1[0]; + rot[1][1] = column_1[1]; + rot[2][1] = column_1[2]; + + rot[0][2] = column_2[0]; + rot[1][2] = column_2[1]; + rot[2][2] = column_2[2]; + + // We compute the field of view + + // line_1^column_0 -> vector of intersection line between + // y_screen=0 and x_camera=0 plane. + // column_2*(...) -> cos of the angle between Z vector et y_screen=0 plane + // * 2 -> field of view = 2 * half angle + + // We need some intermediate values. + Vec dummy = line_1 ^ column_0; + dummy.normalize(); + qreal fov = acos(column_2 * dummy) * 2.0; + + // We set the camera. + Quaternion q; + q.setFromRotationMatrix(rot); + setOrientation(q); + setPosition(cam_pos); + setFieldOfView(fov); +} + +/* + // persp : projectionMatrix_[0] = f/aspectRatio(); +CGAL_INLINE_FUNCTION +void Camera::setFromProjectionMatrix(const GLdouble* projectionMatrix) +{ + QString message; + if ((fabs(projectionMatrix[1]) > 1E-3) || + (fabs(projectionMatrix[2]) > 1E-3) || + (fabs(projectionMatrix[3]) > 1E-3) || + (fabs(projectionMatrix[4]) > 1E-3) || + (fabs(projectionMatrix[6]) > 1E-3) || + (fabs(projectionMatrix[7]) > 1E-3) || + (fabs(projectionMatrix[8]) > 1E-3) || + (fabs(projectionMatrix[9]) > 1E-3)) + message = "Non null coefficient in projection matrix - Aborting"; + else + if ((fabs(projectionMatrix[11]+1.0) < 1E-5) && (fabs(projectionMatrix[15]) < +1E-5)) + { + if (projectionMatrix[5] < 1E-4) + message="Negative field of view in Camera::setFromProjectionMatrix"; + else + setType(Camera::PERSPECTIVE); + } + else + if ((fabs(projectionMatrix[11]) < 1E-5) && (fabs(projectionMatrix[15]-1.0) < +1E-5)) setType(Camera::ORTHOGRAPHIC); else message = "Unable to determine camera +type in setFromProjectionMatrix - Aborting"; + + if (!message.isEmpty()) + { + qWarning(message); + return; + } + + switch (type()) + { + case Camera::PERSPECTIVE: + { + setFieldOfView(2.0 * atan(1.0/projectionMatrix[5])); + const qreal far = projectionMatrix[14] / (2.0 * (1.0 + projectionMatrix[10])); + const qreal near = (projectionMatrix[10]+1.0) / (projectionMatrix[10]-1.0) * +far; setSceneRadius((far-near)/2.0); setSceneCenter(position() + (near + +sceneRadius())*viewDirection()); break; + } + case Camera::ORTHOGRAPHIC: + { + GLdouble w, h; + getOrthoWidthHeight(w,h); + projectionMatrix_[0] = 1.0/w; + projectionMatrix_[5] = 1.0/h; + projectionMatrix_[10] = -2.0/(ZFar - ZNear); + projectionMatrix_[11] = 0.0; + projectionMatrix_[14] = -(ZFar + ZNear)/(ZFar - ZNear); + projectionMatrix_[15] = 1.0; + // same as glOrtho( -w, w, -h, h, zNear(), zFar() ); + break; + } + } +} +*/ + +///////////////////////// Camera to world transform /////////////////////// + +/*! Same as cameraCoordinatesOf(), but with \c qreal[3] parameters (\p src and + * \p res may be identical pointers). */ +CGAL_INLINE_FUNCTION +void Camera::getCameraCoordinatesOf(const qreal src[3], qreal res[3]) const { + Vec r = cameraCoordinatesOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as worldCoordinatesOf(), but with \c qreal[3] parameters (\p src and \p + * res may be identical pointers). */ +CGAL_INLINE_FUNCTION +void Camera::getWorldCoordinatesOf(const qreal src[3], qreal res[3]) const { + Vec r = worldCoordinatesOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Fills \p viewport with the Camera OpenGL viewport. + +This method is mainly used in conjunction with \c gluProject, which requires +such a viewport. Returned values are (0, screenHeight(), screenWidth(), - +screenHeight()), so that the origin is located in the \e upper left corner of +the window (Qt style coordinate system). */ +CGAL_INLINE_FUNCTION +void Camera::getViewport(GLint viewport[4]) const { + viewport[0] = 0; + viewport[1] = screenHeight(); + viewport[2] = screenWidth(); + viewport[3] = -screenHeight(); +} + +//source code of GluProject and GluUnproject, imported here to avoid the dependency to Glu +CGAL_INLINE_FUNCTION +int project(qreal objx, qreal objy, qreal objz, GLdouble *modelview, + GLdouble *projection, int *viewport, GLdouble*winX, GLdouble *winY,GLdouble *winZ) + { + //Transformation vectors + GLdouble fTempo[8]; + //Modelview transform + fTempo[0]=modelview[0]*objx+modelview[4]*objy+modelview[8]*objz+modelview[12]; //w is always 1 + fTempo[1]=modelview[1]*objx+modelview[5]*objy+modelview[9]*objz+modelview[13]; + fTempo[2]=modelview[2]*objx+modelview[6]*objy+modelview[10]*objz+modelview[14]; + fTempo[3]=modelview[3]*objx+modelview[7]*objy+modelview[11]*objz+modelview[15]; + //Projection transform, the final row of projection matrix is always [0 0 -1 0] + //so we optimize for that. + fTempo[4]=projection[0]*fTempo[0]+projection[4]*fTempo[1]+projection[8]*fTempo[2]+projection[12]*fTempo[3]; + fTempo[5]=projection[1]*fTempo[0]+projection[5]*fTempo[1]+projection[9]*fTempo[2]+projection[13]*fTempo[3]; + fTempo[6]=projection[2]*fTempo[0]+projection[6]*fTempo[1]+projection[10]*fTempo[2]+projection[14]*fTempo[3]; + fTempo[7]=-fTempo[2]; + //The result normalizes between -1 and 1 + if(fTempo[7]==0.0) //The w value + return 0; + fTempo[7]=1.0/fTempo[7]; + //Perspective division + fTempo[4]*=fTempo[7]; + fTempo[5]*=fTempo[7]; + fTempo[6]*=fTempo[7]; + //Window coordinates + //Map x, y to range 0-1 + *winX=(fTempo[4]*0.5+0.5)*viewport[2]+viewport[0]; + *winY=(fTempo[5]*0.5+0.5)*viewport[3]+viewport[1]; + //This is only correct when glDepthRange(0.0, 1.0) + *winZ=(1.0+fTempo[6])*0.5; //Between 0 and 1 + return 1; +} + +CGAL_INLINE_FUNCTION +void MultiplyMatrices4by4OpenGL_GLdouble(GLdouble *result, GLdouble *matrix1, GLdouble *matrix2) + { + result[0]=matrix1[0]*matrix2[0]+ + matrix1[4]*matrix2[1]+ + matrix1[8]*matrix2[2]+ + matrix1[12]*matrix2[3]; + result[4]=matrix1[0]*matrix2[4]+ + matrix1[4]*matrix2[5]+ + matrix1[8]*matrix2[6]+ + matrix1[12]*matrix2[7]; + result[8]=matrix1[0]*matrix2[8]+ + matrix1[4]*matrix2[9]+ + matrix1[8]*matrix2[10]+ + matrix1[12]*matrix2[11]; + result[12]=matrix1[0]*matrix2[12]+ + matrix1[4]*matrix2[13]+ + matrix1[8]*matrix2[14]+ + matrix1[12]*matrix2[15]; + result[1]=matrix1[1]*matrix2[0]+ + matrix1[5]*matrix2[1]+ + matrix1[9]*matrix2[2]+ + matrix1[13]*matrix2[3]; + result[5]=matrix1[1]*matrix2[4]+ + matrix1[5]*matrix2[5]+ + matrix1[9]*matrix2[6]+ + matrix1[13]*matrix2[7]; + result[9]=matrix1[1]*matrix2[8]+ + matrix1[5]*matrix2[9]+ + matrix1[9]*matrix2[10]+ + matrix1[13]*matrix2[11]; + result[13]=matrix1[1]*matrix2[12]+ + matrix1[5]*matrix2[13]+ + matrix1[9]*matrix2[14]+ + matrix1[13]*matrix2[15]; + result[2]=matrix1[2]*matrix2[0]+ + matrix1[6]*matrix2[1]+ + matrix1[10]*matrix2[2]+ + matrix1[14]*matrix2[3]; + result[6]=matrix1[2]*matrix2[4]+ + matrix1[6]*matrix2[5]+ + matrix1[10]*matrix2[6]+ + matrix1[14]*matrix2[7]; + result[10]=matrix1[2]*matrix2[8]+ + matrix1[6]*matrix2[9]+ + matrix1[10]*matrix2[10]+ + matrix1[14]*matrix2[11]; + result[14]=matrix1[2]*matrix2[12]+ + matrix1[6]*matrix2[13]+ + matrix1[10]*matrix2[14]+ + matrix1[14]*matrix2[15]; + result[3]=matrix1[3]*matrix2[0]+ + matrix1[7]*matrix2[1]+ + matrix1[11]*matrix2[2]+ + matrix1[15]*matrix2[3]; + result[7]=matrix1[3]*matrix2[4]+ + matrix1[7]*matrix2[5]+ + matrix1[11]*matrix2[6]+ + matrix1[15]*matrix2[7]; + result[11]=matrix1[3]*matrix2[8]+ + matrix1[7]*matrix2[9]+ + matrix1[11]*matrix2[10]+ + matrix1[15]*matrix2[11]; + result[15]=matrix1[3]*matrix2[12]+ + matrix1[7]*matrix2[13]+ + matrix1[11]*matrix2[14]+ + matrix1[15]*matrix2[15]; + } + +CGAL_INLINE_FUNCTION + void MultiplyMatrixByVector4by4OpenGL_GLdouble(GLdouble *resultvector, const GLdouble *matrix, const GLdouble *pvector) + { + resultvector[0]=matrix[0]*pvector[0]+matrix[4]*pvector[1]+matrix[8]*pvector[2]+matrix[12]*pvector[3]; + resultvector[1]=matrix[1]*pvector[0]+matrix[5]*pvector[1]+matrix[9]*pvector[2]+matrix[13]*pvector[3]; + resultvector[2]=matrix[2]*pvector[0]+matrix[6]*pvector[1]+matrix[10]*pvector[2]+matrix[14]*pvector[3]; + resultvector[3]=matrix[3]*pvector[0]+matrix[7]*pvector[1]+matrix[11]*pvector[2]+matrix[15]*pvector[3]; + } + + #define SWAP_ROWS_DOUBLE(a, b) { double *_tmp = a; (a)=(b); (b)=_tmp; } + #define SWAP_ROWS_GLdouble(a, b) { GLdouble *_tmp = a; (a)=(b); (b)=_tmp; } + #define MAT(m,r,c) (m)[(c)*4+(r)] + //This code comes directly from GLU except that it is for GLdouble + CGAL_INLINE_FUNCTION + int glhInvertMatrixf2(GLdouble *m, GLdouble *out) + { + GLdouble wtmp[4][8]; + GLdouble m0, m1, m2, m3, s; + GLdouble *r0, *r1, *r2, *r3; + r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; + r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1), + r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3), + r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0, + r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1), + r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3), + r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0, + r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1), + r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3), + r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0, + r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1), + r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3), + r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0; + /* choose pivot - or die */ + if (fabs(r3[0]) > fabs(r2[0])) + SWAP_ROWS_GLdouble(r3, r2); + if (fabs(r2[0]) > fabs(r1[0])) + SWAP_ROWS_GLdouble(r2, r1); + if (fabs(r1[0]) > fabs(r0[0])) + SWAP_ROWS_GLdouble(r1, r0); + if (0.0 == r0[0]) + return 0; + /* eliminate first variable */ + m1 = r1[0] / r0[0]; + m2 = r2[0] / r0[0]; + m3 = r3[0] / r0[0]; + s = r0[1]; + r1[1] -= m1 * s; + r2[1] -= m2 * s; + r3[1] -= m3 * s; + s = r0[2]; + r1[2] -= m1 * s; + r2[2] -= m2 * s; + r3[2] -= m3 * s; + s = r0[3]; + r1[3] -= m1 * s; + r2[3] -= m2 * s; + r3[3] -= m3 * s; + s = r0[4]; + if (s != 0.0) { + r1[4] -= m1 * s; + r2[4] -= m2 * s; + r3[4] -= m3 * s; + } + s = r0[5]; + if (s != 0.0) { + r1[5] -= m1 * s; + r2[5] -= m2 * s; + r3[5] -= m3 * s; + } + s = r0[6]; + if (s != 0.0) { + r1[6] -= m1 * s; + r2[6] -= m2 * s; + r3[6] -= m3 * s; + } + s = r0[7]; + if (s != 0.0) { + r1[7] -= m1 * s; + r2[7] -= m2 * s; + r3[7] -= m3 * s; + } + /* choose pivot - or die */ + if (fabs(r3[1]) > fabs(r2[1])) + SWAP_ROWS_GLdouble(r3, r2); + if (fabs(r2[1]) > fabs(r1[1])) + SWAP_ROWS_GLdouble(r2, r1); + if (0.0 == r1[1]) + return 0; + /* eliminate second variable */ + m2 = r2[1] / r1[1]; + m3 = r3[1] / r1[1]; + r2[2] -= m2 * r1[2]; + r3[2] -= m3 * r1[2]; + r2[3] -= m2 * r1[3]; + r3[3] -= m3 * r1[3]; + s = r1[4]; + if (0.0 != s) { + r2[4] -= m2 * s; + r3[4] -= m3 * s; + } + s = r1[5]; + if (0.0 != s) { + r2[5] -= m2 * s; + r3[5] -= m3 * s; + } + s = r1[6]; + if (0.0 != s) { + r2[6] -= m2 * s; + r3[6] -= m3 * s; + } + s = r1[7]; + if (0.0 != s) { + r2[7] -= m2 * s; + r3[7] -= m3 * s; + } + /* choose pivot - or die */ + if (fabs(r3[2]) > fabs(r2[2])) + SWAP_ROWS_GLdouble(r3, r2); + if (0.0 == r2[2]) + return 0; + /* eliminate third variable */ + m3 = r3[2] / r2[2]; + r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], + r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7]; + /* last check */ + if (0.0 == r3[3]) + return 0; + s = 1.0 / r3[3]; /* now back substitute row 3 */ + r3[4] *= s; + r3[5] *= s; + r3[6] *= s; + r3[7] *= s; + m2 = r2[3]; /* now back substitute row 2 */ + s = 1.0 / r2[2]; + r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), + r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); + m1 = r1[3]; + r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, + r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; + m0 = r0[3]; + r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, + r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; + m1 = r1[2]; /* now back substitute row 1 */ + s = 1.0 / r1[1]; + r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), + r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); + m0 = r0[2]; + r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, + r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; + m0 = r0[1]; /* now back substitute row 0 */ + s = 1.0 / r0[0]; + r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), + r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); + MAT(out, 0, 0) = r0[4]; + MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6]; + MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4]; + MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6]; + MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4]; + MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6]; + MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4]; + MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6]; + MAT(out, 3, 3) = r3[7]; + return 1; + } +CGAL_INLINE_FUNCTION +int unProject(GLdouble winx, GLdouble winy, GLdouble winz, GLdouble *modelview, GLdouble *projection, int *viewport, + GLdouble *objX,GLdouble *objY,GLdouble *objZ) + { + //Transformation matrices + GLdouble m[16], A[16]; + GLdouble in[4], out[4]; + //Calculation for inverting a matrix, compute projection x modelview + //and store in A[16] + MultiplyMatrices4by4OpenGL_GLdouble(A, projection, modelview); + //Now compute the inverse of matrix A + if(glhInvertMatrixf2(A, m)==0) + return 0; + //Transformation of normalized coordinates between -1 and 1 + in[0]=(winx-(GLdouble)viewport[0])/(GLdouble)viewport[2]*2.0-1.0; + in[1]=(winy-(GLdouble)viewport[1])/(GLdouble)viewport[3]*2.0-1.0; + in[2]=2.0*winz-1.0; + in[3]=1.0; + //Objects coordinates + MultiplyMatrixByVector4by4OpenGL_GLdouble(out, m, in); + if(out[3]==0.0) + return 0; + out[3]=1.0/out[3]; + *objX=out[0]*out[3]; + *objY=out[1]*out[3]; + *objZ=out[2]*out[3]; + return 1; + } + +/*! Returns the screen projected coordinates of a point \p src defined in the \p frame coordinate + system. + When \p frame in \c NULL (default), \p src is expressed in the world coordinate system. + The x and y coordinates of the returned Vec are expressed in pixel, (0,0) being the \e upper left + corner of the window. The z coordinate ranges between 0.0 (near plane) and 1.0 (excluded, far + plane). See the \c gluProject man page for details. + unprojectedCoordinatesOf() performs the inverse transformation. + See the screenCoordSystem example. + This method only uses the intrinsic Camera parameters (see getModelViewMatrix(), + getProjectionMatrix() and getViewport()) and is completely independent of the OpenGL \c + GL_MODELVIEW, \c GL_PROJECTION and viewport matrices. You can hence define a virtual Camera and use + this method to compute projections out of a classical rendering context. + \attention However, if your Camera is not attached to a QGLViewer (used for offscreen computations + for instance), make sure the Camera matrices are updated before calling this method. Call + computeModelViewMatrix() and computeProjectionMatrix() to do so. + If you call this method several times with no change in the matrices, consider precomputing the + projection times modelview matrix to save computation time if required (\c P x \c M in the \c + gluProject man page). + Here is the code corresponding to what this method does (kindly submitted by Robert W. Kuhn) : + \code + Vec project(Vec point) + { + GLint Viewport[4]; + GLdouble Projection[16], Modelview[16]; + GLdouble matrix[16]; + // Precomputation begin + glGetIntegerv(GL_VIEWPORT , Viewport); + glGetDoublev (GL_MODELVIEW_MATRIX , Modelview); + glGetDoublev (GL_PROJECTION_MATRIX, Projection); + for (unsigned short m=0; m<4; ++m) + { + for (unsigned short l=0; l<4; ++l) + { + qreal sum = 0.0; + for (unsigned short k=0; k<4; ++k) + sum += Projection[l+4*k]*Modelview[k+4*m]; + matrix[l+4*m] = sum; + } + } + // Precomputation end + GLdouble v[4], vs[4]; + v[0]=point[0]; v[1]=point[1]; v[2]=point[2]; v[3]=1.0; + vs[0]=matrix[0 ]*v[0] + matrix[4 ]*v[1] + matrix[8 ]*v[2] + matrix[12 ]*v[3]; + vs[1]=matrix[1 ]*v[0] + matrix[5 ]*v[1] + matrix[9 ]*v[2] + matrix[13 ]*v[3]; + vs[2]=matrix[2 ]*v[0] + matrix[6 ]*v[1] + matrix[10]*v[2] + matrix[14 ]*v[3]; + vs[3]=matrix[3 ]*v[0] + matrix[7 ]*v[1] + matrix[11]*v[2] + matrix[15 ]*v[3]; + vs[0] /= vs[3]; + vs[1] /= vs[3]; + vs[2] /= vs[3]; + vs[0] = vs[0] * 0.5 + 0.5; + vs[1] = vs[1] * 0.5 + 0.5; + vs[2] = vs[2] * 0.5 + 0.5; + vs[0] = vs[0] * Viewport[2] + Viewport[0]; + vs[1] = vs[1] * Viewport[3] + Viewport[1]; + return Vec(vs[0], Viewport[3]-vs[1], vs[2]); + } + \endcode + */ +CGAL_INLINE_FUNCTION +Vec Camera::projectedCoordinatesOf(const Vec& src, const Frame* frame) const +{ + GLdouble x,y,z; + static GLint viewport[4]; + getViewport(viewport); + + if (frame) + { + const Vec tmp = frame->inverseCoordinatesOf(src); + project(tmp.x,tmp.y,tmp.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z); + } + else + project(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z); + + return Vec(x,y,z); +} + +/*! Returns the world unprojected coordinates of a point \p src defined in the screen coordinate + system. + The \p src.x and \p src.y input values are expressed in pixels, (0,0) being the \e upper left corner + of the window. \p src.z is a depth value ranging in [0..1[ (respectively corresponding to the near + and far planes). Note that src.z is \e not a linear interpolation between zNear and zFar. + /code + src.z = zFar() / (zFar() - zNear()) * (1.0 - zNear() / z); + /endcode + Where z is the distance from the point you project to the camera, along the viewDirection(). + See the \c gluUnProject man page for details. + The result is expressed in the \p frame coordinate system. When \p frame is \c NULL (default), the + result is expressed in the world coordinates system. The possible \p frame Frame::referenceFrame() + are taken into account. + projectedCoordinatesOf() performs the inverse transformation. + This method only uses the intrinsic Camera parameters (see getModelViewMatrix(), + getProjectionMatrix() and getViewport()) and is completely independent of the OpenGL \c + GL_MODELVIEW, \c GL_PROJECTION and viewport matrices. You can hence define a virtual Camera and use + this method to compute un-projections out of a classical rendering context. + \attention However, if your Camera is not attached to a QGLViewer (used for offscreen computations + for instance), make sure the Camera matrices are updated before calling this method (use + computeModelViewMatrix(), computeProjectionMatrix()). See also setScreenWidthAndHeight(). + This method is not computationally optimized. If you call it several times with no change in the + matrices, you should buffer the entire inverse projection matrix (modelview, projection and then + viewport) to speed-up the queries. See the \c gluUnProject man page for details. */ +CGAL_INLINE_FUNCTION +Vec Camera::unprojectedCoordinatesOf(const Vec& src, const Frame* frame) const +{ + GLdouble x,y,z; + static GLint viewport[4]; + getViewport(viewport); + unProject(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z); + if (frame) + return frame->coordinatesOf(Vec(x,y,z)); + else + return Vec(x,y,z); +} + +/*! Same as projectedCoordinatesOf(), but with \c qreal parameters (\p src and + * \p res can be identical pointers). */ +CGAL_INLINE_FUNCTION +void Camera::getProjectedCoordinatesOf(const qreal src[3], qreal res[3], + const Frame *frame) const { + Vec r = projectedCoordinatesOf(Vec(src), frame); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as unprojectedCoordinatesOf(), but with \c qreal parameters (\p src and + * \p res can be identical pointers). */ +CGAL_INLINE_FUNCTION +void Camera::getUnprojectedCoordinatesOf(const qreal src[3], qreal res[3], + const Frame *frame) const { + Vec r = unprojectedCoordinatesOf(Vec(src), frame); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +///////////////////////////////////// KFI +//////////////////////////////////////////// + +/*! Returns the KeyFrameInterpolator that defines the Camera path number \p i. + +If path \p i is not defined for this index, the method returns a \c NULL +pointer. */ +CGAL_INLINE_FUNCTION +KeyFrameInterpolator *Camera::keyFrameInterpolator(unsigned int i) const { + if (kfi_.contains(i)) + return kfi_[i]; + else + return NULL; +} + +/*! Sets the KeyFrameInterpolator that defines the Camera path of index \p i. + + The previous keyFrameInterpolator() is lost and should be deleted by the + calling method if needed. + + The KeyFrameInterpolator::interpolated() signal of \p kfi probably needs to be + connected to the Camera's associated QGLViewer::update() slot, so that when the + Camera position is interpolated using \p kfi, every interpolation step updates + the display: \code myViewer.camera()->deletePath(3); + myViewer.camera()->setKeyFrameInterpolator(3, myKeyFrameInterpolator); + connect(myKeyFrameInterpolator, SIGNAL(interpolated()), myViewer, + SLOT(update()); \endcode + + \note These connections are done automatically when a Camera is attached to a + QGLViewer, or when a new KeyFrameInterpolator is defined using the + QGLViewer::addKeyFrameKeyboardModifiers() and QGLViewer::pathKey() (default is + Alt+F[1-12]). See the keyboard page for details. + */ +CGAL_INLINE_FUNCTION +void Camera::setKeyFrameInterpolator(unsigned int i, + KeyFrameInterpolator *const kfi) { + if (kfi) + kfi_[i] = kfi; + else + kfi_.remove(i); +} + +/*! Adds the current Camera position() and orientation() as a keyFrame to the +path number \p i. + +This method can also be used if you simply want to save a Camera point of view +(a path made of a single keyFrame). Use playPath() to make the Camera play the +keyFrame path (resp. restore the point of view). Use deletePath() to clear the +path. + +The default keyboard shortcut for this method is Alt+F[1-12]. Set +QGLViewer::pathKey() and QGLViewer::addKeyFrameKeyboardModifiers(). + +If you use directly this method and the keyFrameInterpolator(i) does not exist, +a new one is created. Its KeyFrameInterpolator::interpolated() signal should +then be connected to the QGLViewer::update() slot (see +setKeyFrameInterpolator()). */ +CGAL_INLINE_FUNCTION +void Camera::addKeyFrameToPath(unsigned int i) { + if (!kfi_.contains(i)) + setKeyFrameInterpolator(i, new KeyFrameInterpolator(frame())); + + kfi_[i]->addKeyFrame(*(frame())); +} + +/*! Makes the Camera follow the path of keyFrameInterpolator() number \p i. + + If the interpolation is started, it stops it instead. + + This method silently ignores undefined (empty) paths (see + keyFrameInterpolator()). + + The default keyboard shortcut for this method is F[1-12]. Set + QGLViewer::pathKey() and QGLViewer::playPathKeyboardModifiers(). */ +CGAL_INLINE_FUNCTION +void Camera::playPath(unsigned int i) { + if (kfi_.contains(i)) { + if (kfi_[i]->interpolationIsStarted()) + kfi_[i]->stopInterpolation(); + else + kfi_[i]->startInterpolation(); + } +} + +/*! Resets the path of the keyFrameInterpolator() number \p i. + +If this path is \e not being played (see playPath() and +KeyFrameInterpolator::interpolationIsStarted()), resets it to its starting +position (see KeyFrameInterpolator::resetInterpolation()). If the path is +played, simply stops interpolation. */ +CGAL_INLINE_FUNCTION +void Camera::resetPath(unsigned int i) { + if (kfi_.contains(i)) { + if ((kfi_[i]->interpolationIsStarted())) + kfi_[i]->stopInterpolation(); + else { + kfi_[i]->resetInterpolation(); + kfi_[i]->interpolateAtTime(kfi_[i]->interpolationTime()); + } + } +} + +/*! Deletes the keyFrameInterpolator() of index \p i. + +Disconnect the keyFrameInterpolator() KeyFrameInterpolator::interpolated() +signal before deleting the keyFrameInterpolator() if needed: \code +disconnect(camera()->keyFrameInterpolator(i), SIGNAL(interpolated()), this, +SLOT(update())); camera()->deletePath(i); \endcode */ +CGAL_INLINE_FUNCTION +void Camera::deletePath(unsigned int i) { + if (kfi_.contains(i)) { + kfi_[i]->stopInterpolation(); + delete kfi_[i]; + kfi_.remove(i); + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +/*! Returns an XML \c QDomElement that represents the Camera. + + \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument + factory used to create QDomElement. + + Concatenates the Camera parameters, the ManipulatedCameraFrame::domElement() + and the paths' KeyFrameInterpolator::domElement(). + + Use initFromDOMElement() to restore the Camera state from the resulting \c + QDomElement. + + If you want to save the Camera state in a file, use: + \code + QDomDocument document("myCamera"); + doc.appendChild( myCamera->domElement("Camera", document) ); + + QFile f("myCamera.xml"); + if (f.open(IO_WriteOnly)) + { + QTextStream out(&f); + document.save(out, 2); + } + \endcode + + Note that the QGLViewer::camera() is automatically saved by + QGLViewer::saveStateToFile() when a QGLViewer is closed. Use + QGLViewer::restoreStateFromFile() to restore it back. */ +CGAL_INLINE_FUNCTION +QDomElement Camera::domElement(const QString &name, + QDomDocument &document) const { + QDomElement de = document.createElement(name); + QDomElement paramNode = document.createElement("Parameters"); + paramNode.setAttribute("fieldOfView", QString::number(fieldOfView())); + paramNode.setAttribute("zNearCoefficient", + QString::number(zNearCoefficient())); + paramNode.setAttribute("zClippingCoefficient", + QString::number(zClippingCoefficient())); + paramNode.setAttribute("orthoCoef", QString::number(orthoCoef_)); + paramNode.setAttribute("sceneRadius", QString::number(sceneRadius())); + paramNode.appendChild(sceneCenter().domElement("SceneCenter", document)); + + switch (type()) { + case Camera::PERSPECTIVE: + paramNode.setAttribute("Type", "PERSPECTIVE"); + break; + case Camera::ORTHOGRAPHIC: + paramNode.setAttribute("Type", "ORTHOGRAPHIC"); + break; + } + de.appendChild(paramNode); + + QDomElement stereoNode = document.createElement("Stereo"); + stereoNode.setAttribute("IODist", QString::number(IODistance())); + stereoNode.setAttribute("focusDistance", QString::number(focusDistance())); + stereoNode.setAttribute("physScreenWidth", + QString::number(physicalScreenWidth())); + de.appendChild(stereoNode); + + de.appendChild(frame()->domElement("ManipulatedCameraFrame", document)); + + // KeyFrame paths + for (QMap::ConstIterator + it = kfi_.begin(), + end = kfi_.end(); + it != end; ++it) { + QDomElement kfNode = + (it.value())->domElement("KeyFrameInterpolator", document); + kfNode.setAttribute("index", QString::number(it.key())); + de.appendChild(kfNode); + } + + return de; +} + +/*! Restores the Camera state from a \c QDomElement created by domElement(). + + Use the following code to retrieve a Camera state from a file created using + domElement(): \code + // Load DOM from file + QDomDocument document; + QFile f("myCamera.xml"); + if (f.open(IO_ReadOnly)) + { + document.setContent(&f); + f.close(); + } + + // Parse the DOM tree + QDomElement main = document.documentElement(); + myCamera->initFromDOMElement(main); + \endcode + + The frame() pointer is not modified by this method. The frame() state is + however modified. + + \attention The original keyFrameInterpolator() are deleted and should be copied + first if they are shared. */ +CGAL_INLINE_FUNCTION +void Camera::initFromDOMElement(const QDomElement &element) { + QDomElement child = element.firstChild().toElement(); + + QMutableMapIterator it(kfi_); + while (it.hasNext()) { + it.next(); + deletePath(it.key()); + } + + while (!child.isNull()) { + if (child.tagName() == "Parameters") { + // #CONNECTION# Default values set in constructor + setFieldOfView(DomUtils::qrealFromDom(child, "fieldOfView", M_PI / 4.0)); + setZNearCoefficient( + DomUtils::qrealFromDom(child, "zNearCoefficient", 0.005)); + setZClippingCoefficient( + DomUtils::qrealFromDom(child, "zClippingCoefficient", sqrt(3.0))); + orthoCoef_ = + DomUtils::qrealFromDom(child, "orthoCoef", tan(fieldOfView() / 2.0)); + setSceneRadius( + DomUtils::qrealFromDom(child, "sceneRadius", sceneRadius())); + + setType(PERSPECTIVE); + QString type = child.attribute("Type", "PERSPECTIVE"); + if (type == "PERSPECTIVE") + setType(Camera::PERSPECTIVE); + if (type == "ORTHOGRAPHIC") + setType(Camera::ORTHOGRAPHIC); + + QDomElement child2 = child.firstChild().toElement(); + while (!child2.isNull()) { + /* Although the scene does not change when a camera is loaded, restore + the saved center and radius values. Mainly useful when a the viewer is + restored on startup, with possible additional cameras. */ + if (child2.tagName() == "SceneCenter") + setSceneCenter(Vec(child2)); + + child2 = child2.nextSibling().toElement(); + } + } + + if (child.tagName() == "ManipulatedCameraFrame") + frame()->initFromDOMElement(child); + + if (child.tagName() == "Stereo") { + setIODistance(DomUtils::qrealFromDom(child, "IODist", 0.062)); + setFocusDistance( + DomUtils::qrealFromDom(child, "focusDistance", focusDistance())); + setPhysicalScreenWidth( + DomUtils::qrealFromDom(child, "physScreenWidth", 0.5)); + } + + if (child.tagName() == "KeyFrameInterpolator") { + unsigned int index = DomUtils::uintFromDom(child, "index", 0); + setKeyFrameInterpolator(index, new KeyFrameInterpolator(frame())); + if (keyFrameInterpolator(index)) + keyFrameInterpolator(index)->initFromDOMElement(child); + } + + child = child.nextSibling().toElement(); + } +} + +/*! Gives the coefficients of a 3D half-line passing through the Camera eye and + pixel (x,y). + + The origin of the half line (eye position) is stored in \p orig, while \p dir + contains the properly oriented and normalized direction of the half line. + + \p x and \p y are expressed in Qt format (origin in the upper left corner). Use + screenHeight() - y to convert to OpenGL units. + + This method is useful for analytical intersection in a selection method. + + See the select example for an + illustration. */ +CGAL_INLINE_FUNCTION +void Camera::convertClickToLine(const QPoint &pixel, Vec &orig, + Vec &dir) const { + switch (type()) { + case Camera::PERSPECTIVE: + orig = position(); + dir = Vec(((2.0 * pixel.x() / screenWidth()) - 1.0) * + tan(fieldOfView() / 2.0) * aspectRatio(), + ((2.0 * (screenHeight() - pixel.y()) / screenHeight()) - 1.0) * + tan(fieldOfView() / 2.0), + -1.0); + dir = worldCoordinatesOf(dir) - orig; + dir.normalize(); + break; + + case Camera::ORTHOGRAPHIC: { + GLdouble w, h; + getOrthoWidthHeight(w, h); + orig = Vec((2.0 * pixel.x() / screenWidth() - 1.0) * w, + -(2.0 * pixel.y() / screenHeight() - 1.0) * h, 0.0); + orig = worldCoordinatesOf(orig); + dir = viewDirection(); + break; + } + } +} + + +/*! Returns the 6 plane equations of the Camera frustum. + +The six 4-component vectors of \p coef respectively correspond to the left, +right, near, far, top and bottom Camera frustum planes. Each vector holds a +plane equation of the form: \code a*x + b*y + c*z + d = 0 \endcode where \c a, +\c b, \c c and \c d are the 4 components of each vector, in that order. + +See the frustumCulling example for +an application. + +This format is compatible with the \c glClipPlane() function. One camera frustum +plane can hence be applied in an other viewer to visualize the culling results: +\code + // Retrieve plane equations + GLdouble coef[6][4]; + mainViewer->camera()->getFrustumPlanesCoefficients(coef); + + // These two additional clipping planes (which must have been enabled) + // will reproduce the mainViewer's near and far clipping. + glClipPlane(GL_CLIP_PLANE0, coef[2]); + glClipPlane(GL_CLIP_PLANE1, coef[3]); +\endcode */ +CGAL_INLINE_FUNCTION +void Camera::getFrustumPlanesCoefficients(GLdouble coef[6][4]) const { + // Computed once and for all + const Vec pos = position(); + const Vec viewDir = viewDirection(); + const Vec up = upVector(); + const Vec right = rightVector(); + const qreal posViewDir = pos * viewDir; + + static Vec normal[6]; + static GLdouble dist[6]; + + switch (type()) { + case Camera::PERSPECTIVE: { + const qreal hhfov = horizontalFieldOfView() / 2.0; + const qreal chhfov = cos(hhfov); + const qreal shhfov = sin(hhfov); + normal[0] = -shhfov * viewDir; + normal[1] = normal[0] + chhfov * right; + normal[0] = normal[0] - chhfov * right; + + normal[2] = -viewDir; + normal[3] = viewDir; + + const qreal hfov = fieldOfView() / 2.0; + const qreal chfov = cos(hfov); + const qreal shfov = sin(hfov); + normal[4] = -shfov * viewDir; + normal[5] = normal[4] - chfov * up; + normal[4] = normal[4] + chfov * up; + + for (int i = 0; i < 2; ++i) + dist[i] = pos * normal[i]; + for (int j = 4; j < 6; ++j) + dist[j] = pos * normal[j]; + + // Natural equations are: + // dist[0,1,4,5] = pos * normal[0,1,4,5]; + // dist[2] = (pos + zNear() * viewDir) * normal[2]; + // dist[3] = (pos + zFar() * viewDir) * normal[3]; + + // 2 times less computations using expanded/merged equations. Dir vectors + // are normalized. + const qreal posRightCosHH = chhfov * pos * right; + dist[0] = -shhfov * posViewDir; + dist[1] = dist[0] + posRightCosHH; + dist[0] = dist[0] - posRightCosHH; + const qreal posUpCosH = chfov * pos * up; + dist[4] = -shfov * posViewDir; + dist[5] = dist[4] - posUpCosH; + dist[4] = dist[4] + posUpCosH; + + break; + } + case Camera::ORTHOGRAPHIC: + normal[0] = -right; + normal[1] = right; + normal[4] = up; + normal[5] = -up; + + GLdouble hw, hh; + getOrthoWidthHeight(hw, hh); + dist[0] = (pos - hw * right) * normal[0]; + dist[1] = (pos + hw * right) * normal[1]; + dist[4] = (pos + hh * up) * normal[4]; + dist[5] = (pos - hh * up) * normal[5]; + break; + } + + // Front and far planes are identical for both camera types. + normal[2] = -viewDir; + normal[3] = viewDir; + dist[2] = -posViewDir - zNear(); + dist[3] = posViewDir + zFar(); + + for (int i = 0; i < 6; ++i) { + coef[i][0] = GLdouble(normal[i].x); + coef[i][1] = GLdouble(normal[i].y); + coef[i][2] = GLdouble(normal[i].z); + coef[i][3] = dist[i]; + } +} + +CGAL_INLINE_FUNCTION +void Camera::onFrameModified() { + projectionMatrixIsUpToDate_ = false; + modelViewMatrixIsUpToDate_ = false; +} + +CGAL_INLINE_FUNCTION +void Camera::setHorizontalFieldOfView(qreal hfov) { + setFieldOfView(2.0 * atan(tan(hfov / 2.0) / aspectRatio())); +} + +CGAL_INLINE_FUNCTION +qreal Camera::horizontalFieldOfView() const { + return 2.0 * atan(tan(fieldOfView() / 2.0) * aspectRatio()); +} + +CGAL_INLINE_FUNCTION +qreal Camera::physicalDistanceToScreen() const { + return physicalScreenWidth() / 2.0 / tan(horizontalFieldOfView() / 2.0); +} diff --git a/GraphicsView/include/CGAL/Qt/config.h b/GraphicsView/include/CGAL/Qt/config.h index 8cf25a273ab..27bb75b871b 100644 --- a/GraphicsView/include/CGAL/Qt/config.h +++ b/GraphicsView/include/CGAL/Qt/config.h @@ -48,7 +48,8 @@ Error : libQGLViewer requires a minimum Qt version of 4.0 #define M_PI 3.14159265358979323846 #endif #ifndef QGLVIEWER_STATIC -#ifdef CREATE_QGLVIEWER_DLL +#if ( defined(CGAL_BUILD_SHARED_LIBS) && ( ! defined(CGAL_HEADER_ONLY) ) ) \ + || defined(CGAL_USE_Qt5_RESOURCES) #if QT_VERSION >= 0x040500 #define QGLVIEWER_EXPORT Q_DECL_EXPORT #else @@ -78,13 +79,6 @@ Error : libQGLViewer requires a minimum Qt version of 4.0 // OpenGL headers. #include -// GLU was removed from Qt in version 4.8 -#ifdef Q_OS_MAC -#include -#else -#include -#endif - // Container classes interfaces changed a lot in Qt. // Compatibility patches are all grouped here. #include diff --git a/GraphicsView/include/CGAL/Qt/constraint.h b/GraphicsView/include/CGAL/Qt/constraint.h index fc6521bd41c..2c3af004115 100644 --- a/GraphicsView/include/CGAL/Qt/constraint.h +++ b/GraphicsView/include/CGAL/Qt/constraint.h @@ -26,10 +26,10 @@ #include #include + namespace qglviewer { class Frame; class Camera; - /*! \brief An interface class for Frame constraints. \class Constraint constraint.h QGLViewer/constraint.h @@ -349,14 +349,14 @@ class QGLVIEWER_EXPORT CameraConstraint : public AxisPlaneConstraint { public: explicit CameraConstraint(const Camera *const camera); /*! Virtual destructor. Empty. */ - virtual ~CameraConstraint(){}; + virtual ~CameraConstraint(){} virtual void constrainTranslation(Vec &translation, Frame *const frame); virtual void constrainRotation(Quaternion &rotation, Frame *const frame); /*! Returns the associated Camera. Set using the CameraConstraint constructor. */ - const Camera *camera() const { return camera_; }; + const Camera *camera() const { return camera_; } private: const Camera *const camera_; @@ -364,4 +364,7 @@ private: } // namespace qglviewer +#ifdef CGAL_HEADER_ONLY +//#include +#endif // CGAL_HEADER_ONLY #endif // QGLVIEWER_CONSTRAINT_H diff --git a/GraphicsView/include/CGAL/Qt/constraint_impl.h b/GraphicsView/include/CGAL/Qt/constraint_impl.h new file mode 100644 index 00000000000..be4d6837a8d --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/constraint_impl.h @@ -0,0 +1,301 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include +#include +#include + +using namespace qglviewer; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Constraint // +//////////////////////////////////////////////////////////////////////////////// + +/*! Default constructor. + +translationConstraintType() and rotationConstraintType() are set to +AxisPlaneConstraint::FREE. translationConstraintDirection() and +rotationConstraintDirection() are set to (0,0,0). */ +CGAL_INLINE_FUNCTION +AxisPlaneConstraint::AxisPlaneConstraint() + : translationConstraintType_(FREE), rotationConstraintType_(FREE) { + // Do not use set since setRotationConstraintType needs a read. +} + +/*! Simply calls setTranslationConstraintType() and + * setTranslationConstraintDirection(). */ +CGAL_INLINE_FUNCTION +void AxisPlaneConstraint::setTranslationConstraint(Type type, + const Vec &direction) { + setTranslationConstraintType(type); + setTranslationConstraintDirection(direction); +} + +/*! Defines the translationConstraintDirection(). The coordinate system where \p + * direction is expressed depends on your class implementation. */ +CGAL_INLINE_FUNCTION +void AxisPlaneConstraint::setTranslationConstraintDirection( + const Vec &direction) { + if ((translationConstraintType() != AxisPlaneConstraint::FREE) && + (translationConstraintType() != AxisPlaneConstraint::FORBIDDEN)) { + const qreal norm = direction.norm(); + if (norm < 1E-8) { + qWarning("AxisPlaneConstraint::setTranslationConstraintDir: null vector " + "for translation constraint"); + translationConstraintType_ = AxisPlaneConstraint::FREE; + } else + translationConstraintDir_ = direction / norm; + } +} + +/*! Simply calls setRotationConstraintType() and + * setRotationConstraintDirection(). */ +CGAL_INLINE_FUNCTION +void AxisPlaneConstraint::setRotationConstraint(Type type, + const Vec &direction) { + setRotationConstraintType(type); + setRotationConstraintDirection(direction); +} + +/*! Defines the rotationConstraintDirection(). The coordinate system where \p + * direction is expressed depends on your class implementation. */ +CGAL_INLINE_FUNCTION +void AxisPlaneConstraint::setRotationConstraintDirection(const Vec &direction) { + if ((rotationConstraintType() != AxisPlaneConstraint::FREE) && + (rotationConstraintType() != AxisPlaneConstraint::FORBIDDEN)) { + const qreal norm = direction.norm(); + if (norm < 1E-8) { + qWarning("AxisPlaneConstraint::setRotationConstraintDir: null vector for " + "rotation constraint"); + rotationConstraintType_ = AxisPlaneConstraint::FREE; + } else + rotationConstraintDir_ = direction / norm; + } +} + +/*! Set the Type() of the rotationConstraintType(). Default is + AxisPlaneConstraint::FREE. + + Depending on this value, the Frame will freely rotate + (AxisPlaneConstraint::FREE), will only be able to rotate around an axis + (AxisPlaneConstraint::AXIS), or will not able to rotate at all + (AxisPlaneConstraint::FORBIDDEN). + + Use Frame::setOrientation() to define the orientation of the constrained Frame + before it gets constrained. + + \attention An AxisPlaneConstraint::PLANE Type() is not meaningful for + rotational constraints and will be ignored. */ +CGAL_INLINE_FUNCTION +void AxisPlaneConstraint::setRotationConstraintType(Type type) { + if (rotationConstraintType() == AxisPlaneConstraint::PLANE) { + qWarning("AxisPlaneConstraint::setRotationConstraintType: the PLANE type " + "cannot be used for a rotation constraints"); + return; + } + + rotationConstraintType_ = type; +} + +//////////////////////////////////////////////////////////////////////////////// +// LocalConstraint // +//////////////////////////////////////////////////////////////////////////////// + +/*! Depending on translationConstraintType(), constrain \p translation to be + along an axis or limited to a plane defined in the Frame local coordinate + system by translationConstraintDirection(). */ +CGAL_INLINE_FUNCTION +void LocalConstraint::constrainTranslation(Vec &translation, + Frame *const frame) { + Vec proj; + switch (translationConstraintType()) { + case AxisPlaneConstraint::FREE: + break; + case AxisPlaneConstraint::PLANE: + proj = frame->rotation().rotate(translationConstraintDirection()); + translation.projectOnPlane(proj); + break; + case AxisPlaneConstraint::AXIS: + proj = frame->rotation().rotate(translationConstraintDirection()); + translation.projectOnAxis(proj); + break; + case AxisPlaneConstraint::FORBIDDEN: + translation = Vec(0.0, 0.0, 0.0); + break; + } +} + +/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p + rotation to be a rotation around an axis whose direction is defined in the + Frame local coordinate system by rotationConstraintDirection(). */ +CGAL_INLINE_FUNCTION +void LocalConstraint::constrainRotation(Quaternion &rotation, Frame *const) { + switch (rotationConstraintType()) { + case AxisPlaneConstraint::FREE: + break; + case AxisPlaneConstraint::PLANE: + break; + case AxisPlaneConstraint::AXIS: { + Vec axis = rotationConstraintDirection(); + Vec quat = Vec(rotation[0], rotation[1], rotation[2]); + quat.projectOnAxis(axis); + rotation = Quaternion(quat, 2.0 * acos(rotation[3])); + } break; + case AxisPlaneConstraint::FORBIDDEN: + rotation = Quaternion(); // identity + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// WorldConstraint // +//////////////////////////////////////////////////////////////////////////////// + +/*! Depending on translationConstraintType(), constrain \p translation to be + along an axis or limited to a plane defined in the world coordinate system by + translationConstraintDirection(). */ +CGAL_INLINE_FUNCTION +void WorldConstraint::constrainTranslation(Vec &translation, + Frame *const frame) { + Vec proj; + switch (translationConstraintType()) { + case AxisPlaneConstraint::FREE: + break; + case AxisPlaneConstraint::PLANE: + if (frame->referenceFrame()) { + proj = frame->referenceFrame()->transformOf( + translationConstraintDirection()); + translation.projectOnPlane(proj); + } else + translation.projectOnPlane(translationConstraintDirection()); + break; + case AxisPlaneConstraint::AXIS: + if (frame->referenceFrame()) { + proj = frame->referenceFrame()->transformOf( + translationConstraintDirection()); + translation.projectOnAxis(proj); + } else + translation.projectOnAxis(translationConstraintDirection()); + break; + case AxisPlaneConstraint::FORBIDDEN: + translation = Vec(0.0, 0.0, 0.0); + break; + } +} + +/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p + rotation to be a rotation around an axis whose direction is defined in the + world coordinate system by rotationConstraintDirection(). */ +CGAL_INLINE_FUNCTION +void WorldConstraint::constrainRotation(Quaternion &rotation, + Frame *const frame) { + switch (rotationConstraintType()) { + case AxisPlaneConstraint::FREE: + break; + case AxisPlaneConstraint::PLANE: + break; + case AxisPlaneConstraint::AXIS: { + Vec quat(rotation[0], rotation[1], rotation[2]); + Vec axis = frame->transformOf(rotationConstraintDirection()); + quat.projectOnAxis(axis); + rotation = Quaternion(quat, 2.0 * acos(rotation[3])); + break; + } + case AxisPlaneConstraint::FORBIDDEN: + rotation = Quaternion(); // identity + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// CameraConstraint // +//////////////////////////////////////////////////////////////////////////////// + +/*! Creates a CameraConstraint, whose constrained directions are defined in the + \p camera coordinate system. */ +CGAL_INLINE_FUNCTION +CameraConstraint::CameraConstraint(const Camera *const camera) + : AxisPlaneConstraint(), camera_(camera) {} + +/*! Depending on translationConstraintType(), constrain \p translation to be + along an axis or limited to a plane defined in the camera() coordinate system + by translationConstraintDirection(). */ +CGAL_INLINE_FUNCTION +void CameraConstraint::constrainTranslation(Vec &translation, + Frame *const frame) { + Vec proj; + switch (translationConstraintType()) { + case AxisPlaneConstraint::FREE: + break; + case AxisPlaneConstraint::PLANE: + proj = + camera()->frame()->inverseTransformOf(translationConstraintDirection()); + if (frame->referenceFrame()) + proj = frame->referenceFrame()->transformOf(proj); + translation.projectOnPlane(proj); + break; + case AxisPlaneConstraint::AXIS: + proj = + camera()->frame()->inverseTransformOf(translationConstraintDirection()); + if (frame->referenceFrame()) + proj = frame->referenceFrame()->transformOf(proj); + translation.projectOnAxis(proj); + break; + case AxisPlaneConstraint::FORBIDDEN: + translation = Vec(0.0, 0.0, 0.0); + break; + } +} + +/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p + rotation to be a rotation around an axis whose direction is defined in the + camera() coordinate system by rotationConstraintDirection(). */ +CGAL_INLINE_FUNCTION +void CameraConstraint::constrainRotation(Quaternion &rotation, + Frame *const frame) { + switch (rotationConstraintType()) { + case AxisPlaneConstraint::FREE: + break; + case AxisPlaneConstraint::PLANE: + break; + case AxisPlaneConstraint::AXIS: { + Vec axis = frame->transformOf( + camera()->frame()->inverseTransformOf(rotationConstraintDirection())); + Vec quat = Vec(rotation[0], rotation[1], rotation[2]); + quat.projectOnAxis(axis); + rotation = Quaternion(quat, 2.0 * acos(rotation[3])); + } break; + case AxisPlaneConstraint::FORBIDDEN: + rotation = Quaternion(); // identity + break; + } +} diff --git a/GraphicsView/include/CGAL/Qt/frame.h b/GraphicsView/include/CGAL/Qt/frame.h index c5c7edfba54..3e2b3503f2b 100644 --- a/GraphicsView/include/CGAL/Qt/frame.h +++ b/GraphicsView/include/CGAL/Qt/frame.h @@ -25,11 +25,13 @@ #include #include - -#include -// #include "GL/gl.h" is now included in config.h for ease of configuration +#include +#include +#include namespace qglviewer { +class Constraint; + /*! \brief The Frame class represents a coordinate system, defined by a position and an orientation. \class Frame frame.h QGLViewer/frame.h @@ -456,4 +458,8 @@ private: } // namespace qglviewer +#ifdef CGAL_HEADER_ONLY +//#include +#endif // CGAL_HEADER_ONLY + #endif // QGLVIEWER_FRAME_H diff --git a/GraphicsView/include/CGAL/Qt/frame_impl.h b/GraphicsView/include/CGAL/Qt/frame_impl.h new file mode 100644 index 00000000000..6cf125cdcb0 --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/frame_impl.h @@ -0,0 +1,1186 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include +#include +#include +#include + +using namespace qglviewer; +using namespace std; + +/*! Creates a default Frame. + + Its position() is (0,0,0) and it has an identity orientation() Quaternion. The + referenceFrame() and the constraint() are \c NULL. */ +CGAL_INLINE_FUNCTION +Frame::Frame() : constraint_(NULL), referenceFrame_(NULL) {} + +/*! Creates a Frame with a position() and an orientation(). + + See the Vec and Quaternion documentations for convenient constructors and + methods. + + The Frame is defined in the world coordinate system (its referenceFrame() is \c + NULL). It has a \c NULL associated constraint(). */ +CGAL_INLINE_FUNCTION +Frame::Frame(const Vec &position, const Quaternion &orientation) + : t_(position), q_(orientation), constraint_(NULL), referenceFrame_(NULL) {} + +/*! Equal operator. + + The referenceFrame() and constraint() pointers are copied. + + \attention Signal and slot connections are not copied. */ +CGAL_INLINE_FUNCTION +Frame &Frame::operator=(const Frame &frame) { + // Automatic compiler generated version would not emit the modified() signals + // as is done in setTranslationAndRotation. + setTranslationAndRotation(frame.translation(), frame.rotation()); + setConstraint(frame.constraint()); + setReferenceFrame(frame.referenceFrame()); + return *this; +} + +/*! Copy constructor. + + The translation() and rotation() as well as constraint() and referenceFrame() + pointers are copied. */ +CGAL_INLINE_FUNCTION +Frame::Frame(const Frame &frame) : QObject() { (*this) = frame; } + +/////////////////////////////// MATRICES ////////////////////////////////////// + +/*! Returns the 4x4 OpenGL transformation matrix represented by the Frame. + + This method should be used in conjunction with \c glMultMatrixd() to modify + the OpenGL modelview matrix from a Frame hierarchy. With this Frame hierarchy: + \code + Frame* body = new Frame(); + Frame* leftArm = new Frame(); + Frame* rightArm = new Frame(); + leftArm->setReferenceFrame(body); + rightArm->setReferenceFrame(body); + \endcode + + The associated OpenGL drawing code should look like: + \code + void Viewer::draw() + { + glPushMatrix(); + glMultMatrixd(body->matrix()); + drawBody(); + + glPushMatrix(); + glMultMatrixd(leftArm->matrix()); + drawArm(); + glPopMatrix(); + + glPushMatrix(); + glMultMatrixd(rightArm->matrix()); + drawArm(); + glPopMatrix(); + + glPopMatrix(); + } + \endcode + Note the use of nested \c glPushMatrix() and \c glPopMatrix() blocks to + represent the frame hierarchy: \c leftArm and \c rightArm are both correctly + drawn with respect to the \c body coordinate system. + + This matrix only represents the local Frame transformation (i.e. with respect + to the referenceFrame()). Use worldMatrix() to get the full Frame + transformation matrix (i.e. from the world to the Frame coordinate system). + These two match when the referenceFrame() is \c NULL. + + The result is only valid until the next call to matrix(), getMatrix(), + worldMatrix() or getWorldMatrix(). Use it immediately (as above) or use + getMatrix() instead. + + \attention The OpenGL format of the result is the transpose of the actual + mathematical European representation (translation is on the last \e line + instead of the last \e column). + + \note The scaling factor of the 4x4 matrix is 1.0. */ +CGAL_INLINE_FUNCTION +const GLdouble *Frame::matrix() const { + static GLdouble m[4][4]; + getMatrix(m); + return (const GLdouble *)(m); +} + +/*! \c GLdouble[4][4] version of matrix(). See also getWorldMatrix() and + * matrix(). */ +CGAL_INLINE_FUNCTION +void Frame::getMatrix(GLdouble m[4][4]) const { + q_.getMatrix(m); + + m[3][0] = t_[0]; + m[3][1] = t_[1]; + m[3][2] = t_[2]; +} + +/*! \c GLdouble[16] version of matrix(). See also getWorldMatrix() and matrix(). + */ +CGAL_INLINE_FUNCTION +void Frame::getMatrix(GLdouble m[16]) const { + q_.getMatrix(m); + + m[12] = t_[0]; + m[13] = t_[1]; + m[14] = t_[2]; +} + +/*! Returns a Frame representing the inverse of the Frame space transformation. + + The rotation() of the new Frame is the Quaternion::inverse() of the original + rotation. Its translation() is the negated inverse rotated image of the + original translation. + + If a Frame is considered as a space rigid transformation (translation and + rotation), the inverse() Frame performs the inverse transformation. + + Only the local Frame transformation (i.e. defined with respect to the + referenceFrame()) is inverted. Use worldInverse() for a global inverse. + + The resulting Frame has the same referenceFrame() as the Frame and a \c NULL + constraint(). + + \note The scaling factor of the 4x4 matrix is 1.0. */ +CGAL_INLINE_FUNCTION +Frame Frame::inverse() const { + Frame fr(-(q_.inverseRotate(t_)), q_.inverse()); + fr.setReferenceFrame(referenceFrame()); + return fr; +} + +/*! Returns the 4x4 OpenGL transformation matrix represented by the Frame. + + This method should be used in conjunction with \c glMultMatrixd() to modify + the OpenGL modelview matrix from a Frame: + \code + // The modelview here corresponds to the world coordinate system. + Frame fr(pos, Quaternion(from, to)); + glPushMatrix(); + glMultMatrixd(fr.worldMatrix()); + // draw object in the fr coordinate system. + glPopMatrix(); + \endcode + + This matrix represents the global Frame transformation: the entire + referenceFrame() hierarchy is taken into account to define the Frame + transformation from the world coordinate system. Use matrix() to get the local + Frame transformation matrix (i.e. defined with respect to the + referenceFrame()). These two match when the referenceFrame() is \c NULL. + + The OpenGL format of the result is the transpose of the actual mathematical + European representation (translation is on the last \e line instead of the + last \e column). + + \attention The result is only valid until the next call to matrix(), + getMatrix(), worldMatrix() or getWorldMatrix(). Use it immediately (as above) + or use getWorldMatrix() instead. + + \note The scaling factor of the 4x4 matrix is 1.0. */ +CGAL_INLINE_FUNCTION +const GLdouble *Frame::worldMatrix() const { + // This test is done for efficiency reasons (creates lots of temp objects + // otherwise). + if (referenceFrame()) { + static Frame fr; + fr.setTranslation(position()); + fr.setRotation(orientation()); + return fr.matrix(); + } else + return matrix(); +} + +/*! qreal[4][4] parameter version of worldMatrix(). See also getMatrix() and + * matrix(). */ +CGAL_INLINE_FUNCTION +void Frame::getWorldMatrix(GLdouble m[4][4]) const { + const GLdouble *mat = worldMatrix(); + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + m[i][j] = mat[i * 4 + j]; +} + +/*! qreal[16] parameter version of worldMatrix(). See also getMatrix() and + * matrix(). */ +CGAL_INLINE_FUNCTION +void Frame::getWorldMatrix(GLdouble m[16]) const { + const GLdouble *mat = worldMatrix(); + for (int i = 0; i < 16; ++i) + m[i] = mat[i]; +} + +/*! This is an overloaded method provided for convenience. Same as + * setFromMatrix(). */ +CGAL_INLINE_FUNCTION +void Frame::setFromMatrix(const GLdouble m[4][4]) { + if (fabs(m[3][3]) < 1E-8) { + qWarning("Frame::setFromMatrix: Null homogeneous coefficient"); + return; + } + + qreal rot[3][3]; + for (int i = 0; i < 3; ++i) { + t_[i] = m[3][i] / m[3][3]; + for (int j = 0; j < 3; ++j) + // Beware of the transposition (OpenGL to European math) + rot[i][j] = m[j][i] / m[3][3]; + } + q_.setFromRotationMatrix(rot); + Q_EMIT modified(); +} + +/*! Sets the Frame from an OpenGL matrix representation (rotation in the upper + left 3x3 matrix and translation on the last line). + + Hence, if a code fragment looks like: + \code + GLdouble m[16]={...}; + glMultMatrixd(m); + \endcode + It is equivalent to write: + \code + Frame fr; + fr.setFromMatrix(m); + glMultMatrixd(fr.matrix()); + \endcode + + Using this conversion, you can benefit from the powerful Frame transformation + methods to translate points and vectors to and from the Frame coordinate system + to any other Frame coordinate system (including the world coordinate system). + See coordinatesOf() and transformOf(). + + Emits the modified() signal. See also matrix(), getMatrix() and + Quaternion::setFromRotationMatrix(). + + \attention A Frame does not contain a scale factor. The possible scaling in \p + m will not be converted into the Frame by this method. */ +CGAL_INLINE_FUNCTION +void Frame::setFromMatrix(const GLdouble m[16]) { + GLdouble mat[4][4]; + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + mat[i][j] = m[i * 4 + j]; + setFromMatrix(mat); +} + +//////////////////// SET AND GET LOCAL TRANSLATION AND ROTATION +////////////////////////////////// + +/*! Same as setTranslation(), but with \p qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::setTranslation(qreal x, qreal y, qreal z) { + setTranslation(Vec(x, y, z)); +} + +/*! Fill \c x, \c y and \c z with the translation() of the Frame. */ +CGAL_INLINE_FUNCTION +void Frame::getTranslation(qreal &x, qreal &y, qreal &z) const { + const Vec t = translation(); + x = t[0]; + y = t[1]; + z = t[2]; +} + +/*! Same as setRotation() but with \c qreal Quaternion parameters. */ +CGAL_INLINE_FUNCTION +void Frame::setRotation(qreal q0, qreal q1, qreal q2, qreal q3) { + setRotation(Quaternion(q0, q1, q2, q3)); +} + +/*! The \p q are set to the rotation() of the Frame. + +See Quaternion::Quaternion(qreal, qreal, qreal, qreal) for details on \c q. */ +CGAL_INLINE_FUNCTION +void Frame::getRotation(qreal &q0, qreal &q1, qreal &q2, qreal &q3) const { + const Quaternion q = rotation(); + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; +} + +//////////////////////////////////////////////////////////////////////////////// + +/*! Translates the Frame of \p t (defined in the Frame coordinate system). + + The translation actually applied to the Frame may differ from \p t since it + can be filtered by the constraint(). Use translate(Vec&) or + setTranslationWithConstraint() to retrieve the filtered translation value. Use + setTranslation() to directly translate the Frame without taking the + constraint() into account. + + See also rotate(const Quaternion&). Emits the modified() signal. */ +CGAL_INLINE_FUNCTION +void Frame::translate(const Vec &t) { + Vec tbis = t; + translate(tbis); +} + +/*! Same as translate(const Vec&) but \p t may be modified to satisfy the + translation constraint(). Its new value corresponds to the translation that + has actually been applied to the Frame. */ +CGAL_INLINE_FUNCTION +void Frame::translate(Vec &t) { + if (constraint()) + constraint()->constrainTranslation(t, this); + t_ += t; + Q_EMIT modified(); +} + +/*! Same as translate(const Vec&) but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::translate(qreal x, qreal y, qreal z) { + Vec t(x, y, z); + translate(t); +} + +/*! Same as translate(Vec&) but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::translate(qreal &x, qreal &y, qreal &z) { + Vec t(x, y, z); + translate(t); + x = t[0]; + y = t[1]; + z = t[2]; +} + +/*! Rotates the Frame by \p q (defined in the Frame coordinate system): R = R*q. + + The rotation actually applied to the Frame may differ from \p q since it can + be filtered by the constraint(). Use rotate(Quaternion&) or + setRotationWithConstraint() to retrieve the filtered rotation value. Use + setRotation() to directly rotate the Frame without taking the constraint() + into account. + + See also translate(const Vec&). Emits the modified() signal. */ +CGAL_INLINE_FUNCTION +void Frame::rotate(const Quaternion &q) { + Quaternion qbis = q; + rotate(qbis); +} + +/*! Same as rotate(const Quaternion&) but \p q may be modified to satisfy the + rotation constraint(). Its new value corresponds to the rotation that has + actually been applied to the Frame. */ +CGAL_INLINE_FUNCTION +void Frame::rotate(Quaternion &q) { + if (constraint()) + constraint()->constrainRotation(q, this); + q_ *= q; + q_.normalize(); // Prevents numerical drift + Q_EMIT modified(); +} + +/*! Same as rotate(Quaternion&) but with \c qreal Quaternion parameters. */ +CGAL_INLINE_FUNCTION +void Frame::rotate(qreal &q0, qreal &q1, qreal &q2, qreal &q3) { + Quaternion q(q0, q1, q2, q3); + rotate(q); + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; +} + +/*! Same as rotate(const Quaternion&) but with \c qreal Quaternion parameters. + */ +CGAL_INLINE_FUNCTION +void Frame::rotate(qreal q0, qreal q1, qreal q2, qreal q3) { + Quaternion q(q0, q1, q2, q3); + rotate(q); +} + +/*! Makes the Frame rotate() by \p rotation around \p point. + + \p point is defined in the world coordinate system, while the \p rotation axis + is defined in the Frame coordinate system. + + If the Frame has a constraint(), \p rotation is first constrained using + Constraint::constrainRotation(). The translation which results from the + filtered rotation around \p point is then computed and filtered using + Constraint::constrainTranslation(). The new \p rotation value corresponds to + the rotation that has actually been applied to the Frame. + + Emits the modified() signal. */ +CGAL_INLINE_FUNCTION +void Frame::rotateAroundPoint(Quaternion &rotation, const Vec &point) { + if (constraint()) + constraint()->constrainRotation(rotation, this); + q_ *= rotation; + q_.normalize(); // Prevents numerical drift + Vec trans = point + + Quaternion(inverseTransformOf(rotation.axis()), rotation.angle()) + .rotate(position() - point) - + t_; + if (constraint()) + constraint()->constrainTranslation(trans, this); + t_ += trans; + Q_EMIT modified(); +} + +/*! Same as rotateAroundPoint(), but with a \c const \p rotation Quaternion. + Note that the actual rotation may differ since it can be filtered by the + constraint(). */ +CGAL_INLINE_FUNCTION +void Frame::rotateAroundPoint(const Quaternion &rotation, const Vec &point) { + Quaternion rot = rotation; + rotateAroundPoint(rot, point); +} + +//////////////////// SET AND GET WORLD POSITION AND ORIENTATION +////////////////////////////////// + +/*! Sets the position() of the Frame, defined in the world coordinate system. +Emits the modified() signal. + +Use setTranslation() to define the \e local frame translation (with respect to +the referenceFrame()). The potential constraint() of the Frame is not taken into +account, use setPositionWithConstraint() instead. */ +CGAL_INLINE_FUNCTION +void Frame::setPosition(const Vec &position) { + if (referenceFrame()) + setTranslation(referenceFrame()->coordinatesOf(position)); + else + setTranslation(position); +} + +/*! Same as setPosition(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::setPosition(qreal x, qreal y, qreal z) { + setPosition(Vec(x, y, z)); +} + +/*! Same as successive calls to setPosition() and then setOrientation(). + +Only one modified() signal is emitted, which is convenient if this signal is +connected to a QGLViewer::update() slot. See also setTranslationAndRotation() +and setPositionAndOrientationWithConstraint(). */ +CGAL_INLINE_FUNCTION +void Frame::setPositionAndOrientation(const Vec &position, + const Quaternion &orientation) { + if (referenceFrame()) { + t_ = referenceFrame()->coordinatesOf(position); + q_ = referenceFrame()->orientation().inverse() * orientation; + } else { + t_ = position; + q_ = orientation; + } + Q_EMIT modified(); +} + +/*! Same as successive calls to setTranslation() and then setRotation(). + +Only one modified() signal is emitted, which is convenient if this signal is +connected to a QGLViewer::update() slot. See also setPositionAndOrientation() +and setTranslationAndRotationWithConstraint(). */ +CGAL_INLINE_FUNCTION +void Frame::setTranslationAndRotation(const Vec &translation, + const Quaternion &rotation) { + t_ = translation; + q_ = rotation; + Q_EMIT modified(); +} + +/*! \p x, \p y and \p z are set to the position() of the Frame. */ +CGAL_INLINE_FUNCTION +void Frame::getPosition(qreal &x, qreal &y, qreal &z) const { + Vec p = position(); + x = p.x; + y = p.y; + z = p.z; +} + +/*! Sets the orientation() of the Frame, defined in the world coordinate system. +Emits the modified() signal. + +Use setRotation() to define the \e local frame rotation (with respect to the +referenceFrame()). The potential constraint() of the Frame is not taken into +account, use setOrientationWithConstraint() instead. */ +CGAL_INLINE_FUNCTION +void Frame::setOrientation(const Quaternion &orientation) { + if (referenceFrame()) + setRotation(referenceFrame()->orientation().inverse() * orientation); + else + setRotation(orientation); +} + +/*! Same as setOrientation(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::setOrientation(qreal q0, qreal q1, qreal q2, qreal q3) { + setOrientation(Quaternion(q0, q1, q2, q3)); +} + +/*! Get the current orientation of the frame (same as orientation()). + Parameters are the orientation Quaternion values. + See also setOrientation(). */ + +/*! The \p q are set to the orientation() of the Frame. + +See Quaternion::Quaternion(qreal, qreal, qreal, qreal) for details on \c q. */ +CGAL_INLINE_FUNCTION +void Frame::getOrientation(qreal &q0, qreal &q1, qreal &q2, qreal &q3) const { + Quaternion o = orientation(); + q0 = o[0]; + q1 = o[1]; + q2 = o[2]; + q3 = o[3]; +} + +/*! Returns the position of the Frame, defined in the world coordinate system. + See also orientation(), setPosition() and translation(). */ +CGAL_INLINE_FUNCTION +Vec Frame::position() const { + if (referenceFrame_) + return inverseCoordinatesOf(Vec(0.0, 0.0, 0.0)); + else + return t_; +} + +/*! Returns the orientation of the Frame, defined in the world coordinate + system. See also position(), setOrientation() and rotation(). */ +CGAL_INLINE_FUNCTION +Quaternion Frame::orientation() const { + Quaternion res = rotation(); + const Frame *fr = referenceFrame(); + while (fr != NULL) { + res = fr->rotation() * res; + fr = fr->referenceFrame(); + } + return res; +} + +////////////////////// C o n s t r a i n t V e r s i o n s +///////////////////////////// + +/*! Same as setTranslation(), but \p translation is modified so that the + potential constraint() of the Frame is satisfied. + + Emits the modified() signal. See also setRotationWithConstraint() and + setPositionWithConstraint(). */ +CGAL_INLINE_FUNCTION +void Frame::setTranslationWithConstraint(Vec &translation) { + Vec deltaT = translation - this->translation(); + if (constraint()) + constraint()->constrainTranslation(deltaT, this); + + setTranslation(this->translation() + deltaT); + translation = this->translation(); +} + +/*! Same as setRotation(), but \p rotation is modified so that the potential + constraint() of the Frame is satisfied. + + Emits the modified() signal. See also setTranslationWithConstraint() and + setOrientationWithConstraint(). */ +CGAL_INLINE_FUNCTION +void Frame::setRotationWithConstraint(Quaternion &rotation) { + Quaternion deltaQ = this->rotation().inverse() * rotation; + if (constraint()) + constraint()->constrainRotation(deltaQ, this); + + // Prevent numerical drift + deltaQ.normalize(); + + setRotation(this->rotation() * deltaQ); + q_.normalize(); + rotation = this->rotation(); +} + +/*! Same as setTranslationAndRotation(), but \p translation and \p orientation + are modified to satisfy the constraint(). Emits the modified() signal. */ +CGAL_INLINE_FUNCTION +void Frame::setTranslationAndRotationWithConstraint(Vec &translation, + Quaternion &rotation) { + Vec deltaT = translation - this->translation(); + Quaternion deltaQ = this->rotation().inverse() * rotation; + + if (constraint()) { + constraint()->constrainTranslation(deltaT, this); + constraint()->constrainRotation(deltaQ, this); + } + + // Prevent numerical drift + deltaQ.normalize(); + + t_ += deltaT; + q_ *= deltaQ; + q_.normalize(); + + translation = this->translation(); + rotation = this->rotation(); + + Q_EMIT modified(); +} + +/*! Same as setPosition(), but \p position is modified so that the potential + constraint() of the Frame is satisfied. See also + setOrientationWithConstraint() and setTranslationWithConstraint(). */ +CGAL_INLINE_FUNCTION +void Frame::setPositionWithConstraint(Vec &position) { + if (referenceFrame()) + position = referenceFrame()->coordinatesOf(position); + + setTranslationWithConstraint(position); +} + +/*! Same as setOrientation(), but \p orientation is modified so that the + potential constraint() of the Frame is satisfied. See also + setPositionWithConstraint() and setRotationWithConstraint(). */ +CGAL_INLINE_FUNCTION +void Frame::setOrientationWithConstraint(Quaternion &orientation) { + if (referenceFrame()) + orientation = referenceFrame()->orientation().inverse() * orientation; + + setRotationWithConstraint(orientation); +} + +/*! Same as setPositionAndOrientation() but \p position and \p orientation are +modified to satisfy the constraint. Emits the modified() signal. */ +CGAL_INLINE_FUNCTION +void Frame::setPositionAndOrientationWithConstraint(Vec &position, + Quaternion &orientation) { + if (referenceFrame()) { + position = referenceFrame()->coordinatesOf(position); + orientation = referenceFrame()->orientation().inverse() * orientation; + } + setTranslationAndRotationWithConstraint(position, orientation); +} + +///////////////////////////// REFERENCE FRAMES +////////////////////////////////////////// + +/*! Sets the referenceFrame() of the Frame. + +The Frame translation() and rotation() are then defined in the referenceFrame() +coordinate system. Use position() and orientation() to express these in the +world coordinate system. + +Emits the modified() signal if \p refFrame differs from the current +referenceFrame(). + +Using this method, you can create a hierarchy of Frames. This hierarchy needs to +be a tree, which root is the world coordinate system (i.e. a \c NULL +referenceFrame()). A warning is printed and no action is performed if setting \p +refFrame as the referenceFrame() would create a loop in the Frame hierarchy (see +settingAsReferenceFrameWillCreateALoop()). */ +CGAL_INLINE_FUNCTION +void Frame::setReferenceFrame(const Frame *const refFrame) { + if (settingAsReferenceFrameWillCreateALoop(refFrame)) + qWarning("Frame::setReferenceFrame would create a loop in Frame hierarchy"); + else { + bool identical = (referenceFrame_ == refFrame); + referenceFrame_ = refFrame; + if (!identical) + Q_EMIT modified(); + } +} + +/*! Returns \c true if setting \p frame as the Frame's referenceFrame() would + create a loop in the Frame hierarchy. */ +CGAL_INLINE_FUNCTION +bool Frame::settingAsReferenceFrameWillCreateALoop(const Frame *const frame) { + const Frame *f = frame; + while (f != NULL) { + if (f == this) + return true; + f = f->referenceFrame(); + } + return false; +} + +///////////////////////// FRAME TRANSFORMATIONS OF 3D POINTS +///////////////////////////////// + +/*! Returns the Frame coordinates of a point \p src defined in the world + coordinate system (converts from world to Frame). + + inverseCoordinatesOf() performs the inverse convertion. transformOf() converts + 3D vectors instead of 3D coordinates. + + See the frameTransform example + for an illustration. */ +CGAL_INLINE_FUNCTION +Vec Frame::coordinatesOf(const Vec &src) const { + if (referenceFrame()) + return localCoordinatesOf(referenceFrame()->coordinatesOf(src)); + else + return localCoordinatesOf(src); +} + +/*! Returns the world coordinates of the point whose position in the Frame + coordinate system is \p src (converts from Frame to world). + + coordinatesOf() performs the inverse convertion. Use inverseTransformOf() to + transform 3D vectors instead of 3D coordinates. */ +CGAL_INLINE_FUNCTION +Vec Frame::inverseCoordinatesOf(const Vec &src) const { + const Frame *fr = this; + Vec res = src; + while (fr != NULL) { + res = fr->localInverseCoordinatesOf(res); + fr = fr->referenceFrame(); + } + return res; +} + +/*! Returns the Frame coordinates of a point \p src defined in the + referenceFrame() coordinate system (converts from referenceFrame() to Frame). + + localInverseCoordinatesOf() performs the inverse convertion. See also + localTransformOf(). */ +CGAL_INLINE_FUNCTION +Vec Frame::localCoordinatesOf(const Vec &src) const { + return rotation().inverseRotate(src - translation()); +} + +/*! Returns the referenceFrame() coordinates of a point \p src defined in the + Frame coordinate system (converts from Frame to referenceFrame()). + + localCoordinatesOf() performs the inverse convertion. See also + localInverseTransformOf(). */ +CGAL_INLINE_FUNCTION +Vec Frame::localInverseCoordinatesOf(const Vec &src) const { + return rotation().rotate(src) + translation(); +} + +/*! Returns the Frame coordinates of the point whose position in the \p from + coordinate system is \p src (converts from \p from to Frame). + + coordinatesOfIn() performs the inverse transformation. */ +CGAL_INLINE_FUNCTION +Vec Frame::coordinatesOfFrom(const Vec &src, const Frame *const from) const { + if (this == from) + return src; + else if (referenceFrame()) + return localCoordinatesOf(referenceFrame()->coordinatesOfFrom(src, from)); + else + return localCoordinatesOf(from->inverseCoordinatesOf(src)); +} + +/*! Returns the \p in coordinates of the point whose position in the Frame + coordinate system is \p src (converts from Frame to \p in). + + coordinatesOfFrom() performs the inverse transformation. */ +CGAL_INLINE_FUNCTION +Vec Frame::coordinatesOfIn(const Vec &src, const Frame *const in) const { + const Frame *fr = this; + Vec res = src; + while ((fr != NULL) && (fr != in)) { + res = fr->localInverseCoordinatesOf(res); + fr = fr->referenceFrame(); + } + + if (fr != in) + // in was not found in the branch of this, res is now expressed in the world + // coordinate system. Simply convert to in coordinate system. + res = in->coordinatesOf(res); + + return res; +} + +////// qreal[3] versions + +/*! Same as coordinatesOf(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getCoordinatesOf(const qreal src[3], qreal res[3]) const { + const Vec r = coordinatesOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as inverseCoordinatesOf(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getInverseCoordinatesOf(const qreal src[3], qreal res[3]) const { + const Vec r = inverseCoordinatesOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as localCoordinatesOf(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getLocalCoordinatesOf(const qreal src[3], qreal res[3]) const { + const Vec r = localCoordinatesOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as localInverseCoordinatesOf(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getLocalInverseCoordinatesOf(const qreal src[3], + qreal res[3]) const { + const Vec r = localInverseCoordinatesOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as coordinatesOfIn(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getCoordinatesOfIn(const qreal src[3], qreal res[3], + const Frame *const in) const { + const Vec r = coordinatesOfIn(Vec(src), in); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as coordinatesOfFrom(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getCoordinatesOfFrom(const qreal src[3], qreal res[3], + const Frame *const from) const { + const Vec r = coordinatesOfFrom(Vec(src), from); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +///////////////////////// FRAME TRANSFORMATIONS OF VECTORS +///////////////////////////////// + +/*! Returns the Frame transform of a vector \p src defined in the world + coordinate system (converts vectors from world to Frame). + + inverseTransformOf() performs the inverse transformation. coordinatesOf() + converts 3D coordinates instead of 3D vectors (here only the rotational part of + the transformation is taken into account). + + See the frameTransform example + for an illustration. */ +CGAL_INLINE_FUNCTION +Vec Frame::transformOf(const Vec &src) const { + if (referenceFrame()) + return localTransformOf(referenceFrame()->transformOf(src)); + else + return localTransformOf(src); +} + +/*! Returns the world transform of the vector whose coordinates in the Frame + coordinate system is \p src (converts vectors from Frame to world). + + transformOf() performs the inverse transformation. Use inverseCoordinatesOf() + to transform 3D coordinates instead of 3D vectors. */ +CGAL_INLINE_FUNCTION +Vec Frame::inverseTransformOf(const Vec &src) const { + const Frame *fr = this; + Vec res = src; + while (fr != NULL) { + res = fr->localInverseTransformOf(res); + fr = fr->referenceFrame(); + } + return res; +} + +/*! Returns the Frame transform of a vector \p src defined in the + referenceFrame() coordinate system (converts vectors from referenceFrame() to + Frame). + + localInverseTransformOf() performs the inverse transformation. See also + localCoordinatesOf(). */ +CGAL_INLINE_FUNCTION +Vec Frame::localTransformOf(const Vec &src) const { + return rotation().inverseRotate(src); +} + +/*! Returns the referenceFrame() transform of a vector \p src defined in the + Frame coordinate system (converts vectors from Frame to referenceFrame()). + + localTransformOf() performs the inverse transformation. See also + localInverseCoordinatesOf(). */ +CGAL_INLINE_FUNCTION +Vec Frame::localInverseTransformOf(const Vec &src) const { + return rotation().rotate(src); +} + +/*! Returns the Frame transform of the vector whose coordinates in the \p from + coordinate system is \p src (converts vectors from \p from to Frame). + + transformOfIn() performs the inverse transformation. */ +CGAL_INLINE_FUNCTION +Vec Frame::transformOfFrom(const Vec &src, const Frame *const from) const { + if (this == from) + return src; + else if (referenceFrame()) + return localTransformOf(referenceFrame()->transformOfFrom(src, from)); + else + return localTransformOf(from->inverseTransformOf(src)); +} + +/*! Returns the \p in transform of the vector whose coordinates in the Frame + coordinate system is \p src (converts vectors from Frame to \p in). + + transformOfFrom() performs the inverse transformation. */ +CGAL_INLINE_FUNCTION +Vec Frame::transformOfIn(const Vec &src, const Frame *const in) const { + const Frame *fr = this; + Vec res = src; + while ((fr != NULL) && (fr != in)) { + res = fr->localInverseTransformOf(res); + fr = fr->referenceFrame(); + } + + if (fr != in) + // in was not found in the branch of this, res is now expressed in the world + // coordinate system. Simply convert to in coordinate system. + res = in->transformOf(res); + + return res; +} + +///////////////// qreal[3] versions ////////////////////// + +/*! Same as transformOf(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getTransformOf(const qreal src[3], qreal res[3]) const { + Vec r = transformOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as inverseTransformOf(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getInverseTransformOf(const qreal src[3], qreal res[3]) const { + Vec r = inverseTransformOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as localTransformOf(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getLocalTransformOf(const qreal src[3], qreal res[3]) const { + Vec r = localTransformOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as localInverseTransformOf(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getLocalInverseTransformOf(const qreal src[3], qreal res[3]) const { + Vec r = localInverseTransformOf(Vec(src)); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as transformOfIn(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getTransformOfIn(const qreal src[3], qreal res[3], + const Frame *const in) const { + Vec r = transformOfIn(Vec(src), in); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +/*! Same as transformOfFrom(), but with \c qreal parameters. */ +CGAL_INLINE_FUNCTION +void Frame::getTransformOfFrom(const qreal src[3], qreal res[3], + const Frame *const from) const { + Vec r = transformOfFrom(Vec(src), from); + for (int i = 0; i < 3; ++i) + res[i] = r[i]; +} + +//////////////////////////// STATE ////////////////////////////// + +/*! Returns an XML \c QDomElement that represents the Frame. + + \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument + factory used to create QDomElement. + + The resulting QDomElement looks like: + \code + + + + + \endcode + + Use initFromDOMElement() to restore the Frame state from the resulting \c + QDomElement. + + + See Vec::domElement() for a complete example. See also + + Quaternion::domElement(), Camera::domElement()... + + \attention The constraint() and referenceFrame() are not saved in the + QDomElement. */ +CGAL_INLINE_FUNCTION +QDomElement Frame::domElement(const QString &name, + QDomDocument &document) const { + // TODO: use translation and rotation instead when referenceFrame is coded... + QDomElement e = document.createElement(name); + e.appendChild(position().domElement("position", document)); + e.appendChild(orientation().domElement("orientation", document)); + return e; +} + +/*! Restores the Frame state from a \c QDomElement created by domElement(). + + See domElement() for the \c QDomElement syntax. See the + Vec::initFromDOMElement() and Quaternion::initFromDOMElement() documentations + for details on default values if an argument is missing. + + \attention The constraint() and referenceFrame() are not restored by this + method and are left unchanged. */ +CGAL_INLINE_FUNCTION +void Frame::initFromDOMElement(const QDomElement &element) { + // TODO: use translation and rotation instead when referenceFrame is coded... + + // Reset default values. Attention: destroys constraint. + // *this = Frame(); + // This instead ? Better : what is not set is not changed. + // setPositionAndOrientation(Vec(), Quaternion()); + + QDomElement child = element.firstChild().toElement(); + while (!child.isNull()) { + if (child.tagName() == "position") + setPosition(Vec(child)); + if (child.tagName() == "orientation") + setOrientation(Quaternion(child).normalized()); + + child = child.nextSibling().toElement(); + } +} + +///////////////////////////////// ALIGN ///////////////////////////////// + +/*! Aligns the Frame with \p frame, so that two of their axis are parallel. + +If one of the X, Y and Z axis of the Frame is almost parallel to any of the X, +Y, or Z axis of \p frame, the Frame is rotated so that these two axis actually +become parallel. + +If, after this first rotation, two other axis are also almost parallel, a second +alignment is performed. The two frames then have identical orientations, up to +90 degrees rotations. + +\p threshold measures how close two axis must be to be considered parallel. It +is compared with the absolute values of the dot product of the normalized axis. +As a result, useful range is sqrt(2)/2 (systematic alignment) to 1 (no +alignment). + +When \p move is set to \c true, the Frame position() is also affected by the +alignment. The new Frame's position() is such that the \p frame position +(computed with coordinatesOf(), in the Frame coordinates system) does not +change. + +\p frame may be \c NULL and then represents the world coordinate system (same +convention than for the referenceFrame()). + +The rotation (and translation when \p move is \c true) applied to the Frame are +filtered by the possible constraint(). */ +CGAL_INLINE_FUNCTION +void Frame::alignWithFrame(const Frame *const frame, bool move, + qreal threshold) { + Vec directions[2][3]; + for (unsigned short d = 0; d < 3; ++d) { + Vec dir((d == 0) ? 1.0 : 0.0, (d == 1) ? 1.0 : 0.0, (d == 2) ? 1.0 : 0.0); + if (frame) + directions[0][d] = frame->inverseTransformOf(dir); + else + directions[0][d] = dir; + directions[1][d] = inverseTransformOf(dir); + } + + qreal maxProj = 0.0; + qreal proj; + unsigned short index[2]; + index[0] = index[1] = 0; + for (unsigned short i = 0; i < 3; ++i) + for (unsigned short j = 0; j < 3; ++j) + if ((proj = fabs(directions[0][i] * directions[1][j])) >= maxProj) { + index[0] = i; + index[1] = j; + maxProj = proj; + } + + Frame old; + old = *this; + + qreal coef = directions[0][index[0]] * directions[1][index[1]]; + if (fabs(coef) >= threshold) { + const Vec axis = cross(directions[0][index[0]], directions[1][index[1]]); + qreal angle = asin(axis.norm()); + if (coef >= 0.0) + angle = -angle; + rotate(rotation().inverse() * Quaternion(axis, angle) * orientation()); + + // Try to align an other axis direction + unsigned short d = (index[1] + 1) % 3; + Vec dir((d == 0) ? 1.0 : 0.0, (d == 1) ? 1.0 : 0.0, (d == 2) ? 1.0 : 0.0); + dir = inverseTransformOf(dir); + + qreal max = 0.0; + for (unsigned short i = 0; i < 3; ++i) { + qreal proj = fabs(directions[0][i] * dir); + if (proj > max) { + index[0] = i; + max = proj; + } + } + + if (max >= threshold) { + const Vec axis = cross(directions[0][index[0]], dir); + qreal angle = asin(axis.norm()); + if (directions[0][index[0]] * dir >= 0.0) + angle = -angle; + rotate(rotation().inverse() * Quaternion(axis, angle) * orientation()); + } + } + + if (move) { + Vec center; + if (frame) + center = frame->position(); + + translate(center - orientation().rotate(old.coordinatesOf(center)) - + translation()); + } +} + +/*! Translates the Frame so that its position() lies on the line defined by \p +origin and \p direction (defined in the world coordinate system). + +Simply uses an orthogonal projection. \p direction does not need to be +normalized. */ +CGAL_INLINE_FUNCTION +void Frame::projectOnLine(const Vec &origin, const Vec &direction) { + // If you are trying to find a bug here, because of memory problems, you waste + // your time. This is a bug in the gcc 3.3 compiler. Compile the library in + // debug mode and test. Uncommenting this line also seems to solve the + // problem. Horrible. cout << "position = " << position() << endl; If you + // found a problem or are using a different compiler, please let me know. + const Vec shift = origin - position(); + Vec proj = shift; + proj.projectOnAxis(direction); + translate(shift - proj); +} diff --git a/GraphicsView/include/CGAL/Qt/keyFrameInterpolator.h b/GraphicsView/include/CGAL/Qt/keyFrameInterpolator.h index db8f4ed2b1d..56f1c6a201f 100644 --- a/GraphicsView/include/CGAL/Qt/keyFrameInterpolator.h +++ b/GraphicsView/include/CGAL/Qt/keyFrameInterpolator.h @@ -28,14 +28,13 @@ #include // Not actually needed, but some bad compilers (Microsoft VS6) complain. -#include +//#include // If you compiler complains about incomplete type, uncomment the next line // #include "frame.h" // and comment "class Frame;" 3 lines below namespace qglviewer { -class Camera; class Frame; /*! \brief A keyFrame Catmull-Rom Frame interpolator. \class KeyFrameInterpolator keyFrameInterpolator.h @@ -292,12 +291,6 @@ public Q_SLOTS: virtual void interpolateAtTime(qreal time); //@} - /*! @name Path drawing */ - //@{ -public: - virtual void drawPath(int mask = 1, int nbFrames = 6, qreal scale = 1.0); - //@} - /*! @name XML representation */ //@{ public: diff --git a/GraphicsView/include/CGAL/Qt/keyFrameInterpolator_impl.h b/GraphicsView/include/CGAL/Qt/keyFrameInterpolator_impl.h new file mode 100644 index 00000000000..ca8c2886578 --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/keyFrameInterpolator_impl.h @@ -0,0 +1,587 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include +#include + +using namespace qglviewer; +using namespace std; + +/*! Creates a KeyFrameInterpolator, with \p frame as associated frame(). + + The frame() can be set or changed using setFrame(). + + interpolationTime(), interpolationSpeed() and interpolationPeriod() are set to + their default values. */ +CGAL_INLINE_FUNCTION +KeyFrameInterpolator::KeyFrameInterpolator(Frame *frame) + : frame_(NULL), period_(40), interpolationTime_(0.0), + interpolationSpeed_(1.0), interpolationStarted_(false), + closedPath_(false), loopInterpolation_(false), pathIsValid_(false), + valuesAreValid_(true), currentFrameValid_(false) +// #CONNECTION# Values cut pasted initFromDOMElement() +{ + setFrame(frame); + for (int i = 0; i < 4; ++i) + currentFrame_[i] = new QMutableListIterator(keyFrame_); + connect(&timer_, SIGNAL(timeout()), SLOT(update())); +} + +/*! Virtual destructor. Clears the keyFrame path. */ +CGAL_INLINE_FUNCTION +KeyFrameInterpolator::~KeyFrameInterpolator() { + deletePath(); + for (int i = 0; i < 4; ++i) + delete currentFrame_[i]; +} + +/*! Sets the frame() associated to the KeyFrameInterpolator. */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::setFrame(Frame *const frame) { + if (this->frame()) + disconnect(this, SIGNAL(interpolated()), this->frame(), + SIGNAL(interpolated())); + + frame_ = frame; + + if (this->frame()) + connect(this, SIGNAL(interpolated()), this->frame(), + SIGNAL(interpolated())); +} + +/*! Updates frame() state according to current interpolationTime(). Then adds + interpolationPeriod()*interpolationSpeed() to interpolationTime(). + + This internal method is called by a timer when interpolationIsStarted(). It + can be used for debugging purpose. stopInterpolation() is called when + interpolationTime() reaches firstTime() or lastTime(), unless + loopInterpolation() is \c true. */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::update() { + interpolateAtTime(interpolationTime()); + + interpolationTime_ += interpolationSpeed() * interpolationPeriod() / 1000.0; + + if (interpolationTime() > keyFrame_.last()->time()) { + if (loopInterpolation()) + setInterpolationTime(keyFrame_.first()->time() + interpolationTime_ - + keyFrame_.last()->time()); + else { + // Make sure last KeyFrame is reached and displayed + interpolateAtTime(keyFrame_.last()->time()); + stopInterpolation(); + } + Q_EMIT endReached(); + } else if (interpolationTime() < keyFrame_.first()->time()) { + if (loopInterpolation()) + setInterpolationTime(keyFrame_.last()->time() - + keyFrame_.first()->time() + interpolationTime_); + else { + // Make sure first KeyFrame is reached and displayed + interpolateAtTime(keyFrame_.first()->time()); + stopInterpolation(); + } + Q_EMIT endReached(); + } +} + +/*! Starts the interpolation process. + + A timer is started with an interpolationPeriod() period that updates the + frame()'s position and orientation. interpolationIsStarted() will return \c + true until stopInterpolation() or toggleInterpolation() is called. + + If \p period is positive, it is set as the new interpolationPeriod(). The + previous interpolationPeriod() is used otherwise (default). + + If interpolationTime() is larger than lastTime(), interpolationTime() is reset + to firstTime() before interpolation starts (and inversely for negative + interpolationSpeed()). + + Use setInterpolationTime() before calling this method to change the starting + interpolationTime(). + + See the keyFrames example for an + illustration. + + You may also be interested in QGLViewer::animate() and + QGLViewer::startAnimation(). + + \attention The keyFrames must be defined (see addKeyFrame()) \e before you + startInterpolation(), or else the interpolation will naturally immediately + stop. */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::startInterpolation(int period) { + if (period >= 0) + setInterpolationPeriod(period); + + if (!keyFrame_.isEmpty()) { + if ((interpolationSpeed() > 0.0) && + (interpolationTime() >= keyFrame_.last()->time())) + setInterpolationTime(keyFrame_.first()->time()); + if ((interpolationSpeed() < 0.0) && + (interpolationTime() <= keyFrame_.first()->time())) + setInterpolationTime(keyFrame_.last()->time()); + timer_.start(interpolationPeriod()); + interpolationStarted_ = true; + update(); + } +} + +/*! Stops an interpolation started with startInterpolation(). See + * interpolationIsStarted() and toggleInterpolation(). */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::stopInterpolation() { + timer_.stop(); + interpolationStarted_ = false; +} + +/*! Stops the interpolation and resets interpolationTime() to the firstTime(). + +If desired, call interpolateAtTime() after this method to actually move the +frame() to firstTime(). */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::resetInterpolation() { + stopInterpolation(); + setInterpolationTime(firstTime()); +} + +/*! Appends a new keyFrame to the path, with its associated \p time (in + seconds). + + The keyFrame is given as a pointer to a Frame, which will be connected to the + KeyFrameInterpolator: when \p frame is modified, the KeyFrameInterpolator path + is updated accordingly. This allows for dynamic paths, where keyFrame can be + edited, even during the interpolation. See the keyFrames example for an illustration. + + \c NULL \p frame pointers are silently ignored. The keyFrameTime() has to be + monotonously increasing over keyFrames. + + Use addKeyFrame(const Frame&, qreal) to add keyFrame by values. */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::addKeyFrame(const Frame *const frame, qreal time) { + if (!frame) + return; + + if (keyFrame_.isEmpty()) + interpolationTime_ = time; + + if ((!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time)) + qWarning( + "Error in KeyFrameInterpolator::addKeyFrame: time is not monotone"); + else + keyFrame_.append(new KeyFrame(frame, time)); + connect(frame, SIGNAL(modified()), SLOT(invalidateValues())); + valuesAreValid_ = false; + pathIsValid_ = false; + currentFrameValid_ = false; + resetInterpolation(); +} + +/*! Appends a new keyFrame to the path, with its associated \p time (in + seconds). + + The path will use the current \p frame state. If you want the path to change + when \p frame is modified, you need to pass a \e pointer to the Frame instead + (see addKeyFrame(const Frame*, qreal)). + + The keyFrameTime() have to be monotonously increasing over keyFrames. */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::addKeyFrame(const Frame &frame, qreal time) { + if (keyFrame_.isEmpty()) + interpolationTime_ = time; + + if ((!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time)) + qWarning( + "Error in KeyFrameInterpolator::addKeyFrame: time is not monotone"); + else + keyFrame_.append(new KeyFrame(frame, time)); + + valuesAreValid_ = false; + pathIsValid_ = false; + currentFrameValid_ = false; + resetInterpolation(); +} + +/*! Appends a new keyFrame to the path. + + Same as addKeyFrame(const Frame* frame, qreal), except that the keyFrameTime() + is set to the previous keyFrameTime() plus one second (or 0.0 if there is no + previous keyFrame). */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::addKeyFrame(const Frame *const frame) { + qreal time; + if (keyFrame_.isEmpty()) + time = 0.0; + else + time = lastTime() + 1.0; + + addKeyFrame(frame, time); +} + +/*! Appends a new keyFrame to the path. + + Same as addKeyFrame(const Frame& frame, qreal), except that the keyFrameTime() + is automatically set to previous keyFrameTime() plus one second (or 0.0 if + there is no previous keyFrame). */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::addKeyFrame(const Frame &frame) { + qreal time; + if (keyFrame_.isEmpty()) + time = 0.0; + else + time = keyFrame_.last()->time() + 1.0; + + addKeyFrame(frame, time); +} + +/*! Removes all keyFrames from the path. The numberOfKeyFrames() is set to 0. */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::deletePath() { + stopInterpolation(); + qDeleteAll(keyFrame_); + keyFrame_.clear(); + pathIsValid_ = false; + valuesAreValid_ = false; + currentFrameValid_ = false; +} + + +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::updateModifiedFrameValues() { + Quaternion prevQ = keyFrame_.first()->orientation(); + KeyFrame *kf; + for (int i = 0; i < keyFrame_.size(); ++i) { + kf = keyFrame_.at(i); + if (kf->frame()) + kf->updateValuesFromPointer(); + kf->flipOrientationIfNeeded(prevQ); + prevQ = kf->orientation(); + } + + KeyFrame *prev = keyFrame_.first(); + kf = keyFrame_.first(); + int index = 1; + while (kf) { + KeyFrame *next = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL; + index++; + if (next) + kf->computeTangent(prev, next); + else + kf->computeTangent(prev, kf); + prev = kf; + kf = next; + } + valuesAreValid_ = true; +} + +/*! Returns the Frame associated with the keyFrame at index \p index. + + See also keyFrameTime(). \p index has to be in the range + 0..numberOfKeyFrames()-1. + + \note If this keyFrame was defined using a pointer to a Frame (see + addKeyFrame(const Frame* const)), the \e current pointed Frame state is + returned. */ +CGAL_INLINE_FUNCTION +Frame KeyFrameInterpolator::keyFrame(int index) const { + const KeyFrame *const kf = keyFrame_.at(index); + return Frame(kf->position(), kf->orientation()); +} + +/*! Returns the time corresponding to the \p index keyFrame. + + See also keyFrame(). \p index has to be in the range 0..numberOfKeyFrames()-1. + */ +CGAL_INLINE_FUNCTION +qreal KeyFrameInterpolator::keyFrameTime(int index) const { + return keyFrame_.at(index)->time(); +} + +/*! Returns the duration of the KeyFrameInterpolator path, expressed in seconds. + + Simply corresponds to lastTime() - firstTime(). Returns 0.0 if the path has + less than 2 keyFrames. See also keyFrameTime(). */ +CGAL_INLINE_FUNCTION +qreal KeyFrameInterpolator::duration() const { + return lastTime() - firstTime(); +} + +/*! Returns the time corresponding to the first keyFrame, expressed in seconds. + +Returns 0.0 if the path is empty. See also lastTime(), duration() and +keyFrameTime(). */ +CGAL_INLINE_FUNCTION +qreal KeyFrameInterpolator::firstTime() const { + if (keyFrame_.isEmpty()) + return 0.0; + else + return keyFrame_.first()->time(); +} + +/*! Returns the time corresponding to the last keyFrame, expressed in seconds. + +Returns 0.0 if the path is empty. See also firstTime(), duration() and +keyFrameTime(). */ +CGAL_INLINE_FUNCTION +qreal KeyFrameInterpolator::lastTime() const { + if (keyFrame_.isEmpty()) + return 0.0; + else + return keyFrame_.last()->time(); +} + +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::updateCurrentKeyFrameForTime(qreal time) { + // Assertion: times are sorted in monotone order. + // Assertion: keyFrame_ is not empty + + // TODO: Special case for loops when closed path is implemented !! + if (!currentFrameValid_) + // Recompute everything from scrach + currentFrame_[1]->toFront(); + + while (currentFrame_[1]->peekNext()->time() > time) { + currentFrameValid_ = false; + if (!currentFrame_[1]->hasPrevious()) + break; + currentFrame_[1]->previous(); + } + + if (!currentFrameValid_) + *currentFrame_[2] = *currentFrame_[1]; + + while (currentFrame_[2]->peekNext()->time() < time) { + currentFrameValid_ = false; + if (!currentFrame_[2]->hasNext()) + break; + currentFrame_[2]->next(); + } + + if (!currentFrameValid_) { + *currentFrame_[1] = *currentFrame_[2]; + if ((currentFrame_[1]->hasPrevious()) && + (time < currentFrame_[2]->peekNext()->time())) + currentFrame_[1]->previous(); + + *currentFrame_[0] = *currentFrame_[1]; + if (currentFrame_[0]->hasPrevious()) + currentFrame_[0]->previous(); + + *currentFrame_[3] = *currentFrame_[2]; + if (currentFrame_[3]->hasNext()) + currentFrame_[3]->next(); + + currentFrameValid_ = true; + splineCacheIsValid_ = false; + } + + // cout << "Time = " << time << " : " << currentFrame_[0]->peekNext()->time() + // << " , " << currentFrame_[1]->peekNext()->time() << " , " << + // currentFrame_[2]->peekNext()->time() << " , " << + // currentFrame_[3]->peekNext()->time() << endl; +} + +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::updateSplineCache() { + Vec delta = currentFrame_[2]->peekNext()->position() - + currentFrame_[1]->peekNext()->position(); + v1 = 3.0 * delta - 2.0 * currentFrame_[1]->peekNext()->tgP() - + currentFrame_[2]->peekNext()->tgP(); + v2 = -2.0 * delta + currentFrame_[1]->peekNext()->tgP() + + currentFrame_[2]->peekNext()->tgP(); + splineCacheIsValid_ = true; +} + +/*! Interpolate frame() at time \p time (expressed in seconds). + interpolationTime() is set to \p time and frame() is set accordingly. + + If you simply want to change interpolationTime() but not the frame() state, + use setInterpolationTime() instead. + + Emits the interpolated() signal and makes the frame() emit the + Frame::interpolated() signal. */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::interpolateAtTime(qreal time) { + setInterpolationTime(time); + + if ((keyFrame_.isEmpty()) || (!frame())) + return; + + if (!valuesAreValid_) + updateModifiedFrameValues(); + + updateCurrentKeyFrameForTime(time); + + if (!splineCacheIsValid_) + updateSplineCache(); + + qreal alpha; + qreal dt = currentFrame_[2]->peekNext()->time() - + currentFrame_[1]->peekNext()->time(); + if (dt == 0.0) + alpha = 0.0; + else + alpha = (time - currentFrame_[1]->peekNext()->time()) / dt; + + // Linear interpolation - debug + // Vec pos = alpha*(currentFrame_[2]->peekNext()->position()) + + // (1.0-alpha)*(currentFrame_[1]->peekNext()->position()); + Vec pos = + currentFrame_[1]->peekNext()->position() + + alpha * (currentFrame_[1]->peekNext()->tgP() + alpha * (v1 + alpha * v2)); + Quaternion q = Quaternion::squad( + currentFrame_[1]->peekNext()->orientation(), + currentFrame_[1]->peekNext()->tgQ(), currentFrame_[2]->peekNext()->tgQ(), + currentFrame_[2]->peekNext()->orientation(), alpha); + frame()->setPositionAndOrientationWithConstraint(pos, q); + + Q_EMIT interpolated(); +} + +/*! Returns an XML \c QDomElement that represents the KeyFrameInterpolator. + + The resulting QDomElement holds the KeyFrameInterpolator parameters as well as + the path keyFrames (if the keyFrame is defined by a pointer to a Frame, use its + current value). + + \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument + factory used to create QDomElement. + + Use initFromDOMElement() to restore the ManipulatedFrame state from the + resulting QDomElement. + + + See Vec::domElement() for a complete example. See also + Quaternion::domElement(), Camera::domElement()... + + Note that the Camera::keyFrameInterpolator() are automatically saved by + QGLViewer::saveStateToFile() when a QGLViewer is closed. */ +CGAL_INLINE_FUNCTION +QDomElement KeyFrameInterpolator::domElement(const QString &name, + QDomDocument &document) const { + QDomElement de = document.createElement(name); + int count = 0; + Q_FOREACH (KeyFrame *kf, keyFrame_) { + Frame fr(kf->position(), kf->orientation()); + QDomElement kfNode = fr.domElement("KeyFrame", document); + kfNode.setAttribute("index", QString::number(count)); + kfNode.setAttribute("time", QString::number(kf->time())); + de.appendChild(kfNode); + ++count; + } + de.setAttribute("nbKF", QString::number(keyFrame_.count())); + de.setAttribute("time", QString::number(interpolationTime())); + de.setAttribute("speed", QString::number(interpolationSpeed())); + de.setAttribute("period", QString::number(interpolationPeriod())); + DomUtils::setBoolAttribute(de, "closedPath", closedPath()); + DomUtils::setBoolAttribute(de, "loop", loopInterpolation()); + return de; +} + +/*! Restores the KeyFrameInterpolator state from a \c QDomElement created by + domElement(). + + Note that the frame() pointer is not included in the domElement(): you need to + setFrame() after this method to attach a Frame to the KeyFrameInterpolator. + + See Vec::initFromDOMElement() for a complete code example. + + See also Camera::initFromDOMElement() and Frame::initFromDOMElement(). */ +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::initFromDOMElement(const QDomElement &element) { + qDeleteAll(keyFrame_); + keyFrame_.clear(); + QDomElement child = element.firstChild().toElement(); + while (!child.isNull()) { + if (child.tagName() == "KeyFrame") { + Frame fr; + fr.initFromDOMElement(child); + qreal time = DomUtils::qrealFromDom(child, "time", 0.0); + addKeyFrame(fr, time); + } + + child = child.nextSibling().toElement(); + } + + // #CONNECTION# Values cut pasted from constructor + setInterpolationTime(DomUtils::qrealFromDom(element, "time", 0.0)); + setInterpolationSpeed(DomUtils::qrealFromDom(element, "speed", 1.0)); + setInterpolationPeriod(DomUtils::intFromDom(element, "period", 40)); + setClosedPath(DomUtils::boolFromDom(element, "closedPath", false)); + setLoopInterpolation(DomUtils::boolFromDom(element, "loop", false)); + + // setFrame(NULL); + pathIsValid_ = false; + valuesAreValid_ = false; + currentFrameValid_ = false; + + stopInterpolation(); +} + +#ifndef DOXYGEN + +//////////// KeyFrame private class implementation ///////// +CGAL_INLINE_FUNCTION +KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame &fr, qreal t) + : time_(t), frame_(NULL) { + p_ = fr.position(); + q_ = fr.orientation(); +} + +CGAL_INLINE_FUNCTION +KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame *fr, qreal t) + : time_(t), frame_(fr) { + updateValuesFromPointer(); +} + +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::KeyFrame::updateValuesFromPointer() { + p_ = frame()->position(); + q_ = frame()->orientation(); +} + +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::KeyFrame::computeTangent( + const KeyFrame *const prev, const KeyFrame *const next) { + tgP_ = 0.5 * (next->position() - prev->position()); + tgQ_ = Quaternion::squadTangent(prev->orientation(), q_, next->orientation()); +} + +CGAL_INLINE_FUNCTION +void KeyFrameInterpolator::KeyFrame::flipOrientationIfNeeded( + const Quaternion &prev) { + if (Quaternion::dot(prev, q_) < 0.0) + q_.negate(); +} + +#endif // DOXYGEN diff --git a/GraphicsView/include/CGAL/Qt/manipulatedCameraFrame.h b/GraphicsView/include/CGAL/Qt/manipulatedCameraFrame.h index e69924fb1a8..b6fa6808f47 100644 --- a/GraphicsView/include/CGAL/Qt/manipulatedCameraFrame.h +++ b/GraphicsView/include/CGAL/Qt/manipulatedCameraFrame.h @@ -22,8 +22,11 @@ #ifndef QGLVIEWER_MANIPULATED_CAMERA_FRAME_H #define QGLVIEWER_MANIPULATED_CAMERA_FRAME_H +#include +#include #include +#include namespace qglviewer { /*! \brief The ManipulatedCameraFrame class represents a ManipulatedFrame with @@ -41,8 +44,8 @@ namespace qglviewer { A ManipulatedCameraFrame can also "fly" in the scene. It basically moves forward, and turns according to the mouse motion. See flySpeed(), - sceneUpVector() and the QGLViewer::MOVE_FORWARD and QGLViewer::MOVE_BACKWARD - QGLViewer::MouseAction. + sceneUpVector() and the MOVE_FORWARD and MOVE_BACKWARD + MouseAction. See the mouse page for a description of the possible actions that can be performed using the mouse and their bindings. @@ -74,7 +77,7 @@ public: When the ManipulatedCameraFrame is associated to a Camera, Camera::pivotPoint() also returns this value. This point can interactively be changed using the mouse (see Camera::setPivotPointFromPixel() and - QGLViewer::RAP_FROM_PIXEL and QGLViewer::RAP_IS_CENTER in the mouse page). */ Vec pivotPoint() const { return pivotPoint_; } /*! Sets the pivotPoint(), defined in the world coordinate system. */ @@ -125,7 +128,7 @@ public: rotatesAroundUpVector_ = constrained; } - /*! Returns whether or not the QGLViewer::ZOOM action zooms on the pivot + /*! Returns whether or not the ZOOM action zooms on the pivot point. When set to \c false (default), a zoom action will move the camera along its @@ -169,8 +172,8 @@ public: /*! Returns the fly speed, expressed in OpenGL units. It corresponds to the incremental displacement that is periodically applied to - the ManipulatedCameraFrame position when a QGLViewer::MOVE_FORWARD or - QGLViewer::MOVE_BACKWARD QGLViewer::MouseAction is proceeded. + the ManipulatedCameraFrame position when a MOVE_FORWARD or + MOVE_BACKWARD MouseAction is proceeded. \attention When the ManipulatedCameraFrame is set as the Camera::frame(), this value is set according to the QGLViewer::sceneRadius() by @@ -180,8 +183,8 @@ public: /*! Returns the up vector of the scene, expressed in the world coordinate system. - In 'fly mode' (corresponding to the QGLViewer::MOVE_FORWARD and - QGLViewer::MOVE_BACKWARD QGLViewer::MouseAction bindings), horizontal + In 'fly mode' (corresponding to the MOVE_FORWARD and + MOVE_BACKWARD MouseAction bindings), horizontal displacements of the mouse rotate the ManipulatedCameraFrame around this vector. Vertical displacements rotate always around the Camera \c X axis. @@ -229,7 +232,7 @@ public Q_SLOTS: protected: virtual void startAction( int ma, - bool withConstraint = true); // int is really a QGLViewer::MouseAction + bool withConstraint = true); // int is really a MouseAction #endif private Q_SLOTS: @@ -260,4 +263,7 @@ private: } // namespace qglviewer +#ifdef CGAL_HEADER_ONLY +//#include +#endif // CGAL_HEADER_ONLY #endif // QGLVIEWER_MANIPULATED_CAMERA_FRAME_H diff --git a/GraphicsView/include/CGAL/Qt/manipulatedCameraFrame_impl.h b/GraphicsView/include/CGAL/Qt/manipulatedCameraFrame_impl.h new file mode 100644 index 00000000000..fea859b03db --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/manipulatedCameraFrame_impl.h @@ -0,0 +1,500 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif +#include +#include +#include +#include + +#include + +using namespace qglviewer; +using namespace std; + +/*! Default constructor. + + flySpeed() is set to 0.0 and sceneUpVector() is (0,1,0). The pivotPoint() is + set to (0,0,0). + + \attention Created object is removeFromMouseGrabberPool(). */ +CGAL_INLINE_FUNCTION +ManipulatedCameraFrame::ManipulatedCameraFrame() + : driveSpeed_(0.0), sceneUpVector_(0.0, 1.0, 0.0), + rotatesAroundUpVector_(false), zoomsOnPivotPoint_(false) { + setFlySpeed(0.0); + removeFromMouseGrabberPool(); + connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate())); +} + +/*! Equal operator. Calls ManipulatedFrame::operator=() and then copy + * attributes. */ +CGAL_INLINE_FUNCTION +ManipulatedCameraFrame &ManipulatedCameraFrame:: +operator=(const ManipulatedCameraFrame &mcf) { + ManipulatedFrame::operator=(mcf); + + setFlySpeed(mcf.flySpeed()); + setSceneUpVector(mcf.sceneUpVector()); + setRotatesAroundUpVector(mcf.rotatesAroundUpVector_); + setZoomsOnPivotPoint(mcf.zoomsOnPivotPoint_); + + return *this; +} + +/*! Copy constructor. Performs a deep copy of all members using operator=(). */ +CGAL_INLINE_FUNCTION +ManipulatedCameraFrame::ManipulatedCameraFrame( + const ManipulatedCameraFrame &mcf) + : ManipulatedFrame(mcf) { + removeFromMouseGrabberPool(); + connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate())); + (*this) = (mcf); +} + +//////////////////////////////////////////////////////////////////////////////// + +/*! Overloading of ManipulatedFrame::spin(). + +Rotates the ManipulatedCameraFrame around its pivotPoint() instead of its +origin. */ +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::spin() { + rotateAroundPoint(spinningQuaternion(), pivotPoint()); +} + +#ifndef DOXYGEN +/*! Called for continuous frame motion in fly mode (see + MOVE_FORWARD). Emits manipulated(). */ +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::flyUpdate() { + static Vec flyDisp(0.0, 0.0, 0.0); + switch (action_) { + case MOVE_FORWARD: + flyDisp.z = -flySpeed(); + translate(localInverseTransformOf(flyDisp)); + break; + case MOVE_BACKWARD: + flyDisp.z = flySpeed(); + translate(localInverseTransformOf(flyDisp)); + break; + case DRIVE: + flyDisp.z = flySpeed() * driveSpeed_; + translate(localInverseTransformOf(flyDisp)); + break; + default: + break; + } + + // Needs to be out of the switch since ZOOM/fastDraw()/wheelEvent use this + // callback to trigger a final draw(). #CONNECTION# wheelEvent. + Q_EMIT manipulated(); +} + +CGAL_INLINE_FUNCTION +Vec ManipulatedCameraFrame::flyUpVector() const { + qWarning("flyUpVector() is deprecated. Use sceneUpVector() instead."); + return sceneUpVector(); +} + +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::setFlyUpVector(const Vec &up) { + qWarning("setFlyUpVector() is deprecated. Use setSceneUpVector() instead."); + setSceneUpVector(up); +} + +#endif + +/*! This method will be called by the Camera when its orientation is changed, so +that the sceneUpVector (private) is changed accordingly. You should not need to +call this method. */ +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::updateSceneUpVector() { + sceneUpVector_ = inverseTransformOf(Vec(0.0, 1.0, 0.0)); +} + +//////////////////////////////////////////////////////////////////////////////// +// S t a t e s a v i n g a n d r e s t o r i n g // +//////////////////////////////////////////////////////////////////////////////// + +/*! Returns an XML \c QDomElement that represents the ManipulatedCameraFrame. + + Adds to the ManipulatedFrame::domElement() the ManipulatedCameraFrame specific + informations in a \c ManipulatedCameraParameters child QDomElement. + + \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument + factory used to create QDomElement. + + Use initFromDOMElement() to restore the ManipulatedCameraFrame state from the + resulting \c QDomElement. + + See Vec::domElement() for a complete example. See also + Quaternion::domElement(), Frame::domElement(), Camera::domElement()... */ +CGAL_INLINE_FUNCTION +QDomElement ManipulatedCameraFrame::domElement(const QString &name, + QDomDocument &document) const { + QDomElement e = ManipulatedFrame::domElement(name, document); + QDomElement mcp = document.createElement("ManipulatedCameraParameters"); + mcp.setAttribute("flySpeed", QString::number(flySpeed())); + DomUtils::setBoolAttribute(mcp, "rotatesAroundUpVector", + rotatesAroundUpVector()); + DomUtils::setBoolAttribute(mcp, "zoomsOnPivotPoint", zoomsOnPivotPoint()); + mcp.appendChild(sceneUpVector().domElement("sceneUpVector", document)); + e.appendChild(mcp); + return e; +} + +/*! Restores the ManipulatedCameraFrame state from a \c QDomElement created by +domElement(). + +First calls ManipulatedFrame::initFromDOMElement() and then initializes +ManipulatedCameraFrame specific parameters. */ +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::initFromDOMElement(const QDomElement &element) { + // No need to initialize, since default sceneUpVector and flySpeed are not + // meaningful. It's better to keep current ones. And it would destroy + // constraint() and referenceFrame(). *this = ManipulatedCameraFrame(); + ManipulatedFrame::initFromDOMElement(element); + + QDomElement child = element.firstChild().toElement(); + while (!child.isNull()) { + if (child.tagName() == "ManipulatedCameraParameters") { + setFlySpeed(DomUtils::qrealFromDom(child, "flySpeed", flySpeed())); + setRotatesAroundUpVector( + DomUtils::boolFromDom(child, "rotatesAroundUpVector", false)); + setZoomsOnPivotPoint( + DomUtils::boolFromDom(child, "zoomsOnPivotPoint", false)); + + QDomElement schild = child.firstChild().toElement(); + while (!schild.isNull()) { + if (schild.tagName() == "sceneUpVector") + setSceneUpVector(Vec(schild)); + + schild = schild.nextSibling().toElement(); + } + } + child = child.nextSibling().toElement(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// M o u s e h a n d l i n g // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef DOXYGEN +/*! Protected internal method used to handle mouse events. */ +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::startAction(int ma, bool withConstraint) { + ManipulatedFrame::startAction(ma, withConstraint); + + switch (action_) { + case MOVE_FORWARD: + case MOVE_BACKWARD: + case DRIVE: + flyTimer_.setSingleShot(false); + flyTimer_.start(10); + break; + case ROTATE: + constrainedRotationIsReversed_ = transformOf(sceneUpVector_).y < 0.0; + break; + default: + break; + } +} + +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::zoom(qreal delta, const Camera *const camera) { + const qreal sceneRadius = camera->sceneRadius(); + if (zoomsOnPivotPoint_) { + Vec direction = position() - camera->pivotPoint(); + if (direction.norm() > 0.02 * sceneRadius || delta > 0.0) + translate(delta * direction); + } else { + const qreal coef = + qMax(fabs((camera->frame()->coordinatesOf(camera->pivotPoint())).z), + qreal(0.2) * sceneRadius); + Vec trans(0.0, 0.0, -coef * delta); + translate(inverseTransformOf(trans)); + } +} + +#endif + +/*! Overloading of ManipulatedFrame::mouseMoveEvent(). + +Motion depends on mouse binding (see mouse page for +details). The resulting displacements are basically inverted from those of a +ManipulatedFrame. */ +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::mouseMoveEvent(QMouseEvent *const event, + Camera *const camera) { + // #CONNECTION# mouseMoveEvent does the update(). + switch (action_) { + case TRANSLATE: { + const QPoint delta = prevPos_ - event->pos(); + Vec trans(delta.x(), -delta.y(), 0.0); + // Scale to fit the screen mouse displacement + switch (camera->type()) { + case Camera::PERSPECTIVE: + trans *= 2.0 * tan(camera->fieldOfView() / 2.0) * + fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / + camera->screenHeight(); + break; + case Camera::ORTHOGRAPHIC: { + GLdouble w, h; + camera->getOrthoWidthHeight(w, h); + trans[0] *= 2.0 * w / camera->screenWidth(); + trans[1] *= 2.0 * h / camera->screenHeight(); + break; + } + } + translate(inverseTransformOf(translationSensitivity() * trans)); + break; + } + + case MOVE_FORWARD: { + Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); + rotate(rot); + //#CONNECTION# wheelEvent MOVE_FORWARD case + // actual translation is made in flyUpdate(). + // translate(inverseTransformOf(Vec(0.0, 0.0, -flySpeed()))); + break; + } + + case MOVE_BACKWARD: { + Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); + rotate(rot); + // actual translation is made in flyUpdate(). + // translate(inverseTransformOf(Vec(0.0, 0.0, flySpeed()))); + break; + } + + case DRIVE: { + Quaternion rot = turnQuaternion(event->x(), camera); + rotate(rot); + // actual translation is made in flyUpdate(). + driveSpeed_ = 0.01 * (event->y() - pressPos_.y()); + break; + } + + case ZOOM: { + zoom(deltaWithPrevPos(event, camera), camera); + break; + } + + case LOOK_AROUND: { + Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); + rotate(rot); + break; + } + + case ROTATE: { + Quaternion rot; + if (rotatesAroundUpVector_) { + // Multiply by 2.0 to get on average about the same speed as with the + // deformed ball + qreal dx = 2.0 * rotationSensitivity() * (prevPos_.x() - event->x()) / + camera->screenWidth(); + qreal dy = 2.0 * rotationSensitivity() * (prevPos_.y() - event->y()) / + camera->screenHeight(); + if (constrainedRotationIsReversed_) + dx = -dx; + Vec verticalAxis = transformOf(sceneUpVector_); + rot = Quaternion(verticalAxis, dx) * Quaternion(Vec(1.0, 0.0, 0.0), dy); + } else { + Vec trans = camera->projectedCoordinatesOf(pivotPoint()); + rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], + camera); + } + //#CONNECTION# These two methods should go together (spinning detection and + // activation) + computeMouseSpeed(event); + setSpinningQuaternion(rot); + spin(); + break; + } + + case SCREEN_ROTATE: { + Vec trans = camera->projectedCoordinatesOf(pivotPoint()); + + const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]) - + atan2(prevPos_.y() - trans[1], prevPos_.x() - trans[0]); + + Quaternion rot(Vec(0.0, 0.0, 1.0), angle); + //#CONNECTION# These two methods should go together (spinning detection and + // activation) + computeMouseSpeed(event); + setSpinningQuaternion(rot); + spin(); + updateSceneUpVector(); + break; + } + + case ROLL: { + const qreal angle = + M_PI * (event->x() - prevPos_.x()) / camera->screenWidth(); + Quaternion rot(Vec(0.0, 0.0, 1.0), angle); + rotate(rot); + setSpinningQuaternion(rot); + updateSceneUpVector(); + break; + } + + case SCREEN_TRANSLATE: { + Vec trans; + int dir = mouseOriginalDirection(event); + if (dir == 1) + trans.setValue(prevPos_.x() - event->x(), 0.0, 0.0); + else if (dir == -1) + trans.setValue(0.0, event->y() - prevPos_.y(), 0.0); + + switch (camera->type()) { + case Camera::PERSPECTIVE: + trans *= 2.0 * tan(camera->fieldOfView() / 2.0) * + fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / + camera->screenHeight(); + break; + case Camera::ORTHOGRAPHIC: { + GLdouble w, h; + camera->getOrthoWidthHeight(w, h); + trans[0] *= 2.0 * w / camera->screenWidth(); + trans[1] *= 2.0 * h / camera->screenHeight(); + break; + } + } + + translate(inverseTransformOf(translationSensitivity() * trans)); + break; + } + + case ZOOM_ON_REGION: + case NO_MOUSE_ACTION: + break; + } + + if (action_ != NO_MOUSE_ACTION) { + prevPos_ = event->pos(); + if (action_ != ZOOM_ON_REGION) + // ZOOM_ON_REGION should not emit manipulated(). + // prevPos_ is used to draw rectangle feedback. + Q_EMIT manipulated(); + } +} + +/*! This is an overload of ManipulatedFrame::mouseReleaseEvent(). The + MouseAction is terminated. */ +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::mouseReleaseEvent(QMouseEvent *const event, + Camera *const camera) { + if ((action_ == MOVE_FORWARD) || + (action_ == MOVE_BACKWARD) || (action_ == DRIVE)) + flyTimer_.stop(); + + if (action_ == ZOOM_ON_REGION) + camera->fitScreenRegion(QRect(pressPos_, event->pos())); + + ManipulatedFrame::mouseReleaseEvent(event, camera); +} + +/*! This is an overload of ManipulatedFrame::wheelEvent(). + +The wheel behavior depends on the wheel binded action. Current possible actions +are ZOOM, MOVE_FORWARD, MOVE_BACKWARD. +ZOOM speed depends on wheelSensitivity() while +MOVE_FORWARD and MOVE_BACKWARD depend on flySpeed(). See +QGLViewer::setWheelBinding() to customize the binding. */ +CGAL_INLINE_FUNCTION +void ManipulatedCameraFrame::wheelEvent(QWheelEvent *const event, + Camera *const camera) { + //#CONNECTION# QGLViewer::setWheelBinding, ManipulatedFrame::wheelEvent. + switch (action_) { + case ZOOM: { + zoom(wheelDelta(event), camera); + Q_EMIT manipulated(); + break; + } + case MOVE_FORWARD: + case MOVE_BACKWARD: + //#CONNECTION# mouseMoveEvent() MOVE_FORWARD case + translate( + inverseTransformOf(Vec(0.0, 0.0, 0.2 * flySpeed() * event->delta()))); + Q_EMIT manipulated(); + break; + default: + break; + } + + // #CONNECTION# startAction should always be called before + if (previousConstraint_) + setConstraint(previousConstraint_); + + // The wheel triggers a fastDraw. A final update() is needed after the last + // wheel event to polish the rendering using draw(). Since the last wheel + // event does not say its name, we use the flyTimer_ to trigger flyUpdate(), + // which emits manipulated. Two wheel events separated by more than this delay + // milliseconds will trigger a draw(). + const int finalDrawAfterWheelEventDelay = 400; + + // Starts (or prolungates) the timer. + flyTimer_.setSingleShot(true); + flyTimer_.start(finalDrawAfterWheelEventDelay); + + // This could also be done *before* manipulated is emitted, so that + // isManipulated() returns false. But then fastDraw would not be used with + // wheel. Detecting the last wheel event and forcing a final draw() is done + // using the timer_. + action_ = NO_MOUSE_ACTION; +} + +//////////////////////////////////////////////////////////////////////////////// + +/*! Returns a Quaternion that is a rotation around current camera Y, + * proportionnal to the horizontal mouse position. */ +CGAL_INLINE_FUNCTION +Quaternion ManipulatedCameraFrame::turnQuaternion(int x, + const Camera *const camera) { + return Quaternion(Vec(0.0, 1.0, 0.0), rotationSensitivity() * + (prevPos_.x() - x) / + camera->screenWidth()); +} + +/*! Returns a Quaternion that is the composition of two rotations, inferred from + the mouse pitch (X axis) and yaw (sceneUpVector() axis). */ +Quaternion +CGAL_INLINE_FUNCTION +ManipulatedCameraFrame::pitchYawQuaternion(int x, int y, + const Camera *const camera) { + const Quaternion rotX(Vec(1.0, 0.0, 0.0), rotationSensitivity() * + (prevPos_.y() - y) / + camera->screenHeight()); + const Quaternion rotY(transformOf(sceneUpVector()), + rotationSensitivity() * (prevPos_.x() - x) / + camera->screenWidth()); + return rotY * rotX; +} diff --git a/GraphicsView/include/CGAL/Qt/manipulatedFrame.h b/GraphicsView/include/CGAL/Qt/manipulatedFrame.h index 71f82e34fc7..3bb50043747 100644 --- a/GraphicsView/include/CGAL/Qt/manipulatedFrame.h +++ b/GraphicsView/include/CGAL/Qt/manipulatedFrame.h @@ -22,16 +22,18 @@ #ifndef QGLVIEWER_MANIPULATED_FRAME_H #define QGLVIEWER_MANIPULATED_FRAME_H - +#include #include +#include #include -#include +#include #include #include #include namespace qglviewer { + /*! \brief A ManipulatedFrame is a Frame that can be rotated and translated using the mouse. \class ManipulatedFrame manipulatedFrame.h QGLViewer/manipulatedFrame.h @@ -299,7 +301,7 @@ public: modifiers, Qt::MouseButton buttons, MouseHandler handler, MouseAction action, bool withConstraint). */ - QGLViewer::MouseAction currentMouseAction() const { return action_; } + MouseAction currentMouseAction() const { return action_; } //@} /*! @name MouseGrabber implementation */ @@ -322,12 +324,12 @@ protected: Quaternion deformedBallQuaternion(int x, int y, qreal cx, qreal cy, const Camera *const camera); - QGLViewer::MouseAction action_; + MouseAction action_; Constraint *previousConstraint_; // When manipulation is without Contraint. virtual void startAction( int ma, - bool withConstraint = true); // int is really a QGLViewer::MouseAction + bool withConstraint = true); // int is really a MouseAction void computeMouseSpeed(const QMouseEvent *const e); int mouseOriginalDirection(const QMouseEvent *const e); @@ -372,4 +374,7 @@ private: } // namespace qglviewer +#ifdef CGAL_HEADER_ONLY +//#include +#endif // CGAL_HEADER_ONLY #endif // QGLVIEWER_MANIPULATED_FRAME_H diff --git a/GraphicsView/include/CGAL/Qt/manipulatedFrame_impl.h b/GraphicsView/include/CGAL/Qt/manipulatedFrame_impl.h new file mode 100644 index 00000000000..12444bfc113 --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/manipulatedFrame_impl.h @@ -0,0 +1,592 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include +#include +#include +#include + +#include + +#include + +using namespace qglviewer; +using namespace std; + +/*! Default constructor. + + The translation is set to (0,0,0), with an identity rotation (0,0,0,1) (see + Frame constructor for details). + + The different sensitivities are set to their default values (see + rotationSensitivity(), translationSensitivity(), spinningSensitivity() and + wheelSensitivity()). */ +CGAL_INLINE_FUNCTION +ManipulatedFrame::ManipulatedFrame() + : action_(NO_MOUSE_ACTION), keepsGrabbingMouse_(false) { + // #CONNECTION# initFromDOMElement and accessor docs + setRotationSensitivity(1.0); + setTranslationSensitivity(1.0); + setSpinningSensitivity(0.3); + setWheelSensitivity(1.0); + setZoomSensitivity(1.0); + + isSpinning_ = false; + previousConstraint_ = NULL; + + connect(&spinningTimer_, SIGNAL(timeout()), SLOT(spinUpdate())); +} + +/*! Equal operator. Calls Frame::operator=() and then copy attributes. */ +CGAL_INLINE_FUNCTION +ManipulatedFrame &ManipulatedFrame::operator=(const ManipulatedFrame &mf) { + Frame::operator=(mf); + + setRotationSensitivity(mf.rotationSensitivity()); + setTranslationSensitivity(mf.translationSensitivity()); + setSpinningSensitivity(mf.spinningSensitivity()); + setWheelSensitivity(mf.wheelSensitivity()); + setZoomSensitivity(mf.zoomSensitivity()); + + mouseSpeed_ = 0.0; + dirIsFixed_ = false; + keepsGrabbingMouse_ = false; + action_ = NO_MOUSE_ACTION; + + return *this; +} + +/*! Copy constructor. Performs a deep copy of all attributes using operator=(). + */ +CGAL_INLINE_FUNCTION +ManipulatedFrame::ManipulatedFrame(const ManipulatedFrame &mf) + : Frame(mf), MouseGrabber() { + (*this) = mf; +} + +//////////////////////////////////////////////////////////////////////////////// + +/*! Implementation of the MouseGrabber main method. + +The ManipulatedFrame grabsMouse() when the mouse is within a 10 pixels region +around its Camera::projectedCoordinatesOf() position(). + +See the mouseGrabber example for an +illustration. */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::checkIfGrabsMouse(int x, int y, + const Camera *const camera) { + const int thresold = 10; + const Vec proj = camera->projectedCoordinatesOf(position()); + setGrabsMouse(keepsGrabbingMouse_ || ((fabs(x - proj.x) < thresold) && + (fabs(y - proj.y) < thresold))); +} + +//////////////////////////////////////////////////////////////////////////////// +// S t a t e s a v i n g a n d r e s t o r i n g // +//////////////////////////////////////////////////////////////////////////////// + +/*! Returns an XML \c QDomElement that represents the ManipulatedFrame. + + Adds to the Frame::domElement() the ManipulatedFrame specific informations in a + \c ManipulatedParameters child QDomElement. + + \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument + factory used to create QDomElement. + + Use initFromDOMElement() to restore the ManipulatedFrame state from the + resulting \c QDomElement. + + See Vec::domElement() for a complete example. See also + Quaternion::domElement(), Camera::domElement()... */ +CGAL_INLINE_FUNCTION +QDomElement ManipulatedFrame::domElement(const QString &name, + QDomDocument &document) const { + QDomElement e = Frame::domElement(name, document); + QDomElement mp = document.createElement("ManipulatedParameters"); + mp.setAttribute("rotSens", QString::number(rotationSensitivity())); + mp.setAttribute("transSens", QString::number(translationSensitivity())); + mp.setAttribute("spinSens", QString::number(spinningSensitivity())); + mp.setAttribute("wheelSens", QString::number(wheelSensitivity())); + mp.setAttribute("zoomSens", QString::number(zoomSensitivity())); + e.appendChild(mp); + return e; +} + +/*! Restores the ManipulatedFrame state from a \c QDomElement created by +domElement(). + +Fields that are not described in \p element are set to their default values (see +ManipulatedFrame()). + +First calls Frame::initFromDOMElement() and then initializes ManipulatedFrame +specific parameters. Note that constraint() and referenceFrame() are not +restored and are left unchanged. + + +See Vec::initFromDOMElement() for a complete code example. */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::initFromDOMElement(const QDomElement &element) { + // Not called since it would set constraint() and referenceFrame() to NULL. + // *this = ManipulatedFrame(); + Frame::initFromDOMElement(element); + + stopSpinning(); + + QDomElement child = element.firstChild().toElement(); + while (!child.isNull()) { + if (child.tagName() == "ManipulatedParameters") { + // #CONNECTION# constructor default values and accessor docs + setRotationSensitivity(DomUtils::qrealFromDom(child, "rotSens", 1.0)); + setTranslationSensitivity( + DomUtils::qrealFromDom(child, "transSens", 1.0)); + setSpinningSensitivity(DomUtils::qrealFromDom(child, "spinSens", 0.3)); + setWheelSensitivity(DomUtils::qrealFromDom(child, "wheelSens", 1.0)); + setZoomSensitivity(DomUtils::qrealFromDom(child, "zoomSens", 1.0)); + } + child = child.nextSibling().toElement(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// M o u s e h a n d l i n g // +//////////////////////////////////////////////////////////////////////////////// + +/*! Returns \c true when the ManipulatedFrame is being manipulated with the + mouse. + + Can be used to change the display of the manipulated object during + manipulation. + + When Camera::frame() of the QGLViewer::camera() isManipulated(), + QGLViewer::fastDraw() is used in place of QGLViewer::draw() for scene + rendering. A simplified drawing will then allow for interactive camera + displacements. */ +CGAL_INLINE_FUNCTION +bool ManipulatedFrame::isManipulated() const { + return action_ != NO_MOUSE_ACTION; +} + +/*! Starts the spinning of the ManipulatedFrame. + +This method starts a timer that will call spin() every \p updateInterval +milliseconds. The ManipulatedFrame isSpinning() until you call stopSpinning(). +*/ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::startSpinning(int updateInterval) { + isSpinning_ = true; + spinningTimer_.start(updateInterval); +} + +/*! Rotates the ManipulatedFrame by its spinningQuaternion(). Called by a timer + when the ManipulatedFrame isSpinning(). */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::spin() { rotate(spinningQuaternion()); } + +/* spin() and spinUpdate() differ since spin can be used by itself (for instance + by QGLViewer::SCREEN_ROTATE) without a spun emission. Much nicer to use the + spinningQuaternion() and hence spin() for these incremental updates. Nothing + special to be done for continuous spinning with this design. */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::spinUpdate() { + spin(); + Q_EMIT spun(); +} + +#ifndef DOXYGEN +/*! Protected internal method used to handle mouse events. */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::startAction(int ma, bool withConstraint) { + action_ = (MouseAction)(ma); + + // #CONNECTION# manipulatedFrame::wheelEvent, + // manipulatedCameraFrame::wheelEvent and mouseReleaseEvent() restore previous + // constraint + if (withConstraint) + previousConstraint_ = NULL; + else { + previousConstraint_ = constraint(); + setConstraint(NULL); + } + + switch (action_) { + case ROTATE: + case SCREEN_ROTATE: + mouseSpeed_ = 0.0; + stopSpinning(); + break; + + case SCREEN_TRANSLATE: + dirIsFixed_ = false; + break; + + default: + break; + } +} + +/*! Updates mouse speed, measured in pixels/milliseconds. Should be called by +any method which wants to use mouse speed. Currently used to trigger spinning in +mouseReleaseEvent(). */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::computeMouseSpeed(const QMouseEvent *const e) { + const QPoint delta = (e->pos() - prevPos_); + const qreal dist = sqrt(qreal(delta.x() * delta.x() + delta.y() * delta.y())); + delay_ = last_move_time.restart(); + if (delay_ == 0) + // Less than a millisecond: assume delay = 1ms + mouseSpeed_ = dist; + else + mouseSpeed_ = dist / delay_; +} + +/*! Return 1 if mouse motion was started horizontally and -1 if it was more +vertical. Returns 0 if this could not be determined yet (perfect diagonal +motion, rare). */ +CGAL_INLINE_FUNCTION +int ManipulatedFrame::mouseOriginalDirection(const QMouseEvent *const e) { + static bool horiz = + true; // Two simultaneous manipulatedFrame require two mice ! + + if (!dirIsFixed_) { + const QPoint delta = e->pos() - pressPos_; + dirIsFixed_ = abs(delta.x()) != abs(delta.y()); + horiz = abs(delta.x()) > abs(delta.y()); + } + + if (dirIsFixed_) + if (horiz) + return 1; + else + return -1; + else + return 0; +} + +CGAL_INLINE_FUNCTION +qreal ManipulatedFrame::deltaWithPrevPos(QMouseEvent *const event, + Camera *const camera) const { + qreal dx = qreal(event->x() - prevPos_.x()) / camera->screenWidth(); + qreal dy = qreal(event->y() - prevPos_.y()) / camera->screenHeight(); + + qreal value = fabs(dx) > fabs(dy) ? dx : dy; + return value * zoomSensitivity(); +} + +CGAL_INLINE_FUNCTION +qreal ManipulatedFrame::wheelDelta(const QWheelEvent *event) const { + static const qreal WHEEL_SENSITIVITY_COEF = 8E-4; + return event->delta() * wheelSensitivity() * WHEEL_SENSITIVITY_COEF; +} + +CGAL_INLINE_FUNCTION +void ManipulatedFrame::zoom(qreal delta, const Camera *const camera) { + Vec trans(0.0, 0.0, (camera->position() - position()).norm() * delta); + + trans = camera->frame()->orientation().rotate(trans); + if (referenceFrame()) + trans = referenceFrame()->transformOf(trans); + translate(trans); +} + +#endif // DOXYGEN + +/*! Initiates the ManipulatedFrame mouse manipulation. + +Overloading of MouseGrabber::mousePressEvent(). See also mouseMoveEvent() and +mouseReleaseEvent(). + +The mouse behavior depends on which button is pressed. See the QGLViewer mouse page for details. */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::mousePressEvent(QMouseEvent *const event, + Camera *const camera) { + Q_UNUSED(camera); + + if (grabsMouse()) + keepsGrabbingMouse_ = true; + + // #CONNECTION setMouseBinding + // action_ should no longer possibly be NO_MOUSE_ACTION since this value is + // not inserted in mouseBinding_ + // if (action_ == NO_MOUSE_ACTION) + // event->ignore(); + + prevPos_ = pressPos_ = event->pos(); +} + +/*! Modifies the ManipulatedFrame according to the mouse motion. + +Actual behavior depends on mouse bindings. See the MouseAction enum +and the QGLViewer mouse page for details. + +The \p camera is used to fit the mouse motion with the display parameters (see +Camera::screenWidth(), Camera::screenHeight(), Camera::fieldOfView()). + +Emits the manipulated() signal. */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::mouseMoveEvent(QMouseEvent *const event, + Camera *const camera) { + switch (action_) { + case TRANSLATE: { + const QPoint delta = event->pos() - prevPos_; + Vec trans(delta.x(), -delta.y(), 0.0); + // Scale to fit the screen mouse displacement + switch (camera->type()) { + case Camera::PERSPECTIVE: + trans *= 2.0 * tan(camera->fieldOfView() / 2.0) * + fabs((camera->frame()->coordinatesOf(position())).z) / + camera->screenHeight(); + break; + case Camera::ORTHOGRAPHIC: { + GLdouble w, h; + camera->getOrthoWidthHeight(w, h); + trans[0] *= 2.0 * w / camera->screenWidth(); + trans[1] *= 2.0 * h / camera->screenHeight(); + break; + } + } + // Transform to world coordinate system. + trans = + camera->frame()->orientation().rotate(translationSensitivity() * trans); + // And then down to frame + if (referenceFrame()) + trans = referenceFrame()->transformOf(trans); + translate(trans); + break; + } + + case ZOOM: { + zoom(deltaWithPrevPos(event, camera), camera); + break; + } + + case SCREEN_ROTATE: { + Vec trans = camera->projectedCoordinatesOf(position()); + + const qreal prev_angle = + atan2(prevPos_.y() - trans[1], prevPos_.x() - trans[0]); + const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]); + + const Vec axis = + transformOf(camera->frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0))); + Quaternion rot(axis, angle - prev_angle); + //#CONNECTION# These two methods should go together (spinning detection and + // activation) + computeMouseSpeed(event); + setSpinningQuaternion(rot); + spin(); + break; + } + + case SCREEN_TRANSLATE: { + Vec trans; + int dir = mouseOriginalDirection(event); + if (dir == 1) + trans.setValue(event->x() - prevPos_.x(), 0.0, 0.0); + else if (dir == -1) + trans.setValue(0.0, prevPos_.y() - event->y(), 0.0); + + switch (camera->type()) { + case Camera::PERSPECTIVE: + trans *= 2.0 * tan(camera->fieldOfView() / 2.0) * + fabs((camera->frame()->coordinatesOf(position())).z) / + camera->screenHeight(); + break; + case Camera::ORTHOGRAPHIC: { + GLdouble w, h; + camera->getOrthoWidthHeight(w, h); + trans[0] *= 2.0 * w / camera->screenWidth(); + trans[1] *= 2.0 * h / camera->screenHeight(); + break; + } + } + // Transform to world coordinate system. + trans = + camera->frame()->orientation().rotate(translationSensitivity() * trans); + // And then down to frame + if (referenceFrame()) + trans = referenceFrame()->transformOf(trans); + + translate(trans); + break; + } + + case ROTATE: { + Vec trans = camera->projectedCoordinatesOf(position()); + Quaternion rot = deformedBallQuaternion(event->x(), event->y(), trans[0], + trans[1], camera); + trans = Vec(-rot[0], -rot[1], -rot[2]); + trans = camera->frame()->orientation().rotate(trans); + trans = transformOf(trans); + rot[0] = trans[0]; + rot[1] = trans[1]; + rot[2] = trans[2]; + //#CONNECTION# These two methods should go together (spinning detection and + // activation) + computeMouseSpeed(event); + setSpinningQuaternion(rot); + spin(); + break; + } + + case MOVE_FORWARD: + case MOVE_BACKWARD: + case LOOK_AROUND: + case ROLL: + case DRIVE: + case ZOOM_ON_REGION: + // These MouseAction values make no sense for a manipulatedFrame + break; + + case NO_MOUSE_ACTION: + // Possible when the ManipulatedFrame is a MouseGrabber. This method is then + // called without startAction because of mouseTracking. + break; + } + + if (action_ != NO_MOUSE_ACTION) { + prevPos_ = event->pos(); + Q_EMIT manipulated(); + } +} + +/*! Stops the ManipulatedFrame mouse manipulation. + +Overloading of MouseGrabber::mouseReleaseEvent(). + +If the action was a ROTATE MouseAction, a continuous +spinning is possible if the speed of the mouse cursor is larger than +spinningSensitivity() when the button is released. Press the rotate button again +to stop spinning. See startSpinning() and isSpinning(). */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::mouseReleaseEvent(QMouseEvent *const event, + Camera *const camera) { + Q_UNUSED(event); + Q_UNUSED(camera); + + keepsGrabbingMouse_ = false; + + if (previousConstraint_) + setConstraint(previousConstraint_); + + if (((action_ == ROTATE) || + (action_ == SCREEN_ROTATE)) && + (mouseSpeed_ >= spinningSensitivity())) + startSpinning(delay_); + + action_ = NO_MOUSE_ACTION; +} + +/*! Overloading of MouseGrabber::mouseDoubleClickEvent(). + +Left button double click aligns the ManipulatedFrame with the \p camera axis +(see alignWithFrame() and ALIGN_FRAME). Right button projects the +ManipulatedFrame on the \p camera view direction. */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::mouseDoubleClickEvent(QMouseEvent *const event, + Camera *const camera) { + if (event->modifiers() == Qt::NoModifier) + switch (event->button()) { + case Qt::LeftButton: + alignWithFrame(camera->frame()); + break; + case Qt::RightButton: + projectOnLine(camera->position(), camera->viewDirection()); + break; + default: + break; + } +} + +/*! Overloading of MouseGrabber::wheelEvent(). + +Using the wheel is equivalent to a ZOOM MouseAction. See + QGLViewer::setWheelBinding(), setWheelSensitivity(). */ +CGAL_INLINE_FUNCTION +void ManipulatedFrame::wheelEvent(QWheelEvent *const event, + Camera *const camera) { + //#CONNECTION# QGLViewer::setWheelBinding + if (action_ == ZOOM) { + zoom(wheelDelta(event), camera); + Q_EMIT manipulated(); + } + + // #CONNECTION# startAction should always be called before + if (previousConstraint_) + setConstraint(previousConstraint_); + + action_ = NO_MOUSE_ACTION; +} + +//////////////////////////////////////////////////////////////////////////////// + +/*! Returns "pseudo-distance" from (x,y) to ball of radius size. +\arg for a point inside the ball, it is proportional to the euclidean distance +to the ball \arg for a point outside the ball, it is proportional to the inverse +of this distance (tends to zero) on the ball, the function is continuous. */ +static qreal projectOnBall(qreal x, qreal y) { + // If you change the size value, change angle computation in + // deformedBallQuaternion(). + const qreal size = 1.0; + const qreal size2 = size * size; + const qreal size_limit = size2 * 0.5; + + const qreal d = x * x + y * y; + return d < size_limit ? sqrt(size2 - d) : size_limit / sqrt(d); +} + +#ifndef DOXYGEN +/*! Returns a quaternion computed according to the mouse motion. Mouse positions +are projected on a deformed ball, centered on (\p cx,\p cy). */ +Quaternion +CGAL_INLINE_FUNCTION +ManipulatedFrame::deformedBallQuaternion(int x, int y, qreal cx, qreal cy, + const Camera *const camera) { + // Points on the deformed ball + qreal px = + rotationSensitivity() * (prevPos_.x() - cx) / camera->screenWidth(); + qreal py = + rotationSensitivity() * (cy - prevPos_.y()) / camera->screenHeight(); + qreal dx = rotationSensitivity() * (x - cx) / camera->screenWidth(); + qreal dy = rotationSensitivity() * (cy - y) / camera->screenHeight(); + + const Vec p1(px, py, projectOnBall(px, py)); + const Vec p2(dx, dy, projectOnBall(dx, dy)); + // Approximation of rotation angle + // Should be divided by the projectOnBall size, but it is 1.0 + const Vec axis = cross(p2, p1); + const qreal angle = + 5.0 * + asin(sqrt(axis.squaredNorm() / p1.squaredNorm() / p2.squaredNorm())); + return Quaternion(axis, angle); +} +#endif // DOXYGEN diff --git a/GraphicsView/include/CGAL/Qt/mouseGrabber.h b/GraphicsView/include/CGAL/Qt/mouseGrabber.h index 04c808ef076..399082ed3b5 100644 --- a/GraphicsView/include/CGAL/Qt/mouseGrabber.h +++ b/GraphicsView/include/CGAL/Qt/mouseGrabber.h @@ -140,7 +140,7 @@ public: MouseGrabber(); /*! Virtual destructor. Removes the MouseGrabber from the MouseGrabberPool(). */ - virtual ~MouseGrabber() { MouseGrabber::MouseGrabberPool_.removeAll(this); } + virtual ~MouseGrabber() { MouseGrabber::MouseGrabberPool().removeAll(this); } /*! @name Mouse grabbing detection */ //@{ @@ -198,12 +198,8 @@ public: You should not have to directly use this list. Use removeFromMouseGrabberPool() and addInMouseGrabberPool() to modify this list. - - \attention This method returns a \c QPtrList with Qt 3 and a \c - QList with Qt 2. */ - static const QList &MouseGrabberPool() { - return MouseGrabber::MouseGrabberPool_; - } +*/ + static QList &MouseGrabberPool(); /*! Returns \c true if the MouseGrabber is currently in the MouseGrabberPool() list. @@ -212,7 +208,7 @@ public: removeFromMouseGrabberPool(), the QGLViewers no longer checkIfGrabsMouse() on this MouseGrabber. Use addInMouseGrabberPool() to insert it back. */ bool isInMouseGrabberPool() const { - return MouseGrabber::MouseGrabberPool_.contains( + return MouseGrabber::MouseGrabberPool().contains( const_cast(this)); } void addInMouseGrabberPool(); @@ -289,10 +285,11 @@ private: bool grabsMouse_; - // Q G L V i e w e r p o o l - static QList MouseGrabberPool_; }; } // namespace qglviewer +#ifdef CGAL_HEADER_ONLY +//#include +#endif // CGAL_HEADER_ONLY #endif // QGLVIEWER_MOUSE_GRABBER_H diff --git a/GraphicsView/include/CGAL/Qt/mouseGrabber_impl.h b/GraphicsView/include/CGAL/Qt/mouseGrabber_impl.h new file mode 100644 index 00000000000..415d0ec99f1 --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/mouseGrabber_impl.h @@ -0,0 +1,90 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include + +using namespace qglviewer; + +// Static private variable +CGAL_INLINE_FUNCTION + QList &MouseGrabber::MouseGrabberPool() { + static QList MouseGrabberPool_; + return MouseGrabberPool_; +} + +/*! Default constructor. + +Adds the created MouseGrabber in the MouseGrabberPool(). grabsMouse() is set to +\c false. */ +CGAL_INLINE_FUNCTION +MouseGrabber::MouseGrabber() : grabsMouse_(false) { addInMouseGrabberPool(); } + +/*! Adds the MouseGrabber in the MouseGrabberPool(). + +All created MouseGrabber are automatically added in the MouseGrabberPool() by +the constructor. Trying to add a MouseGrabber that already +isInMouseGrabberPool() has no effect. + +Use removeFromMouseGrabberPool() to remove the MouseGrabber from the list, so +that it is no longer tested with checkIfGrabsMouse() by the QGLViewer, and hence +can no longer grab mouse focus. Use isInMouseGrabberPool() to know the current +state of the MouseGrabber. */ +CGAL_INLINE_FUNCTION +void MouseGrabber::addInMouseGrabberPool() { + if (!isInMouseGrabberPool()) + MouseGrabber::MouseGrabberPool().append(this); +} + +/*! Removes the MouseGrabber from the MouseGrabberPool(). + +See addInMouseGrabberPool() for details. Removing a MouseGrabber that is not in +MouseGrabberPool() has no effect. */ +CGAL_INLINE_FUNCTION +void MouseGrabber::removeFromMouseGrabberPool() { + if (isInMouseGrabberPool()) + MouseGrabber::MouseGrabberPool().removeAll(const_cast(this)); +} + +/*! Clears the MouseGrabberPool(). + + Use this method only if it is faster to clear the MouseGrabberPool() and then + to add back a few MouseGrabbers than to remove each one independently. Use + QGLViewer::setMouseTracking(false) instead if you want to disable mouse + grabbing. + + When \p autoDelete is \c true, the MouseGrabbers of the MouseGrabberPool() are + actually deleted (use this only if you're sure of what you do). */ +CGAL_INLINE_FUNCTION +void MouseGrabber::clearMouseGrabberPool(bool autoDelete) { + if (autoDelete) + qDeleteAll(MouseGrabber::MouseGrabberPool()); + MouseGrabber::MouseGrabberPool().clear(); +} diff --git a/GraphicsView/include/CGAL/Qt/qglviewer.h b/GraphicsView/include/CGAL/Qt/qglviewer.h index b4682fee556..22e32b7d6d3 100644 --- a/GraphicsView/include/CGAL/Qt/qglviewer.h +++ b/GraphicsView/include/CGAL/Qt/qglviewer.h @@ -22,8 +22,13 @@ #ifndef QGLVIEWER_QGLVIEWER_H #define QGLVIEWER_QGLVIEWER_H - +#include +#include +#include #include +#include +#include +#include #include #include @@ -33,16 +38,13 @@ #include #include #include +#include #include + class QTabWidget; - namespace qglviewer { -class MouseGrabber; -class ManipulatedFrame; -class ManipulatedCameraFrame; } // namespace qglviewer - /*! \brief A versatile 3D OpenGL viewer based on QOpenGLWidget. \class QGLViewer qglviewer.h QGLViewer/qglviewer.h @@ -248,7 +250,7 @@ public: Default value is 1.0. This method is equivalent to camera()->sceneRadius(). See setSceneRadius(). */ - qreal sceneRadius() const { return camera()->sceneRadius(); } + qreal sceneRadius() const; /*! Returns the scene center, defined in world coordinates. See sceneRadius() for details. @@ -258,24 +260,20 @@ public: Do not mismatch this value (that only depends on the scene) with the qglviewer::Camera::pivotPoint(). */ - qglviewer::Vec sceneCenter() const { return camera()->sceneCenter(); } + qglviewer::Vec sceneCenter() const; public Q_SLOTS: /*! Sets the sceneRadius(). The camera() qglviewer::Camera::flySpeed() is set to 1% of this value by this method. Simple wrapper around camera()->setSceneRadius(). */ - virtual void setSceneRadius(qreal radius) { - camera()->setSceneRadius(radius); - } + virtual void setSceneRadius(qreal radius); /*! Sets the sceneCenter(), defined in world coordinates. \attention The qglviewer::Camera::pivotPoint() is set to the sceneCenter() value by this method. */ - virtual void setSceneCenter(const qglviewer::Vec ¢er) { - camera()->setSceneCenter(center); - } + virtual void setSceneCenter(const qglviewer::Vec ¢er); /*! Convenient way to call setSceneCenter() and setSceneRadius() from a (world axis aligned) bounding box of the scene. Takes the offset into account. @@ -286,17 +284,12 @@ public Q_SLOTS: setSceneRadius((max-min).norm() / 2.0); \endcode */ void setSceneBoundingBox(const qglviewer::Vec &min, - const qglviewer::Vec &max) { - camera()->setSceneBoundingBox(min + offset(), max + offset()); - } + const qglviewer::Vec &max); /*! Moves the camera so that the entire scene is visible. Simple wrapper around qglviewer::Camera::showEntireScene(). */ - void showEntireScene() { - camera()->showEntireScene(); - update(); - } + void showEntireScene() ; //@} /*! @name Associated objects */ @@ -861,34 +854,10 @@ compatible with raster mode): use \c glRasterPos3fv() instead. */ /*! @name Keyboard customization */ //@{ public: - /*! Defines the different actions that can be associated with a keyboard - shortcut using setShortcut(). - - See the keyboard page for details. */ - enum KeyboardAction { - DRAW_AXIS, - DRAW_GRID, - DISPLAY_FPS, - ENABLE_TEXT, - EXIT_VIEWER, - CAMERA_MODE, - FULL_SCREEN, - STEREO, - ANIMATION, - HELP, - EDIT_CAMERA, - MOVE_CAMERA_LEFT, - MOVE_CAMERA_RIGHT, - MOVE_CAMERA_UP, - MOVE_CAMERA_DOWN, - INCREASE_FLYSPEED, - DECREASE_FLYSPEED - }; - - unsigned int shortcut(KeyboardAction action) const; + unsigned int shortcut(qglviewer::KeyboardAction action) const; #ifndef DOXYGEN // QGLViewer 1.x - unsigned int keyboardAccelerator(KeyboardAction action) const; + unsigned int keyboardAccelerator(qglviewer::KeyboardAction action) const; Qt::Key keyFrameKey(unsigned int index) const; Qt::KeyboardModifiers playKeyFramePathStateKey() const; // QGLViewer 2.0 without Qt4 support @@ -900,9 +869,9 @@ public: Qt::KeyboardModifiers playPathKeyboardModifiers() const; public Q_SLOTS: - void setShortcut(KeyboardAction action, unsigned int key); + void setShortcut(qglviewer::KeyboardAction action, unsigned int key); #ifndef DOXYGEN - void setKeyboardAccelerator(KeyboardAction action, unsigned int key); + void setKeyboardAccelerator(qglviewer::KeyboardAction action, unsigned int key); #endif void setKeyDescription(unsigned int key, QString description); void clearShortcuts(); @@ -924,97 +893,53 @@ public Q_SLOTS: public: /*! @name Mouse customization */ //@{ - /*! Defines the different mouse handlers: camera() or manipulatedFrame(). - - Used by setMouseBinding(), setMouseBinding(Qt::KeyboardModifiers modifiers, - Qt::MouseButtons, ClickAction, bool, int) and setWheelBinding() to define - which handler receives the mouse events. */ - enum MouseHandler { CAMERA, FRAME }; - - /*! Defines the possible actions that can be binded to a mouse click using - setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, ClickAction, bool, - int). - - See the mouse page for details. */ - enum ClickAction { - NO_CLICK_ACTION, - ZOOM_ON_PIXEL, - ZOOM_TO_FIT, - SELECT, - RAP_FROM_PIXEL, - RAP_IS_CENTER, - CENTER_FRAME, - CENTER_SCENE, - SHOW_ENTIRE_SCENE, - ALIGN_FRAME, - ALIGN_CAMERA - }; - - /*! Defines the possible actions that can be binded to a mouse action (a - click, followed by a mouse displacement). - - These actions may be binded to the camera() or to the manipulatedFrame() (see - QGLViewer::MouseHandler) using setMouseBinding(). */ - enum MouseAction { - NO_MOUSE_ACTION, - ROTATE, - ZOOM, - TRANSLATE, - MOVE_FORWARD, - LOOK_AROUND, - MOVE_BACKWARD, - SCREEN_ROTATE, - ROLL, - DRIVE, - SCREEN_TRANSLATE, - ZOOM_ON_REGION - }; + #ifndef DOXYGEN - MouseAction mouseAction(unsigned int state) const; + qglviewer::MouseAction mouseAction(unsigned int state) const; int mouseHandler(unsigned int state) const; - int mouseButtonState(MouseHandler handler, MouseAction action, + int mouseButtonState(qglviewer::MouseHandler handler, qglviewer::MouseAction action, bool withConstraint = true) const; - ClickAction clickAction(unsigned int state, bool doubleClick, + qglviewer::ClickAction clickAction(unsigned int state, bool doubleClick, Qt::MouseButtons buttonsBefore) const; - void getClickButtonState(ClickAction action, unsigned int &state, + void getClickButtonState(qglviewer::ClickAction action, unsigned int &state, bool &doubleClick, Qt::MouseButtons &buttonsBefore) const; - unsigned int wheelButtonState(MouseHandler handler, MouseAction action, + unsigned int wheelButtonState(qglviewer::MouseHandler handler, qglviewer::MouseAction action, bool withConstraint = true) const; #endif - MouseAction mouseAction(Qt::Key key, Qt::KeyboardModifiers modifiers, + qglviewer::MouseAction mouseAction(Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton button) const; int mouseHandler(Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton button) const; - void getMouseActionBinding(MouseHandler handler, MouseAction action, + void getMouseActionBinding(qglviewer::MouseHandler handler, qglviewer::MouseAction action, bool withConstraint, Qt::Key &key, Qt::KeyboardModifiers &modifiers, Qt::MouseButton &button) const; - ClickAction clickAction(Qt::Key key, Qt::KeyboardModifiers modifiers, + qglviewer::ClickAction clickAction(Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton button, bool doubleClick = false, Qt::MouseButtons buttonsBefore = Qt::NoButton) const; - void getClickActionBinding(ClickAction action, Qt::Key &key, + void getClickActionBinding(qglviewer::ClickAction action, Qt::Key &key, Qt::KeyboardModifiers &modifiers, Qt::MouseButton &button, bool &doubleClick, Qt::MouseButtons &buttonsBefore) const; - MouseAction wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) const; + qglviewer::MouseAction wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) const; int wheelHandler(Qt::Key key, Qt::KeyboardModifiers modifiers) const; - void getWheelActionBinding(MouseHandler handler, MouseAction action, + void getWheelActionBinding(qglviewer::MouseHandler handler, qglviewer::MouseAction action, bool withConstraint, Qt::Key &key, Qt::KeyboardModifiers &modifiers) const; public Q_SLOTS: #ifndef DOXYGEN - void setMouseBinding(unsigned int state, MouseHandler handler, - MouseAction action, bool withConstraint = true); - void setMouseBinding(unsigned int state, ClickAction action, + void setMouseBinding(unsigned int state, qglviewer::MouseHandler handler, + qglviewer::MouseAction action, bool withConstraint = true); + void setMouseBinding(unsigned int state, qglviewer::ClickAction action, bool doubleClick = false, Qt::MouseButtons buttonsBefore = Qt::NoButton); void @@ -1024,13 +949,13 @@ public Q_SLOTS: #endif void setMouseBinding(Qt::KeyboardModifiers modifiers, Qt::MouseButton buttons, - MouseHandler handler, MouseAction action, + qglviewer::MouseHandler handler, qglviewer::MouseAction action, bool withConstraint = true); void setMouseBinding(Qt::KeyboardModifiers modifiers, Qt::MouseButton button, - ClickAction action, bool doubleClick = false, + qglviewer::ClickAction action, bool doubleClick = false, Qt::MouseButtons buttonsBefore = Qt::NoButton); - void setWheelBinding(Qt::KeyboardModifiers modifiers, MouseHandler handler, - MouseAction action, bool withConstraint = true); + void setWheelBinding(Qt::KeyboardModifiers modifiers, qglviewer::MouseHandler handler, + qglviewer::MouseAction action, bool withConstraint = true); void setMouseBindingDescription(Qt::KeyboardModifiers modifiers, Qt::MouseButton button, QString description, @@ -1038,14 +963,14 @@ public Q_SLOTS: Qt::MouseButtons buttonsBefore = Qt::NoButton); void setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, - Qt::MouseButton buttons, MouseHandler handler, - MouseAction action, bool withConstraint = true); + Qt::MouseButton buttons, qglviewer::MouseHandler handler, + qglviewer::MouseAction action, bool withConstraint = true); void setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, - Qt::MouseButton button, ClickAction action, + Qt::MouseButton button, qglviewer::ClickAction action, bool doubleClick = false, Qt::MouseButtons buttonsBefore = Qt::NoButton); void setWheelBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, - MouseHandler handler, MouseAction action, + qglviewer::MouseHandler handler, qglviewer::MouseAction action, bool withConstraint = true); void setMouseBindingDescription(Qt::Key key, Qt::KeyboardModifiers modifiers, @@ -1056,18 +981,18 @@ public Q_SLOTS: void clearMouseBindings(); #ifndef DOXYGEN - MouseAction wheelAction(Qt::KeyboardModifiers modifiers) const; + qglviewer::MouseAction wheelAction(Qt::KeyboardModifiers modifiers) const; int wheelHandler(Qt::KeyboardModifiers modifiers) const; - void setHandlerKeyboardModifiers(MouseHandler handler, + void setHandlerKeyboardModifiers(qglviewer::MouseHandler handler, Qt::KeyboardModifiers modifiers); - void setHandlerStateKey(MouseHandler handler, unsigned int buttonState); - void setMouseStateKey(MouseHandler handler, unsigned int buttonState); + void setHandlerStateKey(qglviewer::MouseHandler handler, unsigned int buttonState); + void setMouseStateKey(qglviewer::MouseHandler handler, unsigned int buttonState); #endif private: - static QString mouseActionString(QGLViewer::MouseAction ma); - static QString clickActionString(QGLViewer::ClickAction ca); + static QString mouseActionString(qglviewer::MouseAction ma); + static QString clickActionString(qglviewer::ClickAction ca); //@} /*! @name State persistence */ @@ -1119,12 +1044,8 @@ public: foreach (QGLViewer* viewer, QGLViewer::QGLViewerPool()) connect(myObject, SIGNAL(IHaveChangedSignal()), viewer, SLOT(update())); \endcode - - \attention With Qt version 3, this method returns a \c QPtrList instead. Use a - \c QPtrListIterator to iterate on the list instead.*/ - static const QList &QGLViewerPool() { - return QGLViewer::QGLViewerPool_; - } +*/ + static QList &QGLViewerPool(); /*! Returns the index of the QGLViewer \p viewer in the QGLViewerPool(). This index in unique and can be used to identify the different created QGLViewers @@ -1135,7 +1056,7 @@ public: available position in that list. Returns -1 if the QGLViewer could not be found (which should not be possible). */ static int QGLViewerIndex(const QGLViewer *const viewer) { - return QGLViewer::QGLViewerPool_.indexOf(const_cast(viewer)); + return QGLViewer::QGLViewerPool().indexOf(const_cast(viewer)); } //@} @@ -1168,7 +1089,7 @@ private: // Set parameters to their default values. Called by the constructors. void defaultConstructor(); - void handleKeyboardAction(KeyboardAction id); + void handleKeyboardAction(qglviewer::KeyboardAction id); // C a m e r a qglviewer::Camera *camera_; @@ -1227,8 +1148,8 @@ private: // S h o r t c u t k e y s void setDefaultShortcuts(); QString cameraPathKeysString() const; - QMap keyboardActionDescription_; - QMap keyboardBinding_; + QMap keyboardActionDescription_; + QMap keyboardBinding_; QMap keyDescription_; // K e y F r a m e s s h o r t c u t s @@ -1246,8 +1167,8 @@ private: #ifndef DOXYGEN // M o u s e a c t i o n s struct MouseActionPrivate { - MouseHandler handler; - MouseAction action; + qglviewer::MouseHandler handler; + qglviewer::MouseAction action; bool withConstraint; }; @@ -1319,15 +1240,12 @@ private: QMap mouseDescription_; void setDefaultMouseBindings(); - void performClickAction(ClickAction ca, const QMouseEvent *const e); + void performClickAction(qglviewer::ClickAction ca, const QMouseEvent *const e); QMap mouseBinding_; QMap wheelBinding_; - QMap clickBinding_; + QMap clickBinding_; Qt::Key currentlyPressedKey_; - // Q G L V i e w e r p o o l - static QList QGLViewerPool_; - // S t a t e F i l e QString stateFileName_; @@ -1371,5 +1289,8 @@ public: bool isOpenGL_4_3()const {return is_ogl_4_3; } }; +#ifdef CGAL_HEADER_ONLY +#include +#endif // CGAL_HEADER_ONLY #endif // QGLVIEWER_QGLVIEWER_H diff --git a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h new file mode 100644 index 00000000000..ed5445ddcc7 --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h @@ -0,0 +1,4321 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace qglviewer; + +// Static private variable +CGAL_INLINE_FUNCTION +QList &QGLViewer::QGLViewerPool() { + static QList QGLViewerPool_; + return QGLViewerPool_; +} + +/*! \mainpage + +libQGLViewer is a free C++ library based on Qt that enables the quick creation +of OpenGL 3D viewers. It features a powerful camera trackball and simple +applications simply require an implementation of the draw() method. +This makes it a tool of choice for OpenGL beginners and assignments. It provides +mouse manipulated frames, stereo display, interpolated +keyFrames, object selection, and much more. It is fully +customizable and easy to extend to create complex applications, with a possible +Qt GUI. + +libQGLViewer is not a 3D viewer that can be used directly to view 3D +scenes in various formats. It is more likely to be the starting point for the +coding of such a viewer. + +libQGLViewer is based on the Qt toolkit and hence compiles on any architecture +(Unix-Linux, Mac, Windows, ...). Full reference documentation and many examples +are provided. + +See the project main page for details on the project and installation steps. */ + +CGAL_INLINE_FUNCTION +void QGLViewer::defaultConstructor() { + + int poolIndex = QGLViewer::QGLViewerPool().indexOf(NULL); + setFocusPolicy(Qt::StrongFocus); + + if (poolIndex >= 0) + QGLViewer::QGLViewerPool().replace(poolIndex, this); + else + QGLViewer::QGLViewerPool().append(this); + camera_ = new Camera(this); + setCamera(camera()); + + setDefaultShortcuts(); + setDefaultMouseBindings(); + + fpsTime_.start(); + fpsCounter_ = 0; + f_p_s_ = 0.0; + fpsString_ = tr("%1Hz", "Frames per seconds, in Hertz").arg("?"); + visualHint_ = 0; + previousPathId_ = 0; + // prevPos_ is not initialized since pos() is not meaningful here. + // It will be set when setFullScreen(false) is called after + // setFullScreen(true) + + // #CONNECTION# default values in initFromDOMElement() + manipulatedFrame_ = NULL; + manipulatedFrameIsACamera_ = false; + mouseGrabberIsAManipulatedFrame_ = false; + mouseGrabberIsAManipulatedCameraFrame_ = false; + displayMessage_ = false; + connect(&messageTimer_, SIGNAL(timeout()), SLOT(hideMessage())); + messageTimer_.setSingleShot(true); + helpWidget_ = NULL; + setMouseGrabber(NULL); + + setSceneRadius(1.0); + showEntireScene(); + setStateFileName(".qglviewer.xml"); + + // #CONNECTION# default values in initFromDOMElement() + setAxisIsDrawn(false); + setGridIsDrawn(false); + setFPSIsDisplayed(false); + setCameraIsEdited(false); + setTextIsEnabled(true); + setStereoDisplay(false); + // Make sure move() is not called, which would call initializeGL() + fullScreen_ = false; + setFullScreen(false); + + animationTimerId_ = 0; + stopAnimation(); + setAnimationPeriod(40); // 25Hz + + selectBuffer_ = NULL; + setSelectBufferSize(4 * 1000); + setSelectRegionWidth(3); + setSelectRegionHeight(3); + setSelectedName(-1); + + bufferTextureId_ = 0; + bufferTextureMaxU_ = 0.0; + bufferTextureMaxV_ = 0.0; + bufferTextureWidth_ = 0; + bufferTextureHeight_ = 0; + previousBufferTextureFormat_ = 0; + previousBufferTextureInternalFormat_ = 0; + currentlyPressedKey_ = Qt::Key(0); + + setAttribute(Qt::WA_NoSystemBackground); + axisIsDrawn_ = true; + + _offset = qglviewer::Vec(0,0,0); +} + +#ifndef DOXYGEN +/*! These contructors are deprecated since version 2.7.0, since they are not + * supported by QOpenGlWidget */ + +/*! Constructor. See \c QGLWidget documentation for details. + +All viewer parameters (display flags, scene parameters, associated objects...) +are set to their default values. See the associated documentation. + +If the \p shareWidget parameter points to a valid \c QGLWidget, the QGLViewer +will share the OpenGL context with \p shareWidget (see isSharing()). */ +CGAL_INLINE_FUNCTION +QGLViewer::QGLViewer(QWidget *parent, + Qt::WindowFlags flags) + : QOpenGLWidget(parent, flags) { + defaultConstructor(); +} + +CGAL_INLINE_FUNCTION +QGLViewer::QGLViewer(QGLContext*, + QWidget *parent, + Qt::WindowFlags flags) + : QOpenGLWidget(parent, flags) { + defaultConstructor(); +} +#endif // DOXYGEN + +/*! Virtual destructor. + +The viewer is replaced by \c NULL in the QGLViewerPool() (in order to preserve +other viewer's indexes) and allocated memory is released. The camera() is +deleted and should be copied before if it is shared by an other viewer. */ +CGAL_INLINE_FUNCTION +QGLViewer::~QGLViewer() { + // See closeEvent comment. Destructor is called (and not closeEvent) only when + // the widget is embedded. Hence we saveToFile here. It is however a bad idea + // if virtual domElement() has been overloaded ! if (parent()) + // saveStateToFileForAllViewers(); + + QGLViewer::QGLViewerPool().replace(QGLViewer::QGLViewerPool().indexOf(this), + NULL); + + camera()->deleteLater(); + delete[] selectBuffer_; + if (helpWidget()) { + // Needed for Qt 4 which has no main widget. + helpWidget()->close(); + delete helpWidget_; + } +} + +static QString QGLViewerVersionString() { + return QString::number((QGLVIEWER_VERSION & 0xff0000) >> 16) + "." + + QString::number((QGLVIEWER_VERSION & 0x00ff00) >> 8) + "." + + QString::number(QGLVIEWER_VERSION & 0x0000ff); +} + +static Qt::KeyboardModifiers keyboardModifiersFromState(unsigned int state) { + // Convertion of keyboard modifiers and mouse buttons as an int is no longer + // supported : emulate + return Qt::KeyboardModifiers(int(state & 0xFF000000)); +} + +static Qt::MouseButton mouseButtonFromState(unsigned int state) { + // Convertion of keyboard modifiers and mouse buttons as an int is no longer + // supported : emulate + return Qt::MouseButton(state & 0xFFFF); +} + +/*! Initializes the QGLViewer OpenGL context and then calls user-defined init(). + +This method is automatically called once, before the first call to paintGL(). + +Overload init() instead of this method to modify viewer specific OpenGL state. + +If a 4.3 context could not be set, a 2.1 context will be used instead. + \see `isOpenGL_4_3()` +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::initializeGL() { + QSurfaceFormat format; + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setVersion(4,3); + format.setProfile(QSurfaceFormat::CompatibilityProfile); + format.setSamples(0); + context()->setFormat(format); + bool created = context()->create(); + if(!created || context()->format().profile() != QSurfaceFormat::CompatibilityProfile) { + // impossible to get a 4.3 compatibility profile, retry with 2.0 + format.setVersion(2,1); + context()->setFormat(format); + created = context()->create(); + is_ogl_4_3 = false; + } + else + { + is_ogl_4_3 = true; + } + makeCurrent(); + QOpenGLFunctions_2_1::initializeOpenGLFunctions(); + // Default colors + setForegroundColor(QColor(180, 180, 180)); + setBackgroundColor(QColor(51, 51, 51)); + + // Clear the buffer where we're going to draw + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // Calls user defined method. Default emits a signal. + init(); + + //OpenGL buffers and programs initialization + for(int i=0; i= 0; --view) { + // Clears screen, set model view matrix with shifted matrix for ith buffer + preDrawStereo(view); + // Used defined method. Default is empty + if (camera()->frame()->isManipulated()) + fastDraw(); + else + draw(); + postDraw(); + } + } else { + // Clears screen, set model view matrix... + preDraw(); + // Used defined method. Default calls draw() + if (camera()->frame()->isManipulated()) + fastDraw(); + else + draw(); + // Add visual hints: axis, camera, grid... + postDraw(); + } + Q_EMIT drawFinished(true); +} + +/*! Sets OpenGL state before draw(). + +Default behavior clears screen and sets the projection and modelView matrices: +\code +glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + +camera()->loadProjectionMatrix(); +camera()->loadModelViewMatrix(); +\endcode + +Emits the drawNeeded() signal once this is done (see the callback example). */ +CGAL_INLINE_FUNCTION +void QGLViewer::preDraw() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // GL_PROJECTION matrix + camera()->loadProjectionMatrix(); + // GL_MODELVIEW matrix + camera()->loadModelViewMatrix(); + + Q_EMIT drawNeeded(); +} + +/*! Called after draw() to draw viewer visual hints. + +Default implementation displays axis, grid, FPS... when the respective flags are +sets. + +See the multiSelect and thumbnail examples for an +overloading illustration. + +The GLContext (color, LIGHTING, BLEND...) is \e not modified by this method, so +that in draw(), the user can rely on the OpenGL context he defined. Respect this +convention (by pushing/popping the different attributes) if you overload this +method. */ +CGAL_INLINE_FUNCTION +void QGLViewer::postDraw() { + // Pivot point, line when camera rolls, zoom region + if (gridIsDrawn()) { + glLineWidth(1.0); + drawGrid(camera()->sceneRadius()); + } + if (axisIsDrawn()) { + glLineWidth(2.0); + drawAxis(1.0); + } + + + drawVisualHints(); + // FPS computation + const unsigned int maxCounter = 20; + if (++fpsCounter_ == maxCounter) { + f_p_s_ = 1000.0 * maxCounter / fpsTime_.restart(); + fpsString_ = tr("%1Hz", "Frames per seconds, in Hertz") + .arg(f_p_s_, 0, 'f', ((f_p_s_ < 10.0) ? 1 : 0)); + fpsCounter_ = 0; + } + + // Restore foregroundColor + float color[4]; + color[0] = foregroundColor().red() / 255.0f; + color[1] = foregroundColor().green() / 255.0f; + color[2] = foregroundColor().blue() / 255.0f; + color[3] = 1.0f; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + if (FPSIsDisplayed()) + displayFPS(); + if (displayMessage_) + drawText(10, height() - 10, message_); + + // Restore GL state + glPopAttrib(); + glPopMatrix(); +} + +/*! Called before draw() (instead of preDraw()) when viewer displaysInStereo(). + +Same as preDraw() except that the glDrawBuffer() is set to \c GL_BACK_LEFT or \c +GL_BACK_RIGHT depending on \p leftBuffer, and it uses +qglviewer::Camera::loadProjectionMatrixStereo() and +qglviewer::Camera::loadModelViewMatrixStereo() instead. */ +CGAL_INLINE_FUNCTION +void QGLViewer::preDrawStereo(bool leftBuffer) { + // Set buffer to draw in + // Seems that SGI and Crystal Eyes are not synchronized correctly ! + // That's why we don't draw in the appropriate buffer... + if (!leftBuffer) + glDrawBuffer(GL_BACK_LEFT); + else + glDrawBuffer(GL_BACK_RIGHT); + + // Clear the buffer where we're going to draw + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // GL_PROJECTION matrix + camera()->loadProjectionMatrixStereo(leftBuffer); + // GL_MODELVIEW matrix + camera()->loadModelViewMatrixStereo(leftBuffer); + + Q_EMIT drawNeeded(); +} + +/*! Draws a simplified version of the scene to guarantee interactive camera +displacements. + +This method is called instead of draw() when the qglviewer::Camera::frame() is +qglviewer::ManipulatedCameraFrame::isManipulated(). Default implementation +simply calls draw(). + +Overload this method if your scene is too complex to allow for interactive +camera manipulation. See the fastDraw +example for an illustration. */ +CGAL_INLINE_FUNCTION +void QGLViewer::fastDraw() { draw(); } + +/*! Starts (\p edit = \c true, default) or stops (\p edit=\c false) the edition +of the camera(). + +Current implementation is limited to paths display. Get current state using +cameraIsEdited(). + +\attention This method sets the qglviewer::Camera::zClippingCoefficient() to 5.0 +when \p edit is \c true, so that the Camera paths (see +qglviewer::Camera::keyFrameInterpolator()) are not clipped. It restores the +previous value when \p edit is \c false. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setCameraIsEdited(bool edit) { + cameraIsEdited_ = edit; + if (edit) { + previousCameraZClippingCoefficient_ = camera()->zClippingCoefficient(); + // #CONNECTION# 5.0 also used in domElement() and in initFromDOMElement(). + camera()->setZClippingCoefficient(5.0); + } else + camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_); + + Q_EMIT cameraIsEditedChanged(edit); + + update(); +} + +// Key bindings. 0 means not defined +CGAL_INLINE_FUNCTION +void QGLViewer::setDefaultShortcuts() { + // D e f a u l t a c c e l e r a t o r s + setShortcut(DRAW_AXIS, Qt::Key_A); + setShortcut(DRAW_GRID, Qt::Key_G); + setShortcut(DISPLAY_FPS, Qt::Key_F); + setShortcut(ENABLE_TEXT, Qt::SHIFT + Qt::Key_Question); + setShortcut(EXIT_VIEWER, Qt::Key_Escape); + setShortcut(CAMERA_MODE, Qt::Key_Space); + setShortcut(FULL_SCREEN, Qt::ALT + Qt::Key_Return); + setShortcut(STEREO, Qt::Key_S); + setShortcut(ANIMATION, Qt::Key_Return); + setShortcut(HELP, Qt::Key_H); + setShortcut(EDIT_CAMERA, Qt::Key_C); + setShortcut(MOVE_CAMERA_LEFT, Qt::Key_Left); + setShortcut(MOVE_CAMERA_RIGHT, Qt::Key_Right); + setShortcut(MOVE_CAMERA_UP, Qt::Key_Up); + setShortcut(MOVE_CAMERA_DOWN, Qt::Key_Down); + setShortcut(INCREASE_FLYSPEED, Qt::Key_Plus); + setShortcut(DECREASE_FLYSPEED, Qt::Key_Minus); + + keyboardActionDescription_[DISPLAY_FPS] = + tr("Toggles the display of the FPS", "DISPLAY_FPS action description"); + keyboardActionDescription_[FULL_SCREEN] = + tr("Toggles full screen display", "FULL_SCREEN action description"); + keyboardActionDescription_[DRAW_AXIS] = tr( + "Toggles the display of the world axis", "DRAW_AXIS action description"); + keyboardActionDescription_[DRAW_GRID] = + tr("Toggles the display of the XY grid", "DRAW_GRID action description"); + keyboardActionDescription_[CAMERA_MODE] = tr( + "Changes camera mode (observe or fly)", "CAMERA_MODE action description"); + keyboardActionDescription_[STEREO] = + tr("Toggles stereo display", "STEREO action description"); + keyboardActionDescription_[HELP] = + tr("Opens this help window", "HELP action description"); + keyboardActionDescription_[ANIMATION] = + tr("Starts/stops the animation", "ANIMATION action description"); + keyboardActionDescription_[EDIT_CAMERA] = + tr("Toggles camera paths display", + "EDIT_CAMERA action description"); // TODO change + keyboardActionDescription_[ENABLE_TEXT] = + tr("Toggles the display of the text", "ENABLE_TEXT action description"); + keyboardActionDescription_[EXIT_VIEWER] = + tr("Exits program", "EXIT_VIEWER action description"); + keyboardActionDescription_[MOVE_CAMERA_LEFT] = + tr("Moves camera left", "MOVE_CAMERA_LEFT action description"); + keyboardActionDescription_[MOVE_CAMERA_RIGHT] = + tr("Moves camera right", "MOVE_CAMERA_RIGHT action description"); + keyboardActionDescription_[MOVE_CAMERA_UP] = + tr("Moves camera up", "MOVE_CAMERA_UP action description"); + keyboardActionDescription_[MOVE_CAMERA_DOWN] = + tr("Moves camera down", "MOVE_CAMERA_DOWN action description"); + keyboardActionDescription_[INCREASE_FLYSPEED] = + tr("Increases fly speed", "INCREASE_FLYSPEED action description"); + keyboardActionDescription_[DECREASE_FLYSPEED] = + tr("Decreases fly speed", "DECREASE_FLYSPEED action description"); + + // K e y f r a m e s s h o r t c u t k e y s + setPathKey(Qt::Key_F1, 1); + setPathKey(Qt::Key_F2, 2); + setPathKey(Qt::Key_F3, 3); + setPathKey(Qt::Key_F4, 4); + setPathKey(Qt::Key_F5, 5); + setPathKey(Qt::Key_F6, 6); + setPathKey(Qt::Key_F7, 7); + setPathKey(Qt::Key_F8, 8); + setPathKey(Qt::Key_F9, 9); + setPathKey(Qt::Key_F10, 10); + setPathKey(Qt::Key_F11, 11); + setPathKey(Qt::Key_F12, 12); + + setAddKeyFrameKeyboardModifiers(Qt::AltModifier); + setPlayPathKeyboardModifiers(Qt::NoModifier); +} + +// M o u s e b e h a v i o r +CGAL_INLINE_FUNCTION +void QGLViewer::setDefaultMouseBindings() { + const Qt::KeyboardModifiers cameraKeyboardModifiers = Qt::NoModifier; + const Qt::KeyboardModifiers frameKeyboardModifiers = Qt::ControlModifier; + + //#CONNECTION# toggleCameraMode() + for (int handler = 0; handler < 2; ++handler) { + MouseHandler mh = (MouseHandler)(handler); + Qt::KeyboardModifiers modifiers = + (mh == FRAME) ? frameKeyboardModifiers : cameraKeyboardModifiers; + + setMouseBinding(modifiers, Qt::LeftButton, mh, ROTATE); + setMouseBinding(modifiers, Qt::MidButton, mh, ZOOM); + setMouseBinding(modifiers, Qt::RightButton, mh, TRANSLATE); + + setMouseBinding(Qt::Key_R, modifiers, Qt::LeftButton, mh, SCREEN_ROTATE); + + setWheelBinding(modifiers, mh, ZOOM); + } + + // Z o o m o n r e g i o n + setMouseBinding(Qt::ShiftModifier, Qt::MidButton, CAMERA, ZOOM_ON_REGION); + + // S e l e c t + setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, SELECT); + + setMouseBinding(Qt::ShiftModifier, Qt::RightButton, RAP_FROM_PIXEL); + // D o u b l e c l i c k + setMouseBinding(Qt::NoModifier, Qt::LeftButton, ALIGN_CAMERA, true); + setMouseBinding(Qt::NoModifier, Qt::MidButton, SHOW_ENTIRE_SCENE, true); + setMouseBinding(Qt::NoModifier, Qt::RightButton, CENTER_SCENE, true); + + setMouseBinding(frameKeyboardModifiers, Qt::LeftButton, ALIGN_FRAME, true); + // middle double click makes no sense for manipulated frame + setMouseBinding(frameKeyboardModifiers, Qt::RightButton, CENTER_FRAME, true); + + // A c t i o n s w i t h k e y m o d i f i e r s + setMouseBinding(Qt::Key_Z, Qt::NoModifier, Qt::LeftButton, ZOOM_ON_PIXEL); + setMouseBinding(Qt::Key_Z, Qt::NoModifier, Qt::RightButton, ZOOM_TO_FIT); + +#ifdef Q_OS_MAC + // Specific Mac bindings for touchpads. Two fingers emulate a wheelEvent which + // zooms. There is no right button available : make Option key + left emulate + // the right button. A Control+Left indeed emulates a right click (OS X system + // configuration), but it does no seem to support dragging. Done at the end to + // override previous settings. + const Qt::KeyboardModifiers macKeyboardModifiers = Qt::AltModifier; + + setMouseBinding(macKeyboardModifiers, Qt::LeftButton, CAMERA, TRANSLATE); + setMouseBinding(macKeyboardModifiers, Qt::LeftButton, CENTER_SCENE, true); + setMouseBinding(frameKeyboardModifiers | macKeyboardModifiers, Qt::LeftButton, + CENTER_FRAME, true); + setMouseBinding(frameKeyboardModifiers | macKeyboardModifiers, Qt::LeftButton, + FRAME, TRANSLATE); +#endif +} + +/*! Associates a new qglviewer::Camera to the viewer. + +You should only use this method when you derive a new class from +qglviewer::Camera and want to use one of its instances instead of the original +class. + +It you simply want to save and restore Camera positions, use +qglviewer::Camera::addKeyFrameToPath() and qglviewer::Camera::playPath() +instead. + +This method silently ignores \c NULL \p camera pointers. The calling method is +responsible for deleting the previous camera pointer in order to prevent memory +leaks if needed. + +The sceneRadius() and sceneCenter() of \p camera are set to the \e current +QGLViewer values. + +All the \p camera qglviewer::Camera::keyFrameInterpolator() +qglviewer::KeyFrameInterpolator::interpolated() signals are connected to the +viewer update() slot. The connections with the previous viewer's camera are +removed. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setCamera(Camera *const camera) { + if (!camera) + return; + + camera->setSceneRadius(sceneRadius()); + camera->setSceneCenter(sceneCenter()); + camera->setScreenWidthAndHeight(width(), height()); + + // Disconnect current camera from this viewer. + disconnect(this->camera()->frame(), SIGNAL(manipulated()), this, + SLOT(update())); + disconnect(this->camera()->frame(), SIGNAL(spun()), this, SLOT(update())); + + // Connect camera frame to this viewer. + connect(camera->frame(), SIGNAL(manipulated()), SLOT(update())); + connect(camera->frame(), SIGNAL(spun()), SLOT(update())); + + connectAllCameraKFIInterpolatedSignals(false); + camera_ = camera; + connectAllCameraKFIInterpolatedSignals(); + + previousCameraZClippingCoefficient_ = this->camera()->zClippingCoefficient(); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::connectAllCameraKFIInterpolatedSignals(bool connection) { + for (QMap::ConstIterator + it = camera()->kfi_.begin(), + end = camera()->kfi_.end(); + it != end; ++it) { + if (connection) + connect(camera()->keyFrameInterpolator(it.key()), SIGNAL(interpolated()), + SLOT(update())); + else + disconnect(camera()->keyFrameInterpolator(it.key()), + SIGNAL(interpolated()), this, SLOT(update())); + } + + if (connection) + connect(camera()->interpolationKfi_, SIGNAL(interpolated()), + SLOT(update())); + else + disconnect(camera()->interpolationKfi_, SIGNAL(interpolated()), this, + SLOT(update())); +} + +/*! Draws a representation of \p light. + +Called in draw(), this method is useful to debug or display your light setup. +Light drawing depends on the type of light (point, spot, directional). + +The method retrieves the light setup using \c glGetLightfv. Position and define +your lights before calling this method. + +Light is drawn using its diffuse color. Disabled lights are not displayed. + +Drawing size is proportional to sceneRadius(). Use \p scale to rescale it. + +See the drawLight example for an +illustration. + +\attention You need to enable \c GL_COLOR_MATERIAL before calling this method. +\c glColor is set to the light diffuse color. */ +CGAL_INLINE_FUNCTION +void QGLViewer::drawLight(GLenum, qreal ) const { +} + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) +CGAL_INLINE_FUNCTION +void QGLViewer::renderText(int x, int y, const QString &str, + const QFont &font) { + QColor fontColor = QColor(0, 0, + 0, 255); + + // Render text + QPainter painter(this); + painter.setPen(fontColor); + painter.setFont(font); + painter.drawText(x, y, str); + painter.end(); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::renderText(double x, double y, double z, const QString &str, + const QFont &font) { + const Vec proj = camera_->projectedCoordinatesOf(Vec(x, y, z)); + renderText(proj.x, proj.y, str, font); +} +#endif + +/*! Draws \p text at position \p x, \p y (expressed in screen coordinates +pixels, origin in the upper left corner of the widget). + +The default QApplication::font() is used to render the text when no \p fnt is +specified. Use QApplication::setFont() to define this default font. + +You should disable \c GL_LIGHTING and \c GL_DEPTH_TEST before this method so +that colors are properly rendered. + +This method can be used in conjunction with the +qglviewer::Camera::projectedCoordinatesOf() method to display a text attached to +an object. In your draw() method use: \code qglviewer::Vec screenPos = +camera()->projectedCoordinatesOf(myFrame.position()); +drawText((int)screenPos[0], (int)screenPos[1], "My Object"); +\endcode +See the screenCoordSystem +example for an illustration. + +Text is displayed only when textIsEnabled() (default). This mechanism allows the +user to conveniently remove all the displayed text with a single keyboard +shortcut. + +See also displayMessage() to drawText() for only a short amount of time. + +Use renderText(x,y,z, text) instead if you want to draw a text located + at a specific 3D position instead of 2D screen coordinates (fixed size text, +facing the camera). + +The \c GL_MODELVIEW and \c GL_PROJECTION matrices are not modified by this +method. +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::drawText(int x, int y, const QString &text, const QFont &fnt) { + if (!textIsEnabled()) + return; + + renderText(x, y, text, fnt); +} + +/*! Briefly displays a message in the lower left corner of the widget. +Convenient to provide feedback to the user. + +\p message is displayed during \p delay milliseconds (default is 2 seconds) +using drawText(). + +This method should not be called in draw(). If you want to display a text in +each draw(), use drawText() instead. + +If this method is called when a message is already displayed, the new message +replaces the old one. Use setTextIsEnabled() (default shortcut is '?') to enable +or disable text (and hence messages) display. */ +CGAL_INLINE_FUNCTION +void QGLViewer::displayMessage(const QString &message, int delay) { + message_ = message; + displayMessage_ = true; + // Was set to single shot in defaultConstructor. + messageTimer_.start(delay); + if (textIsEnabled()) + update(); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::hideMessage() { + displayMessage_ = false; + if (textIsEnabled()) + update(); +} + +/*! Displays the averaged currentFPS() frame rate in the upper left corner of +the widget. + +update() should be called in a loop in order to have a meaningful value (this is +the case when you continuously move the camera using the mouse or when +animationIsStarted()). setAnimationPeriod(0) to make this loop as fast as +possible in order to reach and measure the maximum available frame rate. + +When FPSIsDisplayed() is \c true (default is \c false), this method is called by +postDraw() to display the currentFPS(). Use QApplication::setFont() to define +the font (see drawText()). */ +CGAL_INLINE_FUNCTION +void QGLViewer::displayFPS() { + drawText(10, + int(1.5 * ((QApplication::font().pixelSize() > 0) + ? QApplication::font().pixelSize() + : QApplication::font().pointSize())), + fpsString_); +} + +/*! Modify the projection matrix so that drawing can be done directly with 2D +screen coordinates. + +Once called, the \p x and \p y coordinates passed to \c glVertex are expressed +in pixels screen coordinates. The origin (0,0) is in the upper left corner of +the widget by default. This follows the Qt standards, so that you can directly +use the \c pos() provided by for instance \c QMouseEvent. Set \p upward to \c +true to place the origin in the \e lower left corner, thus following the OpenGL +and mathematical standards. It is always possible to switch between the two +representations using \c newY = height() - \c y. + +You need to call stopScreenCoordinatesSystem() at the end of the drawing block +to restore the previous camera matrix. + +In practice, this method should be used in draw(). It sets an appropriate +orthographic projection matrix and then sets \c glMatrixMode to \c GL_MODELVIEW. + +See the screenCoordSystem, multiSelect and backgroundImage examples +for an illustration. + +You may want to disable \c GL_LIGHTING, to enable \c GL_LINE_SMOOTH or \c +GL_BLEND to draw when this method is used. + +If you want to link 2D drawings to 3D objects, use +qglviewer::Camera::projectedCoordinatesOf() to compute the 2D projection on +screen of a 3D point (see the screenCoordSystem example). See +also drawText(). + +In this mode, you should use z values that are in the [0.0, 1.0[ range (0.0 +corresponding to the near clipping plane and 1.0 being just beyond the far +clipping plane). This interval matches the values that can be read from the +z-buffer. Note that if you use the convenient \c glVertex2i() to provide +coordinates, the implicit 0.0 z coordinate will make your drawings appear \e on +\e top of the rest of the scene. */ +CGAL_INLINE_FUNCTION +void QGLViewer::startScreenCoordinatesSystem(bool ) const { +} + +/*! Stops the pixel coordinate drawing block started by +startScreenCoordinatesSystem(). + +The \c GL_MODELVIEW and \c GL_PROJECTION matrices modified in +startScreenCoordinatesSystem() are restored. \c glMatrixMode is set to \c +GL_MODELVIEW. */ +CGAL_INLINE_FUNCTION +void QGLViewer::stopScreenCoordinatesSystem() const { +} + +/*! Overloading of the \c QObject method. + +If animationIsStarted(), calls animate() and draw(). */ +CGAL_INLINE_FUNCTION +void QGLViewer::timerEvent(QTimerEvent *) { + if (animationIsStarted()) { + animate(); + update(); + } +} + +/*! Starts the animation loop. See animationIsStarted(). */ +CGAL_INLINE_FUNCTION +void QGLViewer::startAnimation() { + animationTimerId_ = startTimer(animationPeriod()); + animationStarted_ = true; +} + +/*! Stops animation. See animationIsStarted(). */ +CGAL_INLINE_FUNCTION +void QGLViewer::stopAnimation() { + animationStarted_ = false; + if (animationTimerId_ != 0) + killTimer(animationTimerId_); +} + +/*! Overloading of the \c QWidget method. + +Saves the viewer state using saveStateToFile() and then calls +QOpenGLWidget::closeEvent(). */ +CGAL_INLINE_FUNCTION +void QGLViewer::closeEvent(QCloseEvent *e) { + // When the user clicks on the window close (x) button: + // - If the viewer is a top level window, closeEvent is called and then saves + // to file. - Otherwise, nothing happen s:( When the user press the + // EXIT_VIEWER keyboard shortcut: - If the viewer is a top level window, + // saveStateToFile() is also called - Otherwise, closeEvent is NOT called and + // keyPressEvent does the job. + + /* After tests: + E : Embedded widget + N : Widget created with new + C : closeEvent called + D : destructor called + + E N C D + y y + y n y + n y y + n n y y + + closeEvent is called iif the widget is NOT embedded. + + Destructor is called iif the widget is created on the stack + or if widget (resp. parent if embedded) is created with WDestructiveClose + flag. + + closeEvent always before destructor. + + Close using qApp->closeAllWindows or (x) is identical. + */ + + // #CONNECTION# Also done for EXIT_VIEWER in keyPressEvent(). + saveStateToFile(); + QOpenGLWidget::closeEvent(e); +} + +/*! Simple wrapper method: calls \c select(event->pos()). + +Emits \c pointSelected(e) which is useful only if you rely on the Qt signal-slot +mechanism and you did not overload QGLViewer. If you choose to derive your own +viewer class, simply overload select() (or probably simply drawWithNames(), see +the select example) to implement your +selection mechanism. + +This method is called when you use the QGLViewer::SELECT mouse binding(s) +(default is Shift + left button). Use setMouseBinding() to change this. */ +CGAL_INLINE_FUNCTION +void QGLViewer::select(const QMouseEvent *event) { + // For those who don't derive but rather rely on the signal-slot mechanism. + Q_EMIT pointSelected(event); + select(event->pos()); +} + +/*! This method performs a selection in the scene from pixel coordinates. + +It is called when the user clicks on the QGLViewer::SELECT +QGLViewer::ClickAction binded button(s) (default is Shift + LeftButton). + +This template method successively calls four other methods: +\code +beginSelection(point); +drawWithNames(); +endSelection(point); +postSelection(point); +\endcode + +The default implementation of these methods is as follows (see the methods' +documentation for more details): + +\arg beginSelection() sets the \c GL_SELECT mode with the appropriate picking +matrices. A rectangular frustum (of size defined by selectRegionWidth() and +selectRegionHeight()) centered on \p point is created. + +\arg drawWithNames() is empty and should be overloaded. It draws each selectable +object of the scene, enclosed by calls to \c glPushName() / \c glPopName() to +tag the object with an integer id. + +\arg endSelection() then restores \c GL_RENDER mode and analyzes the +selectBuffer() to set in selectedName() the id of the object that was drawn in +the region. If several object are in the region, the closest one in the depth +buffer is chosen. If no object has been drawn under cursor, selectedName() is +set to -1. + +\arg postSelection() is empty and can be overloaded for possible +signal/display/interface update. + +See the \c glSelectBuffer() man page for details on this \c GL_SELECT mechanism. + +This default implementation is quite limited: only the closer object is +selected, and only one level of names can be pushed. However, this reveals +sufficient in many cases and you usually only have to overload drawWithNames() +to implement a simple object selection process. See the select example for an illustration. + +If you need a more complex selection process (such as a point, edge or triangle +selection, which is easier with a 2 or 3 levels selectBuffer() heap, and which +requires a finer depth sorting to privilege point over edge and edges over +triangles), overload the endSelection() method. Use setSelectRegionWidth(), +setSelectRegionHeight() and setSelectBufferSize() to tune the select buffer +configuration. See the multiSelect +example for an illustration. + +\p point is the center pixel (origin in the upper left corner) of the selection +region. Use qglviewer::Camera::convertClickToLine() to transform these +coordinates in a 3D ray if you want to perform an analytical intersection. + +\attention \c GL_SELECT mode seems to report wrong results when used in +conjunction with backface culling. If you encounter problems try to \c +glDisable(GL_CULL_FACE). */ +CGAL_INLINE_FUNCTION +void QGLViewer::select(const QPoint &point) { + beginSelection(point); + drawWithNames(); + endSelection(point); + postSelection(point); +} + +/*! This method should prepare the selection. It is called by select() before +drawWithNames(). +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::beginSelection(const QPoint &point) +{ + makeCurrent(); + glEnable(GL_SCISSOR_TEST); + glScissor(point.x(), camera()->screenHeight()-1-point.y(), 1, 1); +} + +/*! This method is called by select() after scene elements were drawn by +drawWithNames(). + It clears the OpenGL state set by beginSelection*/ +CGAL_INLINE_FUNCTION +void QGLViewer::endSelection(const QPoint &point) { + Q_UNUSED(point); + glDisable(GL_SCISSOR_TEST); +} + +/*! Sets the selectBufferSize(). + +The previous selectBuffer() is deleted and a new one is created. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setSelectBufferSize(int size) { + if (selectBuffer_) + delete[] selectBuffer_; + selectBufferSize_ = size; + selectBuffer_ = new GLuint[selectBufferSize()]; +} + +static QString mouseButtonsString(Qt::MouseButtons b) { + QString result(""); + bool addAmpersand = false; + if (b & Qt::LeftButton) { + result += QGLViewer::tr("Left", "left mouse button"); + addAmpersand = true; + } + if (b & Qt::MidButton) { + if (addAmpersand) + result += " & "; + result += QGLViewer::tr("Middle", "middle mouse button"); + addAmpersand = true; + } + if (b & Qt::RightButton) { + if (addAmpersand) + result += " & "; + result += QGLViewer::tr("Right", "right mouse button"); + } + return result; +} + +CGAL_INLINE_FUNCTION +void QGLViewer::performClickAction(ClickAction ca, const QMouseEvent *const e) { + // Note: action that need it should call update(). + switch (ca) { + // # CONNECTION setMouseBinding prevents adding NO_CLICK_ACTION in + // clickBinding_ This case should hence not be possible. Prevents unused case + // warning. + case NO_CLICK_ACTION: + break; + case ZOOM_ON_PIXEL: + camera()->interpolateToZoomOnPixel(e->pos()); + break; + case ZOOM_TO_FIT: + camera()->interpolateToFitScene(); + break; + case SELECT: + select(e); + update(); + break; + case RAP_FROM_PIXEL: + if (!camera()->setPivotPointFromPixel(e->pos())) + camera()->setPivotPoint(sceneCenter()); + setVisualHintsMask(1); + update(); + break; + case RAP_IS_CENTER: + camera()->setPivotPoint(sceneCenter()); + setVisualHintsMask(1); + update(); + break; + case CENTER_FRAME: + if (manipulatedFrame()) + manipulatedFrame()->projectOnLine(camera()->position(), + camera()->viewDirection()); + break; + case CENTER_SCENE: + camera()->centerScene(); + break; + case SHOW_ENTIRE_SCENE: + camera()->showEntireScene(); + break; + case ALIGN_FRAME: + if (manipulatedFrame()) + manipulatedFrame()->alignWithFrame(camera()->frame()); + break; + case ALIGN_CAMERA: + Frame *frame = new Frame(); + frame->setTranslation(camera()->pivotPoint()); + camera()->frame()->alignWithFrame(frame, true); + delete frame; + break; + } +} + +/*! Overloading of the \c QWidget method. + +When the user clicks on the mouse: +\arg if a mouseGrabber() is defined, qglviewer::MouseGrabber::mousePressEvent() +is called, \arg otherwise, the camera() or the manipulatedFrame() interprets the +mouse displacements, depending on mouse bindings. + +Mouse bindings customization can be achieved using setMouseBinding() and +setWheelBinding(). See the mouse page for a complete +description of mouse bindings. + +See the mouseMoveEvent() documentation for an example of more complex mouse +behavior customization using overloading. + +\note When the mouseGrabber() is a manipulatedFrame(), the modifier keys are not +taken into account. This allows for a direct manipulation of the +manipulatedFrame() when the mouse hovers, which is probably what is expected. */ +CGAL_INLINE_FUNCTION +void QGLViewer::mousePressEvent(QMouseEvent *e) { + //#CONNECTION# mouseDoubleClickEvent has the same structure + //#CONNECTION# mouseString() concatenates bindings description in inverse + // order. + ClickBindingPrivate cbp(e->modifiers(), e->button(), false, + (Qt::MouseButtons)(e->buttons() & ~(e->button())), + currentlyPressedKey_); + + if (clickBinding_.contains(cbp)) { + performClickAction(clickBinding_[cbp], e); + } else if (mouseGrabber()) { + if (mouseGrabberIsAManipulatedFrame_) { + for (QMap::ConstIterator + it = mouseBinding_.begin(), + end = mouseBinding_.end(); + it != end; ++it) + if ((it.value().handler == FRAME) && (it.key().button == e->button())) { + ManipulatedFrame *mf = + dynamic_cast(mouseGrabber()); + if (mouseGrabberIsAManipulatedCameraFrame_) { + mf->ManipulatedFrame::startAction(it.value().action, + it.value().withConstraint); + mf->ManipulatedFrame::mousePressEvent(e, camera()); + } else { + mf->startAction(it.value().action, it.value().withConstraint); + mf->mousePressEvent(e, camera()); + } + break; + } + } else + mouseGrabber()->mousePressEvent(e, camera()); + update(); + } else { + //#CONNECTION# wheelEvent has the same structure + const MouseBindingPrivate mbp(e->modifiers(), e->button(), + currentlyPressedKey_); + + if (mouseBinding_.contains(mbp)) { + MouseActionPrivate map = mouseBinding_[mbp]; + switch (map.handler) { + case CAMERA: + camera()->frame()->startAction(map.action, map.withConstraint); + camera()->frame()->mousePressEvent(e, camera()); + break; + case FRAME: + if (manipulatedFrame()) { + if (manipulatedFrameIsACamera_) { + manipulatedFrame()->ManipulatedFrame::startAction( + map.action, map.withConstraint); + manipulatedFrame()->ManipulatedFrame::mousePressEvent(e, camera()); + } else { + manipulatedFrame()->startAction(map.action, map.withConstraint); + manipulatedFrame()->mousePressEvent(e, camera()); + } + } + break; + } + if (map.action == SCREEN_ROTATE) + // Display visual hint line + update(); + } else + e->ignore(); + } +} + +/*! Overloading of the \c QWidget method. + +Mouse move event is sent to the mouseGrabber() (if any) or to the camera() or +the manipulatedFrame(), depending on mouse bindings (see setMouseBinding()). + +If you want to define your own mouse behavior, do something like this: +\code +CGAL_INLINE_FUNCTION +void Viewer::mousePressEvent(QMouseEvent* e) +{ + +if ((e->button() == myButton) && (e->modifiers() == myModifiers)) + myMouseBehavior = true; +else + QGLViewer::mousePressEvent(e); +} + +CGAL_INLINE_FUNCTION +void Viewer::mouseMoveEvent(QMouseEvent *e) +{ +if (myMouseBehavior) + // Use e->x() and e->y() as you want... +else + QGLViewer::mouseMoveEvent(e); +} + +CGAL_INLINE_FUNCTION +void Viewer::mouseReleaseEvent(QMouseEvent* e) +{ +if (myMouseBehavior) + myMouseBehavior = false; +else + QGLViewer::mouseReleaseEvent(e); +} +\endcode */ +CGAL_INLINE_FUNCTION +void QGLViewer::mouseMoveEvent(QMouseEvent *e) { + if (mouseGrabber()) { + mouseGrabber()->checkIfGrabsMouse(e->x(), e->y(), camera()); + if (mouseGrabber()->grabsMouse()) + if (mouseGrabberIsAManipulatedCameraFrame_) + (dynamic_cast(mouseGrabber())) + ->ManipulatedFrame::mouseMoveEvent(e, camera()); + else + mouseGrabber()->mouseMoveEvent(e, camera()); + else + setMouseGrabber(NULL); + update(); + } + + if (!mouseGrabber()) { + //#CONNECTION# mouseReleaseEvent has the same structure + if (camera()->frame()->isManipulated()) { + camera()->frame()->mouseMoveEvent(e, camera()); + // #CONNECTION# manipulatedCameraFrame::mouseMoveEvent specific if at the + // beginning + if (camera()->frame()->action_ == ZOOM_ON_REGION) + update(); + } else // ! + if ((manipulatedFrame()) && (manipulatedFrame()->isManipulated())) + if (manipulatedFrameIsACamera_) + manipulatedFrame()->ManipulatedFrame::mouseMoveEvent(e, camera()); + else + manipulatedFrame()->mouseMoveEvent(e, camera()); + else if (hasMouseTracking()) { + Q_FOREACH (MouseGrabber *mg, MouseGrabber::MouseGrabberPool()) { + mg->checkIfGrabsMouse(e->x(), e->y(), camera()); + if (mg->grabsMouse()) { + setMouseGrabber(mg); + // Check that MouseGrabber is not disabled + if (mouseGrabber() == mg) { + update(); + break; + } + } + } + } + } +} + +/*! Overloading of the \c QWidget method. + +Calls the mouseGrabber(), camera() or manipulatedFrame \c mouseReleaseEvent +method. + +See the mouseMoveEvent() documentation for an example of mouse behavior +customization. */ +CGAL_INLINE_FUNCTION +void QGLViewer::mouseReleaseEvent(QMouseEvent *e) { + if (mouseGrabber()) { + if (mouseGrabberIsAManipulatedCameraFrame_) + (dynamic_cast(mouseGrabber())) + ->ManipulatedFrame::mouseReleaseEvent(e, camera()); + else + mouseGrabber()->mouseReleaseEvent(e, camera()); + mouseGrabber()->checkIfGrabsMouse(e->x(), e->y(), camera()); + if (!(mouseGrabber()->grabsMouse())) + setMouseGrabber(NULL); + // update(); + } else + //#CONNECTION# mouseMoveEvent has the same structure + if (camera()->frame()->isManipulated()) { + camera()->frame()->mouseReleaseEvent(e, camera()); + } else if ((manipulatedFrame()) && (manipulatedFrame()->isManipulated())) { + if (manipulatedFrameIsACamera_) + manipulatedFrame()->ManipulatedFrame::mouseReleaseEvent(e, camera()); + else + manipulatedFrame()->mouseReleaseEvent(e, camera()); + } else + e->ignore(); + + // Not absolutely needed (see above commented code for the optimal version), + // but may reveal useful for specific applications. + update(); +} + +/*! Overloading of the \c QWidget method. + +If defined, the wheel event is sent to the mouseGrabber(). It is otherwise sent +according to wheel bindings (see setWheelBinding()). */ +CGAL_INLINE_FUNCTION +void QGLViewer::wheelEvent(QWheelEvent *e) { + if (mouseGrabber()) { + if (mouseGrabberIsAManipulatedFrame_) { + for (QMap::ConstIterator + it = wheelBinding_.begin(), + end = wheelBinding_.end(); + it != end; ++it) + if (it.value().handler == FRAME) { + ManipulatedFrame *mf = + dynamic_cast(mouseGrabber()); + if (mouseGrabberIsAManipulatedCameraFrame_) { + mf->ManipulatedFrame::startAction(it.value().action, + it.value().withConstraint); + mf->ManipulatedFrame::wheelEvent(e, camera()); + } else { + mf->startAction(it.value().action, it.value().withConstraint); + mf->wheelEvent(e, camera()); + } + break; + } + } else + mouseGrabber()->wheelEvent(e, camera()); + update(); + } else { + //#CONNECTION# mousePressEvent has the same structure + WheelBindingPrivate wbp(e->modifiers(), currentlyPressedKey_); + + if (wheelBinding_.contains(wbp)) { + MouseActionPrivate map = wheelBinding_[wbp]; + switch (map.handler) { + case CAMERA: + camera()->frame()->startAction(map.action, map.withConstraint); + camera()->frame()->wheelEvent(e, camera()); + break; + case FRAME: + if (manipulatedFrame()) { + if (manipulatedFrameIsACamera_) { + manipulatedFrame()->ManipulatedFrame::startAction( + map.action, map.withConstraint); + manipulatedFrame()->ManipulatedFrame::wheelEvent(e, camera()); + } else { + manipulatedFrame()->startAction(map.action, map.withConstraint); + manipulatedFrame()->wheelEvent(e, camera()); + } + } + break; + } + } else + e->ignore(); + } +} + +/*! Overloading of the \c QWidget method. + +The behavior of the mouse double click depends on the mouse binding. See +setMouseBinding() and the mouse page. */ +CGAL_INLINE_FUNCTION +void QGLViewer::mouseDoubleClickEvent(QMouseEvent *e) { + //#CONNECTION# mousePressEvent has the same structure + ClickBindingPrivate cbp(e->modifiers(), e->button(), true, + (Qt::MouseButtons)(e->buttons() & ~(e->button())), + currentlyPressedKey_); + if (clickBinding_.contains(cbp)) + performClickAction(clickBinding_[cbp], e); + else if (mouseGrabber()) + mouseGrabber()->mouseDoubleClickEvent(e, camera()); + else + e->ignore(); +} + +/*! Sets the state of displaysInStereo(). See also toggleStereoDisplay(). + +First checks that the display is able to handle stereovision using +CGAL_INLINE_FUNCTION +QOpenGLWidget::format(). Opens a warning message box in case of failure. Emits +the stereoChanged() signal otherwise. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setStereoDisplay(bool stereo) { + if (format().stereo()) { + stereo_ = stereo; + if (!displaysInStereo()) { + glDrawBuffer(GL_BACK_LEFT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDrawBuffer(GL_BACK_RIGHT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + Q_EMIT stereoChanged(stereo_); + + update(); + } else if (stereo) + QMessageBox::warning(this, + tr("Stereo not supported", "Message box window title"), + tr("Stereo is not supported on this display.")); + else + stereo_ = false; +} + +/*! Sets the isFullScreen() state. + +If the QGLViewer is embedded in an other QWidget (see +CGAL_INLINE_FUNCTION +QWidget::topLevelWidget()), this widget is displayed in full screen instead. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setFullScreen(bool fullScreen) { + if (fullScreen_ == fullScreen) + return; + + fullScreen_ = fullScreen; + + QWidget *tlw = topLevelWidget(); + + if (isFullScreen()) { + prevPos_ = topLevelWidget()->pos(); + tlw->showFullScreen(); + tlw->move(0, 0); + } else { + tlw->showNormal(); + tlw->move(prevPos_); + } +} + +/*! Directly defines the mouseGrabber(). + +You should not call this method directly as it bypasses the +qglviewer::MouseGrabber::checkIfGrabsMouse() test performed by mouseMoveEvent(). + +If the MouseGrabber is disabled (see mouseGrabberIsEnabled()), this method +silently does nothing. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseGrabber(MouseGrabber *mouseGrabber) { + if (!mouseGrabberIsEnabled(mouseGrabber)) + return; + + mouseGrabber_ = mouseGrabber; + + mouseGrabberIsAManipulatedFrame_ = + (dynamic_cast(mouseGrabber) != NULL); + mouseGrabberIsAManipulatedCameraFrame_ = + ((dynamic_cast(mouseGrabber) != NULL) && + (mouseGrabber != camera()->frame())); + Q_EMIT mouseGrabberChanged(mouseGrabber); +} + +/*! Sets the mouseGrabberIsEnabled() state. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseGrabberIsEnabled( + const qglviewer::MouseGrabber *const mouseGrabber, bool enabled) { + if (enabled) + disabledMouseGrabbers_.remove(reinterpret_cast(mouseGrabber)); + else + disabledMouseGrabbers_[reinterpret_cast(mouseGrabber)]; +} + +CGAL_INLINE_FUNCTION +QString QGLViewer::mouseActionString(MouseAction ma) { + switch (ma) { + case qglviewer::NO_MOUSE_ACTION: + return QString::null; + case qglviewer::ROTATE: + return QGLViewer::tr("Rotates", "ROTATE mouse action"); + case qglviewer::ZOOM: + return QGLViewer::tr("Zooms", "ZOOM mouse action"); + case qglviewer::TRANSLATE: + return QGLViewer::tr("Translates", "TRANSLATE mouse action"); + case qglviewer::MOVE_FORWARD: + return QGLViewer::tr("Moves forward", "MOVE_FORWARD mouse action"); + case qglviewer::LOOK_AROUND: + return QGLViewer::tr("Looks around", "LOOK_AROUND mouse action"); + case qglviewer::MOVE_BACKWARD: + return QGLViewer::tr("Moves backward", "MOVE_BACKWARD mouse action"); + case qglviewer::SCREEN_ROTATE: + return QGLViewer::tr("Rotates in screen plane", + "SCREEN_ROTATE mouse action"); + case qglviewer::ROLL: + return QGLViewer::tr("Rolls", "ROLL mouse action"); + case qglviewer::DRIVE: + return QGLViewer::tr("Drives", "DRIVE mouse action"); + case qglviewer::SCREEN_TRANSLATE: + return QGLViewer::tr("Horizontally/Vertically translates", + "SCREEN_TRANSLATE mouse action"); + case qglviewer::ZOOM_ON_REGION: + return QGLViewer::tr("Zooms on region for", "ZOOM_ON_REGION mouse action"); + } + return QString::null; +} + +CGAL_INLINE_FUNCTION +QString QGLViewer::clickActionString(qglviewer::ClickAction ca) { + switch (ca) { + case qglviewer::NO_CLICK_ACTION: + return QString::null; + case qglviewer::ZOOM_ON_PIXEL: + return QGLViewer::tr("Zooms on pixel", "ZOOM_ON_PIXEL click action"); + case qglviewer::ZOOM_TO_FIT: + return QGLViewer::tr("Zooms to fit scene", "ZOOM_TO_FIT click action"); + case qglviewer::SELECT: + return QGLViewer::tr("Selects", "SELECT click action"); + case qglviewer::RAP_FROM_PIXEL: + return QGLViewer::tr("Sets pivot point", "RAP_FROM_PIXEL click action"); + case qglviewer::RAP_IS_CENTER: + return QGLViewer::tr("Resets pivot point", "RAP_IS_CENTER click action"); + case qglviewer::CENTER_FRAME: + return QGLViewer::tr("Centers manipulated frame", + "CENTER_FRAME click action"); + case qglviewer::CENTER_SCENE: + return QGLViewer::tr("Centers scene", "CENTER_SCENE click action"); + case qglviewer::SHOW_ENTIRE_SCENE: + return QGLViewer::tr("Shows entire scene", + "SHOW_ENTIRE_SCENE click action"); + case qglviewer::ALIGN_FRAME: + return QGLViewer::tr("Aligns manipulated frame", + "ALIGN_FRAME click action"); + case qglviewer::ALIGN_CAMERA: + return QGLViewer::tr("Aligns camera", "ALIGN_CAMERA click action"); + } + return QString::null; +} + +static QString keyString(unsigned int key) { +#if QT_VERSION >= 0x040100 + return QKeySequence(int(key)).toString(QKeySequence::NativeText); +#else + return QString(QKeySequence(key)); +#endif +} + +CGAL_INLINE_FUNCTION +QString QGLViewer::formatClickActionPrivate(ClickBindingPrivate cbp) { + bool buttonsBefore = cbp.buttonsBefore != Qt::NoButton; + QString keyModifierString = keyString(cbp.modifiers + cbp.key); + if (!keyModifierString.isEmpty()) { +#ifdef Q_OS_MAC + // modifiers never has a '+' sign. Add one space to clearly separate + // modifiers (and possible key) from button + keyModifierString += " "; +#else + // modifiers might be of the form : 'S' or 'Ctrl+S' or 'Ctrl+'. For + // consistency, add an other '+' if needed, no spaces + if (!keyModifierString.endsWith('+')) + keyModifierString += "+"; +#endif + } + + return tr("%1%2%3%4%5%6", "Modifier / button or wheel / double click / with " + "/ button / pressed") + .arg(keyModifierString) + .arg(mouseButtonsString(cbp.button) + + (cbp.button == Qt::NoButton ? tr("Wheel", "Mouse wheel") : "")) + .arg(cbp.doubleClick ? tr(" double click", "Suffix after mouse button") + : "") + .arg(buttonsBefore ? tr(" with ", "As in : Left button with Ctrl pressed") + : "") + .arg(buttonsBefore ? mouseButtonsString(cbp.buttonsBefore) : "") + .arg(buttonsBefore + ? tr(" pressed", "As in : Left button with Ctrl pressed") + : ""); +} + +CGAL_INLINE_FUNCTION +bool QGLViewer::isValidShortcutKey(int key) { + return (key >= Qt::Key_Any && key < Qt::Key_Escape) || + (key >= Qt::Key_F1 && key <= Qt::Key_F35); +} + +#ifndef DOXYGEN +/*! This method is deprecated since version 2.5.0 + + Use setMouseBindingDescription(Qt::KeyboardModifiers, Qt::MouseButtons, + QString, bool, Qt::MouseButtons) instead. +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBindingDescription(unsigned int state, + QString description, + bool doubleClick, + Qt::MouseButtons buttonsBefore) { + qWarning("setMouseBindingDescription(int state,...) is deprecated. Use the " + "modifier/button equivalent"); + setMouseBindingDescription(keyboardModifiersFromState(state), + mouseButtonFromState(state), description, + doubleClick, buttonsBefore); +} +#endif + +/*! Defines a custom mouse binding description, displayed in the help() window's + Mouse tab. + + Same as calling setMouseBindingDescription(Qt::Key, Qt::KeyboardModifiers, + Qt::MouseButton, QString, bool, Qt::MouseButtons), with a key value of + Qt::Key(0) (i.e. binding description when no regular key needs to be pressed). + */ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBindingDescription(Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, + QString description, + bool doubleClick, + Qt::MouseButtons buttonsBefore) { + setMouseBindingDescription(Qt::Key(0), modifiers, button, description, + doubleClick, buttonsBefore); +} + +/*! Defines a custom mouse binding description, displayed in the help() window's +Mouse tab. + +\p modifiers is a combination of Qt::KeyboardModifiers (\c Qt::ControlModifier, +\c Qt::AltModifier, \c Qt::ShiftModifier, \c Qt::MetaModifier). Possibly +combined using the \c "|" operator. + +\p button is one of the Qt::MouseButtons (\c Qt::LeftButton, \c Qt::MidButton, +\c Qt::RightButton...). + +\p doubleClick indicates whether or not the user has to double click this button +to perform the described action. \p buttonsBefore lists the buttons that need to +be pressed before the double click. + +Set an empty \p description to \e remove a mouse binding description. + +\code +// The R key combined with the Left mouse button rotates the camera in the +screen plane. setMouseBindingDescription(Qt::Key_R, Qt::NoModifier, +Qt::LeftButton, "Rotates camera in screen plane"); + +// A left button double click toggles full screen +setMouseBindingDescription(Qt::NoModifier, Qt::LeftButton, "Toggles full screen +mode", true); + +// Removes the description of Ctrl+Right button +setMouseBindingDescription(Qt::ControlModifier, Qt::RightButton, ""); +\endcode + +Overload mouseMoveEvent() and friends to implement your custom mouse behavior +(see the mouseMoveEvent() documentation for an example). See the keyboardAndMouse example for an +illustration. + +Use setMouseBinding() and setWheelBinding() to change the standard mouse action +bindings. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBindingDescription( + Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton button, + QString description, bool doubleClick, Qt::MouseButtons buttonsBefore) { + ClickBindingPrivate cbp(modifiers, button, doubleClick, buttonsBefore, key); + + if (description.isEmpty()) + mouseDescription_.remove(cbp); + else + mouseDescription_[cbp] = description; +} + +static QString tableLine(const QString &left, const QString &right) { + static bool even = false; + const QString tdtd(""); + const QString tdtr("\n"); + + QString res(""; + else + res += "#ffffff\">"; + res += "" + left + tdtd + right + tdtr; + even = !even; + + return res; +} + +/*! Returns a QString that describes the application mouse bindings, displayed +in the help() window \c Mouse tab. + +Result is a table that describes custom application mouse binding descriptions +defined using setMouseBindingDescription() as well as standard mouse bindings +(defined using setMouseBinding() and setWheelBinding()). See the mouse page for details on mouse bindings. + +See also helpString() and keyboardString(). */ +CGAL_INLINE_FUNCTION +QString QGLViewer::mouseString() const { + QString text( + "
\n"); + const QString trtd("\n"); + const QString tdtd("\n") + .arg(tr("Button(s)", + "Buttons column header in help window mouse tab")) + .arg(tr("Description", + "Description column header in help window mouse tab")); + + QMap mouseBinding; + + // User-defined mouse bindings come first. + for (QMap::ConstIterator + itm = mouseDescription_.begin(), + endm = mouseDescription_.end(); + itm != endm; ++itm) + mouseBinding[itm.key()] = itm.value(); + + for (QMap::ConstIterator + it = mouseBinding.begin(), + end = mouseBinding.end(); + it != end; ++it) { + // Should not be needed (see setMouseBindingDescription()) + if (it.value().isNull()) + continue; + + text += tableLine(formatClickActionPrivate(it.key()), it.value()); + } + + // Optional separator line + if (!mouseBinding.isEmpty()) { + mouseBinding.clear(); + text += QString("\n") + .arg(tr("Standard mouse bindings", "In help window mouse tab")); + } + + // Then concatenates the descriptions of wheelBinding_, mouseBinding_ and + // clickBinding_. The order is significant and corresponds to the priorities + // set in mousePressEvent() (reverse priority order, last one overwrites + // previous) #CONNECTION# mousePressEvent() order + for (QMap::ConstIterator + itmb = mouseBinding_.begin(), + endmb = mouseBinding_.end(); + itmb != endmb; ++itmb) { + ClickBindingPrivate cbp(itmb.key().modifiers, itmb.key().button, false, + Qt::NoButton, itmb.key().key); + + QString text = mouseActionString(itmb.value().action); + + if (!text.isNull()) { + switch (itmb.value().handler) { + case CAMERA: + text += " " + tr("camera", "Suffix after action"); + break; + case FRAME: + text += " " + tr("manipulated frame", "Suffix after action"); + break; + } + if (!(itmb.value().withConstraint)) + text += "*"; + } + mouseBinding[cbp] = text; + } + + for (QMap::ConstIterator + itw = wheelBinding_.begin(), + endw = wheelBinding_.end(); + itw != endw; ++itw) { + ClickBindingPrivate cbp(itw.key().modifiers, Qt::NoButton, false, + Qt::NoButton, itw.key().key); + + QString text = mouseActionString(itw.value().action); + + if (!text.isNull()) { + switch (itw.value().handler) { + case CAMERA: + text += " " + tr("camera", "Suffix after action"); + break; + case FRAME: + text += " " + tr("manipulated frame", "Suffix after action"); + break; + } + if (!(itw.value().withConstraint)) + text += "*"; + } + + mouseBinding[cbp] = text; + } + + for (QMap::ConstIterator + itcb = clickBinding_.begin(), + endcb = clickBinding_.end(); + itcb != endcb; ++itcb) + mouseBinding[itcb.key()] = clickActionString(itcb.value()); + + for (QMap::ConstIterator + it2 = mouseBinding.begin(), + end2 = mouseBinding.end(); + it2 != end2; ++it2) { + if (it2.value().isNull()) + continue; + + text += tableLine(formatClickActionPrivate(it2.key()), it2.value()); + } + + text += "
"); + const QString tdtr("
"); + + text += QString("
%1%2
%1
"; + + return text; +} + +/*! Defines a custom keyboard shortcut description, that will be displayed in +the help() window \c Keyboard tab. + +The \p key definition is given as an \c int using Qt enumerated values. Set an +empty \p description to remove a shortcut description: \code +setKeyDescription(Qt::Key_W, "Toggles wireframe display"); +setKeyDescription(Qt::CTRL+Qt::Key_L, "Loads a new scene"); +// Removes a description +setKeyDescription(Qt::CTRL+Qt::Key_C, ""); +\endcode + +See the keyboardAndMouse example +for illustration and the keyboard page for +details. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setKeyDescription(unsigned int key, QString description) { + if (description.isEmpty()) + keyDescription_.remove(key); + else + keyDescription_[key] = description; +} + +CGAL_INLINE_FUNCTION +QString QGLViewer::cameraPathKeysString() const { + if (pathIndex_.isEmpty()) + return QString::null; + + QVector keys; + keys.reserve(pathIndex_.count()); + for (QMap::ConstIterator i = pathIndex_.begin(), + endi = pathIndex_.end(); + i != endi; ++i) + keys.push_back(i.key()); + qSort(keys); + + QVector::const_iterator it = keys.begin(), end = keys.end(); + QString res = keyString(*it); + + const int maxDisplayedKeys = 6; + int nbDisplayedKeys = 0; + Qt::Key previousKey = (*it); + int state = 0; + ++it; + while ((it != end) && (nbDisplayedKeys < maxDisplayedKeys - 1)) { + switch (state) { + case 0: + if ((*it) == previousKey + 1) + state++; + else { + res += ", " + keyString(*it); + nbDisplayedKeys++; + } + break; + case 1: + if ((*it) == previousKey + 1) + state++; + else { + res += ", " + keyString(previousKey); + res += ", " + keyString(*it); + nbDisplayedKeys += 2; + state = 0; + } + break; + default: + if ((*it) != previousKey + 1) { + res += ".." + keyString(previousKey); + res += ", " + keyString(*it); + nbDisplayedKeys += 2; + state = 0; + } + break; + } + previousKey = *it; + ++it; + } + + if (state == 1) + res += ", " + keyString(previousKey); + if (state == 2) + res += ".." + keyString(previousKey); + if (it != end) + res += "..."; + + return res; +} + +/*! Returns a QString that describes the application keyboard shortcut bindings, +and that will be displayed in the help() window \c Keyboard tab. + +Default value is a table that describes the custom shortcuts defined using +setKeyDescription() as well as the \e standard QGLViewer::KeyboardAction +shortcuts (defined using setShortcut()). See the keyboard page for details on key customization. + +See also helpString() and mouseString(). */ +CGAL_INLINE_FUNCTION +QString QGLViewer::keyboardString() const { + QString text( + "
\n"); + text += QString("\n") + .arg(QGLViewer::tr("Key(s)", + "Keys column header in help window mouse tab")) + .arg(QGLViewer::tr( + "Description", + "Description column header in help window mouse tab")); + + QMap keyDescription; + + // 1 - User defined key descriptions + for (QMap::ConstIterator kd = keyDescription_.begin(), + kdend = keyDescription_.end(); + kd != kdend; ++kd) + keyDescription[kd.key()] = kd.value(); + + // Add to text in sorted order + for (QMap::ConstIterator kb = keyDescription.begin(), + endb = keyDescription.end(); + kb != endb; ++kb) + text += tableLine(keyString(kb.key()), kb.value()); + + // 2 - Optional separator line + if (!keyDescription.isEmpty()) { + keyDescription.clear(); + text += QString("\n") + .arg(QGLViewer::tr("Standard viewer keys", + "In help window keys tab")); + } + + // 3 - KeyboardAction bindings description + for (QMap::ConstIterator + it = keyboardBinding_.begin(), + end = keyboardBinding_.end(); + it != end; ++it) + if ((it.value() != 0) && + ((!cameraIsInRotateMode()) || + ((it.key() != INCREASE_FLYSPEED) && (it.key() != DECREASE_FLYSPEED)))) + keyDescription[it.value()] = keyboardActionDescription_[it.key()]; + + // Add to text in sorted order + for (QMap::ConstIterator kb2 = keyDescription.begin(), + endb2 = keyDescription.end(); + kb2 != endb2; ++kb2) + text += tableLine(keyString(kb2.key()), kb2.value()); + + // 4 - Camera paths keys description + const QString cpks = cameraPathKeysString(); + if (!cpks.isNull()) { + text += "\n"; + text += tableLine( + keyString(playPathKeyboardModifiers()) + "" + + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "", + QGLViewer::tr("Plays path (or resets saved position)")); + text += tableLine( + keyString(addKeyFrameKeyboardModifiers()) + "" + + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "", + QGLViewer::tr("Adds a key frame to path (or defines a position)")); + text += tableLine( + keyString(addKeyFrameKeyboardModifiers()) + "" + + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "+" + + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "", + QGLViewer::tr("Deletes path (or saved position)")); + } + text += "
%1%2
%1
\n"; + text += QGLViewer::tr("Camera paths are controlled using the %1 keys " + "(noted Fx below):", + "Help window key tab camera keys") + .arg(cpks) + + "
"; + + return text; +} + +/*! Displays the help window "About" tab. See help() for details. */ +CGAL_INLINE_FUNCTION +void QGLViewer::aboutQGLViewer() { + help(); + helpWidget()->setCurrentIndex(3); +} + +/*! Opens a modal help window that includes four tabs, respectively filled with +helpString(), keyboardString(), mouseString() and about libQGLViewer. + +Rich html-like text can be used (see the QStyleSheet documentation). This method +is called when the user presses the QGLViewer::HELP key (default is 'H'). + +You can use helpWidget() to access to the help widget (to add/remove tabs, +change layout...). + +The helpRequired() signal is emitted. */ +CGAL_INLINE_FUNCTION +void QGLViewer::help() { + Q_EMIT helpRequired(); + + bool resize = false; + int width = 600; + int height = 400; + + static QString label[] = {tr("&Help", "Help window tab title"), + tr("&Keyboard", "Help window tab title"), + tr("&Mouse", "Help window tab title"), + tr("&About", "Help window about title")}; + + if (!helpWidget()) { + // Qt4 requires a NULL parent... + helpWidget_ = new QTabWidget(NULL); + helpWidget()->setWindowTitle(tr("Help", "Help window title")); + + resize = true; + for (int i = 0; i < 4; ++i) { + QTextEdit *tab = new QTextEdit(NULL); + tab->setReadOnly(true); + + helpWidget()->insertTab(i, tab, label[i]); + if (i == 3) { +#include "resources/qglviewer-icon.xpm" + QPixmap pixmap(qglviewer_icon); + tab->document()->addResource(QTextDocument::ImageResource, + QUrl("mydata://qglviewer-icon.xpm"), + QVariant(pixmap)); + } + } + } + + for (int i = 0; i < 4; ++i) { + QString text; + switch (i) { + case 0: + text = helpString(); + break; + case 1: + text = keyboardString(); + break; + case 2: + text = mouseString(); + break; + case 3: + text = QString("

") + + tr("

libQGLViewer

" + "

Version %1


" + "A versatile 3D viewer based on OpenGL and Qt
" + "Copyright 2002-%2 Gilles Debunne
" + "%3") + .arg(QGLViewerVersionString()) + .arg("2014") + .arg("http://www.libqglviewer.com") + + QString("
"); + break; + default: + break; + } + + QTextEdit *textEdit = (QTextEdit *)(helpWidget()->widget(i)); + textEdit->setHtml(text); + textEdit->setText(text); + + if (resize && (textEdit->height() > height)) + height = textEdit->height(); + } + + if (resize) + helpWidget()->resize(width, height + 40); // 40 pixels is ~ tabs' height + helpWidget()->show(); + helpWidget()->raise(); +} + +/*! Overloading of the \c QWidget method. + +Default keyboard shortcuts are defined using setShortcut(). Overload this method +to implement a specific keyboard binding. Call the original method if you do not +catch the event to preserve the viewer default key bindings: \code void +CGAL_INLINE_FUNCTION +Viewer::keyPressEvent(QKeyEvent *e) +{ + // Defines the Alt+R shortcut. + if ((e->key() == Qt::Key_R) && (e->modifiers() == Qt::AltModifier)) + { + myResetFunction(); + update(); // Refresh display + } + else + QGLViewer::keyPressEvent(e); +} + +// With Qt 2 or 3, you would retrieve modifiers keys using : +// const Qt::ButtonState modifiers = (Qt::ButtonState)(e->state() & +Qt::KeyButtonMask); \endcode When you define a new keyboard shortcut, use +setKeyDescription() to provide a short description which is displayed in the +help() window Keyboard tab. See the keyboardAndMouse example for an +illustration. + +CGAL_INLINE_FUNCTION +See also QOpenGLWidget::keyReleaseEvent(). */ +CGAL_INLINE_FUNCTION +void QGLViewer::keyPressEvent(QKeyEvent *e) { + if (e->key() == 0) { + e->ignore(); + return; + } + + const Qt::Key key = Qt::Key(e->key()); + + const Qt::KeyboardModifiers modifiers = e->modifiers(); + + QMap::ConstIterator it = keyboardBinding_ + .begin(), + end = + keyboardBinding_.end(); + const unsigned int target = key | modifiers; + while ((it != end) && (it.value() != target)) + ++it; + + if (it != end) + handleKeyboardAction(it.key()); + else if (pathIndex_.contains(Qt::Key(key))) { + // Camera paths + unsigned int index = pathIndex_[Qt::Key(key)]; + + // not safe, but try to double press on two viewers at the same time ! + static QTime doublePress; + + if (modifiers == playPathKeyboardModifiers()) { + int elapsed = doublePress.restart(); + if ((elapsed < 250) && (index == previousPathId_)) + camera()->resetPath(index); + else { + // Stop previous interpolation before starting a new one. + if (index != previousPathId_) { + KeyFrameInterpolator *previous = + camera()->keyFrameInterpolator(previousPathId_); + if ((previous) && (previous->interpolationIsStarted())) + previous->resetInterpolation(); + } + camera()->playPath(index); + } + previousPathId_ = index; + } else if (modifiers == addKeyFrameKeyboardModifiers()) { + int elapsed = doublePress.restart(); + if ((elapsed < 250) && (index == previousPathId_)) { + if (camera()->keyFrameInterpolator(index)) { + disconnect(camera()->keyFrameInterpolator(index), + SIGNAL(interpolated()), this, SLOT(update())); + if (camera()->keyFrameInterpolator(index)->numberOfKeyFrames() > 1) + displayMessage( + tr("Path %1 deleted", "Feedback message").arg(index)); + else + displayMessage( + tr("Position %1 deleted", "Feedback message").arg(index)); + camera()->deletePath(index); + } + } else { + bool nullBefore = (camera()->keyFrameInterpolator(index) == NULL); + camera()->addKeyFrameToPath(index); + if (nullBefore) + connect(camera()->keyFrameInterpolator(index), SIGNAL(interpolated()), + SLOT(update())); + int nbKF = camera()->keyFrameInterpolator(index)->numberOfKeyFrames(); + if (nbKF > 1) + displayMessage(tr("Path %1, position %2 added", "Feedback message") + .arg(index) + .arg(nbKF)); + else + displayMessage( + tr("Position %1 saved", "Feedback message").arg(index)); + } + previousPathId_ = index; + } + update(); + } else { + if (isValidShortcutKey(key)) + currentlyPressedKey_ = key; + e->ignore(); + } +} + +CGAL_INLINE_FUNCTION +void QGLViewer::keyReleaseEvent(QKeyEvent *e) { + if (isValidShortcutKey(e->key())) + currentlyPressedKey_ = Qt::Key(0); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::handleKeyboardAction(KeyboardAction id) { + switch (id) { + case DRAW_AXIS: + toggleAxisIsDrawn(); + break; + case DRAW_GRID: + toggleGridIsDrawn(); + break; + case DISPLAY_FPS: + toggleFPSIsDisplayed(); + break; + case ENABLE_TEXT: + toggleTextIsEnabled(); + break; + case EXIT_VIEWER: + saveStateToFileForAllViewers(); + qApp->closeAllWindows(); + break; + case FULL_SCREEN: + toggleFullScreen(); + break; + case STEREO: + toggleStereoDisplay(); + break; + case ANIMATION: + toggleAnimation(); + break; + case HELP: + help(); + break; + case EDIT_CAMERA: + toggleCameraIsEdited(); + break; + case CAMERA_MODE: + toggleCameraMode(); + displayMessage(cameraIsInRotateMode() + ? tr("Camera in observer mode", "Feedback message") + : tr("Camera in fly mode", "Feedback message")); + break; + + case MOVE_CAMERA_LEFT: + camera()->frame()->translate(camera()->frame()->inverseTransformOf( + Vec(-10.0 * camera()->flySpeed(), 0.0, 0.0))); + update(); + break; + case MOVE_CAMERA_RIGHT: + camera()->frame()->translate(camera()->frame()->inverseTransformOf( + Vec(10.0 * camera()->flySpeed(), 0.0, 0.0))); + update(); + break; + case MOVE_CAMERA_UP: + camera()->frame()->translate(camera()->frame()->inverseTransformOf( + Vec(0.0, 10.0 * camera()->flySpeed(), 0.0))); + update(); + break; + case MOVE_CAMERA_DOWN: + camera()->frame()->translate(camera()->frame()->inverseTransformOf( + Vec(0.0, -10.0 * camera()->flySpeed(), 0.0))); + update(); + break; + + case INCREASE_FLYSPEED: + camera()->setFlySpeed(camera()->flySpeed() * 1.5); + break; + case DECREASE_FLYSPEED: + camera()->setFlySpeed(camera()->flySpeed() / 1.5); + break; + } +} + +/*! Callback method used when the widget size is modified. + +If you overload this method, first call the inherited method. Also called when +the widget is created, before its first display. */ +CGAL_INLINE_FUNCTION +void QGLViewer::resizeGL(int width, int height) { + QOpenGLWidget::resizeGL(width, height); + glViewport(0, 0, GLint(width), GLint(height)); + camera()->setScreenWidthAndHeight(this->width(), this->height()); +} + +////////////////////////////////////////////////////////////////////////// +// K e y b o a r d s h o r t c u t s // +////////////////////////////////////////////////////////////////////////// + +/*! Defines the shortcut() that triggers a given QGLViewer::KeyboardAction. + +Here are some examples: +\code +// Press 'Q' to exit application +setShortcut(EXIT_VIEWER, Qt::Key_Q); + +// Alt+M toggles camera mode +setShortcut(CAMERA_MODE, Qt::ALT + Qt::Key_M); + +// The DISPLAY_FPS action is disabled +setShortcut(DISPLAY_FPS, 0); +\endcode + +Only one shortcut can be assigned to a given QGLViewer::KeyboardAction (new +bindings replace previous ones). If several KeyboardAction are binded to the +same shortcut, only one of them is active. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setShortcut(KeyboardAction action, unsigned int key) { + keyboardBinding_[action] = key; +} + +/*! Returns the keyboard shortcut associated to a given +QGLViewer::KeyboardAction. + +Result is an \c unsigned \c int defined using Qt enumerated values, as in \c +Qt::Key_Q or \c Qt::CTRL + Qt::Key_X. Use Qt::MODIFIER_MASK to separate the key +from the state keys. Returns \c 0 if the KeyboardAction is disabled (not +binded). Set using setShortcut(). + +If you want to define keyboard shortcuts for custom actions (say, open a scene +file), overload keyPressEvent() and then setKeyDescription(). + +These shortcuts and their descriptions are automatically included in the help() +window \c Keyboard tab. + +See the keyboard page for details and default +values and the keyboardAndMouse +example for a practical illustration. */ +CGAL_INLINE_FUNCTION +unsigned int QGLViewer::shortcut(KeyboardAction action) const { + if (keyboardBinding_.contains(action)) + return keyboardBinding_[action]; + else + return 0; +} + +#ifndef DOXYGEN +CGAL_INLINE_FUNCTION +void QGLViewer::setKeyboardAccelerator(KeyboardAction action, + unsigned int key) { + qWarning("setKeyboardAccelerator is deprecated. Use setShortcut instead."); + setShortcut(action, key); +} + +CGAL_INLINE_FUNCTION +unsigned int QGLViewer::keyboardAccelerator(KeyboardAction action) const { + qWarning("keyboardAccelerator is deprecated. Use shortcut instead."); + return shortcut(action); +} +#endif + +/////// Key Frames associated keys /////// + +/*! Returns the keyboard key associated to camera Key Frame path \p index. + +Default values are F1..F12 for indexes 1..12. + +addKeyFrameKeyboardModifiers() (resp. playPathKeyboardModifiers()) define the +state key(s) that must be pressed with this key to add a KeyFrame to (resp. to +play) the associated Key Frame path. If you quickly press twice the pathKey(), +the path is reset (resp. deleted). + +Use camera()->keyFrameInterpolator( \p index ) to retrieve the +KeyFrameInterpolator that defines the path. + +If several keys are binded to a given \p index (see setPathKey()), one of them +is returned. Returns \c 0 if no key is associated with this index. + +See also the keyboard page. */ +CGAL_INLINE_FUNCTION +Qt::Key QGLViewer::pathKey(unsigned int index) const { + for (QMap::ConstIterator it = pathIndex_.begin(), + end = pathIndex_.end(); + it != end; ++it) + if (it.value() == index) + return it.key(); + return Qt::Key(0); +} + +/*! Sets the pathKey() associated with the camera Key Frame path \p index. + +Several keys can be binded to the same \p index. Use a negated \p key value to +delete the binding (the \p index value is then ignored): \code +// Press 'space' to play/pause/add/delete camera path of index 0. +setPathKey(Qt::Key_Space, 0); + +// Remove this binding +setPathKey(-Qt::Key_Space); +\endcode */ +CGAL_INLINE_FUNCTION +void QGLViewer::setPathKey(int key, unsigned int index) { + Qt::Key k = Qt::Key(abs(key)); + if (key < 0) + pathIndex_.remove(k); + else + pathIndex_[k] = index; +} + +/*! Sets the playPathKeyboardModifiers() value. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setPlayPathKeyboardModifiers(Qt::KeyboardModifiers modifiers) { + playPathKeyboardModifiers_ = modifiers; +} + +/*! Sets the addKeyFrameKeyboardModifiers() value. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setAddKeyFrameKeyboardModifiers( + Qt::KeyboardModifiers modifiers) { + addKeyFrameKeyboardModifiers_ = modifiers; +} + +/*! Returns the keyboard modifiers that must be pressed with a pathKey() to add +the current camera position to a KeyFrame path. + +It can be \c Qt::NoModifier, \c Qt::ControlModifier, \c Qt::ShiftModifier, \c +Qt::AltModifier, \c Qt::MetaModifier or a combination of these (using the +bitwise '|' operator). + +Default value is Qt::AltModifier. Defined using +setAddKeyFrameKeyboardModifiers(). + +See also playPathKeyboardModifiers(). */ +CGAL_INLINE_FUNCTION +Qt::KeyboardModifiers QGLViewer::addKeyFrameKeyboardModifiers() const { + return addKeyFrameKeyboardModifiers_; +} + +/*! Returns the keyboard modifiers that must be pressed with a pathKey() to play +a camera KeyFrame path. + +It can be \c Qt::NoModifier, \c Qt::ControlModifier, \c Qt::ShiftModifier, \c +Qt::AltModifier, \c Qt::MetaModifier or a combination of these (using the +bitwise '|' operator). + +Default value is Qt::NoModifier. Defined using setPlayPathKeyboardModifiers(). + +See also addKeyFrameKeyboardModifiers(). */ +CGAL_INLINE_FUNCTION +Qt::KeyboardModifiers QGLViewer::playPathKeyboardModifiers() const { + return playPathKeyboardModifiers_; +} + +#ifndef DOXYGEN +// Deprecated methods +CGAL_INLINE_FUNCTION +Qt::KeyboardModifiers QGLViewer::addKeyFrameStateKey() const { + qWarning("addKeyFrameStateKey has been renamed addKeyFrameKeyboardModifiers"); + return addKeyFrameKeyboardModifiers(); +} + +CGAL_INLINE_FUNCTION +Qt::KeyboardModifiers QGLViewer::playPathStateKey() const { + qWarning("playPathStateKey has been renamed playPathKeyboardModifiers"); + return playPathKeyboardModifiers(); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setAddKeyFrameStateKey(unsigned int buttonState) { + qWarning("setAddKeyFrameStateKey has been renamed " + "setAddKeyFrameKeyboardModifiers"); + setAddKeyFrameKeyboardModifiers(keyboardModifiersFromState(buttonState)); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setPlayPathStateKey(unsigned int buttonState) { + qWarning("setPlayPathStateKey has been renamed setPlayPathKeyboardModifiers"); + setPlayPathKeyboardModifiers(keyboardModifiersFromState(buttonState)); +} + +CGAL_INLINE_FUNCTION +Qt::Key QGLViewer::keyFrameKey(unsigned int index) const { + qWarning("keyFrameKey has been renamed pathKey."); + return pathKey(index); +} + +CGAL_INLINE_FUNCTION +Qt::KeyboardModifiers QGLViewer::playKeyFramePathStateKey() const { + qWarning( + "playKeyFramePathStateKey has been renamed playPathKeyboardModifiers."); + return playPathKeyboardModifiers(); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setKeyFrameKey(unsigned int index, int key) { + qWarning("setKeyFrameKey is deprecated, use setPathKey instead, with swapped " + "parameters."); + setPathKey(key, index); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setPlayKeyFramePathStateKey(unsigned int buttonState) { + qWarning("setPlayKeyFramePathStateKey has been renamed " + "setPlayPathKeyboardModifiers."); + setPlayPathKeyboardModifiers(keyboardModifiersFromState(buttonState)); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// M o u s e b e h a v i o r s t a t e k e y s // +//////////////////////////////////////////////////////////////////////////////// +#ifndef DOXYGEN +/*! This method has been deprecated since version 2.5.0 + +Associates keyboard modifiers to MouseHandler \p handler. + +The \p modifiers parameter is \c Qt::AltModifier, \c Qt::ShiftModifier, \c +Qt::ControlModifier, \c Qt::MetaModifier or a combination of these using the '|' +bitwise operator. + +\e All the \p handler's associated bindings will then need the specified \p +modifiers key(s) to be activated. + +With this code, +\code +setHandlerKeyboardModifiers(QGLViewer::CAMERA, Qt::AltModifier); +setHandlerKeyboardModifiers(QGLViewer::FRAME, Qt::NoModifier); +\endcode +you will have to press the \c Alt key while pressing mouse buttons in order to +move the camera(), while no key will be needed to move the manipulatedFrame(). + +This method has a very basic implementation: every action binded to \p handler +has its keyboard modifier replaced by \p modifiers. If \p handler had some +actions binded to different modifiers, these settings will be lost. You should +hence consider using setMouseBinding() for finer tuning. + +The default binding associates \c Qt::ControlModifier to all the +QGLViewer::FRAME actions and \c Qt::NoModifier to all QGLViewer::CAMERA actions. +See mouse page for details. + +\attention This method calls setMouseBinding(), which ensures that only one +action is binded to a given modifiers. If you want to \e swap the +QGLViewer::CAMERA and QGLViewer::FRAME keyboard modifiers, you have to use a +temporary dummy modifier (as if you were swapping two variables) or else the +first call will overwrite the previous settings: \code +// Associate FRAME with Alt (temporary value) +setHandlerKeyboardModifiers(QGLViewer::FRAME, Qt::AltModifier); +// Control is associated with CAMERA +setHandlerKeyboardModifiers(QGLViewer::CAMERA, Qt::ControlModifier); +// And finally, FRAME can be associated with NoModifier +setHandlerKeyboardModifiers(QGLViewer::FRAME, Qt::NoModifier); +\endcode */ +CGAL_INLINE_FUNCTION +void QGLViewer::setHandlerKeyboardModifiers(MouseHandler handler, + Qt::KeyboardModifiers modifiers) { + qWarning("setHandlerKeyboardModifiers is deprecated, call setMouseBinding() " + "instead"); + + QMap newMouseBinding; + QMap newWheelBinding; + QMap newClickBinding_; + + QMap::Iterator mit; + QMap::Iterator wit; + + // First copy unchanged bindings. + for (mit = mouseBinding_.begin(); mit != mouseBinding_.end(); ++mit) + if ((mit.value().handler != handler) || + (mit.value().action == ZOOM_ON_REGION)) + newMouseBinding[mit.key()] = mit.value(); + + for (wit = wheelBinding_.begin(); wit != wheelBinding_.end(); ++wit) + if (wit.value().handler != handler) + newWheelBinding[wit.key()] = wit.value(); + + // Then, add modified bindings, that can overwrite the previous ones. + for (mit = mouseBinding_.begin(); mit != mouseBinding_.end(); ++mit) + if ((mit.value().handler == handler) && + (mit.value().action != ZOOM_ON_REGION)) { + MouseBindingPrivate mbp(modifiers, mit.key().button, mit.key().key); + newMouseBinding[mbp] = mit.value(); + } + + for (wit = wheelBinding_.begin(); wit != wheelBinding_.end(); ++wit) + if (wit.value().handler == handler) { + WheelBindingPrivate wbp(modifiers, wit.key().key); + newWheelBinding[wbp] = wit.value(); + } + + // Same for button bindings + for (QMap::ConstIterator + cb = clickBinding_.begin(), + end = clickBinding_.end(); + cb != end; ++cb) + if (((handler == CAMERA) && + ((cb.value() == CENTER_SCENE) || (cb.value() == ALIGN_CAMERA))) || + ((handler == FRAME) && + ((cb.value() == CENTER_FRAME) || (cb.value() == ALIGN_FRAME)))) { + ClickBindingPrivate cbp(modifiers, cb.key().button, cb.key().doubleClick, + cb.key().buttonsBefore, cb.key().key); + newClickBinding_[cbp] = cb.value(); + } else + newClickBinding_[cb.key()] = cb.value(); + + mouseBinding_ = newMouseBinding; + wheelBinding_ = newWheelBinding; + clickBinding_ = newClickBinding_; +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setHandlerStateKey(MouseHandler handler, + unsigned int buttonState) { + qWarning("setHandlerStateKey has been renamed setHandlerKeyboardModifiers"); + setHandlerKeyboardModifiers(handler, keyboardModifiersFromState(buttonState)); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseStateKey(MouseHandler handler, + unsigned int buttonState) { + qWarning("setMouseStateKey has been renamed setHandlerKeyboardModifiers."); + setHandlerKeyboardModifiers(handler, keyboardModifiersFromState(buttonState)); +} + +/*! This method is deprecated since version 2.5.0 + + Use setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, MouseHandler, + MouseAction, bool) instead. +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBinding(unsigned int state, MouseHandler handler, + MouseAction action, bool withConstraint) { + qWarning("setMouseBinding(int state, MouseHandler...) is deprecated. Use the " + "modifier/button equivalent"); + setMouseBinding(keyboardModifiersFromState(state), + mouseButtonFromState(state), handler, action, withConstraint); +} +#endif + +/*! Defines a MouseAction binding. + + Same as calling setMouseBinding(Qt::Key, Qt::KeyboardModifiers, + Qt::MouseButton, MouseHandler, MouseAction, bool), with a key value of + Qt::Key(0) (i.e. no regular extra key needs to be pressed to perform this + action). */ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBinding(Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, MouseHandler handler, + MouseAction action, bool withConstraint) { + setMouseBinding(Qt::Key(0), modifiers, button, handler, action, + withConstraint); +} + +/*! Associates a MouseAction to any mouse \p button, while keyboard \p modifiers +and \p key are pressed. The receiver of the mouse events is a MouseHandler +(QGLViewer::CAMERA or QGLViewer::FRAME). + +The parameters should read: when the mouse \p button is pressed, while the +keyboard \p modifiers and \p key are down, activate \p action on \p handler. Use +Qt::NoModifier to indicate that no modifier key is needed, and a \p key value of +0 if no regular key has to be pressed (or simply use +setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButton, MouseHandler, +MouseAction, bool)). + +Use the '|' operator to combine modifiers: +\code +// The R key combined with the Left mouse button rotates the camera in the +screen plane. setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, CAMERA, +SCREEN_ROTATE); + +// Alt + Shift and Left button rotates the manipulatedFrame(). +setMouseBinding(Qt::AltModifier | Qt::ShiftModifier, Qt::LeftButton, FRAME, +ROTATE); \endcode + +If \p withConstraint is \c true (default), the possible +qglviewer::Frame::constraint() of the associated Frame will be enforced during +motion. + +The list of all possible MouseAction, some binding examples and default bindings +are provided in the mouse page. + +See the keyboardAndMouse example +for an illustration. + +If no mouse button is specified, the binding is ignored. If an action was +previously associated with this keyboard and button combination, it is silently +overwritten (call mouseAction() before to check). + +To remove a specific mouse binding, use \p NO_MOUSE_ACTION as the \p action. + +See also setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, ClickAction, +bool, int), setWheelBinding() and clearMouseBindings(). */ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, MouseHandler handler, + MouseAction action, bool withConstraint) { + if ((handler == FRAME) && + ((action == MOVE_FORWARD) || (action == MOVE_BACKWARD) || + (action == ROLL) || (action == LOOK_AROUND) || + (action == ZOOM_ON_REGION))) { + qWarning("Cannot bind %s to FRAME", + mouseActionString(action).toLatin1().constData()); + return; + } + + if (button == Qt::NoButton) { + qWarning("No mouse button specified in setMouseBinding"); + return; + } + + MouseActionPrivate map; + map.handler = handler; + map.action = action; + map.withConstraint = withConstraint; + + MouseBindingPrivate mbp(modifiers, button, key); + if (action == NO_MOUSE_ACTION) + mouseBinding_.remove(mbp); + else + mouseBinding_.insert(mbp, map); + + ClickBindingPrivate cbp(modifiers, button, false, Qt::NoButton, key); + clickBinding_.remove(cbp); +} + +#ifndef DOXYGEN +/*! This method is deprecated since version 2.5.0 + + Use setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, MouseHandler, + MouseAction, bool) instead. +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBinding(unsigned int state, ClickAction action, + bool doubleClick, + Qt::MouseButtons buttonsBefore) { + qWarning("setMouseBinding(int state, ClickAction...) is deprecated. Use the " + "modifier/button equivalent"); + setMouseBinding(keyboardModifiersFromState(state), + mouseButtonFromState(state), action, doubleClick, + buttonsBefore); +} +#endif + +/*! Defines a ClickAction binding. + + Same as calling setMouseBinding(Qt::Key, Qt::KeyboardModifiers, + Qt::MouseButton, ClickAction, bool, Qt::MouseButtons), with a key value of + Qt::Key(0) (i.e. no regular key needs to be pressed to activate this action). + */ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBinding(Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, ClickAction action, + bool doubleClick, + Qt::MouseButtons buttonsBefore) { + setMouseBinding(Qt::Key(0), modifiers, button, action, doubleClick, + buttonsBefore); +} + +/*! Associates a ClickAction to a button and keyboard key and modifier(s) +combination. + +The parameters should read: when \p button is pressed, while the \p modifiers +and \p key keys are down, and possibly as a \p doubleClick, then perform \p +action. Use Qt::NoModifier to indicate that no modifier key is needed, and a \p +key value of 0 if no regular key has to be pressed (or simply use +setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButton, ClickAction, bool, +Qt::MouseButtons)). + +If \p buttonsBefore is specified (valid only when \p doubleClick is \c true), +then this (or these) other mouse button(s) has (have) to be pressed \e before +the double click occurs in order to execute \p action. + +The list of all possible ClickAction, some binding examples and default bindings +are listed in the mouse page. See also the +setMouseBinding() documentation. + +See the keyboardAndMouse example +for an illustration. + +The binding is ignored if Qt::NoButton is specified as \p buttons. + +See also setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, MouseHandler, +MouseAction, bool), setWheelBinding() and clearMouseBindings(). +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, ClickAction action, + bool doubleClick, + Qt::MouseButtons buttonsBefore) { + if ((buttonsBefore != Qt::NoButton) && !doubleClick) { + qWarning("Buttons before is only meaningful when doubleClick is true in " + "setMouseBinding()."); + return; + } + + if (button == Qt::NoButton) { + qWarning("No mouse button specified in setMouseBinding"); + return; + } + + ClickBindingPrivate cbp(modifiers, button, doubleClick, buttonsBefore, key); + + // #CONNECTION performClickAction comment on NO_CLICK_ACTION + if (action == NO_CLICK_ACTION) + clickBinding_.remove(cbp); + else + clickBinding_.insert(cbp, action); + + if ((!doubleClick) && (buttonsBefore == Qt::NoButton)) { + MouseBindingPrivate mbp(modifiers, button, key); + mouseBinding_.remove(mbp); + } +} + +/*! Defines a mouse wheel binding. + + Same as calling setWheelBinding(Qt::Key, Qt::KeyboardModifiers, MouseHandler, + MouseAction, bool), with a key value of Qt::Key(0) (i.e. no regular key needs + to be pressed to activate this action). */ +CGAL_INLINE_FUNCTION +void QGLViewer::setWheelBinding(Qt::KeyboardModifiers modifiers, + MouseHandler handler, MouseAction action, + bool withConstraint) { + setWheelBinding(Qt::Key(0), modifiers, handler, action, withConstraint); +} + +/*! Associates a MouseAction and a MouseHandler to a mouse wheel event. + +This method is very similar to setMouseBinding(), but specific to the wheel. + +In the current implementation only QGLViewer::ZOOM can be associated with +QGLViewer::FRAME, while QGLViewer::CAMERA can receive QGLViewer::ZOOM and +QGLViewer::MOVE_FORWARD. + +The difference between QGLViewer::ZOOM and QGLViewer::MOVE_FORWARD is that +QGLViewer::ZOOM speed depends on the distance to the object, while +QGLViewer::MOVE_FORWARD moves at a constant speed defined by +qglviewer::Camera::flySpeed(). */ +CGAL_INLINE_FUNCTION +void QGLViewer::setWheelBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, + MouseHandler handler, MouseAction action, + bool withConstraint) { + //#CONNECTION# ManipulatedFrame::wheelEvent and + // ManipulatedCameraFrame::wheelEvent switches + if ((action != ZOOM) && (action != MOVE_FORWARD) && + (action != MOVE_BACKWARD) && (action != NO_MOUSE_ACTION)) { + qWarning("Cannot bind %s to wheel", + mouseActionString(action).toLatin1().constData()); + return; + } + + if ((handler == FRAME) && (action != ZOOM) && (action != NO_MOUSE_ACTION)) { + qWarning("Cannot bind %s to FRAME wheel", + mouseActionString(action).toLatin1().constData()); + return; + } + + MouseActionPrivate map; + map.handler = handler; + map.action = action; + map.withConstraint = withConstraint; + + WheelBindingPrivate wbp(modifiers, key); + if (action == NO_MOUSE_ACTION) + wheelBinding_.remove(wbp); + else + wheelBinding_[wbp] = map; +} + +/*! Clears all the default mouse bindings. + +After this call, you will have to use setMouseBinding() and setWheelBinding() to +restore the mouse bindings you are interested in. +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::clearMouseBindings() { + mouseBinding_.clear(); + clickBinding_.clear(); + wheelBinding_.clear(); +} + +/*! Clears all the default keyboard shortcuts. + +After this call, you will have to use setShortcut() to define your own keyboard +shortcuts. +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::clearShortcuts() { + keyboardBinding_.clear(); + pathIndex_.clear(); +} + +/*! This method is deprecated since version 2.5.0 + + Use mouseAction(Qt::Key, Qt::KeyboardModifiers, Qt::MouseButtons) instead. +*/ +CGAL_INLINE_FUNCTION +MouseAction QGLViewer::mouseAction(unsigned int state) const { + qWarning("mouseAction(int state,...) is deprecated. Use the modifier/button " + "equivalent"); + return mouseAction(Qt::Key(0), keyboardModifiersFromState(state), + mouseButtonFromState(state)); +} + +/*! Returns the MouseAction the will be triggered when the mouse \p button is +pressed, while the keyboard \p modifiers and \p key are pressed. + +Returns QGLViewer::NO_MOUSE_ACTION if no action is associated with this +combination. Use 0 for \p key to indicate that no regular key needs to be +pressed. + +For instance, to know which motion corresponds to Alt+LeftButton, do: +\code +MouseAction ma = mouseAction(0, Qt::AltModifier, Qt::LeftButton); +if (ma != QGLViewer::NO_MOUSE_ACTION) ... +\endcode + +Use mouseHandler() to know which object (QGLViewer::CAMERA or QGLViewer::FRAME) +will execute this action. */ +CGAL_INLINE_FUNCTION +MouseAction QGLViewer::mouseAction(Qt::Key key, + Qt::KeyboardModifiers modifiers, + Qt::MouseButton button) const { + MouseBindingPrivate mbp(modifiers, button, key); + if (mouseBinding_.contains(mbp)) + return mouseBinding_[mbp].action; + else + return NO_MOUSE_ACTION; +} + +/*! This method is deprecated since version 2.5.0 + + Use mouseHanler(Qt::Key, Qt::KeyboardModifiers, Qt::MouseButtons) instead. +*/ +CGAL_INLINE_FUNCTION +int QGLViewer::mouseHandler(unsigned int state) const { + qWarning("mouseHandler(int state,...) is deprecated. Use the modifier/button " + "equivalent"); + return mouseHandler(Qt::Key(0), keyboardModifiersFromState(state), + mouseButtonFromState(state)); +} + +/*! Returns the MouseHandler which will be activated when the mouse \p button is +pressed, while the \p modifiers and \p key are pressed. + +If no action is associated with this combination, returns \c -1. Use 0 for \p +key and Qt::NoModifier for \p modifiers to represent the lack of a key press. + +For instance, to know which handler receives the Alt+LeftButton, do: +\code +int mh = mouseHandler(0, Qt::AltModifier, Qt::LeftButton); +if (mh == QGLViewer::CAMERA) ... +\endcode + +Use mouseAction() to know which action (see the MouseAction enum) will be +performed on this handler. */ +CGAL_INLINE_FUNCTION +int QGLViewer::mouseHandler(Qt::Key key, Qt::KeyboardModifiers modifiers, + Qt::MouseButton button) const { + MouseBindingPrivate mbp(modifiers, button, key); + if (mouseBinding_.contains(mbp)) + return mouseBinding_[mbp].handler; + else + return -1; +} + +#ifndef DOXYGEN +/*! This method is deprecated since version 2.5.0 + + Use mouseButtons() and keyboardModifiers() instead. +*/ +CGAL_INLINE_FUNCTION +int QGLViewer::mouseButtonState(MouseHandler handler, MouseAction action, + bool withConstraint) const { + qWarning("mouseButtonState() is deprecated. Use mouseButtons() and " + "keyboardModifiers() instead"); + for (QMap::ConstIterator + it = mouseBinding_.begin(), + end = mouseBinding_.end(); + it != end; ++it) + if ((it.value().handler == handler) && (it.value().action == action) && + (it.value().withConstraint == withConstraint)) + return (int)it.key().modifiers | (int)it.key().button; + + return Qt::NoButton; +} +#endif + +/*! Returns the keyboard state that triggers \p action on \p handler \p +withConstraint using the mouse wheel. + +If such a binding exists, results are stored in the \p key and \p modifiers +parameters. If the MouseAction \p action is not bound, \p key is set to the +illegal -1 value. If several keyboard states trigger the MouseAction, one of +them is returned. + +See also setMouseBinding(), getClickActionBinding() and getMouseActionBinding(). +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::getWheelActionBinding(MouseHandler handler, MouseAction action, + bool withConstraint, Qt::Key &key, + Qt::KeyboardModifiers &modifiers) const { + for (QMap::ConstIterator + it = wheelBinding_.begin(), + end = wheelBinding_.end(); + it != end; ++it) + if ((it.value().handler == handler) && (it.value().action == action) && + (it.value().withConstraint == withConstraint)) { + key = it.key().key; + modifiers = it.key().modifiers; + return; + } + + key = Qt::Key(-1); + modifiers = Qt::NoModifier; +} + +/*! Returns the mouse and keyboard state that triggers \p action on \p handler +\p withConstraint. + +If such a binding exists, results are stored in the \p key, \p modifiers and \p +button parameters. If the MouseAction \p action is not bound, \p button is set +to \c Qt::NoButton. If several mouse and keyboard states trigger the +MouseAction, one of them is returned. + +See also setMouseBinding(), getClickActionBinding() and getWheelActionBinding(). +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::getMouseActionBinding(MouseHandler handler, MouseAction action, + bool withConstraint, Qt::Key &key, + Qt::KeyboardModifiers &modifiers, + Qt::MouseButton &button) const { + for (QMap::ConstIterator + it = mouseBinding_.begin(), + end = mouseBinding_.end(); + it != end; ++it) { + if ((it.value().handler == handler) && (it.value().action == action) && + (it.value().withConstraint == withConstraint)) { + key = it.key().key; + modifiers = it.key().modifiers; + button = it.key().button; + return; + } + } + + key = Qt::Key(0); + modifiers = Qt::NoModifier; + button = Qt::NoButton; +} + +/*! Returns the MouseAction (if any) that is performed when using the wheel, +when the \p modifiers and \p key keyboard keys are pressed. + +Returns NO_MOUSE_ACTION if no such binding has been defined using +setWheelBinding(). + +Same as mouseAction(), but for the wheel action. See also wheelHandler(). +*/ +MouseAction +CGAL_INLINE_FUNCTION +QGLViewer::wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) const { + WheelBindingPrivate wbp(modifiers, key); + if (wheelBinding_.contains(wbp)) + return wheelBinding_[wbp].action; + else + return NO_MOUSE_ACTION; +} + +/*! Returns the MouseHandler (if any) that receives wheel events when the \p + modifiers and \p key keyboard keys are pressed. + + Returns -1 if no no such binding has been defined using setWheelBinding(). See + also wheelAction(). +*/ +CGAL_INLINE_FUNCTION +int QGLViewer::wheelHandler(Qt::Key key, + Qt::KeyboardModifiers modifiers) const { + WheelBindingPrivate wbp(modifiers, key); + if (wheelBinding_.contains(wbp)) + return wheelBinding_[wbp].handler; + else + return -1; +} + +/*! Same as mouseAction(), but for the ClickAction set using setMouseBinding(). + +Returns NO_CLICK_ACTION if no click action is associated with this keyboard and +mouse buttons combination. */ +CGAL_INLINE_FUNCTION +qglviewer::ClickAction +QGLViewer::clickAction(Qt::Key key, Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, bool doubleClick, + Qt::MouseButtons buttonsBefore) const { + ClickBindingPrivate cbp(modifiers, button, doubleClick, buttonsBefore, key); + if (clickBinding_.contains(cbp)) + return clickBinding_[cbp]; + else + return NO_CLICK_ACTION; +} + +#ifndef DOXYGEN +/*! This method is deprecated since version 2.5.0 + + Use wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) instead. */ +MouseAction +CGAL_INLINE_FUNCTION +QGLViewer::wheelAction(Qt::KeyboardModifiers modifiers) const { + qWarning("wheelAction() is deprecated. Use the new wheelAction() method with " + "a key parameter instead"); + return wheelAction(Qt::Key(0), modifiers); +} + +/*! This method is deprecated since version 2.5.0 + + Use wheelHandler(Qt::Key key, Qt::KeyboardModifiers modifiers) instead. */ +CGAL_INLINE_FUNCTION +int QGLViewer::wheelHandler(Qt::KeyboardModifiers modifiers) const { + qWarning("wheelHandler() is deprecated. Use the new wheelHandler() method " + "with a key parameter instead"); + return wheelHandler(Qt::Key(0), modifiers); +} + +/*! This method is deprecated since version 2.5.0 + + Use wheelAction() and wheelHandler() instead. */ +CGAL_INLINE_FUNCTION +unsigned int QGLViewer::wheelButtonState(MouseHandler handler, + MouseAction action, + bool withConstraint) const { + qWarning("wheelButtonState() is deprecated. Use the wheelAction() and " + "wheelHandler() instead"); + for (QMap::ConstIterator + it = wheelBinding_.begin(), + end = wheelBinding_.end(); + it != end; ++it) + if ((it.value().handler == handler) && (it.value().action == action) && + (it.value().withConstraint == withConstraint)) + return it.key().key + it.key().modifiers; + + return -1; +} + +/*! This method is deprecated since version 2.5.0 + + Use clickAction(Qt::KeyboardModifiers, Qt::MouseButtons, bool, + Qt::MouseButtons) instead. +*/ +qglviewer::ClickAction +CGAL_INLINE_FUNCTION +QGLViewer::clickAction(unsigned int state, bool doubleClick, + Qt::MouseButtons buttonsBefore) const { + qWarning("clickAction(int state,...) is deprecated. Use the modifier/button " + "equivalent"); + return clickAction(Qt::Key(0), keyboardModifiersFromState(state), + mouseButtonFromState(state), doubleClick, buttonsBefore); +} + +/*! This method is deprecated since version 2.5.0 + + Use getClickActionState(ClickAction, Qt::Key, Qt::KeyboardModifiers, + Qt::MouseButton, bool, Qt::MouseButtons) instead. +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::getClickButtonState(ClickAction action, unsigned int &state, + bool &doubleClick, + Qt::MouseButtons &buttonsBefore) const { + qWarning("getClickButtonState(int state,...) is deprecated. Use the " + "modifier/button equivalent"); + Qt::KeyboardModifiers modifiers; + Qt::MouseButton button; + Qt::Key key; + getClickActionBinding(action, key, modifiers, button, doubleClick, + buttonsBefore); + state = (unsigned int)modifiers | (unsigned int)button | (unsigned int)key; +} +#endif + +/*! Returns the mouse and keyboard state that triggers \p action. + +If such a binding exists, results are stored in the \p key, \p modifiers, \p +button, \p doubleClick and \p buttonsBefore parameters. If the ClickAction \p +action is not bound, \p button is set to \c Qt::NoButton. If several mouse +buttons trigger in the ClickAction, one of them is returned. + +See also setMouseBinding(), getMouseActionBinding() and getWheelActionBinding(). +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::getClickActionBinding(ClickAction action, Qt::Key &key, + Qt::KeyboardModifiers &modifiers, + Qt::MouseButton &button, + bool &doubleClick, + Qt::MouseButtons &buttonsBefore) const { + for (QMap::ConstIterator + it = clickBinding_.begin(), + end = clickBinding_.end(); + it != end; ++it) + if (it.value() == action) { + modifiers = it.key().modifiers; + button = it.key().button; + doubleClick = it.key().doubleClick; + buttonsBefore = it.key().buttonsBefore; + key = it.key().key; + return; + } + + modifiers = Qt::NoModifier; + button = Qt::NoButton; + doubleClick = false; + buttonsBefore = Qt::NoButton; + key = Qt::Key(0); +} + +/*! This function should be used in conjunction with toggleCameraMode(). It +returns \c true when at least one mouse button is binded to the \c ROTATE +mouseAction. This is crude way of determining which "mode" the camera is in. */ +CGAL_INLINE_FUNCTION +bool QGLViewer::cameraIsInRotateMode() const { + //#CONNECTION# used in toggleCameraMode() and keyboardString() + Qt::Key key; + Qt::KeyboardModifiers modifiers; + Qt::MouseButton button; + getMouseActionBinding(CAMERA, ROTATE, true /*constraint*/, key, modifiers, + button); + return button != Qt::NoButton; +} + +/*! Swaps between two predefined camera mouse bindings. + +The first mode makes the camera observe the scene while revolving around the +qglviewer::Camera::pivotPoint(). The second mode is designed for walkthrough +applications and simulates a flying camera. + +Practically, the three mouse buttons are respectively binded to: +\arg In rotate mode: QGLViewer::ROTATE, QGLViewer::ZOOM, QGLViewer::TRANSLATE. +\arg In fly mode: QGLViewer::MOVE_FORWARD, QGLViewer::LOOK_AROUND, +QGLViewer::MOVE_BACKWARD. + +The current mode is determined by checking if a mouse button is binded to +QGLViewer::ROTATE for the QGLViewer::CAMERA. The state key that was previously +used to move the camera is preserved. */ +CGAL_INLINE_FUNCTION +void QGLViewer::toggleCameraMode() { + Qt::Key key; + Qt::KeyboardModifiers modifiers; + Qt::MouseButton button; + getMouseActionBinding(CAMERA, ROTATE, true /*constraint*/, key, modifiers, + button); + bool rotateMode = button != Qt::NoButton; + + if (!rotateMode) { + getMouseActionBinding(CAMERA, MOVE_FORWARD, true /*constraint*/, key, + modifiers, button); + } + + //#CONNECTION# setDefaultMouseBindings() + if (rotateMode) { + camera()->frame()->updateSceneUpVector(); + camera()->frame()->stopSpinning(); + + setMouseBinding(modifiers, Qt::LeftButton, CAMERA, MOVE_FORWARD); + setMouseBinding(modifiers, Qt::MidButton, CAMERA, LOOK_AROUND); + setMouseBinding(modifiers, Qt::RightButton, CAMERA, MOVE_BACKWARD); + + setMouseBinding(Qt::Key_R, modifiers, Qt::LeftButton, CAMERA, ROLL); + + setMouseBinding(Qt::NoModifier, Qt::LeftButton, NO_CLICK_ACTION, true); + setMouseBinding(Qt::NoModifier, Qt::MidButton, NO_CLICK_ACTION, true); + setMouseBinding(Qt::NoModifier, Qt::RightButton, NO_CLICK_ACTION, true); + + setWheelBinding(modifiers, CAMERA, MOVE_FORWARD); + } else { + // Should stop flyTimer. But unlikely and not easy. + setMouseBinding(modifiers, Qt::LeftButton, CAMERA, ROTATE); + setMouseBinding(modifiers, Qt::MidButton, CAMERA, ZOOM); + setMouseBinding(modifiers, Qt::RightButton, CAMERA, TRANSLATE); + + setMouseBinding(Qt::Key_R, modifiers, Qt::LeftButton, CAMERA, + SCREEN_ROTATE); + + setMouseBinding(Qt::NoModifier, Qt::LeftButton, ALIGN_CAMERA, true); + setMouseBinding(Qt::NoModifier, Qt::MidButton, SHOW_ENTIRE_SCENE, true); + setMouseBinding(Qt::NoModifier, Qt::RightButton, CENTER_SCENE, true); + + setWheelBinding(modifiers, CAMERA, ZOOM); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// M a n i p u l a t e d f r a m e s // +//////////////////////////////////////////////////////////////////////////////// + +/*! Sets the viewer's manipulatedFrame(). + +Several objects can be manipulated simultaneously, as is done the multiSelect example. + +Defining the \e own viewer's camera()->frame() as the manipulatedFrame() is +possible and will result in a classical camera manipulation. See the luxo example for an illustration. + +Note that a qglviewer::ManipulatedCameraFrame can be set as the +manipulatedFrame(): it is possible to manipulate the camera of a first viewer in +a second viewer. */ +CGAL_INLINE_FUNCTION +void QGLViewer::setManipulatedFrame(ManipulatedFrame *frame) { + if (manipulatedFrame()) { + manipulatedFrame()->stopSpinning(); + + if (manipulatedFrame() != camera()->frame()) { + disconnect(manipulatedFrame(), SIGNAL(manipulated()), this, + SLOT(update())); + disconnect(manipulatedFrame(), SIGNAL(spun()), this, SLOT(update())); + } + } + + manipulatedFrame_ = frame; + + manipulatedFrameIsACamera_ = + ((manipulatedFrame() != camera()->frame()) && + (dynamic_cast(manipulatedFrame()) != NULL)); + + if (manipulatedFrame()) { + // Prevent multiple connections, that would result in useless display + // updates + if (manipulatedFrame() != camera()->frame()) { + connect(manipulatedFrame(), SIGNAL(manipulated()), SLOT(update())); + connect(manipulatedFrame(), SIGNAL(spun()), SLOT(update())); + } + } +} + +#ifndef DOXYGEN +//////////////////////////////////////////////////////////////////////////////// +// V i s u a l H i n t s // +//////////////////////////////////////////////////////////////////////////////// +/*! Draws viewer related visual hints. + +CGAL_INLINE_FUNCTION +Displays the new qglviewer::Camera::pivotPoint() when it is changed. See the mouse page for details. Also draws a line between +qglviewer::Camera::pivotPoint() and mouse cursor when the camera is rotated +around the camera Z axis. + +See also setVisualHintsMask() and resetVisualHints(). The hint color is +foregroundColor(). + +\note These methods may become more interesting one day. The current design is +too limited and should be improved when other visual hints must be drawn. + +Limitation : One needs to have access to visualHint_ to overload this method. + +Removed from the documentation for this reason. */ +CGAL_INLINE_FUNCTION +void QGLViewer::drawVisualHints() { + // G r i d + rendering_program.bind(); + vaos[GRID].bind(); + QMatrix4x4 mvpMatrix; + double mat[16]; + camera()->getModelViewProjectionMatrix(mat); + for(int i=0; i < 16; i++) + { + mvpMatrix.data()[i] = (float)mat[i]; + } + QMatrix4x4 mvMatrix; + for(int i=0; i < 16; i++) + { + mvMatrix.data()[i] = camera()->orientation().inverse().matrix()[i]; + } + rendering_program.setUniformValue("mvp_matrix", mvpMatrix); + rendering_program.setUniformValue("color", QColor(Qt::lightGray)); + glDrawArrays(GL_LINES, 0, static_cast(grid_size)); + vaos[GRID].release(); + rendering_program.release(); + + rendering_program_light.bind(); + vaos[GRID_AXIS].bind(); + + rendering_program_light.setUniformValue("mvp_matrix", mvpMatrix); + rendering_program_light.setUniformValue("mv_matrix", mvMatrix); + glDrawArrays(GL_TRIANGLES, 0, static_cast(g_axis_size/9)); + vaos[GRID_AXIS].release(); + + //A x i s + qglviewer::Camera::Type camera_type = camera()->type(); + camera()->setType(qglviewer::Camera::ORTHOGRAPHIC); + for(int i=0; i < 16; i++) + { + mvMatrix.data()[i] = camera()->orientation().inverse().matrix()[i]; + } + mvpMatrix.setToIdentity(); + mvpMatrix.ortho(-1,1,-1,1,-1,1); + mvpMatrix = mvpMatrix*mvMatrix; + rendering_program_light.setUniformValue("mvp_matrix", mvpMatrix); + rendering_program_light.setUniformValue("mv_matrix", mvMatrix); + camera()->setType(camera_type); + vaos[AXIS].bind(); + int viewport[4]; + int scissor[4]; + + // The viewport and the scissor are changed to fit the upper right + // corner. Original values are saved. + glGetIntegerv(GL_VIEWPORT, viewport); + glGetIntegerv(GL_SCISSOR_BOX, scissor); + + // Axis viewport size, in pixels + int size = 100; + glViewport(width()*devicePixelRatio()-size, height()*devicePixelRatio()-size, size, size); + glScissor (width()*devicePixelRatio()-size, height()*devicePixelRatio()-size, size, size); + glDrawArrays(GL_TRIANGLES, 0, static_cast(axis_size / 9)); + // The viewport and the scissor are restored. + glScissor(scissor[0],scissor[1],scissor[2],scissor[3]); + glViewport(viewport[0],viewport[1],viewport[2],viewport[3]); + vaos[AXIS].release(); + rendering_program_light.release(); + + //P i v o t - P o i n t + if (visualHint_ & 1) + { + std::vector vertices; + for(int i=0; i< 4; ++i) + { + float x = (pow(-1, i)*(1-i/2)); + float y = (pow(-1, i)*(i/2)); + vertices.push_back(x); + vertices.push_back(y); + vertices.push_back(0); + } + rendering_program.bind(); + vaos[PIVOT_POINT].bind(); + vbos[Pivot_point].bind(); + vbos[Pivot_point].allocate(vertices.data(),static_cast(vertices.size()*sizeof(float))); + rendering_program.enableAttributeArray("vertex"); + rendering_program.setAttributeBuffer("vertex",GL_FLOAT,0,3); + vbos[Pivot_point].release(); + mvpMatrix.setToIdentity(); + mvpMatrix.ortho(-1,1,-1,1,-1,1); + size=30*devicePixelRatio(); + rendering_program.setUniformValue("mvp_matrix", mvpMatrix); + glViewport((camera()->projectedCoordinatesOf(camera()->pivotPoint()).x-size/2)*devicePixelRatio(), (height() - camera()->projectedCoordinatesOf(camera()->pivotPoint()).y-size/2)*devicePixelRatio(), size, size); + glScissor ((camera()->projectedCoordinatesOf(camera()->pivotPoint()).x-size/2)*devicePixelRatio(), (height() - camera()->projectedCoordinatesOf(camera()->pivotPoint()).y-size/2)*devicePixelRatio(), size, size); + rendering_program.setUniformValue("color", QColor(Qt::black)); + glLineWidth(3.0); + glDrawArrays(GL_LINES, 0, static_cast(4)); + rendering_program.setUniformValue("color", QColor(Qt::white)); + glLineWidth(3.0); + glDrawArrays(GL_LINES, 0, static_cast(4)); + glLineWidth(1.0); + // The viewport and the scissor are restored. + glScissor(scissor[0],scissor[1],scissor[2],scissor[3]); + glViewport(viewport[0],viewport[1],viewport[2],viewport[3]); + vaos[PIVOT_POINT].release(); + rendering_program.release(); + } + +} + +/*! Defines the mask that will be used to drawVisualHints(). The only available +mask is currently 1, corresponding to the display of the +qglviewer::Camera::pivotPoint(). resetVisualHints() is automatically called +after \p delay milliseconds (default is 2 seconds). */ +CGAL_INLINE_FUNCTION +void QGLViewer::setVisualHintsMask(int mask, int delay) { + visualHint_ = visualHint_ | mask; + QTimer::singleShot(delay, this, SLOT(resetVisualHints())); +} + +/*! Reset the mask used by drawVisualHints(). Called by setVisualHintsMask() + * after 2 seconds to reset the display. */ +CGAL_INLINE_FUNCTION +void QGLViewer::resetVisualHints() { visualHint_ = 0; } +#endif + +//////////////////////////////////////////////////////////////////////////////// +// A x i s a n d G r i d d i s p l a y l i s t s // +//////////////////////////////////////////////////////////////////////////////// + +/*! Draws a 3D arrow between the 3D point \p from and the 3D point \p to. +\p data is filled with the three components of a point, then its normal, and then its color, which makes it filled like this: +[P1.x-P1.y-P1.z-N1.x-N1.y-N1.z-C1.r-C1.g-C1.b|P2.x-P2.y-P2.z-N2.x-N2.y-N2.z-C2.r-C2.g-C2.b|...] +*/ +CGAL_INLINE_FUNCTION +void QGLViewer::drawArrow(double r,double R, int prec, qglviewer::Vec from, + qglviewer::Vec to, qglviewer::Vec color, + std::vector &data) { + qglviewer::Vec temp = to-from; + QVector3D dir = QVector3D(temp.x, temp.y, temp.z); + QMatrix4x4 mat; + mat.setToIdentity(); + mat.translate(from.x, from.y, from.z); + mat.scale(dir.length()); + dir.normalize(); + float angle = 0.0; + if(std::sqrt((dir.x()*dir.x()+dir.y()*dir.y())) > 1) + angle = 90.0f; + else + angle =acos(dir.y()/std::sqrt(dir.x()*dir.x()+dir.y()*dir.y()+dir.z()*dir.z()))*180.0/M_PI; + + QVector3D axis; + axis = QVector3D(dir.z(), 0, -dir.x()); + mat.rotate(angle, axis); + + //Head + const float Rf = static_cast(R); + for(int d = 0; d<360; d+= 360/prec) + { + float D = (float) (d * M_PI / 180.); + float a = (float) std::atan(Rf / 0.33); + QVector4D p(0., 1., 0, 1.); + QVector4D n(Rf*sin(D), sin(a), Rf*cos(D), 1.); + QVector4D pR = mat*p; + QVector4D nR = mat*n; + + //point A1 + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back((float)color.x); + data.push_back((float)color.y); + data.push_back((float)color.z); + + //point B1 + p = QVector4D(Rf*sin(D), 0.66f, Rf* cos(D), 1.f); + n = QVector4D(sin(D), sin(a), cos(D), 1.); + pR = mat*p; + nR = mat*n; + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back((float)color.x); + data.push_back((float)color.y); + data.push_back((float)color.z); + //point C1 + D = (d+360/prec)*M_PI/180.0; + p = QVector4D(Rf* sin(D), 0.66f, Rf* cos(D), 1.f); + n = QVector4D(sin(D), sin(a), cos(D), 1.0); + pR = mat*p; + nR = mat*n; + + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back((float)color.x); + data.push_back((float)color.y); + data.push_back((float)color.z); + + } + + //cylinder + //body of the cylinder + const float rf = static_cast(r); + for(int d = 0; d<360; d+= 360/prec) + { + //point A1 + double D = d*M_PI/180.0; + QVector4D p(rf*sin(D), 0.66f, rf*cos(D), 1.f); + QVector4D n(sin(D), 0.f, cos(D), 1.f); + QVector4D pR = mat*p; + QVector4D nR = mat*n; + + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back(color.x); + data.push_back(color.y); + data.push_back(color.z); + //point B1 + p = QVector4D(rf * sin(D),0,rf*cos(D), 1.0); + n = QVector4D(sin(D), 0, cos(D), 1.0); + pR = mat*p; + nR = mat*n; + + + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back(color.x); + data.push_back(color.y); + data.push_back(color.z); + //point C1 + D = (d+360/prec)*M_PI/180.0; + p = QVector4D(rf * sin(D),0,rf*cos(D), 1.0); + n = QVector4D(sin(D), 0, cos(D), 1.0); + pR = mat*p; + nR = mat*n; + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back(color.x); + data.push_back(color.y); + data.push_back(color.z); + //point A2 + D = (d+360/prec)*M_PI/180.0; + + p = QVector4D(rf * sin(D),0,rf*cos(D), 1.0); + n = QVector4D(sin(D), 0, cos(D), 1.0); + pR = mat*p; + nR = mat*n; + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back((float)color.x); + data.push_back((float)color.y); + data.push_back((float)color.z); + //point B2 + p = QVector4D(rf * sin(D), 0.66f, rf*cos(D), 1.f); + n = QVector4D(sin(D), 0, cos(D), 1.0); + pR = mat*p; + nR = mat*n; + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back((float)color.x); + data.push_back((float)color.y); + data.push_back((float)color.z); + //point C2 + D = d*M_PI/180.0; + p = QVector4D(rf * sin(D), 0.66f, rf*cos(D), 1.f); + n = QVector4D(sin(D), 0.f, cos(D), 1.f); + pR = mat*p; + nR = mat*n; + data.push_back(pR.x()); + data.push_back(pR.y()); + data.push_back(pR.z()); + data.push_back(nR.x()); + data.push_back(nR.y()); + data.push_back(nR.z()); + data.push_back(color.x); + data.push_back(color.y); + data.push_back(color.z); + + } +} + +/*! Draws an XYZ axis, with a given size (default is 1.0). + +The axis orientation matches the current modelView matrix state: +three arrows (red, green and blue) of length \p length are drawn along the +positive X, Y and Z directions in the top right corner of the screen. +X arrow is red, Y arrow is green and Z arrow is blue.*/ +CGAL_INLINE_FUNCTION +void QGLViewer::drawAxis(qreal length) { + std::vector data; + data.resize(0); + drawArrow(0.06,0.12,10, qglviewer::Vec(0,0,0),qglviewer::Vec(length,0,0),qglviewer::Vec(1,0,0), data); + drawArrow(0.06,0.12,10, qglviewer::Vec(0,0,0),qglviewer::Vec(0,length,0),qglviewer::Vec(0,1,0), data); + drawArrow(0.06,0.12,10, qglviewer::Vec(0,0,0),qglviewer::Vec(0,0,length),qglviewer::Vec(0,0,1), data); + rendering_program_light.bind(); + vaos[AXIS].bind(); + vbos[Axis].bind(); + vbos[Axis].allocate(data.data(), static_cast(data.size()) * sizeof(float)); + rendering_program_light.enableAttributeArray("vertex"); + rendering_program_light.setAttributeBuffer("vertex",GL_FLOAT,0,3, + static_cast(9*sizeof(float))); + + rendering_program_light.enableAttributeArray("normal"); + rendering_program_light.setAttributeBuffer("normal",GL_FLOAT,3*sizeof(float),3, + static_cast(9*sizeof(float))); + + rendering_program_light.enableAttributeArray("colors"); + rendering_program_light.setAttributeBuffer("colors",GL_FLOAT,6*sizeof(float),3, + static_cast(9*sizeof(float))); + vbos[Axis].release(); + vaos[AXIS].release(); + axis_size = data.size(); + rendering_program_light.release(); +} + +/*! Draws a grid in the XY plane, centered on (0,0,0) (defined in the current +coordinate system). + +\p size (OpenGL units) and \p nbSubdivisions define its geometry.*/ +CGAL_INLINE_FUNCTION +void QGLViewer::drawGrid(qreal size, int nbSubdivisions) { + + //The Grid + std::vector v_Grid; + for (int i=0; i<=nbSubdivisions; ++i) + { + const float pos = size*(2.0*i/nbSubdivisions-1.0); + v_Grid.push_back(pos); + v_Grid.push_back(-size); + v_Grid.push_back(0.0); + + v_Grid.push_back(pos); + v_Grid.push_back(+size); + v_Grid.push_back(0.0); + + v_Grid.push_back(-size); + v_Grid.push_back(pos); + v_Grid.push_back(0.0); + + v_Grid.push_back( size); + v_Grid.push_back( pos); + v_Grid.push_back( 0.0); + } + rendering_program.bind(); + vaos[GRID].bind(); + vbos[Grid].bind(); + vbos[Grid].allocate(v_Grid.data(),static_cast(v_Grid.size()*sizeof(float))); + rendering_program.enableAttributeArray("vertex"); + rendering_program.setAttributeBuffer("vertex",GL_FLOAT,0,3); + vbos[Grid].release(); + vaos[GRID].release(); + rendering_program.release(); + grid_size = v_Grid.size(); + + //The Axis + std::vector d_axis; + d_axis.resize(0); + //d_axis is filled by drawArrow always this way : V.x V.y V.z N.x N.y N.z C.r C.g C.b, so it is possible + //to use a single buffer with offset and stride + drawArrow(0.005*size,0.02*size,10, qglviewer::Vec(0,0,0),qglviewer::Vec(size,0,0),qglviewer::Vec(1,0,0), d_axis); + drawArrow(0.005*size,0.02*size,10, qglviewer::Vec(0,0,0),qglviewer::Vec(0,size,0),qglviewer::Vec(0,1,0), d_axis); + + rendering_program_light.bind(); + vaos[GRID_AXIS].bind(); + vbos[Grid_axis].bind(); + vbos[Grid_axis].allocate(d_axis.data(), static_cast(d_axis.size()) * sizeof(float)); + rendering_program_light.enableAttributeArray("vertex"); + rendering_program_light.setAttributeBuffer("vertex",GL_FLOAT,0,3, + static_cast(9*sizeof(float))); + + rendering_program_light.enableAttributeArray("normal"); + rendering_program_light.setAttributeBuffer("normal",GL_FLOAT,3*sizeof(float),3, + static_cast(9*sizeof(float))); + + rendering_program_light.enableAttributeArray("colors"); + rendering_program_light.setAttributeBuffer("colors",GL_FLOAT,6*sizeof(float),3, + static_cast(9*sizeof(float))); + vbos[Grid_axis].release(); + + vaos[GRID_AXIS].release(); + rendering_program_light.release(); + + g_axis_size = d_axis.size(); +} + +//////////////////////////////////////////////////////////////////////////////// +// S t a t i c m e t h o d s : Q G L V i e w e r P o o l // +//////////////////////////////////////////////////////////////////////////////// + +/*! saveStateToFile() is called on all the QGLViewers using the QGLViewerPool(). + */ +CGAL_INLINE_FUNCTION +void QGLViewer::saveStateToFileForAllViewers() { + Q_FOREACH (QGLViewer *viewer, QGLViewer::QGLViewerPool()) { + if (viewer) + viewer->saveStateToFile(); + } +} + +////////////////////////////////////////////////////////////////////////// +// S a v e s t a t e b e t w e e n s e s s i o n s // +////////////////////////////////////////////////////////////////////////// + +/*! Returns the state file name. Default value is \c .qglviewer.xml. + +This is the name of the XML file where saveStateToFile() saves the viewer state +(camera state, widget geometry, display flags... see domElement()) on exit. Use +restoreStateFromFile() to restore this state later (usually in your init() +method). + +Setting this value to \c QString::null will disable the automatic state file +saving that normally occurs on exit. + +If more than one viewer are created by the application, this function will +return a numbered file name (as in ".qglviewer1.xml", ".qglviewer2.xml"... using +QGLViewer::QGLViewerIndex()) for extra viewers. Each viewer will then read back +its own information in restoreStateFromFile(), provided that the viewers are +created in the same order, which is usually the case. */ +CGAL_INLINE_FUNCTION +QString QGLViewer::stateFileName() const { + QString name = stateFileName_; + + if (!name.isEmpty() && QGLViewer::QGLViewerIndex(this) > 0) { + QFileInfo fi(name); + if (fi.suffix().isEmpty()) + name += QString::number(QGLViewer::QGLViewerIndex(this)); + else + name = fi.absolutePath() + '/' + fi.completeBaseName() + + QString::number(QGLViewer::QGLViewerIndex(this)) + "." + + fi.suffix(); + } + + return name; +} + +/*! Saves in stateFileName() an XML representation of the QGLViewer state, +obtained from domElement(). + +Use restoreStateFromFile() to restore this viewer state. + +This method is automatically called when a viewer is closed (using Escape or +using the window's upper right \c x close button). setStateFileName() to \c +QString::null to prevent this. */ +CGAL_INLINE_FUNCTION +void QGLViewer::saveStateToFile() { + QString name = stateFileName(); + + if (name.isEmpty()) + return; + + QFileInfo fileInfo(name); + + if (fileInfo.isDir()) { + QMessageBox::warning( + this, tr("Save to file error", "Message box window title"), + tr("State file name (%1) references a directory instead of a file.") + .arg(name)); + return; + } + + const QString dirName = fileInfo.absolutePath(); + if (!QFileInfo(dirName).exists()) { + QDir dir; + if (!(dir.mkdir(dirName))) { + QMessageBox::warning(this, + tr("Save to file error", "Message box window title"), + tr("Unable to create directory %1").arg(dirName)); + return; + } + } + + // Write the DOM tree to file + QFile f(name); + if (f.open(QIODevice::WriteOnly)) { + QTextStream out(&f); + QDomDocument doc("QGLVIEWER"); + doc.appendChild(domElement("QGLViewer", doc)); + doc.save(out, 2); + f.flush(); + f.close(); + } else + QMessageBox::warning( + this, tr("Save to file error", "Message box window title"), + tr("Unable to save to file %1").arg(name) + ":\n" + f.errorString()); +} + +/*! Restores the QGLViewer state from the stateFileName() file using +initFromDOMElement(). + +States are saved using saveStateToFile(), which is automatically called on +viewer exit. + +Returns \c true when the restoration is successful. Possible problems are an non +existing or unreadable stateFileName() file, an empty stateFileName() or an XML +syntax error. + +A manipulatedFrame() should be defined \e before calling this method, so that +its state can be restored. Initialization code put \e after this function will +override saved values: \code void Viewer::init() +{ +// Default initialization goes here (including the declaration of a possible +manipulatedFrame). + +if (!restoreStateFromFile()) +showEntireScene(); // Previous state cannot be restored: fit camera to scene. + +// Specific initialization that overrides file savings goes here. +} +\endcode */ +CGAL_INLINE_FUNCTION +bool QGLViewer::restoreStateFromFile() { + QString name = stateFileName(); + + if (name.isEmpty()) + return false; + + QFileInfo fileInfo(name); + + if (!fileInfo.isFile()) + // No warning since it would be displayed at first start. + return false; + + if (!fileInfo.isReadable()) { + QMessageBox::warning( + this, tr("Problem in state restoration", "Message box window title"), + tr("File %1 is not readable.").arg(name)); + return false; + } + + // Read the DOM tree form file + QFile f(name); + if (f.open(QIODevice::ReadOnly)) { + QDomDocument doc; + doc.setContent(&f); + f.close(); + QDomElement main = doc.documentElement(); + initFromDOMElement(main); + } else { + QMessageBox::warning( + this, tr("Open file error", "Message box window title"), + tr("Unable to open file %1").arg(name) + ":\n" + f.errorString()); + return false; + } + + return true; +} + +/*! Returns an XML \c QDomElement that represents the QGLViewer. + +Used by saveStateToFile(). restoreStateFromFile() uses initFromDOMElement() to +restore the QGLViewer state from the resulting \c QDomElement. + +\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument +factory used to create QDomElement. + +The created QDomElement contains state values (axisIsDrawn(), FPSIsDisplayed(), +isFullScreen()...), viewer geometry, as well as camera() (see +qglviewer::Camera::domElement()) and manipulatedFrame() (if defined, see +qglviewer::ManipulatedFrame::domElement()) states. + +Overload this method to add your own attributes to the state file: +\code +CGAL_INLINE_FUNCTION +QDomElement Viewer::domElement(const QString& name, QDomDocument& document) +const +{ +// Creates a custom node for a light +QDomElement de = document.createElement("Light"); +de.setAttribute("state", (lightIsOn()?"on":"off")); +// Note the include of the ManipulatedFrame domElement method. +de.appendChild(lightManipulatedFrame()->domElement("LightFrame", document)); + +// Get default state domElement and append custom node +CGAL_INLINE_FUNCTION +QDomElement res = QGLViewer::domElement(name, document); +res.appendChild(de); +return res; +} +\endcode +See initFromDOMElement() for the associated restoration code. + +\attention For the manipulatedFrame(), qglviewer::Frame::constraint() and +qglviewer::Frame::referenceFrame() are not saved. See +qglviewer::Frame::domElement(). */ +CGAL_INLINE_FUNCTION +QDomElement QGLViewer::domElement(const QString &name, + QDomDocument &document) const { + QDomElement de = document.createElement(name); + de.setAttribute("version", QGLViewerVersionString()); + + QDomElement stateNode = document.createElement("State"); + // hasMouseTracking() is not saved + stateNode.appendChild(DomUtils::QColorDomElement( + foregroundColor(), "foregroundColor", document)); + stateNode.appendChild(DomUtils::QColorDomElement( + backgroundColor(), "backgroundColor", document)); + DomUtils::setBoolAttribute(stateNode, "stereo", displaysInStereo()); + // Revolve or fly camera mode is not saved + de.appendChild(stateNode); + + QDomElement displayNode = document.createElement("Display"); + DomUtils::setBoolAttribute(displayNode, "axisIsDrawn", axisIsDrawn()); + DomUtils::setBoolAttribute(displayNode, "gridIsDrawn", gridIsDrawn()); + DomUtils::setBoolAttribute(displayNode, "FPSIsDisplayed", FPSIsDisplayed()); + DomUtils::setBoolAttribute(displayNode, "cameraIsEdited", cameraIsEdited()); + // textIsEnabled() is not saved + de.appendChild(displayNode); + + QDomElement geometryNode = document.createElement("Geometry"); + DomUtils::setBoolAttribute(geometryNode, "fullScreen", isFullScreen()); + if (isFullScreen()) { + geometryNode.setAttribute("prevPosX", QString::number(prevPos_.x())); + geometryNode.setAttribute("prevPosY", QString::number(prevPos_.y())); + } else { + QWidget *tlw = topLevelWidget(); + geometryNode.setAttribute("width", QString::number(tlw->width())); + geometryNode.setAttribute("height", QString::number(tlw->height())); + geometryNode.setAttribute("posX", QString::number(tlw->pos().x())); + geometryNode.setAttribute("posY", QString::number(tlw->pos().y())); + } + de.appendChild(geometryNode); + + // Restore original Camera zClippingCoefficient before saving. + if (cameraIsEdited()) + camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_); + de.appendChild(camera()->domElement("Camera", document)); + if (cameraIsEdited()) + // #CONNECTION# 5.0 from setCameraIsEdited() + camera()->setZClippingCoefficient(5.0); + + if (manipulatedFrame()) + de.appendChild( + manipulatedFrame()->domElement("ManipulatedFrame", document)); + + return de; +} + +/*! Restores the QGLViewer state from a \c QDomElement created by domElement(). + +Used by restoreStateFromFile() to restore the QGLViewer state from a file. + +Overload this method to retrieve custom attributes from the QGLViewer state +file. This code corresponds to the one given in the domElement() documentation: +\code +CGAL_INLINE_FUNCTION +void Viewer::initFromDOMElement(const QDomElement& element) +{ +// Restore standard state +CGAL_INLINE_FUNCTION +QGLViewer::initFromDOMElement(element); + +QDomElement child=element.firstChild().toElement(); +while (!child.isNull()) +{ +if (child.tagName() == "Light") +{ +if (child.hasAttribute("state")) +setLightOn(child.attribute("state").toLower() == "on"); + +// Assumes there is only one child. Otherwise you need to parse child's children +recursively. QDomElement lf = child.firstChild().toElement(); if (!lf.isNull() +&& lf.tagName() == "LightFrame") +lightManipulatedFrame()->initFromDomElement(lf); +} +child = child.nextSibling().toElement(); +} +} +\endcode + +CGAL_INLINE_FUNCTION +See also qglviewer::Camera::initFromDOMElement(), +qglviewer::ManipulatedFrame::initFromDOMElement(). + +\note The manipulatedFrame() \e pointer is not modified by this method. If +defined, its state is simply set from the \p element values. */ +CGAL_INLINE_FUNCTION +void QGLViewer::initFromDOMElement(const QDomElement &element) { + const QString version = element.attribute("version"); + // if (version != QGLViewerVersionString()) + if (version[0] != '2') + // Patches for previous versions should go here when the state file syntax + // is modified. + qWarning("State file created using QGLViewer version %s may not be " + "correctly read.", + version.toLatin1().constData()); + + QDomElement child = element.firstChild().toElement(); + bool tmpCameraIsEdited = cameraIsEdited(); + while (!child.isNull()) { + if (child.tagName() == "State") { + // #CONNECTION# default values from defaultConstructor() + // setMouseTracking(DomUtils::boolFromDom(child, "mouseTracking", false)); + setStereoDisplay(DomUtils::boolFromDom(child, "stereo", false)); + // if ((child.attribute("cameraMode", "revolve") == "fly") && + // (cameraIsInRevolveMode())) toggleCameraMode(); + + QDomElement ch = child.firstChild().toElement(); + while (!ch.isNull()) { + if (ch.tagName() == "foregroundColor") + setForegroundColor(DomUtils::QColorFromDom(ch)); + if (ch.tagName() == "backgroundColor") + setBackgroundColor(DomUtils::QColorFromDom(ch)); + ch = ch.nextSibling().toElement(); + } + } + + if (child.tagName() == "Display") { + // #CONNECTION# default values from defaultConstructor() + setAxisIsDrawn(DomUtils::boolFromDom(child, "axisIsDrawn", false)); + setGridIsDrawn(DomUtils::boolFromDom(child, "gridIsDrawn", false)); + setFPSIsDisplayed(DomUtils::boolFromDom(child, "FPSIsDisplayed", false)); + // See comment below. + tmpCameraIsEdited = DomUtils::boolFromDom(child, "cameraIsEdited", false); + // setTextIsEnabled(DomUtils::boolFromDom(child, "textIsEnabled", true)); + } + + if (child.tagName() == "Geometry") { + setFullScreen(DomUtils::boolFromDom(child, "fullScreen", false)); + + if (isFullScreen()) { + prevPos_.setX(DomUtils::intFromDom(child, "prevPosX", 0)); + prevPos_.setY(DomUtils::intFromDom(child, "prevPosY", 0)); + } else { + int width = DomUtils::intFromDom(child, "width", 600); + int height = DomUtils::intFromDom(child, "height", 400); + topLevelWidget()->resize(width, height); + camera()->setScreenWidthAndHeight(this->width(), this->height()); + + QPoint pos; + pos.setX(DomUtils::intFromDom(child, "posX", 0)); + pos.setY(DomUtils::intFromDom(child, "posY", 0)); + topLevelWidget()->move(pos); + } + } + + if (child.tagName() == "Camera") { + connectAllCameraKFIInterpolatedSignals(false); + camera()->initFromDOMElement(child); + connectAllCameraKFIInterpolatedSignals(); + } + + if ((child.tagName() == "ManipulatedFrame") && (manipulatedFrame())) + manipulatedFrame()->initFromDOMElement(child); + + child = child.nextSibling().toElement(); + } + + // The Camera always stores its "real" zClippingCoef in domElement(). If it is + // edited, its "real" coef must be saved and the coef set to 5.0, as is done + // in setCameraIsEdited(). BUT : Camera and Display are read in an arbitrary + // order. We must initialize Camera's "real" coef BEFORE calling + // setCameraIsEdited. Hence this temp cameraIsEdited and delayed call + cameraIsEdited_ = tmpCameraIsEdited; + if (cameraIsEdited_) { + previousCameraZClippingCoefficient_ = camera()->zClippingCoefficient(); + // #CONNECTION# 5.0 from setCameraIsEdited. + camera()->setZClippingCoefficient(5.0); + } +} + +#ifndef DOXYGEN +/*! This method is deprecated since version 1.3.9-5. Use saveStateToFile() and +setStateFileName() instead. */ +CGAL_INLINE_FUNCTION +void QGLViewer::saveToFile(const QString &fileName) { + if (!fileName.isEmpty()) + setStateFileName(fileName); + + qWarning("saveToFile() is deprecated, use saveStateToFile() instead."); + saveStateToFile(); +} + +/*! This function is deprecated since version 1.3.9-5. Use +restoreStateFromFile() and setStateFileName() instead. */ +CGAL_INLINE_FUNCTION +bool QGLViewer::restoreFromFile(const QString &fileName) { + if (!fileName.isEmpty()) + setStateFileName(fileName); + + qWarning( + "restoreFromFile() is deprecated, use restoreStateFromFile() instead."); + return restoreStateFromFile(); +} +#endif + + +CGAL_INLINE_FUNCTION +void QGLViewer::copyBufferToTexture(GLint , GLenum ) { +} + +/*! Returns the texture id of the texture created by copyBufferToTexture(). + +Use glBindTexture() to use this texture. Note that this is already done by +copyBufferToTexture(). + +Returns \c 0 is copyBufferToTexture() was never called or if the texure was +deleted using glDeleteTextures() since then. */ +CGAL_INLINE_FUNCTION +GLuint QGLViewer::bufferTextureId() const { + return 0; +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setOffset(qglviewer::Vec offset) +{ + this->_offset = offset; +} + +CGAL_INLINE_FUNCTION +qglviewer::Vec QGLViewer::offset()const +{ + return _offset; +} + +CGAL_INLINE_FUNCTION +qreal QGLViewer::sceneRadius() const { return camera()->sceneRadius(); } + +CGAL_INLINE_FUNCTION +qglviewer::Vec QGLViewer::sceneCenter() const { return camera()->sceneCenter(); } + +CGAL_INLINE_FUNCTION +void QGLViewer::setSceneRadius(qreal radius) { + camera()->setSceneRadius(radius); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setSceneCenter(const qglviewer::Vec ¢er) { + camera()->setSceneCenter(center); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::setSceneBoundingBox(const qglviewer::Vec &min, + const qglviewer::Vec &max) { + camera()->setSceneBoundingBox(min + offset(), max + offset()); +} + +CGAL_INLINE_FUNCTION +void QGLViewer::showEntireScene() { + camera()->showEntireScene(); + update(); +} diff --git a/GraphicsView/include/CGAL/Qt/qglviewer_impl_list.h b/GraphicsView/include/CGAL/Qt/qglviewer_impl_list.h new file mode 100644 index 00000000000..c4de925806e --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/qglviewer_impl_list.h @@ -0,0 +1,13 @@ +#ifndef QGLVIEWER_IMPL_LIST_H +#define QGLVIEWER_IMPL_LIST_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif diff --git a/GraphicsView/include/CGAL/Qt/quaternion.h b/GraphicsView/include/CGAL/Qt/quaternion.h index 9d0f8d145e0..c8c19faf3d4 100644 --- a/GraphicsView/include/CGAL/Qt/quaternion.h +++ b/GraphicsView/include/CGAL/Qt/quaternion.h @@ -22,7 +22,7 @@ #ifndef QGLVIEWER_QUATERNION_H #define QGLVIEWER_QUATERNION_H - +#include #include #include #include diff --git a/GraphicsView/include/CGAL/Qt/quaternion_impl.h b/GraphicsView/include/CGAL/Qt/quaternion_impl.h new file mode 100644 index 00000000000..e29a8402b84 --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/quaternion_impl.h @@ -0,0 +1,566 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include +#include +#include // RAND_MAX + +// All the methods are declared inline in Quaternion.h +using namespace qglviewer; +using namespace std; + +/*! Constructs a Quaternion that will rotate from the \p from direction to the +\p to direction. + +Note that this rotation is not uniquely defined. The selected axis is usually +orthogonal to \p from and \p to, minimizing the rotation angle. This method is +robust and can handle small or almost identical vectors. */ +CGAL_INLINE_FUNCTION +Quaternion::Quaternion(const Vec &from, const Vec &to) { + const qreal epsilon = 1E-10; + + const qreal fromSqNorm = from.squaredNorm(); + const qreal toSqNorm = to.squaredNorm(); + // Identity Quaternion when one vector is null + if ((fromSqNorm < epsilon) || (toSqNorm < epsilon)) { + q[0] = q[1] = q[2] = 0.0; + q[3] = 1.0; + } else { + Vec axis = cross(from, to); + const qreal axisSqNorm = axis.squaredNorm(); + + // Aligned vectors, pick any axis, not aligned with from or to + if (axisSqNorm < epsilon) + axis = from.orthogonalVec(); + + qreal angle = asin(sqrt(axisSqNorm / (fromSqNorm * toSqNorm))); + + if (from * to < 0.0) + angle = M_PI - angle; + + setAxisAngle(axis, angle); + } +} + +/*! Returns the image of \p v by the Quaternion inverse() rotation. + +rotate() performs an inverse transformation. Same as inverse().rotate(v). */ +CGAL_INLINE_FUNCTION +Vec Quaternion::inverseRotate(const Vec &v) const { + return inverse().rotate(v); +} + +/*! Returns the image of \p v by the Quaternion rotation. + +See also inverseRotate() and operator*(const Quaternion&, const Vec&). */ +CGAL_INLINE_FUNCTION +Vec Quaternion::rotate(const Vec &v) const { + const qreal q00 = 2.0 * q[0] * q[0]; + const qreal q11 = 2.0 * q[1] * q[1]; + const qreal q22 = 2.0 * q[2] * q[2]; + + const qreal q01 = 2.0 * q[0] * q[1]; + const qreal q02 = 2.0 * q[0] * q[2]; + const qreal q03 = 2.0 * q[0] * q[3]; + + const qreal q12 = 2.0 * q[1] * q[2]; + const qreal q13 = 2.0 * q[1] * q[3]; + + const qreal q23 = 2.0 * q[2] * q[3]; + + return Vec((1.0 - q11 - q22) * v[0] + (q01 - q23) * v[1] + (q02 + q13) * v[2], + (q01 + q23) * v[0] + (1.0 - q22 - q00) * v[1] + (q12 - q03) * v[2], + (q02 - q13) * v[0] + (q12 + q03) * v[1] + + (1.0 - q11 - q00) * v[2]); +} + +/*! Set the Quaternion from a (supposedly correct) 3x3 rotation matrix. + + The matrix is expressed in European format: its three \e columns are the + images by the rotation of the three vectors of an orthogonal basis. Note that + OpenGL uses a symmetric representation for its matrices. + + setFromRotatedBasis() sets a Quaternion from the three axis of a rotated + frame. It actually fills the three columns of a matrix with these rotated + basis vectors and calls this method. */ +CGAL_INLINE_FUNCTION +void Quaternion::setFromRotationMatrix(const qreal m[3][3]) { + // Compute one plus the trace of the matrix + const qreal onePlusTrace = 1.0 + m[0][0] + m[1][1] + m[2][2]; + + if (onePlusTrace > 1E-5) { + // Direct computation + const qreal s = sqrt(onePlusTrace) * 2.0; + q[0] = (m[2][1] - m[1][2]) / s; + q[1] = (m[0][2] - m[2][0]) / s; + q[2] = (m[1][0] - m[0][1]) / s; + q[3] = 0.25 * s; + } else { + // Computation depends on major diagonal term + if ((m[0][0] > m[1][1]) & (m[0][0] > m[2][2])) { + const qreal s = sqrt(1.0 + m[0][0] - m[1][1] - m[2][2]) * 2.0; + q[0] = 0.25 * s; + q[1] = (m[0][1] + m[1][0]) / s; + q[2] = (m[0][2] + m[2][0]) / s; + q[3] = (m[1][2] - m[2][1]) / s; + } else if (m[1][1] > m[2][2]) { + const qreal s = sqrt(1.0 + m[1][1] - m[0][0] - m[2][2]) * 2.0; + q[0] = (m[0][1] + m[1][0]) / s; + q[1] = 0.25 * s; + q[2] = (m[1][2] + m[2][1]) / s; + q[3] = (m[0][2] - m[2][0]) / s; + } else { + const qreal s = sqrt(1.0 + m[2][2] - m[0][0] - m[1][1]) * 2.0; + q[0] = (m[0][2] + m[2][0]) / s; + q[1] = (m[1][2] + m[2][1]) / s; + q[2] = 0.25 * s; + q[3] = (m[0][1] - m[1][0]) / s; + } + } + normalize(); +} + +#ifndef DOXYGEN +CGAL_INLINE_FUNCTION +void Quaternion::setFromRotatedBase(const Vec &X, const Vec &Y, const Vec &Z) { + qWarning("setFromRotatedBase is deprecated, use setFromRotatedBasis instead"); + setFromRotatedBasis(X, Y, Z); +} +#endif + +/*! Sets the Quaternion from the three rotated vectors of an orthogonal basis. + + The three vectors do not have to be normalized but must be orthogonal and + direct (X^Y=k*Z, with k>0). + + \code + Quaternion q; + q.setFromRotatedBasis(X, Y, Z); + // Now q.rotate(Vec(1,0,0)) == X and q.inverseRotate(X) == Vec(1,0,0) + // Same goes for Y and Z with Vec(0,1,0) and Vec(0,0,1). + \endcode + + See also setFromRotationMatrix() and Quaternion(const Vec&, const Vec&). */ +CGAL_INLINE_FUNCTION +void Quaternion::setFromRotatedBasis(const Vec &X, const Vec &Y, const Vec &Z) { + qreal m[3][3]; + qreal normX = X.norm(); + qreal normY = Y.norm(); + qreal normZ = Z.norm(); + + for (int i = 0; i < 3; ++i) { + m[i][0] = X[i] / normX; + m[i][1] = Y[i] / normY; + m[i][2] = Z[i] / normZ; + } + + setFromRotationMatrix(m); +} + +/*! Returns the axis vector and the angle (in radians) of the rotation + represented by the Quaternion. See the axis() and angle() documentations. */ +CGAL_INLINE_FUNCTION +void Quaternion::getAxisAngle(Vec &axis, qreal &angle) const { + angle = 2.0 * acos(q[3]); + axis = Vec(q[0], q[1], q[2]); + const qreal sinus = axis.norm(); + if (sinus > 1E-8) + axis /= sinus; + + if (angle > M_PI) { + angle = 2.0 * qreal(M_PI) - angle; + axis = -axis; + } +} + +/*! Returns the normalized axis direction of the rotation represented by the +Quaternion. + +It is null for an identity Quaternion. See also angle() and getAxisAngle(). */ +CGAL_INLINE_FUNCTION +Vec Quaternion::axis() const { + Vec res = Vec(q[0], q[1], q[2]); + const qreal sinus = res.norm(); + if (sinus > 1E-8) + res /= sinus; + return (acos(q[3]) <= M_PI / 2.0) ? res : -res; +} + +/*! Returns the angle (in radians) of the rotation represented by the + Quaternion. + + This value is always in the range [0-pi]. Larger rotational angles are obtained + by inverting the axis() direction. + + See also axis() and getAxisAngle(). */ +CGAL_INLINE_FUNCTION +qreal Quaternion::angle() const { + const qreal angle = 2.0 * acos(q[3]); + return (angle <= M_PI) ? angle : 2.0 * M_PI - angle; +} + +/*! Returns an XML \c QDomElement that represents the Quaternion. + + \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument + factory used to create QDomElement. + + When output to a file, the resulting QDomElement will look like: + \code + + \endcode + + Use initFromDOMElement() to restore the Quaternion state from the resulting \c + QDomElement. See also the Quaternion(const QDomElement&) constructor. + +CGAL_INLINE_FUNCTION + See the Vec::domElement() documentation for a complete QDomDocument creation + and saving example. + +CGAL_INLINE_FUNCTION + See also Frame::domElement(), Camera::domElement(), +CGAL_INLINE_FUNCTION + KeyFrameInterpolator::domElement()... */ +CGAL_INLINE_FUNCTION +QDomElement Quaternion::domElement(const QString &name, + QDomDocument &document) const { + QDomElement de = document.createElement(name); + de.setAttribute("q0", QString::number(q[0])); + de.setAttribute("q1", QString::number(q[1])); + de.setAttribute("q2", QString::number(q[2])); + de.setAttribute("q3", QString::number(q[3])); + return de; +} + +/*! Restores the Quaternion state from a \c QDomElement created by domElement(). + + The \c QDomElement should contain the \c q0, \c q1 , \c q2 and \c q3 + attributes. If one of these attributes is missing or is not a number, a warning + is displayed and these fields are respectively set to 0.0, 0.0, 0.0 and 1.0 + (identity Quaternion). + + See also the Quaternion(const QDomElement&) constructor. */ +CGAL_INLINE_FUNCTION +void Quaternion::initFromDOMElement(const QDomElement &element) { + Quaternion q(element); + *this = q; +} + +/*! Constructs a Quaternion from a \c QDomElement representing an XML code of + the form \code< anyTagName q0=".." q1=".." q2=".." q3=".." />\endcode + + If one of these attributes is missing or is not a number, a warning is + displayed and the associated value is respectively set to 0, 0, 0 and 1 + (identity Quaternion). + + See also domElement() and initFromDOMElement(). */ +CGAL_INLINE_FUNCTION +Quaternion::Quaternion(const QDomElement &element) { + QStringList attribute; + attribute << "q0" + << "q1" + << "q2" + << "q3"; + for (int i = 0; i < attribute.size(); ++i) + q[i] = DomUtils::qrealFromDom(element, attribute[i], ((i < 3) ? 0.0 : 1.0)); +} + +/*! Returns the Quaternion associated 4x4 OpenGL rotation matrix. + + Use \c glMultMatrixd(q.matrix()) to apply the rotation represented by + Quaternion \c q to the current OpenGL matrix. + + See also getMatrix(), getRotationMatrix() and inverseMatrix(). + + \attention The result is only valid until the next call to matrix(). Use it + immediately (as shown above) or consider using getMatrix() instead. + + \attention The matrix is given in OpenGL format (row-major order) and is the + transpose of the actual mathematical European representation. Consider using + getRotationMatrix() instead. */ +CGAL_INLINE_FUNCTION +const GLdouble *Quaternion::matrix() const { + static GLdouble m[4][4]; + getMatrix(m); + return (const GLdouble *)(m); +} + +/*! Fills \p m with the OpenGL representation of the Quaternion rotation. + +Use matrix() if you do not need to store this matrix and simply want to alter +the current OpenGL matrix. See also getInverseMatrix() and Frame::getMatrix(). +*/ +CGAL_INLINE_FUNCTION +void Quaternion::getMatrix(GLdouble m[4][4]) const { + const qreal q00 = 2.0 * q[0] * q[0]; + const qreal q11 = 2.0 * q[1] * q[1]; + const qreal q22 = 2.0 * q[2] * q[2]; + + const qreal q01 = 2.0 * q[0] * q[1]; + const qreal q02 = 2.0 * q[0] * q[2]; + const qreal q03 = 2.0 * q[0] * q[3]; + + const qreal q12 = 2.0 * q[1] * q[2]; + const qreal q13 = 2.0 * q[1] * q[3]; + + const qreal q23 = 2.0 * q[2] * q[3]; + + m[0][0] = 1.0 - q11 - q22; + m[1][0] = q01 - q23; + m[2][0] = q02 + q13; + + m[0][1] = q01 + q23; + m[1][1] = 1.0 - q22 - q00; + m[2][1] = q12 - q03; + + m[0][2] = q02 - q13; + m[1][2] = q12 + q03; + m[2][2] = 1.0 - q11 - q00; + + m[0][3] = 0.0; + m[1][3] = 0.0; + m[2][3] = 0.0; + + m[3][0] = 0.0; + m[3][1] = 0.0; + m[3][2] = 0.0; + m[3][3] = 1.0; +} + +/*! Same as getMatrix(), but with a \c GLdouble[16] parameter. See also + * getInverseMatrix() and Frame::getMatrix(). */ +CGAL_INLINE_FUNCTION +void Quaternion::getMatrix(GLdouble m[16]) const { + static GLdouble mat[4][4]; + getMatrix(mat); + int count = 0; + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + m[count++] = mat[i][j]; +} + +/*! Fills \p m with the 3x3 rotation matrix associated with the Quaternion. + + See also getInverseRotationMatrix(). + + \attention \p m uses the European mathematical representation of the rotation + matrix. Use matrix() and getMatrix() to retrieve the OpenGL transposed + version. */ +CGAL_INLINE_FUNCTION +void Quaternion::getRotationMatrix(qreal m[3][3]) const { + static GLdouble mat[4][4]; + getMatrix(mat); + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + // Beware of transposition + m[i][j] = qreal(mat[j][i]); +} + +/*! Returns the associated 4x4 OpenGL \e inverse rotation matrix. This is simply + the matrix() of the inverse(). + + \attention The result is only valid until the next call to inverseMatrix(). + Use it immediately (as in \c glMultMatrixd(q.inverseMatrix())) or use + getInverseMatrix() instead. + + \attention The matrix is given in OpenGL format (row-major order) and is the + transpose of the actual mathematical European representation. Consider using + getInverseRotationMatrix() instead. */ +CGAL_INLINE_FUNCTION +const GLdouble *Quaternion::inverseMatrix() const { + static GLdouble m[4][4]; + getInverseMatrix(m); + return (const GLdouble *)(m); +} + +/*! Fills \p m with the OpenGL matrix corresponding to the inverse() rotation. + +Use inverseMatrix() if you do not need to store this matrix and simply want to +alter the current OpenGL matrix. See also getMatrix(). */ +CGAL_INLINE_FUNCTION +void Quaternion::getInverseMatrix(GLdouble m[4][4]) const { + inverse().getMatrix(m); +} + +/*! Same as getInverseMatrix(), but with a \c GLdouble[16] parameter. See also + * getMatrix(). */ +CGAL_INLINE_FUNCTION +void Quaternion::getInverseMatrix(GLdouble m[16]) const { + inverse().getMatrix(m); +} + +/*! \p m is set to the 3x3 \e inverse rotation matrix associated with the + Quaternion. + + \attention This is the classical mathematical rotation matrix. The OpenGL + format uses its transposed version. See inverseMatrix() and getInverseMatrix(). + */ +CGAL_INLINE_FUNCTION +void Quaternion::getInverseRotationMatrix(qreal m[3][3]) const { + static GLdouble mat[4][4]; + getInverseMatrix(mat); + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + // Beware of transposition + m[i][j] = qreal(mat[j][i]); +} + +/*! Returns the slerp interpolation of Quaternions \p a and \p b, at time \p t. + + \p t should range in [0,1]. Result is \p a when \p t=0 and \p b when \p t=1. + + When \p allowFlip is \c true (default) the slerp interpolation will always use + the "shortest path" between the Quaternions' orientations, by "flipping" the + source Quaternion if needed (see negate()). */ +CGAL_INLINE_FUNCTION +Quaternion Quaternion::slerp(const Quaternion &a, const Quaternion &b, qreal t, + bool allowFlip) { + qreal cosAngle = Quaternion::dot(a, b); + + qreal c1, c2; + // Linear interpolation for close orientations + if ((1.0 - fabs(cosAngle)) < 0.01) { + c1 = 1.0 - t; + c2 = t; + } else { + // Spherical interpolation + qreal angle = acos(fabs(cosAngle)); + qreal sinAngle = sin(angle); + c1 = sin(angle * (1.0 - t)) / sinAngle; + c2 = sin(angle * t) / sinAngle; + } + + // Use the shortest path + if (allowFlip && (cosAngle < 0.0)) + c1 = -c1; + + return Quaternion(c1 * a[0] + c2 * b[0], c1 * a[1] + c2 * b[1], + c1 * a[2] + c2 * b[2], c1 * a[3] + c2 * b[3]); +} + +/*! Returns the slerp interpolation of the two Quaternions \p a and \p b, at + time \p t, using tangents \p tgA and \p tgB. + + The resulting Quaternion is "between" \p a and \p b (result is \p a when \p + t=0 and \p b for \p t=1). + + Use squadTangent() to define the Quaternion tangents \p tgA and \p tgB. */ +CGAL_INLINE_FUNCTION +Quaternion Quaternion::squad(const Quaternion &a, const Quaternion &tgA, + const Quaternion &tgB, const Quaternion &b, + qreal t) { + Quaternion ab = Quaternion::slerp(a, b, t); + Quaternion tg = Quaternion::slerp(tgA, tgB, t, false); + return Quaternion::slerp(ab, tg, 2.0 * t * (1.0 - t), false); +} + +/*! Returns the logarithm of the Quaternion. See also exp(). */ +CGAL_INLINE_FUNCTION +Quaternion Quaternion::log() { + qreal len = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]); + + if (len < 1E-6) + return Quaternion(q[0], q[1], q[2], 0.0); + else { + qreal coef = acos(q[3]) / len; + return Quaternion(q[0] * coef, q[1] * coef, q[2] * coef, 0.0); + } +} + +/*! Returns the exponential of the Quaternion. See also log(). */ +CGAL_INLINE_FUNCTION +Quaternion Quaternion::exp() { + qreal theta = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]); + + if (theta < 1E-6) + return Quaternion(q[0], q[1], q[2], cos(theta)); + else { + qreal coef = sin(theta) / theta; + return Quaternion(q[0] * coef, q[1] * coef, q[2] * coef, cos(theta)); + } +} + +/*! Returns log(a. inverse() * b). Useful for squadTangent(). */ +CGAL_INLINE_FUNCTION +Quaternion Quaternion::lnDif(const Quaternion &a, const Quaternion &b) { + Quaternion dif = a.inverse() * b; + dif.normalize(); + return dif.log(); +} + +/*! Returns a tangent Quaternion for \p center, defined by \p before and \p + after Quaternions. + + Useful for smooth spline interpolation of Quaternion with squad() and slerp(). + */ +CGAL_INLINE_FUNCTION +Quaternion Quaternion::squadTangent(const Quaternion &before, + const Quaternion ¢er, + const Quaternion &after) { + Quaternion l1 = Quaternion::lnDif(center, before); + Quaternion l2 = Quaternion::lnDif(center, after); + Quaternion e; + for (int i = 0; i < 4; ++i) + e.q[i] = -0.25 * (l1.q[i] + l2.q[i]); + e = center * (e.exp()); + + // if (Quaternion::dot(e,b) < 0.0) + // e.negate(); + + return e; +} + +CGAL_INLINE_FUNCTION +ostream &operator<<(ostream &o, const Quaternion &Q) { + return o << Q[0] << '\t' << Q[1] << '\t' << Q[2] << '\t' << Q[3]; +} + +/*! Returns a random unit Quaternion. + +You can create a randomly directed unit vector using: +\code +CGAL_INLINE_FUNCTION +Vec randomDir = Quaternion::randomQuaternion() * Vec(1.0, 0.0, 0.0); // or any +other Vec \endcode + +\note This function uses rand() to create pseudo-random numbers and the random +number generator can be initialized using srand().*/ +CGAL_INLINE_FUNCTION +Quaternion Quaternion::randomQuaternion() { + // The rand() function is not very portable and may not be available on your + // system. Add the appropriate include or replace by an other random function + // in case of problem. + qreal seed = rand() / (qreal)RAND_MAX; + qreal r1 = sqrt(1.0 - seed); + qreal r2 = sqrt(seed); + qreal t1 = 2.0 * M_PI * (rand() / (qreal)RAND_MAX); + qreal t2 = 2.0 * M_PI * (rand() / (qreal)RAND_MAX); + return Quaternion(sin(t1) * r1, cos(t1) * r1, sin(t2) * r2, cos(t2) * r2); +} diff --git a/GraphicsView/include/CGAL/Qt/vec_impl.h b/GraphicsView/include/CGAL/Qt/vec_impl.h new file mode 100644 index 00000000000..9102fe9f002 --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/vec_impl.h @@ -0,0 +1,181 @@ +/**************************************************************************** + + Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. + + This file is part of the QGLViewer library version 2.7.0. + + http://www.libqglviewer.com - contact@libqglviewer.com + + This file may be used under the terms of the GNU General Public License + versions 2.0 or 3.0 as published by the Free Software Foundation and + appearing in the LICENSE file included in the packaging of this file. + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + + libQGLViewer uses dual licensing. Commercial/proprietary software must + purchase a libQGLViewer Commercial License. + + This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*****************************************************************************/ + +#ifdef CGAL_HEADER_ONLY +#define CGAL_INLINE_FUNCTION inline + +#include + +#else +#define CGAL_INLINE_FUNCTION +#endif + +#include +#include + +// Most of the methods are declared inline in vec.h + +using namespace qglviewer; +using namespace std; + +/*! Projects the Vec on the axis of direction \p direction that passes through +the origin. + +\p direction does not need to be normalized (but must be non null). */ +CGAL_INLINE_FUNCTION +void Vec::projectOnAxis(const Vec &direction) { +#ifndef QT_NO_DEBUG + if (direction.squaredNorm() < 1.0E-10) + qWarning("Vec::projectOnAxis: axis direction is not normalized (norm=%f).", + direction.norm()); +#endif + + *this = (((*this) * direction) / direction.squaredNorm()) * direction; +} + +/*! Projects the Vec on the plane whose normal is \p normal that passes through +the origin. + +\p normal does not need to be normalized (but must be non null). */ +CGAL_INLINE_FUNCTION +void Vec::projectOnPlane(const Vec &normal) { +#ifndef QT_NO_DEBUG + if (normal.squaredNorm() < 1.0E-10) + qWarning("Vec::projectOnPlane: plane normal is not normalized (norm=%f).", + normal.norm()); +#endif + + *this -= (((*this) * normal) / normal.squaredNorm()) * normal; +} + +/*! Returns a Vec orthogonal to the Vec. Its norm() depends on the Vec, but is + zero only for a null Vec. Note that the function that associates an + orthogonalVec() to a Vec is not continous. */ +CGAL_INLINE_FUNCTION +Vec Vec::orthogonalVec() const { + // Find smallest component. Keep equal case for null values. + if ((fabs(y) >= 0.9 * fabs(x)) && (fabs(z) >= 0.9 * fabs(x))) + return Vec(0.0, -z, y); + else if ((fabs(x) >= 0.9 * fabs(y)) && (fabs(z) >= 0.9 * fabs(y))) + return Vec(-z, 0.0, x); + else + return Vec(-y, x, 0.0); +} + +/*! Constructs a Vec from a \c QDomElement representing an XML code of the form + \code< anyTagName x=".." y=".." z=".." />\endcode + +If one of these attributes is missing or is not a number, a warning is displayed +and the associated value is set to 0.0. + +See also domElement() and initFromDOMElement(). */ +CGAL_INLINE_FUNCTION +Vec::Vec(const QDomElement &element) { + QStringList attribute; + attribute << "x" + << "y" + << "z"; + for (int i = 0; i < attribute.size(); ++i) +#ifdef QGLVIEWER_UNION_NOT_SUPPORTED + this->operator[](i) = DomUtils::qrealFromDom(element, attribute[i], 0.0); +#else + v_[i] = DomUtils::qrealFromDom(element, attribute[i], 0.0); +#endif +} + +/*! Returns an XML \c QDomElement that represents the Vec. + + \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument + factory used to create QDomElement. + + When output to a file, the resulting QDomElement will look like: + \code + + \endcode + + Use initFromDOMElement() to restore the Vec state from the resulting \c + QDomElement. See also the Vec(const QDomElement&) constructor. + + Here is complete example that creates a QDomDocument and saves it into a file: + \code + Vec sunPos; + QDomDocument document("myDocument"); + QDomElement sunElement = document.createElement("Sun"); + document.appendChild(sunElement); + sunElement.setAttribute("brightness", sunBrightness()); + sunElement.appendChild(sunPos.domElement("sunPosition", document)); + // Other additions to the document hierarchy... + + // Save doc document + QFile f("myFile.xml"); + if (f.open(IO_WriteOnly)) + { + QTextStream out(&f); + document.save(out, 2); + f.close(); + } + \endcode + +CGAL_INLINE_FUNCTION + See also Quaternion::domElement(), Frame::domElement(), Camera::domElement()... + */ +CGAL_INLINE_FUNCTION +QDomElement Vec::domElement(const QString &name, QDomDocument &document) const { + QDomElement de = document.createElement(name); + de.setAttribute("x", QString::number(x)); + de.setAttribute("y", QString::number(y)); + de.setAttribute("z", QString::number(z)); + return de; +} + +/*! Restores the Vec state from a \c QDomElement created by domElement(). + + The \c QDomElement should contain \c x, \c y and \c z attributes. If one of + these attributes is missing or is not a number, a warning is displayed and the + associated value is set to 0.0. + + To restore the Vec state from an xml file, use: + \code + // Load DOM from file + QDomDocument doc; + QFile f("myFile.xml"); + if (f.open(IO_ReadOnly)) + { + doc.setContent(&f); + f.close(); + } + // Parse the DOM tree and initialize + QDomElement main=doc.documentElement(); + myVec.initFromDOMElement(main); + \endcode + + See also the Vec(const QDomElement&) constructor. */ +CGAL_INLINE_FUNCTION +void Vec::initFromDOMElement(const QDomElement &element) { + const Vec v(element); + *this = v; +} + +CGAL_INLINE_FUNCTION +ostream &operator<<(ostream &o, const Vec &v) { + return o << v.x << '\t' << v.y << '\t' << v.z; +} diff --git a/GraphicsView/include/CGAL/Qt/viewer_actions.h b/GraphicsView/include/CGAL/Qt/viewer_actions.h new file mode 100644 index 00000000000..96b1042daae --- /dev/null +++ b/GraphicsView/include/CGAL/Qt/viewer_actions.h @@ -0,0 +1,75 @@ +#ifndef VIEWER_ACTIONS_H +#define VIEWER_ACTIONS_H +namespace qglviewer { +/*! Defines the different actions that can be associated with a keyboard +shortcut using setShortcut(). + +See the keyboard page for details. */ +enum KeyboardAction { + DRAW_AXIS, + DRAW_GRID, + DISPLAY_FPS, + ENABLE_TEXT, + EXIT_VIEWER, + CAMERA_MODE, + FULL_SCREEN, + STEREO, + ANIMATION, + HELP, + EDIT_CAMERA, + MOVE_CAMERA_LEFT, + MOVE_CAMERA_RIGHT, + MOVE_CAMERA_UP, + MOVE_CAMERA_DOWN, + INCREASE_FLYSPEED, + DECREASE_FLYSPEED +}; + +/*! Defines the different mouse handlers: camera() or manipulatedFrame(). + +Used by setMouseBinding(), setMouseBinding(Qt::KeyboardModifiers modifiers, +Qt::MouseButtons, ClickAction, bool, int) and setWheelBinding() to define +which handler receives the mouse events. */ +enum MouseHandler { CAMERA, FRAME }; + +/*! Defines the possible actions that can be binded to a mouse click using +setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, ClickAction, bool, +int). + +See the mouse page for details. */ +enum ClickAction { + NO_CLICK_ACTION, + ZOOM_ON_PIXEL, + ZOOM_TO_FIT, + SELECT, + RAP_FROM_PIXEL, + RAP_IS_CENTER, + CENTER_FRAME, + CENTER_SCENE, + SHOW_ENTIRE_SCENE, + ALIGN_FRAME, + ALIGN_CAMERA +}; + +/*! Defines the possible actions that can be binded to a mouse action (a +click, followed by a mouse displacement). + +These actions may be binded to the camera() or to the manipulatedFrame() (see +QGLViewer::MouseHandler) using setMouseBinding(). */ +enum MouseAction { + NO_MOUSE_ACTION, + ROTATE, + ZOOM, + TRANSLATE, + MOVE_FORWARD, + LOOK_AROUND, + MOVE_BACKWARD, + SCREEN_ROTATE, + ROLL, + DRIVE, + SCREEN_TRANSLATE, + ZOOM_ON_REGION +}; + +} +#endif // VIEWER_ACTIONS_H diff --git a/GraphicsView/src/CGAL_Qt5/camera.cpp b/GraphicsView/src/CGAL_Qt5/camera.cpp index 6eba0765391..56220557c63 100644 --- a/GraphicsView/src/CGAL_Qt5/camera.cpp +++ b/GraphicsView/src/CGAL_Qt5/camera.cpp @@ -19,2498 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include -#include -#include -#include +#include -using namespace std; -using namespace qglviewer; - -/*! Default constructor. - - sceneCenter() is set to (0,0,0) and sceneRadius() is set to 1.0. type() is - Camera::PERSPECTIVE, with a \c M_PI/4 fieldOfView(). - - See IODistance(), physicalDistanceToScreen(), physicalScreenWidth() and - focusDistance() documentations for default stereo parameter values. */ -Camera::Camera() - : frame_(NULL), fieldOfView_(M_PI / 4.0), modelViewMatrixIsUpToDate_(false), - projectionMatrixIsUpToDate_(false) { - // #CONNECTION# Camera copy constructor - interpolationKfi_ = new KeyFrameInterpolator; - // Requires the interpolationKfi_ - setFrame(new ManipulatedCameraFrame()); - - // #CONNECTION# All these default values identical in initFromDOMElement. - - // Requires fieldOfView() to define focusDistance() - setSceneRadius(1.0); - - // Initial value (only scaled after this) - orthoCoef_ = tan(fieldOfView() / 2.0); - - // Also defines the pivotPoint(), which changes orthoCoef_. Requires a - // frame(). - setSceneCenter(Vec(0.0, 0.0, 0.0)); - - // Requires fieldOfView() when called with ORTHOGRAPHIC. Attention to - // projectionMatrix_ below. - setType(PERSPECTIVE); - - // #CONNECTION# initFromDOMElement default values - setZNearCoefficient(0.005); - setZClippingCoefficient(sqrt(3.0)); - - // Dummy values - setScreenWidthAndHeight(600, 400); - - // Stereo parameters - setIODistance(0.062); - setPhysicalScreenWidth(0.5); - // focusDistance is set from setFieldOfView() - - // #CONNECTION# Camera copy constructor - for (unsigned short j = 0; j < 16; ++j) { - modelViewMatrix_[j] = ((j % 5 == 0) ? 1.0 : 0.0); - // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost - // everywhere. - projectionMatrix_[j] = 0.0; - } - computeProjectionMatrix(); -} - -/*! Virtual destructor. - - The frame() is deleted, but the different keyFrameInterpolator() are \e not - deleted (in case they are shared). */ -Camera::~Camera() { - delete frame_; - delete interpolationKfi_; -} - -/*! Copy constructor. Performs a deep copy using operator=(). */ -Camera::Camera(const Camera &camera) : QObject(), frame_(NULL) { - // #CONNECTION# Camera constructor - interpolationKfi_ = new KeyFrameInterpolator; - // Requires the interpolationKfi_ - setFrame(new ManipulatedCameraFrame(*camera.frame())); - - for (unsigned short j = 0; j < 16; ++j) { - modelViewMatrix_[j] = ((j % 5 == 0) ? 1.0 : 0.0); - // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost - // everywhere. - projectionMatrix_[j] = 0.0; - } - - (*this) = camera; -} - -/*! Equal operator. - - All the parameters of \p camera are copied. The frame() pointer is not - modified, but its Frame::position() and Frame::orientation() are set to those - of \p camera. - - \attention The Camera screenWidth() and screenHeight() are set to those of \p - camera. If your Camera is associated with a QGLViewer, you should update these - value after the call to this method: \code - *(camera()) = otherCamera; - camera()->setScreenWidthAndHeight(width(), height()); - \endcode - The same applies to sceneCenter() and sceneRadius(), if needed. */ -Camera &Camera::operator=(const Camera &camera) { - setScreenWidthAndHeight(camera.screenWidth(), camera.screenHeight()); - setFieldOfView(camera.fieldOfView()); - setSceneRadius(camera.sceneRadius()); - setSceneCenter(camera.sceneCenter()); - setZNearCoefficient(camera.zNearCoefficient()); - setZClippingCoefficient(camera.zClippingCoefficient()); - setType(camera.type()); - - // Stereo parameters - setIODistance(camera.IODistance()); - setFocusDistance(camera.focusDistance()); - setPhysicalScreenWidth(camera.physicalScreenWidth()); - - orthoCoef_ = camera.orthoCoef_; - projectionMatrixIsUpToDate_ = false; - - // frame_ and interpolationKfi_ pointers are not shared. - frame_->setReferenceFrame(NULL); - frame_->setPosition(camera.position()); - frame_->setOrientation(camera.orientation()); - - interpolationKfi_->resetInterpolation(); - - kfi_ = camera.kfi_; - - computeProjectionMatrix(); - computeModelViewMatrix(); - - return *this; -} - -/*! Sets Camera screenWidth() and screenHeight() (expressed in pixels). - -You should not call this method when the Camera is associated with a QGLViewer, -since the latter automatically updates these values when it is resized (hence -overwritting your values). - -Non-positive dimension are silently replaced by a 1 pixel value to ensure -frustrum coherence. - -If your Camera is used without a QGLViewer (offscreen rendering, shadow maps), -use setAspectRatio() instead to define the projection matrix. */ -void Camera::setScreenWidthAndHeight(int width, int height) { - // Prevent negative and zero dimensions that would cause divisions by zero. - screenWidth_ = width > 0 ? width : 1; - screenHeight_ = height > 0 ? height : 1; - projectionMatrixIsUpToDate_ = false; -} - -/*! Returns the near clipping plane distance used by the Camera projection - matrix. - - The clipping planes' positions depend on the sceneRadius() and sceneCenter() - rather than being fixed small-enough and large-enough values. A good scene - dimension approximation will hence result in an optimal precision of the - z-buffer. - - The near clipping plane is positioned at a distance equal to - zClippingCoefficient() * sceneRadius() in front of the sceneCenter(): \code - zNear = distanceToSceneCenter() - zClippingCoefficient()*sceneRadius(); - \endcode - - In order to prevent negative or too small zNear() values (which would degrade - the z precision), zNearCoefficient() is used when the Camera is inside the - sceneRadius() sphere: \code const qreal zMin = zNearCoefficient() * - zClippingCoefficient() * sceneRadius(); if (zNear < zMin) zNear = zMin; - // With an ORTHOGRAPHIC type, the value is simply clamped to 0.0 - \endcode - - See also the zFar(), zClippingCoefficient() and zNearCoefficient() - documentations. - - If you need a completely different zNear computation, overload the zNear() and - zFar() methods in a new class that publicly inherits from Camera and use - QGLViewer::setCamera(): \code class myCamera :: public qglviewer::Camera - { - virtual qreal Camera::zNear() const { return 0.001; }; - virtual qreal Camera::zFar() const { return 100.0; }; - } - \endcode - - See the standardCamera example - for an application. - - \attention The value is always positive although the clipping plane is - positioned at a negative z value in the Camera coordinate system. This follows - the \c gluPerspective standard. */ -qreal Camera::zNear() const { - const qreal zNearScene = zClippingCoefficient() * sceneRadius(); - qreal z = distanceToSceneCenter() - zNearScene; - - // Prevents negative or null zNear values. - const qreal zMin = zNearCoefficient() * zNearScene; - if (z < zMin) - switch (type()) { - case Camera::PERSPECTIVE: - z = zMin; - break; - case Camera::ORTHOGRAPHIC: - z = 0.0; - break; - } - return z; -} - -/*! Returns the far clipping plane distance used by the Camera projection -matrix. - -The far clipping plane is positioned at a distance equal to -zClippingCoefficient() * sceneRadius() behind the sceneCenter(): \code zFar = -distanceToSceneCenter() + zClippingCoefficient()*sceneRadius(); \endcode - -See the zNear() documentation for details. */ -qreal Camera::zFar() const { - return distanceToSceneCenter() + zClippingCoefficient() * sceneRadius(); -} - -/*! Sets the vertical fieldOfView() of the Camera (in radians). - -Note that focusDistance() is set to sceneRadius() / tan(fieldOfView()/2) by this -method. */ -void Camera::setFieldOfView(qreal fov) { - fieldOfView_ = fov; - setFocusDistance(sceneRadius() / tan(fov / 2.0)); - projectionMatrixIsUpToDate_ = false; -} - -/*! Defines the Camera type(). - -Changing the camera Type alters the viewport and the objects' sizes can be -changed. This method garantees that the two frustum match in a plane normal to -viewDirection(), passing through the pivotPoint(). - -Prefix the type with \c Camera if needed, as in: -\code -camera()->setType(Camera::ORTHOGRAPHIC); -// or even qglviewer::Camera::ORTHOGRAPHIC if you do not use namespace -\endcode */ -void Camera::setType(Type type) { - // make ORTHOGRAPHIC frustum fit PERSPECTIVE (at least in plane normal to - // viewDirection(), passing through RAP). Done only when CHANGING type since - // orthoCoef_ may have been changed with a setPivotPoint() in the meantime. - if ((type == Camera::ORTHOGRAPHIC) && (type_ == Camera::PERSPECTIVE)) - orthoCoef_ = tan(fieldOfView() / 2.0); - type_ = type; - projectionMatrixIsUpToDate_ = false; -} - -/*! Sets the Camera frame(). - -If you want to move the Camera, use setPosition() and setOrientation() or one of -the Camera positioning methods (lookAt(), fitSphere(), showEntireScene()...) -instead. - -If you want to save the Camera position(), there's no need to call this method -either. Use addKeyFrameToPath() and playPath() instead. - -This method is actually mainly useful if you derive the ManipulatedCameraFrame -class and want to use an instance of your new class to move the Camera. - -A \c NULL \p mcf pointer will silently be ignored. The calling method is -responsible for deleting the previous frame() pointer if needed in order to -prevent memory leaks. */ -void Camera::setFrame(ManipulatedCameraFrame *const mcf) { - if (!mcf) - return; - - if (frame_) { - disconnect(frame_, SIGNAL(modified()), this, SLOT(onFrameModified())); - } - - frame_ = mcf; - interpolationKfi_->setFrame(frame()); - - connect(frame_, SIGNAL(modified()), this, SLOT(onFrameModified())); - onFrameModified(); -} - -/*! Returns the distance from the Camera center to sceneCenter(), projected - along the Camera Z axis. Used by zNear() and zFar() to optimize the Z range. -*/ -qreal Camera::distanceToSceneCenter() const { - return fabs((frame()->coordinatesOf(sceneCenter())).z); -} - -/*! Returns the \p halfWidth and \p halfHeight of the Camera orthographic - frustum. - - These values are only valid and used when the Camera is of type() - Camera::ORTHOGRAPHIC. They are expressed in OpenGL units and are used by - loadProjectionMatrix() to define the projection matrix using: \code glOrtho( - -halfWidth, halfWidth, -halfHeight, halfHeight, zNear(), zFar() ) \endcode - - These values are proportional to the Camera (z projected) distance to the - pivotPoint(). When zooming on the object, the Camera is translated forward \e - and its frustum is narrowed, making the object appear bigger on screen, as - intuitively expected. - - Overload this method to change this behavior if desired, as is done in the - standardCamera example. */ -void Camera::getOrthoWidthHeight(GLdouble &halfWidth, - GLdouble &halfHeight) const { - const qreal dist = orthoCoef_ * fabs(cameraCoordinatesOf(pivotPoint()).z); - //#CONNECTION# fitScreenRegion - halfWidth = dist * ((aspectRatio() < 1.0) ? 1.0 : aspectRatio()); - halfHeight = dist * ((aspectRatio() < 1.0) ? 1.0 / aspectRatio() : 1.0); -} - -/*! Computes the projection matrix associated with the Camera. - - If type() is Camera::PERSPECTIVE, defines a \c GL_PROJECTION matrix similar to - what would \c gluPerspective() do using the fieldOfView(), window - aspectRatio(), zNear() and zFar() parameters. - - If type() is Camera::ORTHOGRAPHIC, the projection matrix is as what \c - glOrtho() would do. Frustum's width and height are set using - getOrthoWidthHeight(). - - Both types use zNear() and zFar() to place clipping planes. These values are - determined from sceneRadius() and sceneCenter() so that they best fit the scene - size. - - Use getProjectionMatrix() to retrieve this matrix. Overload - loadProjectionMatrix() if you want your Camera to use an exotic projection - matrix. - - \note You must call this method if your Camera is not associated with a - QGLViewer and is used for offscreen computations (using - (un)projectedCoordinatesOf() for instance). loadProjectionMatrix() does it - otherwise. */ -void Camera::computeProjectionMatrix() const { - if (projectionMatrixIsUpToDate_) - return; - - const qreal ZNear = zNear(); - const qreal ZFar = zFar(); - - switch (type()) { - case Camera::PERSPECTIVE: { - // #CONNECTION# all non null coefficients were set to 0.0 in constructor. - const qreal f = 1.0 / tan(fieldOfView() / 2.0); - projectionMatrix_[0] = f / aspectRatio(); - projectionMatrix_[5] = f; - projectionMatrix_[10] = (ZNear + ZFar) / (ZNear - ZFar); - projectionMatrix_[11] = -1.0; - projectionMatrix_[14] = 2.0 * ZNear * ZFar / (ZNear - ZFar); - projectionMatrix_[15] = 0.0; - // same as gluPerspective( 180.0*fieldOfView()/M_PI, aspectRatio(), zNear(), - // zFar() ); - break; - } - case Camera::ORTHOGRAPHIC: { - GLdouble w, h; - getOrthoWidthHeight(w, h); - projectionMatrix_[0] = 1.0 / w; - projectionMatrix_[5] = 1.0 / h; - projectionMatrix_[10] = -2.0 / (ZFar - ZNear); - projectionMatrix_[11] = 0.0; - projectionMatrix_[14] = -(ZFar + ZNear) / (ZFar - ZNear); - projectionMatrix_[15] = 1.0; - // same as glOrtho( -w, w, -h, h, zNear(), zFar() ); - break; - } - } - - projectionMatrixIsUpToDate_ = true; -} - -/*! Computes the modelView matrix associated with the Camera's position() and - orientation(). - - This matrix converts from the world coordinates system to the Camera - coordinates system, so that coordinates can then be projected on screen using - the projection matrix (see computeProjectionMatrix()). - - Use getModelViewMatrix() to retrieve this matrix. - - \note You must call this method if your Camera is not associated with a - QGLViewer and is used for offscreen computations (using - (un)projectedCoordinatesOf() for instance). loadModelViewMatrix() does it - otherwise. */ -void Camera::computeModelViewMatrix() const { - if (modelViewMatrixIsUpToDate_) - return; - - const Quaternion q = frame()->orientation(); - - const qreal q00 = 2.0 * q[0] * q[0]; - const qreal q11 = 2.0 * q[1] * q[1]; - const qreal q22 = 2.0 * q[2] * q[2]; - - const qreal q01 = 2.0 * q[0] * q[1]; - const qreal q02 = 2.0 * q[0] * q[2]; - const qreal q03 = 2.0 * q[0] * q[3]; - - const qreal q12 = 2.0 * q[1] * q[2]; - const qreal q13 = 2.0 * q[1] * q[3]; - - const qreal q23 = 2.0 * q[2] * q[3]; - - modelViewMatrix_[0] = 1.0 - q11 - q22; - modelViewMatrix_[1] = q01 - q23; - modelViewMatrix_[2] = q02 + q13; - modelViewMatrix_[3] = 0.0; - - modelViewMatrix_[4] = q01 + q23; - modelViewMatrix_[5] = 1.0 - q22 - q00; - modelViewMatrix_[6] = q12 - q03; - modelViewMatrix_[7] = 0.0; - - modelViewMatrix_[8] = q02 - q13; - modelViewMatrix_[9] = q12 + q03; - modelViewMatrix_[10] = 1.0 - q11 - q00; - modelViewMatrix_[11] = 0.0; - - const Vec t = q.inverseRotate(frame()->position()); - - modelViewMatrix_[12] = -t.x; - modelViewMatrix_[13] = -t.y; - modelViewMatrix_[14] = -t.z; - modelViewMatrix_[15] = 1.0; - - modelViewMatrixIsUpToDate_ = true; -} - -/*! Loads the OpenGL \c GL_PROJECTION matrix with the Camera projection matrix. - - The Camera projection matrix is computed using computeProjectionMatrix(). - - When \p reset is \c true (default), the method clears the previous projection - matrix by calling \c glLoadIdentity before setting the matrix. Setting \p reset - to \c false is useful for \c GL_SELECT mode, to combine the pushed matrix with - a picking matrix. See QGLViewer::beginSelection() for details. - - This method is used by QGLViewer::preDraw() (called before user's - QGLViewer::draw() method) to set the \c GL_PROJECTION matrix according to the - viewer's QGLViewer::camera() settings. - - Use getProjectionMatrix() to retrieve this matrix. Overload this method if you - want your Camera to use an exotic projection matrix. See also - loadModelViewMatrix(). - - \attention \c glMatrixMode is set to \c GL_PROJECTION. - - \attention If you use several OpenGL contexts and bypass the Qt main refresh - loop, you should call QOpenGLWidget::makeCurrent() before this method in order - to activate the right OpenGL context. */ -void Camera::loadProjectionMatrix(bool reset) const { - // WARNING: makeCurrent must be called by every calling method - glMatrixMode(GL_PROJECTION); - - if (reset) - glLoadIdentity(); - - computeProjectionMatrix(); - - glMultMatrixd(projectionMatrix_); -} - -/*! Loads the OpenGL \c GL_MODELVIEW matrix with the modelView matrix - corresponding to the Camera. - - Calls computeModelViewMatrix() to compute the Camera's modelView matrix. - - This method is used by QGLViewer::preDraw() (called before user's - QGLViewer::draw() method) to set the \c GL_MODELVIEW matrix according to the - viewer's QGLViewer::camera() position() and orientation(). - - As a result, the vertices used in QGLViewer::draw() can be defined in the so - called world coordinate system. They are multiplied by this matrix to get - converted to the Camera coordinate system, before getting projected using the - \c GL_PROJECTION matrix (see loadProjectionMatrix()). - - When \p reset is \c true (default), the method loads (overwrites) the \c - GL_MODELVIEW matrix. Setting \p reset to \c false simply calls \c glMultMatrixd - (might be useful for some applications). - - Overload this method or simply call glLoadMatrixd() at the beginning of - QGLViewer::draw() if you want your Camera to use an exotic modelView matrix. - See also loadProjectionMatrix(). - - getModelViewMatrix() returns the 4x4 modelView matrix. - - \attention glMatrixMode is set to \c GL_MODELVIEW - - \attention If you use several OpenGL contexts and bypass the Qt main refresh - loop, you should call QOpenGLWidget::makeCurrent() before this method in order - to activate the right OpenGL context. */ -void Camera::loadModelViewMatrix(bool reset) const { - // WARNING: makeCurrent must be called by every calling method - glMatrixMode(GL_MODELVIEW); - computeModelViewMatrix(); - if (reset) - glLoadMatrixd(modelViewMatrix_); - else - glMultMatrixd(modelViewMatrix_); -} - -/*! Same as loadProjectionMatrix() but for a stereo setup. - - Only the Camera::PERSPECTIVE type() is supported for stereo mode. See - QGLViewer::setStereoDisplay(). - - Uses focusDistance(), IODistance(), and physicalScreenWidth() to compute - cameras offset and asymmetric frustums. - - When \p leftBuffer is \c true, computes the projection matrix associated to the - left eye (right eye otherwise). See also loadModelViewMatrixStereo(). - - See the stereoViewer and the anaglyph examples for an - illustration. - - To retrieve this matrix, use a code like: - \code - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - loadProjectionMatrixStereo(left_or_right); - glGetDoublev(GL_PROJECTION_MATRIX, m); - glPopMatrix(); - \endcode - Note that getProjectionMatrix() always returns the mono-vision matrix. - - \attention glMatrixMode is set to \c GL_PROJECTION. */ -void Camera::loadProjectionMatrixStereo(bool leftBuffer) const { - qreal left, right, bottom, top; - qreal screenHalfWidth, halfWidth, side, shift, delta; - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - switch (type()) { - case Camera::PERSPECTIVE: - // compute half width of screen, - // corresponding to zero parallax plane to deduce decay of cameras - screenHalfWidth = focusDistance() * tan(horizontalFieldOfView() / 2.0); - shift = screenHalfWidth * IODistance() / physicalScreenWidth(); - // should be * current y / y total - // to take into account that the window doesn't cover the entire screen - - // compute half width of "view" at znear and the delta corresponding to - // the shifted camera to deduce what to set for asymmetric frustums - halfWidth = zNear() * tan(horizontalFieldOfView() / 2.0); - delta = shift * zNear() / focusDistance(); - side = leftBuffer ? -1.0 : 1.0; - - left = -halfWidth + side * delta; - right = halfWidth + side * delta; - top = halfWidth / aspectRatio(); - bottom = -top; - glFrustum(left, right, bottom, top, zNear(), zFar()); - break; - - case Camera::ORTHOGRAPHIC: - qWarning("Camera::setProjectionMatrixStereo: Stereo not available with " - "Ortho mode"); - break; - } -} - -/*! Same as loadModelViewMatrix() but for a stereo setup. - - Only the Camera::PERSPECTIVE type() is supported for stereo mode. See - QGLViewer::setStereoDisplay(). - - The modelView matrix is almost identical to the mono-vision one. It is simply - translated along its horizontal axis by a value that depends on stereo - parameters (see focusDistance(), IODistance(), and physicalScreenWidth()). - - When \p leftBuffer is \c true, computes the modelView matrix associated to the - left eye (right eye otherwise). - - loadProjectionMatrixStereo() explains how to retrieve to resulting matrix. - - See the stereoViewer and the anaglyph examples for an - illustration. - - \attention glMatrixMode is set to \c GL_MODELVIEW. */ -void Camera::loadModelViewMatrixStereo(bool leftBuffer) const { - // WARNING: makeCurrent must be called by every calling method - glMatrixMode(GL_MODELVIEW); - - qreal halfWidth = focusDistance() * tan(horizontalFieldOfView() / 2.0); - qreal shift = - halfWidth * IODistance() / - physicalScreenWidth(); // * current window width / full screen width - - computeModelViewMatrix(); - if (leftBuffer) - modelViewMatrix_[12] -= shift; - else - modelViewMatrix_[12] += shift; - glLoadMatrixd(modelViewMatrix_); -} - -/*! Fills \p m with the Camera projection matrix values. - - Based on computeProjectionMatrix() to make sure the Camera projection matrix is - up to date. - - This matrix only reflects the Camera's internal parameters and it may differ - from the \c GL_PROJECTION matrix retrieved using \c - glGetDoublev(GL_PROJECTION_MATRIX, m). It actually represents the state of the - \c GL_PROJECTION after QGLViewer::preDraw(), at the beginning of - QGLViewer::draw(). If you modified the \c GL_PROJECTION matrix (for instance - using QGLViewer::startScreenCoordinatesSystem()), the two results differ. - - The result is an OpenGL 4x4 matrix, which is given in \e column-major order - (see \c glMultMatrix man page for details). - - See also getModelViewMatrix() and setFromProjectionMatrix(). */ -void Camera::getProjectionMatrix(GLdouble m[16]) const { - computeProjectionMatrix(); - for (unsigned short i = 0; i < 16; ++i) - m[i] = projectionMatrix_[i]; -} - -/*! Overloaded getProjectionMatrix(GLdouble m[16]) method using a \c GLfloat - * array instead. */ -void Camera::getProjectionMatrix(GLfloat m[16]) const { - static GLdouble mat[16]; - getProjectionMatrix(mat); - for (unsigned short i = 0; i < 16; ++i) - m[i] = float(mat[i]); -} - -/*! Fills \p m with the Camera modelView matrix values. - - First calls computeModelViewMatrix() to define the Camera modelView matrix. - - Note that this matrix may \e not be the one you would get from a \c - glGetDoublev(GL_MODELVIEW_MATRIX, m). It actually represents the state of the - \c GL_MODELVIEW after QGLViewer::preDraw(), at the \e beginning of - QGLViewer::draw(). It converts from the world to the Camera coordinate system. - As soon as you modify the \c GL_MODELVIEW in your QGLViewer::draw() method - (using glTranslate, glRotate... or similar methods), the two matrices differ. - - The result is an OpenGL 4x4 matrix, which is given in \e column-major order - (see \c glMultMatrix man page for details). - - See also getProjectionMatrix() and setFromModelViewMatrix(). */ -void Camera::getModelViewMatrix(GLdouble m[16]) const { - // May not be needed, but easier like this. - // Prevents from retrieving matrix in stereo mode -> overwrites shifted value. - computeModelViewMatrix(); - for (unsigned short i = 0; i < 16; ++i) - m[i] = modelViewMatrix_[i]; -} - -/*! Overloaded getModelViewMatrix(GLdouble m[16]) method using a \c GLfloat - * array instead. */ -void Camera::getModelViewMatrix(GLfloat m[16]) const { - static GLdouble mat[16]; - getModelViewMatrix(mat); - for (unsigned short i = 0; i < 16; ++i) - m[i] = float(mat[i]); -} - -/*! Fills \p m with the product of the ModelView and Projection matrices. - - Calls getModelViewMatrix() and getProjectionMatrix() and then fills \p m with - the product of these two matrices. */ -void Camera::getModelViewProjectionMatrix(GLdouble m[16]) const { - GLdouble mv[16]; - GLdouble proj[16]; - getModelViewMatrix(mv); - getProjectionMatrix(proj); - - for (unsigned short i = 0; i < 4; ++i) { - for (unsigned short j = 0; j < 4; ++j) { - qreal sum = 0.0; - for (unsigned short k = 0; k < 4; ++k) - sum += proj[i + 4 * k] * mv[k + 4 * j]; - m[i + 4 * j] = sum; - } - } -} - -/*! Overloaded getModelViewProjectionMatrix(GLdouble m[16]) method using a \c - * GLfloat array instead. */ -void Camera::getModelViewProjectionMatrix(GLfloat m[16]) const { - static GLdouble mat[16]; - getModelViewProjectionMatrix(mat); - for (unsigned short i = 0; i < 16; ++i) - m[i] = float(mat[i]); -} - -/*! Sets the sceneRadius() value. Negative values are ignored. - -\attention This methods also sets focusDistance() to sceneRadius() / -tan(fieldOfView()/2) and flySpeed() to 1% of sceneRadius(). */ -void Camera::setSceneRadius(qreal radius) { - if (radius <= 0.0) { - qWarning("Scene radius must be positive - Ignoring value"); - return; - } - - sceneRadius_ = radius; - projectionMatrixIsUpToDate_ = false; - - setFocusDistance(sceneRadius() / tan(fieldOfView() / 2.0)); - - frame()->setFlySpeed(0.01 * sceneRadius()); -} - -/*! Similar to setSceneRadius() and setSceneCenter(), but the scene limits are - defined by a (world axis aligned) bounding box. */ -void Camera::setSceneBoundingBox(const Vec &min, const Vec &max) { - setSceneCenter((min + max) / 2.0); - setSceneRadius(0.5 * (max - min).norm()); -} - -/*! Sets the sceneCenter(). - - \attention This method also sets the pivotPoint() to sceneCenter(). */ -void Camera::setSceneCenter(const Vec ¢er) { - sceneCenter_ = center; - setPivotPoint(sceneCenter()); - projectionMatrixIsUpToDate_ = false; -} - -/*! setSceneCenter() to the result of pointUnderPixel(\p pixel). - - Returns \c true if a pointUnderPixel() was found and sceneCenter() was - actually changed. - - See also setPivotPointFromPixel(). See the pointUnderPixel() documentation. */ -bool Camera::setSceneCenterFromPixel(const QPoint &pixel) { - bool found; - Vec point = pointUnderPixel(pixel, found); - if (found) - setSceneCenter(point); - return found; -} - -#ifndef DOXYGEN -void Camera::setRevolveAroundPoint(const Vec &point) { - qWarning( - "setRevolveAroundPoint() is deprecated, use setPivotPoint() instead"); - setPivotPoint(point); -} -bool Camera::setRevolveAroundPointFromPixel(const QPoint &pixel) { - qWarning("setRevolveAroundPointFromPixel() is deprecated, use " - "setPivotPointFromPixel() instead"); - return setPivotPointFromPixel(pixel); -} -Vec Camera::revolveAroundPoint() const { - qWarning("revolveAroundPoint() is deprecated, use pivotPoint() instead"); - return pivotPoint(); -} -#endif - -/*! Changes the pivotPoint() to \p point (defined in the world coordinate - * system). */ -void Camera::setPivotPoint(const Vec &point) { - const qreal prevDist = fabs(cameraCoordinatesOf(pivotPoint()).z); - - // If frame's RAP is set directly, projectionMatrixIsUpToDate_ should also be - // set to false to ensure proper recomputation of the ORTHO projection matrix. - frame()->setPivotPoint(point); - - // orthoCoef_ is used to compensate for changes of the pivotPoint, so that the - // image does not change when the pivotPoint is changed in ORTHOGRAPHIC mode. - const qreal newDist = fabs(cameraCoordinatesOf(pivotPoint()).z); - // Prevents division by zero when rap is set to camera position - if ((prevDist > 1E-9) && (newDist > 1E-9)) - orthoCoef_ *= prevDist / newDist; - projectionMatrixIsUpToDate_ = false; -} - -/*! The pivotPoint() is set to the point located under \p pixel on screen. - -Returns \c true if a pointUnderPixel() was found. If no point was found under \p -pixel, the pivotPoint() is left unchanged. - -\p pixel is expressed in Qt format (origin in the upper left corner of the -window). See pointUnderPixel(). - -See also setSceneCenterFromPixel(). */ -bool Camera::setPivotPointFromPixel(const QPoint &pixel) { - bool found; - Vec point = pointUnderPixel(pixel, found); - if (found) - setPivotPoint(point); - return found; -} - -/*! Returns the ratio between pixel and OpenGL units at \p position. - - A line of \c n * pixelGLRatio() OpenGL units, located at \p position in the - world coordinates system, will be projected with a length of \c n pixels on - screen. - - Use this method to scale objects so that they have a constant pixel size on - screen. The following code will draw a 20 pixel line, starting at sceneCenter() - and always directed along the screen vertical direction: \code - glBegin(GL_LINES); - glVertex3fv(sceneCenter()); - glVertex3fv(sceneCenter() + 20 * pixelGLRatio(sceneCenter()) * - camera()->upVector()); glEnd(); \endcode */ -qreal Camera::pixelGLRatio(const Vec &position) const { - switch (type()) { - case Camera::PERSPECTIVE: - return 2.0 * fabs((frame()->coordinatesOf(position)).z) * - tan(fieldOfView() / 2.0) / screenHeight(); - case Camera::ORTHOGRAPHIC: { - GLdouble w, h; - getOrthoWidthHeight(w, h); - return 2.0 * h / screenHeight(); - } - } - // Bad compilers complain - return 1.0; -} - -/*! Changes the Camera fieldOfView() so that the entire scene (defined by - QGLViewer::sceneCenter() and QGLViewer::sceneRadius()) is visible from the - Camera position(). - - The position() and orientation() of the Camera are not modified and you first - have to orientate the Camera in order to actually see the scene (see lookAt(), - showEntireScene() or fitSphere()). - - This method is especially useful for \e shadow \e maps computation. Use the - Camera positioning tools (setPosition(), lookAt()) to position a Camera at the - light position. Then use this method to define the fieldOfView() so that the - shadow map resolution is optimally used: \code - // The light camera needs size hints in order to optimize its fieldOfView - lightCamera->setSceneRadius(sceneRadius()); - lightCamera->setSceneCenter(sceneCenter()); - - // Place the light camera. - lightCamera->setPosition(lightFrame->position()); - lightCamera->lookAt(sceneCenter()); - lightCamera->setFOVToFitScene(); - \endcode - - See the (soon available) shadowMap contribution example for a practical - implementation. - - \attention The fieldOfView() is clamped to M_PI/2.0. This happens when the - Camera is at a distance lower than sqrt(2.0) * sceneRadius() from the - sceneCenter(). It optimizes the shadow map resolution, although it may miss - some parts of the scene. */ -void Camera::setFOVToFitScene() { - if (distanceToSceneCenter() > sqrt(2.0) * sceneRadius()) - setFieldOfView(2.0 * asin(sceneRadius() / distanceToSceneCenter())); - else - setFieldOfView(M_PI / 2.0); -} - -/*! Makes the Camera smoothly zoom on the pointUnderPixel() \p pixel. - - Nothing happens if no pointUnderPixel() is found. Otherwise a - KeyFrameInterpolator is created that animates the Camera on a one second path - that brings the Camera closer to the point under \p pixel. - - See also interpolateToFitScene(). */ -void Camera::interpolateToZoomOnPixel(const QPoint &pixel) { - const qreal coef = 0.1; - - bool found; - Vec target = pointUnderPixel(pixel, found); - - if (!found) - return; - - if (interpolationKfi_->interpolationIsStarted()) - interpolationKfi_->stopInterpolation(); - - interpolationKfi_->deletePath(); - interpolationKfi_->addKeyFrame(*(frame())); - - interpolationKfi_->addKeyFrame( - Frame(0.3 * frame()->position() + 0.7 * target, frame()->orientation()), - 0.4); - - // Small hack: attach a temporary frame to take advantage of lookAt without - // modifying frame - static ManipulatedCameraFrame *tempFrame = new ManipulatedCameraFrame(); - ManipulatedCameraFrame *const originalFrame = frame(); - tempFrame->setPosition(coef * frame()->position() + (1.0 - coef) * target); - tempFrame->setOrientation(frame()->orientation()); - setFrame(tempFrame); - lookAt(target); - setFrame(originalFrame); - - interpolationKfi_->addKeyFrame(*(tempFrame), 1.0); - - interpolationKfi_->startInterpolation(); -} - -/*! Interpolates the Camera on a one second KeyFrameInterpolator path so that - the entire scene fits the screen at the end. - - The scene is defined by its sceneCenter() and its sceneRadius(). See - showEntireScene(). - - The orientation() of the Camera is not modified. See also - interpolateToZoomOnPixel(). */ -void Camera::interpolateToFitScene() { - if (interpolationKfi_->interpolationIsStarted()) - interpolationKfi_->stopInterpolation(); - - interpolationKfi_->deletePath(); - interpolationKfi_->addKeyFrame(*(frame())); - - // Small hack: attach a temporary frame to take advantage of lookAt without - // modifying frame - static ManipulatedCameraFrame *tempFrame = new ManipulatedCameraFrame(); - ManipulatedCameraFrame *const originalFrame = frame(); - tempFrame->setPosition(frame()->position()); - tempFrame->setOrientation(frame()->orientation()); - setFrame(tempFrame); - showEntireScene(); - setFrame(originalFrame); - - interpolationKfi_->addKeyFrame(*(tempFrame)); - - interpolationKfi_->startInterpolation(); -} - -/*! Smoothly interpolates the Camera on a KeyFrameInterpolator path so that it - goes to \p fr. - - \p fr is expressed in world coordinates. \p duration tunes the interpolation - speed (default is 1 second). - - See also interpolateToFitScene() and interpolateToZoomOnPixel(). */ -void Camera::interpolateTo(const Frame &fr, qreal duration) { - if (interpolationKfi_->interpolationIsStarted()) - interpolationKfi_->stopInterpolation(); - - interpolationKfi_->deletePath(); - interpolationKfi_->addKeyFrame(*(frame())); - interpolationKfi_->addKeyFrame(fr, duration); - - interpolationKfi_->startInterpolation(); -} - -/*! Returns the coordinates of the 3D point located at pixel (x,y) on screen. - - Calls a \c glReadPixel to get the pixel depth and applies an - unprojectedCoordinatesOf() to the result. \p found indicates whether a point - was found or not (i.e. background pixel, result's depth is zFar() in that - case). - - \p x and \p y are expressed in pixel units with an origin in the upper left - corner. Use screenHeight() - y to convert to OpenGL standard. - - \attention This method assumes that a GL context is available, and that its - content was drawn using the Camera (i.e. using its projection and modelview - matrices). This method hence cannot be used for offscreen Camera computations. - Use cameraCoordinatesOf() and worldCoordinatesOf() to perform similar - operations in that case. - - \note The precision of the z-Buffer highly depends on how the zNear() and - zFar() values are fitted to your scene. Loose boundaries will result in - imprecision along the viewing direction. */ -Vec Camera::pointUnderPixel(const QPoint &pixel, bool &found) const { - float depth; - // Qt uses upper corner for its origin while GL uses the lower corner. - glReadPixels(pixel.x(), screenHeight() - 1 - pixel.y(), 1, 1, - GL_DEPTH_COMPONENT, GL_FLOAT, &depth); - found = depth < 1.0; - Vec point(pixel.x(), pixel.y(), depth); - point = unprojectedCoordinatesOf(point); - return point; -} - -/*! Moves the Camera so that the entire scene is visible. - - Simply calls fitSphere() on a sphere defined by sceneCenter() and - sceneRadius(). - - You will typically use this method in QGLViewer::init() after you defined a new - sceneRadius(). */ -void Camera::showEntireScene() { fitSphere(sceneCenter(), sceneRadius()); } - -/*! Moves the Camera so that its sceneCenter() is projected on the center of the - window. The orientation() and fieldOfView() are unchanged. - - Simply projects the current position on a line passing through sceneCenter(). - See also showEntireScene().*/ -void Camera::centerScene() { - frame()->projectOnLine(sceneCenter(), viewDirection()); -} - -/*! Sets the Camera orientation(), so that it looks at point \p target (defined - in the world coordinate system). - - The Camera position() is not modified. Simply setViewDirection(). - - See also setUpVector(), setOrientation(), showEntireScene(), fitSphere() and - fitBoundingBox(). */ -void Camera::lookAt(const Vec &target) { - setViewDirection(target - position()); -} - -/*! Moves the Camera so that the sphere defined by (\p center, \p radius) is - visible and fits in the frustum. - - The Camera is simply translated to center the sphere in the screen and make it - fit the frustum. Its orientation() and its fieldOfView() are unchanged. - - You should therefore orientate the Camera before you call this method. See - lookAt(), setOrientation() and setUpVector(). */ -void Camera::fitSphere(const Vec ¢er, qreal radius) { - qreal distance = 0.0; - switch (type()) { - case Camera::PERSPECTIVE: { - const qreal yview = radius / sin(fieldOfView() / 2.0); - const qreal xview = radius / sin(horizontalFieldOfView() / 2.0); - distance = qMax(xview, yview); - break; - } - case Camera::ORTHOGRAPHIC: { - distance = - ((center - pivotPoint()) * viewDirection()) + (radius / orthoCoef_); - break; - } - } - Vec newPos(center - distance * viewDirection()); - frame()->setPositionWithConstraint(newPos); -} - -/*! Moves the Camera so that the (world axis aligned) bounding box (\p min, \p - max) is entirely visible, using fitSphere(). */ -void Camera::fitBoundingBox(const Vec &min, const Vec &max) { - qreal diameter = qMax(fabs(max[1] - min[1]), fabs(max[0] - min[0])); - diameter = qMax(fabs(max[2] - min[2]), diameter); - fitSphere(0.5 * (min + max), 0.5 * diameter); -} - -/*! Moves the Camera so that the rectangular screen region defined by \p - rectangle (pixel units, with origin in the upper left corner) fits the screen. - - The Camera is translated (its orientation() is unchanged) so that \p rectangle - is entirely visible. Since the pixel coordinates only define a \e frustum in - 3D, it's the intersection of this frustum with a plane (orthogonal to the - viewDirection() and passing through the sceneCenter()) that is used to define - the 3D rectangle that is eventually fitted. */ -void Camera::fitScreenRegion(const QRect &rectangle) { - const Vec vd = viewDirection(); - const qreal distToPlane = distanceToSceneCenter(); - const QPoint center = rectangle.center(); - - Vec orig, dir; - convertClickToLine(center, orig, dir); - Vec newCenter = orig + distToPlane / (dir * vd) * dir; - - convertClickToLine(QPoint(rectangle.x(), center.y()), orig, dir); - const Vec pointX = orig + distToPlane / (dir * vd) * dir; - - convertClickToLine(QPoint(center.x(), rectangle.y()), orig, dir); - const Vec pointY = orig + distToPlane / (dir * vd) * dir; - - qreal distance = 0.0; - switch (type()) { - case Camera::PERSPECTIVE: { - const qreal distX = - (pointX - newCenter).norm() / sin(horizontalFieldOfView() / 2.0); - const qreal distY = (pointY - newCenter).norm() / sin(fieldOfView() / 2.0); - distance = qMax(distX, distY); - break; - } - case Camera::ORTHOGRAPHIC: { - const qreal dist = ((newCenter - pivotPoint()) * vd); - //#CONNECTION# getOrthoWidthHeight - const qreal distX = (pointX - newCenter).norm() / orthoCoef_ / - ((aspectRatio() < 1.0) ? 1.0 : aspectRatio()); - const qreal distY = (pointY - newCenter).norm() / orthoCoef_ / - ((aspectRatio() < 1.0) ? 1.0 / aspectRatio() : 1.0); - distance = dist + qMax(distX, distY); - break; - } - } - - Vec newPos(newCenter - distance * vd); - frame()->setPositionWithConstraint(newPos); -} - -/*! Rotates the Camera so that its upVector() becomes \p up (defined in the - world coordinate system). - - The Camera is rotated around an axis orthogonal to \p up and to the current - upVector() direction. Use this method in order to define the Camera horizontal - plane. - - When \p noMove is set to \c false, the orientation modification is compensated - by a translation, so that the pivotPoint() stays projected at the same position - on screen. This is especially useful when the Camera is used as an observer of - the scene (default mouse binding). - - When \p noMove is \c true (default), the Camera position() is left unchanged, - which is an intuitive behavior when the Camera is in a walkthrough fly mode - (see the QGLViewer::MOVE_FORWARD and QGLViewer::MOVE_BACKWARD - QGLViewer::MouseAction). - - The frame()'s ManipulatedCameraFrame::sceneUpVector() is set accordingly. - - See also setViewDirection(), lookAt() and setOrientation(). */ -void Camera::setUpVector(const Vec &up, bool noMove) { - Quaternion q(Vec(0.0, 1.0, 0.0), frame()->transformOf(up)); - - if (!noMove) - frame()->setPosition(pivotPoint() - - (frame()->orientation() * q) - .rotate(frame()->coordinatesOf(pivotPoint()))); - - frame()->rotate(q); - - // Useful in fly mode to keep the horizontal direction. - frame()->updateSceneUpVector(); -} - -/*! Sets the orientation() of the Camera using polar coordinates. - - \p theta rotates the Camera around its Y axis, and \e then \p phi rotates it - around its X axis. The polar coordinates are defined in the world coordinates - system: \p theta = \p phi = 0 means that the Camera is directed towards the - world Z axis. Both angles are expressed in radians. - - See also setUpVector(). The position() of the Camera is unchanged, you may want - to call showEntireScene() after this method to move the Camera. - - This method can be useful to create Quicktime VR panoramic sequences, see the - QGLViewer::saveSnapshot() documentation for details. */ -void Camera::setOrientation(qreal theta, qreal phi) { - Vec axis(0.0, 1.0, 0.0); - const Quaternion rot1(axis, theta); - axis = Vec(-cos(theta), 0.0, sin(theta)); - const Quaternion rot2(axis, phi); - setOrientation(rot1 * rot2); -} - -/*! Sets the Camera orientation(), defined in the world coordinate system. */ -void Camera::setOrientation(const Quaternion &q) { - frame()->setOrientation(q); - frame()->updateSceneUpVector(); -} - -/*! Rotates the Camera so that its viewDirection() is \p direction (defined in - the world coordinate system). - - The Camera position() is not modified. The Camera is rotated so that the - horizon (defined by its upVector()) is preserved. See also lookAt() and - setUpVector(). */ -void Camera::setViewDirection(const Vec &direction) { - if (direction.squaredNorm() < 1E-10) - return; - - Vec xAxis = direction ^ upVector(); - if (xAxis.squaredNorm() < 1E-10) { - // target is aligned with upVector, this means a rotation around X axis - // X axis is then unchanged, let's keep it ! - xAxis = frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0)); - } - - Quaternion q; - q.setFromRotatedBasis(xAxis, xAxis ^ direction, -direction); - frame()->setOrientationWithConstraint(q); -} - -// Compute a 3 by 3 determinant. -static qreal det(qreal m00, qreal m01, qreal m02, qreal m10, qreal m11, - qreal m12, qreal m20, qreal m21, qreal m22) { - return m00 * m11 * m22 + m01 * m12 * m20 + m02 * m10 * m21 - m20 * m11 * m02 - - m10 * m01 * m22 - m00 * m21 * m12; -} - -// Computes the index of element [i][j] in a \c qreal matrix[3][4]. -static inline unsigned int ind(unsigned int i, unsigned int j) { - return (i * 4 + j); -} - -/*! Returns the Camera position (the eye), defined in the world coordinate -system. - -Use setPosition() to set the Camera position. Other convenient methods are -showEntireScene() or fitSphere(). Actually returns \c frame()->position(). - -This position corresponds to the projection center of a Camera::PERSPECTIVE -Camera. It is not located in the image plane, which is at a zNear() distance -ahead. */ -Vec Camera::position() const { return frame()->position(); } - -/*! Returns the normalized up vector of the Camera, defined in the world -coordinate system. - -Set using setUpVector() or setOrientation(). It is orthogonal to viewDirection() -and to rightVector(). - -It corresponds to the Y axis of the associated frame() (actually returns -frame()->inverseTransformOf(Vec(0.0, 1.0, 0.0)) ). */ -Vec Camera::upVector() const { - return frame()->inverseTransformOf(Vec(0.0, 1.0, 0.0)); -} -/*! Returns the normalized view direction of the Camera, defined in the world -coordinate system. - -Change this value using setViewDirection(), lookAt() or setOrientation(). It is -orthogonal to upVector() and to rightVector(). - -This corresponds to the negative Z axis of the frame() ( -frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0)) ). */ -Vec Camera::viewDirection() const { - return frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0)); -} - -/*! Returns the normalized right vector of the Camera, defined in the world -coordinate system. - -This vector lies in the Camera horizontal plane, directed along the X axis -(orthogonal to upVector() and to viewDirection()). Set using setUpVector(), -lookAt() or setOrientation(). - -Simply returns frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0)). */ -Vec Camera::rightVector() const { - return frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0)); -} - -/*! Returns the Camera orientation, defined in the world coordinate system. - -Actually returns \c frame()->orientation(). Use setOrientation(), setUpVector() -or lookAt() to set the Camera orientation. */ -Quaternion Camera::orientation() const { return frame()->orientation(); } - -/*! Sets the Camera position() (the eye), defined in the world coordinate - * system. */ -void Camera::setPosition(const Vec &pos) { frame()->setPosition(pos); } - -/*! Returns the Camera frame coordinates of a point \p src defined in world -coordinates. - -worldCoordinatesOf() performs the inverse transformation. - -Note that the point coordinates are simply converted in a different coordinate -system. They are not projected on screen. Use projectedCoordinatesOf() for that. -*/ -Vec Camera::cameraCoordinatesOf(const Vec &src) const { - return frame()->coordinatesOf(src); -} - -/*! Returns the world coordinates of the point whose position \p src is defined -in the Camera coordinate system. - -cameraCoordinatesOf() performs the inverse transformation. */ -Vec Camera::worldCoordinatesOf(const Vec &src) const { - return frame()->inverseCoordinatesOf(src); -} - -/*! Returns the fly speed of the Camera. - -Simply returns frame()->flySpeed(). See the ManipulatedCameraFrame::flySpeed() -documentation. This value is only meaningful when the MouseAction bindings is -QGLViewer::MOVE_FORWARD or QGLViewer::MOVE_BACKWARD. - -Set to 1% of the sceneRadius() by setSceneRadius(). See also setFlySpeed(). */ -qreal Camera::flySpeed() const { return frame()->flySpeed(); } - -/*! Sets the Camera flySpeed(). - -\attention This value is modified by setSceneRadius(). */ -void Camera::setFlySpeed(qreal speed) { frame()->setFlySpeed(speed); } - -/*! The point the Camera pivots around with the QGLViewer::ROTATE mouse binding. -Defined in world coordinate system. - -Default value is the sceneCenter(). - -\attention setSceneCenter() changes this value. */ -Vec Camera::pivotPoint() const { return frame()->pivotPoint(); } - -/*! Sets the Camera's position() and orientation() from an OpenGL ModelView -matrix. - -This enables a Camera initialisation from an other OpenGL application. \p -modelView is a 16 GLdouble vector representing a valid OpenGL ModelView matrix, -such as one can get using: \code GLdouble mvm[16]; -glGetDoublev(GL_MODELVIEW_MATRIX, mvm); -myCamera->setFromModelViewMatrix(mvm); -\endcode - -After this method has been called, getModelViewMatrix() returns a matrix -equivalent to \p modelView. - -Only the orientation() and position() of the Camera are modified. - -\note If you defined your matrix as \c GLdouble \c mvm[4][4], pass \c -&(mvm[0][0]) as a parameter. */ -void Camera::setFromModelViewMatrix(const GLdouble *const modelViewMatrix) { - // Get upper left (rotation) matrix - qreal upperLeft[3][3]; - for (int i = 0; i < 3; ++i) - for (int j = 0; j < 3; ++j) - upperLeft[i][j] = modelViewMatrix[i * 4 + j]; - - // Transform upperLeft into the associated Quaternion - Quaternion q; - q.setFromRotationMatrix(upperLeft); - - setOrientation(q); - setPosition(-q.rotate( - Vec(modelViewMatrix[12], modelViewMatrix[13], modelViewMatrix[14]))); -} - -/*! Defines the Camera position(), orientation() and fieldOfView() from a - projection matrix. - - \p matrix has to be given in the format used by vision algorithm. It has 3 - lines and 4 columns. It transforms a point from the world homogeneous - coordinate system (4 coordinates: \c sx, \c sy, \c sz and \c s) into a point in - the screen homogeneous coordinate system (3 coordinates: \c sx, \c sy, and \c - s, where \c x and \c y are the pixel coordinates on the screen). - - Its three lines correspond to the homogeneous coordinates of the normals to the - planes x=0, y=0 and z=0, defined in the Camera coordinate system. - - The elements of the matrix are ordered in line major order: you can call \c - setFromProjectionMatrix(&(matrix[0][0])) if you defined your matrix as a \c - qreal \c matrix[3][4]. - - \attention Passing the result of getProjectionMatrix() or getModelViewMatrix() - to this method is not possible (purposefully incompatible matrix dimensions). - \p matrix is more likely to be the product of these two matrices, without the - last line. - - Use setFromModelViewMatrix() to set position() and orientation() from a \c - GL_MODELVIEW matrix. fieldOfView() can also be retrieved from a \e perspective - \c GL_PROJECTION matrix using 2.0 * atan(1.0/projectionMatrix[5]). - - This code was written by Sylvain Paris. */ -void Camera::setFromProjectionMatrix(const qreal matrix[12]) { - // The 3 lines of the matrix are the normals to the planes x=0, y=0, z=0 - // in the camera CS. As we normalize them, we do not need the 4th coordinate. - Vec line_0(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 2)]); - Vec line_1(matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 2)]); - Vec line_2(matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 2)]); - - line_0.normalize(); - line_1.normalize(); - line_2.normalize(); - - // The camera position is at (0,0,0) in the camera CS so it is the - // intersection of the 3 planes. It can be seen as the kernel - // of the 3x4 projection matrix. We calculate it through 4 dimensional - // vectorial product. We go directly into 3D that is to say we directly - // divide the first 3 coordinates by the 4th one. - - // We derive the 4 dimensional vectorial product formula from the - // computation of a 4x4 determinant that is developped according to - // its 4th column. This implies some 3x3 determinants. - const Vec cam_pos = - Vec(det(matrix[ind(0, 1)], matrix[ind(0, 2)], matrix[ind(0, 3)], - matrix[ind(1, 1)], matrix[ind(1, 2)], matrix[ind(1, 3)], - matrix[ind(2, 1)], matrix[ind(2, 2)], matrix[ind(2, 3)]), - - -det(matrix[ind(0, 0)], matrix[ind(0, 2)], matrix[ind(0, 3)], - matrix[ind(1, 0)], matrix[ind(1, 2)], matrix[ind(1, 3)], - matrix[ind(2, 0)], matrix[ind(2, 2)], matrix[ind(2, 3)]), - - det(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 3)], - matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 3)], - matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 3)])) / - - (-det(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 2)], - matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 2)], - matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 2)])); - - // We compute the rotation matrix column by column. - - // GL Z axis is front facing. - Vec column_2 = -line_2; - - // X-axis is almost like line_0 but should be orthogonal to the Z axis. - Vec column_0 = ((column_2 ^ line_0) ^ column_2); - column_0.normalize(); - - // Y-axis is almost like line_1 but should be orthogonal to the Z axis. - // Moreover line_1 is downward oriented as the screen CS. - Vec column_1 = -((column_2 ^ line_1) ^ column_2); - column_1.normalize(); - - qreal rot[3][3]; - rot[0][0] = column_0[0]; - rot[1][0] = column_0[1]; - rot[2][0] = column_0[2]; - - rot[0][1] = column_1[0]; - rot[1][1] = column_1[1]; - rot[2][1] = column_1[2]; - - rot[0][2] = column_2[0]; - rot[1][2] = column_2[1]; - rot[2][2] = column_2[2]; - - // We compute the field of view - - // line_1^column_0 -> vector of intersection line between - // y_screen=0 and x_camera=0 plane. - // column_2*(...) -> cos of the angle between Z vector et y_screen=0 plane - // * 2 -> field of view = 2 * half angle - - // We need some intermediate values. - Vec dummy = line_1 ^ column_0; - dummy.normalize(); - qreal fov = acos(column_2 * dummy) * 2.0; - - // We set the camera. - Quaternion q; - q.setFromRotationMatrix(rot); - setOrientation(q); - setPosition(cam_pos); - setFieldOfView(fov); -} - -/* - // persp : projectionMatrix_[0] = f/aspectRatio(); -void Camera::setFromProjectionMatrix(const GLdouble* projectionMatrix) -{ - QString message; - if ((fabs(projectionMatrix[1]) > 1E-3) || - (fabs(projectionMatrix[2]) > 1E-3) || - (fabs(projectionMatrix[3]) > 1E-3) || - (fabs(projectionMatrix[4]) > 1E-3) || - (fabs(projectionMatrix[6]) > 1E-3) || - (fabs(projectionMatrix[7]) > 1E-3) || - (fabs(projectionMatrix[8]) > 1E-3) || - (fabs(projectionMatrix[9]) > 1E-3)) - message = "Non null coefficient in projection matrix - Aborting"; - else - if ((fabs(projectionMatrix[11]+1.0) < 1E-5) && (fabs(projectionMatrix[15]) < -1E-5)) - { - if (projectionMatrix[5] < 1E-4) - message="Negative field of view in Camera::setFromProjectionMatrix"; - else - setType(Camera::PERSPECTIVE); - } - else - if ((fabs(projectionMatrix[11]) < 1E-5) && (fabs(projectionMatrix[15]-1.0) < -1E-5)) setType(Camera::ORTHOGRAPHIC); else message = "Unable to determine camera -type in setFromProjectionMatrix - Aborting"; - - if (!message.isEmpty()) - { - qWarning(message); - return; - } - - switch (type()) - { - case Camera::PERSPECTIVE: - { - setFieldOfView(2.0 * atan(1.0/projectionMatrix[5])); - const qreal far = projectionMatrix[14] / (2.0 * (1.0 + projectionMatrix[10])); - const qreal near = (projectionMatrix[10]+1.0) / (projectionMatrix[10]-1.0) * -far; setSceneRadius((far-near)/2.0); setSceneCenter(position() + (near + -sceneRadius())*viewDirection()); break; - } - case Camera::ORTHOGRAPHIC: - { - GLdouble w, h; - getOrthoWidthHeight(w,h); - projectionMatrix_[0] = 1.0/w; - projectionMatrix_[5] = 1.0/h; - projectionMatrix_[10] = -2.0/(ZFar - ZNear); - projectionMatrix_[11] = 0.0; - projectionMatrix_[14] = -(ZFar + ZNear)/(ZFar - ZNear); - projectionMatrix_[15] = 1.0; - // same as glOrtho( -w, w, -h, h, zNear(), zFar() ); - break; - } - } -} -*/ - -///////////////////////// Camera to world transform /////////////////////// - -/*! Same as cameraCoordinatesOf(), but with \c qreal[3] parameters (\p src and - * \p res may be identical pointers). */ -void Camera::getCameraCoordinatesOf(const qreal src[3], qreal res[3]) const { - Vec r = cameraCoordinatesOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as worldCoordinatesOf(), but with \c qreal[3] parameters (\p src and \p - * res may be identical pointers). */ -void Camera::getWorldCoordinatesOf(const qreal src[3], qreal res[3]) const { - Vec r = worldCoordinatesOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Fills \p viewport with the Camera OpenGL viewport. - -This method is mainly used in conjunction with \c gluProject, which requires -such a viewport. Returned values are (0, screenHeight(), screenWidth(), - -screenHeight()), so that the origin is located in the \e upper left corner of -the window (Qt style coordinate system). */ -void Camera::getViewport(GLint viewport[4]) const { - viewport[0] = 0; - viewport[1] = screenHeight(); - viewport[2] = screenWidth(); - viewport[3] = -screenHeight(); -} - -//source code of GluProject and GluUnproject, imported here to avoid the dependency to Glu -int project(qreal objx, qreal objy, qreal objz, GLdouble *modelview, - GLdouble *projection, int *viewport, GLdouble*winX, GLdouble *winY,GLdouble *winZ) - { - //Transformation vectors - GLdouble fTempo[8]; - //Modelview transform - fTempo[0]=modelview[0]*objx+modelview[4]*objy+modelview[8]*objz+modelview[12]; //w is always 1 - fTempo[1]=modelview[1]*objx+modelview[5]*objy+modelview[9]*objz+modelview[13]; - fTempo[2]=modelview[2]*objx+modelview[6]*objy+modelview[10]*objz+modelview[14]; - fTempo[3]=modelview[3]*objx+modelview[7]*objy+modelview[11]*objz+modelview[15]; - //Projection transform, the final row of projection matrix is always [0 0 -1 0] - //so we optimize for that. - fTempo[4]=projection[0]*fTempo[0]+projection[4]*fTempo[1]+projection[8]*fTempo[2]+projection[12]*fTempo[3]; - fTempo[5]=projection[1]*fTempo[0]+projection[5]*fTempo[1]+projection[9]*fTempo[2]+projection[13]*fTempo[3]; - fTempo[6]=projection[2]*fTempo[0]+projection[6]*fTempo[1]+projection[10]*fTempo[2]+projection[14]*fTempo[3]; - fTempo[7]=-fTempo[2]; - //The result normalizes between -1 and 1 - if(fTempo[7]==0.0) //The w value - return 0; - fTempo[7]=1.0/fTempo[7]; - //Perspective division - fTempo[4]*=fTempo[7]; - fTempo[5]*=fTempo[7]; - fTempo[6]*=fTempo[7]; - //Window coordinates - //Map x, y to range 0-1 - *winX=(fTempo[4]*0.5+0.5)*viewport[2]+viewport[0]; - *winY=(fTempo[5]*0.5+0.5)*viewport[3]+viewport[1]; - //This is only correct when glDepthRange(0.0, 1.0) - *winZ=(1.0+fTempo[6])*0.5; //Between 0 and 1 - return 1; -} - -void MultiplyMatrices4by4OpenGL_GLdouble(GLdouble *result, GLdouble *matrix1, GLdouble *matrix2) - { - result[0]=matrix1[0]*matrix2[0]+ - matrix1[4]*matrix2[1]+ - matrix1[8]*matrix2[2]+ - matrix1[12]*matrix2[3]; - result[4]=matrix1[0]*matrix2[4]+ - matrix1[4]*matrix2[5]+ - matrix1[8]*matrix2[6]+ - matrix1[12]*matrix2[7]; - result[8]=matrix1[0]*matrix2[8]+ - matrix1[4]*matrix2[9]+ - matrix1[8]*matrix2[10]+ - matrix1[12]*matrix2[11]; - result[12]=matrix1[0]*matrix2[12]+ - matrix1[4]*matrix2[13]+ - matrix1[8]*matrix2[14]+ - matrix1[12]*matrix2[15]; - result[1]=matrix1[1]*matrix2[0]+ - matrix1[5]*matrix2[1]+ - matrix1[9]*matrix2[2]+ - matrix1[13]*matrix2[3]; - result[5]=matrix1[1]*matrix2[4]+ - matrix1[5]*matrix2[5]+ - matrix1[9]*matrix2[6]+ - matrix1[13]*matrix2[7]; - result[9]=matrix1[1]*matrix2[8]+ - matrix1[5]*matrix2[9]+ - matrix1[9]*matrix2[10]+ - matrix1[13]*matrix2[11]; - result[13]=matrix1[1]*matrix2[12]+ - matrix1[5]*matrix2[13]+ - matrix1[9]*matrix2[14]+ - matrix1[13]*matrix2[15]; - result[2]=matrix1[2]*matrix2[0]+ - matrix1[6]*matrix2[1]+ - matrix1[10]*matrix2[2]+ - matrix1[14]*matrix2[3]; - result[6]=matrix1[2]*matrix2[4]+ - matrix1[6]*matrix2[5]+ - matrix1[10]*matrix2[6]+ - matrix1[14]*matrix2[7]; - result[10]=matrix1[2]*matrix2[8]+ - matrix1[6]*matrix2[9]+ - matrix1[10]*matrix2[10]+ - matrix1[14]*matrix2[11]; - result[14]=matrix1[2]*matrix2[12]+ - matrix1[6]*matrix2[13]+ - matrix1[10]*matrix2[14]+ - matrix1[14]*matrix2[15]; - result[3]=matrix1[3]*matrix2[0]+ - matrix1[7]*matrix2[1]+ - matrix1[11]*matrix2[2]+ - matrix1[15]*matrix2[3]; - result[7]=matrix1[3]*matrix2[4]+ - matrix1[7]*matrix2[5]+ - matrix1[11]*matrix2[6]+ - matrix1[15]*matrix2[7]; - result[11]=matrix1[3]*matrix2[8]+ - matrix1[7]*matrix2[9]+ - matrix1[11]*matrix2[10]+ - matrix1[15]*matrix2[11]; - result[15]=matrix1[3]*matrix2[12]+ - matrix1[7]*matrix2[13]+ - matrix1[11]*matrix2[14]+ - matrix1[15]*matrix2[15]; - } - - void MultiplyMatrixByVector4by4OpenGL_GLdouble(GLdouble *resultvector, const GLdouble *matrix, const GLdouble *pvector) - { - resultvector[0]=matrix[0]*pvector[0]+matrix[4]*pvector[1]+matrix[8]*pvector[2]+matrix[12]*pvector[3]; - resultvector[1]=matrix[1]*pvector[0]+matrix[5]*pvector[1]+matrix[9]*pvector[2]+matrix[13]*pvector[3]; - resultvector[2]=matrix[2]*pvector[0]+matrix[6]*pvector[1]+matrix[10]*pvector[2]+matrix[14]*pvector[3]; - resultvector[3]=matrix[3]*pvector[0]+matrix[7]*pvector[1]+matrix[11]*pvector[2]+matrix[15]*pvector[3]; - } - - #define SWAP_ROWS_DOUBLE(a, b) { double *_tmp = a; (a)=(b); (b)=_tmp; } - #define SWAP_ROWS_GLdouble(a, b) { GLdouble *_tmp = a; (a)=(b); (b)=_tmp; } - #define MAT(m,r,c) (m)[(c)*4+(r)] - //This code comes directly from GLU except that it is for GLdouble - int glhInvertMatrixf2(GLdouble *m, GLdouble *out) - { - GLdouble wtmp[4][8]; - GLdouble m0, m1, m2, m3, s; - GLdouble *r0, *r1, *r2, *r3; - r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; - r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1), - r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3), - r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0, - r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1), - r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3), - r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0, - r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1), - r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3), - r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0, - r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1), - r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3), - r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0; - /* choose pivot - or die */ - if (fabs(r3[0]) > fabs(r2[0])) - SWAP_ROWS_GLdouble(r3, r2); - if (fabs(r2[0]) > fabs(r1[0])) - SWAP_ROWS_GLdouble(r2, r1); - if (fabs(r1[0]) > fabs(r0[0])) - SWAP_ROWS_GLdouble(r1, r0); - if (0.0 == r0[0]) - return 0; - /* eliminate first variable */ - m1 = r1[0] / r0[0]; - m2 = r2[0] / r0[0]; - m3 = r3[0] / r0[0]; - s = r0[1]; - r1[1] -= m1 * s; - r2[1] -= m2 * s; - r3[1] -= m3 * s; - s = r0[2]; - r1[2] -= m1 * s; - r2[2] -= m2 * s; - r3[2] -= m3 * s; - s = r0[3]; - r1[3] -= m1 * s; - r2[3] -= m2 * s; - r3[3] -= m3 * s; - s = r0[4]; - if (s != 0.0) { - r1[4] -= m1 * s; - r2[4] -= m2 * s; - r3[4] -= m3 * s; - } - s = r0[5]; - if (s != 0.0) { - r1[5] -= m1 * s; - r2[5] -= m2 * s; - r3[5] -= m3 * s; - } - s = r0[6]; - if (s != 0.0) { - r1[6] -= m1 * s; - r2[6] -= m2 * s; - r3[6] -= m3 * s; - } - s = r0[7]; - if (s != 0.0) { - r1[7] -= m1 * s; - r2[7] -= m2 * s; - r3[7] -= m3 * s; - } - /* choose pivot - or die */ - if (fabs(r3[1]) > fabs(r2[1])) - SWAP_ROWS_GLdouble(r3, r2); - if (fabs(r2[1]) > fabs(r1[1])) - SWAP_ROWS_GLdouble(r2, r1); - if (0.0 == r1[1]) - return 0; - /* eliminate second variable */ - m2 = r2[1] / r1[1]; - m3 = r3[1] / r1[1]; - r2[2] -= m2 * r1[2]; - r3[2] -= m3 * r1[2]; - r2[3] -= m2 * r1[3]; - r3[3] -= m3 * r1[3]; - s = r1[4]; - if (0.0 != s) { - r2[4] -= m2 * s; - r3[4] -= m3 * s; - } - s = r1[5]; - if (0.0 != s) { - r2[5] -= m2 * s; - r3[5] -= m3 * s; - } - s = r1[6]; - if (0.0 != s) { - r2[6] -= m2 * s; - r3[6] -= m3 * s; - } - s = r1[7]; - if (0.0 != s) { - r2[7] -= m2 * s; - r3[7] -= m3 * s; - } - /* choose pivot - or die */ - if (fabs(r3[2]) > fabs(r2[2])) - SWAP_ROWS_GLdouble(r3, r2); - if (0.0 == r2[2]) - return 0; - /* eliminate third variable */ - m3 = r3[2] / r2[2]; - r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], - r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7]; - /* last check */ - if (0.0 == r3[3]) - return 0; - s = 1.0 / r3[3]; /* now back substitute row 3 */ - r3[4] *= s; - r3[5] *= s; - r3[6] *= s; - r3[7] *= s; - m2 = r2[3]; /* now back substitute row 2 */ - s = 1.0 / r2[2]; - r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), - r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); - m1 = r1[3]; - r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, - r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; - m0 = r0[3]; - r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, - r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; - m1 = r1[2]; /* now back substitute row 1 */ - s = 1.0 / r1[1]; - r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), - r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); - m0 = r0[2]; - r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, - r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; - m0 = r0[1]; /* now back substitute row 0 */ - s = 1.0 / r0[0]; - r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), - r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); - MAT(out, 0, 0) = r0[4]; - MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6]; - MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4]; - MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6]; - MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4]; - MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6]; - MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4]; - MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6]; - MAT(out, 3, 3) = r3[7]; - return 1; - } - -int unProject(GLdouble winx, GLdouble winy, GLdouble winz, GLdouble *modelview, GLdouble *projection, int *viewport, - GLdouble *objX,GLdouble *objY,GLdouble *objZ) - { - //Transformation matrices - GLdouble m[16], A[16]; - GLdouble in[4], out[4]; - //Calculation for inverting a matrix, compute projection x modelview - //and store in A[16] - MultiplyMatrices4by4OpenGL_GLdouble(A, projection, modelview); - //Now compute the inverse of matrix A - if(glhInvertMatrixf2(A, m)==0) - return 0; - //Transformation of normalized coordinates between -1 and 1 - in[0]=(winx-(GLdouble)viewport[0])/(GLdouble)viewport[2]*2.0-1.0; - in[1]=(winy-(GLdouble)viewport[1])/(GLdouble)viewport[3]*2.0-1.0; - in[2]=2.0*winz-1.0; - in[3]=1.0; - //Objects coordinates - MultiplyMatrixByVector4by4OpenGL_GLdouble(out, m, in); - if(out[3]==0.0) - return 0; - out[3]=1.0/out[3]; - *objX=out[0]*out[3]; - *objY=out[1]*out[3]; - *objZ=out[2]*out[3]; - return 1; - } - -/*! Returns the screen projected coordinates of a point \p src defined in the \p frame coordinate - system. - When \p frame in \c NULL (default), \p src is expressed in the world coordinate system. - The x and y coordinates of the returned Vec are expressed in pixel, (0,0) being the \e upper left - corner of the window. The z coordinate ranges between 0.0 (near plane) and 1.0 (excluded, far - plane). See the \c gluProject man page for details. - unprojectedCoordinatesOf() performs the inverse transformation. - See the screenCoordSystem example. - This method only uses the intrinsic Camera parameters (see getModelViewMatrix(), - getProjectionMatrix() and getViewport()) and is completely independent of the OpenGL \c - GL_MODELVIEW, \c GL_PROJECTION and viewport matrices. You can hence define a virtual Camera and use - this method to compute projections out of a classical rendering context. - \attention However, if your Camera is not attached to a QGLViewer (used for offscreen computations - for instance), make sure the Camera matrices are updated before calling this method. Call - computeModelViewMatrix() and computeProjectionMatrix() to do so. - If you call this method several times with no change in the matrices, consider precomputing the - projection times modelview matrix to save computation time if required (\c P x \c M in the \c - gluProject man page). - Here is the code corresponding to what this method does (kindly submitted by Robert W. Kuhn) : - \code - Vec project(Vec point) - { - GLint Viewport[4]; - GLdouble Projection[16], Modelview[16]; - GLdouble matrix[16]; - // Precomputation begin - glGetIntegerv(GL_VIEWPORT , Viewport); - glGetDoublev (GL_MODELVIEW_MATRIX , Modelview); - glGetDoublev (GL_PROJECTION_MATRIX, Projection); - for (unsigned short m=0; m<4; ++m) - { - for (unsigned short l=0; l<4; ++l) - { - qreal sum = 0.0; - for (unsigned short k=0; k<4; ++k) - sum += Projection[l+4*k]*Modelview[k+4*m]; - matrix[l+4*m] = sum; - } - } - // Precomputation end - GLdouble v[4], vs[4]; - v[0]=point[0]; v[1]=point[1]; v[2]=point[2]; v[3]=1.0; - vs[0]=matrix[0 ]*v[0] + matrix[4 ]*v[1] + matrix[8 ]*v[2] + matrix[12 ]*v[3]; - vs[1]=matrix[1 ]*v[0] + matrix[5 ]*v[1] + matrix[9 ]*v[2] + matrix[13 ]*v[3]; - vs[2]=matrix[2 ]*v[0] + matrix[6 ]*v[1] + matrix[10]*v[2] + matrix[14 ]*v[3]; - vs[3]=matrix[3 ]*v[0] + matrix[7 ]*v[1] + matrix[11]*v[2] + matrix[15 ]*v[3]; - vs[0] /= vs[3]; - vs[1] /= vs[3]; - vs[2] /= vs[3]; - vs[0] = vs[0] * 0.5 + 0.5; - vs[1] = vs[1] * 0.5 + 0.5; - vs[2] = vs[2] * 0.5 + 0.5; - vs[0] = vs[0] * Viewport[2] + Viewport[0]; - vs[1] = vs[1] * Viewport[3] + Viewport[1]; - return Vec(vs[0], Viewport[3]-vs[1], vs[2]); - } - \endcode - */ -Vec Camera::projectedCoordinatesOf(const Vec& src, const Frame* frame) const -{ - GLdouble x,y,z; - static GLint viewport[4]; - getViewport(viewport); - - if (frame) - { - const Vec tmp = frame->inverseCoordinatesOf(src); - project(tmp.x,tmp.y,tmp.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z); - } - else - project(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z); - - return Vec(x,y,z); -} - -/*! Returns the world unprojected coordinates of a point \p src defined in the screen coordinate - system. - The \p src.x and \p src.y input values are expressed in pixels, (0,0) being the \e upper left corner - of the window. \p src.z is a depth value ranging in [0..1[ (respectively corresponding to the near - and far planes). Note that src.z is \e not a linear interpolation between zNear and zFar. - /code - src.z = zFar() / (zFar() - zNear()) * (1.0 - zNear() / z); - /endcode - Where z is the distance from the point you project to the camera, along the viewDirection(). - See the \c gluUnProject man page for details. - The result is expressed in the \p frame coordinate system. When \p frame is \c NULL (default), the - result is expressed in the world coordinates system. The possible \p frame Frame::referenceFrame() - are taken into account. - projectedCoordinatesOf() performs the inverse transformation. - This method only uses the intrinsic Camera parameters (see getModelViewMatrix(), - getProjectionMatrix() and getViewport()) and is completely independent of the OpenGL \c - GL_MODELVIEW, \c GL_PROJECTION and viewport matrices. You can hence define a virtual Camera and use - this method to compute un-projections out of a classical rendering context. - \attention However, if your Camera is not attached to a QGLViewer (used for offscreen computations - for instance), make sure the Camera matrices are updated before calling this method (use - computeModelViewMatrix(), computeProjectionMatrix()). See also setScreenWidthAndHeight(). - This method is not computationally optimized. If you call it several times with no change in the - matrices, you should buffer the entire inverse projection matrix (modelview, projection and then - viewport) to speed-up the queries. See the \c gluUnProject man page for details. */ -Vec Camera::unprojectedCoordinatesOf(const Vec& src, const Frame* frame) const -{ - GLdouble x,y,z; - static GLint viewport[4]; - getViewport(viewport); - unProject(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z); - if (frame) - return frame->coordinatesOf(Vec(x,y,z)); - else - return Vec(x,y,z); -} - -/*! Same as projectedCoordinatesOf(), but with \c qreal parameters (\p src and - * \p res can be identical pointers). */ -void Camera::getProjectedCoordinatesOf(const qreal src[3], qreal res[3], - const Frame *frame) const { - Vec r = projectedCoordinatesOf(Vec(src), frame); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as unprojectedCoordinatesOf(), but with \c qreal parameters (\p src and - * \p res can be identical pointers). */ -void Camera::getUnprojectedCoordinatesOf(const qreal src[3], qreal res[3], - const Frame *frame) const { - Vec r = unprojectedCoordinatesOf(Vec(src), frame); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -///////////////////////////////////// KFI -//////////////////////////////////////////// - -/*! Returns the KeyFrameInterpolator that defines the Camera path number \p i. - -If path \p i is not defined for this index, the method returns a \c NULL -pointer. */ -KeyFrameInterpolator *Camera::keyFrameInterpolator(unsigned int i) const { - if (kfi_.contains(i)) - return kfi_[i]; - else - return NULL; -} - -/*! Sets the KeyFrameInterpolator that defines the Camera path of index \p i. - - The previous keyFrameInterpolator() is lost and should be deleted by the - calling method if needed. - - The KeyFrameInterpolator::interpolated() signal of \p kfi probably needs to be - connected to the Camera's associated QGLViewer::update() slot, so that when the - Camera position is interpolated using \p kfi, every interpolation step updates - the display: \code myViewer.camera()->deletePath(3); - myViewer.camera()->setKeyFrameInterpolator(3, myKeyFrameInterpolator); - connect(myKeyFrameInterpolator, SIGNAL(interpolated()), myViewer, - SLOT(update()); \endcode - - \note These connections are done automatically when a Camera is attached to a - QGLViewer, or when a new KeyFrameInterpolator is defined using the - QGLViewer::addKeyFrameKeyboardModifiers() and QGLViewer::pathKey() (default is - Alt+F[1-12]). See the keyboard page for details. - */ -void Camera::setKeyFrameInterpolator(unsigned int i, - KeyFrameInterpolator *const kfi) { - if (kfi) - kfi_[i] = kfi; - else - kfi_.remove(i); -} - -/*! Adds the current Camera position() and orientation() as a keyFrame to the -path number \p i. - -This method can also be used if you simply want to save a Camera point of view -(a path made of a single keyFrame). Use playPath() to make the Camera play the -keyFrame path (resp. restore the point of view). Use deletePath() to clear the -path. - -The default keyboard shortcut for this method is Alt+F[1-12]. Set -QGLViewer::pathKey() and QGLViewer::addKeyFrameKeyboardModifiers(). - -If you use directly this method and the keyFrameInterpolator(i) does not exist, -a new one is created. Its KeyFrameInterpolator::interpolated() signal should -then be connected to the QGLViewer::update() slot (see -setKeyFrameInterpolator()). */ -void Camera::addKeyFrameToPath(unsigned int i) { - if (!kfi_.contains(i)) - setKeyFrameInterpolator(i, new KeyFrameInterpolator(frame())); - - kfi_[i]->addKeyFrame(*(frame())); -} - -/*! Makes the Camera follow the path of keyFrameInterpolator() number \p i. - - If the interpolation is started, it stops it instead. - - This method silently ignores undefined (empty) paths (see - keyFrameInterpolator()). - - The default keyboard shortcut for this method is F[1-12]. Set - QGLViewer::pathKey() and QGLViewer::playPathKeyboardModifiers(). */ -void Camera::playPath(unsigned int i) { - if (kfi_.contains(i)) { - if (kfi_[i]->interpolationIsStarted()) - kfi_[i]->stopInterpolation(); - else - kfi_[i]->startInterpolation(); - } -} - -/*! Resets the path of the keyFrameInterpolator() number \p i. - -If this path is \e not being played (see playPath() and -KeyFrameInterpolator::interpolationIsStarted()), resets it to its starting -position (see KeyFrameInterpolator::resetInterpolation()). If the path is -played, simply stops interpolation. */ -void Camera::resetPath(unsigned int i) { - if (kfi_.contains(i)) { - if ((kfi_[i]->interpolationIsStarted())) - kfi_[i]->stopInterpolation(); - else { - kfi_[i]->resetInterpolation(); - kfi_[i]->interpolateAtTime(kfi_[i]->interpolationTime()); - } - } -} - -/*! Deletes the keyFrameInterpolator() of index \p i. - -Disconnect the keyFrameInterpolator() KeyFrameInterpolator::interpolated() -signal before deleting the keyFrameInterpolator() if needed: \code -disconnect(camera()->keyFrameInterpolator(i), SIGNAL(interpolated()), this, -SLOT(update())); camera()->deletePath(i); \endcode */ -void Camera::deletePath(unsigned int i) { - if (kfi_.contains(i)) { - kfi_[i]->stopInterpolation(); - delete kfi_[i]; - kfi_.remove(i); - } -} - -/*! Draws all the Camera paths defined by the keyFrameInterpolator(). - - Simply calls KeyFrameInterpolator::drawPath() for all the defined paths. The - path color is the current \c glColor(). - - \attention The OpenGL state is modified by this method: see - KeyFrameInterpolator::drawPath(). */ -void Camera::drawAllPaths() { - for (QMap::ConstIterator - it = kfi_.begin(), - end = kfi_.end(); - it != end; ++it) - (it.value())->drawPath(3, 5, sceneRadius()); -} - -//////////////////////////////////////////////////////////////////////////////// - -/*! Returns an XML \c QDomElement that represents the Camera. - - \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument - factory used to create QDomElement. - - Concatenates the Camera parameters, the ManipulatedCameraFrame::domElement() - and the paths' KeyFrameInterpolator::domElement(). - - Use initFromDOMElement() to restore the Camera state from the resulting \c - QDomElement. - - If you want to save the Camera state in a file, use: - \code - QDomDocument document("myCamera"); - doc.appendChild( myCamera->domElement("Camera", document) ); - - QFile f("myCamera.xml"); - if (f.open(IO_WriteOnly)) - { - QTextStream out(&f); - document.save(out, 2); - } - \endcode - - Note that the QGLViewer::camera() is automatically saved by - QGLViewer::saveStateToFile() when a QGLViewer is closed. Use - QGLViewer::restoreStateFromFile() to restore it back. */ -QDomElement Camera::domElement(const QString &name, - QDomDocument &document) const { - QDomElement de = document.createElement(name); - QDomElement paramNode = document.createElement("Parameters"); - paramNode.setAttribute("fieldOfView", QString::number(fieldOfView())); - paramNode.setAttribute("zNearCoefficient", - QString::number(zNearCoefficient())); - paramNode.setAttribute("zClippingCoefficient", - QString::number(zClippingCoefficient())); - paramNode.setAttribute("orthoCoef", QString::number(orthoCoef_)); - paramNode.setAttribute("sceneRadius", QString::number(sceneRadius())); - paramNode.appendChild(sceneCenter().domElement("SceneCenter", document)); - - switch (type()) { - case Camera::PERSPECTIVE: - paramNode.setAttribute("Type", "PERSPECTIVE"); - break; - case Camera::ORTHOGRAPHIC: - paramNode.setAttribute("Type", "ORTHOGRAPHIC"); - break; - } - de.appendChild(paramNode); - - QDomElement stereoNode = document.createElement("Stereo"); - stereoNode.setAttribute("IODist", QString::number(IODistance())); - stereoNode.setAttribute("focusDistance", QString::number(focusDistance())); - stereoNode.setAttribute("physScreenWidth", - QString::number(physicalScreenWidth())); - de.appendChild(stereoNode); - - de.appendChild(frame()->domElement("ManipulatedCameraFrame", document)); - - // KeyFrame paths - for (QMap::ConstIterator - it = kfi_.begin(), - end = kfi_.end(); - it != end; ++it) { - QDomElement kfNode = - (it.value())->domElement("KeyFrameInterpolator", document); - kfNode.setAttribute("index", QString::number(it.key())); - de.appendChild(kfNode); - } - - return de; -} - -/*! Restores the Camera state from a \c QDomElement created by domElement(). - - Use the following code to retrieve a Camera state from a file created using - domElement(): \code - // Load DOM from file - QDomDocument document; - QFile f("myCamera.xml"); - if (f.open(IO_ReadOnly)) - { - document.setContent(&f); - f.close(); - } - - // Parse the DOM tree - QDomElement main = document.documentElement(); - myCamera->initFromDOMElement(main); - \endcode - - The frame() pointer is not modified by this method. The frame() state is - however modified. - - \attention The original keyFrameInterpolator() are deleted and should be copied - first if they are shared. */ -void Camera::initFromDOMElement(const QDomElement &element) { - QDomElement child = element.firstChild().toElement(); - - QMutableMapIterator it(kfi_); - while (it.hasNext()) { - it.next(); - deletePath(it.key()); - } - - while (!child.isNull()) { - if (child.tagName() == "Parameters") { - // #CONNECTION# Default values set in constructor - setFieldOfView(DomUtils::qrealFromDom(child, "fieldOfView", M_PI / 4.0)); - setZNearCoefficient( - DomUtils::qrealFromDom(child, "zNearCoefficient", 0.005)); - setZClippingCoefficient( - DomUtils::qrealFromDom(child, "zClippingCoefficient", sqrt(3.0))); - orthoCoef_ = - DomUtils::qrealFromDom(child, "orthoCoef", tan(fieldOfView() / 2.0)); - setSceneRadius( - DomUtils::qrealFromDom(child, "sceneRadius", sceneRadius())); - - setType(PERSPECTIVE); - QString type = child.attribute("Type", "PERSPECTIVE"); - if (type == "PERSPECTIVE") - setType(Camera::PERSPECTIVE); - if (type == "ORTHOGRAPHIC") - setType(Camera::ORTHOGRAPHIC); - - QDomElement child2 = child.firstChild().toElement(); - while (!child2.isNull()) { - /* Although the scene does not change when a camera is loaded, restore - the saved center and radius values. Mainly useful when a the viewer is - restored on startup, with possible additional cameras. */ - if (child2.tagName() == "SceneCenter") - setSceneCenter(Vec(child2)); - - child2 = child2.nextSibling().toElement(); - } - } - - if (child.tagName() == "ManipulatedCameraFrame") - frame()->initFromDOMElement(child); - - if (child.tagName() == "Stereo") { - setIODistance(DomUtils::qrealFromDom(child, "IODist", 0.062)); - setFocusDistance( - DomUtils::qrealFromDom(child, "focusDistance", focusDistance())); - setPhysicalScreenWidth( - DomUtils::qrealFromDom(child, "physScreenWidth", 0.5)); - } - - if (child.tagName() == "KeyFrameInterpolator") { - unsigned int index = DomUtils::uintFromDom(child, "index", 0); - setKeyFrameInterpolator(index, new KeyFrameInterpolator(frame())); - if (keyFrameInterpolator(index)) - keyFrameInterpolator(index)->initFromDOMElement(child); - } - - child = child.nextSibling().toElement(); - } -} - -/*! Gives the coefficients of a 3D half-line passing through the Camera eye and - pixel (x,y). - - The origin of the half line (eye position) is stored in \p orig, while \p dir - contains the properly oriented and normalized direction of the half line. - - \p x and \p y are expressed in Qt format (origin in the upper left corner). Use - screenHeight() - y to convert to OpenGL units. - - This method is useful for analytical intersection in a selection method. - - See the select example for an - illustration. */ -void Camera::convertClickToLine(const QPoint &pixel, Vec &orig, - Vec &dir) const { - switch (type()) { - case Camera::PERSPECTIVE: - orig = position(); - dir = Vec(((2.0 * pixel.x() / screenWidth()) - 1.0) * - tan(fieldOfView() / 2.0) * aspectRatio(), - ((2.0 * (screenHeight() - pixel.y()) / screenHeight()) - 1.0) * - tan(fieldOfView() / 2.0), - -1.0); - dir = worldCoordinatesOf(dir) - orig; - dir.normalize(); - break; - - case Camera::ORTHOGRAPHIC: { - GLdouble w, h; - getOrthoWidthHeight(w, h); - orig = Vec((2.0 * pixel.x() / screenWidth() - 1.0) * w, - -(2.0 * pixel.y() / screenHeight() - 1.0) * h, 0.0); - orig = worldCoordinatesOf(orig); - dir = viewDirection(); - break; - } - } -} - -#ifndef DOXYGEN -/*! This method has been deprecated in libQGLViewer version 2.2.0 */ -void Camera::drawCamera(qreal, qreal, qreal) { - qWarning("drawCamera is deprecated. Use Camera::draw() instead."); -} -#endif - -/*! Draws a representation of the Camera in the 3D world. - -The near and far planes are drawn as quads, the frustum is drawn using lines and -the camera up vector is represented by an arrow to disambiguate the drawing. See -the standardCamera example for an -illustration. - -Note that the current \c glColor and \c glPolygonMode are used to draw the near -and far planes. See the frustumCulling -example for an example of semi-transparent plane drawing. Similarly, the -current \c glLineWidth and \c glColor is used to draw the frustum outline. - -When \p drawFarPlane is \c false, only the near plane is drawn. \p scale can be -used to scale the drawing: a value of 1.0 (default) will draw the Camera's -frustum at its actual size. - -This method assumes that the \c glMatrixMode is \c GL_MODELVIEW and that the -current ModelView matrix corresponds to the world coordinate system (as it is at -the beginning of QGLViewer::draw()). The Camera is then correctly positioned and -orientated. - -\note The drawing of a QGLViewer's own QGLViewer::camera() should not be -visible, but may create artefacts due to numerical imprecisions. */ -void Camera::draw(bool drawFarPlane, qreal scale) const { - glPushMatrix(); - glMultMatrixd(frame()->worldMatrix()); - - // 0 is the upper left coordinates of the near corner, 1 for the far one - Vec points[2]; - - points[0].z = scale * zNear(); - points[1].z = scale * zFar(); - - switch (type()) { - case Camera::PERSPECTIVE: { - points[0].y = points[0].z * tan(fieldOfView() / 2.0); - points[0].x = points[0].y * aspectRatio(); - - const qreal ratio = points[1].z / points[0].z; - - points[1].y = ratio * points[0].y; - points[1].x = ratio * points[0].x; - break; - } - case Camera::ORTHOGRAPHIC: { - GLdouble hw, hh; - getOrthoWidthHeight(hw, hh); - points[0].x = points[1].x = scale * qreal(hw); - points[0].y = points[1].y = scale * qreal(hh); - break; - } - } - - const int farIndex = drawFarPlane ? 1 : 0; - - // Near and (optionally) far plane(s) - glBegin(GL_QUADS); - for (int i = farIndex; i >= 0; --i) { - glNormal3d(0.0f, 0.0f, (i == 0) ? 1.0f : -1.0f); - glVertex3d(points[i].x, points[i].y, -points[i].z); - glVertex3d(-points[i].x, points[i].y, -points[i].z); - glVertex3d(-points[i].x, -points[i].y, -points[i].z); - glVertex3d(points[i].x, -points[i].y, -points[i].z); - } - glEnd(); - - // Up arrow - const qreal arrowHeight = 1.5 * points[0].y; - const qreal baseHeight = 1.2 * points[0].y; - const qreal arrowHalfWidth = 0.5 * points[0].x; - const qreal baseHalfWidth = 0.3 * points[0].x; - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - // Base - glBegin(GL_QUADS); - glVertex3d(-baseHalfWidth, points[0].y, -points[0].z); - glVertex3d(baseHalfWidth, points[0].y, -points[0].z); - glVertex3d(baseHalfWidth, baseHeight, -points[0].z); - glVertex3d(-baseHalfWidth, baseHeight, -points[0].z); - glEnd(); - - // Arrow - glBegin(GL_TRIANGLES); - glVertex3d(0.0, arrowHeight, -points[0].z); - glVertex3d(-arrowHalfWidth, baseHeight, -points[0].z); - glVertex3d(arrowHalfWidth, baseHeight, -points[0].z); - glEnd(); - - // Frustum lines - switch (type()) { - case Camera::PERSPECTIVE: - glBegin(GL_LINES); - glVertex3d(0.0, 0.0, 0.0); - glVertex3d(points[farIndex].x, points[farIndex].y, -points[farIndex].z); - glVertex3d(0.0, 0.0, 0.0); - glVertex3d(-points[farIndex].x, points[farIndex].y, -points[farIndex].z); - glVertex3d(0.0, 0.0, 0.0); - glVertex3d(-points[farIndex].x, -points[farIndex].y, -points[farIndex].z); - glVertex3d(0.0, 0.0, 0.0); - glVertex3d(points[farIndex].x, -points[farIndex].y, -points[farIndex].z); - glEnd(); - break; - case Camera::ORTHOGRAPHIC: - if (drawFarPlane) { - glBegin(GL_LINES); - glVertex3d(points[0].x, points[0].y, -points[0].z); - glVertex3d(points[1].x, points[1].y, -points[1].z); - glVertex3d(-points[0].x, points[0].y, -points[0].z); - glVertex3d(-points[1].x, points[1].y, -points[1].z); - glVertex3d(-points[0].x, -points[0].y, -points[0].z); - glVertex3d(-points[1].x, -points[1].y, -points[1].z); - glVertex3d(points[0].x, -points[0].y, -points[0].z); - glVertex3d(points[1].x, -points[1].y, -points[1].z); - glEnd(); - } - } - - glPopMatrix(); -} - -/*! Returns the 6 plane equations of the Camera frustum. - -The six 4-component vectors of \p coef respectively correspond to the left, -right, near, far, top and bottom Camera frustum planes. Each vector holds a -plane equation of the form: \code a*x + b*y + c*z + d = 0 \endcode where \c a, -\c b, \c c and \c d are the 4 components of each vector, in that order. - -See the frustumCulling example for -an application. - -This format is compatible with the \c glClipPlane() function. One camera frustum -plane can hence be applied in an other viewer to visualize the culling results: -\code - // Retrieve plane equations - GLdouble coef[6][4]; - mainViewer->camera()->getFrustumPlanesCoefficients(coef); - - // These two additional clipping planes (which must have been enabled) - // will reproduce the mainViewer's near and far clipping. - glClipPlane(GL_CLIP_PLANE0, coef[2]); - glClipPlane(GL_CLIP_PLANE1, coef[3]); -\endcode */ -void Camera::getFrustumPlanesCoefficients(GLdouble coef[6][4]) const { - // Computed once and for all - const Vec pos = position(); - const Vec viewDir = viewDirection(); - const Vec up = upVector(); - const Vec right = rightVector(); - const qreal posViewDir = pos * viewDir; - - static Vec normal[6]; - static GLdouble dist[6]; - - switch (type()) { - case Camera::PERSPECTIVE: { - const qreal hhfov = horizontalFieldOfView() / 2.0; - const qreal chhfov = cos(hhfov); - const qreal shhfov = sin(hhfov); - normal[0] = -shhfov * viewDir; - normal[1] = normal[0] + chhfov * right; - normal[0] = normal[0] - chhfov * right; - - normal[2] = -viewDir; - normal[3] = viewDir; - - const qreal hfov = fieldOfView() / 2.0; - const qreal chfov = cos(hfov); - const qreal shfov = sin(hfov); - normal[4] = -shfov * viewDir; - normal[5] = normal[4] - chfov * up; - normal[4] = normal[4] + chfov * up; - - for (int i = 0; i < 2; ++i) - dist[i] = pos * normal[i]; - for (int j = 4; j < 6; ++j) - dist[j] = pos * normal[j]; - - // Natural equations are: - // dist[0,1,4,5] = pos * normal[0,1,4,5]; - // dist[2] = (pos + zNear() * viewDir) * normal[2]; - // dist[3] = (pos + zFar() * viewDir) * normal[3]; - - // 2 times less computations using expanded/merged equations. Dir vectors - // are normalized. - const qreal posRightCosHH = chhfov * pos * right; - dist[0] = -shhfov * posViewDir; - dist[1] = dist[0] + posRightCosHH; - dist[0] = dist[0] - posRightCosHH; - const qreal posUpCosH = chfov * pos * up; - dist[4] = -shfov * posViewDir; - dist[5] = dist[4] - posUpCosH; - dist[4] = dist[4] + posUpCosH; - - break; - } - case Camera::ORTHOGRAPHIC: - normal[0] = -right; - normal[1] = right; - normal[4] = up; - normal[5] = -up; - - GLdouble hw, hh; - getOrthoWidthHeight(hw, hh); - dist[0] = (pos - hw * right) * normal[0]; - dist[1] = (pos + hw * right) * normal[1]; - dist[4] = (pos + hh * up) * normal[4]; - dist[5] = (pos - hh * up) * normal[5]; - break; - } - - // Front and far planes are identical for both camera types. - normal[2] = -viewDir; - normal[3] = viewDir; - dist[2] = -posViewDir - zNear(); - dist[3] = posViewDir + zFar(); - - for (int i = 0; i < 6; ++i) { - coef[i][0] = GLdouble(normal[i].x); - coef[i][1] = GLdouble(normal[i].y); - coef[i][2] = GLdouble(normal[i].z); - coef[i][3] = dist[i]; - } -} - -void Camera::onFrameModified() { - projectionMatrixIsUpToDate_ = false; - modelViewMatrixIsUpToDate_ = false; -} +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/constraint.cpp b/GraphicsView/src/CGAL_Qt5/constraint.cpp index 50a8c66a2d9..fb34857fc4f 100644 --- a/GraphicsView/src/CGAL_Qt5/constraint.cpp +++ b/GraphicsView/src/CGAL_Qt5/constraint.cpp @@ -19,262 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include -#include -#include -#include +#include -using namespace qglviewer; -using namespace std; - -//////////////////////////////////////////////////////////////////////////////// -// Constraint // -//////////////////////////////////////////////////////////////////////////////// - -/*! Default constructor. - -translationConstraintType() and rotationConstraintType() are set to -AxisPlaneConstraint::FREE. translationConstraintDirection() and -rotationConstraintDirection() are set to (0,0,0). */ -AxisPlaneConstraint::AxisPlaneConstraint() - : translationConstraintType_(FREE), rotationConstraintType_(FREE) { - // Do not use set since setRotationConstraintType needs a read. -} - -/*! Simply calls setTranslationConstraintType() and - * setTranslationConstraintDirection(). */ -void AxisPlaneConstraint::setTranslationConstraint(Type type, - const Vec &direction) { - setTranslationConstraintType(type); - setTranslationConstraintDirection(direction); -} - -/*! Defines the translationConstraintDirection(). The coordinate system where \p - * direction is expressed depends on your class implementation. */ -void AxisPlaneConstraint::setTranslationConstraintDirection( - const Vec &direction) { - if ((translationConstraintType() != AxisPlaneConstraint::FREE) && - (translationConstraintType() != AxisPlaneConstraint::FORBIDDEN)) { - const qreal norm = direction.norm(); - if (norm < 1E-8) { - qWarning("AxisPlaneConstraint::setTranslationConstraintDir: null vector " - "for translation constraint"); - translationConstraintType_ = AxisPlaneConstraint::FREE; - } else - translationConstraintDir_ = direction / norm; - } -} - -/*! Simply calls setRotationConstraintType() and - * setRotationConstraintDirection(). */ -void AxisPlaneConstraint::setRotationConstraint(Type type, - const Vec &direction) { - setRotationConstraintType(type); - setRotationConstraintDirection(direction); -} - -/*! Defines the rotationConstraintDirection(). The coordinate system where \p - * direction is expressed depends on your class implementation. */ -void AxisPlaneConstraint::setRotationConstraintDirection(const Vec &direction) { - if ((rotationConstraintType() != AxisPlaneConstraint::FREE) && - (rotationConstraintType() != AxisPlaneConstraint::FORBIDDEN)) { - const qreal norm = direction.norm(); - if (norm < 1E-8) { - qWarning("AxisPlaneConstraint::setRotationConstraintDir: null vector for " - "rotation constraint"); - rotationConstraintType_ = AxisPlaneConstraint::FREE; - } else - rotationConstraintDir_ = direction / norm; - } -} - -/*! Set the Type() of the rotationConstraintType(). Default is - AxisPlaneConstraint::FREE. - - Depending on this value, the Frame will freely rotate - (AxisPlaneConstraint::FREE), will only be able to rotate around an axis - (AxisPlaneConstraint::AXIS), or will not able to rotate at all - (AxisPlaneConstraint::FORBIDDEN). - - Use Frame::setOrientation() to define the orientation of the constrained Frame - before it gets constrained. - - \attention An AxisPlaneConstraint::PLANE Type() is not meaningful for - rotational constraints and will be ignored. */ -void AxisPlaneConstraint::setRotationConstraintType(Type type) { - if (rotationConstraintType() == AxisPlaneConstraint::PLANE) { - qWarning("AxisPlaneConstraint::setRotationConstraintType: the PLANE type " - "cannot be used for a rotation constraints"); - return; - } - - rotationConstraintType_ = type; -} - -//////////////////////////////////////////////////////////////////////////////// -// LocalConstraint // -//////////////////////////////////////////////////////////////////////////////// - -/*! Depending on translationConstraintType(), constrain \p translation to be - along an axis or limited to a plane defined in the Frame local coordinate - system by translationConstraintDirection(). */ -void LocalConstraint::constrainTranslation(Vec &translation, - Frame *const frame) { - Vec proj; - switch (translationConstraintType()) { - case AxisPlaneConstraint::FREE: - break; - case AxisPlaneConstraint::PLANE: - proj = frame->rotation().rotate(translationConstraintDirection()); - translation.projectOnPlane(proj); - break; - case AxisPlaneConstraint::AXIS: - proj = frame->rotation().rotate(translationConstraintDirection()); - translation.projectOnAxis(proj); - break; - case AxisPlaneConstraint::FORBIDDEN: - translation = Vec(0.0, 0.0, 0.0); - break; - } -} - -/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p - rotation to be a rotation around an axis whose direction is defined in the - Frame local coordinate system by rotationConstraintDirection(). */ -void LocalConstraint::constrainRotation(Quaternion &rotation, Frame *const) { - switch (rotationConstraintType()) { - case AxisPlaneConstraint::FREE: - break; - case AxisPlaneConstraint::PLANE: - break; - case AxisPlaneConstraint::AXIS: { - Vec axis = rotationConstraintDirection(); - Vec quat = Vec(rotation[0], rotation[1], rotation[2]); - quat.projectOnAxis(axis); - rotation = Quaternion(quat, 2.0 * acos(rotation[3])); - } break; - case AxisPlaneConstraint::FORBIDDEN: - rotation = Quaternion(); // identity - break; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// WorldConstraint // -//////////////////////////////////////////////////////////////////////////////// - -/*! Depending on translationConstraintType(), constrain \p translation to be - along an axis or limited to a plane defined in the world coordinate system by - translationConstraintDirection(). */ -void WorldConstraint::constrainTranslation(Vec &translation, - Frame *const frame) { - Vec proj; - switch (translationConstraintType()) { - case AxisPlaneConstraint::FREE: - break; - case AxisPlaneConstraint::PLANE: - if (frame->referenceFrame()) { - proj = frame->referenceFrame()->transformOf( - translationConstraintDirection()); - translation.projectOnPlane(proj); - } else - translation.projectOnPlane(translationConstraintDirection()); - break; - case AxisPlaneConstraint::AXIS: - if (frame->referenceFrame()) { - proj = frame->referenceFrame()->transformOf( - translationConstraintDirection()); - translation.projectOnAxis(proj); - } else - translation.projectOnAxis(translationConstraintDirection()); - break; - case AxisPlaneConstraint::FORBIDDEN: - translation = Vec(0.0, 0.0, 0.0); - break; - } -} - -/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p - rotation to be a rotation around an axis whose direction is defined in the - world coordinate system by rotationConstraintDirection(). */ -void WorldConstraint::constrainRotation(Quaternion &rotation, - Frame *const frame) { - switch (rotationConstraintType()) { - case AxisPlaneConstraint::FREE: - break; - case AxisPlaneConstraint::PLANE: - break; - case AxisPlaneConstraint::AXIS: { - Vec quat(rotation[0], rotation[1], rotation[2]); - Vec axis = frame->transformOf(rotationConstraintDirection()); - quat.projectOnAxis(axis); - rotation = Quaternion(quat, 2.0 * acos(rotation[3])); - break; - } - case AxisPlaneConstraint::FORBIDDEN: - rotation = Quaternion(); // identity - break; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// CameraConstraint // -//////////////////////////////////////////////////////////////////////////////// - -/*! Creates a CameraConstraint, whose constrained directions are defined in the - \p camera coordinate system. */ -CameraConstraint::CameraConstraint(const Camera *const camera) - : AxisPlaneConstraint(), camera_(camera) {} - -/*! Depending on translationConstraintType(), constrain \p translation to be - along an axis or limited to a plane defined in the camera() coordinate system - by translationConstraintDirection(). */ -void CameraConstraint::constrainTranslation(Vec &translation, - Frame *const frame) { - Vec proj; - switch (translationConstraintType()) { - case AxisPlaneConstraint::FREE: - break; - case AxisPlaneConstraint::PLANE: - proj = - camera()->frame()->inverseTransformOf(translationConstraintDirection()); - if (frame->referenceFrame()) - proj = frame->referenceFrame()->transformOf(proj); - translation.projectOnPlane(proj); - break; - case AxisPlaneConstraint::AXIS: - proj = - camera()->frame()->inverseTransformOf(translationConstraintDirection()); - if (frame->referenceFrame()) - proj = frame->referenceFrame()->transformOf(proj); - translation.projectOnAxis(proj); - break; - case AxisPlaneConstraint::FORBIDDEN: - translation = Vec(0.0, 0.0, 0.0); - break; - } -} - -/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p - rotation to be a rotation around an axis whose direction is defined in the - camera() coordinate system by rotationConstraintDirection(). */ -void CameraConstraint::constrainRotation(Quaternion &rotation, - Frame *const frame) { - switch (rotationConstraintType()) { - case AxisPlaneConstraint::FREE: - break; - case AxisPlaneConstraint::PLANE: - break; - case AxisPlaneConstraint::AXIS: { - Vec axis = frame->transformOf( - camera()->frame()->inverseTransformOf(rotationConstraintDirection())); - Vec quat = Vec(rotation[0], rotation[1], rotation[2]); - quat.projectOnAxis(axis); - rotation = Quaternion(quat, 2.0 * acos(rotation[3])); - } break; - case AxisPlaneConstraint::FORBIDDEN: - rotation = Quaternion(); // identity - break; - } -} +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/frame.cpp b/GraphicsView/src/CGAL_Qt5/frame.cpp index b6fcf67a5cb..bd38124db35 100644 --- a/GraphicsView/src/CGAL_Qt5/frame.cpp +++ b/GraphicsView/src/CGAL_Qt5/frame.cpp @@ -19,1083 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include -#include -#include +#include -using namespace qglviewer; -using namespace std; - -/*! Creates a default Frame. - - Its position() is (0,0,0) and it has an identity orientation() Quaternion. The - referenceFrame() and the constraint() are \c NULL. */ -Frame::Frame() : constraint_(NULL), referenceFrame_(NULL) {} - -/*! Creates a Frame with a position() and an orientation(). - - See the Vec and Quaternion documentations for convenient constructors and - methods. - - The Frame is defined in the world coordinate system (its referenceFrame() is \c - NULL). It has a \c NULL associated constraint(). */ -Frame::Frame(const Vec &position, const Quaternion &orientation) - : t_(position), q_(orientation), constraint_(NULL), referenceFrame_(NULL) {} - -/*! Equal operator. - - The referenceFrame() and constraint() pointers are copied. - - \attention Signal and slot connections are not copied. */ -Frame &Frame::operator=(const Frame &frame) { - // Automatic compiler generated version would not emit the modified() signals - // as is done in setTranslationAndRotation. - setTranslationAndRotation(frame.translation(), frame.rotation()); - setConstraint(frame.constraint()); - setReferenceFrame(frame.referenceFrame()); - return *this; -} - -/*! Copy constructor. - - The translation() and rotation() as well as constraint() and referenceFrame() - pointers are copied. */ -Frame::Frame(const Frame &frame) : QObject() { (*this) = frame; } - -/////////////////////////////// MATRICES ////////////////////////////////////// - -/*! Returns the 4x4 OpenGL transformation matrix represented by the Frame. - - This method should be used in conjunction with \c glMultMatrixd() to modify - the OpenGL modelview matrix from a Frame hierarchy. With this Frame hierarchy: - \code - Frame* body = new Frame(); - Frame* leftArm = new Frame(); - Frame* rightArm = new Frame(); - leftArm->setReferenceFrame(body); - rightArm->setReferenceFrame(body); - \endcode - - The associated OpenGL drawing code should look like: - \code - void Viewer::draw() - { - glPushMatrix(); - glMultMatrixd(body->matrix()); - drawBody(); - - glPushMatrix(); - glMultMatrixd(leftArm->matrix()); - drawArm(); - glPopMatrix(); - - glPushMatrix(); - glMultMatrixd(rightArm->matrix()); - drawArm(); - glPopMatrix(); - - glPopMatrix(); - } - \endcode - Note the use of nested \c glPushMatrix() and \c glPopMatrix() blocks to - represent the frame hierarchy: \c leftArm and \c rightArm are both correctly - drawn with respect to the \c body coordinate system. - - This matrix only represents the local Frame transformation (i.e. with respect - to the referenceFrame()). Use worldMatrix() to get the full Frame - transformation matrix (i.e. from the world to the Frame coordinate system). - These two match when the referenceFrame() is \c NULL. - - The result is only valid until the next call to matrix(), getMatrix(), - worldMatrix() or getWorldMatrix(). Use it immediately (as above) or use - getMatrix() instead. - - \attention The OpenGL format of the result is the transpose of the actual - mathematical European representation (translation is on the last \e line - instead of the last \e column). - - \note The scaling factor of the 4x4 matrix is 1.0. */ -const GLdouble *Frame::matrix() const { - static GLdouble m[4][4]; - getMatrix(m); - return (const GLdouble *)(m); -} - -/*! \c GLdouble[4][4] version of matrix(). See also getWorldMatrix() and - * matrix(). */ -void Frame::getMatrix(GLdouble m[4][4]) const { - q_.getMatrix(m); - - m[3][0] = t_[0]; - m[3][1] = t_[1]; - m[3][2] = t_[2]; -} - -/*! \c GLdouble[16] version of matrix(). See also getWorldMatrix() and matrix(). - */ -void Frame::getMatrix(GLdouble m[16]) const { - q_.getMatrix(m); - - m[12] = t_[0]; - m[13] = t_[1]; - m[14] = t_[2]; -} - -/*! Returns a Frame representing the inverse of the Frame space transformation. - - The rotation() of the new Frame is the Quaternion::inverse() of the original - rotation. Its translation() is the negated inverse rotated image of the - original translation. - - If a Frame is considered as a space rigid transformation (translation and - rotation), the inverse() Frame performs the inverse transformation. - - Only the local Frame transformation (i.e. defined with respect to the - referenceFrame()) is inverted. Use worldInverse() for a global inverse. - - The resulting Frame has the same referenceFrame() as the Frame and a \c NULL - constraint(). - - \note The scaling factor of the 4x4 matrix is 1.0. */ -Frame Frame::inverse() const { - Frame fr(-(q_.inverseRotate(t_)), q_.inverse()); - fr.setReferenceFrame(referenceFrame()); - return fr; -} - -/*! Returns the 4x4 OpenGL transformation matrix represented by the Frame. - - This method should be used in conjunction with \c glMultMatrixd() to modify - the OpenGL modelview matrix from a Frame: - \code - // The modelview here corresponds to the world coordinate system. - Frame fr(pos, Quaternion(from, to)); - glPushMatrix(); - glMultMatrixd(fr.worldMatrix()); - // draw object in the fr coordinate system. - glPopMatrix(); - \endcode - - This matrix represents the global Frame transformation: the entire - referenceFrame() hierarchy is taken into account to define the Frame - transformation from the world coordinate system. Use matrix() to get the local - Frame transformation matrix (i.e. defined with respect to the - referenceFrame()). These two match when the referenceFrame() is \c NULL. - - The OpenGL format of the result is the transpose of the actual mathematical - European representation (translation is on the last \e line instead of the - last \e column). - - \attention The result is only valid until the next call to matrix(), - getMatrix(), worldMatrix() or getWorldMatrix(). Use it immediately (as above) - or use getWorldMatrix() instead. - - \note The scaling factor of the 4x4 matrix is 1.0. */ -const GLdouble *Frame::worldMatrix() const { - // This test is done for efficiency reasons (creates lots of temp objects - // otherwise). - if (referenceFrame()) { - static Frame fr; - fr.setTranslation(position()); - fr.setRotation(orientation()); - return fr.matrix(); - } else - return matrix(); -} - -/*! qreal[4][4] parameter version of worldMatrix(). See also getMatrix() and - * matrix(). */ -void Frame::getWorldMatrix(GLdouble m[4][4]) const { - const GLdouble *mat = worldMatrix(); - for (int i = 0; i < 4; ++i) - for (int j = 0; j < 4; ++j) - m[i][j] = mat[i * 4 + j]; -} - -/*! qreal[16] parameter version of worldMatrix(). See also getMatrix() and - * matrix(). */ -void Frame::getWorldMatrix(GLdouble m[16]) const { - const GLdouble *mat = worldMatrix(); - for (int i = 0; i < 16; ++i) - m[i] = mat[i]; -} - -/*! This is an overloaded method provided for convenience. Same as - * setFromMatrix(). */ -void Frame::setFromMatrix(const GLdouble m[4][4]) { - if (fabs(m[3][3]) < 1E-8) { - qWarning("Frame::setFromMatrix: Null homogeneous coefficient"); - return; - } - - qreal rot[3][3]; - for (int i = 0; i < 3; ++i) { - t_[i] = m[3][i] / m[3][3]; - for (int j = 0; j < 3; ++j) - // Beware of the transposition (OpenGL to European math) - rot[i][j] = m[j][i] / m[3][3]; - } - q_.setFromRotationMatrix(rot); - Q_EMIT modified(); -} - -/*! Sets the Frame from an OpenGL matrix representation (rotation in the upper - left 3x3 matrix and translation on the last line). - - Hence, if a code fragment looks like: - \code - GLdouble m[16]={...}; - glMultMatrixd(m); - \endcode - It is equivalent to write: - \code - Frame fr; - fr.setFromMatrix(m); - glMultMatrixd(fr.matrix()); - \endcode - - Using this conversion, you can benefit from the powerful Frame transformation - methods to translate points and vectors to and from the Frame coordinate system - to any other Frame coordinate system (including the world coordinate system). - See coordinatesOf() and transformOf(). - - Emits the modified() signal. See also matrix(), getMatrix() and - Quaternion::setFromRotationMatrix(). - - \attention A Frame does not contain a scale factor. The possible scaling in \p - m will not be converted into the Frame by this method. */ -void Frame::setFromMatrix(const GLdouble m[16]) { - GLdouble mat[4][4]; - for (int i = 0; i < 4; ++i) - for (int j = 0; j < 4; ++j) - mat[i][j] = m[i * 4 + j]; - setFromMatrix(mat); -} - -//////////////////// SET AND GET LOCAL TRANSLATION AND ROTATION -////////////////////////////////// - -/*! Same as setTranslation(), but with \p qreal parameters. */ -void Frame::setTranslation(qreal x, qreal y, qreal z) { - setTranslation(Vec(x, y, z)); -} - -/*! Fill \c x, \c y and \c z with the translation() of the Frame. */ -void Frame::getTranslation(qreal &x, qreal &y, qreal &z) const { - const Vec t = translation(); - x = t[0]; - y = t[1]; - z = t[2]; -} - -/*! Same as setRotation() but with \c qreal Quaternion parameters. */ -void Frame::setRotation(qreal q0, qreal q1, qreal q2, qreal q3) { - setRotation(Quaternion(q0, q1, q2, q3)); -} - -/*! The \p q are set to the rotation() of the Frame. - -See Quaternion::Quaternion(qreal, qreal, qreal, qreal) for details on \c q. */ -void Frame::getRotation(qreal &q0, qreal &q1, qreal &q2, qreal &q3) const { - const Quaternion q = rotation(); - q0 = q[0]; - q1 = q[1]; - q2 = q[2]; - q3 = q[3]; -} - -//////////////////////////////////////////////////////////////////////////////// - -/*! Translates the Frame of \p t (defined in the Frame coordinate system). - - The translation actually applied to the Frame may differ from \p t since it - can be filtered by the constraint(). Use translate(Vec&) or - setTranslationWithConstraint() to retrieve the filtered translation value. Use - setTranslation() to directly translate the Frame without taking the - constraint() into account. - - See also rotate(const Quaternion&). Emits the modified() signal. */ -void Frame::translate(const Vec &t) { - Vec tbis = t; - translate(tbis); -} - -/*! Same as translate(const Vec&) but \p t may be modified to satisfy the - translation constraint(). Its new value corresponds to the translation that - has actually been applied to the Frame. */ -void Frame::translate(Vec &t) { - if (constraint()) - constraint()->constrainTranslation(t, this); - t_ += t; - Q_EMIT modified(); -} - -/*! Same as translate(const Vec&) but with \c qreal parameters. */ -void Frame::translate(qreal x, qreal y, qreal z) { - Vec t(x, y, z); - translate(t); -} - -/*! Same as translate(Vec&) but with \c qreal parameters. */ -void Frame::translate(qreal &x, qreal &y, qreal &z) { - Vec t(x, y, z); - translate(t); - x = t[0]; - y = t[1]; - z = t[2]; -} - -/*! Rotates the Frame by \p q (defined in the Frame coordinate system): R = R*q. - - The rotation actually applied to the Frame may differ from \p q since it can - be filtered by the constraint(). Use rotate(Quaternion&) or - setRotationWithConstraint() to retrieve the filtered rotation value. Use - setRotation() to directly rotate the Frame without taking the constraint() - into account. - - See also translate(const Vec&). Emits the modified() signal. */ -void Frame::rotate(const Quaternion &q) { - Quaternion qbis = q; - rotate(qbis); -} - -/*! Same as rotate(const Quaternion&) but \p q may be modified to satisfy the - rotation constraint(). Its new value corresponds to the rotation that has - actually been applied to the Frame. */ -void Frame::rotate(Quaternion &q) { - if (constraint()) - constraint()->constrainRotation(q, this); - q_ *= q; - q_.normalize(); // Prevents numerical drift - Q_EMIT modified(); -} - -/*! Same as rotate(Quaternion&) but with \c qreal Quaternion parameters. */ -void Frame::rotate(qreal &q0, qreal &q1, qreal &q2, qreal &q3) { - Quaternion q(q0, q1, q2, q3); - rotate(q); - q0 = q[0]; - q1 = q[1]; - q2 = q[2]; - q3 = q[3]; -} - -/*! Same as rotate(const Quaternion&) but with \c qreal Quaternion parameters. - */ -void Frame::rotate(qreal q0, qreal q1, qreal q2, qreal q3) { - Quaternion q(q0, q1, q2, q3); - rotate(q); -} - -/*! Makes the Frame rotate() by \p rotation around \p point. - - \p point is defined in the world coordinate system, while the \p rotation axis - is defined in the Frame coordinate system. - - If the Frame has a constraint(), \p rotation is first constrained using - Constraint::constrainRotation(). The translation which results from the - filtered rotation around \p point is then computed and filtered using - Constraint::constrainTranslation(). The new \p rotation value corresponds to - the rotation that has actually been applied to the Frame. - - Emits the modified() signal. */ -void Frame::rotateAroundPoint(Quaternion &rotation, const Vec &point) { - if (constraint()) - constraint()->constrainRotation(rotation, this); - q_ *= rotation; - q_.normalize(); // Prevents numerical drift - Vec trans = point + - Quaternion(inverseTransformOf(rotation.axis()), rotation.angle()) - .rotate(position() - point) - - t_; - if (constraint()) - constraint()->constrainTranslation(trans, this); - t_ += trans; - Q_EMIT modified(); -} - -/*! Same as rotateAroundPoint(), but with a \c const \p rotation Quaternion. - Note that the actual rotation may differ since it can be filtered by the - constraint(). */ -void Frame::rotateAroundPoint(const Quaternion &rotation, const Vec &point) { - Quaternion rot = rotation; - rotateAroundPoint(rot, point); -} - -//////////////////// SET AND GET WORLD POSITION AND ORIENTATION -////////////////////////////////// - -/*! Sets the position() of the Frame, defined in the world coordinate system. -Emits the modified() signal. - -Use setTranslation() to define the \e local frame translation (with respect to -the referenceFrame()). The potential constraint() of the Frame is not taken into -account, use setPositionWithConstraint() instead. */ -void Frame::setPosition(const Vec &position) { - if (referenceFrame()) - setTranslation(referenceFrame()->coordinatesOf(position)); - else - setTranslation(position); -} - -/*! Same as setPosition(), but with \c qreal parameters. */ -void Frame::setPosition(qreal x, qreal y, qreal z) { - setPosition(Vec(x, y, z)); -} - -/*! Same as successive calls to setPosition() and then setOrientation(). - -Only one modified() signal is emitted, which is convenient if this signal is -connected to a QGLViewer::update() slot. See also setTranslationAndRotation() -and setPositionAndOrientationWithConstraint(). */ -void Frame::setPositionAndOrientation(const Vec &position, - const Quaternion &orientation) { - if (referenceFrame()) { - t_ = referenceFrame()->coordinatesOf(position); - q_ = referenceFrame()->orientation().inverse() * orientation; - } else { - t_ = position; - q_ = orientation; - } - Q_EMIT modified(); -} - -/*! Same as successive calls to setTranslation() and then setRotation(). - -Only one modified() signal is emitted, which is convenient if this signal is -connected to a QGLViewer::update() slot. See also setPositionAndOrientation() -and setTranslationAndRotationWithConstraint(). */ -void Frame::setTranslationAndRotation(const Vec &translation, - const Quaternion &rotation) { - t_ = translation; - q_ = rotation; - Q_EMIT modified(); -} - -/*! \p x, \p y and \p z are set to the position() of the Frame. */ -void Frame::getPosition(qreal &x, qreal &y, qreal &z) const { - Vec p = position(); - x = p.x; - y = p.y; - z = p.z; -} - -/*! Sets the orientation() of the Frame, defined in the world coordinate system. -Emits the modified() signal. - -Use setRotation() to define the \e local frame rotation (with respect to the -referenceFrame()). The potential constraint() of the Frame is not taken into -account, use setOrientationWithConstraint() instead. */ -void Frame::setOrientation(const Quaternion &orientation) { - if (referenceFrame()) - setRotation(referenceFrame()->orientation().inverse() * orientation); - else - setRotation(orientation); -} - -/*! Same as setOrientation(), but with \c qreal parameters. */ -void Frame::setOrientation(qreal q0, qreal q1, qreal q2, qreal q3) { - setOrientation(Quaternion(q0, q1, q2, q3)); -} - -/*! Get the current orientation of the frame (same as orientation()). - Parameters are the orientation Quaternion values. - See also setOrientation(). */ - -/*! The \p q are set to the orientation() of the Frame. - -See Quaternion::Quaternion(qreal, qreal, qreal, qreal) for details on \c q. */ -void Frame::getOrientation(qreal &q0, qreal &q1, qreal &q2, qreal &q3) const { - Quaternion o = orientation(); - q0 = o[0]; - q1 = o[1]; - q2 = o[2]; - q3 = o[3]; -} - -/*! Returns the position of the Frame, defined in the world coordinate system. - See also orientation(), setPosition() and translation(). */ -Vec Frame::position() const { - if (referenceFrame_) - return inverseCoordinatesOf(Vec(0.0, 0.0, 0.0)); - else - return t_; -} - -/*! Returns the orientation of the Frame, defined in the world coordinate - system. See also position(), setOrientation() and rotation(). */ -Quaternion Frame::orientation() const { - Quaternion res = rotation(); - const Frame *fr = referenceFrame(); - while (fr != NULL) { - res = fr->rotation() * res; - fr = fr->referenceFrame(); - } - return res; -} - -////////////////////// C o n s t r a i n t V e r s i o n s -///////////////////////////// - -/*! Same as setTranslation(), but \p translation is modified so that the - potential constraint() of the Frame is satisfied. - - Emits the modified() signal. See also setRotationWithConstraint() and - setPositionWithConstraint(). */ -void Frame::setTranslationWithConstraint(Vec &translation) { - Vec deltaT = translation - this->translation(); - if (constraint()) - constraint()->constrainTranslation(deltaT, this); - - setTranslation(this->translation() + deltaT); - translation = this->translation(); -} - -/*! Same as setRotation(), but \p rotation is modified so that the potential - constraint() of the Frame is satisfied. - - Emits the modified() signal. See also setTranslationWithConstraint() and - setOrientationWithConstraint(). */ -void Frame::setRotationWithConstraint(Quaternion &rotation) { - Quaternion deltaQ = this->rotation().inverse() * rotation; - if (constraint()) - constraint()->constrainRotation(deltaQ, this); - - // Prevent numerical drift - deltaQ.normalize(); - - setRotation(this->rotation() * deltaQ); - q_.normalize(); - rotation = this->rotation(); -} - -/*! Same as setTranslationAndRotation(), but \p translation and \p orientation - are modified to satisfy the constraint(). Emits the modified() signal. */ -void Frame::setTranslationAndRotationWithConstraint(Vec &translation, - Quaternion &rotation) { - Vec deltaT = translation - this->translation(); - Quaternion deltaQ = this->rotation().inverse() * rotation; - - if (constraint()) { - constraint()->constrainTranslation(deltaT, this); - constraint()->constrainRotation(deltaQ, this); - } - - // Prevent numerical drift - deltaQ.normalize(); - - t_ += deltaT; - q_ *= deltaQ; - q_.normalize(); - - translation = this->translation(); - rotation = this->rotation(); - - Q_EMIT modified(); -} - -/*! Same as setPosition(), but \p position is modified so that the potential - constraint() of the Frame is satisfied. See also - setOrientationWithConstraint() and setTranslationWithConstraint(). */ -void Frame::setPositionWithConstraint(Vec &position) { - if (referenceFrame()) - position = referenceFrame()->coordinatesOf(position); - - setTranslationWithConstraint(position); -} - -/*! Same as setOrientation(), but \p orientation is modified so that the - potential constraint() of the Frame is satisfied. See also - setPositionWithConstraint() and setRotationWithConstraint(). */ -void Frame::setOrientationWithConstraint(Quaternion &orientation) { - if (referenceFrame()) - orientation = referenceFrame()->orientation().inverse() * orientation; - - setRotationWithConstraint(orientation); -} - -/*! Same as setPositionAndOrientation() but \p position and \p orientation are -modified to satisfy the constraint. Emits the modified() signal. */ -void Frame::setPositionAndOrientationWithConstraint(Vec &position, - Quaternion &orientation) { - if (referenceFrame()) { - position = referenceFrame()->coordinatesOf(position); - orientation = referenceFrame()->orientation().inverse() * orientation; - } - setTranslationAndRotationWithConstraint(position, orientation); -} - -///////////////////////////// REFERENCE FRAMES -////////////////////////////////////////// - -/*! Sets the referenceFrame() of the Frame. - -The Frame translation() and rotation() are then defined in the referenceFrame() -coordinate system. Use position() and orientation() to express these in the -world coordinate system. - -Emits the modified() signal if \p refFrame differs from the current -referenceFrame(). - -Using this method, you can create a hierarchy of Frames. This hierarchy needs to -be a tree, which root is the world coordinate system (i.e. a \c NULL -referenceFrame()). A warning is printed and no action is performed if setting \p -refFrame as the referenceFrame() would create a loop in the Frame hierarchy (see -settingAsReferenceFrameWillCreateALoop()). */ -void Frame::setReferenceFrame(const Frame *const refFrame) { - if (settingAsReferenceFrameWillCreateALoop(refFrame)) - qWarning("Frame::setReferenceFrame would create a loop in Frame hierarchy"); - else { - bool identical = (referenceFrame_ == refFrame); - referenceFrame_ = refFrame; - if (!identical) - Q_EMIT modified(); - } -} - -/*! Returns \c true if setting \p frame as the Frame's referenceFrame() would - create a loop in the Frame hierarchy. */ -bool Frame::settingAsReferenceFrameWillCreateALoop(const Frame *const frame) { - const Frame *f = frame; - while (f != NULL) { - if (f == this) - return true; - f = f->referenceFrame(); - } - return false; -} - -///////////////////////// FRAME TRANSFORMATIONS OF 3D POINTS -///////////////////////////////// - -/*! Returns the Frame coordinates of a point \p src defined in the world - coordinate system (converts from world to Frame). - - inverseCoordinatesOf() performs the inverse convertion. transformOf() converts - 3D vectors instead of 3D coordinates. - - See the frameTransform example - for an illustration. */ -Vec Frame::coordinatesOf(const Vec &src) const { - if (referenceFrame()) - return localCoordinatesOf(referenceFrame()->coordinatesOf(src)); - else - return localCoordinatesOf(src); -} - -/*! Returns the world coordinates of the point whose position in the Frame - coordinate system is \p src (converts from Frame to world). - - coordinatesOf() performs the inverse convertion. Use inverseTransformOf() to - transform 3D vectors instead of 3D coordinates. */ -Vec Frame::inverseCoordinatesOf(const Vec &src) const { - const Frame *fr = this; - Vec res = src; - while (fr != NULL) { - res = fr->localInverseCoordinatesOf(res); - fr = fr->referenceFrame(); - } - return res; -} - -/*! Returns the Frame coordinates of a point \p src defined in the - referenceFrame() coordinate system (converts from referenceFrame() to Frame). - - localInverseCoordinatesOf() performs the inverse convertion. See also - localTransformOf(). */ -Vec Frame::localCoordinatesOf(const Vec &src) const { - return rotation().inverseRotate(src - translation()); -} - -/*! Returns the referenceFrame() coordinates of a point \p src defined in the - Frame coordinate system (converts from Frame to referenceFrame()). - - localCoordinatesOf() performs the inverse convertion. See also - localInverseTransformOf(). */ -Vec Frame::localInverseCoordinatesOf(const Vec &src) const { - return rotation().rotate(src) + translation(); -} - -/*! Returns the Frame coordinates of the point whose position in the \p from - coordinate system is \p src (converts from \p from to Frame). - - coordinatesOfIn() performs the inverse transformation. */ -Vec Frame::coordinatesOfFrom(const Vec &src, const Frame *const from) const { - if (this == from) - return src; - else if (referenceFrame()) - return localCoordinatesOf(referenceFrame()->coordinatesOfFrom(src, from)); - else - return localCoordinatesOf(from->inverseCoordinatesOf(src)); -} - -/*! Returns the \p in coordinates of the point whose position in the Frame - coordinate system is \p src (converts from Frame to \p in). - - coordinatesOfFrom() performs the inverse transformation. */ -Vec Frame::coordinatesOfIn(const Vec &src, const Frame *const in) const { - const Frame *fr = this; - Vec res = src; - while ((fr != NULL) && (fr != in)) { - res = fr->localInverseCoordinatesOf(res); - fr = fr->referenceFrame(); - } - - if (fr != in) - // in was not found in the branch of this, res is now expressed in the world - // coordinate system. Simply convert to in coordinate system. - res = in->coordinatesOf(res); - - return res; -} - -////// qreal[3] versions - -/*! Same as coordinatesOf(), but with \c qreal parameters. */ -void Frame::getCoordinatesOf(const qreal src[3], qreal res[3]) const { - const Vec r = coordinatesOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as inverseCoordinatesOf(), but with \c qreal parameters. */ -void Frame::getInverseCoordinatesOf(const qreal src[3], qreal res[3]) const { - const Vec r = inverseCoordinatesOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as localCoordinatesOf(), but with \c qreal parameters. */ -void Frame::getLocalCoordinatesOf(const qreal src[3], qreal res[3]) const { - const Vec r = localCoordinatesOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as localInverseCoordinatesOf(), but with \c qreal parameters. */ -void Frame::getLocalInverseCoordinatesOf(const qreal src[3], - qreal res[3]) const { - const Vec r = localInverseCoordinatesOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as coordinatesOfIn(), but with \c qreal parameters. */ -void Frame::getCoordinatesOfIn(const qreal src[3], qreal res[3], - const Frame *const in) const { - const Vec r = coordinatesOfIn(Vec(src), in); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as coordinatesOfFrom(), but with \c qreal parameters. */ -void Frame::getCoordinatesOfFrom(const qreal src[3], qreal res[3], - const Frame *const from) const { - const Vec r = coordinatesOfFrom(Vec(src), from); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -///////////////////////// FRAME TRANSFORMATIONS OF VECTORS -///////////////////////////////// - -/*! Returns the Frame transform of a vector \p src defined in the world - coordinate system (converts vectors from world to Frame). - - inverseTransformOf() performs the inverse transformation. coordinatesOf() - converts 3D coordinates instead of 3D vectors (here only the rotational part of - the transformation is taken into account). - - See the frameTransform example - for an illustration. */ -Vec Frame::transformOf(const Vec &src) const { - if (referenceFrame()) - return localTransformOf(referenceFrame()->transformOf(src)); - else - return localTransformOf(src); -} - -/*! Returns the world transform of the vector whose coordinates in the Frame - coordinate system is \p src (converts vectors from Frame to world). - - transformOf() performs the inverse transformation. Use inverseCoordinatesOf() - to transform 3D coordinates instead of 3D vectors. */ -Vec Frame::inverseTransformOf(const Vec &src) const { - const Frame *fr = this; - Vec res = src; - while (fr != NULL) { - res = fr->localInverseTransformOf(res); - fr = fr->referenceFrame(); - } - return res; -} - -/*! Returns the Frame transform of a vector \p src defined in the - referenceFrame() coordinate system (converts vectors from referenceFrame() to - Frame). - - localInverseTransformOf() performs the inverse transformation. See also - localCoordinatesOf(). */ -Vec Frame::localTransformOf(const Vec &src) const { - return rotation().inverseRotate(src); -} - -/*! Returns the referenceFrame() transform of a vector \p src defined in the - Frame coordinate system (converts vectors from Frame to referenceFrame()). - - localTransformOf() performs the inverse transformation. See also - localInverseCoordinatesOf(). */ -Vec Frame::localInverseTransformOf(const Vec &src) const { - return rotation().rotate(src); -} - -/*! Returns the Frame transform of the vector whose coordinates in the \p from - coordinate system is \p src (converts vectors from \p from to Frame). - - transformOfIn() performs the inverse transformation. */ -Vec Frame::transformOfFrom(const Vec &src, const Frame *const from) const { - if (this == from) - return src; - else if (referenceFrame()) - return localTransformOf(referenceFrame()->transformOfFrom(src, from)); - else - return localTransformOf(from->inverseTransformOf(src)); -} - -/*! Returns the \p in transform of the vector whose coordinates in the Frame - coordinate system is \p src (converts vectors from Frame to \p in). - - transformOfFrom() performs the inverse transformation. */ -Vec Frame::transformOfIn(const Vec &src, const Frame *const in) const { - const Frame *fr = this; - Vec res = src; - while ((fr != NULL) && (fr != in)) { - res = fr->localInverseTransformOf(res); - fr = fr->referenceFrame(); - } - - if (fr != in) - // in was not found in the branch of this, res is now expressed in the world - // coordinate system. Simply convert to in coordinate system. - res = in->transformOf(res); - - return res; -} - -///////////////// qreal[3] versions ////////////////////// - -/*! Same as transformOf(), but with \c qreal parameters. */ -void Frame::getTransformOf(const qreal src[3], qreal res[3]) const { - Vec r = transformOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as inverseTransformOf(), but with \c qreal parameters. */ -void Frame::getInverseTransformOf(const qreal src[3], qreal res[3]) const { - Vec r = inverseTransformOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as localTransformOf(), but with \c qreal parameters. */ -void Frame::getLocalTransformOf(const qreal src[3], qreal res[3]) const { - Vec r = localTransformOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as localInverseTransformOf(), but with \c qreal parameters. */ -void Frame::getLocalInverseTransformOf(const qreal src[3], qreal res[3]) const { - Vec r = localInverseTransformOf(Vec(src)); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as transformOfIn(), but with \c qreal parameters. */ -void Frame::getTransformOfIn(const qreal src[3], qreal res[3], - const Frame *const in) const { - Vec r = transformOfIn(Vec(src), in); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -/*! Same as transformOfFrom(), but with \c qreal parameters. */ -void Frame::getTransformOfFrom(const qreal src[3], qreal res[3], - const Frame *const from) const { - Vec r = transformOfFrom(Vec(src), from); - for (int i = 0; i < 3; ++i) - res[i] = r[i]; -} - -//////////////////////////// STATE ////////////////////////////// - -/*! Returns an XML \c QDomElement that represents the Frame. - - \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument - factory used to create QDomElement. - - The resulting QDomElement looks like: - \code - - - - - \endcode - - Use initFromDOMElement() to restore the Frame state from the resulting \c - QDomElement. - - See Vec::domElement() for a complete example. See also - Quaternion::domElement(), Camera::domElement()... - - \attention The constraint() and referenceFrame() are not saved in the - QDomElement. */ -QDomElement Frame::domElement(const QString &name, - QDomDocument &document) const { - // TODO: use translation and rotation instead when referenceFrame is coded... - QDomElement e = document.createElement(name); - e.appendChild(position().domElement("position", document)); - e.appendChild(orientation().domElement("orientation", document)); - return e; -} - -/*! Restores the Frame state from a \c QDomElement created by domElement(). - - See domElement() for the \c QDomElement syntax. See the - Vec::initFromDOMElement() and Quaternion::initFromDOMElement() documentations - for details on default values if an argument is missing. - - \attention The constraint() and referenceFrame() are not restored by this - method and are left unchanged. */ -void Frame::initFromDOMElement(const QDomElement &element) { - // TODO: use translation and rotation instead when referenceFrame is coded... - - // Reset default values. Attention: destroys constraint. - // *this = Frame(); - // This instead ? Better : what is not set is not changed. - // setPositionAndOrientation(Vec(), Quaternion()); - - QDomElement child = element.firstChild().toElement(); - while (!child.isNull()) { - if (child.tagName() == "position") - setPosition(Vec(child)); - if (child.tagName() == "orientation") - setOrientation(Quaternion(child).normalized()); - - child = child.nextSibling().toElement(); - } -} - -///////////////////////////////// ALIGN ///////////////////////////////// - -/*! Aligns the Frame with \p frame, so that two of their axis are parallel. - -If one of the X, Y and Z axis of the Frame is almost parallel to any of the X, -Y, or Z axis of \p frame, the Frame is rotated so that these two axis actually -become parallel. - -If, after this first rotation, two other axis are also almost parallel, a second -alignment is performed. The two frames then have identical orientations, up to -90 degrees rotations. - -\p threshold measures how close two axis must be to be considered parallel. It -is compared with the absolute values of the dot product of the normalized axis. -As a result, useful range is sqrt(2)/2 (systematic alignment) to 1 (no -alignment). - -When \p move is set to \c true, the Frame position() is also affected by the -alignment. The new Frame's position() is such that the \p frame position -(computed with coordinatesOf(), in the Frame coordinates system) does not -change. - -\p frame may be \c NULL and then represents the world coordinate system (same -convention than for the referenceFrame()). - -The rotation (and translation when \p move is \c true) applied to the Frame are -filtered by the possible constraint(). */ -void Frame::alignWithFrame(const Frame *const frame, bool move, - qreal threshold) { - Vec directions[2][3]; - for (unsigned short d = 0; d < 3; ++d) { - Vec dir((d == 0) ? 1.0 : 0.0, (d == 1) ? 1.0 : 0.0, (d == 2) ? 1.0 : 0.0); - if (frame) - directions[0][d] = frame->inverseTransformOf(dir); - else - directions[0][d] = dir; - directions[1][d] = inverseTransformOf(dir); - } - - qreal maxProj = 0.0; - qreal proj; - unsigned short index[2]; - index[0] = index[1] = 0; - for (unsigned short i = 0; i < 3; ++i) - for (unsigned short j = 0; j < 3; ++j) - if ((proj = fabs(directions[0][i] * directions[1][j])) >= maxProj) { - index[0] = i; - index[1] = j; - maxProj = proj; - } - - Frame old; - old = *this; - - qreal coef = directions[0][index[0]] * directions[1][index[1]]; - if (fabs(coef) >= threshold) { - const Vec axis = cross(directions[0][index[0]], directions[1][index[1]]); - qreal angle = asin(axis.norm()); - if (coef >= 0.0) - angle = -angle; - rotate(rotation().inverse() * Quaternion(axis, angle) * orientation()); - - // Try to align an other axis direction - unsigned short d = (index[1] + 1) % 3; - Vec dir((d == 0) ? 1.0 : 0.0, (d == 1) ? 1.0 : 0.0, (d == 2) ? 1.0 : 0.0); - dir = inverseTransformOf(dir); - - qreal max = 0.0; - for (unsigned short i = 0; i < 3; ++i) { - qreal proj = fabs(directions[0][i] * dir); - if (proj > max) { - index[0] = i; - max = proj; - } - } - - if (max >= threshold) { - const Vec axis = cross(directions[0][index[0]], dir); - qreal angle = asin(axis.norm()); - if (directions[0][index[0]] * dir >= 0.0) - angle = -angle; - rotate(rotation().inverse() * Quaternion(axis, angle) * orientation()); - } - } - - if (move) { - Vec center; - if (frame) - center = frame->position(); - - translate(center - orientation().rotate(old.coordinatesOf(center)) - - translation()); - } -} - -/*! Translates the Frame so that its position() lies on the line defined by \p -origin and \p direction (defined in the world coordinate system). - -Simply uses an orthogonal projection. \p direction does not need to be -normalized. */ -void Frame::projectOnLine(const Vec &origin, const Vec &direction) { - // If you are trying to find a bug here, because of memory problems, you waste - // your time. This is a bug in the gcc 3.3 compiler. Compile the library in - // debug mode and test. Uncommenting this line also seems to solve the - // problem. Horrible. cout << "position = " << position() << endl; If you - // found a problem or are using a different compiler, please let me know. - const Vec shift = origin - position(); - Vec proj = shift; - proj.projectOnAxis(direction); - translate(shift - proj); -} +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/keyFrameInterpolator.cpp b/GraphicsView/src/CGAL_Qt5/keyFrameInterpolator.cpp index 89476f14b65..ce3f0fcc697 100644 --- a/GraphicsView/src/CGAL_Qt5/keyFrameInterpolator.cpp +++ b/GraphicsView/src/CGAL_Qt5/keyFrameInterpolator.cpp @@ -19,693 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY -#include -#include // for QGLViewer::drawAxis and Camera::drawCamera +#include +#include -using namespace qglviewer; -using namespace std; - -/*! Creates a KeyFrameInterpolator, with \p frame as associated frame(). - - The frame() can be set or changed using setFrame(). - - interpolationTime(), interpolationSpeed() and interpolationPeriod() are set to - their default values. */ -KeyFrameInterpolator::KeyFrameInterpolator(Frame *frame) - : frame_(NULL), period_(40), interpolationTime_(0.0), - interpolationSpeed_(1.0), interpolationStarted_(false), - closedPath_(false), loopInterpolation_(false), pathIsValid_(false), - valuesAreValid_(true), currentFrameValid_(false) -// #CONNECTION# Values cut pasted initFromDOMElement() -{ - setFrame(frame); - for (int i = 0; i < 4; ++i) - currentFrame_[i] = new QMutableListIterator(keyFrame_); - connect(&timer_, SIGNAL(timeout()), SLOT(update())); -} - -/*! Virtual destructor. Clears the keyFrame path. */ -KeyFrameInterpolator::~KeyFrameInterpolator() { - deletePath(); - for (int i = 0; i < 4; ++i) - delete currentFrame_[i]; -} - -/*! Sets the frame() associated to the KeyFrameInterpolator. */ -void KeyFrameInterpolator::setFrame(Frame *const frame) { - if (this->frame()) - disconnect(this, SIGNAL(interpolated()), this->frame(), - SIGNAL(interpolated())); - - frame_ = frame; - - if (this->frame()) - connect(this, SIGNAL(interpolated()), this->frame(), - SIGNAL(interpolated())); -} - -/*! Updates frame() state according to current interpolationTime(). Then adds - interpolationPeriod()*interpolationSpeed() to interpolationTime(). - - This internal method is called by a timer when interpolationIsStarted(). It - can be used for debugging purpose. stopInterpolation() is called when - interpolationTime() reaches firstTime() or lastTime(), unless - loopInterpolation() is \c true. */ -void KeyFrameInterpolator::update() { - interpolateAtTime(interpolationTime()); - - interpolationTime_ += interpolationSpeed() * interpolationPeriod() / 1000.0; - - if (interpolationTime() > keyFrame_.last()->time()) { - if (loopInterpolation()) - setInterpolationTime(keyFrame_.first()->time() + interpolationTime_ - - keyFrame_.last()->time()); - else { - // Make sure last KeyFrame is reached and displayed - interpolateAtTime(keyFrame_.last()->time()); - stopInterpolation(); - } - Q_EMIT endReached(); - } else if (interpolationTime() < keyFrame_.first()->time()) { - if (loopInterpolation()) - setInterpolationTime(keyFrame_.last()->time() - - keyFrame_.first()->time() + interpolationTime_); - else { - // Make sure first KeyFrame is reached and displayed - interpolateAtTime(keyFrame_.first()->time()); - stopInterpolation(); - } - Q_EMIT endReached(); - } -} - -/*! Starts the interpolation process. - - A timer is started with an interpolationPeriod() period that updates the - frame()'s position and orientation. interpolationIsStarted() will return \c - true until stopInterpolation() or toggleInterpolation() is called. - - If \p period is positive, it is set as the new interpolationPeriod(). The - previous interpolationPeriod() is used otherwise (default). - - If interpolationTime() is larger than lastTime(), interpolationTime() is reset - to firstTime() before interpolation starts (and inversely for negative - interpolationSpeed()). - - Use setInterpolationTime() before calling this method to change the starting - interpolationTime(). - - See the keyFrames example for an - illustration. - - You may also be interested in QGLViewer::animate() and - QGLViewer::startAnimation(). - - \attention The keyFrames must be defined (see addKeyFrame()) \e before you - startInterpolation(), or else the interpolation will naturally immediately - stop. */ -void KeyFrameInterpolator::startInterpolation(int period) { - if (period >= 0) - setInterpolationPeriod(period); - - if (!keyFrame_.isEmpty()) { - if ((interpolationSpeed() > 0.0) && - (interpolationTime() >= keyFrame_.last()->time())) - setInterpolationTime(keyFrame_.first()->time()); - if ((interpolationSpeed() < 0.0) && - (interpolationTime() <= keyFrame_.first()->time())) - setInterpolationTime(keyFrame_.last()->time()); - timer_.start(interpolationPeriod()); - interpolationStarted_ = true; - update(); - } -} - -/*! Stops an interpolation started with startInterpolation(). See - * interpolationIsStarted() and toggleInterpolation(). */ -void KeyFrameInterpolator::stopInterpolation() { - timer_.stop(); - interpolationStarted_ = false; -} - -/*! Stops the interpolation and resets interpolationTime() to the firstTime(). - -If desired, call interpolateAtTime() after this method to actually move the -frame() to firstTime(). */ -void KeyFrameInterpolator::resetInterpolation() { - stopInterpolation(); - setInterpolationTime(firstTime()); -} - -/*! Appends a new keyFrame to the path, with its associated \p time (in - seconds). - - The keyFrame is given as a pointer to a Frame, which will be connected to the - KeyFrameInterpolator: when \p frame is modified, the KeyFrameInterpolator path - is updated accordingly. This allows for dynamic paths, where keyFrame can be - edited, even during the interpolation. See the keyFrames example for an illustration. - - \c NULL \p frame pointers are silently ignored. The keyFrameTime() has to be - monotonously increasing over keyFrames. - - Use addKeyFrame(const Frame&, qreal) to add keyFrame by values. */ -void KeyFrameInterpolator::addKeyFrame(const Frame *const frame, qreal time) { - if (!frame) - return; - - if (keyFrame_.isEmpty()) - interpolationTime_ = time; - - if ((!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time)) - qWarning( - "Error in KeyFrameInterpolator::addKeyFrame: time is not monotone"); - else - keyFrame_.append(new KeyFrame(frame, time)); - connect(frame, SIGNAL(modified()), SLOT(invalidateValues())); - valuesAreValid_ = false; - pathIsValid_ = false; - currentFrameValid_ = false; - resetInterpolation(); -} - -/*! Appends a new keyFrame to the path, with its associated \p time (in - seconds). - - The path will use the current \p frame state. If you want the path to change - when \p frame is modified, you need to pass a \e pointer to the Frame instead - (see addKeyFrame(const Frame*, qreal)). - - The keyFrameTime() have to be monotonously increasing over keyFrames. */ -void KeyFrameInterpolator::addKeyFrame(const Frame &frame, qreal time) { - if (keyFrame_.isEmpty()) - interpolationTime_ = time; - - if ((!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time)) - qWarning( - "Error in KeyFrameInterpolator::addKeyFrame: time is not monotone"); - else - keyFrame_.append(new KeyFrame(frame, time)); - - valuesAreValid_ = false; - pathIsValid_ = false; - currentFrameValid_ = false; - resetInterpolation(); -} - -/*! Appends a new keyFrame to the path. - - Same as addKeyFrame(const Frame* frame, qreal), except that the keyFrameTime() - is set to the previous keyFrameTime() plus one second (or 0.0 if there is no - previous keyFrame). */ -void KeyFrameInterpolator::addKeyFrame(const Frame *const frame) { - qreal time; - if (keyFrame_.isEmpty()) - time = 0.0; - else - time = lastTime() + 1.0; - - addKeyFrame(frame, time); -} - -/*! Appends a new keyFrame to the path. - - Same as addKeyFrame(const Frame& frame, qreal), except that the keyFrameTime() - is automatically set to previous keyFrameTime() plus one second (or 0.0 if - there is no previous keyFrame). */ -void KeyFrameInterpolator::addKeyFrame(const Frame &frame) { - qreal time; - if (keyFrame_.isEmpty()) - time = 0.0; - else - time = keyFrame_.last()->time() + 1.0; - - addKeyFrame(frame, time); -} - -/*! Removes all keyFrames from the path. The numberOfKeyFrames() is set to 0. */ -void KeyFrameInterpolator::deletePath() { - stopInterpolation(); - qDeleteAll(keyFrame_); - keyFrame_.clear(); - pathIsValid_ = false; - valuesAreValid_ = false; - currentFrameValid_ = false; -} - -static void drawCamera(qreal scale) { - glDisable(GL_LIGHTING); - - const qreal halfHeight = scale * 0.07; - const qreal halfWidth = halfHeight * 1.3; - const qreal dist = halfHeight / tan(qreal(M_PI) / 8.0); - - const qreal arrowHeight = 1.5 * halfHeight; - const qreal baseHeight = 1.2 * halfHeight; - const qreal arrowHalfWidth = 0.5 * halfWidth; - const qreal baseHalfWidth = 0.3 * halfWidth; - - // Frustum outline - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glBegin(GL_LINE_STRIP); - glVertex3d(-halfWidth, halfHeight, -dist); - glVertex3d(-halfWidth, -halfHeight, -dist); - glVertex3d(0.0, 0.0, 0.0); - glVertex3d(halfWidth, -halfHeight, -dist); - glVertex3d(-halfWidth, -halfHeight, -dist); - glEnd(); - glBegin(GL_LINE_STRIP); - glVertex3d(halfWidth, -halfHeight, -dist); - glVertex3d(halfWidth, halfHeight, -dist); - glVertex3d(0.0, 0.0, 0.0); - glVertex3d(-halfWidth, halfHeight, -dist); - glVertex3d(halfWidth, halfHeight, -dist); - glEnd(); - - // Up arrow - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - // Base - glBegin(GL_QUADS); - glVertex3d(-baseHalfWidth, halfHeight, -dist); - glVertex3d(baseHalfWidth, halfHeight, -dist); - glVertex3d(baseHalfWidth, baseHeight, -dist); - glVertex3d(-baseHalfWidth, baseHeight, -dist); - glEnd(); - - // Arrow - glBegin(GL_TRIANGLES); - glVertex3d(0.0, arrowHeight, -dist); - glVertex3d(-arrowHalfWidth, baseHeight, -dist); - glVertex3d(arrowHalfWidth, baseHeight, -dist); - glEnd(); -} - -/*! Draws the path used to interpolate the frame(). - - \p mask controls what is drawn: if (mask & 1) (default), the position path is - drawn. If (mask & 2), a camera representation is regularly drawn and if (mask - & 4), an oriented axis is regularly drawn. Examples: - - \code - drawPath(); // Simply draws the interpolation path - drawPath(3); // Draws path and cameras - drawPath(5); // Draws path and axis - \endcode - - In the case where camera or axis is drawn, \p nbFrames controls the number of - objects (axis or camera) drawn between two successive keyFrames. When \p - nbFrames=1, only the path KeyFrames are drawn. \p nbFrames=2 also draws the - intermediate orientation, etc. The maximum value is 30. \p nbFrames should - divide 30 so that an object is drawn for each KeyFrame. Default value is 6. - - \p scale (default=1.0) controls the scaling of the camera and axis drawing. A - value of QGLViewer::sceneRadius() should give good results. - - See the keyFrames example for an - illustration. - - The color of the path is the current \c glColor(). - - \attention The OpenGL state is modified by this method: GL_LIGHTING is - disabled and line width set to 2. Use this code to preserve your current - OpenGL state: \code glPushAttrib(GL_ALL_ATTRIB_BITS); - drawPathModifyGLState(mask, nbFrames, scale); - glPopAttrib(); - \endcode */ -void KeyFrameInterpolator::drawPath(int mask, int nbFrames, qreal scale) { - const int nbSteps = 30; - if (!pathIsValid_) { - path_.clear(); - - if (keyFrame_.isEmpty()) - return; - - if (!valuesAreValid_) - updateModifiedFrameValues(); - - if (keyFrame_.first() == keyFrame_.last()) - path_.push_back(Frame(keyFrame_.first()->position(), - keyFrame_.first()->orientation())); - else { - static Frame fr; - KeyFrame *kf_[4]; - kf_[0] = keyFrame_.first(); - kf_[1] = kf_[0]; - int index = 1; - kf_[2] = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL; - index++; - kf_[3] = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL; - - while (kf_[2]) { - Vec diff = kf_[2]->position() - kf_[1]->position(); - Vec v1 = 3.0 * diff - 2.0 * kf_[1]->tgP() - kf_[2]->tgP(); - Vec v2 = -2.0 * diff + kf_[1]->tgP() + kf_[2]->tgP(); - - // cout << kf_[0]->time() << " , " << kf_[1]->time() << " , " << - // kf_[2]->time() << " , " << kf_[3]->time() << endl; - for (int step = 0; step < nbSteps; ++step) { - qreal alpha = step / static_cast(nbSteps); - fr.setPosition(kf_[1]->position() + - alpha * (kf_[1]->tgP() + alpha * (v1 + alpha * v2))); - fr.setOrientation(Quaternion::squad(kf_[1]->orientation(), - kf_[1]->tgQ(), kf_[2]->tgQ(), - kf_[2]->orientation(), alpha)); - path_.push_back(fr); - } - - // Shift - kf_[0] = kf_[1]; - kf_[1] = kf_[2]; - kf_[2] = kf_[3]; - index++; - kf_[3] = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL; - } - // Add last KeyFrame - path_.push_back(Frame(kf_[1]->position(), kf_[1]->orientation())); - } - pathIsValid_ = true; - } - - if (mask) { - glDisable(GL_LIGHTING); - glLineWidth(2); - - if (mask & 1) { - glBegin(GL_LINE_STRIP); - Q_FOREACH (Frame fr, path_) - glVertex3fv(fr.position()); - glEnd(); - } - if (mask & 6) { - int count = 0; - if (nbFrames > nbSteps) - nbFrames = nbSteps; - qreal goal = 0.0; - Q_FOREACH (Frame fr, path_) - if ((count++) >= goal) { - goal += nbSteps / static_cast(nbFrames); - glPushMatrix(); - glMultMatrixd(fr.matrix()); - if (mask & 2) - drawCamera(scale); - //if (mask & 4) - // QGLViewer::drawAxis(scale / 10.0); - glPopMatrix(); - } - } - } -} - -void KeyFrameInterpolator::updateModifiedFrameValues() { - Quaternion prevQ = keyFrame_.first()->orientation(); - KeyFrame *kf; - for (int i = 0; i < keyFrame_.size(); ++i) { - kf = keyFrame_.at(i); - if (kf->frame()) - kf->updateValuesFromPointer(); - kf->flipOrientationIfNeeded(prevQ); - prevQ = kf->orientation(); - } - - KeyFrame *prev = keyFrame_.first(); - kf = keyFrame_.first(); - int index = 1; - while (kf) { - KeyFrame *next = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL; - index++; - if (next) - kf->computeTangent(prev, next); - else - kf->computeTangent(prev, kf); - prev = kf; - kf = next; - } - valuesAreValid_ = true; -} - -/*! Returns the Frame associated with the keyFrame at index \p index. - - See also keyFrameTime(). \p index has to be in the range - 0..numberOfKeyFrames()-1. - - \note If this keyFrame was defined using a pointer to a Frame (see - addKeyFrame(const Frame* const)), the \e current pointed Frame state is - returned. */ -Frame KeyFrameInterpolator::keyFrame(int index) const { - const KeyFrame *const kf = keyFrame_.at(index); - return Frame(kf->position(), kf->orientation()); -} - -/*! Returns the time corresponding to the \p index keyFrame. - - See also keyFrame(). \p index has to be in the range 0..numberOfKeyFrames()-1. - */ -qreal KeyFrameInterpolator::keyFrameTime(int index) const { - return keyFrame_.at(index)->time(); -} - -/*! Returns the duration of the KeyFrameInterpolator path, expressed in seconds. - - Simply corresponds to lastTime() - firstTime(). Returns 0.0 if the path has - less than 2 keyFrames. See also keyFrameTime(). */ -qreal KeyFrameInterpolator::duration() const { - return lastTime() - firstTime(); -} - -/*! Returns the time corresponding to the first keyFrame, expressed in seconds. - -Returns 0.0 if the path is empty. See also lastTime(), duration() and -keyFrameTime(). */ -qreal KeyFrameInterpolator::firstTime() const { - if (keyFrame_.isEmpty()) - return 0.0; - else - return keyFrame_.first()->time(); -} - -/*! Returns the time corresponding to the last keyFrame, expressed in seconds. - -Returns 0.0 if the path is empty. See also firstTime(), duration() and -keyFrameTime(). */ -qreal KeyFrameInterpolator::lastTime() const { - if (keyFrame_.isEmpty()) - return 0.0; - else - return keyFrame_.last()->time(); -} - -void KeyFrameInterpolator::updateCurrentKeyFrameForTime(qreal time) { - // Assertion: times are sorted in monotone order. - // Assertion: keyFrame_ is not empty - - // TODO: Special case for loops when closed path is implemented !! - if (!currentFrameValid_) - // Recompute everything from scrach - currentFrame_[1]->toFront(); - - while (currentFrame_[1]->peekNext()->time() > time) { - currentFrameValid_ = false; - if (!currentFrame_[1]->hasPrevious()) - break; - currentFrame_[1]->previous(); - } - - if (!currentFrameValid_) - *currentFrame_[2] = *currentFrame_[1]; - - while (currentFrame_[2]->peekNext()->time() < time) { - currentFrameValid_ = false; - if (!currentFrame_[2]->hasNext()) - break; - currentFrame_[2]->next(); - } - - if (!currentFrameValid_) { - *currentFrame_[1] = *currentFrame_[2]; - if ((currentFrame_[1]->hasPrevious()) && - (time < currentFrame_[2]->peekNext()->time())) - currentFrame_[1]->previous(); - - *currentFrame_[0] = *currentFrame_[1]; - if (currentFrame_[0]->hasPrevious()) - currentFrame_[0]->previous(); - - *currentFrame_[3] = *currentFrame_[2]; - if (currentFrame_[3]->hasNext()) - currentFrame_[3]->next(); - - currentFrameValid_ = true; - splineCacheIsValid_ = false; - } - - // cout << "Time = " << time << " : " << currentFrame_[0]->peekNext()->time() - // << " , " << currentFrame_[1]->peekNext()->time() << " , " << - // currentFrame_[2]->peekNext()->time() << " , " << - // currentFrame_[3]->peekNext()->time() << endl; -} - -void KeyFrameInterpolator::updateSplineCache() { - Vec delta = currentFrame_[2]->peekNext()->position() - - currentFrame_[1]->peekNext()->position(); - v1 = 3.0 * delta - 2.0 * currentFrame_[1]->peekNext()->tgP() - - currentFrame_[2]->peekNext()->tgP(); - v2 = -2.0 * delta + currentFrame_[1]->peekNext()->tgP() + - currentFrame_[2]->peekNext()->tgP(); - splineCacheIsValid_ = true; -} - -/*! Interpolate frame() at time \p time (expressed in seconds). - interpolationTime() is set to \p time and frame() is set accordingly. - - If you simply want to change interpolationTime() but not the frame() state, - use setInterpolationTime() instead. - - Emits the interpolated() signal and makes the frame() emit the - Frame::interpolated() signal. */ -void KeyFrameInterpolator::interpolateAtTime(qreal time) { - setInterpolationTime(time); - - if ((keyFrame_.isEmpty()) || (!frame())) - return; - - if (!valuesAreValid_) - updateModifiedFrameValues(); - - updateCurrentKeyFrameForTime(time); - - if (!splineCacheIsValid_) - updateSplineCache(); - - qreal alpha; - qreal dt = currentFrame_[2]->peekNext()->time() - - currentFrame_[1]->peekNext()->time(); - if (dt == 0.0) - alpha = 0.0; - else - alpha = (time - currentFrame_[1]->peekNext()->time()) / dt; - - // Linear interpolation - debug - // Vec pos = alpha*(currentFrame_[2]->peekNext()->position()) + - // (1.0-alpha)*(currentFrame_[1]->peekNext()->position()); - Vec pos = - currentFrame_[1]->peekNext()->position() + - alpha * (currentFrame_[1]->peekNext()->tgP() + alpha * (v1 + alpha * v2)); - Quaternion q = Quaternion::squad( - currentFrame_[1]->peekNext()->orientation(), - currentFrame_[1]->peekNext()->tgQ(), currentFrame_[2]->peekNext()->tgQ(), - currentFrame_[2]->peekNext()->orientation(), alpha); - frame()->setPositionAndOrientationWithConstraint(pos, q); - - Q_EMIT interpolated(); -} - -/*! Returns an XML \c QDomElement that represents the KeyFrameInterpolator. - - The resulting QDomElement holds the KeyFrameInterpolator parameters as well as - the path keyFrames (if the keyFrame is defined by a pointer to a Frame, use its - current value). - - \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument - factory used to create QDomElement. - - Use initFromDOMElement() to restore the ManipulatedFrame state from the - resulting QDomElement. - - See Vec::domElement() for a complete example. See also - Quaternion::domElement(), Camera::domElement()... - - Note that the Camera::keyFrameInterpolator() are automatically saved by - QGLViewer::saveStateToFile() when a QGLViewer is closed. */ -QDomElement KeyFrameInterpolator::domElement(const QString &name, - QDomDocument &document) const { - QDomElement de = document.createElement(name); - int count = 0; - Q_FOREACH (KeyFrame *kf, keyFrame_) { - Frame fr(kf->position(), kf->orientation()); - QDomElement kfNode = fr.domElement("KeyFrame", document); - kfNode.setAttribute("index", QString::number(count)); - kfNode.setAttribute("time", QString::number(kf->time())); - de.appendChild(kfNode); - ++count; - } - de.setAttribute("nbKF", QString::number(keyFrame_.count())); - de.setAttribute("time", QString::number(interpolationTime())); - de.setAttribute("speed", QString::number(interpolationSpeed())); - de.setAttribute("period", QString::number(interpolationPeriod())); - DomUtils::setBoolAttribute(de, "closedPath", closedPath()); - DomUtils::setBoolAttribute(de, "loop", loopInterpolation()); - return de; -} - -/*! Restores the KeyFrameInterpolator state from a \c QDomElement created by - domElement(). - - Note that the frame() pointer is not included in the domElement(): you need to - setFrame() after this method to attach a Frame to the KeyFrameInterpolator. - - See Vec::initFromDOMElement() for a complete code example. - - See also Camera::initFromDOMElement() and Frame::initFromDOMElement(). */ -void KeyFrameInterpolator::initFromDOMElement(const QDomElement &element) { - qDeleteAll(keyFrame_); - keyFrame_.clear(); - QDomElement child = element.firstChild().toElement(); - while (!child.isNull()) { - if (child.tagName() == "KeyFrame") { - Frame fr; - fr.initFromDOMElement(child); - qreal time = DomUtils::qrealFromDom(child, "time", 0.0); - addKeyFrame(fr, time); - } - - child = child.nextSibling().toElement(); - } - - // #CONNECTION# Values cut pasted from constructor - setInterpolationTime(DomUtils::qrealFromDom(element, "time", 0.0)); - setInterpolationSpeed(DomUtils::qrealFromDom(element, "speed", 1.0)); - setInterpolationPeriod(DomUtils::intFromDom(element, "period", 40)); - setClosedPath(DomUtils::boolFromDom(element, "closedPath", false)); - setLoopInterpolation(DomUtils::boolFromDom(element, "loop", false)); - - // setFrame(NULL); - pathIsValid_ = false; - valuesAreValid_ = false; - currentFrameValid_ = false; - - stopInterpolation(); -} - -#ifndef DOXYGEN - -//////////// KeyFrame private class implementation ///////// -KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame &fr, qreal t) - : time_(t), frame_(NULL) { - p_ = fr.position(); - q_ = fr.orientation(); -} - -KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame *fr, qreal t) - : time_(t), frame_(fr) { - updateValuesFromPointer(); -} - -void KeyFrameInterpolator::KeyFrame::updateValuesFromPointer() { - p_ = frame()->position(); - q_ = frame()->orientation(); -} - -void KeyFrameInterpolator::KeyFrame::computeTangent( - const KeyFrame *const prev, const KeyFrame *const next) { - tgP_ = 0.5 * (next->position() - prev->position()); - tgQ_ = Quaternion::squadTangent(prev->orientation(), q_, next->orientation()); -} - -void KeyFrameInterpolator::KeyFrame::flipOrientationIfNeeded( - const Quaternion &prev) { - if (Quaternion::dot(prev, q_) < 0.0) - q_.negate(); -} - -#endif // DOXYGEN +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/manipulatedCameraFrame.cpp b/GraphicsView/src/CGAL_Qt5/manipulatedCameraFrame.cpp index afefbafa464..50313d5f8d0 100644 --- a/GraphicsView/src/CGAL_Qt5/manipulatedCameraFrame.cpp +++ b/GraphicsView/src/CGAL_Qt5/manipulatedCameraFrame.cpp @@ -19,456 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include -#include -#include +#include -#include - -using namespace qglviewer; -using namespace std; - -/*! Default constructor. - - flySpeed() is set to 0.0 and sceneUpVector() is (0,1,0). The pivotPoint() is - set to (0,0,0). - - \attention Created object is removeFromMouseGrabberPool(). */ -ManipulatedCameraFrame::ManipulatedCameraFrame() - : driveSpeed_(0.0), sceneUpVector_(0.0, 1.0, 0.0), - rotatesAroundUpVector_(false), zoomsOnPivotPoint_(false) { - setFlySpeed(0.0); - removeFromMouseGrabberPool(); - connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate())); -} - -/*! Equal operator. Calls ManipulatedFrame::operator=() and then copy - * attributes. */ -ManipulatedCameraFrame &ManipulatedCameraFrame:: -operator=(const ManipulatedCameraFrame &mcf) { - ManipulatedFrame::operator=(mcf); - - setFlySpeed(mcf.flySpeed()); - setSceneUpVector(mcf.sceneUpVector()); - setRotatesAroundUpVector(mcf.rotatesAroundUpVector_); - setZoomsOnPivotPoint(mcf.zoomsOnPivotPoint_); - - return *this; -} - -/*! Copy constructor. Performs a deep copy of all members using operator=(). */ -ManipulatedCameraFrame::ManipulatedCameraFrame( - const ManipulatedCameraFrame &mcf) - : ManipulatedFrame(mcf) { - removeFromMouseGrabberPool(); - connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate())); - (*this) = (mcf); -} - -//////////////////////////////////////////////////////////////////////////////// - -/*! Overloading of ManipulatedFrame::spin(). - -Rotates the ManipulatedCameraFrame around its pivotPoint() instead of its -origin. */ -void ManipulatedCameraFrame::spin() { - rotateAroundPoint(spinningQuaternion(), pivotPoint()); -} - -#ifndef DOXYGEN -/*! Called for continuous frame motion in fly mode (see - QGLViewer::MOVE_FORWARD). Emits manipulated(). */ -void ManipulatedCameraFrame::flyUpdate() { - static Vec flyDisp(0.0, 0.0, 0.0); - switch (action_) { - case QGLViewer::MOVE_FORWARD: - flyDisp.z = -flySpeed(); - translate(localInverseTransformOf(flyDisp)); - break; - case QGLViewer::MOVE_BACKWARD: - flyDisp.z = flySpeed(); - translate(localInverseTransformOf(flyDisp)); - break; - case QGLViewer::DRIVE: - flyDisp.z = flySpeed() * driveSpeed_; - translate(localInverseTransformOf(flyDisp)); - break; - default: - break; - } - - // Needs to be out of the switch since ZOOM/fastDraw()/wheelEvent use this - // callback to trigger a final draw(). #CONNECTION# wheelEvent. - Q_EMIT manipulated(); -} - -Vec ManipulatedCameraFrame::flyUpVector() const { - qWarning("flyUpVector() is deprecated. Use sceneUpVector() instead."); - return sceneUpVector(); -} - -void ManipulatedCameraFrame::setFlyUpVector(const Vec &up) { - qWarning("setFlyUpVector() is deprecated. Use setSceneUpVector() instead."); - setSceneUpVector(up); -} - -#endif - -/*! This method will be called by the Camera when its orientation is changed, so -that the sceneUpVector (private) is changed accordingly. You should not need to -call this method. */ -void ManipulatedCameraFrame::updateSceneUpVector() { - sceneUpVector_ = inverseTransformOf(Vec(0.0, 1.0, 0.0)); -} - -//////////////////////////////////////////////////////////////////////////////// -// S t a t e s a v i n g a n d r e s t o r i n g // -//////////////////////////////////////////////////////////////////////////////// - -/*! Returns an XML \c QDomElement that represents the ManipulatedCameraFrame. - - Adds to the ManipulatedFrame::domElement() the ManipulatedCameraFrame specific - informations in a \c ManipulatedCameraParameters child QDomElement. - - \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument - factory used to create QDomElement. - - Use initFromDOMElement() to restore the ManipulatedCameraFrame state from the - resulting \c QDomElement. - - See Vec::domElement() for a complete example. See also - Quaternion::domElement(), Frame::domElement(), Camera::domElement()... */ -QDomElement ManipulatedCameraFrame::domElement(const QString &name, - QDomDocument &document) const { - QDomElement e = ManipulatedFrame::domElement(name, document); - QDomElement mcp = document.createElement("ManipulatedCameraParameters"); - mcp.setAttribute("flySpeed", QString::number(flySpeed())); - DomUtils::setBoolAttribute(mcp, "rotatesAroundUpVector", - rotatesAroundUpVector()); - DomUtils::setBoolAttribute(mcp, "zoomsOnPivotPoint", zoomsOnPivotPoint()); - mcp.appendChild(sceneUpVector().domElement("sceneUpVector", document)); - e.appendChild(mcp); - return e; -} - -/*! Restores the ManipulatedCameraFrame state from a \c QDomElement created by -domElement(). - -First calls ManipulatedFrame::initFromDOMElement() and then initializes -ManipulatedCameraFrame specific parameters. */ -void ManipulatedCameraFrame::initFromDOMElement(const QDomElement &element) { - // No need to initialize, since default sceneUpVector and flySpeed are not - // meaningful. It's better to keep current ones. And it would destroy - // constraint() and referenceFrame(). *this = ManipulatedCameraFrame(); - ManipulatedFrame::initFromDOMElement(element); - - QDomElement child = element.firstChild().toElement(); - while (!child.isNull()) { - if (child.tagName() == "ManipulatedCameraParameters") { - setFlySpeed(DomUtils::qrealFromDom(child, "flySpeed", flySpeed())); - setRotatesAroundUpVector( - DomUtils::boolFromDom(child, "rotatesAroundUpVector", false)); - setZoomsOnPivotPoint( - DomUtils::boolFromDom(child, "zoomsOnPivotPoint", false)); - - QDomElement schild = child.firstChild().toElement(); - while (!schild.isNull()) { - if (schild.tagName() == "sceneUpVector") - setSceneUpVector(Vec(schild)); - - schild = schild.nextSibling().toElement(); - } - } - child = child.nextSibling().toElement(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// M o u s e h a n d l i n g // -//////////////////////////////////////////////////////////////////////////////// - -#ifndef DOXYGEN -/*! Protected internal method used to handle mouse events. */ -void ManipulatedCameraFrame::startAction(int ma, bool withConstraint) { - ManipulatedFrame::startAction(ma, withConstraint); - - switch (action_) { - case QGLViewer::MOVE_FORWARD: - case QGLViewer::MOVE_BACKWARD: - case QGLViewer::DRIVE: - flyTimer_.setSingleShot(false); - flyTimer_.start(10); - break; - case QGLViewer::ROTATE: - constrainedRotationIsReversed_ = transformOf(sceneUpVector_).y < 0.0; - break; - default: - break; - } -} - -void ManipulatedCameraFrame::zoom(qreal delta, const Camera *const camera) { - const qreal sceneRadius = camera->sceneRadius(); - if (zoomsOnPivotPoint_) { - Vec direction = position() - camera->pivotPoint(); - if (direction.norm() > 0.02 * sceneRadius || delta > 0.0) - translate(delta * direction); - } else { - const qreal coef = - qMax(fabs((camera->frame()->coordinatesOf(camera->pivotPoint())).z), - qreal(0.2) * sceneRadius); - Vec trans(0.0, 0.0, -coef * delta); - translate(inverseTransformOf(trans)); - } -} - -#endif - -/*! Overloading of ManipulatedFrame::mouseMoveEvent(). - -Motion depends on mouse binding (see mouse page for -details). The resulting displacements are basically inverted from those of a -ManipulatedFrame. */ -void ManipulatedCameraFrame::mouseMoveEvent(QMouseEvent *const event, - Camera *const camera) { - // #CONNECTION# QGLViewer::mouseMoveEvent does the update(). - switch (action_) { - case QGLViewer::TRANSLATE: { - const QPoint delta = prevPos_ - event->pos(); - Vec trans(delta.x(), -delta.y(), 0.0); - // Scale to fit the screen mouse displacement - switch (camera->type()) { - case Camera::PERSPECTIVE: - trans *= 2.0 * tan(camera->fieldOfView() / 2.0) * - fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / - camera->screenHeight(); - break; - case Camera::ORTHOGRAPHIC: { - GLdouble w, h; - camera->getOrthoWidthHeight(w, h); - trans[0] *= 2.0 * w / camera->screenWidth(); - trans[1] *= 2.0 * h / camera->screenHeight(); - break; - } - } - translate(inverseTransformOf(translationSensitivity() * trans)); - break; - } - - case QGLViewer::MOVE_FORWARD: { - Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); - rotate(rot); - //#CONNECTION# wheelEvent MOVE_FORWARD case - // actual translation is made in flyUpdate(). - // translate(inverseTransformOf(Vec(0.0, 0.0, -flySpeed()))); - break; - } - - case QGLViewer::MOVE_BACKWARD: { - Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); - rotate(rot); - // actual translation is made in flyUpdate(). - // translate(inverseTransformOf(Vec(0.0, 0.0, flySpeed()))); - break; - } - - case QGLViewer::DRIVE: { - Quaternion rot = turnQuaternion(event->x(), camera); - rotate(rot); - // actual translation is made in flyUpdate(). - driveSpeed_ = 0.01 * (event->y() - pressPos_.y()); - break; - } - - case QGLViewer::ZOOM: { - zoom(deltaWithPrevPos(event, camera), camera); - break; - } - - case QGLViewer::LOOK_AROUND: { - Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); - rotate(rot); - break; - } - - case QGLViewer::ROTATE: { - Quaternion rot; - if (rotatesAroundUpVector_) { - // Multiply by 2.0 to get on average about the same speed as with the - // deformed ball - qreal dx = 2.0 * rotationSensitivity() * (prevPos_.x() - event->x()) / - camera->screenWidth(); - qreal dy = 2.0 * rotationSensitivity() * (prevPos_.y() - event->y()) / - camera->screenHeight(); - if (constrainedRotationIsReversed_) - dx = -dx; - Vec verticalAxis = transformOf(sceneUpVector_); - rot = Quaternion(verticalAxis, dx) * Quaternion(Vec(1.0, 0.0, 0.0), dy); - } else { - Vec trans = camera->projectedCoordinatesOf(pivotPoint()); - rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], - camera); - } - //#CONNECTION# These two methods should go together (spinning detection and - // activation) - computeMouseSpeed(event); - setSpinningQuaternion(rot); - spin(); - break; - } - - case QGLViewer::SCREEN_ROTATE: { - Vec trans = camera->projectedCoordinatesOf(pivotPoint()); - - const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]) - - atan2(prevPos_.y() - trans[1], prevPos_.x() - trans[0]); - - Quaternion rot(Vec(0.0, 0.0, 1.0), angle); - //#CONNECTION# These two methods should go together (spinning detection and - // activation) - computeMouseSpeed(event); - setSpinningQuaternion(rot); - spin(); - updateSceneUpVector(); - break; - } - - case QGLViewer::ROLL: { - const qreal angle = - M_PI * (event->x() - prevPos_.x()) / camera->screenWidth(); - Quaternion rot(Vec(0.0, 0.0, 1.0), angle); - rotate(rot); - setSpinningQuaternion(rot); - updateSceneUpVector(); - break; - } - - case QGLViewer::SCREEN_TRANSLATE: { - Vec trans; - int dir = mouseOriginalDirection(event); - if (dir == 1) - trans.setValue(prevPos_.x() - event->x(), 0.0, 0.0); - else if (dir == -1) - trans.setValue(0.0, event->y() - prevPos_.y(), 0.0); - - switch (camera->type()) { - case Camera::PERSPECTIVE: - trans *= 2.0 * tan(camera->fieldOfView() / 2.0) * - fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / - camera->screenHeight(); - break; - case Camera::ORTHOGRAPHIC: { - GLdouble w, h; - camera->getOrthoWidthHeight(w, h); - trans[0] *= 2.0 * w / camera->screenWidth(); - trans[1] *= 2.0 * h / camera->screenHeight(); - break; - } - } - - translate(inverseTransformOf(translationSensitivity() * trans)); - break; - } - - case QGLViewer::ZOOM_ON_REGION: - case QGLViewer::NO_MOUSE_ACTION: - break; - } - - if (action_ != QGLViewer::NO_MOUSE_ACTION) { - prevPos_ = event->pos(); - if (action_ != QGLViewer::ZOOM_ON_REGION) - // ZOOM_ON_REGION should not emit manipulated(). - // prevPos_ is used to draw rectangle feedback. - Q_EMIT manipulated(); - } -} - -/*! This is an overload of ManipulatedFrame::mouseReleaseEvent(). The - QGLViewer::MouseAction is terminated. */ -void ManipulatedCameraFrame::mouseReleaseEvent(QMouseEvent *const event, - Camera *const camera) { - if ((action_ == QGLViewer::MOVE_FORWARD) || - (action_ == QGLViewer::MOVE_BACKWARD) || (action_ == QGLViewer::DRIVE)) - flyTimer_.stop(); - - if (action_ == QGLViewer::ZOOM_ON_REGION) - camera->fitScreenRegion(QRect(pressPos_, event->pos())); - - ManipulatedFrame::mouseReleaseEvent(event, camera); -} - -/*! This is an overload of ManipulatedFrame::wheelEvent(). - -The wheel behavior depends on the wheel binded action. Current possible actions -are QGLViewer::ZOOM, QGLViewer::MOVE_FORWARD, QGLViewer::MOVE_BACKWARD. -QGLViewer::ZOOM speed depends on wheelSensitivity() while -QGLViewer::MOVE_FORWARD and QGLViewer::MOVE_BACKWARD depend on flySpeed(). See -QGLViewer::setWheelBinding() to customize the binding. */ -void ManipulatedCameraFrame::wheelEvent(QWheelEvent *const event, - Camera *const camera) { - //#CONNECTION# QGLViewer::setWheelBinding, ManipulatedFrame::wheelEvent. - switch (action_) { - case QGLViewer::ZOOM: { - zoom(wheelDelta(event), camera); - Q_EMIT manipulated(); - break; - } - case QGLViewer::MOVE_FORWARD: - case QGLViewer::MOVE_BACKWARD: - //#CONNECTION# mouseMoveEvent() MOVE_FORWARD case - translate( - inverseTransformOf(Vec(0.0, 0.0, 0.2 * flySpeed() * event->delta()))); - Q_EMIT manipulated(); - break; - default: - break; - } - - // #CONNECTION# startAction should always be called before - if (previousConstraint_) - setConstraint(previousConstraint_); - - // The wheel triggers a fastDraw. A final update() is needed after the last - // wheel event to polish the rendering using draw(). Since the last wheel - // event does not say its name, we use the flyTimer_ to trigger flyUpdate(), - // which emits manipulated. Two wheel events separated by more than this delay - // milliseconds will trigger a draw(). - const int finalDrawAfterWheelEventDelay = 400; - - // Starts (or prolungates) the timer. - flyTimer_.setSingleShot(true); - flyTimer_.start(finalDrawAfterWheelEventDelay); - - // This could also be done *before* manipulated is emitted, so that - // isManipulated() returns false. But then fastDraw would not be used with - // wheel. Detecting the last wheel event and forcing a final draw() is done - // using the timer_. - action_ = QGLViewer::NO_MOUSE_ACTION; -} - -//////////////////////////////////////////////////////////////////////////////// - -/*! Returns a Quaternion that is a rotation around current camera Y, - * proportionnal to the horizontal mouse position. */ -Quaternion ManipulatedCameraFrame::turnQuaternion(int x, - const Camera *const camera) { - return Quaternion(Vec(0.0, 1.0, 0.0), rotationSensitivity() * - (prevPos_.x() - x) / - camera->screenWidth()); -} - -/*! Returns a Quaternion that is the composition of two rotations, inferred from - the mouse pitch (X axis) and yaw (sceneUpVector() axis). */ -Quaternion -ManipulatedCameraFrame::pitchYawQuaternion(int x, int y, - const Camera *const camera) { - const Quaternion rotX(Vec(1.0, 0.0, 0.0), rotationSensitivity() * - (prevPos_.y() - y) / - camera->screenHeight()); - const Quaternion rotY(transformOf(sceneUpVector()), - rotationSensitivity() * (prevPos_.x() - x) / - camera->screenWidth()); - return rotY * rotX; -} +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/manipulatedFrame.cpp b/GraphicsView/src/CGAL_Qt5/manipulatedFrame.cpp index 1620337b99e..7bb2ff7ad98 100644 --- a/GraphicsView/src/CGAL_Qt5/manipulatedFrame.cpp +++ b/GraphicsView/src/CGAL_Qt5/manipulatedFrame.cpp @@ -19,543 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include -#include -#include -#include -#include +#include -#include - -#include - -using namespace qglviewer; -using namespace std; - -/*! Default constructor. - - The translation is set to (0,0,0), with an identity rotation (0,0,0,1) (see - Frame constructor for details). - - The different sensitivities are set to their default values (see - rotationSensitivity(), translationSensitivity(), spinningSensitivity() and - wheelSensitivity()). */ -ManipulatedFrame::ManipulatedFrame() - : action_(QGLViewer::NO_MOUSE_ACTION), keepsGrabbingMouse_(false) { - // #CONNECTION# initFromDOMElement and accessor docs - setRotationSensitivity(1.0); - setTranslationSensitivity(1.0); - setSpinningSensitivity(0.3); - setWheelSensitivity(1.0); - setZoomSensitivity(1.0); - - isSpinning_ = false; - previousConstraint_ = NULL; - - connect(&spinningTimer_, SIGNAL(timeout()), SLOT(spinUpdate())); -} - -/*! Equal operator. Calls Frame::operator=() and then copy attributes. */ -ManipulatedFrame &ManipulatedFrame::operator=(const ManipulatedFrame &mf) { - Frame::operator=(mf); - - setRotationSensitivity(mf.rotationSensitivity()); - setTranslationSensitivity(mf.translationSensitivity()); - setSpinningSensitivity(mf.spinningSensitivity()); - setWheelSensitivity(mf.wheelSensitivity()); - setZoomSensitivity(mf.zoomSensitivity()); - - mouseSpeed_ = 0.0; - dirIsFixed_ = false; - keepsGrabbingMouse_ = false; - action_ = QGLViewer::NO_MOUSE_ACTION; - - return *this; -} - -/*! Copy constructor. Performs a deep copy of all attributes using operator=(). - */ -ManipulatedFrame::ManipulatedFrame(const ManipulatedFrame &mf) - : Frame(mf), MouseGrabber() { - (*this) = mf; -} - -//////////////////////////////////////////////////////////////////////////////// - -/*! Implementation of the MouseGrabber main method. - -The ManipulatedFrame grabsMouse() when the mouse is within a 10 pixels region -around its Camera::projectedCoordinatesOf() position(). - -See the mouseGrabber example for an -illustration. */ -void ManipulatedFrame::checkIfGrabsMouse(int x, int y, - const Camera *const camera) { - const int thresold = 10; - const Vec proj = camera->projectedCoordinatesOf(position()); - setGrabsMouse(keepsGrabbingMouse_ || ((fabs(x - proj.x) < thresold) && - (fabs(y - proj.y) < thresold))); -} - -//////////////////////////////////////////////////////////////////////////////// -// S t a t e s a v i n g a n d r e s t o r i n g // -//////////////////////////////////////////////////////////////////////////////// - -/*! Returns an XML \c QDomElement that represents the ManipulatedFrame. - - Adds to the Frame::domElement() the ManipulatedFrame specific informations in a - \c ManipulatedParameters child QDomElement. - - \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument - factory used to create QDomElement. - - Use initFromDOMElement() to restore the ManipulatedFrame state from the - resulting \c QDomElement. - - See Vec::domElement() for a complete example. See also - Quaternion::domElement(), Camera::domElement()... */ -QDomElement ManipulatedFrame::domElement(const QString &name, - QDomDocument &document) const { - QDomElement e = Frame::domElement(name, document); - QDomElement mp = document.createElement("ManipulatedParameters"); - mp.setAttribute("rotSens", QString::number(rotationSensitivity())); - mp.setAttribute("transSens", QString::number(translationSensitivity())); - mp.setAttribute("spinSens", QString::number(spinningSensitivity())); - mp.setAttribute("wheelSens", QString::number(wheelSensitivity())); - mp.setAttribute("zoomSens", QString::number(zoomSensitivity())); - e.appendChild(mp); - return e; -} - -/*! Restores the ManipulatedFrame state from a \c QDomElement created by -domElement(). - -Fields that are not described in \p element are set to their default values (see -ManipulatedFrame()). - -First calls Frame::initFromDOMElement() and then initializes ManipulatedFrame -specific parameters. Note that constraint() and referenceFrame() are not -restored and are left unchanged. - -See Vec::initFromDOMElement() for a complete code example. */ -void ManipulatedFrame::initFromDOMElement(const QDomElement &element) { - // Not called since it would set constraint() and referenceFrame() to NULL. - // *this = ManipulatedFrame(); - Frame::initFromDOMElement(element); - - stopSpinning(); - - QDomElement child = element.firstChild().toElement(); - while (!child.isNull()) { - if (child.tagName() == "ManipulatedParameters") { - // #CONNECTION# constructor default values and accessor docs - setRotationSensitivity(DomUtils::qrealFromDom(child, "rotSens", 1.0)); - setTranslationSensitivity( - DomUtils::qrealFromDom(child, "transSens", 1.0)); - setSpinningSensitivity(DomUtils::qrealFromDom(child, "spinSens", 0.3)); - setWheelSensitivity(DomUtils::qrealFromDom(child, "wheelSens", 1.0)); - setZoomSensitivity(DomUtils::qrealFromDom(child, "zoomSens", 1.0)); - } - child = child.nextSibling().toElement(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// M o u s e h a n d l i n g // -//////////////////////////////////////////////////////////////////////////////// - -/*! Returns \c true when the ManipulatedFrame is being manipulated with the - mouse. - - Can be used to change the display of the manipulated object during - manipulation. - - When Camera::frame() of the QGLViewer::camera() isManipulated(), - QGLViewer::fastDraw() is used in place of QGLViewer::draw() for scene - rendering. A simplified drawing will then allow for interactive camera - displacements. */ -bool ManipulatedFrame::isManipulated() const { - return action_ != QGLViewer::NO_MOUSE_ACTION; -} - -/*! Starts the spinning of the ManipulatedFrame. - -This method starts a timer that will call spin() every \p updateInterval -milliseconds. The ManipulatedFrame isSpinning() until you call stopSpinning(). -*/ -void ManipulatedFrame::startSpinning(int updateInterval) { - isSpinning_ = true; - spinningTimer_.start(updateInterval); -} - -/*! Rotates the ManipulatedFrame by its spinningQuaternion(). Called by a timer - when the ManipulatedFrame isSpinning(). */ -void ManipulatedFrame::spin() { rotate(spinningQuaternion()); } - -/* spin() and spinUpdate() differ since spin can be used by itself (for instance - by QGLViewer::SCREEN_ROTATE) without a spun emission. Much nicer to use the - spinningQuaternion() and hence spin() for these incremental updates. Nothing - special to be done for continuous spinning with this design. */ -void ManipulatedFrame::spinUpdate() { - spin(); - Q_EMIT spun(); -} - -#ifndef DOXYGEN -/*! Protected internal method used to handle mouse events. */ -void ManipulatedFrame::startAction(int ma, bool withConstraint) { - action_ = (QGLViewer::MouseAction)(ma); - - // #CONNECTION# manipulatedFrame::wheelEvent, - // manipulatedCameraFrame::wheelEvent and mouseReleaseEvent() restore previous - // constraint - if (withConstraint) - previousConstraint_ = NULL; - else { - previousConstraint_ = constraint(); - setConstraint(NULL); - } - - switch (action_) { - case QGLViewer::ROTATE: - case QGLViewer::SCREEN_ROTATE: - mouseSpeed_ = 0.0; - stopSpinning(); - break; - - case QGLViewer::SCREEN_TRANSLATE: - dirIsFixed_ = false; - break; - - default: - break; - } -} - -/*! Updates mouse speed, measured in pixels/milliseconds. Should be called by -any method which wants to use mouse speed. Currently used to trigger spinning in -mouseReleaseEvent(). */ -void ManipulatedFrame::computeMouseSpeed(const QMouseEvent *const e) { - const QPoint delta = (e->pos() - prevPos_); - const qreal dist = sqrt(qreal(delta.x() * delta.x() + delta.y() * delta.y())); - delay_ = last_move_time.restart(); - if (delay_ == 0) - // Less than a millisecond: assume delay = 1ms - mouseSpeed_ = dist; - else - mouseSpeed_ = dist / delay_; -} - -/*! Return 1 if mouse motion was started horizontally and -1 if it was more -vertical. Returns 0 if this could not be determined yet (perfect diagonal -motion, rare). */ -int ManipulatedFrame::mouseOriginalDirection(const QMouseEvent *const e) { - static bool horiz = - true; // Two simultaneous manipulatedFrame require two mice ! - - if (!dirIsFixed_) { - const QPoint delta = e->pos() - pressPos_; - dirIsFixed_ = abs(delta.x()) != abs(delta.y()); - horiz = abs(delta.x()) > abs(delta.y()); - } - - if (dirIsFixed_) - if (horiz) - return 1; - else - return -1; - else - return 0; -} - -qreal ManipulatedFrame::deltaWithPrevPos(QMouseEvent *const event, - Camera *const camera) const { - qreal dx = qreal(event->x() - prevPos_.x()) / camera->screenWidth(); - qreal dy = qreal(event->y() - prevPos_.y()) / camera->screenHeight(); - - qreal value = fabs(dx) > fabs(dy) ? dx : dy; - return value * zoomSensitivity(); -} - -qreal ManipulatedFrame::wheelDelta(const QWheelEvent *event) const { - static const qreal WHEEL_SENSITIVITY_COEF = 8E-4; - return event->delta() * wheelSensitivity() * WHEEL_SENSITIVITY_COEF; -} - -void ManipulatedFrame::zoom(qreal delta, const Camera *const camera) { - Vec trans(0.0, 0.0, (camera->position() - position()).norm() * delta); - - trans = camera->frame()->orientation().rotate(trans); - if (referenceFrame()) - trans = referenceFrame()->transformOf(trans); - translate(trans); -} - -#endif // DOXYGEN - -/*! Initiates the ManipulatedFrame mouse manipulation. - -Overloading of MouseGrabber::mousePressEvent(). See also mouseMoveEvent() and -mouseReleaseEvent(). - -The mouse behavior depends on which button is pressed. See the QGLViewer mouse page for details. */ -void ManipulatedFrame::mousePressEvent(QMouseEvent *const event, - Camera *const camera) { - Q_UNUSED(camera); - - if (grabsMouse()) - keepsGrabbingMouse_ = true; - - // #CONNECTION setMouseBinding - // action_ should no longer possibly be NO_MOUSE_ACTION since this value is - // not inserted in mouseBinding_ - // if (action_ == QGLViewer::NO_MOUSE_ACTION) - // event->ignore(); - - prevPos_ = pressPos_ = event->pos(); -} - -/*! Modifies the ManipulatedFrame according to the mouse motion. - -Actual behavior depends on mouse bindings. See the QGLViewer::MouseAction enum -and the QGLViewer mouse page for details. - -The \p camera is used to fit the mouse motion with the display parameters (see -Camera::screenWidth(), Camera::screenHeight(), Camera::fieldOfView()). - -Emits the manipulated() signal. */ -void ManipulatedFrame::mouseMoveEvent(QMouseEvent *const event, - Camera *const camera) { - switch (action_) { - case QGLViewer::TRANSLATE: { - const QPoint delta = event->pos() - prevPos_; - Vec trans(delta.x(), -delta.y(), 0.0); - // Scale to fit the screen mouse displacement - switch (camera->type()) { - case Camera::PERSPECTIVE: - trans *= 2.0 * tan(camera->fieldOfView() / 2.0) * - fabs((camera->frame()->coordinatesOf(position())).z) / - camera->screenHeight(); - break; - case Camera::ORTHOGRAPHIC: { - GLdouble w, h; - camera->getOrthoWidthHeight(w, h); - trans[0] *= 2.0 * w / camera->screenWidth(); - trans[1] *= 2.0 * h / camera->screenHeight(); - break; - } - } - // Transform to world coordinate system. - trans = - camera->frame()->orientation().rotate(translationSensitivity() * trans); - // And then down to frame - if (referenceFrame()) - trans = referenceFrame()->transformOf(trans); - translate(trans); - break; - } - - case QGLViewer::ZOOM: { - zoom(deltaWithPrevPos(event, camera), camera); - break; - } - - case QGLViewer::SCREEN_ROTATE: { - Vec trans = camera->projectedCoordinatesOf(position()); - - const qreal prev_angle = - atan2(prevPos_.y() - trans[1], prevPos_.x() - trans[0]); - const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]); - - const Vec axis = - transformOf(camera->frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0))); - Quaternion rot(axis, angle - prev_angle); - //#CONNECTION# These two methods should go together (spinning detection and - // activation) - computeMouseSpeed(event); - setSpinningQuaternion(rot); - spin(); - break; - } - - case QGLViewer::SCREEN_TRANSLATE: { - Vec trans; - int dir = mouseOriginalDirection(event); - if (dir == 1) - trans.setValue(event->x() - prevPos_.x(), 0.0, 0.0); - else if (dir == -1) - trans.setValue(0.0, prevPos_.y() - event->y(), 0.0); - - switch (camera->type()) { - case Camera::PERSPECTIVE: - trans *= 2.0 * tan(camera->fieldOfView() / 2.0) * - fabs((camera->frame()->coordinatesOf(position())).z) / - camera->screenHeight(); - break; - case Camera::ORTHOGRAPHIC: { - GLdouble w, h; - camera->getOrthoWidthHeight(w, h); - trans[0] *= 2.0 * w / camera->screenWidth(); - trans[1] *= 2.0 * h / camera->screenHeight(); - break; - } - } - // Transform to world coordinate system. - trans = - camera->frame()->orientation().rotate(translationSensitivity() * trans); - // And then down to frame - if (referenceFrame()) - trans = referenceFrame()->transformOf(trans); - - translate(trans); - break; - } - - case QGLViewer::ROTATE: { - Vec trans = camera->projectedCoordinatesOf(position()); - Quaternion rot = deformedBallQuaternion(event->x(), event->y(), trans[0], - trans[1], camera); - trans = Vec(-rot[0], -rot[1], -rot[2]); - trans = camera->frame()->orientation().rotate(trans); - trans = transformOf(trans); - rot[0] = trans[0]; - rot[1] = trans[1]; - rot[2] = trans[2]; - //#CONNECTION# These two methods should go together (spinning detection and - // activation) - computeMouseSpeed(event); - setSpinningQuaternion(rot); - spin(); - break; - } - - case QGLViewer::MOVE_FORWARD: - case QGLViewer::MOVE_BACKWARD: - case QGLViewer::LOOK_AROUND: - case QGLViewer::ROLL: - case QGLViewer::DRIVE: - case QGLViewer::ZOOM_ON_REGION: - // These MouseAction values make no sense for a manipulatedFrame - break; - - case QGLViewer::NO_MOUSE_ACTION: - // Possible when the ManipulatedFrame is a MouseGrabber. This method is then - // called without startAction because of mouseTracking. - break; - } - - if (action_ != QGLViewer::NO_MOUSE_ACTION) { - prevPos_ = event->pos(); - Q_EMIT manipulated(); - } -} - -/*! Stops the ManipulatedFrame mouse manipulation. - -Overloading of MouseGrabber::mouseReleaseEvent(). - -If the action was a QGLViewer::ROTATE QGLViewer::MouseAction, a continuous -spinning is possible if the speed of the mouse cursor is larger than -spinningSensitivity() when the button is released. Press the rotate button again -to stop spinning. See startSpinning() and isSpinning(). */ -void ManipulatedFrame::mouseReleaseEvent(QMouseEvent *const event, - Camera *const camera) { - Q_UNUSED(event); - Q_UNUSED(camera); - - keepsGrabbingMouse_ = false; - - if (previousConstraint_) - setConstraint(previousConstraint_); - - if (((action_ == QGLViewer::ROTATE) || - (action_ == QGLViewer::SCREEN_ROTATE)) && - (mouseSpeed_ >= spinningSensitivity())) - startSpinning(delay_); - - action_ = QGLViewer::NO_MOUSE_ACTION; -} - -/*! Overloading of MouseGrabber::mouseDoubleClickEvent(). - -Left button double click aligns the ManipulatedFrame with the \p camera axis -(see alignWithFrame() and QGLViewer::ALIGN_FRAME). Right button projects the -ManipulatedFrame on the \p camera view direction. */ -void ManipulatedFrame::mouseDoubleClickEvent(QMouseEvent *const event, - Camera *const camera) { - if (event->modifiers() == Qt::NoModifier) - switch (event->button()) { - case Qt::LeftButton: - alignWithFrame(camera->frame()); - break; - case Qt::RightButton: - projectOnLine(camera->position(), camera->viewDirection()); - break; - default: - break; - } -} - -/*! Overloading of MouseGrabber::wheelEvent(). - -Using the wheel is equivalent to a QGLViewer::ZOOM QGLViewer::MouseAction. See - QGLViewer::setWheelBinding(), setWheelSensitivity(). */ -void ManipulatedFrame::wheelEvent(QWheelEvent *const event, - Camera *const camera) { - //#CONNECTION# QGLViewer::setWheelBinding - if (action_ == QGLViewer::ZOOM) { - zoom(wheelDelta(event), camera); - Q_EMIT manipulated(); - } - - // #CONNECTION# startAction should always be called before - if (previousConstraint_) - setConstraint(previousConstraint_); - - action_ = QGLViewer::NO_MOUSE_ACTION; -} - -//////////////////////////////////////////////////////////////////////////////// - -/*! Returns "pseudo-distance" from (x,y) to ball of radius size. -\arg for a point inside the ball, it is proportional to the euclidean distance -to the ball \arg for a point outside the ball, it is proportional to the inverse -of this distance (tends to zero) on the ball, the function is continuous. */ -static qreal projectOnBall(qreal x, qreal y) { - // If you change the size value, change angle computation in - // deformedBallQuaternion(). - const qreal size = 1.0; - const qreal size2 = size * size; - const qreal size_limit = size2 * 0.5; - - const qreal d = x * x + y * y; - return d < size_limit ? sqrt(size2 - d) : size_limit / sqrt(d); -} - -#ifndef DOXYGEN -/*! Returns a quaternion computed according to the mouse motion. Mouse positions -are projected on a deformed ball, centered on (\p cx,\p cy). */ -Quaternion -ManipulatedFrame::deformedBallQuaternion(int x, int y, qreal cx, qreal cy, - const Camera *const camera) { - // Points on the deformed ball - qreal px = - rotationSensitivity() * (prevPos_.x() - cx) / camera->screenWidth(); - qreal py = - rotationSensitivity() * (cy - prevPos_.y()) / camera->screenHeight(); - qreal dx = rotationSensitivity() * (x - cx) / camera->screenWidth(); - qreal dy = rotationSensitivity() * (cy - y) / camera->screenHeight(); - - const Vec p1(px, py, projectOnBall(px, py)); - const Vec p2(dx, dy, projectOnBall(dx, dy)); - // Approximation of rotation angle - // Should be divided by the projectOnBall size, but it is 1.0 - const Vec axis = cross(p2, p1); - const qreal angle = - 5.0 * - asin(sqrt(axis.squaredNorm() / p1.squaredNorm() / p2.squaredNorm())); - return Quaternion(axis, angle); -} -#endif // DOXYGEN +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/mouseGrabber.cpp b/GraphicsView/src/CGAL_Qt5/mouseGrabber.cpp index c78258f2f91..63079e0cd2a 100644 --- a/GraphicsView/src/CGAL_Qt5/mouseGrabber.cpp +++ b/GraphicsView/src/CGAL_Qt5/mouseGrabber.cpp @@ -19,55 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include +#include -using namespace qglviewer; - -// Static private variable -QList MouseGrabber::MouseGrabberPool_; - -/*! Default constructor. - -Adds the created MouseGrabber in the MouseGrabberPool(). grabsMouse() is set to -\c false. */ -MouseGrabber::MouseGrabber() : grabsMouse_(false) { addInMouseGrabberPool(); } - -/*! Adds the MouseGrabber in the MouseGrabberPool(). - -All created MouseGrabber are automatically added in the MouseGrabberPool() by -the constructor. Trying to add a MouseGrabber that already -isInMouseGrabberPool() has no effect. - -Use removeFromMouseGrabberPool() to remove the MouseGrabber from the list, so -that it is no longer tested with checkIfGrabsMouse() by the QGLViewer, and hence -can no longer grab mouse focus. Use isInMouseGrabberPool() to know the current -state of the MouseGrabber. */ -void MouseGrabber::addInMouseGrabberPool() { - if (!isInMouseGrabberPool()) - MouseGrabber::MouseGrabberPool_.append(this); -} - -/*! Removes the MouseGrabber from the MouseGrabberPool(). - -See addInMouseGrabberPool() for details. Removing a MouseGrabber that is not in -MouseGrabberPool() has no effect. */ -void MouseGrabber::removeFromMouseGrabberPool() { - if (isInMouseGrabberPool()) - MouseGrabber::MouseGrabberPool_.removeAll(const_cast(this)); -} - -/*! Clears the MouseGrabberPool(). - - Use this method only if it is faster to clear the MouseGrabberPool() and then - to add back a few MouseGrabbers than to remove each one independently. Use - QGLViewer::setMouseTracking(false) instead if you want to disable mouse - grabbing. - - When \p autoDelete is \c true, the MouseGrabbers of the MouseGrabberPool() are - actually deleted (use this only if you're sure of what you do). */ -void MouseGrabber::clearMouseGrabberPool(bool autoDelete) { - if (autoDelete) - qDeleteAll(MouseGrabber::MouseGrabberPool_); - MouseGrabber::MouseGrabberPool_.clear(); -} +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/qglviewer-icon.xpm b/GraphicsView/src/CGAL_Qt5/qglviewer-icon.xpm deleted file mode 100644 index 5c7e72a57e2..00000000000 --- a/GraphicsView/src/CGAL_Qt5/qglviewer-icon.xpm +++ /dev/null @@ -1,359 +0,0 @@ -/* XPM */ -static const char * qglviewer_icon[] = { -"100 100 256 2", -" c None", -". c #0A0B27", -"+ c #090B2C", -"@ c #150C12", -"# c #080F34", -"$ c #1A0E1A", -"% c #220D0B", -"& c #260B0B", -"* c #230E1C", -"= c #12113E", -"- c #2C0D10", -"; c #2F0D09", -"> c #310D14", -", c #1A123A", -"' c #261025", -") c #17134B", -"! c #151453", -"~ c #1B1733", -"{ c #14135E", -"] c #281430", -"^ c #0E1867", -"/ c #3B1215", -"( c #32132A", -"_ c #1D1849", -": c #121C58", -"< c #431014", -"[ c #141974", -"} c #3A152E", -"| c #201A5E", -"1 c #1D204C", -"2 c #401431", -"3 c #261960", -"4 c #48171C", -"5 c #411B21", -"6 c #371B45", -"7 c #2A1C6B", -"8 c #0E229B", -"9 c #52171D", -"0 c #441A35", -"a c #1B2582", -"b c #65150F", -"c c #2D2076", -"d c #4D1B3A", -"e c #57182F", -"f c #3E1F54", -"g c #5E1924", -"h c #2E2567", -"i c #002BD1", -"j c #002AD9", -"k c #6D1A13", -"l c #36237A", -"m c #002FCD", -"n c #701A0D", -"o c #462060", -"p c #651C23", -"q c #322581", -"r c #552429", -"s c #581E41", -"t c #671E1B", -"u c #771911", -"v c #6C1B2B", -"w c #562341", -"x c #342A79", -"y c #3C2484", -"z c #062FE6", -"A c #68212B", -"B c #4D236B", -"C c #612045", -"D c #42267B", -"E c #811B10", -"F c #7C1E0E", -"G c #0033F1", -"H c #0032F9", -"I c #3A298E", -"J c #472872", -"K c #72202A", -"L c #4F2774", -"M c #652449", -"N c #442890", -"O c #1F37A8", -"P c #87200E", -"Q c #852014", -"R c #402B98", -"S c #6C244D", -"T c #7D2230", -"U c #8F1F12", -"V c #70254A", -"W c #542A7E", -"X c #212FF2", -"Y c #6E2E28", -"Z c #442E9B", -"` c #772834", -" . c #971E15", -".. c #552B86", -"+. c #693132", -"@. c #702A46", -"#. c #7B2645", -"$. c #862435", -"%. c #79264E", -"&. c #5A2B88", -"*. c #1B3AE9", -"=. c #9A2211", -"-. c #4E2EA0", -";. c #1E3BDE", -">. c #722F3D", -",. c #5E2C91", -"'. c #592F91", -"). c #7A322A", -"!. c #1D42D7", -"~. c #902539", -"{. c #7D2A52", -"]. c #862E26", -"^. c #822853", -"/. c #922B20", -"(. c #4E34AA", -"_. c #A62411", -":. c #5D309A", -"<. c #59387B", -"[. c #5631AB", -"}. c #5533A6", -"|. c #912840", -"1. c #8C2B3F", -"2. c #AE2215", -"3. c #862C57", -"4. c #5D32A8", -"5. c #882C53", -"6. c #5C34A2", -"7. c #6231A2", -"8. c #A32A18", -"9. c #5C398B", -"0. c #4D4480", -"a. c #982A3F", -"b. c #8D2B5A", -"c. c #962C44", -"d. c #902C56", -"e. c #7A3B43", -"f. c #3249C3", -"g. c #B42812", -"h. c #583E9B", -"i. c #BA2516", -"j. c #524297", -"k. c #55448B", -"l. c #5E3BA3", -"m. c #9E2C47", -"n. c #5A4676", -"o. c #873847", -"p. c #404E94", -"q. c #932F59", -"r. c #BE2810", -"s. c #992D5B", -"t. c #5941A4", -"u. c #8A3F36", -"v. c #9D2F58", -"w. c #78415D", -"x. c #A72D4D", -"y. c #A2304B", -"z. c #C72815", -"A. c #C12C13", -"B. c #654298", -"C. c #654780", -"D. c #8F385F", -"E. c #98345D", -"F. c #2F51E3", -"G. c #A93724", -"H. c #A0325B", -"I. c #CA2B10", -"J. c #A93054", -"K. c #A6305D", -"L. c #D42A00", -"M. c #D22914", -"N. c #89415D", -"O. c #6546A5", -"P. c #AB3255", -"Q. c #DC2806", -"R. c #A3355D", -"S. c #AA325A", -"T. c #A13F2F", -"U. c #6A4A8E", -"V. c #A53754", -"W. c #DE2A00", -"X. c #CF300A", -"Y. c #D62C0E", -"Z. c #A8385B", -"`. c #4B54C3", -" + c #964356", -".+ c #E82903", -"++ c #8F4757", -"@+ c #DF2C14", -"#+ c #D93011", -"$+ c #A23D62", -"%+ c #8C4E44", -"&+ c #E32F00", -"*+ c #6E4CA5", -"=+ c #6E4F9B", -"-+ c #A04259", -";+ c #9D4266", -">+ c #D03716", -",+ c #E3300D", -"'+ c #6A51A7", -")+ c #BA3F2F", -"!+ c #EC2E07", -"~+ c #A9405F", -"{+ c #EB2E13", -"]+ c #EF3100", -"^+ c #4A5CDC", -"/+ c #984A68", -"(+ c #B54536", -"_+ c #4563C9", -":+ c #F62E03", -"<+ c #EF310B", -"[+ c #E73411", -"}+ c #AA4B3E", -"|+ c #F62E10", -"1+ c #CE401F", -"2+ c #D33D23", -"3+ c #7455A7", -"4+ c #C54426", -"5+ c #A5496A", -"6+ c #F93106", -"7+ c #A45048", -"8+ c #CD412C", -"9+ c #F2350E", -"0+ c #AA4A64", -"a+ c #5266C7", -"b+ c #DE411E", -"c+ c #EF3C18", -"d+ c #755FAD", -"e+ c #C94C34", -"f+ c #C14F3F", -"g+ c #DB452E", -"h+ c #AB526E", -"i+ c #EB421F", -"j+ c #AA566A", -"k+ c #A55B6B", -"l+ c #AA5873", -"m+ c #E94824", -"n+ c #D94D35", -"o+ c #DB4F31", -"p+ c #5B71DE", -"q+ c #A85F76", -"r+ c #EC4C31", -"s+ c #E2522E", -"t+ c #E84F30", -"u+ c #CD5D42", -"v+ c #6976CD", -"w+ c #D9583F", -"x+ c #E55537", -"y+ c #E7573E", -"z+ c #D65F4C", -"A+ c #CA6457", -"B+ c #E35C3E", -"C+ c #D1634E", -"D+ c #E26447", -"E+ c #E1674E", -"F+ c #DE6F57", -"G+ c #DE705E", -" ", -" ", -" C+ ", -" ` >.o. 1+X.u+ ", -" e.A K K T $.T ++ 1+L.L.X. ", -" A A v ` T T T 1.~.++ X.L.L.L.W.1+ ", -" g A K K T T $.$.1.$.1. >+L.L.L.L.L.L. ", -" r g A v v T T $.1.$.$.|.|. + u+X.L.L.L.L.W.W.W.2+ ", -" f r g g K K v T T T |.|.1.1.c.-+ u+X.L.L.L.Q.L.Q.Q.W.W. ", -" 6 o B C. 4 9 p p v T T T 1.~.$.1.~.a.~.1. u+X.L.L.L.Q.L.W.L.W.W.W.o+ ", -" f o B C. 4 9 9 g v v T T $.1.1.~.|.c.c.c.m. 4+L.L.L.Q.L.Q.Q.W.&+W.W.,+b+ ", -" f o J L U. 9 9 A v K T $.1.1.$.|.|.c.c.c.a.a.q+ 4+L.L.L.L.L.Q.W.W.&+L.&+&+&+W. ", -" f o B W L <. < 9 g A e.` ` $.$.|.$.1.~.a.a.c.c.a.k+ 1+L.L.Q.Q.W.L.W.L.&+&+&+,+&+&+w+ ", -" 6 f B L L .. 4 4 9 o.$.|.$.c.a.c.c.m.m.y.c.q+ u+L.W.Q.L.W.&+&+W.&+,+&+&+&+.+s+ ", -" p. f o L W W .. / < r 1.|.|.c.c.c.c.y.m.y.a.k+ X.L.W.L.,+,+&+,+&+&+&+[+&+&+b+ ", -" 3 6 B L W W &.9. / o.|.|.c.a.m.y.a.a.y.y.q+ e+W.W.&+W.W.,+&+&+&+&+.+&+!+b+ ", -" a : f o L L ....&.B. / ++a.a.m.c.a.y.y.a.y.y.q+ u+W.W.W.&+.+&+{+&+[+&+!+&+]+&+ ", -" O ^ 1 f J W ....&.'.U. / 5 k+c.m.c.m.y.m.y.y.y.y. L.&+&+&+&+&+&+!+]+!+[+!+!+!+ ", -" f.[ ) $ <.L W ..&.'.'. / k+c.m.a.m.y.a.y.y.y.y. b+&+,+&+.+&+&+&+[+]+&+!+&+!+E+ ", -" `.8 ! = $ <.W ..&.'.'.B. - k+y.a.y.y.a.y.x.y.x.V. b+.+&+!+&+[+!+!+]+!+!+!+9+!+B+ ", -" a+m { = # $ ....'.'.'.,.=+ - k+m.y.y.y.V.y.x.y.x.V. w+!+&+!+]+&+[+&+!+[+<+!+]+&+B+ ", -" v+m 8 ) # $ U...'.'.'.,.:. - -+m.m.y.V.x.y.y.x.m.0+ s+&+!+[+&+!+]+<+!+]+]+[+<+9+B+ ", -" ;.m ) = + $ 9.&.'.l.l.,.*+ & -+y.y.x.x.J.x.y.x.y.0+ B+&+]+!+<+!+!+[+]+!+]+<+]+<+B+ ", -" ;.i [ = + . $ &.'.,.,.,.:.*+ & 0+y.y.x.y.y.y.J.V.x.l+ s+!+!+!+[+<+]+]+!+]+]+]+!+]+B+ ", -" ;.z ^+1 # + $ =+l.'.l.7.:.'. % y.x.y.x.x.P.y.x.P.y. B+.+[+]+!+[+]+]+<+]+]+]+]+<+B+ ", -" ;.z ;. + + . $ '.:.:.:.:.:.3+ & V.y.x.y.x.J.P.P.V.Z. x+<+]+]+]+:+]+]+]+9+<+9+9+]+ ", -" p+*.G G p+ + . @ U.:.:.7.:.6.7. % h+P.V.P.P.y.J.P.x.y.0+ s+]+9+]+]+9+9+]+]+]+]+]+9+<+ ", -" f. p+F.H H G F. . . . $ l.:.:.:.6.:.O. & 0+x.J.J.V.P.x.x.P.P.h+ x+<+]+<+]+]+]+9+]+]+]+9+]+<+ ", -" i G X X X G H ;. . . $ =+:.:.6.l.6.:.3+ % V.y.y.x.P.V.V.P.P.J. m+!+]+]+]+9+]+|+9+|+]+9+]+c+ ", -" m z G H G H G p+ . . * O.6.6.:.7.6.:. & V.P.P.P.x.P.P.V.V.V. i+]+]+<+|+:+]+]+:+]+:+:+]+m+ ", -" i z G G G G F. . . * 3+7.7.6.6.6.6.3+ % 0+x.P.x.V.x.x.x.x.P.0+ [+9+]+]+]+|+]+]+]+9+9+9+]+m+ ", -" i z G G H *. . . * :.6.6.7.6.7.:. & 0+P.P.P.P.P.P.P.P.V. :+:+]+]+9+]+9+9+|+:+:+:+]+B+ ", -" i z G G G p+ . * *+7.}.6.4.6.6.*+ % V.y.P.x.P.x.P.P.P.V. F+]+|+9+:+]+:+:+]+]+:+|+|+:+B+ ", -" i z G G ^+ . ' n.4.6.7.4.7.4.6. & 0+x.P.P.P.P.P.P.P.P.~+ s+9+]+:+]+|+6+]+]+|+9+]+]+9+ ", -" i z G F. . * O.4.6.6.6.6.4.*+ & ~+P.P.P.P.P.P.P.P.Z.l+ s+6+9+|+]+9+|+|+]+6+:+6+6+9+ ", -" j z !. . ] J 4.6.4.[.4.[.l. & P.P.P.P.P.P.P.P.P.V. c+9+6+]+|+6+9+]+6+9+|+9+|+i+ ", -" a+i ;. . ' l.[.(.6.6.6.4.*+ & h+P.P.S.P.S.P.P.P.P.0+ :+|+]+]+|+6+|+9+6+]+6+]+9+t+ ", -" a+!. . ] t.(.6.7.4.4.[.6. & S.P.P.P.P.P.S.S.S.S.q+ D+9+9+|+6+]+9+]+6+|+6+9+6+:+E+ ", -" . 3+7.[.6.(.6.6.6.*+ & h+S.S.P.S.~+P.P.P.P.~+ r+6+]+9+6+6+6+9+]+9+6+9+6+9+ ", -" . 6.(.6.4.4.4.(.6. & ~+P.P.P.P.P.P.P.P.P.h+ c+6+|+6+9+6+9+6+|+6+|+6+9+9+ ", -" . l.[.[.(.6.(.4.(.3+ % l+S.V.~+P.P.P.~+P.P.S. 9+]+|+6+9+6+|+9+9+]+|+6+9+t+ ", -" ~ *+(.6.4.4.4.6.4.l. % ~+P.S.P.S.P.S.P.S.S.0+ B+9+6+9+9+6+|+9+6+6+|+]+]+|+D+ ", -" ~ '+(.[.(.(.(.4.4.6.d+ & S.P.S.P.S.P.S.P.P.~+ i+9+6+6+9+6+6+:+9+:+6+|+6+c+ ", -" = '+(.[.(.(.[.4.[.(.O. % ~+~+S.Z.S.Z.S.P.Z.S.~+ 9+6+6+9+6+9+:+9+6+6+9+6+]+m+ ", -" _ '+Z }.}.4.6.(.(.[.4. & q+S.S.S.~+S.~+S.S.S.S.l+ x+]+9+6+9+6+9+6+9+9+6+9+|+6+B+ ", -" ) h h.Z -.-.}.(.(.[.[.(.'+ & R.Z.Z.S.S.S.S.~+Z.Z.R. r+|+6+|+6+9+6+|+6+6+9+6+9+]+F+ ", -" _ 3 x k.j.I Z -.Z Z }.4.(.(.4.t. % l+S.S.Z.Z.Z.S.S.S.S.Z.h+ F+]+9+6+9+6+9+6+9+9+6+9+6+6+c+ ", -" ) 3 7 q I I Z N }.}.}.(.}.}.(. & Z.Z.S.Z.Z.S.Z.Z.Z.Z.~+ B+6+6+9+6+|+6+9+6+6+|+6+6+9+y+ ", -" = ! 7 c q I N Z Z Z Z -.}.}.}.'+ % l+S.Z.S.S.Z.S.S.S.S.Z.h+ c+9+6+9+6+9+6+|+9+6+9+9+6+6+ ", -" _ ) 3 l q N N Z N -.}.Z -.(.O. & R.R.R.S.K.S.Z.Z.Z.V.$+ D+6+6+9+6+9+6+9+6+6+9+6+6+9+m+ ", -" _ 3 3 l q I N Z Z -.-.}.}.Z & $+S.S.Z.Z.Z.K.K.K.K.S.h+ i+9+6+9+6+9+6+9+9+6+9+9+6+9+B+ ", -" _ | 7 l y I I N N Z -.(.-.d+ % l+R.R.R.R.S.K.Z.S.S.S.~+ F+9+6+9+|+6+6+9+6+6+9+|+6+6+9+ ", -" _ 3 7 c q I R Z :.Z Z -.'+ & R.K.K.K.R.Z.S.Z.Z.Z.K.l+ r+9+6+6+9+6+6+6+9+6+6+9+9+6+t+ ", -" ) | 7 l y y I N R Z -.D & ;+R.Z.Z.S.K.K.R.Z.S.S.5+ G+6+6+9+9+6+9+9+9+6+6+9+6+6+]+E+ ", -" 3 x c I y I Z Z Z h.' & 5+K.K.R.R.S.K.S.K.R.R.S. t+9+6+|+9+6+6+6+6+9+6+9+|+|+i+ ", -" | c l q I R I Z h. $ & $+R.s.K.R.R.Z.R.K.K.K.5+ 9+6+9+6+6+6+c+6+9+6+9+|+6+6+x+ ", -" h 7 l q y N N j. * & H.H.R.s.K.H.H.K.Z.K.S.Z. r+9+6+9+6+6+6+6+6+6+6+9+9+9+c+ ", -" 0.l q y I y '+ ' ; ;+v.v.K.R.H.K.K.v.H.Z.R.5+ G+9+|+6+c+6+c+6+c+6+6+9+6+|+6+B+ ", -" 0.x D j. ' & ;+s.R.s.K.H.s.s.R.K.K.K.$+ m+9+9+|+9+6+9+6+6+c+6+6+9+9+6+ ", -" ( ( ; ;+v.$+s.R.v.H.R.K.K.R.s.R. E+9+|+9+6+6+c+6+c+6+6+9+9+6+6+t+ ", -" ( } & ; /+5.v.v.v.s.H.R.H.H.s.K.R.5+ i+9+|+9+9+6+6+6+c+6+6+6+9+6+9+ ", -" ( } - ; /+q.s.s.s.E.H.v.K.v.R.K.R.$+ y+|+|+9+9+|+9+c+6+6+c+9+9+|+9+x+ ", -" ( 2 0 - ; D.d.q.q.E.v.v.s.s.H.s.R.v.$+ 9+9+|+|+9+6+6+9+c+6+6+6+c+6+9+ ", -" } 0 0 ; ; 3.q.b.q.q.s.E.s.H.H.v.K.v.H.5+ r+9+|+9+9+6+c+9+6+6+c+9+9+|+|+x+ ", -" } 0 d d ; < N.5.3.q.q.q.q.s.E.E.E.s.v.s.H.5+ D+9+9+|+9+9+6+c+9+6+|+6+9+9+9+9+ ", -" } 2 s s s M w.w.e @.{.^.3.3.b.d.q.q.q.s.v.v.s.H.s.$+ i+9+|+9+|+c+6+|+6+9+9+9+9+6+|+y+ ", -" 0 0 d s C M V V V ^.^.5.5.5.3.d.q.q.q.q.s.v.E.R.E. r+9+c+|+9+|+9+c+c+|+|+9+6+9+6+i+ ", -" 2 2 s w C C V V %.{.{.5.b.5.q.3.d.q.q.s.E.E.v.s.q+ w+9+9+9+9+9+9+|+|+|+9+9+|+9+|+9+E+ ", -" 0 0 d w C C S V V {.%.#.3.5.d.d.d.q.q.s.s.E.E.q+ E+c+9+9+c+9+c+9+|+9+|+9+9+9+9+|+i+ ", -" } d s s C M V S %.%.^.3.3.3.3.3.d.d.q.d.q.s./+ i+!+c+9+9+9+|+9+9+|+9+|+9+|+|+9+G+ ", -" 0 d d e M M V S {.{.^.3.3.d.d.D.d.d.q.q.v./+ r+[+|+{+9+c+9+9+9+9+9+9+|+9+9+9+B+ ", -" d s s s V S V S %.{.{.5.3.3.d.d.q.q.q./+ o+[+!+c+9+|+9+c+9+c+9+c+9+9+|+9+m+ ", -" 0 s C s V V %.V {.^.{.5.5.3.3.D.d.q./+ n+{+[+{+[+c+c+|+9+9+9+|+9+c+9+|+|+G+ ", -" d w s M M M V %.%.{.3.{.3.d.d.d.q./+ w+[+[+{+&+{+{+{+9+c+9+c+9+c+|+9+c+B+ ", -" w s @.S S S %.%.{.5.5.^.3.b.D. G+@+[+[+[+c+[+<+[+9+c+|+c+9+9+c+|+m+ ", -" w s C M V V S %.^.{.{.3.5.N. C+@+[+{+&+{+&+<+[+{+{+{+9+9+9+c+9+c+G+ ", -" w M M S V {.V ^.5.5.{. w+,+,+@+[+[+{+[+{+c+9+<+[+9+9+9+9+{+D+ ", -" M @.V V {.{.#.++ w+@+@+@+[+{+[+[+{+[+&+[+[+{+c+{+{+{+x+ ", -" w.>.p t b ). 8+@+,+[+,+@+{+[+[+&+{+{+{+[+!+[+9+9+b+ ", -" +.b b n u %+ 2+#+,+@+Q.@+[+@+,+[+[+[+[+[+<+[+<+{+i+ ", -" b b k u u u+M.M.#+#+@+,+@+[+@+[+{+[+[+{+[+[+[+[+{+z+ ", -" b b n u u /. A+2+M.#+#+#+,+@+@+,+,+[+@+,+[+{+[+{+{+[+<+w+ ", -" t k k F u P /.7+ A+e+z.#+#+@+M.#+#+,+#+@+Q.[+@+@+,+{+&+[+{+[+B+ ", -" Y n u u E Q U U T.7+ f+4+I.M.>+M.M.Y.@+M.@+#+@+,+@+@+[+[+[+[+[+[+[+n+ ", -" k n F Q Q U .=._.8.G.(+(+)+g.i.z.r.I.M.I.>+I.#+#+#+#+#+#+,+,+Q.,+@+,+@+{+{+o+ ", -" k u u E U U U =.=._.2.g.i.i.i.A.r.z.z.I.M.Y.#+>+#+#+#+Q.@+@+@+@+,+[+@+[+,+s+ ", -" ).n E E P U =.=.=.8._._.g.g.i.i.A.A.z.I.z.M.Y.M.M.M.#+#+#+,+@+@+@+[+,+@+g+ ", -" u u Q U U U =._._.2.g.g.g.A.A.A.z.A.z.M.I.I.Y.#+#+#+#+@+#+,+,+,+,+@+o+ ", -" ).F Q U U =.=.=.8._.2.2.g.r.r.r.I.I.I.z.M.I.M.#+M.#+#+#+#+@+@+@+,+w+ ", -" Q Q P U U =._._.2._.g.g.g.i.A.A.A.A.I.I.M.X.M.#+@+#+@+@+@+,+#+z+ ", -" u.U /.U U 8._.8._.2.2.g.g.i.A.A.I.z.I.I.M.I.Y.>+@+#+#+#+#+#+A+ ", -" ].U U =.=.=.2.2._.g.i.i.A.i.A.A.I.z.I.>+Y.>+Y.Y.#+#+#+n+ ", -" /.U .=.=._._.2.2.g.i.A.A.z.A.I.I.M.I.M.M.M.M.Y.M.z+ ", -" /.=.8._._.2._.g.g.g.g.i.A.z.A.z.I.I.Y.I.X.#+8+ ", -" /.8.8._._.2.2.i.i.A.i.A.z.z.I.I.I.M.M.8+A+ ", -" }+8._.2.2.g.2.A.i.A.A.A.>+z.z.z.4+A+ ", -" T.)+_.g.2.A.r.A.A.r.A.4+z+ ", -" }+}+(+(+f+f+A+ ", -" ", -" "}; diff --git a/GraphicsView/src/CGAL_Qt5/qglviewer.cpp b/GraphicsView/src/CGAL_Qt5/qglviewer.cpp index fac0698f875..e55e9848794 100644 --- a/GraphicsView/src/CGAL_Qt5/qglviewer.cpp +++ b/GraphicsView/src/CGAL_Qt5/qglviewer.cpp @@ -19,4121 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace qglviewer; - -// Static private variable -QList QGLViewer::QGLViewerPool_; - -/*! \mainpage - -libQGLViewer is a free C++ library based on Qt that enables the quick creation -of OpenGL 3D viewers. It features a powerful camera trackball and simple -applications simply require an implementation of the draw() method. -This makes it a tool of choice for OpenGL beginners and assignments. It provides -mouse manipulated frames, stereo display, interpolated -keyFrames, object selection, and much more. It is fully -customizable and easy to extend to create complex applications, with a possible -Qt GUI. - -libQGLViewer is not a 3D viewer that can be used directly to view 3D -scenes in various formats. It is more likely to be the starting point for the -coding of such a viewer. - -libQGLViewer is based on the Qt toolkit and hence compiles on any architecture -(Unix-Linux, Mac, Windows, ...). Full reference documentation and many examples -are provided. - -See the project main page for details on the project and installation steps. */ - -void QGLViewer::defaultConstructor() { - int poolIndex = QGLViewer::QGLViewerPool_.indexOf(NULL); - setFocusPolicy(Qt::StrongFocus); - - if (poolIndex >= 0) - QGLViewer::QGLViewerPool_.replace(poolIndex, this); - else - QGLViewer::QGLViewerPool_.append(this); - camera_ = new Camera(); - setCamera(camera()); - - setDefaultShortcuts(); - setDefaultMouseBindings(); - - fpsTime_.start(); - fpsCounter_ = 0; - f_p_s_ = 0.0; - fpsString_ = tr("%1Hz", "Frames per seconds, in Hertz").arg("?"); - visualHint_ = 0; - previousPathId_ = 0; - // prevPos_ is not initialized since pos() is not meaningful here. - // It will be set when setFullScreen(false) is called after - // setFullScreen(true) - - // #CONNECTION# default values in initFromDOMElement() - manipulatedFrame_ = NULL; - manipulatedFrameIsACamera_ = false; - mouseGrabberIsAManipulatedFrame_ = false; - mouseGrabberIsAManipulatedCameraFrame_ = false; - displayMessage_ = false; - connect(&messageTimer_, SIGNAL(timeout()), SLOT(hideMessage())); - messageTimer_.setSingleShot(true); - helpWidget_ = NULL; - setMouseGrabber(NULL); - - setSceneRadius(1.0); - showEntireScene(); - setStateFileName(".qglviewer.xml"); - - // #CONNECTION# default values in initFromDOMElement() - setAxisIsDrawn(false); - setGridIsDrawn(false); - setFPSIsDisplayed(false); - setCameraIsEdited(false); - setTextIsEnabled(true); - setStereoDisplay(false); - // Make sure move() is not called, which would call initializeGL() - fullScreen_ = false; - setFullScreen(false); - - animationTimerId_ = 0; - stopAnimation(); - setAnimationPeriod(40); // 25Hz - - selectBuffer_ = NULL; - setSelectBufferSize(4 * 1000); - setSelectRegionWidth(3); - setSelectRegionHeight(3); - setSelectedName(-1); - - bufferTextureId_ = 0; - bufferTextureMaxU_ = 0.0; - bufferTextureMaxV_ = 0.0; - bufferTextureWidth_ = 0; - bufferTextureHeight_ = 0; - previousBufferTextureFormat_ = 0; - previousBufferTextureInternalFormat_ = 0; - currentlyPressedKey_ = Qt::Key(0); - - setAttribute(Qt::WA_NoSystemBackground); - axisIsDrawn_ = true; - - _offset = qglviewer::Vec(0,0,0); -} - -#ifndef DOXYGEN -/*! These contructors are deprecated since version 2.7.0, since they are not - * supported by QOpenGlWidget */ - -/*! Constructor. See \c QGLWidget documentation for details. - -All viewer parameters (display flags, scene parameters, associated objects...) -are set to their default values. See the associated documentation. - -If the \p shareWidget parameter points to a valid \c QGLWidget, the QGLViewer -will share the OpenGL context with \p shareWidget (see isSharing()). */ -QGLViewer::QGLViewer(QWidget *parent, - Qt::WindowFlags flags) - : QOpenGLWidget(parent, flags) { - defaultConstructor(); -} - -QGLViewer::QGLViewer(QGLContext*, - QWidget *parent, - Qt::WindowFlags flags) - : QOpenGLWidget(parent, flags) { - defaultConstructor(); -} -#endif // DOXYGEN - -/*! Virtual destructor. - -The viewer is replaced by \c NULL in the QGLViewerPool() (in order to preserve -other viewer's indexes) and allocated memory is released. The camera() is -deleted and should be copied before if it is shared by an other viewer. */ -QGLViewer::~QGLViewer() { - // See closeEvent comment. Destructor is called (and not closeEvent) only when - // the widget is embedded. Hence we saveToFile here. It is however a bad idea - // if virtual domElement() has been overloaded ! if (parent()) - // saveStateToFileForAllViewers(); - - QGLViewer::QGLViewerPool_.replace(QGLViewer::QGLViewerPool_.indexOf(this), - NULL); - - delete camera(); - delete[] selectBuffer_; - if (helpWidget()) { - // Needed for Qt 4 which has no main widget. - helpWidget()->close(); - delete helpWidget_; - } -} - -static QString QGLViewerVersionString() { - return QString::number((QGLVIEWER_VERSION & 0xff0000) >> 16) + "." + - QString::number((QGLVIEWER_VERSION & 0x00ff00) >> 8) + "." + - QString::number(QGLVIEWER_VERSION & 0x0000ff); -} - -static Qt::KeyboardModifiers keyboardModifiersFromState(unsigned int state) { - // Convertion of keyboard modifiers and mouse buttons as an int is no longer - // supported : emulate - return Qt::KeyboardModifiers(int(state & 0xFF000000)); -} - -static Qt::MouseButton mouseButtonFromState(unsigned int state) { - // Convertion of keyboard modifiers and mouse buttons as an int is no longer - // supported : emulate - return Qt::MouseButton(state & 0xFFFF); -} - -/*! Initializes the QGLViewer OpenGL context and then calls user-defined init(). - -This method is automatically called once, before the first call to paintGL(). - -Overload init() instead of this method to modify viewer specific OpenGL state. - -If a 4.3 context could not be set, a 2.1 context will be used instead. - \see `isOpenGL_4_3()` -*/ -void QGLViewer::initializeGL() { - QSurfaceFormat format; - format.setDepthBufferSize(24); - format.setStencilBufferSize(8); - format.setVersion(4,3); - format.setProfile(QSurfaceFormat::CompatibilityProfile); - format.setSamples(0); - context()->setFormat(format); - bool created = context()->create(); - if(!created || context()->format().profile() != QSurfaceFormat::CompatibilityProfile) { - // impossible to get a 4.3 compatibility profile, retry with 2.0 - format.setVersion(2,1); - context()->setFormat(format); - created = context()->create(); - is_ogl_4_3 = false; - } - else - { - is_ogl_4_3 = true; - } - makeCurrent(); - QOpenGLFunctions_2_1::initializeOpenGLFunctions(); - // Default colors - setForegroundColor(QColor(180, 180, 180)); - setBackgroundColor(QColor(51, 51, 51)); - - // Clear the buffer where we're going to draw - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // Calls user defined method. Default emits a signal. - init(); - - //OpenGL buffers and programs initialization - for(int i=0; i= 0; --view) { - // Clears screen, set model view matrix with shifted matrix for ith buffer - preDrawStereo(view); - // Used defined method. Default is empty - if (camera()->frame()->isManipulated()) - fastDraw(); - else - draw(); - postDraw(); - } - } else { - // Clears screen, set model view matrix... - preDraw(); - // Used defined method. Default calls draw() - if (camera()->frame()->isManipulated()) - fastDraw(); - else - draw(); - // Add visual hints: axis, camera, grid... - postDraw(); - } - Q_EMIT drawFinished(true); -} - -/*! Sets OpenGL state before draw(). - -Default behavior clears screen and sets the projection and modelView matrices: -\code -glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - -camera()->loadProjectionMatrix(); -camera()->loadModelViewMatrix(); -\endcode - -Emits the drawNeeded() signal once this is done (see the callback example). */ -void QGLViewer::preDraw() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // GL_PROJECTION matrix - camera()->loadProjectionMatrix(); - // GL_MODELVIEW matrix - camera()->loadModelViewMatrix(); - - Q_EMIT drawNeeded(); -} - -/*! Called after draw() to draw viewer visual hints. - -Default implementation displays axis, grid, FPS... when the respective flags are -sets. - -See the multiSelect and thumbnail examples for an -overloading illustration. - -The GLContext (color, LIGHTING, BLEND...) is \e not modified by this method, so -that in draw(), the user can rely on the OpenGL context he defined. Respect this -convention (by pushing/popping the different attributes) if you overload this -method. */ -void QGLViewer::postDraw() { - // Pivot point, line when camera rolls, zoom region - if (gridIsDrawn()) { - glLineWidth(1.0); - drawGrid(camera()->sceneRadius()); - } - if (axisIsDrawn()) { - glLineWidth(2.0); - drawAxis(1.0); - } - - - drawVisualHints(); - // FPS computation - const unsigned int maxCounter = 20; - if (++fpsCounter_ == maxCounter) { - f_p_s_ = 1000.0 * maxCounter / fpsTime_.restart(); - fpsString_ = tr("%1Hz", "Frames per seconds, in Hertz") - .arg(f_p_s_, 0, 'f', ((f_p_s_ < 10.0) ? 1 : 0)); - fpsCounter_ = 0; - } - - // Restore foregroundColor - float color[4]; - color[0] = foregroundColor().red() / 255.0f; - color[1] = foregroundColor().green() / 255.0f; - color[2] = foregroundColor().blue() / 255.0f; - color[3] = 1.0f; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - - if (FPSIsDisplayed()) - displayFPS(); - if (displayMessage_) - drawText(10, height() - 10, message_); - - // Restore GL state - glPopAttrib(); - glPopMatrix(); -} - -/*! Called before draw() (instead of preDraw()) when viewer displaysInStereo(). - -Same as preDraw() except that the glDrawBuffer() is set to \c GL_BACK_LEFT or \c -GL_BACK_RIGHT depending on \p leftBuffer, and it uses -qglviewer::Camera::loadProjectionMatrixStereo() and -qglviewer::Camera::loadModelViewMatrixStereo() instead. */ -void QGLViewer::preDrawStereo(bool leftBuffer) { - // Set buffer to draw in - // Seems that SGI and Crystal Eyes are not synchronized correctly ! - // That's why we don't draw in the appropriate buffer... - if (!leftBuffer) - glDrawBuffer(GL_BACK_LEFT); - else - glDrawBuffer(GL_BACK_RIGHT); - - // Clear the buffer where we're going to draw - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // GL_PROJECTION matrix - camera()->loadProjectionMatrixStereo(leftBuffer); - // GL_MODELVIEW matrix - camera()->loadModelViewMatrixStereo(leftBuffer); - - Q_EMIT drawNeeded(); -} - -/*! Draws a simplified version of the scene to guarantee interactive camera -displacements. - -This method is called instead of draw() when the qglviewer::Camera::frame() is -qglviewer::ManipulatedCameraFrame::isManipulated(). Default implementation -simply calls draw(). - -Overload this method if your scene is too complex to allow for interactive -camera manipulation. See the fastDraw -example for an illustration. */ -void QGLViewer::fastDraw() { draw(); } - -/*! Starts (\p edit = \c true, default) or stops (\p edit=\c false) the edition -of the camera(). - -Current implementation is limited to paths display. Get current state using -cameraIsEdited(). - -\attention This method sets the qglviewer::Camera::zClippingCoefficient() to 5.0 -when \p edit is \c true, so that the Camera paths (see -qglviewer::Camera::keyFrameInterpolator()) are not clipped. It restores the -previous value when \p edit is \c false. */ -void QGLViewer::setCameraIsEdited(bool edit) { - cameraIsEdited_ = edit; - if (edit) { - previousCameraZClippingCoefficient_ = camera()->zClippingCoefficient(); - // #CONNECTION# 5.0 also used in domElement() and in initFromDOMElement(). - camera()->setZClippingCoefficient(5.0); - } else - camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_); - - Q_EMIT cameraIsEditedChanged(edit); - - update(); -} - -// Key bindings. 0 means not defined -void QGLViewer::setDefaultShortcuts() { - // D e f a u l t a c c e l e r a t o r s - setShortcut(DRAW_AXIS, Qt::Key_A); - setShortcut(DRAW_GRID, Qt::Key_G); - setShortcut(DISPLAY_FPS, Qt::Key_F); - setShortcut(ENABLE_TEXT, Qt::SHIFT + Qt::Key_Question); - setShortcut(EXIT_VIEWER, Qt::Key_Escape); - setShortcut(CAMERA_MODE, Qt::Key_Space); - setShortcut(FULL_SCREEN, Qt::ALT + Qt::Key_Return); - setShortcut(STEREO, Qt::Key_S); - setShortcut(ANIMATION, Qt::Key_Return); - setShortcut(HELP, Qt::Key_H); - setShortcut(EDIT_CAMERA, Qt::Key_C); - setShortcut(MOVE_CAMERA_LEFT, Qt::Key_Left); - setShortcut(MOVE_CAMERA_RIGHT, Qt::Key_Right); - setShortcut(MOVE_CAMERA_UP, Qt::Key_Up); - setShortcut(MOVE_CAMERA_DOWN, Qt::Key_Down); - setShortcut(INCREASE_FLYSPEED, Qt::Key_Plus); - setShortcut(DECREASE_FLYSPEED, Qt::Key_Minus); - - keyboardActionDescription_[DISPLAY_FPS] = - tr("Toggles the display of the FPS", "DISPLAY_FPS action description"); - keyboardActionDescription_[FULL_SCREEN] = - tr("Toggles full screen display", "FULL_SCREEN action description"); - keyboardActionDescription_[DRAW_AXIS] = tr( - "Toggles the display of the world axis", "DRAW_AXIS action description"); - keyboardActionDescription_[DRAW_GRID] = - tr("Toggles the display of the XY grid", "DRAW_GRID action description"); - keyboardActionDescription_[CAMERA_MODE] = tr( - "Changes camera mode (observe or fly)", "CAMERA_MODE action description"); - keyboardActionDescription_[STEREO] = - tr("Toggles stereo display", "STEREO action description"); - keyboardActionDescription_[HELP] = - tr("Opens this help window", "HELP action description"); - keyboardActionDescription_[ANIMATION] = - tr("Starts/stops the animation", "ANIMATION action description"); - keyboardActionDescription_[EDIT_CAMERA] = - tr("Toggles camera paths display", - "EDIT_CAMERA action description"); // TODO change - keyboardActionDescription_[ENABLE_TEXT] = - tr("Toggles the display of the text", "ENABLE_TEXT action description"); - keyboardActionDescription_[EXIT_VIEWER] = - tr("Exits program", "EXIT_VIEWER action description"); - keyboardActionDescription_[MOVE_CAMERA_LEFT] = - tr("Moves camera left", "MOVE_CAMERA_LEFT action description"); - keyboardActionDescription_[MOVE_CAMERA_RIGHT] = - tr("Moves camera right", "MOVE_CAMERA_RIGHT action description"); - keyboardActionDescription_[MOVE_CAMERA_UP] = - tr("Moves camera up", "MOVE_CAMERA_UP action description"); - keyboardActionDescription_[MOVE_CAMERA_DOWN] = - tr("Moves camera down", "MOVE_CAMERA_DOWN action description"); - keyboardActionDescription_[INCREASE_FLYSPEED] = - tr("Increases fly speed", "INCREASE_FLYSPEED action description"); - keyboardActionDescription_[DECREASE_FLYSPEED] = - tr("Decreases fly speed", "DECREASE_FLYSPEED action description"); - - // K e y f r a m e s s h o r t c u t k e y s - setPathKey(Qt::Key_F1, 1); - setPathKey(Qt::Key_F2, 2); - setPathKey(Qt::Key_F3, 3); - setPathKey(Qt::Key_F4, 4); - setPathKey(Qt::Key_F5, 5); - setPathKey(Qt::Key_F6, 6); - setPathKey(Qt::Key_F7, 7); - setPathKey(Qt::Key_F8, 8); - setPathKey(Qt::Key_F9, 9); - setPathKey(Qt::Key_F10, 10); - setPathKey(Qt::Key_F11, 11); - setPathKey(Qt::Key_F12, 12); - - setAddKeyFrameKeyboardModifiers(Qt::AltModifier); - setPlayPathKeyboardModifiers(Qt::NoModifier); -} - -// M o u s e b e h a v i o r -void QGLViewer::setDefaultMouseBindings() { - const Qt::KeyboardModifiers cameraKeyboardModifiers = Qt::NoModifier; - const Qt::KeyboardModifiers frameKeyboardModifiers = Qt::ControlModifier; - - //#CONNECTION# toggleCameraMode() - for (int handler = 0; handler < 2; ++handler) { - MouseHandler mh = (MouseHandler)(handler); - Qt::KeyboardModifiers modifiers = - (mh == FRAME) ? frameKeyboardModifiers : cameraKeyboardModifiers; - - setMouseBinding(modifiers, Qt::LeftButton, mh, ROTATE); - setMouseBinding(modifiers, Qt::MidButton, mh, ZOOM); - setMouseBinding(modifiers, Qt::RightButton, mh, TRANSLATE); - - setMouseBinding(Qt::Key_R, modifiers, Qt::LeftButton, mh, SCREEN_ROTATE); - - setWheelBinding(modifiers, mh, ZOOM); - } - - // Z o o m o n r e g i o n - setMouseBinding(Qt::ShiftModifier, Qt::MidButton, CAMERA, ZOOM_ON_REGION); - - // S e l e c t - setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, SELECT); - - setMouseBinding(Qt::ShiftModifier, Qt::RightButton, RAP_FROM_PIXEL); - // D o u b l e c l i c k - setMouseBinding(Qt::NoModifier, Qt::LeftButton, ALIGN_CAMERA, true); - setMouseBinding(Qt::NoModifier, Qt::MidButton, SHOW_ENTIRE_SCENE, true); - setMouseBinding(Qt::NoModifier, Qt::RightButton, CENTER_SCENE, true); - - setMouseBinding(frameKeyboardModifiers, Qt::LeftButton, ALIGN_FRAME, true); - // middle double click makes no sense for manipulated frame - setMouseBinding(frameKeyboardModifiers, Qt::RightButton, CENTER_FRAME, true); - - // A c t i o n s w i t h k e y m o d i f i e r s - setMouseBinding(Qt::Key_Z, Qt::NoModifier, Qt::LeftButton, ZOOM_ON_PIXEL); - setMouseBinding(Qt::Key_Z, Qt::NoModifier, Qt::RightButton, ZOOM_TO_FIT); - -#ifdef Q_OS_MAC - // Specific Mac bindings for touchpads. Two fingers emulate a wheelEvent which - // zooms. There is no right button available : make Option key + left emulate - // the right button. A Control+Left indeed emulates a right click (OS X system - // configuration), but it does no seem to support dragging. Done at the end to - // override previous settings. - const Qt::KeyboardModifiers macKeyboardModifiers = Qt::AltModifier; - - setMouseBinding(macKeyboardModifiers, Qt::LeftButton, CAMERA, TRANSLATE); - setMouseBinding(macKeyboardModifiers, Qt::LeftButton, CENTER_SCENE, true); - setMouseBinding(frameKeyboardModifiers | macKeyboardModifiers, Qt::LeftButton, - CENTER_FRAME, true); - setMouseBinding(frameKeyboardModifiers | macKeyboardModifiers, Qt::LeftButton, - FRAME, TRANSLATE); -#endif -} - -/*! Associates a new qglviewer::Camera to the viewer. - -You should only use this method when you derive a new class from -qglviewer::Camera and want to use one of its instances instead of the original -class. - -It you simply want to save and restore Camera positions, use -qglviewer::Camera::addKeyFrameToPath() and qglviewer::Camera::playPath() -instead. - -This method silently ignores \c NULL \p camera pointers. The calling method is -responsible for deleting the previous camera pointer in order to prevent memory -leaks if needed. - -The sceneRadius() and sceneCenter() of \p camera are set to the \e current -QGLViewer values. - -All the \p camera qglviewer::Camera::keyFrameInterpolator() -qglviewer::KeyFrameInterpolator::interpolated() signals are connected to the -viewer update() slot. The connections with the previous viewer's camera are -removed. */ -void QGLViewer::setCamera(Camera *const camera) { - if (!camera) - return; - - camera->setSceneRadius(sceneRadius()); - camera->setSceneCenter(sceneCenter()); - camera->setScreenWidthAndHeight(width(), height()); - - // Disconnect current camera from this viewer. - disconnect(this->camera()->frame(), SIGNAL(manipulated()), this, - SLOT(update())); - disconnect(this->camera()->frame(), SIGNAL(spun()), this, SLOT(update())); - - // Connect camera frame to this viewer. - connect(camera->frame(), SIGNAL(manipulated()), SLOT(update())); - connect(camera->frame(), SIGNAL(spun()), SLOT(update())); - - connectAllCameraKFIInterpolatedSignals(false); - camera_ = camera; - connectAllCameraKFIInterpolatedSignals(); - - previousCameraZClippingCoefficient_ = this->camera()->zClippingCoefficient(); -} - -void QGLViewer::connectAllCameraKFIInterpolatedSignals(bool connection) { - for (QMap::ConstIterator - it = camera()->kfi_.begin(), - end = camera()->kfi_.end(); - it != end; ++it) { - if (connection) - connect(camera()->keyFrameInterpolator(it.key()), SIGNAL(interpolated()), - SLOT(update())); - else - disconnect(camera()->keyFrameInterpolator(it.key()), - SIGNAL(interpolated()), this, SLOT(update())); - } - - if (connection) - connect(camera()->interpolationKfi_, SIGNAL(interpolated()), - SLOT(update())); - else - disconnect(camera()->interpolationKfi_, SIGNAL(interpolated()), this, - SLOT(update())); -} - -/*! Draws a representation of \p light. - -Called in draw(), this method is useful to debug or display your light setup. -Light drawing depends on the type of light (point, spot, directional). - -The method retrieves the light setup using \c glGetLightfv. Position and define -your lights before calling this method. - -Light is drawn using its diffuse color. Disabled lights are not displayed. - -Drawing size is proportional to sceneRadius(). Use \p scale to rescale it. - -See the drawLight example for an -illustration. - -\attention You need to enable \c GL_COLOR_MATERIAL before calling this method. -\c glColor is set to the light diffuse color. */ -void QGLViewer::drawLight(GLenum light, qreal scale) const { -} - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) -void QGLViewer::renderText(int x, int y, const QString &str, - const QFont &font) { - QColor fontColor = QColor(0, 0, - 0, 255); - - // Render text - QPainter painter(this); - painter.setPen(fontColor); - painter.setFont(font); - painter.drawText(x, y, str); - painter.end(); -} - -void QGLViewer::renderText(double x, double y, double z, const QString &str, - const QFont &font) { - const Vec proj = camera_->projectedCoordinatesOf(Vec(x, y, z)); - renderText(proj.x, proj.y, str, font); -} -#endif - -/*! Draws \p text at position \p x, \p y (expressed in screen coordinates -pixels, origin in the upper left corner of the widget). - -The default QApplication::font() is used to render the text when no \p fnt is -specified. Use QApplication::setFont() to define this default font. - -You should disable \c GL_LIGHTING and \c GL_DEPTH_TEST before this method so -that colors are properly rendered. - -This method can be used in conjunction with the -qglviewer::Camera::projectedCoordinatesOf() method to display a text attached to -an object. In your draw() method use: \code qglviewer::Vec screenPos = -camera()->projectedCoordinatesOf(myFrame.position()); -drawText((int)screenPos[0], (int)screenPos[1], "My Object"); -\endcode -See the screenCoordSystem -example for an illustration. - -Text is displayed only when textIsEnabled() (default). This mechanism allows the -user to conveniently remove all the displayed text with a single keyboard -shortcut. - -See also displayMessage() to drawText() for only a short amount of time. - -Use renderText(x,y,z, text) instead if you want to draw a text located - at a specific 3D position instead of 2D screen coordinates (fixed size text, -facing the camera). - -The \c GL_MODELVIEW and \c GL_PROJECTION matrices are not modified by this -method. -*/ -void QGLViewer::drawText(int x, int y, const QString &text, const QFont &fnt) { - if (!textIsEnabled()) - return; - - renderText(x, y, text, fnt); -} - -/*! Briefly displays a message in the lower left corner of the widget. -Convenient to provide feedback to the user. - -\p message is displayed during \p delay milliseconds (default is 2 seconds) -using drawText(). - -This method should not be called in draw(). If you want to display a text in -each draw(), use drawText() instead. - -If this method is called when a message is already displayed, the new message -replaces the old one. Use setTextIsEnabled() (default shortcut is '?') to enable -or disable text (and hence messages) display. */ -void QGLViewer::displayMessage(const QString &message, int delay) { - message_ = message; - displayMessage_ = true; - // Was set to single shot in defaultConstructor. - messageTimer_.start(delay); - if (textIsEnabled()) - update(); -} - -void QGLViewer::hideMessage() { - displayMessage_ = false; - if (textIsEnabled()) - update(); -} - -/*! Displays the averaged currentFPS() frame rate in the upper left corner of -the widget. - -update() should be called in a loop in order to have a meaningful value (this is -the case when you continuously move the camera using the mouse or when -animationIsStarted()). setAnimationPeriod(0) to make this loop as fast as -possible in order to reach and measure the maximum available frame rate. - -When FPSIsDisplayed() is \c true (default is \c false), this method is called by -postDraw() to display the currentFPS(). Use QApplication::setFont() to define -the font (see drawText()). */ -void QGLViewer::displayFPS() { - drawText(10, - int(1.5 * ((QApplication::font().pixelSize() > 0) - ? QApplication::font().pixelSize() - : QApplication::font().pointSize())), - fpsString_); -} - -/*! Modify the projection matrix so that drawing can be done directly with 2D -screen coordinates. - -Once called, the \p x and \p y coordinates passed to \c glVertex are expressed -in pixels screen coordinates. The origin (0,0) is in the upper left corner of -the widget by default. This follows the Qt standards, so that you can directly -use the \c pos() provided by for instance \c QMouseEvent. Set \p upward to \c -true to place the origin in the \e lower left corner, thus following the OpenGL -and mathematical standards. It is always possible to switch between the two -representations using \c newY = height() - \c y. - -You need to call stopScreenCoordinatesSystem() at the end of the drawing block -to restore the previous camera matrix. - -In practice, this method should be used in draw(). It sets an appropriate -orthographic projection matrix and then sets \c glMatrixMode to \c GL_MODELVIEW. - -See the screenCoordSystem, multiSelect and backgroundImage examples -for an illustration. - -You may want to disable \c GL_LIGHTING, to enable \c GL_LINE_SMOOTH or \c -GL_BLEND to draw when this method is used. - -If you want to link 2D drawings to 3D objects, use -qglviewer::Camera::projectedCoordinatesOf() to compute the 2D projection on -screen of a 3D point (see the screenCoordSystem example). See -also drawText(). - -In this mode, you should use z values that are in the [0.0, 1.0[ range (0.0 -corresponding to the near clipping plane and 1.0 being just beyond the far -clipping plane). This interval matches the values that can be read from the -z-buffer. Note that if you use the convenient \c glVertex2i() to provide -coordinates, the implicit 0.0 z coordinate will make your drawings appear \e on -\e top of the rest of the scene. */ -void QGLViewer::startScreenCoordinatesSystem(bool ) const { -} - -/*! Stops the pixel coordinate drawing block started by -startScreenCoordinatesSystem(). - -The \c GL_MODELVIEW and \c GL_PROJECTION matrices modified in -startScreenCoordinatesSystem() are restored. \c glMatrixMode is set to \c -GL_MODELVIEW. */ -void QGLViewer::stopScreenCoordinatesSystem() const { -} - -/*! Overloading of the \c QObject method. - -If animationIsStarted(), calls animate() and draw(). */ -void QGLViewer::timerEvent(QTimerEvent *) { - if (animationIsStarted()) { - animate(); - update(); - } -} - -/*! Starts the animation loop. See animationIsStarted(). */ -void QGLViewer::startAnimation() { - animationTimerId_ = startTimer(animationPeriod()); - animationStarted_ = true; -} - -/*! Stops animation. See animationIsStarted(). */ -void QGLViewer::stopAnimation() { - animationStarted_ = false; - if (animationTimerId_ != 0) - killTimer(animationTimerId_); -} - -/*! Overloading of the \c QWidget method. - -Saves the viewer state using saveStateToFile() and then calls -QOpenGLWidget::closeEvent(). */ -void QGLViewer::closeEvent(QCloseEvent *e) { - // When the user clicks on the window close (x) button: - // - If the viewer is a top level window, closeEvent is called and then saves - // to file. - Otherwise, nothing happen s:( When the user press the - // EXIT_VIEWER keyboard shortcut: - If the viewer is a top level window, - // saveStateToFile() is also called - Otherwise, closeEvent is NOT called and - // keyPressEvent does the job. - - /* After tests: - E : Embedded widget - N : Widget created with new - C : closeEvent called - D : destructor called - - E N C D - y y - y n y - n y y - n n y y - - closeEvent is called iif the widget is NOT embedded. - - Destructor is called iif the widget is created on the stack - or if widget (resp. parent if embedded) is created with WDestructiveClose - flag. - - closeEvent always before destructor. - - Close using qApp->closeAllWindows or (x) is identical. - */ - - // #CONNECTION# Also done for EXIT_VIEWER in keyPressEvent(). - saveStateToFile(); - QOpenGLWidget::closeEvent(e); -} - -/*! Simple wrapper method: calls \c select(event->pos()). - -Emits \c pointSelected(e) which is useful only if you rely on the Qt signal-slot -mechanism and you did not overload QGLViewer. If you choose to derive your own -viewer class, simply overload select() (or probably simply drawWithNames(), see -the select example) to implement your -selection mechanism. - -This method is called when you use the QGLViewer::SELECT mouse binding(s) -(default is Shift + left button). Use setMouseBinding() to change this. */ -void QGLViewer::select(const QMouseEvent *event) { - // For those who don't derive but rather rely on the signal-slot mechanism. - Q_EMIT pointSelected(event); - select(event->pos()); -} - -/*! This method performs a selection in the scene from pixel coordinates. - -It is called when the user clicks on the QGLViewer::SELECT -QGLViewer::ClickAction binded button(s) (default is Shift + LeftButton). - -This template method successively calls four other methods: -\code -beginSelection(point); -drawWithNames(); -endSelection(point); -postSelection(point); -\endcode - -The default implementation of these methods is as follows (see the methods' -documentation for more details): - -\arg beginSelection() sets the \c GL_SELECT mode with the appropriate picking -matrices. A rectangular frustum (of size defined by selectRegionWidth() and -selectRegionHeight()) centered on \p point is created. - -\arg drawWithNames() is empty and should be overloaded. It draws each selectable -object of the scene, enclosed by calls to \c glPushName() / \c glPopName() to -tag the object with an integer id. - -\arg endSelection() then restores \c GL_RENDER mode and analyzes the -selectBuffer() to set in selectedName() the id of the object that was drawn in -the region. If several object are in the region, the closest one in the depth -buffer is chosen. If no object has been drawn under cursor, selectedName() is -set to -1. - -\arg postSelection() is empty and can be overloaded for possible -signal/display/interface update. - -See the \c glSelectBuffer() man page for details on this \c GL_SELECT mechanism. - -This default implementation is quite limited: only the closer object is -selected, and only one level of names can be pushed. However, this reveals -sufficient in many cases and you usually only have to overload drawWithNames() -to implement a simple object selection process. See the select example for an illustration. - -If you need a more complex selection process (such as a point, edge or triangle -selection, which is easier with a 2 or 3 levels selectBuffer() heap, and which -requires a finer depth sorting to privilege point over edge and edges over -triangles), overload the endSelection() method. Use setSelectRegionWidth(), -setSelectRegionHeight() and setSelectBufferSize() to tune the select buffer -configuration. See the multiSelect -example for an illustration. - -\p point is the center pixel (origin in the upper left corner) of the selection -region. Use qglviewer::Camera::convertClickToLine() to transform these -coordinates in a 3D ray if you want to perform an analytical intersection. - -\attention \c GL_SELECT mode seems to report wrong results when used in -conjunction with backface culling. If you encounter problems try to \c -glDisable(GL_CULL_FACE). */ -void QGLViewer::select(const QPoint &point) { - beginSelection(point); - drawWithNames(); - endSelection(point); - postSelection(point); -} - -/*! This method should prepare the selection. It is called by select() before -drawWithNames(). -*/ -void QGLViewer::beginSelection(const QPoint &point) -{ - makeCurrent(); - glEnable(GL_SCISSOR_TEST); - glScissor(point.x(), camera()->screenHeight()-1-point.y(), 1, 1); -} - -/*! This method is called by select() after scene elements were drawn by -drawWithNames(). - It clears the OpenGL state set by beginSelection*/ -void QGLViewer::endSelection(const QPoint &point) { - Q_UNUSED(point); - glDisable(GL_SCISSOR_TEST); -} - -/*! Sets the selectBufferSize(). - -The previous selectBuffer() is deleted and a new one is created. */ -void QGLViewer::setSelectBufferSize(int size) { - if (selectBuffer_) - delete[] selectBuffer_; - selectBufferSize_ = size; - selectBuffer_ = new GLuint[selectBufferSize()]; -} - -static QString mouseButtonsString(Qt::MouseButtons b) { - QString result(""); - bool addAmpersand = false; - if (b & Qt::LeftButton) { - result += QGLViewer::tr("Left", "left mouse button"); - addAmpersand = true; - } - if (b & Qt::MidButton) { - if (addAmpersand) - result += " & "; - result += QGLViewer::tr("Middle", "middle mouse button"); - addAmpersand = true; - } - if (b & Qt::RightButton) { - if (addAmpersand) - result += " & "; - result += QGLViewer::tr("Right", "right mouse button"); - } - return result; -} - -void QGLViewer::performClickAction(ClickAction ca, const QMouseEvent *const e) { - // Note: action that need it should call update(). - switch (ca) { - // # CONNECTION setMouseBinding prevents adding NO_CLICK_ACTION in - // clickBinding_ This case should hence not be possible. Prevents unused case - // warning. - case NO_CLICK_ACTION: - break; - case ZOOM_ON_PIXEL: - camera()->interpolateToZoomOnPixel(e->pos()); - break; - case ZOOM_TO_FIT: - camera()->interpolateToFitScene(); - break; - case SELECT: - select(e); - update(); - break; - case RAP_FROM_PIXEL: - if (!camera()->setPivotPointFromPixel(e->pos())) - camera()->setPivotPoint(sceneCenter()); - setVisualHintsMask(1); - update(); - break; - case RAP_IS_CENTER: - camera()->setPivotPoint(sceneCenter()); - setVisualHintsMask(1); - update(); - break; - case CENTER_FRAME: - if (manipulatedFrame()) - manipulatedFrame()->projectOnLine(camera()->position(), - camera()->viewDirection()); - break; - case CENTER_SCENE: - camera()->centerScene(); - break; - case SHOW_ENTIRE_SCENE: - camera()->showEntireScene(); - break; - case ALIGN_FRAME: - if (manipulatedFrame()) - manipulatedFrame()->alignWithFrame(camera()->frame()); - break; - case ALIGN_CAMERA: - Frame *frame = new Frame(); - frame->setTranslation(camera()->pivotPoint()); - camera()->frame()->alignWithFrame(frame, true); - delete frame; - break; - } -} - -/*! Overloading of the \c QWidget method. - -When the user clicks on the mouse: -\arg if a mouseGrabber() is defined, qglviewer::MouseGrabber::mousePressEvent() -is called, \arg otherwise, the camera() or the manipulatedFrame() interprets the -mouse displacements, depending on mouse bindings. - -Mouse bindings customization can be achieved using setMouseBinding() and -setWheelBinding(). See the mouse page for a complete -description of mouse bindings. - -See the mouseMoveEvent() documentation for an example of more complex mouse -behavior customization using overloading. - -\note When the mouseGrabber() is a manipulatedFrame(), the modifier keys are not -taken into account. This allows for a direct manipulation of the -manipulatedFrame() when the mouse hovers, which is probably what is expected. */ -void QGLViewer::mousePressEvent(QMouseEvent *e) { - //#CONNECTION# mouseDoubleClickEvent has the same structure - //#CONNECTION# mouseString() concatenates bindings description in inverse - // order. - ClickBindingPrivate cbp(e->modifiers(), e->button(), false, - (Qt::MouseButtons)(e->buttons() & ~(e->button())), - currentlyPressedKey_); - - if (clickBinding_.contains(cbp)) { - performClickAction(clickBinding_[cbp], e); - } else if (mouseGrabber()) { - if (mouseGrabberIsAManipulatedFrame_) { - for (QMap::ConstIterator - it = mouseBinding_.begin(), - end = mouseBinding_.end(); - it != end; ++it) - if ((it.value().handler == FRAME) && (it.key().button == e->button())) { - ManipulatedFrame *mf = - dynamic_cast(mouseGrabber()); - if (mouseGrabberIsAManipulatedCameraFrame_) { - mf->ManipulatedFrame::startAction(it.value().action, - it.value().withConstraint); - mf->ManipulatedFrame::mousePressEvent(e, camera()); - } else { - mf->startAction(it.value().action, it.value().withConstraint); - mf->mousePressEvent(e, camera()); - } - break; - } - } else - mouseGrabber()->mousePressEvent(e, camera()); - update(); - } else { - //#CONNECTION# wheelEvent has the same structure - const MouseBindingPrivate mbp(e->modifiers(), e->button(), - currentlyPressedKey_); - - if (mouseBinding_.contains(mbp)) { - MouseActionPrivate map = mouseBinding_[mbp]; - switch (map.handler) { - case CAMERA: - camera()->frame()->startAction(map.action, map.withConstraint); - camera()->frame()->mousePressEvent(e, camera()); - break; - case FRAME: - if (manipulatedFrame()) { - if (manipulatedFrameIsACamera_) { - manipulatedFrame()->ManipulatedFrame::startAction( - map.action, map.withConstraint); - manipulatedFrame()->ManipulatedFrame::mousePressEvent(e, camera()); - } else { - manipulatedFrame()->startAction(map.action, map.withConstraint); - manipulatedFrame()->mousePressEvent(e, camera()); - } - } - break; - } - if (map.action == SCREEN_ROTATE) - // Display visual hint line - update(); - } else - e->ignore(); - } -} - -/*! Overloading of the \c QWidget method. - -Mouse move event is sent to the mouseGrabber() (if any) or to the camera() or -the manipulatedFrame(), depending on mouse bindings (see setMouseBinding()). - -If you want to define your own mouse behavior, do something like this: -\code -void Viewer::mousePressEvent(QMouseEvent* e) -{ - -if ((e->button() == myButton) && (e->modifiers() == myModifiers)) - myMouseBehavior = true; -else - QGLViewer::mousePressEvent(e); -} - -void Viewer::mouseMoveEvent(QMouseEvent *e) -{ -if (myMouseBehavior) - // Use e->x() and e->y() as you want... -else - QGLViewer::mouseMoveEvent(e); -} - -void Viewer::mouseReleaseEvent(QMouseEvent* e) -{ -if (myMouseBehavior) - myMouseBehavior = false; -else - QGLViewer::mouseReleaseEvent(e); -} -\endcode */ -void QGLViewer::mouseMoveEvent(QMouseEvent *e) { - if (mouseGrabber()) { - mouseGrabber()->checkIfGrabsMouse(e->x(), e->y(), camera()); - if (mouseGrabber()->grabsMouse()) - if (mouseGrabberIsAManipulatedCameraFrame_) - (dynamic_cast(mouseGrabber())) - ->ManipulatedFrame::mouseMoveEvent(e, camera()); - else - mouseGrabber()->mouseMoveEvent(e, camera()); - else - setMouseGrabber(NULL); - update(); - } - - if (!mouseGrabber()) { - //#CONNECTION# mouseReleaseEvent has the same structure - if (camera()->frame()->isManipulated()) { - camera()->frame()->mouseMoveEvent(e, camera()); - // #CONNECTION# manipulatedCameraFrame::mouseMoveEvent specific if at the - // beginning - if (camera()->frame()->action_ == ZOOM_ON_REGION) - update(); - } else // ! - if ((manipulatedFrame()) && (manipulatedFrame()->isManipulated())) - if (manipulatedFrameIsACamera_) - manipulatedFrame()->ManipulatedFrame::mouseMoveEvent(e, camera()); - else - manipulatedFrame()->mouseMoveEvent(e, camera()); - else if (hasMouseTracking()) { - Q_FOREACH (MouseGrabber *mg, MouseGrabber::MouseGrabberPool()) { - mg->checkIfGrabsMouse(e->x(), e->y(), camera()); - if (mg->grabsMouse()) { - setMouseGrabber(mg); - // Check that MouseGrabber is not disabled - if (mouseGrabber() == mg) { - update(); - break; - } - } - } - } - } -} - -/*! Overloading of the \c QWidget method. - -Calls the mouseGrabber(), camera() or manipulatedFrame \c mouseReleaseEvent -method. - -See the mouseMoveEvent() documentation for an example of mouse behavior -customization. */ -void QGLViewer::mouseReleaseEvent(QMouseEvent *e) { - if (mouseGrabber()) { - if (mouseGrabberIsAManipulatedCameraFrame_) - (dynamic_cast(mouseGrabber())) - ->ManipulatedFrame::mouseReleaseEvent(e, camera()); - else - mouseGrabber()->mouseReleaseEvent(e, camera()); - mouseGrabber()->checkIfGrabsMouse(e->x(), e->y(), camera()); - if (!(mouseGrabber()->grabsMouse())) - setMouseGrabber(NULL); - // update(); - } else - //#CONNECTION# mouseMoveEvent has the same structure - if (camera()->frame()->isManipulated()) { - camera()->frame()->mouseReleaseEvent(e, camera()); - } else if ((manipulatedFrame()) && (manipulatedFrame()->isManipulated())) { - if (manipulatedFrameIsACamera_) - manipulatedFrame()->ManipulatedFrame::mouseReleaseEvent(e, camera()); - else - manipulatedFrame()->mouseReleaseEvent(e, camera()); - } else - e->ignore(); - - // Not absolutely needed (see above commented code for the optimal version), - // but may reveal useful for specific applications. - update(); -} - -/*! Overloading of the \c QWidget method. - -If defined, the wheel event is sent to the mouseGrabber(). It is otherwise sent -according to wheel bindings (see setWheelBinding()). */ -void QGLViewer::wheelEvent(QWheelEvent *e) { - if (mouseGrabber()) { - if (mouseGrabberIsAManipulatedFrame_) { - for (QMap::ConstIterator - it = wheelBinding_.begin(), - end = wheelBinding_.end(); - it != end; ++it) - if (it.value().handler == FRAME) { - ManipulatedFrame *mf = - dynamic_cast(mouseGrabber()); - if (mouseGrabberIsAManipulatedCameraFrame_) { - mf->ManipulatedFrame::startAction(it.value().action, - it.value().withConstraint); - mf->ManipulatedFrame::wheelEvent(e, camera()); - } else { - mf->startAction(it.value().action, it.value().withConstraint); - mf->wheelEvent(e, camera()); - } - break; - } - } else - mouseGrabber()->wheelEvent(e, camera()); - update(); - } else { - //#CONNECTION# mousePressEvent has the same structure - WheelBindingPrivate wbp(e->modifiers(), currentlyPressedKey_); - - if (wheelBinding_.contains(wbp)) { - MouseActionPrivate map = wheelBinding_[wbp]; - switch (map.handler) { - case CAMERA: - camera()->frame()->startAction(map.action, map.withConstraint); - camera()->frame()->wheelEvent(e, camera()); - break; - case FRAME: - if (manipulatedFrame()) { - if (manipulatedFrameIsACamera_) { - manipulatedFrame()->ManipulatedFrame::startAction( - map.action, map.withConstraint); - manipulatedFrame()->ManipulatedFrame::wheelEvent(e, camera()); - } else { - manipulatedFrame()->startAction(map.action, map.withConstraint); - manipulatedFrame()->wheelEvent(e, camera()); - } - } - break; - } - } else - e->ignore(); - } -} - -/*! Overloading of the \c QWidget method. - -The behavior of the mouse double click depends on the mouse binding. See -setMouseBinding() and the mouse page. */ -void QGLViewer::mouseDoubleClickEvent(QMouseEvent *e) { - //#CONNECTION# mousePressEvent has the same structure - ClickBindingPrivate cbp(e->modifiers(), e->button(), true, - (Qt::MouseButtons)(e->buttons() & ~(e->button())), - currentlyPressedKey_); - if (clickBinding_.contains(cbp)) - performClickAction(clickBinding_[cbp], e); - else if (mouseGrabber()) - mouseGrabber()->mouseDoubleClickEvent(e, camera()); - else - e->ignore(); -} - -/*! Sets the state of displaysInStereo(). See also toggleStereoDisplay(). - -First checks that the display is able to handle stereovision using -QOpenGLWidget::format(). Opens a warning message box in case of failure. Emits -the stereoChanged() signal otherwise. */ -void QGLViewer::setStereoDisplay(bool stereo) { - if (format().stereo()) { - stereo_ = stereo; - if (!displaysInStereo()) { - glDrawBuffer(GL_BACK_LEFT); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glDrawBuffer(GL_BACK_RIGHT); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - - Q_EMIT stereoChanged(stereo_); - - update(); - } else if (stereo) - QMessageBox::warning(this, - tr("Stereo not supported", "Message box window title"), - tr("Stereo is not supported on this display.")); - else - stereo_ = false; -} - -/*! Sets the isFullScreen() state. - -If the QGLViewer is embedded in an other QWidget (see -QWidget::topLevelWidget()), this widget is displayed in full screen instead. */ -void QGLViewer::setFullScreen(bool fullScreen) { - if (fullScreen_ == fullScreen) - return; - - fullScreen_ = fullScreen; - - QWidget *tlw = topLevelWidget(); - - if (isFullScreen()) { - prevPos_ = topLevelWidget()->pos(); - tlw->showFullScreen(); - tlw->move(0, 0); - } else { - tlw->showNormal(); - tlw->move(prevPos_); - } -} - -/*! Directly defines the mouseGrabber(). - -You should not call this method directly as it bypasses the -qglviewer::MouseGrabber::checkIfGrabsMouse() test performed by mouseMoveEvent(). - -If the MouseGrabber is disabled (see mouseGrabberIsEnabled()), this method -silently does nothing. */ -void QGLViewer::setMouseGrabber(MouseGrabber *mouseGrabber) { - if (!mouseGrabberIsEnabled(mouseGrabber)) - return; - - mouseGrabber_ = mouseGrabber; - - mouseGrabberIsAManipulatedFrame_ = - (dynamic_cast(mouseGrabber) != NULL); - mouseGrabberIsAManipulatedCameraFrame_ = - ((dynamic_cast(mouseGrabber) != NULL) && - (mouseGrabber != camera()->frame())); - Q_EMIT mouseGrabberChanged(mouseGrabber); -} - -/*! Sets the mouseGrabberIsEnabled() state. */ -void QGLViewer::setMouseGrabberIsEnabled( - const qglviewer::MouseGrabber *const mouseGrabber, bool enabled) { - if (enabled) - disabledMouseGrabbers_.remove(reinterpret_cast(mouseGrabber)); - else - disabledMouseGrabbers_[reinterpret_cast(mouseGrabber)]; -} - -QString QGLViewer::mouseActionString(QGLViewer::MouseAction ma) { - switch (ma) { - case QGLViewer::NO_MOUSE_ACTION: - return QString::null; - case QGLViewer::ROTATE: - return QGLViewer::tr("Rotates", "ROTATE mouse action"); - case QGLViewer::ZOOM: - return QGLViewer::tr("Zooms", "ZOOM mouse action"); - case QGLViewer::TRANSLATE: - return QGLViewer::tr("Translates", "TRANSLATE mouse action"); - case QGLViewer::MOVE_FORWARD: - return QGLViewer::tr("Moves forward", "MOVE_FORWARD mouse action"); - case QGLViewer::LOOK_AROUND: - return QGLViewer::tr("Looks around", "LOOK_AROUND mouse action"); - case QGLViewer::MOVE_BACKWARD: - return QGLViewer::tr("Moves backward", "MOVE_BACKWARD mouse action"); - case QGLViewer::SCREEN_ROTATE: - return QGLViewer::tr("Rotates in screen plane", - "SCREEN_ROTATE mouse action"); - case QGLViewer::ROLL: - return QGLViewer::tr("Rolls", "ROLL mouse action"); - case QGLViewer::DRIVE: - return QGLViewer::tr("Drives", "DRIVE mouse action"); - case QGLViewer::SCREEN_TRANSLATE: - return QGLViewer::tr("Horizontally/Vertically translates", - "SCREEN_TRANSLATE mouse action"); - case QGLViewer::ZOOM_ON_REGION: - return QGLViewer::tr("Zooms on region for", "ZOOM_ON_REGION mouse action"); - } - return QString::null; -} - -QString QGLViewer::clickActionString(QGLViewer::ClickAction ca) { - switch (ca) { - case QGLViewer::NO_CLICK_ACTION: - return QString::null; - case QGLViewer::ZOOM_ON_PIXEL: - return QGLViewer::tr("Zooms on pixel", "ZOOM_ON_PIXEL click action"); - case QGLViewer::ZOOM_TO_FIT: - return QGLViewer::tr("Zooms to fit scene", "ZOOM_TO_FIT click action"); - case QGLViewer::SELECT: - return QGLViewer::tr("Selects", "SELECT click action"); - case QGLViewer::RAP_FROM_PIXEL: - return QGLViewer::tr("Sets pivot point", "RAP_FROM_PIXEL click action"); - case QGLViewer::RAP_IS_CENTER: - return QGLViewer::tr("Resets pivot point", "RAP_IS_CENTER click action"); - case QGLViewer::CENTER_FRAME: - return QGLViewer::tr("Centers manipulated frame", - "CENTER_FRAME click action"); - case QGLViewer::CENTER_SCENE: - return QGLViewer::tr("Centers scene", "CENTER_SCENE click action"); - case QGLViewer::SHOW_ENTIRE_SCENE: - return QGLViewer::tr("Shows entire scene", - "SHOW_ENTIRE_SCENE click action"); - case QGLViewer::ALIGN_FRAME: - return QGLViewer::tr("Aligns manipulated frame", - "ALIGN_FRAME click action"); - case QGLViewer::ALIGN_CAMERA: - return QGLViewer::tr("Aligns camera", "ALIGN_CAMERA click action"); - } - return QString::null; -} - -static QString keyString(unsigned int key) { -#if QT_VERSION >= 0x040100 - return QKeySequence(int(key)).toString(QKeySequence::NativeText); -#else - return QString(QKeySequence(key)); -#endif -} - -QString QGLViewer::formatClickActionPrivate(ClickBindingPrivate cbp) { - bool buttonsBefore = cbp.buttonsBefore != Qt::NoButton; - QString keyModifierString = keyString(cbp.modifiers + cbp.key); - if (!keyModifierString.isEmpty()) { -#ifdef Q_OS_MAC - // modifiers never has a '+' sign. Add one space to clearly separate - // modifiers (and possible key) from button - keyModifierString += " "; -#else - // modifiers might be of the form : 'S' or 'Ctrl+S' or 'Ctrl+'. For - // consistency, add an other '+' if needed, no spaces - if (!keyModifierString.endsWith('+')) - keyModifierString += "+"; -#endif - } - - return tr("%1%2%3%4%5%6", "Modifier / button or wheel / double click / with " - "/ button / pressed") - .arg(keyModifierString) - .arg(mouseButtonsString(cbp.button) + - (cbp.button == Qt::NoButton ? tr("Wheel", "Mouse wheel") : "")) - .arg(cbp.doubleClick ? tr(" double click", "Suffix after mouse button") - : "") - .arg(buttonsBefore ? tr(" with ", "As in : Left button with Ctrl pressed") - : "") - .arg(buttonsBefore ? mouseButtonsString(cbp.buttonsBefore) : "") - .arg(buttonsBefore - ? tr(" pressed", "As in : Left button with Ctrl pressed") - : ""); -} - -bool QGLViewer::isValidShortcutKey(int key) { - return (key >= Qt::Key_Any && key < Qt::Key_Escape) || - (key >= Qt::Key_F1 && key <= Qt::Key_F35); -} - -#ifndef DOXYGEN -/*! This method is deprecated since version 2.5.0 - - Use setMouseBindingDescription(Qt::KeyboardModifiers, Qt::MouseButtons, - QString, bool, Qt::MouseButtons) instead. -*/ -void QGLViewer::setMouseBindingDescription(unsigned int state, - QString description, - bool doubleClick, - Qt::MouseButtons buttonsBefore) { - qWarning("setMouseBindingDescription(int state,...) is deprecated. Use the " - "modifier/button equivalent"); - setMouseBindingDescription(keyboardModifiersFromState(state), - mouseButtonFromState(state), description, - doubleClick, buttonsBefore); -} -#endif - -/*! Defines a custom mouse binding description, displayed in the help() window's - Mouse tab. - - Same as calling setMouseBindingDescription(Qt::Key, Qt::KeyboardModifiers, - Qt::MouseButton, QString, bool, Qt::MouseButtons), with a key value of - Qt::Key(0) (i.e. binding description when no regular key needs to be pressed). - */ -void QGLViewer::setMouseBindingDescription(Qt::KeyboardModifiers modifiers, - Qt::MouseButton button, - QString description, - bool doubleClick, - Qt::MouseButtons buttonsBefore) { - setMouseBindingDescription(Qt::Key(0), modifiers, button, description, - doubleClick, buttonsBefore); -} - -/*! Defines a custom mouse binding description, displayed in the help() window's -Mouse tab. - -\p modifiers is a combination of Qt::KeyboardModifiers (\c Qt::ControlModifier, -\c Qt::AltModifier, \c Qt::ShiftModifier, \c Qt::MetaModifier). Possibly -combined using the \c "|" operator. - -\p button is one of the Qt::MouseButtons (\c Qt::LeftButton, \c Qt::MidButton, -\c Qt::RightButton...). - -\p doubleClick indicates whether or not the user has to double click this button -to perform the described action. \p buttonsBefore lists the buttons that need to -be pressed before the double click. - -Set an empty \p description to \e remove a mouse binding description. - -\code -// The R key combined with the Left mouse button rotates the camera in the -screen plane. setMouseBindingDescription(Qt::Key_R, Qt::NoModifier, -Qt::LeftButton, "Rotates camera in screen plane"); - -// A left button double click toggles full screen -setMouseBindingDescription(Qt::NoModifier, Qt::LeftButton, "Toggles full screen -mode", true); - -// Removes the description of Ctrl+Right button -setMouseBindingDescription(Qt::ControlModifier, Qt::RightButton, ""); -\endcode - -Overload mouseMoveEvent() and friends to implement your custom mouse behavior -(see the mouseMoveEvent() documentation for an example). See the keyboardAndMouse example for an -illustration. - -Use setMouseBinding() and setWheelBinding() to change the standard mouse action -bindings. */ -void QGLViewer::setMouseBindingDescription( - Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton button, - QString description, bool doubleClick, Qt::MouseButtons buttonsBefore) { - ClickBindingPrivate cbp(modifiers, button, doubleClick, buttonsBefore, key); - - if (description.isEmpty()) - mouseDescription_.remove(cbp); - else - mouseDescription_[cbp] = description; -} - -static QString tableLine(const QString &left, const QString &right) { - static bool even = false; - const QString tdtd("
"); - const QString tdtr("\n"); - - QString res(""; - else - res += "#ffffff\">"; - res += "" + left + tdtd + right + tdtr; - even = !even; - - return res; -} - -/*! Returns a QString that describes the application mouse bindings, displayed -in the help() window \c Mouse tab. - -Result is a table that describes custom application mouse binding descriptions -defined using setMouseBindingDescription() as well as standard mouse bindings -(defined using setMouseBinding() and setWheelBinding()). See the mouse page for details on mouse bindings. - -See also helpString() and keyboardString(). */ -QString QGLViewer::mouseString() const { - QString text( - "
\n"); - const QString trtd("\n"); - const QString tdtd("\n") - .arg(tr("Button(s)", - "Buttons column header in help window mouse tab")) - .arg(tr("Description", - "Description column header in help window mouse tab")); - - QMap mouseBinding; - - // User-defined mouse bindings come first. - for (QMap::ConstIterator - itm = mouseDescription_.begin(), - endm = mouseDescription_.end(); - itm != endm; ++itm) - mouseBinding[itm.key()] = itm.value(); - - for (QMap::ConstIterator - it = mouseBinding.begin(), - end = mouseBinding.end(); - it != end; ++it) { - // Should not be needed (see setMouseBindingDescription()) - if (it.value().isNull()) - continue; - - text += tableLine(formatClickActionPrivate(it.key()), it.value()); - } - - // Optional separator line - if (!mouseBinding.isEmpty()) { - mouseBinding.clear(); - text += QString("\n") - .arg(tr("Standard mouse bindings", "In help window mouse tab")); - } - - // Then concatenates the descriptions of wheelBinding_, mouseBinding_ and - // clickBinding_. The order is significant and corresponds to the priorities - // set in mousePressEvent() (reverse priority order, last one overwrites - // previous) #CONNECTION# mousePressEvent() order - for (QMap::ConstIterator - itmb = mouseBinding_.begin(), - endmb = mouseBinding_.end(); - itmb != endmb; ++itmb) { - ClickBindingPrivate cbp(itmb.key().modifiers, itmb.key().button, false, - Qt::NoButton, itmb.key().key); - - QString text = mouseActionString(itmb.value().action); - - if (!text.isNull()) { - switch (itmb.value().handler) { - case CAMERA: - text += " " + tr("camera", "Suffix after action"); - break; - case FRAME: - text += " " + tr("manipulated frame", "Suffix after action"); - break; - } - if (!(itmb.value().withConstraint)) - text += "*"; - } - mouseBinding[cbp] = text; - } - - for (QMap::ConstIterator - itw = wheelBinding_.begin(), - endw = wheelBinding_.end(); - itw != endw; ++itw) { - ClickBindingPrivate cbp(itw.key().modifiers, Qt::NoButton, false, - Qt::NoButton, itw.key().key); - - QString text = mouseActionString(itw.value().action); - - if (!text.isNull()) { - switch (itw.value().handler) { - case CAMERA: - text += " " + tr("camera", "Suffix after action"); - break; - case FRAME: - text += " " + tr("manipulated frame", "Suffix after action"); - break; - } - if (!(itw.value().withConstraint)) - text += "*"; - } - - mouseBinding[cbp] = text; - } - - for (QMap::ConstIterator - itcb = clickBinding_.begin(), - endcb = clickBinding_.end(); - itcb != endcb; ++itcb) - mouseBinding[itcb.key()] = clickActionString(itcb.value()); - - for (QMap::ConstIterator - it2 = mouseBinding.begin(), - end2 = mouseBinding.end(); - it2 != end2; ++it2) { - if (it2.value().isNull()) - continue; - - text += tableLine(formatClickActionPrivate(it2.key()), it2.value()); - } - - text += "
"); - const QString tdtr("
"); - - text += QString("
%1%2
%1
"; - - return text; -} - -/*! Defines a custom keyboard shortcut description, that will be displayed in -the help() window \c Keyboard tab. - -The \p key definition is given as an \c int using Qt enumerated values. Set an -empty \p description to remove a shortcut description: \code -setKeyDescription(Qt::Key_W, "Toggles wireframe display"); -setKeyDescription(Qt::CTRL+Qt::Key_L, "Loads a new scene"); -// Removes a description -setKeyDescription(Qt::CTRL+Qt::Key_C, ""); -\endcode - -See the keyboardAndMouse example -for illustration and the keyboard page for -details. */ -void QGLViewer::setKeyDescription(unsigned int key, QString description) { - if (description.isEmpty()) - keyDescription_.remove(key); - else - keyDescription_[key] = description; -} - -QString QGLViewer::cameraPathKeysString() const { - if (pathIndex_.isEmpty()) - return QString::null; - - QVector keys; - keys.reserve(pathIndex_.count()); - for (QMap::ConstIterator i = pathIndex_.begin(), - endi = pathIndex_.end(); - i != endi; ++i) - keys.push_back(i.key()); - qSort(keys); - - QVector::const_iterator it = keys.begin(), end = keys.end(); - QString res = keyString(*it); - - const int maxDisplayedKeys = 6; - int nbDisplayedKeys = 0; - Qt::Key previousKey = (*it); - int state = 0; - ++it; - while ((it != end) && (nbDisplayedKeys < maxDisplayedKeys - 1)) { - switch (state) { - case 0: - if ((*it) == previousKey + 1) - state++; - else { - res += ", " + keyString(*it); - nbDisplayedKeys++; - } - break; - case 1: - if ((*it) == previousKey + 1) - state++; - else { - res += ", " + keyString(previousKey); - res += ", " + keyString(*it); - nbDisplayedKeys += 2; - state = 0; - } - break; - default: - if ((*it) != previousKey + 1) { - res += ".." + keyString(previousKey); - res += ", " + keyString(*it); - nbDisplayedKeys += 2; - state = 0; - } - break; - } - previousKey = *it; - ++it; - } - - if (state == 1) - res += ", " + keyString(previousKey); - if (state == 2) - res += ".." + keyString(previousKey); - if (it != end) - res += "..."; - - return res; -} - -/*! Returns a QString that describes the application keyboard shortcut bindings, -and that will be displayed in the help() window \c Keyboard tab. - -Default value is a table that describes the custom shortcuts defined using -setKeyDescription() as well as the \e standard QGLViewer::KeyboardAction -shortcuts (defined using setShortcut()). See the keyboard page for details on key customization. - -See also helpString() and mouseString(). */ -QString QGLViewer::keyboardString() const { - QString text( - "
\n"); - text += QString("\n") - .arg(QGLViewer::tr("Key(s)", - "Keys column header in help window mouse tab")) - .arg(QGLViewer::tr( - "Description", - "Description column header in help window mouse tab")); - - QMap keyDescription; - - // 1 - User defined key descriptions - for (QMap::ConstIterator kd = keyDescription_.begin(), - kdend = keyDescription_.end(); - kd != kdend; ++kd) - keyDescription[kd.key()] = kd.value(); - - // Add to text in sorted order - for (QMap::ConstIterator kb = keyDescription.begin(), - endb = keyDescription.end(); - kb != endb; ++kb) - text += tableLine(keyString(kb.key()), kb.value()); - - // 2 - Optional separator line - if (!keyDescription.isEmpty()) { - keyDescription.clear(); - text += QString("\n") - .arg(QGLViewer::tr("Standard viewer keys", - "In help window keys tab")); - } - - // 3 - KeyboardAction bindings description - for (QMap::ConstIterator - it = keyboardBinding_.begin(), - end = keyboardBinding_.end(); - it != end; ++it) - if ((it.value() != 0) && - ((!cameraIsInRotateMode()) || - ((it.key() != INCREASE_FLYSPEED) && (it.key() != DECREASE_FLYSPEED)))) - keyDescription[it.value()] = keyboardActionDescription_[it.key()]; - - // Add to text in sorted order - for (QMap::ConstIterator kb2 = keyDescription.begin(), - endb2 = keyDescription.end(); - kb2 != endb2; ++kb2) - text += tableLine(keyString(kb2.key()), kb2.value()); - - // 4 - Camera paths keys description - const QString cpks = cameraPathKeysString(); - if (!cpks.isNull()) { - text += "\n"; - text += tableLine( - keyString(playPathKeyboardModifiers()) + "" + - QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "", - QGLViewer::tr("Plays path (or resets saved position)")); - text += tableLine( - keyString(addKeyFrameKeyboardModifiers()) + "" + - QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "", - QGLViewer::tr("Adds a key frame to path (or defines a position)")); - text += tableLine( - keyString(addKeyFrameKeyboardModifiers()) + "" + - QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "+" + - QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "", - QGLViewer::tr("Deletes path (or saved position)")); - } - text += "
%1%2
%1
\n"; - text += QGLViewer::tr("Camera paths are controlled using the %1 keys " - "(noted Fx below):", - "Help window key tab camera keys") - .arg(cpks) + - "
"; - - return text; -} - -/*! Displays the help window "About" tab. See help() for details. */ -void QGLViewer::aboutQGLViewer() { - help(); - helpWidget()->setCurrentIndex(3); -} - -/*! Opens a modal help window that includes four tabs, respectively filled with -helpString(), keyboardString(), mouseString() and about libQGLViewer. - -Rich html-like text can be used (see the QStyleSheet documentation). This method -is called when the user presses the QGLViewer::HELP key (default is 'H'). - -You can use helpWidget() to access to the help widget (to add/remove tabs, -change layout...). - -The helpRequired() signal is emitted. */ -void QGLViewer::help() { - Q_EMIT helpRequired(); - - bool resize = false; - int width = 600; - int height = 400; - - static QString label[] = {tr("&Help", "Help window tab title"), - tr("&Keyboard", "Help window tab title"), - tr("&Mouse", "Help window tab title"), - tr("&About", "Help window about title")}; - - if (!helpWidget()) { - // Qt4 requires a NULL parent... - helpWidget_ = new QTabWidget(NULL); - helpWidget()->setWindowTitle(tr("Help", "Help window title")); - - resize = true; - for (int i = 0; i < 4; ++i) { - QTextEdit *tab = new QTextEdit(NULL); - tab->setReadOnly(true); - - helpWidget()->insertTab(i, tab, label[i]); - if (i == 3) { -#include "qglviewer-icon.xpm" - QPixmap pixmap(qglviewer_icon); - tab->document()->addResource(QTextDocument::ImageResource, - QUrl("mydata://qglviewer-icon.xpm"), - QVariant(pixmap)); - } - } - } - - for (int i = 0; i < 4; ++i) { - QString text; - switch (i) { - case 0: - text = helpString(); - break; - case 1: - text = keyboardString(); - break; - case 2: - text = mouseString(); - break; - case 3: - text = QString("

") + - tr("

libQGLViewer

" - "

Version %1


" - "A versatile 3D viewer based on OpenGL and Qt
" - "Copyright 2002-%2 Gilles Debunne
" - "%3") - .arg(QGLViewerVersionString()) - .arg("2014") - .arg("http://www.libqglviewer.com") + - QString("
"); - break; - default: - break; - } - - QTextEdit *textEdit = (QTextEdit *)(helpWidget()->widget(i)); - textEdit->setHtml(text); - textEdit->setText(text); - - if (resize && (textEdit->height() > height)) - height = textEdit->height(); - } - - if (resize) - helpWidget()->resize(width, height + 40); // 40 pixels is ~ tabs' height - helpWidget()->show(); - helpWidget()->raise(); -} - -/*! Overloading of the \c QWidget method. - -Default keyboard shortcuts are defined using setShortcut(). Overload this method -to implement a specific keyboard binding. Call the original method if you do not -catch the event to preserve the viewer default key bindings: \code void -Viewer::keyPressEvent(QKeyEvent *e) -{ - // Defines the Alt+R shortcut. - if ((e->key() == Qt::Key_R) && (e->modifiers() == Qt::AltModifier)) - { - myResetFunction(); - update(); // Refresh display - } - else - QGLViewer::keyPressEvent(e); -} - -// With Qt 2 or 3, you would retrieve modifiers keys using : -// const Qt::ButtonState modifiers = (Qt::ButtonState)(e->state() & -Qt::KeyButtonMask); \endcode When you define a new keyboard shortcut, use -setKeyDescription() to provide a short description which is displayed in the -help() window Keyboard tab. See the keyboardAndMouse example for an -illustration. - -See also QOpenGLWidget::keyReleaseEvent(). */ -void QGLViewer::keyPressEvent(QKeyEvent *e) { - if (e->key() == 0) { - e->ignore(); - return; - } - - const Qt::Key key = Qt::Key(e->key()); - - const Qt::KeyboardModifiers modifiers = e->modifiers(); - - QMap::ConstIterator it = keyboardBinding_ - .begin(), - end = - keyboardBinding_.end(); - const unsigned int target = key | modifiers; - while ((it != end) && (it.value() != target)) - ++it; - - if (it != end) - handleKeyboardAction(it.key()); - else if (pathIndex_.contains(Qt::Key(key))) { - // Camera paths - unsigned int index = pathIndex_[Qt::Key(key)]; - - // not safe, but try to double press on two viewers at the same time ! - static QTime doublePress; - - if (modifiers == playPathKeyboardModifiers()) { - int elapsed = doublePress.restart(); - if ((elapsed < 250) && (index == previousPathId_)) - camera()->resetPath(index); - else { - // Stop previous interpolation before starting a new one. - if (index != previousPathId_) { - KeyFrameInterpolator *previous = - camera()->keyFrameInterpolator(previousPathId_); - if ((previous) && (previous->interpolationIsStarted())) - previous->resetInterpolation(); - } - camera()->playPath(index); - } - previousPathId_ = index; - } else if (modifiers == addKeyFrameKeyboardModifiers()) { - int elapsed = doublePress.restart(); - if ((elapsed < 250) && (index == previousPathId_)) { - if (camera()->keyFrameInterpolator(index)) { - disconnect(camera()->keyFrameInterpolator(index), - SIGNAL(interpolated()), this, SLOT(update())); - if (camera()->keyFrameInterpolator(index)->numberOfKeyFrames() > 1) - displayMessage( - tr("Path %1 deleted", "Feedback message").arg(index)); - else - displayMessage( - tr("Position %1 deleted", "Feedback message").arg(index)); - camera()->deletePath(index); - } - } else { - bool nullBefore = (camera()->keyFrameInterpolator(index) == NULL); - camera()->addKeyFrameToPath(index); - if (nullBefore) - connect(camera()->keyFrameInterpolator(index), SIGNAL(interpolated()), - SLOT(update())); - int nbKF = camera()->keyFrameInterpolator(index)->numberOfKeyFrames(); - if (nbKF > 1) - displayMessage(tr("Path %1, position %2 added", "Feedback message") - .arg(index) - .arg(nbKF)); - else - displayMessage( - tr("Position %1 saved", "Feedback message").arg(index)); - } - previousPathId_ = index; - } - update(); - } else { - if (isValidShortcutKey(key)) - currentlyPressedKey_ = key; - e->ignore(); - } -} - -void QGLViewer::keyReleaseEvent(QKeyEvent *e) { - if (isValidShortcutKey(e->key())) - currentlyPressedKey_ = Qt::Key(0); -} - -void QGLViewer::handleKeyboardAction(KeyboardAction id) { - switch (id) { - case DRAW_AXIS: - toggleAxisIsDrawn(); - break; - case DRAW_GRID: - toggleGridIsDrawn(); - break; - case DISPLAY_FPS: - toggleFPSIsDisplayed(); - break; - case ENABLE_TEXT: - toggleTextIsEnabled(); - break; - case EXIT_VIEWER: - saveStateToFileForAllViewers(); - qApp->closeAllWindows(); - break; - case FULL_SCREEN: - toggleFullScreen(); - break; - case STEREO: - toggleStereoDisplay(); - break; - case ANIMATION: - toggleAnimation(); - break; - case HELP: - help(); - break; - case EDIT_CAMERA: - toggleCameraIsEdited(); - break; - case CAMERA_MODE: - toggleCameraMode(); - displayMessage(cameraIsInRotateMode() - ? tr("Camera in observer mode", "Feedback message") - : tr("Camera in fly mode", "Feedback message")); - break; - - case MOVE_CAMERA_LEFT: - camera()->frame()->translate(camera()->frame()->inverseTransformOf( - Vec(-10.0 * camera()->flySpeed(), 0.0, 0.0))); - update(); - break; - case MOVE_CAMERA_RIGHT: - camera()->frame()->translate(camera()->frame()->inverseTransformOf( - Vec(10.0 * camera()->flySpeed(), 0.0, 0.0))); - update(); - break; - case MOVE_CAMERA_UP: - camera()->frame()->translate(camera()->frame()->inverseTransformOf( - Vec(0.0, 10.0 * camera()->flySpeed(), 0.0))); - update(); - break; - case MOVE_CAMERA_DOWN: - camera()->frame()->translate(camera()->frame()->inverseTransformOf( - Vec(0.0, -10.0 * camera()->flySpeed(), 0.0))); - update(); - break; - - case INCREASE_FLYSPEED: - camera()->setFlySpeed(camera()->flySpeed() * 1.5); - break; - case DECREASE_FLYSPEED: - camera()->setFlySpeed(camera()->flySpeed() / 1.5); - break; - } -} - -/*! Callback method used when the widget size is modified. - -If you overload this method, first call the inherited method. Also called when -the widget is created, before its first display. */ -void QGLViewer::resizeGL(int width, int height) { - QOpenGLWidget::resizeGL(width, height); - glViewport(0, 0, GLint(width), GLint(height)); - camera()->setScreenWidthAndHeight(this->width(), this->height()); -} - -////////////////////////////////////////////////////////////////////////// -// K e y b o a r d s h o r t c u t s // -////////////////////////////////////////////////////////////////////////// - -/*! Defines the shortcut() that triggers a given QGLViewer::KeyboardAction. - -Here are some examples: -\code -// Press 'Q' to exit application -setShortcut(EXIT_VIEWER, Qt::Key_Q); - -// Alt+M toggles camera mode -setShortcut(CAMERA_MODE, Qt::ALT + Qt::Key_M); - -// The DISPLAY_FPS action is disabled -setShortcut(DISPLAY_FPS, 0); -\endcode - -Only one shortcut can be assigned to a given QGLViewer::KeyboardAction (new -bindings replace previous ones). If several KeyboardAction are binded to the -same shortcut, only one of them is active. */ -void QGLViewer::setShortcut(KeyboardAction action, unsigned int key) { - keyboardBinding_[action] = key; -} - -/*! Returns the keyboard shortcut associated to a given -QGLViewer::KeyboardAction. - -Result is an \c unsigned \c int defined using Qt enumerated values, as in \c -Qt::Key_Q or \c Qt::CTRL + Qt::Key_X. Use Qt::MODIFIER_MASK to separate the key -from the state keys. Returns \c 0 if the KeyboardAction is disabled (not -binded). Set using setShortcut(). - -If you want to define keyboard shortcuts for custom actions (say, open a scene -file), overload keyPressEvent() and then setKeyDescription(). - -These shortcuts and their descriptions are automatically included in the help() -window \c Keyboard tab. - -See the keyboard page for details and default -values and the keyboardAndMouse -example for a practical illustration. */ -unsigned int QGLViewer::shortcut(KeyboardAction action) const { - if (keyboardBinding_.contains(action)) - return keyboardBinding_[action]; - else - return 0; -} - -#ifndef DOXYGEN -void QGLViewer::setKeyboardAccelerator(KeyboardAction action, - unsigned int key) { - qWarning("setKeyboardAccelerator is deprecated. Use setShortcut instead."); - setShortcut(action, key); -} - -unsigned int QGLViewer::keyboardAccelerator(KeyboardAction action) const { - qWarning("keyboardAccelerator is deprecated. Use shortcut instead."); - return shortcut(action); -} -#endif - -/////// Key Frames associated keys /////// - -/*! Returns the keyboard key associated to camera Key Frame path \p index. - -Default values are F1..F12 for indexes 1..12. - -addKeyFrameKeyboardModifiers() (resp. playPathKeyboardModifiers()) define the -state key(s) that must be pressed with this key to add a KeyFrame to (resp. to -play) the associated Key Frame path. If you quickly press twice the pathKey(), -the path is reset (resp. deleted). - -Use camera()->keyFrameInterpolator( \p index ) to retrieve the -KeyFrameInterpolator that defines the path. - -If several keys are binded to a given \p index (see setPathKey()), one of them -is returned. Returns \c 0 if no key is associated with this index. - -See also the keyboard page. */ -Qt::Key QGLViewer::pathKey(unsigned int index) const { - for (QMap::ConstIterator it = pathIndex_.begin(), - end = pathIndex_.end(); - it != end; ++it) - if (it.value() == index) - return it.key(); - return Qt::Key(0); -} - -/*! Sets the pathKey() associated with the camera Key Frame path \p index. - -Several keys can be binded to the same \p index. Use a negated \p key value to -delete the binding (the \p index value is then ignored): \code -// Press 'space' to play/pause/add/delete camera path of index 0. -setPathKey(Qt::Key_Space, 0); - -// Remove this binding -setPathKey(-Qt::Key_Space); -\endcode */ -void QGLViewer::setPathKey(int key, unsigned int index) { - Qt::Key k = Qt::Key(abs(key)); - if (key < 0) - pathIndex_.remove(k); - else - pathIndex_[k] = index; -} - -/*! Sets the playPathKeyboardModifiers() value. */ -void QGLViewer::setPlayPathKeyboardModifiers(Qt::KeyboardModifiers modifiers) { - playPathKeyboardModifiers_ = modifiers; -} - -/*! Sets the addKeyFrameKeyboardModifiers() value. */ -void QGLViewer::setAddKeyFrameKeyboardModifiers( - Qt::KeyboardModifiers modifiers) { - addKeyFrameKeyboardModifiers_ = modifiers; -} - -/*! Returns the keyboard modifiers that must be pressed with a pathKey() to add -the current camera position to a KeyFrame path. - -It can be \c Qt::NoModifier, \c Qt::ControlModifier, \c Qt::ShiftModifier, \c -Qt::AltModifier, \c Qt::MetaModifier or a combination of these (using the -bitwise '|' operator). - -Default value is Qt::AltModifier. Defined using -setAddKeyFrameKeyboardModifiers(). - -See also playPathKeyboardModifiers(). */ -Qt::KeyboardModifiers QGLViewer::addKeyFrameKeyboardModifiers() const { - return addKeyFrameKeyboardModifiers_; -} - -/*! Returns the keyboard modifiers that must be pressed with a pathKey() to play -a camera KeyFrame path. - -It can be \c Qt::NoModifier, \c Qt::ControlModifier, \c Qt::ShiftModifier, \c -Qt::AltModifier, \c Qt::MetaModifier or a combination of these (using the -bitwise '|' operator). - -Default value is Qt::NoModifier. Defined using setPlayPathKeyboardModifiers(). - -See also addKeyFrameKeyboardModifiers(). */ -Qt::KeyboardModifiers QGLViewer::playPathKeyboardModifiers() const { - return playPathKeyboardModifiers_; -} - -#ifndef DOXYGEN -// Deprecated methods -Qt::KeyboardModifiers QGLViewer::addKeyFrameStateKey() const { - qWarning("addKeyFrameStateKey has been renamed addKeyFrameKeyboardModifiers"); - return addKeyFrameKeyboardModifiers(); -} - -Qt::KeyboardModifiers QGLViewer::playPathStateKey() const { - qWarning("playPathStateKey has been renamed playPathKeyboardModifiers"); - return playPathKeyboardModifiers(); -} - -void QGLViewer::setAddKeyFrameStateKey(unsigned int buttonState) { - qWarning("setAddKeyFrameStateKey has been renamed " - "setAddKeyFrameKeyboardModifiers"); - setAddKeyFrameKeyboardModifiers(keyboardModifiersFromState(buttonState)); -} - -void QGLViewer::setPlayPathStateKey(unsigned int buttonState) { - qWarning("setPlayPathStateKey has been renamed setPlayPathKeyboardModifiers"); - setPlayPathKeyboardModifiers(keyboardModifiersFromState(buttonState)); -} - -Qt::Key QGLViewer::keyFrameKey(unsigned int index) const { - qWarning("keyFrameKey has been renamed pathKey."); - return pathKey(index); -} - -Qt::KeyboardModifiers QGLViewer::playKeyFramePathStateKey() const { - qWarning( - "playKeyFramePathStateKey has been renamed playPathKeyboardModifiers."); - return playPathKeyboardModifiers(); -} - -void QGLViewer::setKeyFrameKey(unsigned int index, int key) { - qWarning("setKeyFrameKey is deprecated, use setPathKey instead, with swapped " - "parameters."); - setPathKey(key, index); -} - -void QGLViewer::setPlayKeyFramePathStateKey(unsigned int buttonState) { - qWarning("setPlayKeyFramePathStateKey has been renamed " - "setPlayPathKeyboardModifiers."); - setPlayPathKeyboardModifiers(keyboardModifiersFromState(buttonState)); -} -#endif - -//////////////////////////////////////////////////////////////////////////////// -// M o u s e b e h a v i o r s t a t e k e y s // -//////////////////////////////////////////////////////////////////////////////// -#ifndef DOXYGEN -/*! This method has been deprecated since version 2.5.0 - -Associates keyboard modifiers to MouseHandler \p handler. - -The \p modifiers parameter is \c Qt::AltModifier, \c Qt::ShiftModifier, \c -Qt::ControlModifier, \c Qt::MetaModifier or a combination of these using the '|' -bitwise operator. - -\e All the \p handler's associated bindings will then need the specified \p -modifiers key(s) to be activated. - -With this code, -\code -setHandlerKeyboardModifiers(QGLViewer::CAMERA, Qt::AltModifier); -setHandlerKeyboardModifiers(QGLViewer::FRAME, Qt::NoModifier); -\endcode -you will have to press the \c Alt key while pressing mouse buttons in order to -move the camera(), while no key will be needed to move the manipulatedFrame(). - -This method has a very basic implementation: every action binded to \p handler -has its keyboard modifier replaced by \p modifiers. If \p handler had some -actions binded to different modifiers, these settings will be lost. You should -hence consider using setMouseBinding() for finer tuning. - -The default binding associates \c Qt::ControlModifier to all the -QGLViewer::FRAME actions and \c Qt::NoModifier to all QGLViewer::CAMERA actions. -See mouse page for details. - -\attention This method calls setMouseBinding(), which ensures that only one -action is binded to a given modifiers. If you want to \e swap the -QGLViewer::CAMERA and QGLViewer::FRAME keyboard modifiers, you have to use a -temporary dummy modifier (as if you were swapping two variables) or else the -first call will overwrite the previous settings: \code -// Associate FRAME with Alt (temporary value) -setHandlerKeyboardModifiers(QGLViewer::FRAME, Qt::AltModifier); -// Control is associated with CAMERA -setHandlerKeyboardModifiers(QGLViewer::CAMERA, Qt::ControlModifier); -// And finally, FRAME can be associated with NoModifier -setHandlerKeyboardModifiers(QGLViewer::FRAME, Qt::NoModifier); -\endcode */ -void QGLViewer::setHandlerKeyboardModifiers(MouseHandler handler, - Qt::KeyboardModifiers modifiers) { - qWarning("setHandlerKeyboardModifiers is deprecated, call setMouseBinding() " - "instead"); - - QMap newMouseBinding; - QMap newWheelBinding; - QMap newClickBinding_; - - QMap::Iterator mit; - QMap::Iterator wit; - - // First copy unchanged bindings. - for (mit = mouseBinding_.begin(); mit != mouseBinding_.end(); ++mit) - if ((mit.value().handler != handler) || - (mit.value().action == ZOOM_ON_REGION)) - newMouseBinding[mit.key()] = mit.value(); - - for (wit = wheelBinding_.begin(); wit != wheelBinding_.end(); ++wit) - if (wit.value().handler != handler) - newWheelBinding[wit.key()] = wit.value(); - - // Then, add modified bindings, that can overwrite the previous ones. - for (mit = mouseBinding_.begin(); mit != mouseBinding_.end(); ++mit) - if ((mit.value().handler == handler) && - (mit.value().action != ZOOM_ON_REGION)) { - MouseBindingPrivate mbp(modifiers, mit.key().button, mit.key().key); - newMouseBinding[mbp] = mit.value(); - } - - for (wit = wheelBinding_.begin(); wit != wheelBinding_.end(); ++wit) - if (wit.value().handler == handler) { - WheelBindingPrivate wbp(modifiers, wit.key().key); - newWheelBinding[wbp] = wit.value(); - } - - // Same for button bindings - for (QMap::ConstIterator - cb = clickBinding_.begin(), - end = clickBinding_.end(); - cb != end; ++cb) - if (((handler == CAMERA) && - ((cb.value() == CENTER_SCENE) || (cb.value() == ALIGN_CAMERA))) || - ((handler == FRAME) && - ((cb.value() == CENTER_FRAME) || (cb.value() == ALIGN_FRAME)))) { - ClickBindingPrivate cbp(modifiers, cb.key().button, cb.key().doubleClick, - cb.key().buttonsBefore, cb.key().key); - newClickBinding_[cbp] = cb.value(); - } else - newClickBinding_[cb.key()] = cb.value(); - - mouseBinding_ = newMouseBinding; - wheelBinding_ = newWheelBinding; - clickBinding_ = newClickBinding_; -} - -void QGLViewer::setHandlerStateKey(MouseHandler handler, - unsigned int buttonState) { - qWarning("setHandlerStateKey has been renamed setHandlerKeyboardModifiers"); - setHandlerKeyboardModifiers(handler, keyboardModifiersFromState(buttonState)); -} - -void QGLViewer::setMouseStateKey(MouseHandler handler, - unsigned int buttonState) { - qWarning("setMouseStateKey has been renamed setHandlerKeyboardModifiers."); - setHandlerKeyboardModifiers(handler, keyboardModifiersFromState(buttonState)); -} - -/*! This method is deprecated since version 2.5.0 - - Use setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, MouseHandler, - MouseAction, bool) instead. -*/ -void QGLViewer::setMouseBinding(unsigned int state, MouseHandler handler, - MouseAction action, bool withConstraint) { - qWarning("setMouseBinding(int state, MouseHandler...) is deprecated. Use the " - "modifier/button equivalent"); - setMouseBinding(keyboardModifiersFromState(state), - mouseButtonFromState(state), handler, action, withConstraint); -} -#endif - -/*! Defines a MouseAction binding. - - Same as calling setMouseBinding(Qt::Key, Qt::KeyboardModifiers, - Qt::MouseButton, MouseHandler, MouseAction, bool), with a key value of - Qt::Key(0) (i.e. no regular extra key needs to be pressed to perform this - action). */ -void QGLViewer::setMouseBinding(Qt::KeyboardModifiers modifiers, - Qt::MouseButton button, MouseHandler handler, - MouseAction action, bool withConstraint) { - setMouseBinding(Qt::Key(0), modifiers, button, handler, action, - withConstraint); -} - -/*! Associates a MouseAction to any mouse \p button, while keyboard \p modifiers -and \p key are pressed. The receiver of the mouse events is a MouseHandler -(QGLViewer::CAMERA or QGLViewer::FRAME). - -The parameters should read: when the mouse \p button is pressed, while the -keyboard \p modifiers and \p key are down, activate \p action on \p handler. Use -Qt::NoModifier to indicate that no modifier key is needed, and a \p key value of -0 if no regular key has to be pressed (or simply use -setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButton, MouseHandler, -MouseAction, bool)). - -Use the '|' operator to combine modifiers: -\code -// The R key combined with the Left mouse button rotates the camera in the -screen plane. setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, CAMERA, -SCREEN_ROTATE); - -// Alt + Shift and Left button rotates the manipulatedFrame(). -setMouseBinding(Qt::AltModifier | Qt::ShiftModifier, Qt::LeftButton, FRAME, -ROTATE); \endcode - -If \p withConstraint is \c true (default), the possible -qglviewer::Frame::constraint() of the associated Frame will be enforced during -motion. - -The list of all possible MouseAction, some binding examples and default bindings -are provided in the mouse page. - -See the keyboardAndMouse example -for an illustration. - -If no mouse button is specified, the binding is ignored. If an action was -previously associated with this keyboard and button combination, it is silently -overwritten (call mouseAction() before to check). - -To remove a specific mouse binding, use \p NO_MOUSE_ACTION as the \p action. - -See also setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, ClickAction, -bool, int), setWheelBinding() and clearMouseBindings(). */ -void QGLViewer::setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, - Qt::MouseButton button, MouseHandler handler, - MouseAction action, bool withConstraint) { - if ((handler == FRAME) && - ((action == MOVE_FORWARD) || (action == MOVE_BACKWARD) || - (action == ROLL) || (action == LOOK_AROUND) || - (action == ZOOM_ON_REGION))) { - qWarning("Cannot bind %s to FRAME", - mouseActionString(action).toLatin1().constData()); - return; - } - - if (button == Qt::NoButton) { - qWarning("No mouse button specified in setMouseBinding"); - return; - } - - MouseActionPrivate map; - map.handler = handler; - map.action = action; - map.withConstraint = withConstraint; - - MouseBindingPrivate mbp(modifiers, button, key); - if (action == NO_MOUSE_ACTION) - mouseBinding_.remove(mbp); - else - mouseBinding_.insert(mbp, map); - - ClickBindingPrivate cbp(modifiers, button, false, Qt::NoButton, key); - clickBinding_.remove(cbp); -} - -#ifndef DOXYGEN -/*! This method is deprecated since version 2.5.0 - - Use setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, MouseHandler, - MouseAction, bool) instead. -*/ -void QGLViewer::setMouseBinding(unsigned int state, ClickAction action, - bool doubleClick, - Qt::MouseButtons buttonsBefore) { - qWarning("setMouseBinding(int state, ClickAction...) is deprecated. Use the " - "modifier/button equivalent"); - setMouseBinding(keyboardModifiersFromState(state), - mouseButtonFromState(state), action, doubleClick, - buttonsBefore); -} -#endif - -/*! Defines a ClickAction binding. - - Same as calling setMouseBinding(Qt::Key, Qt::KeyboardModifiers, - Qt::MouseButton, ClickAction, bool, Qt::MouseButtons), with a key value of - Qt::Key(0) (i.e. no regular key needs to be pressed to activate this action). - */ -void QGLViewer::setMouseBinding(Qt::KeyboardModifiers modifiers, - Qt::MouseButton button, ClickAction action, - bool doubleClick, - Qt::MouseButtons buttonsBefore) { - setMouseBinding(Qt::Key(0), modifiers, button, action, doubleClick, - buttonsBefore); -} - -/*! Associates a ClickAction to a button and keyboard key and modifier(s) -combination. - -The parameters should read: when \p button is pressed, while the \p modifiers -and \p key keys are down, and possibly as a \p doubleClick, then perform \p -action. Use Qt::NoModifier to indicate that no modifier key is needed, and a \p -key value of 0 if no regular key has to be pressed (or simply use -setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButton, ClickAction, bool, -Qt::MouseButtons)). - -If \p buttonsBefore is specified (valid only when \p doubleClick is \c true), -then this (or these) other mouse button(s) has (have) to be pressed \e before -the double click occurs in order to execute \p action. - -The list of all possible ClickAction, some binding examples and default bindings -are listed in the mouse page. See also the -setMouseBinding() documentation. - -See the keyboardAndMouse example -for an illustration. - -The binding is ignored if Qt::NoButton is specified as \p buttons. - -See also setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, MouseHandler, -MouseAction, bool), setWheelBinding() and clearMouseBindings(). -*/ -void QGLViewer::setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, - Qt::MouseButton button, ClickAction action, - bool doubleClick, - Qt::MouseButtons buttonsBefore) { - if ((buttonsBefore != Qt::NoButton) && !doubleClick) { - qWarning("Buttons before is only meaningful when doubleClick is true in " - "setMouseBinding()."); - return; - } - - if (button == Qt::NoButton) { - qWarning("No mouse button specified in setMouseBinding"); - return; - } - - ClickBindingPrivate cbp(modifiers, button, doubleClick, buttonsBefore, key); - - // #CONNECTION performClickAction comment on NO_CLICK_ACTION - if (action == NO_CLICK_ACTION) - clickBinding_.remove(cbp); - else - clickBinding_.insert(cbp, action); - - if ((!doubleClick) && (buttonsBefore == Qt::NoButton)) { - MouseBindingPrivate mbp(modifiers, button, key); - mouseBinding_.remove(mbp); - } -} - -/*! Defines a mouse wheel binding. - - Same as calling setWheelBinding(Qt::Key, Qt::KeyboardModifiers, MouseHandler, - MouseAction, bool), with a key value of Qt::Key(0) (i.e. no regular key needs - to be pressed to activate this action). */ -void QGLViewer::setWheelBinding(Qt::KeyboardModifiers modifiers, - MouseHandler handler, MouseAction action, - bool withConstraint) { - setWheelBinding(Qt::Key(0), modifiers, handler, action, withConstraint); -} - -/*! Associates a MouseAction and a MouseHandler to a mouse wheel event. - -This method is very similar to setMouseBinding(), but specific to the wheel. - -In the current implementation only QGLViewer::ZOOM can be associated with -QGLViewer::FRAME, while QGLViewer::CAMERA can receive QGLViewer::ZOOM and -QGLViewer::MOVE_FORWARD. - -The difference between QGLViewer::ZOOM and QGLViewer::MOVE_FORWARD is that -QGLViewer::ZOOM speed depends on the distance to the object, while -QGLViewer::MOVE_FORWARD moves at a constant speed defined by -qglviewer::Camera::flySpeed(). */ -void QGLViewer::setWheelBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, - MouseHandler handler, MouseAction action, - bool withConstraint) { - //#CONNECTION# ManipulatedFrame::wheelEvent and - // ManipulatedCameraFrame::wheelEvent switches - if ((action != ZOOM) && (action != MOVE_FORWARD) && - (action != MOVE_BACKWARD) && (action != NO_MOUSE_ACTION)) { - qWarning("Cannot bind %s to wheel", - mouseActionString(action).toLatin1().constData()); - return; - } - - if ((handler == FRAME) && (action != ZOOM) && (action != NO_MOUSE_ACTION)) { - qWarning("Cannot bind %s to FRAME wheel", - mouseActionString(action).toLatin1().constData()); - return; - } - - MouseActionPrivate map; - map.handler = handler; - map.action = action; - map.withConstraint = withConstraint; - - WheelBindingPrivate wbp(modifiers, key); - if (action == NO_MOUSE_ACTION) - wheelBinding_.remove(wbp); - else - wheelBinding_[wbp] = map; -} - -/*! Clears all the default mouse bindings. - -After this call, you will have to use setMouseBinding() and setWheelBinding() to -restore the mouse bindings you are interested in. -*/ -void QGLViewer::clearMouseBindings() { - mouseBinding_.clear(); - clickBinding_.clear(); - wheelBinding_.clear(); -} - -/*! Clears all the default keyboard shortcuts. - -After this call, you will have to use setShortcut() to define your own keyboard -shortcuts. -*/ -void QGLViewer::clearShortcuts() { - keyboardBinding_.clear(); - pathIndex_.clear(); -} - -/*! This method is deprecated since version 2.5.0 - - Use mouseAction(Qt::Key, Qt::KeyboardModifiers, Qt::MouseButtons) instead. -*/ -QGLViewer::MouseAction QGLViewer::mouseAction(unsigned int state) const { - qWarning("mouseAction(int state,...) is deprecated. Use the modifier/button " - "equivalent"); - return mouseAction(Qt::Key(0), keyboardModifiersFromState(state), - mouseButtonFromState(state)); -} - -/*! Returns the MouseAction the will be triggered when the mouse \p button is -pressed, while the keyboard \p modifiers and \p key are pressed. - -Returns QGLViewer::NO_MOUSE_ACTION if no action is associated with this -combination. Use 0 for \p key to indicate that no regular key needs to be -pressed. - -For instance, to know which motion corresponds to Alt+LeftButton, do: -\code -QGLViewer::MouseAction ma = mouseAction(0, Qt::AltModifier, Qt::LeftButton); -if (ma != QGLViewer::NO_MOUSE_ACTION) ... -\endcode - -Use mouseHandler() to know which object (QGLViewer::CAMERA or QGLViewer::FRAME) -will execute this action. */ -QGLViewer::MouseAction QGLViewer::mouseAction(Qt::Key key, - Qt::KeyboardModifiers modifiers, - Qt::MouseButton button) const { - MouseBindingPrivate mbp(modifiers, button, key); - if (mouseBinding_.contains(mbp)) - return mouseBinding_[mbp].action; - else - return NO_MOUSE_ACTION; -} - -/*! This method is deprecated since version 2.5.0 - - Use mouseHanler(Qt::Key, Qt::KeyboardModifiers, Qt::MouseButtons) instead. -*/ -int QGLViewer::mouseHandler(unsigned int state) const { - qWarning("mouseHandler(int state,...) is deprecated. Use the modifier/button " - "equivalent"); - return mouseHandler(Qt::Key(0), keyboardModifiersFromState(state), - mouseButtonFromState(state)); -} - -/*! Returns the MouseHandler which will be activated when the mouse \p button is -pressed, while the \p modifiers and \p key are pressed. - -If no action is associated with this combination, returns \c -1. Use 0 for \p -key and Qt::NoModifier for \p modifiers to represent the lack of a key press. - -For instance, to know which handler receives the Alt+LeftButton, do: -\code -int mh = mouseHandler(0, Qt::AltModifier, Qt::LeftButton); -if (mh == QGLViewer::CAMERA) ... -\endcode - -Use mouseAction() to know which action (see the MouseAction enum) will be -performed on this handler. */ -int QGLViewer::mouseHandler(Qt::Key key, Qt::KeyboardModifiers modifiers, - Qt::MouseButton button) const { - MouseBindingPrivate mbp(modifiers, button, key); - if (mouseBinding_.contains(mbp)) - return mouseBinding_[mbp].handler; - else - return -1; -} - -#ifndef DOXYGEN -/*! This method is deprecated since version 2.5.0 - - Use mouseButtons() and keyboardModifiers() instead. -*/ -int QGLViewer::mouseButtonState(MouseHandler handler, MouseAction action, - bool withConstraint) const { - qWarning("mouseButtonState() is deprecated. Use mouseButtons() and " - "keyboardModifiers() instead"); - for (QMap::ConstIterator - it = mouseBinding_.begin(), - end = mouseBinding_.end(); - it != end; ++it) - if ((it.value().handler == handler) && (it.value().action == action) && - (it.value().withConstraint == withConstraint)) - return (int)it.key().modifiers | (int)it.key().button; - - return Qt::NoButton; -} -#endif - -/*! Returns the keyboard state that triggers \p action on \p handler \p -withConstraint using the mouse wheel. - -If such a binding exists, results are stored in the \p key and \p modifiers -parameters. If the MouseAction \p action is not bound, \p key is set to the -illegal -1 value. If several keyboard states trigger the MouseAction, one of -them is returned. - -See also setMouseBinding(), getClickActionBinding() and getMouseActionBinding(). -*/ -void QGLViewer::getWheelActionBinding(MouseHandler handler, MouseAction action, - bool withConstraint, Qt::Key &key, - Qt::KeyboardModifiers &modifiers) const { - for (QMap::ConstIterator - it = wheelBinding_.begin(), - end = wheelBinding_.end(); - it != end; ++it) - if ((it.value().handler == handler) && (it.value().action == action) && - (it.value().withConstraint == withConstraint)) { - key = it.key().key; - modifiers = it.key().modifiers; - return; - } - - key = Qt::Key(-1); - modifiers = Qt::NoModifier; -} - -/*! Returns the mouse and keyboard state that triggers \p action on \p handler -\p withConstraint. - -If such a binding exists, results are stored in the \p key, \p modifiers and \p -button parameters. If the MouseAction \p action is not bound, \p button is set -to \c Qt::NoButton. If several mouse and keyboard states trigger the -MouseAction, one of them is returned. - -See also setMouseBinding(), getClickActionBinding() and getWheelActionBinding(). -*/ -void QGLViewer::getMouseActionBinding(MouseHandler handler, MouseAction action, - bool withConstraint, Qt::Key &key, - Qt::KeyboardModifiers &modifiers, - Qt::MouseButton &button) const { - for (QMap::ConstIterator - it = mouseBinding_.begin(), - end = mouseBinding_.end(); - it != end; ++it) { - if ((it.value().handler == handler) && (it.value().action == action) && - (it.value().withConstraint == withConstraint)) { - key = it.key().key; - modifiers = it.key().modifiers; - button = it.key().button; - return; - } - } - - key = Qt::Key(0); - modifiers = Qt::NoModifier; - button = Qt::NoButton; -} - -/*! Returns the MouseAction (if any) that is performed when using the wheel, -when the \p modifiers and \p key keyboard keys are pressed. - -Returns NO_MOUSE_ACTION if no such binding has been defined using -setWheelBinding(). - -Same as mouseAction(), but for the wheel action. See also wheelHandler(). -*/ -QGLViewer::MouseAction -QGLViewer::wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) const { - WheelBindingPrivate wbp(modifiers, key); - if (wheelBinding_.contains(wbp)) - return wheelBinding_[wbp].action; - else - return NO_MOUSE_ACTION; -} - -/*! Returns the MouseHandler (if any) that receives wheel events when the \p - modifiers and \p key keyboard keys are pressed. - - Returns -1 if no no such binding has been defined using setWheelBinding(). See - also wheelAction(). -*/ -int QGLViewer::wheelHandler(Qt::Key key, - Qt::KeyboardModifiers modifiers) const { - WheelBindingPrivate wbp(modifiers, key); - if (wheelBinding_.contains(wbp)) - return wheelBinding_[wbp].handler; - else - return -1; -} - -/*! Same as mouseAction(), but for the ClickAction set using setMouseBinding(). - -Returns NO_CLICK_ACTION if no click action is associated with this keyboard and -mouse buttons combination. */ -QGLViewer::ClickAction -QGLViewer::clickAction(Qt::Key key, Qt::KeyboardModifiers modifiers, - Qt::MouseButton button, bool doubleClick, - Qt::MouseButtons buttonsBefore) const { - ClickBindingPrivate cbp(modifiers, button, doubleClick, buttonsBefore, key); - if (clickBinding_.contains(cbp)) - return clickBinding_[cbp]; - else - return NO_CLICK_ACTION; -} - -#ifndef DOXYGEN -/*! This method is deprecated since version 2.5.0 - - Use wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) instead. */ -QGLViewer::MouseAction -QGLViewer::wheelAction(Qt::KeyboardModifiers modifiers) const { - qWarning("wheelAction() is deprecated. Use the new wheelAction() method with " - "a key parameter instead"); - return wheelAction(Qt::Key(0), modifiers); -} - -/*! This method is deprecated since version 2.5.0 - - Use wheelHandler(Qt::Key key, Qt::KeyboardModifiers modifiers) instead. */ -int QGLViewer::wheelHandler(Qt::KeyboardModifiers modifiers) const { - qWarning("wheelHandler() is deprecated. Use the new wheelHandler() method " - "with a key parameter instead"); - return wheelHandler(Qt::Key(0), modifiers); -} - -/*! This method is deprecated since version 2.5.0 - - Use wheelAction() and wheelHandler() instead. */ -unsigned int QGLViewer::wheelButtonState(MouseHandler handler, - MouseAction action, - bool withConstraint) const { - qWarning("wheelButtonState() is deprecated. Use the wheelAction() and " - "wheelHandler() instead"); - for (QMap::ConstIterator - it = wheelBinding_.begin(), - end = wheelBinding_.end(); - it != end; ++it) - if ((it.value().handler == handler) && (it.value().action == action) && - (it.value().withConstraint == withConstraint)) - return it.key().key + it.key().modifiers; - - return -1; -} - -/*! This method is deprecated since version 2.5.0 - - Use clickAction(Qt::KeyboardModifiers, Qt::MouseButtons, bool, - Qt::MouseButtons) instead. -*/ -QGLViewer::ClickAction -QGLViewer::clickAction(unsigned int state, bool doubleClick, - Qt::MouseButtons buttonsBefore) const { - qWarning("clickAction(int state,...) is deprecated. Use the modifier/button " - "equivalent"); - return clickAction(Qt::Key(0), keyboardModifiersFromState(state), - mouseButtonFromState(state), doubleClick, buttonsBefore); -} - -/*! This method is deprecated since version 2.5.0 - - Use getClickActionState(ClickAction, Qt::Key, Qt::KeyboardModifiers, - Qt::MouseButton, bool, Qt::MouseButtons) instead. -*/ -void QGLViewer::getClickButtonState(ClickAction action, unsigned int &state, - bool &doubleClick, - Qt::MouseButtons &buttonsBefore) const { - qWarning("getClickButtonState(int state,...) is deprecated. Use the " - "modifier/button equivalent"); - Qt::KeyboardModifiers modifiers; - Qt::MouseButton button; - Qt::Key key; - getClickActionBinding(action, key, modifiers, button, doubleClick, - buttonsBefore); - state = (unsigned int)modifiers | (unsigned int)button | (unsigned int)key; -} -#endif - -/*! Returns the mouse and keyboard state that triggers \p action. - -If such a binding exists, results are stored in the \p key, \p modifiers, \p -button, \p doubleClick and \p buttonsBefore parameters. If the ClickAction \p -action is not bound, \p button is set to \c Qt::NoButton. If several mouse -buttons trigger in the ClickAction, one of them is returned. - -See also setMouseBinding(), getMouseActionBinding() and getWheelActionBinding(). -*/ -void QGLViewer::getClickActionBinding(ClickAction action, Qt::Key &key, - Qt::KeyboardModifiers &modifiers, - Qt::MouseButton &button, - bool &doubleClick, - Qt::MouseButtons &buttonsBefore) const { - for (QMap::ConstIterator - it = clickBinding_.begin(), - end = clickBinding_.end(); - it != end; ++it) - if (it.value() == action) { - modifiers = it.key().modifiers; - button = it.key().button; - doubleClick = it.key().doubleClick; - buttonsBefore = it.key().buttonsBefore; - key = it.key().key; - return; - } - - modifiers = Qt::NoModifier; - button = Qt::NoButton; - doubleClick = false; - buttonsBefore = Qt::NoButton; - key = Qt::Key(0); -} - -/*! This function should be used in conjunction with toggleCameraMode(). It -returns \c true when at least one mouse button is binded to the \c ROTATE -mouseAction. This is crude way of determining which "mode" the camera is in. */ -bool QGLViewer::cameraIsInRotateMode() const { - //#CONNECTION# used in toggleCameraMode() and keyboardString() - Qt::Key key; - Qt::KeyboardModifiers modifiers; - Qt::MouseButton button; - getMouseActionBinding(CAMERA, ROTATE, true /*constraint*/, key, modifiers, - button); - return button != Qt::NoButton; -} - -/*! Swaps between two predefined camera mouse bindings. - -The first mode makes the camera observe the scene while revolving around the -qglviewer::Camera::pivotPoint(). The second mode is designed for walkthrough -applications and simulates a flying camera. - -Practically, the three mouse buttons are respectively binded to: -\arg In rotate mode: QGLViewer::ROTATE, QGLViewer::ZOOM, QGLViewer::TRANSLATE. -\arg In fly mode: QGLViewer::MOVE_FORWARD, QGLViewer::LOOK_AROUND, -QGLViewer::MOVE_BACKWARD. - -The current mode is determined by checking if a mouse button is binded to -QGLViewer::ROTATE for the QGLViewer::CAMERA. The state key that was previously -used to move the camera is preserved. */ -void QGLViewer::toggleCameraMode() { - Qt::Key key; - Qt::KeyboardModifiers modifiers; - Qt::MouseButton button; - getMouseActionBinding(CAMERA, ROTATE, true /*constraint*/, key, modifiers, - button); - bool rotateMode = button != Qt::NoButton; - - if (!rotateMode) { - getMouseActionBinding(CAMERA, MOVE_FORWARD, true /*constraint*/, key, - modifiers, button); - } - - //#CONNECTION# setDefaultMouseBindings() - if (rotateMode) { - camera()->frame()->updateSceneUpVector(); - camera()->frame()->stopSpinning(); - - setMouseBinding(modifiers, Qt::LeftButton, CAMERA, MOVE_FORWARD); - setMouseBinding(modifiers, Qt::MidButton, CAMERA, LOOK_AROUND); - setMouseBinding(modifiers, Qt::RightButton, CAMERA, MOVE_BACKWARD); - - setMouseBinding(Qt::Key_R, modifiers, Qt::LeftButton, CAMERA, ROLL); - - setMouseBinding(Qt::NoModifier, Qt::LeftButton, NO_CLICK_ACTION, true); - setMouseBinding(Qt::NoModifier, Qt::MidButton, NO_CLICK_ACTION, true); - setMouseBinding(Qt::NoModifier, Qt::RightButton, NO_CLICK_ACTION, true); - - setWheelBinding(modifiers, CAMERA, MOVE_FORWARD); - } else { - // Should stop flyTimer. But unlikely and not easy. - setMouseBinding(modifiers, Qt::LeftButton, CAMERA, ROTATE); - setMouseBinding(modifiers, Qt::MidButton, CAMERA, ZOOM); - setMouseBinding(modifiers, Qt::RightButton, CAMERA, TRANSLATE); - - setMouseBinding(Qt::Key_R, modifiers, Qt::LeftButton, CAMERA, - SCREEN_ROTATE); - - setMouseBinding(Qt::NoModifier, Qt::LeftButton, ALIGN_CAMERA, true); - setMouseBinding(Qt::NoModifier, Qt::MidButton, SHOW_ENTIRE_SCENE, true); - setMouseBinding(Qt::NoModifier, Qt::RightButton, CENTER_SCENE, true); - - setWheelBinding(modifiers, CAMERA, ZOOM); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// M a n i p u l a t e d f r a m e s // -//////////////////////////////////////////////////////////////////////////////// - -/*! Sets the viewer's manipulatedFrame(). - -Several objects can be manipulated simultaneously, as is done the multiSelect example. - -Defining the \e own viewer's camera()->frame() as the manipulatedFrame() is -possible and will result in a classical camera manipulation. See the luxo example for an illustration. - -Note that a qglviewer::ManipulatedCameraFrame can be set as the -manipulatedFrame(): it is possible to manipulate the camera of a first viewer in -a second viewer. */ -void QGLViewer::setManipulatedFrame(ManipulatedFrame *frame) { - if (manipulatedFrame()) { - manipulatedFrame()->stopSpinning(); - - if (manipulatedFrame() != camera()->frame()) { - disconnect(manipulatedFrame(), SIGNAL(manipulated()), this, - SLOT(update())); - disconnect(manipulatedFrame(), SIGNAL(spun()), this, SLOT(update())); - } - } - - manipulatedFrame_ = frame; - - manipulatedFrameIsACamera_ = - ((manipulatedFrame() != camera()->frame()) && - (dynamic_cast(manipulatedFrame()) != NULL)); - - if (manipulatedFrame()) { - // Prevent multiple connections, that would result in useless display - // updates - if (manipulatedFrame() != camera()->frame()) { - connect(manipulatedFrame(), SIGNAL(manipulated()), SLOT(update())); - connect(manipulatedFrame(), SIGNAL(spun()), SLOT(update())); - } - } -} - -#ifndef DOXYGEN -//////////////////////////////////////////////////////////////////////////////// -// V i s u a l H i n t s // -//////////////////////////////////////////////////////////////////////////////// -/*! Draws viewer related visual hints. - -Displays the new qglviewer::Camera::pivotPoint() when it is changed. See the mouse page for details. Also draws a line between -qglviewer::Camera::pivotPoint() and mouse cursor when the camera is rotated -around the camera Z axis. - -See also setVisualHintsMask() and resetVisualHints(). The hint color is -foregroundColor(). - -\note These methods may become more interesting one day. The current design is -too limited and should be improved when other visual hints must be drawn. - -Limitation : One needs to have access to visualHint_ to overload this method. - -Removed from the documentation for this reason. */ -void QGLViewer::drawVisualHints() { - // G r i d - rendering_program.bind(); - vaos[GRID].bind(); - QMatrix4x4 mvpMatrix; - double mat[16]; - camera()->getModelViewProjectionMatrix(mat); - for(int i=0; i < 16; i++) - { - mvpMatrix.data()[i] = (float)mat[i]; - } - QMatrix4x4 mvMatrix; - for(int i=0; i < 16; i++) - { - mvMatrix.data()[i] = camera()->orientation().inverse().matrix()[i]; - } - rendering_program.setUniformValue("mvp_matrix", mvpMatrix); - rendering_program.setUniformValue("color", QColor(Qt::lightGray)); - glDrawArrays(GL_LINES, 0, static_cast(grid_size)); - vaos[GRID].release(); - rendering_program.release(); - - rendering_program_light.bind(); - vaos[GRID_AXIS].bind(); - - rendering_program_light.setUniformValue("mvp_matrix", mvpMatrix); - rendering_program_light.setUniformValue("mv_matrix", mvMatrix); - glDrawArrays(GL_TRIANGLES, 0, static_cast(g_axis_size/9)); - vaos[GRID_AXIS].release(); - - //A x i s - qglviewer::Camera::Type camera_type = camera()->type(); - camera()->setType(qglviewer::Camera::ORTHOGRAPHIC); - for(int i=0; i < 16; i++) - { - mvMatrix.data()[i] = camera()->orientation().inverse().matrix()[i]; - } - mvpMatrix.setToIdentity(); - mvpMatrix.ortho(-1,1,-1,1,-1,1); - mvpMatrix = mvpMatrix*mvMatrix; - rendering_program_light.setUniformValue("mvp_matrix", mvpMatrix); - rendering_program_light.setUniformValue("mv_matrix", mvMatrix); - camera()->setType(camera_type); - vaos[AXIS].bind(); - int viewport[4]; - int scissor[4]; - - // The viewport and the scissor are changed to fit the upper right - // corner. Original values are saved. - glGetIntegerv(GL_VIEWPORT, viewport); - glGetIntegerv(GL_SCISSOR_BOX, scissor); - - // Axis viewport size, in pixels - int size = 100; - glViewport(width()*devicePixelRatio()-size, height()*devicePixelRatio()-size, size, size); - glScissor (width()*devicePixelRatio()-size, height()*devicePixelRatio()-size, size, size); - glDrawArrays(GL_TRIANGLES, 0, static_cast(axis_size / 9)); - // The viewport and the scissor are restored. - glScissor(scissor[0],scissor[1],scissor[2],scissor[3]); - glViewport(viewport[0],viewport[1],viewport[2],viewport[3]); - vaos[AXIS].release(); - rendering_program_light.release(); - - //P i v o t - P o i n t - if (visualHint_ & 1) - { - std::vector vertices; - for(int i=0; i< 4; ++i) - { - float x = (pow(-1, i)*(1-i/2)); - float y = (pow(-1, i)*(i/2)); - vertices.push_back(x); - vertices.push_back(y); - vertices.push_back(0); - } - rendering_program.bind(); - vaos[PIVOT_POINT].bind(); - vbos[Pivot_point].bind(); - vbos[Pivot_point].allocate(vertices.data(),static_cast(vertices.size()*sizeof(float))); - rendering_program.enableAttributeArray("vertex"); - rendering_program.setAttributeBuffer("vertex",GL_FLOAT,0,3); - vbos[Pivot_point].release(); - mvpMatrix.setToIdentity(); - mvpMatrix.ortho(-1,1,-1,1,-1,1); - size=30*devicePixelRatio(); - rendering_program.setUniformValue("mvp_matrix", mvpMatrix); - glViewport((camera()->projectedCoordinatesOf(camera()->pivotPoint()).x-size/2)*devicePixelRatio(), (height() - camera()->projectedCoordinatesOf(camera()->pivotPoint()).y-size/2)*devicePixelRatio(), size, size); - glScissor ((camera()->projectedCoordinatesOf(camera()->pivotPoint()).x-size/2)*devicePixelRatio(), (height() - camera()->projectedCoordinatesOf(camera()->pivotPoint()).y-size/2)*devicePixelRatio(), size, size); - rendering_program.setUniformValue("color", QColor(Qt::black)); - glLineWidth(3.0); - glDrawArrays(GL_LINES, 0, static_cast(4)); - rendering_program.setUniformValue("color", QColor(Qt::white)); - glLineWidth(3.0); - glDrawArrays(GL_LINES, 0, static_cast(4)); - glLineWidth(1.0); - // The viewport and the scissor are restored. - glScissor(scissor[0],scissor[1],scissor[2],scissor[3]); - glViewport(viewport[0],viewport[1],viewport[2],viewport[3]); - vaos[PIVOT_POINT].release(); - rendering_program.release(); - } - -} - -/*! Defines the mask that will be used to drawVisualHints(). The only available -mask is currently 1, corresponding to the display of the -qglviewer::Camera::pivotPoint(). resetVisualHints() is automatically called -after \p delay milliseconds (default is 2 seconds). */ -void QGLViewer::setVisualHintsMask(int mask, int delay) { - visualHint_ = visualHint_ | mask; - QTimer::singleShot(delay, this, SLOT(resetVisualHints())); -} - -/*! Reset the mask used by drawVisualHints(). Called by setVisualHintsMask() - * after 2 seconds to reset the display. */ -void QGLViewer::resetVisualHints() { visualHint_ = 0; } -#endif - -//////////////////////////////////////////////////////////////////////////////// -// A x i s a n d G r i d d i s p l a y l i s t s // -//////////////////////////////////////////////////////////////////////////////// - -/*! Draws a 3D arrow between the 3D point \p from and the 3D point \p to. -\p data is filled with the three components of a point, then its normal, and then its color, which makes it filled like this: -[P1.x-P1.y-P1.z-N1.x-N1.y-N1.z-C1.r-C1.g-C1.b|P2.x-P2.y-P2.z-N2.x-N2.y-N2.z-C2.r-C2.g-C2.b|...] -*/ -void QGLViewer::drawArrow(double r,double R, int prec, qglviewer::Vec from, - qglviewer::Vec to, qglviewer::Vec color, - std::vector &data) { - qglviewer::Vec temp = to-from; - QVector3D dir = QVector3D(temp.x, temp.y, temp.z); - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(from.x, from.y, from.z); - mat.scale(dir.length()); - dir.normalize(); - float angle = 0.0; - if(std::sqrt((dir.x()*dir.x()+dir.y()*dir.y())) > 1) - angle = 90.0f; - else - angle =acos(dir.y()/std::sqrt(dir.x()*dir.x()+dir.y()*dir.y()+dir.z()*dir.z()))*180.0/M_PI; - - QVector3D axis; - axis = QVector3D(dir.z(), 0, -dir.x()); - mat.rotate(angle, axis); - - //Head - const float Rf = static_cast(R); - for(int d = 0; d<360; d+= 360/prec) - { - float D = (float) (d * M_PI / 180.); - float a = (float) std::atan(Rf / 0.33); - QVector4D p(0., 1., 0, 1.); - QVector4D n(Rf*sin(D), sin(a), Rf*cos(D), 1.); - QVector4D pR = mat*p; - QVector4D nR = mat*n; - - //point A1 - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back((float)color.x); - data.push_back((float)color.y); - data.push_back((float)color.z); - - //point B1 - p = QVector4D(Rf*sin(D), 0.66f, Rf* cos(D), 1.f); - n = QVector4D(sin(D), sin(a), cos(D), 1.); - pR = mat*p; - nR = mat*n; - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back((float)color.x); - data.push_back((float)color.y); - data.push_back((float)color.z); - //point C1 - D = (d+360/prec)*M_PI/180.0; - p = QVector4D(Rf* sin(D), 0.66f, Rf* cos(D), 1.f); - n = QVector4D(sin(D), sin(a), cos(D), 1.0); - pR = mat*p; - nR = mat*n; - - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back((float)color.x); - data.push_back((float)color.y); - data.push_back((float)color.z); - - } - - //cylinder - //body of the cylinder - const float rf = static_cast(r); - for(int d = 0; d<360; d+= 360/prec) - { - //point A1 - double D = d*M_PI/180.0; - QVector4D p(rf*sin(D), 0.66f, rf*cos(D), 1.f); - QVector4D n(sin(D), 0.f, cos(D), 1.f); - QVector4D pR = mat*p; - QVector4D nR = mat*n; - - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back(color.x); - data.push_back(color.y); - data.push_back(color.z); - //point B1 - p = QVector4D(rf * sin(D),0,rf*cos(D), 1.0); - n = QVector4D(sin(D), 0, cos(D), 1.0); - pR = mat*p; - nR = mat*n; - - - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back(color.x); - data.push_back(color.y); - data.push_back(color.z); - //point C1 - D = (d+360/prec)*M_PI/180.0; - p = QVector4D(rf * sin(D),0,rf*cos(D), 1.0); - n = QVector4D(sin(D), 0, cos(D), 1.0); - pR = mat*p; - nR = mat*n; - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back(color.x); - data.push_back(color.y); - data.push_back(color.z); - //point A2 - D = (d+360/prec)*M_PI/180.0; - - p = QVector4D(rf * sin(D),0,rf*cos(D), 1.0); - n = QVector4D(sin(D), 0, cos(D), 1.0); - pR = mat*p; - nR = mat*n; - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back((float)color.x); - data.push_back((float)color.y); - data.push_back((float)color.z); - //point B2 - p = QVector4D(rf * sin(D), 0.66f, rf*cos(D), 1.f); - n = QVector4D(sin(D), 0, cos(D), 1.0); - pR = mat*p; - nR = mat*n; - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back((float)color.x); - data.push_back((float)color.y); - data.push_back((float)color.z); - //point C2 - D = d*M_PI/180.0; - p = QVector4D(rf * sin(D), 0.66f, rf*cos(D), 1.f); - n = QVector4D(sin(D), 0.f, cos(D), 1.f); - pR = mat*p; - nR = mat*n; - data.push_back(pR.x()); - data.push_back(pR.y()); - data.push_back(pR.z()); - data.push_back(nR.x()); - data.push_back(nR.y()); - data.push_back(nR.z()); - data.push_back(color.x); - data.push_back(color.y); - data.push_back(color.z); - - } -} - -/*! Draws an XYZ axis, with a given size (default is 1.0). - -The axis orientation matches the current modelView matrix state: -three arrows (red, green and blue) of length \p length are drawn along the -positive X, Y and Z directions in the top right corner of the screen. -X arrow is red, Y arrow is green and Z arrow is blue.*/ -void QGLViewer::drawAxis(qreal length) { - std::vector data; - data.resize(0); - drawArrow(0.06,0.12,10, qglviewer::Vec(0,0,0),qglviewer::Vec(length,0,0),qglviewer::Vec(1,0,0), data); - drawArrow(0.06,0.12,10, qglviewer::Vec(0,0,0),qglviewer::Vec(0,length,0),qglviewer::Vec(0,1,0), data); - drawArrow(0.06,0.12,10, qglviewer::Vec(0,0,0),qglviewer::Vec(0,0,length),qglviewer::Vec(0,0,1), data); - rendering_program_light.bind(); - vaos[AXIS].bind(); - vbos[Axis].bind(); - vbos[Axis].allocate(data.data(), static_cast(data.size()) * sizeof(float)); - rendering_program_light.enableAttributeArray("vertex"); - rendering_program_light.setAttributeBuffer("vertex",GL_FLOAT,0,3, - static_cast(9*sizeof(float))); - - rendering_program_light.enableAttributeArray("normal"); - rendering_program_light.setAttributeBuffer("normal",GL_FLOAT,3*sizeof(float),3, - static_cast(9*sizeof(float))); - - rendering_program_light.enableAttributeArray("colors"); - rendering_program_light.setAttributeBuffer("colors",GL_FLOAT,6*sizeof(float),3, - static_cast(9*sizeof(float))); - vbos[Axis].release(); - vaos[AXIS].release(); - axis_size = data.size(); - rendering_program_light.release(); -} - -/*! Draws a grid in the XY plane, centered on (0,0,0) (defined in the current -coordinate system). - -\p size (OpenGL units) and \p nbSubdivisions define its geometry.*/ -void QGLViewer::drawGrid(qreal size, int nbSubdivisions) { - - //The Grid - std::vector v_Grid; - for (int i=0; i<=nbSubdivisions; ++i) - { - const float pos = size*(2.0*i/nbSubdivisions-1.0); - v_Grid.push_back(pos); - v_Grid.push_back(-size); - v_Grid.push_back(0.0); - - v_Grid.push_back(pos); - v_Grid.push_back(+size); - v_Grid.push_back(0.0); - - v_Grid.push_back(-size); - v_Grid.push_back(pos); - v_Grid.push_back(0.0); - - v_Grid.push_back( size); - v_Grid.push_back( pos); - v_Grid.push_back( 0.0); - } - rendering_program.bind(); - vaos[GRID].bind(); - vbos[Grid].bind(); - vbos[Grid].allocate(v_Grid.data(),static_cast(v_Grid.size()*sizeof(float))); - rendering_program.enableAttributeArray("vertex"); - rendering_program.setAttributeBuffer("vertex",GL_FLOAT,0,3); - vbos[Grid].release(); - vaos[GRID].release(); - rendering_program.release(); - grid_size = v_Grid.size(); - - //The Axis - std::vector d_axis; - d_axis.resize(0); - //d_axis is filled by drawArrow always this way : V.x V.y V.z N.x N.y N.z C.r C.g C.b, so it is possible - //to use a single buffer with offset and stride - drawArrow(0.005*size,0.02*size,10, qglviewer::Vec(0,0,0),qglviewer::Vec(size,0,0),qglviewer::Vec(1,0,0), d_axis); - drawArrow(0.005*size,0.02*size,10, qglviewer::Vec(0,0,0),qglviewer::Vec(0,size,0),qglviewer::Vec(0,1,0), d_axis); - - rendering_program_light.bind(); - vaos[GRID_AXIS].bind(); - vbos[Grid_axis].bind(); - vbos[Grid_axis].allocate(d_axis.data(), static_cast(d_axis.size()) * sizeof(float)); - rendering_program_light.enableAttributeArray("vertex"); - rendering_program_light.setAttributeBuffer("vertex",GL_FLOAT,0,3, - static_cast(9*sizeof(float))); - - rendering_program_light.enableAttributeArray("normal"); - rendering_program_light.setAttributeBuffer("normal",GL_FLOAT,3*sizeof(float),3, - static_cast(9*sizeof(float))); - - rendering_program_light.enableAttributeArray("colors"); - rendering_program_light.setAttributeBuffer("colors",GL_FLOAT,6*sizeof(float),3, - static_cast(9*sizeof(float))); - vbos[Grid_axis].release(); - - vaos[GRID_AXIS].release(); - rendering_program_light.release(); - - g_axis_size = d_axis.size(); -} - -//////////////////////////////////////////////////////////////////////////////// -// S t a t i c m e t h o d s : Q G L V i e w e r P o o l // -//////////////////////////////////////////////////////////////////////////////// - -/*! saveStateToFile() is called on all the QGLViewers using the QGLViewerPool(). - */ -void QGLViewer::saveStateToFileForAllViewers() { - Q_FOREACH (QGLViewer *viewer, QGLViewer::QGLViewerPool()) { - if (viewer) - viewer->saveStateToFile(); - } -} - -////////////////////////////////////////////////////////////////////////// -// S a v e s t a t e b e t w e e n s e s s i o n s // -////////////////////////////////////////////////////////////////////////// - -/*! Returns the state file name. Default value is \c .qglviewer.xml. - -This is the name of the XML file where saveStateToFile() saves the viewer state -(camera state, widget geometry, display flags... see domElement()) on exit. Use -restoreStateFromFile() to restore this state later (usually in your init() -method). - -Setting this value to \c QString::null will disable the automatic state file -saving that normally occurs on exit. - -If more than one viewer are created by the application, this function will -return a numbered file name (as in ".qglviewer1.xml", ".qglviewer2.xml"... using -QGLViewer::QGLViewerIndex()) for extra viewers. Each viewer will then read back -its own information in restoreStateFromFile(), provided that the viewers are -created in the same order, which is usually the case. */ -QString QGLViewer::stateFileName() const { - QString name = stateFileName_; - - if (!name.isEmpty() && QGLViewer::QGLViewerIndex(this) > 0) { - QFileInfo fi(name); - if (fi.suffix().isEmpty()) - name += QString::number(QGLViewer::QGLViewerIndex(this)); - else - name = fi.absolutePath() + '/' + fi.completeBaseName() + - QString::number(QGLViewer::QGLViewerIndex(this)) + "." + - fi.suffix(); - } - - return name; -} - -/*! Saves in stateFileName() an XML representation of the QGLViewer state, -obtained from domElement(). - -Use restoreStateFromFile() to restore this viewer state. - -This method is automatically called when a viewer is closed (using Escape or -using the window's upper right \c x close button). setStateFileName() to \c -QString::null to prevent this. */ -void QGLViewer::saveStateToFile() { - QString name = stateFileName(); - - if (name.isEmpty()) - return; - - QFileInfo fileInfo(name); - - if (fileInfo.isDir()) { - QMessageBox::warning( - this, tr("Save to file error", "Message box window title"), - tr("State file name (%1) references a directory instead of a file.") - .arg(name)); - return; - } - - const QString dirName = fileInfo.absolutePath(); - if (!QFileInfo(dirName).exists()) { - QDir dir; - if (!(dir.mkdir(dirName))) { - QMessageBox::warning(this, - tr("Save to file error", "Message box window title"), - tr("Unable to create directory %1").arg(dirName)); - return; - } - } - - // Write the DOM tree to file - QFile f(name); - if (f.open(QIODevice::WriteOnly)) { - QTextStream out(&f); - QDomDocument doc("QGLVIEWER"); - doc.appendChild(domElement("QGLViewer", doc)); - doc.save(out, 2); - f.flush(); - f.close(); - } else - QMessageBox::warning( - this, tr("Save to file error", "Message box window title"), - tr("Unable to save to file %1").arg(name) + ":\n" + f.errorString()); -} - -/*! Restores the QGLViewer state from the stateFileName() file using -initFromDOMElement(). - -States are saved using saveStateToFile(), which is automatically called on -viewer exit. - -Returns \c true when the restoration is successful. Possible problems are an non -existing or unreadable stateFileName() file, an empty stateFileName() or an XML -syntax error. - -A manipulatedFrame() should be defined \e before calling this method, so that -its state can be restored. Initialization code put \e after this function will -override saved values: \code void Viewer::init() -{ -// Default initialization goes here (including the declaration of a possible -manipulatedFrame). - -if (!restoreStateFromFile()) -showEntireScene(); // Previous state cannot be restored: fit camera to scene. - -// Specific initialization that overrides file savings goes here. -} -\endcode */ -bool QGLViewer::restoreStateFromFile() { - QString name = stateFileName(); - - if (name.isEmpty()) - return false; - - QFileInfo fileInfo(name); - - if (!fileInfo.isFile()) - // No warning since it would be displayed at first start. - return false; - - if (!fileInfo.isReadable()) { - QMessageBox::warning( - this, tr("Problem in state restoration", "Message box window title"), - tr("File %1 is not readable.").arg(name)); - return false; - } - - // Read the DOM tree form file - QFile f(name); - if (f.open(QIODevice::ReadOnly)) { - QDomDocument doc; - doc.setContent(&f); - f.close(); - QDomElement main = doc.documentElement(); - initFromDOMElement(main); - } else { - QMessageBox::warning( - this, tr("Open file error", "Message box window title"), - tr("Unable to open file %1").arg(name) + ":\n" + f.errorString()); - return false; - } - - return true; -} - -/*! Returns an XML \c QDomElement that represents the QGLViewer. - -Used by saveStateToFile(). restoreStateFromFile() uses initFromDOMElement() to -restore the QGLViewer state from the resulting \c QDomElement. - -\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument -factory used to create QDomElement. - -The created QDomElement contains state values (axisIsDrawn(), FPSIsDisplayed(), -isFullScreen()...), viewer geometry, as well as camera() (see -qglviewer::Camera::domElement()) and manipulatedFrame() (if defined, see -qglviewer::ManipulatedFrame::domElement()) states. - -Overload this method to add your own attributes to the state file: -\code -QDomElement Viewer::domElement(const QString& name, QDomDocument& document) -const -{ -// Creates a custom node for a light -QDomElement de = document.createElement("Light"); -de.setAttribute("state", (lightIsOn()?"on":"off")); -// Note the include of the ManipulatedFrame domElement method. -de.appendChild(lightManipulatedFrame()->domElement("LightFrame", document)); - -// Get default state domElement and append custom node -QDomElement res = QGLViewer::domElement(name, document); -res.appendChild(de); -return res; -} -\endcode -See initFromDOMElement() for the associated restoration code. - -\attention For the manipulatedFrame(), qglviewer::Frame::constraint() and -qglviewer::Frame::referenceFrame() are not saved. See -qglviewer::Frame::domElement(). */ -QDomElement QGLViewer::domElement(const QString &name, - QDomDocument &document) const { - QDomElement de = document.createElement(name); - de.setAttribute("version", QGLViewerVersionString()); - - QDomElement stateNode = document.createElement("State"); - // hasMouseTracking() is not saved - stateNode.appendChild(DomUtils::QColorDomElement( - foregroundColor(), "foregroundColor", document)); - stateNode.appendChild(DomUtils::QColorDomElement( - backgroundColor(), "backgroundColor", document)); - DomUtils::setBoolAttribute(stateNode, "stereo", displaysInStereo()); - // Revolve or fly camera mode is not saved - de.appendChild(stateNode); - - QDomElement displayNode = document.createElement("Display"); - DomUtils::setBoolAttribute(displayNode, "axisIsDrawn", axisIsDrawn()); - DomUtils::setBoolAttribute(displayNode, "gridIsDrawn", gridIsDrawn()); - DomUtils::setBoolAttribute(displayNode, "FPSIsDisplayed", FPSIsDisplayed()); - DomUtils::setBoolAttribute(displayNode, "cameraIsEdited", cameraIsEdited()); - // textIsEnabled() is not saved - de.appendChild(displayNode); - - QDomElement geometryNode = document.createElement("Geometry"); - DomUtils::setBoolAttribute(geometryNode, "fullScreen", isFullScreen()); - if (isFullScreen()) { - geometryNode.setAttribute("prevPosX", QString::number(prevPos_.x())); - geometryNode.setAttribute("prevPosY", QString::number(prevPos_.y())); - } else { - QWidget *tlw = topLevelWidget(); - geometryNode.setAttribute("width", QString::number(tlw->width())); - geometryNode.setAttribute("height", QString::number(tlw->height())); - geometryNode.setAttribute("posX", QString::number(tlw->pos().x())); - geometryNode.setAttribute("posY", QString::number(tlw->pos().y())); - } - de.appendChild(geometryNode); - - // Restore original Camera zClippingCoefficient before saving. - if (cameraIsEdited()) - camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_); - de.appendChild(camera()->domElement("Camera", document)); - if (cameraIsEdited()) - // #CONNECTION# 5.0 from setCameraIsEdited() - camera()->setZClippingCoefficient(5.0); - - if (manipulatedFrame()) - de.appendChild( - manipulatedFrame()->domElement("ManipulatedFrame", document)); - - return de; -} - -/*! Restores the QGLViewer state from a \c QDomElement created by domElement(). - -Used by restoreStateFromFile() to restore the QGLViewer state from a file. - -Overload this method to retrieve custom attributes from the QGLViewer state -file. This code corresponds to the one given in the domElement() documentation: -\code -void Viewer::initFromDOMElement(const QDomElement& element) -{ -// Restore standard state -QGLViewer::initFromDOMElement(element); - -QDomElement child=element.firstChild().toElement(); -while (!child.isNull()) -{ -if (child.tagName() == "Light") -{ -if (child.hasAttribute("state")) -setLightOn(child.attribute("state").toLower() == "on"); - -// Assumes there is only one child. Otherwise you need to parse child's children -recursively. QDomElement lf = child.firstChild().toElement(); if (!lf.isNull() -&& lf.tagName() == "LightFrame") -lightManipulatedFrame()->initFromDomElement(lf); -} -child = child.nextSibling().toElement(); -} -} -\endcode - -See also qglviewer::Camera::initFromDOMElement(), -qglviewer::ManipulatedFrame::initFromDOMElement(). - -\note The manipulatedFrame() \e pointer is not modified by this method. If -defined, its state is simply set from the \p element values. */ -void QGLViewer::initFromDOMElement(const QDomElement &element) { - const QString version = element.attribute("version"); - // if (version != QGLViewerVersionString()) - if (version[0] != '2') - // Patches for previous versions should go here when the state file syntax - // is modified. - qWarning("State file created using QGLViewer version %s may not be " - "correctly read.", - version.toLatin1().constData()); - - QDomElement child = element.firstChild().toElement(); - bool tmpCameraIsEdited = cameraIsEdited(); - while (!child.isNull()) { - if (child.tagName() == "State") { - // #CONNECTION# default values from defaultConstructor() - // setMouseTracking(DomUtils::boolFromDom(child, "mouseTracking", false)); - setStereoDisplay(DomUtils::boolFromDom(child, "stereo", false)); - // if ((child.attribute("cameraMode", "revolve") == "fly") && - // (cameraIsInRevolveMode())) toggleCameraMode(); - - QDomElement ch = child.firstChild().toElement(); - while (!ch.isNull()) { - if (ch.tagName() == "foregroundColor") - setForegroundColor(DomUtils::QColorFromDom(ch)); - if (ch.tagName() == "backgroundColor") - setBackgroundColor(DomUtils::QColorFromDom(ch)); - ch = ch.nextSibling().toElement(); - } - } - - if (child.tagName() == "Display") { - // #CONNECTION# default values from defaultConstructor() - setAxisIsDrawn(DomUtils::boolFromDom(child, "axisIsDrawn", false)); - setGridIsDrawn(DomUtils::boolFromDom(child, "gridIsDrawn", false)); - setFPSIsDisplayed(DomUtils::boolFromDom(child, "FPSIsDisplayed", false)); - // See comment below. - tmpCameraIsEdited = DomUtils::boolFromDom(child, "cameraIsEdited", false); - // setTextIsEnabled(DomUtils::boolFromDom(child, "textIsEnabled", true)); - } - - if (child.tagName() == "Geometry") { - setFullScreen(DomUtils::boolFromDom(child, "fullScreen", false)); - - if (isFullScreen()) { - prevPos_.setX(DomUtils::intFromDom(child, "prevPosX", 0)); - prevPos_.setY(DomUtils::intFromDom(child, "prevPosY", 0)); - } else { - int width = DomUtils::intFromDom(child, "width", 600); - int height = DomUtils::intFromDom(child, "height", 400); - topLevelWidget()->resize(width, height); - camera()->setScreenWidthAndHeight(this->width(), this->height()); - - QPoint pos; - pos.setX(DomUtils::intFromDom(child, "posX", 0)); - pos.setY(DomUtils::intFromDom(child, "posY", 0)); - topLevelWidget()->move(pos); - } - } - - if (child.tagName() == "Camera") { - connectAllCameraKFIInterpolatedSignals(false); - camera()->initFromDOMElement(child); - connectAllCameraKFIInterpolatedSignals(); - } - - if ((child.tagName() == "ManipulatedFrame") && (manipulatedFrame())) - manipulatedFrame()->initFromDOMElement(child); - - child = child.nextSibling().toElement(); - } - - // The Camera always stores its "real" zClippingCoef in domElement(). If it is - // edited, its "real" coef must be saved and the coef set to 5.0, as is done - // in setCameraIsEdited(). BUT : Camera and Display are read in an arbitrary - // order. We must initialize Camera's "real" coef BEFORE calling - // setCameraIsEdited. Hence this temp cameraIsEdited and delayed call - cameraIsEdited_ = tmpCameraIsEdited; - if (cameraIsEdited_) { - previousCameraZClippingCoefficient_ = camera()->zClippingCoefficient(); - // #CONNECTION# 5.0 from setCameraIsEdited. - camera()->setZClippingCoefficient(5.0); - } -} - -#ifndef DOXYGEN -/*! This method is deprecated since version 1.3.9-5. Use saveStateToFile() and -setStateFileName() instead. */ -void QGLViewer::saveToFile(const QString &fileName) { - if (!fileName.isEmpty()) - setStateFileName(fileName); - - qWarning("saveToFile() is deprecated, use saveStateToFile() instead."); - saveStateToFile(); -} - -/*! This function is deprecated since version 1.3.9-5. Use -restoreStateFromFile() and setStateFileName() instead. */ -bool QGLViewer::restoreFromFile(const QString &fileName) { - if (!fileName.isEmpty()) - setStateFileName(fileName); - - qWarning( - "restoreFromFile() is deprecated, use restoreStateFromFile() instead."); - return restoreStateFromFile(); -} -#endif - - -void QGLViewer::copyBufferToTexture(GLint , GLenum ) { -} - -/*! Returns the texture id of the texture created by copyBufferToTexture(). - -Use glBindTexture() to use this texture. Note that this is already done by -copyBufferToTexture(). - -Returns \c 0 is copyBufferToTexture() was never called or if the texure was -deleted using glDeleteTextures() since then. */ -GLuint QGLViewer::bufferTextureId() const { - return 0; -} - -void QGLViewer::setOffset(qglviewer::Vec offset) -{ - this->_offset = offset; -} - -qglviewer::Vec QGLViewer::offset()const -{ - return _offset; -} +#include +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/quaternion.cpp b/GraphicsView/src/CGAL_Qt5/quaternion.cpp index 9ccb7515e32..fb2fe74d1cc 100644 --- a/GraphicsView/src/CGAL_Qt5/quaternion.cpp +++ b/GraphicsView/src/CGAL_Qt5/quaternion.cpp @@ -19,507 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include -#include -#include // RAND_MAX +#include -// All the methods are declared inline in Quaternion.h -using namespace qglviewer; -using namespace std; - -/*! Constructs a Quaternion that will rotate from the \p from direction to the -\p to direction. - -Note that this rotation is not uniquely defined. The selected axis is usually -orthogonal to \p from and \p to, minimizing the rotation angle. This method is -robust and can handle small or almost identical vectors. */ -Quaternion::Quaternion(const Vec &from, const Vec &to) { - const qreal epsilon = 1E-10; - - const qreal fromSqNorm = from.squaredNorm(); - const qreal toSqNorm = to.squaredNorm(); - // Identity Quaternion when one vector is null - if ((fromSqNorm < epsilon) || (toSqNorm < epsilon)) { - q[0] = q[1] = q[2] = 0.0; - q[3] = 1.0; - } else { - Vec axis = cross(from, to); - const qreal axisSqNorm = axis.squaredNorm(); - - // Aligned vectors, pick any axis, not aligned with from or to - if (axisSqNorm < epsilon) - axis = from.orthogonalVec(); - - qreal angle = asin(sqrt(axisSqNorm / (fromSqNorm * toSqNorm))); - - if (from * to < 0.0) - angle = M_PI - angle; - - setAxisAngle(axis, angle); - } -} - -/*! Returns the image of \p v by the Quaternion inverse() rotation. - -rotate() performs an inverse transformation. Same as inverse().rotate(v). */ -Vec Quaternion::inverseRotate(const Vec &v) const { - return inverse().rotate(v); -} - -/*! Returns the image of \p v by the Quaternion rotation. - -See also inverseRotate() and operator*(const Quaternion&, const Vec&). */ -Vec Quaternion::rotate(const Vec &v) const { - const qreal q00 = 2.0 * q[0] * q[0]; - const qreal q11 = 2.0 * q[1] * q[1]; - const qreal q22 = 2.0 * q[2] * q[2]; - - const qreal q01 = 2.0 * q[0] * q[1]; - const qreal q02 = 2.0 * q[0] * q[2]; - const qreal q03 = 2.0 * q[0] * q[3]; - - const qreal q12 = 2.0 * q[1] * q[2]; - const qreal q13 = 2.0 * q[1] * q[3]; - - const qreal q23 = 2.0 * q[2] * q[3]; - - return Vec((1.0 - q11 - q22) * v[0] + (q01 - q23) * v[1] + (q02 + q13) * v[2], - (q01 + q23) * v[0] + (1.0 - q22 - q00) * v[1] + (q12 - q03) * v[2], - (q02 - q13) * v[0] + (q12 + q03) * v[1] + - (1.0 - q11 - q00) * v[2]); -} - -/*! Set the Quaternion from a (supposedly correct) 3x3 rotation matrix. - - The matrix is expressed in European format: its three \e columns are the - images by the rotation of the three vectors of an orthogonal basis. Note that - OpenGL uses a symmetric representation for its matrices. - - setFromRotatedBasis() sets a Quaternion from the three axis of a rotated - frame. It actually fills the three columns of a matrix with these rotated - basis vectors and calls this method. */ -void Quaternion::setFromRotationMatrix(const qreal m[3][3]) { - // Compute one plus the trace of the matrix - const qreal onePlusTrace = 1.0 + m[0][0] + m[1][1] + m[2][2]; - - if (onePlusTrace > 1E-5) { - // Direct computation - const qreal s = sqrt(onePlusTrace) * 2.0; - q[0] = (m[2][1] - m[1][2]) / s; - q[1] = (m[0][2] - m[2][0]) / s; - q[2] = (m[1][0] - m[0][1]) / s; - q[3] = 0.25 * s; - } else { - // Computation depends on major diagonal term - if ((m[0][0] > m[1][1]) & (m[0][0] > m[2][2])) { - const qreal s = sqrt(1.0 + m[0][0] - m[1][1] - m[2][2]) * 2.0; - q[0] = 0.25 * s; - q[1] = (m[0][1] + m[1][0]) / s; - q[2] = (m[0][2] + m[2][0]) / s; - q[3] = (m[1][2] - m[2][1]) / s; - } else if (m[1][1] > m[2][2]) { - const qreal s = sqrt(1.0 + m[1][1] - m[0][0] - m[2][2]) * 2.0; - q[0] = (m[0][1] + m[1][0]) / s; - q[1] = 0.25 * s; - q[2] = (m[1][2] + m[2][1]) / s; - q[3] = (m[0][2] - m[2][0]) / s; - } else { - const qreal s = sqrt(1.0 + m[2][2] - m[0][0] - m[1][1]) * 2.0; - q[0] = (m[0][2] + m[2][0]) / s; - q[1] = (m[1][2] + m[2][1]) / s; - q[2] = 0.25 * s; - q[3] = (m[0][1] - m[1][0]) / s; - } - } - normalize(); -} - -#ifndef DOXYGEN -void Quaternion::setFromRotatedBase(const Vec &X, const Vec &Y, const Vec &Z) { - qWarning("setFromRotatedBase is deprecated, use setFromRotatedBasis instead"); - setFromRotatedBasis(X, Y, Z); -} -#endif - -/*! Sets the Quaternion from the three rotated vectors of an orthogonal basis. - - The three vectors do not have to be normalized but must be orthogonal and - direct (X^Y=k*Z, with k>0). - - \code - Quaternion q; - q.setFromRotatedBasis(X, Y, Z); - // Now q.rotate(Vec(1,0,0)) == X and q.inverseRotate(X) == Vec(1,0,0) - // Same goes for Y and Z with Vec(0,1,0) and Vec(0,0,1). - \endcode - - See also setFromRotationMatrix() and Quaternion(const Vec&, const Vec&). */ -void Quaternion::setFromRotatedBasis(const Vec &X, const Vec &Y, const Vec &Z) { - qreal m[3][3]; - qreal normX = X.norm(); - qreal normY = Y.norm(); - qreal normZ = Z.norm(); - - for (int i = 0; i < 3; ++i) { - m[i][0] = X[i] / normX; - m[i][1] = Y[i] / normY; - m[i][2] = Z[i] / normZ; - } - - setFromRotationMatrix(m); -} - -/*! Returns the axis vector and the angle (in radians) of the rotation - represented by the Quaternion. See the axis() and angle() documentations. */ -void Quaternion::getAxisAngle(Vec &axis, qreal &angle) const { - angle = 2.0 * acos(q[3]); - axis = Vec(q[0], q[1], q[2]); - const qreal sinus = axis.norm(); - if (sinus > 1E-8) - axis /= sinus; - - if (angle > M_PI) { - angle = 2.0 * qreal(M_PI) - angle; - axis = -axis; - } -} - -/*! Returns the normalized axis direction of the rotation represented by the -Quaternion. - -It is null for an identity Quaternion. See also angle() and getAxisAngle(). */ -Vec Quaternion::axis() const { - Vec res = Vec(q[0], q[1], q[2]); - const qreal sinus = res.norm(); - if (sinus > 1E-8) - res /= sinus; - return (acos(q[3]) <= M_PI / 2.0) ? res : -res; -} - -/*! Returns the angle (in radians) of the rotation represented by the - Quaternion. - - This value is always in the range [0-pi]. Larger rotational angles are obtained - by inverting the axis() direction. - - See also axis() and getAxisAngle(). */ -qreal Quaternion::angle() const { - const qreal angle = 2.0 * acos(q[3]); - return (angle <= M_PI) ? angle : 2.0 * M_PI - angle; -} - -/*! Returns an XML \c QDomElement that represents the Quaternion. - - \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument - factory used to create QDomElement. - - When output to a file, the resulting QDomElement will look like: - \code - - \endcode - - Use initFromDOMElement() to restore the Quaternion state from the resulting \c - QDomElement. See also the Quaternion(const QDomElement&) constructor. - - See the Vec::domElement() documentation for a complete QDomDocument creation - and saving example. - - See also Frame::domElement(), Camera::domElement(), - KeyFrameInterpolator::domElement()... */ -QDomElement Quaternion::domElement(const QString &name, - QDomDocument &document) const { - QDomElement de = document.createElement(name); - de.setAttribute("q0", QString::number(q[0])); - de.setAttribute("q1", QString::number(q[1])); - de.setAttribute("q2", QString::number(q[2])); - de.setAttribute("q3", QString::number(q[3])); - return de; -} - -/*! Restores the Quaternion state from a \c QDomElement created by domElement(). - - The \c QDomElement should contain the \c q0, \c q1 , \c q2 and \c q3 - attributes. If one of these attributes is missing or is not a number, a warning - is displayed and these fields are respectively set to 0.0, 0.0, 0.0 and 1.0 - (identity Quaternion). - - See also the Quaternion(const QDomElement&) constructor. */ -void Quaternion::initFromDOMElement(const QDomElement &element) { - Quaternion q(element); - *this = q; -} - -/*! Constructs a Quaternion from a \c QDomElement representing an XML code of - the form \code< anyTagName q0=".." q1=".." q2=".." q3=".." />\endcode - - If one of these attributes is missing or is not a number, a warning is - displayed and the associated value is respectively set to 0, 0, 0 and 1 - (identity Quaternion). - - See also domElement() and initFromDOMElement(). */ -Quaternion::Quaternion(const QDomElement &element) { - QStringList attribute; - attribute << "q0" - << "q1" - << "q2" - << "q3"; - for (int i = 0; i < attribute.size(); ++i) - q[i] = DomUtils::qrealFromDom(element, attribute[i], ((i < 3) ? 0.0 : 1.0)); -} - -/*! Returns the Quaternion associated 4x4 OpenGL rotation matrix. - - Use \c glMultMatrixd(q.matrix()) to apply the rotation represented by - Quaternion \c q to the current OpenGL matrix. - - See also getMatrix(), getRotationMatrix() and inverseMatrix(). - - \attention The result is only valid until the next call to matrix(). Use it - immediately (as shown above) or consider using getMatrix() instead. - - \attention The matrix is given in OpenGL format (row-major order) and is the - transpose of the actual mathematical European representation. Consider using - getRotationMatrix() instead. */ -const GLdouble *Quaternion::matrix() const { - static GLdouble m[4][4]; - getMatrix(m); - return (const GLdouble *)(m); -} - -/*! Fills \p m with the OpenGL representation of the Quaternion rotation. - -Use matrix() if you do not need to store this matrix and simply want to alter -the current OpenGL matrix. See also getInverseMatrix() and Frame::getMatrix(). -*/ -void Quaternion::getMatrix(GLdouble m[4][4]) const { - const qreal q00 = 2.0 * q[0] * q[0]; - const qreal q11 = 2.0 * q[1] * q[1]; - const qreal q22 = 2.0 * q[2] * q[2]; - - const qreal q01 = 2.0 * q[0] * q[1]; - const qreal q02 = 2.0 * q[0] * q[2]; - const qreal q03 = 2.0 * q[0] * q[3]; - - const qreal q12 = 2.0 * q[1] * q[2]; - const qreal q13 = 2.0 * q[1] * q[3]; - - const qreal q23 = 2.0 * q[2] * q[3]; - - m[0][0] = 1.0 - q11 - q22; - m[1][0] = q01 - q23; - m[2][0] = q02 + q13; - - m[0][1] = q01 + q23; - m[1][1] = 1.0 - q22 - q00; - m[2][1] = q12 - q03; - - m[0][2] = q02 - q13; - m[1][2] = q12 + q03; - m[2][2] = 1.0 - q11 - q00; - - m[0][3] = 0.0; - m[1][3] = 0.0; - m[2][3] = 0.0; - - m[3][0] = 0.0; - m[3][1] = 0.0; - m[3][2] = 0.0; - m[3][3] = 1.0; -} - -/*! Same as getMatrix(), but with a \c GLdouble[16] parameter. See also - * getInverseMatrix() and Frame::getMatrix(). */ -void Quaternion::getMatrix(GLdouble m[16]) const { - static GLdouble mat[4][4]; - getMatrix(mat); - int count = 0; - for (int i = 0; i < 4; ++i) - for (int j = 0; j < 4; ++j) - m[count++] = mat[i][j]; -} - -/*! Fills \p m with the 3x3 rotation matrix associated with the Quaternion. - - See also getInverseRotationMatrix(). - - \attention \p m uses the European mathematical representation of the rotation - matrix. Use matrix() and getMatrix() to retrieve the OpenGL transposed - version. */ -void Quaternion::getRotationMatrix(qreal m[3][3]) const { - static GLdouble mat[4][4]; - getMatrix(mat); - for (int i = 0; i < 3; ++i) - for (int j = 0; j < 3; ++j) - // Beware of transposition - m[i][j] = qreal(mat[j][i]); -} - -/*! Returns the associated 4x4 OpenGL \e inverse rotation matrix. This is simply - the matrix() of the inverse(). - - \attention The result is only valid until the next call to inverseMatrix(). - Use it immediately (as in \c glMultMatrixd(q.inverseMatrix())) or use - getInverseMatrix() instead. - - \attention The matrix is given in OpenGL format (row-major order) and is the - transpose of the actual mathematical European representation. Consider using - getInverseRotationMatrix() instead. */ -const GLdouble *Quaternion::inverseMatrix() const { - static GLdouble m[4][4]; - getInverseMatrix(m); - return (const GLdouble *)(m); -} - -/*! Fills \p m with the OpenGL matrix corresponding to the inverse() rotation. - -Use inverseMatrix() if you do not need to store this matrix and simply want to -alter the current OpenGL matrix. See also getMatrix(). */ -void Quaternion::getInverseMatrix(GLdouble m[4][4]) const { - inverse().getMatrix(m); -} - -/*! Same as getInverseMatrix(), but with a \c GLdouble[16] parameter. See also - * getMatrix(). */ -void Quaternion::getInverseMatrix(GLdouble m[16]) const { - inverse().getMatrix(m); -} - -/*! \p m is set to the 3x3 \e inverse rotation matrix associated with the - Quaternion. - - \attention This is the classical mathematical rotation matrix. The OpenGL - format uses its transposed version. See inverseMatrix() and getInverseMatrix(). - */ -void Quaternion::getInverseRotationMatrix(qreal m[3][3]) const { - static GLdouble mat[4][4]; - getInverseMatrix(mat); - for (int i = 0; i < 3; ++i) - for (int j = 0; j < 3; ++j) - // Beware of transposition - m[i][j] = qreal(mat[j][i]); -} - -/*! Returns the slerp interpolation of Quaternions \p a and \p b, at time \p t. - - \p t should range in [0,1]. Result is \p a when \p t=0 and \p b when \p t=1. - - When \p allowFlip is \c true (default) the slerp interpolation will always use - the "shortest path" between the Quaternions' orientations, by "flipping" the - source Quaternion if needed (see negate()). */ -Quaternion Quaternion::slerp(const Quaternion &a, const Quaternion &b, qreal t, - bool allowFlip) { - qreal cosAngle = Quaternion::dot(a, b); - - qreal c1, c2; - // Linear interpolation for close orientations - if ((1.0 - fabs(cosAngle)) < 0.01) { - c1 = 1.0 - t; - c2 = t; - } else { - // Spherical interpolation - qreal angle = acos(fabs(cosAngle)); - qreal sinAngle = sin(angle); - c1 = sin(angle * (1.0 - t)) / sinAngle; - c2 = sin(angle * t) / sinAngle; - } - - // Use the shortest path - if (allowFlip && (cosAngle < 0.0)) - c1 = -c1; - - return Quaternion(c1 * a[0] + c2 * b[0], c1 * a[1] + c2 * b[1], - c1 * a[2] + c2 * b[2], c1 * a[3] + c2 * b[3]); -} - -/*! Returns the slerp interpolation of the two Quaternions \p a and \p b, at - time \p t, using tangents \p tgA and \p tgB. - - The resulting Quaternion is "between" \p a and \p b (result is \p a when \p - t=0 and \p b for \p t=1). - - Use squadTangent() to define the Quaternion tangents \p tgA and \p tgB. */ -Quaternion Quaternion::squad(const Quaternion &a, const Quaternion &tgA, - const Quaternion &tgB, const Quaternion &b, - qreal t) { - Quaternion ab = Quaternion::slerp(a, b, t); - Quaternion tg = Quaternion::slerp(tgA, tgB, t, false); - return Quaternion::slerp(ab, tg, 2.0 * t * (1.0 - t), false); -} - -/*! Returns the logarithm of the Quaternion. See also exp(). */ -Quaternion Quaternion::log() { - qreal len = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]); - - if (len < 1E-6) - return Quaternion(q[0], q[1], q[2], 0.0); - else { - qreal coef = acos(q[3]) / len; - return Quaternion(q[0] * coef, q[1] * coef, q[2] * coef, 0.0); - } -} - -/*! Returns the exponential of the Quaternion. See also log(). */ -Quaternion Quaternion::exp() { - qreal theta = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]); - - if (theta < 1E-6) - return Quaternion(q[0], q[1], q[2], cos(theta)); - else { - qreal coef = sin(theta) / theta; - return Quaternion(q[0] * coef, q[1] * coef, q[2] * coef, cos(theta)); - } -} - -/*! Returns log(a. inverse() * b). Useful for squadTangent(). */ -Quaternion Quaternion::lnDif(const Quaternion &a, const Quaternion &b) { - Quaternion dif = a.inverse() * b; - dif.normalize(); - return dif.log(); -} - -/*! Returns a tangent Quaternion for \p center, defined by \p before and \p - after Quaternions. - - Useful for smooth spline interpolation of Quaternion with squad() and slerp(). - */ -Quaternion Quaternion::squadTangent(const Quaternion &before, - const Quaternion ¢er, - const Quaternion &after) { - Quaternion l1 = Quaternion::lnDif(center, before); - Quaternion l2 = Quaternion::lnDif(center, after); - Quaternion e; - for (int i = 0; i < 4; ++i) - e.q[i] = -0.25 * (l1.q[i] + l2.q[i]); - e = center * (e.exp()); - - // if (Quaternion::dot(e,b) < 0.0) - // e.negate(); - - return e; -} - -ostream &operator<<(ostream &o, const Quaternion &Q) { - return o << Q[0] << '\t' << Q[1] << '\t' << Q[2] << '\t' << Q[3]; -} - -/*! Returns a random unit Quaternion. - -You can create a randomly directed unit vector using: -\code -Vec randomDir = Quaternion::randomQuaternion() * Vec(1.0, 0.0, 0.0); // or any -other Vec \endcode - -\note This function uses rand() to create pseudo-random numbers and the random -number generator can be initialized using srand().*/ -Quaternion Quaternion::randomQuaternion() { - // The rand() function is not very portable and may not be available on your - // system. Add the appropriate include or replace by an other random function - // in case of problem. - qreal seed = rand() / (qreal)RAND_MAX; - qreal r1 = sqrt(1.0 - seed); - qreal r2 = sqrt(seed); - qreal t1 = 2.0 * M_PI * (rand() / (qreal)RAND_MAX); - qreal t2 = 2.0 * M_PI * (rand() / (qreal)RAND_MAX); - return Quaternion(sin(t1) * r1, cos(t1) * r1, sin(t2) * r2, cos(t2) * r2); -} +#endif // CGAL_HEADER_ONLY diff --git a/GraphicsView/src/CGAL_Qt5/vec.cpp b/GraphicsView/src/CGAL_Qt5/vec.cpp index ecd236d2643..ba0ccdcf139 100644 --- a/GraphicsView/src/CGAL_Qt5/vec.cpp +++ b/GraphicsView/src/CGAL_Qt5/vec.cpp @@ -19,146 +19,9 @@ WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ +#ifndef CGAL_HEADER_ONLY #include -#include +#include -// Most of the methods are declared inline in vec.h - -using namespace qglviewer; -using namespace std; - -/*! Projects the Vec on the axis of direction \p direction that passes through -the origin. - -\p direction does not need to be normalized (but must be non null). */ -void Vec::projectOnAxis(const Vec &direction) { -#ifndef QT_NO_DEBUG - if (direction.squaredNorm() < 1.0E-10) - qWarning("Vec::projectOnAxis: axis direction is not normalized (norm=%f).", - direction.norm()); -#endif - - *this = (((*this) * direction) / direction.squaredNorm()) * direction; -} - -/*! Projects the Vec on the plane whose normal is \p normal that passes through -the origin. - -\p normal does not need to be normalized (but must be non null). */ -void Vec::projectOnPlane(const Vec &normal) { -#ifndef QT_NO_DEBUG - if (normal.squaredNorm() < 1.0E-10) - qWarning("Vec::projectOnPlane: plane normal is not normalized (norm=%f).", - normal.norm()); -#endif - - *this -= (((*this) * normal) / normal.squaredNorm()) * normal; -} - -/*! Returns a Vec orthogonal to the Vec. Its norm() depends on the Vec, but is - zero only for a null Vec. Note that the function that associates an - orthogonalVec() to a Vec is not continous. */ -Vec Vec::orthogonalVec() const { - // Find smallest component. Keep equal case for null values. - if ((fabs(y) >= 0.9 * fabs(x)) && (fabs(z) >= 0.9 * fabs(x))) - return Vec(0.0, -z, y); - else if ((fabs(x) >= 0.9 * fabs(y)) && (fabs(z) >= 0.9 * fabs(y))) - return Vec(-z, 0.0, x); - else - return Vec(-y, x, 0.0); -} - -/*! Constructs a Vec from a \c QDomElement representing an XML code of the form - \code< anyTagName x=".." y=".." z=".." />\endcode - -If one of these attributes is missing or is not a number, a warning is displayed -and the associated value is set to 0.0. - -See also domElement() and initFromDOMElement(). */ -Vec::Vec(const QDomElement &element) { - QStringList attribute; - attribute << "x" - << "y" - << "z"; - for (int i = 0; i < attribute.size(); ++i) -#ifdef QGLVIEWER_UNION_NOT_SUPPORTED - this->operator[](i) = DomUtils::qrealFromDom(element, attribute[i], 0.0); -#else - v_[i] = DomUtils::qrealFromDom(element, attribute[i], 0.0); -#endif -} - -/*! Returns an XML \c QDomElement that represents the Vec. - - \p name is the name of the QDomElement tag. \p doc is the \c QDomDocument - factory used to create QDomElement. - - When output to a file, the resulting QDomElement will look like: - \code - - \endcode - - Use initFromDOMElement() to restore the Vec state from the resulting \c - QDomElement. See also the Vec(const QDomElement&) constructor. - - Here is complete example that creates a QDomDocument and saves it into a file: - \code - Vec sunPos; - QDomDocument document("myDocument"); - QDomElement sunElement = document.createElement("Sun"); - document.appendChild(sunElement); - sunElement.setAttribute("brightness", sunBrightness()); - sunElement.appendChild(sunPos.domElement("sunPosition", document)); - // Other additions to the document hierarchy... - - // Save doc document - QFile f("myFile.xml"); - if (f.open(IO_WriteOnly)) - { - QTextStream out(&f); - document.save(out, 2); - f.close(); - } - \endcode - - See also Quaternion::domElement(), Frame::domElement(), Camera::domElement()... - */ -QDomElement Vec::domElement(const QString &name, QDomDocument &document) const { - QDomElement de = document.createElement(name); - de.setAttribute("x", QString::number(x)); - de.setAttribute("y", QString::number(y)); - de.setAttribute("z", QString::number(z)); - return de; -} - -/*! Restores the Vec state from a \c QDomElement created by domElement(). - - The \c QDomElement should contain \c x, \c y and \c z attributes. If one of - these attributes is missing or is not a number, a warning is displayed and the - associated value is set to 0.0. - - To restore the Vec state from an xml file, use: - \code - // Load DOM from file - QDomDocument doc; - QFile f("myFile.xml"); - if (f.open(IO_ReadOnly)) - { - doc.setContent(&f); - f.close(); - } - // Parse the DOM tree and initialize - QDomElement main=doc.documentElement(); - myVec.initFromDOMElement(main); - \endcode - - See also the Vec(const QDomElement&) constructor. */ -void Vec::initFromDOMElement(const QDomElement &element) { - const Vec v(element); - *this = v; -} - -ostream &operator<<(ostream &o, const Vec &v) { - return o << v.x << '\t' << v.y << '\t' << v.z; -} +#endif // CGAL_HEADER_ONLY diff --git a/Installation/cmake/modules/CGAL_Qt5_moc_and_resource_files.cmake b/Installation/cmake/modules/CGAL_Qt5_moc_and_resource_files.cmake index 4682d0d5dfa..b19ae51259c 100644 --- a/Installation/cmake/modules/CGAL_Qt5_moc_and_resource_files.cmake +++ b/Installation/cmake/modules/CGAL_Qt5_moc_and_resource_files.cmake @@ -3,19 +3,20 @@ if(CGAL_Qt5_moc_and_resource_files_included) endif() set(CGAL_Qt5_moc_and_resource_files_included TRUE) - -qt5_wrap_cpp(_CGAL_Qt5_MOC_FILES_private - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewNavigation.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/DemosMainWindow.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsItem.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewInput.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/camera.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/frame.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/keyFrameInterpolator.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedCameraFrame.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedFrame.h - ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/qglviewer.h - ) +if(NOT CGAL_HEADER_ONLY) + qt5_wrap_cpp(_CGAL_Qt5_MOC_FILES_private + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewNavigation.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/DemosMainWindow.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsItem.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewInput.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/camera.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/frame.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/keyFrameInterpolator.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedCameraFrame.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedFrame.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/qglviewer.h + ) +endif()#CGAL_HEADER_ONLY # qrc files (resources files, that contain icons, at least) if(EXISTS ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/demo/resources/CGAL.qrc) diff --git a/Installation/cmake/modules/CGAL_SetupCGAL_Qt5Dependencies.cmake b/Installation/cmake/modules/CGAL_SetupCGAL_Qt5Dependencies.cmake index 70fa7a3ef1b..9888c59d113 100644 --- a/Installation/cmake/modules/CGAL_SetupCGAL_Qt5Dependencies.cmake +++ b/Installation/cmake/modules/CGAL_SetupCGAL_Qt5Dependencies.cmake @@ -53,10 +53,24 @@ if(NOT CGAL_Qt5_MISSING_DEPS) include(${CMAKE_CURRENT_LIST_DIR}/CGAL_Qt5_moc_and_resource_files.cmake) if(CGAL_HEADER_ONLY AND (WITH_demos OR WITH_examples OR NOT CGAL_BUILDING_LIBS) AND NOT TARGET CGAL_Qt5_moc_and_resources) - add_library(CGAL_Qt5_moc_and_resources STATIC ${_CGAL_Qt5_MOC_FILES_private} ${_CGAL_Qt5_RESOURCE_FILES_private}) + add_library(CGAL_Qt5_moc_and_resources STATIC + ${_CGAL_Qt5_MOC_FILES_private} + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewNavigation.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/DemosMainWindow.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsItem.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewInput.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/camera.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/frame.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/keyFrameInterpolator.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedCameraFrame.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedFrame.h + ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/qglviewer.h + + ${_CGAL_Qt5_RESOURCE_FILES_private}) set_target_properties(CGAL_Qt5_moc_and_resources PROPERTIES POSITION_INDEPENDENT_CODE TRUE - EXCLUDE_FROM_ALL TRUE) + EXCLUDE_FROM_ALL TRUE + AUTOMOC TRUE) target_link_libraries(CGAL_Qt5_moc_and_resources CGAL::CGAL Qt5::Widgets Qt5::OpenGL Qt5::Svg Qt5::Xml) add_library(CGAL::CGAL_Qt5_moc_and_resources ALIAS CGAL_Qt5_moc_and_resources) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Volume_plane.h b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Volume_plane.h index 820399171df..215f64f851c 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Volume_plane.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Volume_plane.h @@ -660,8 +660,8 @@ bool Volume_plane::eventFilter(QObject *, QEvent *event) viewer->setMouseBinding( Qt::NoModifier, Qt::LeftButton, - QGLViewer::FRAME, - QGLViewer::TRANSLATE); + qglviewer::FRAME, + qglviewer::TRANSLATE); } } else if(event->type() == QEvent::MouseButtonRelease && is_grabbing) @@ -670,8 +670,8 @@ bool Volume_plane::eventFilter(QObject *, QEvent *event) viewer->setMouseBinding( Qt::NoModifier, Qt::LeftButton, - QGLViewer::CAMERA, - QGLViewer::ROTATE); + qglviewer::CAMERA, + qglviewer::ROTATE); } return false; } diff --git a/Polyhedron/demo/Polyhedron/Plugins/PCA/Scene_edit_box_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/PCA/Scene_edit_box_item.cpp index 004308485fc..866124cc54d 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PCA/Scene_edit_box_item.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PCA/Scene_edit_box_item.cpp @@ -988,7 +988,7 @@ void Scene_edit_box_item_priv::reset_selection() selection_on = false; QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin(); viewer->setManipulatedFrame(frame); - viewer->setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, QGLViewer::SELECT); + viewer->setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, qglviewer::SELECT); constraint.setTranslationConstraintType(qglviewer::AxisPlaneConstraint::FREE); selected_vertices.clear(); } @@ -1060,8 +1060,8 @@ bool Scene_edit_box_item::eventFilter(QObject *obj, QEvent *event) viewer->setMouseBinding( Qt::NoModifier, Qt::LeftButton, - QGLViewer::FRAME, - QGLViewer::TRANSLATE); + qglviewer::FRAME, + qglviewer::TRANSLATE); } else { @@ -1108,8 +1108,8 @@ bool Scene_edit_box_item::eventFilter(QObject *obj, QEvent *event) viewer->setMouseBinding( Qt::NoModifier, Qt::LeftButton, - QGLViewer::CAMERA, - QGLViewer::ROTATE); + qglviewer::CAMERA, + qglviewer::ROTATE); } else if(event->type() == QEvent::KeyRelease) { diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_3.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_3.cpp index 2775bf4460d..0bb96d804da 100644 --- a/Polyhedron/demo/Polyhedron/Polyhedron_3.cpp +++ b/Polyhedron/demo/Polyhedron/Polyhedron_3.cpp @@ -1,6 +1,6 @@ #include "Polyhedron_demo.h" #include - +#include /*! * \brief Defines the entry point of the demo. * Creates the application and sets a main window. diff --git a/Polyhedron/demo/Polyhedron/Viewer.cpp b/Polyhedron/demo/Polyhedron/Viewer.cpp index 0da05ba6e00..b45b90b4b57 100644 --- a/Polyhedron/demo/Polyhedron/Viewer.cpp +++ b/Polyhedron/demo/Polyhedron/Viewer.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -92,7 +91,7 @@ Viewer::Viewer(QWidget* parent, bool antialiasing) connect( d->textRenderer, SIGNAL(sendMessage(QString,int)), this, SLOT(printMessage(QString,int)) ); connect(&d->messageTimer, SIGNAL(timeout()), SLOT(hideMessage())); - setShortcut(EXIT_VIEWER, 0); + setShortcut(qglviewer::EXIT_VIEWER, 0); setKeyDescription(Qt::Key_T, tr("Turn the camera by 180 degrees")); setKeyDescription(Qt::Key_M, @@ -107,12 +106,12 @@ Viewer::Viewer(QWidget* parent, bool antialiasing) #if QGLVIEWER_VERSION >= 0x020501 //modify mouse bindings that have been updated - setMouseBinding(Qt::Key(0), Qt::NoModifier, Qt::LeftButton, RAP_FROM_PIXEL, true, Qt::RightButton); + setMouseBinding(Qt::Key(0), Qt::NoModifier, Qt::LeftButton, qglviewer::RAP_FROM_PIXEL, true, Qt::RightButton); setMouseBindingDescription(Qt::ShiftModifier, Qt::RightButton, tr("Select and pop context menu")); - setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, RAP_FROM_PIXEL); + setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, qglviewer::RAP_FROM_PIXEL); //use the new API for these - setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, SELECT); + setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, qglviewer::SELECT); setMouseBindingDescription(Qt::Key(0), Qt::ShiftModifier, Qt::LeftButton, tr("Selects and display context " diff --git a/Polyhedron/demo/Polyhedron/Viewer.h b/Polyhedron/demo/Polyhedron/Viewer.h index 4ed012db3d5..33611878f0e 100644 --- a/Polyhedron/demo/Polyhedron/Viewer.h +++ b/Polyhedron/demo/Polyhedron/Viewer.h @@ -9,8 +9,6 @@ #include #include #include - -#include #include #include #include @@ -112,7 +110,7 @@ public Q_SLOTS: void setBindingSelect() Q_DECL_OVERRIDE { #if QGLVIEWER_VERSION >= 0x020501 - setMouseBinding(::Qt::ShiftModifier, ::Qt::LeftButton, SELECT); + setMouseBinding(::Qt::ShiftModifier, ::Qt::LeftButton, qglviewer::SELECT); #else setMouseBinding(::Qt::SHIFT + ::Qt::LeftButton, SELECT); #endif @@ -121,7 +119,7 @@ public Q_SLOTS: virtual void setNoBinding() Q_DECL_OVERRIDE { #if QGLVIEWER_VERSION >= 0x020501 - setMouseBinding(::Qt::ShiftModifier, ::Qt::LeftButton, NO_CLICK_ACTION); + setMouseBinding(::Qt::ShiftModifier, ::Qt::LeftButton, qglviewer::NO_CLICK_ACTION); #else setMouseBinding(::Qt::SHIFT + ::Qt::LeftButton, NO_CLICK_ACTION); #endif