From 93859133989a6b59009da1f87b9625e1c94805cb Mon Sep 17 00:00:00 2001 From: Liangliang Nan Date: Sat, 14 Dec 2024 17:24:26 +0100 Subject: [PATCH] Squashed commit of the following: commit a1a45b26a15d20e5c14138604cdca844a6723fa1 Author: Liangliang Nan Date: Sat Dec 14 17:24:00 2024 +0100 Update CMakeLists.txt commit eb025fac98c2447c2f8882493b40e058cb515640 Author: Liangliang Nan Date: Sat Dec 14 17:20:58 2024 +0100 Update ReadMe.md commit 1ccac0bb18f4bea60b92fe961fa84d96d8a9b9be Author: Liangliang Nan Date: Sat Dec 14 17:11:09 2024 +0100 compatible with both Qt5 and Qt6 commit e06cff0cc3a3cc7f1558c80681f4ce8033eaf592 Author: Liangliang Nan Date: Sat Dec 14 16:25:20 2024 +0100 works with Qt5 commit 862bb32e782300082613a9698ee6100562e1020c Author: Liangliang Nan Date: Sat Dec 14 15:04:41 2024 +0100 clean commit 6fcda7fbccc2faa4c119bc3ace309ceafe8ee64a Author: Liangliang Nan Date: Sat Dec 14 15:00:17 2024 +0100 works with Qt6 commit 48d07d96a9b6f601fce52950a1f2f8a7df571d70 Author: Liangliang Nan Date: Sat Dec 14 14:15:00 2024 +0100 test with Qt5 commit 6d7af35224a1f3f2c3ce21590d89a39fbce2b4a0 Author: Liangliang Nan Date: Sat Dec 14 14:11:22 2024 +0100 test with Qt5 commit 5266c38c99818108f3cb9966d7b4eda95232a735 Author: Liangliang Nan Date: Sat Dec 14 14:08:57 2024 +0100 test Qt5 commit 93c9650c3981a52e8fd0b9388b7df3a28d977dfb Author: Liangliang Nan Date: Sat Dec 14 14:04:15 2024 +0100 no specific version of Qt5 commit 8874e65f48e3e82d64749183ed88d64dcd06f819 Author: Liangliang Nan Date: Sat Dec 14 14:02:16 2024 +0100 use Qt6; updated qglviewer --- .gitattributes | 2 +- .gitignore | 4 +- ReadMe.md | 5 +- code/3rd_QGLViewer-2.6.3/CMakeLists.txt | 153 - code/3rd_QGLViewer-2.6.3/ImageInterface.cpp | 47 - code/3rd_QGLViewer-2.6.3/ImageInterface.h | 46 - code/3rd_QGLViewer-2.6.3/ImageInterface.ui | 386 -- .../VRender/AxisAlignedBox.h | 101 - .../VRender/BackFaceCullingOptimizer.cpp | 85 - code/3rd_QGLViewer-2.6.3/VRender/Exporter.cpp | 108 - .../VRender/FIGExporter.cpp | 225 - code/3rd_QGLViewer-2.6.3/VRender/Optimizer.h | 91 - code/3rd_QGLViewer-2.6.3/VRender/ParserGL.h | 88 - code/3rd_QGLViewer-2.6.3/VRender/SortMethod.h | 102 - code/3rd_QGLViewer-2.6.3/VRender/Types.h | 89 - code/3rd_QGLViewer-2.6.3/camera.cpp | 2213 --------- code/3rd_QGLViewer-2.6.3/camera.h | 516 -- code/3rd_QGLViewer-2.6.3/config.h | 120 - code/3rd_QGLViewer-2.6.3/constraint.cpp | 291 -- code/3rd_QGLViewer-2.6.3/constraint.h | 338 -- code/3rd_QGLViewer-2.6.3/domUtils.h | 161 - code/3rd_QGLViewer-2.6.3/frame.cpp | 1144 ----- code/3rd_QGLViewer-2.6.3/frame.h | 415 -- .../keyFrameInterpolator.cpp | 714 --- .../keyFrameInterpolator.h | 357 -- .../manipulatedCameraFrame.cpp | 469 -- .../manipulatedCameraFrame.h | 233 - code/3rd_QGLViewer-2.6.3/manipulatedFrame.cpp | 550 --- code/3rd_QGLViewer-2.6.3/manipulatedFrame.h | 335 -- code/3rd_QGLViewer-2.6.3/mouseGrabber.cpp | 76 - code/3rd_QGLViewer-2.6.3/mouseGrabber.h | 264 -- code/3rd_QGLViewer-2.6.3/qglviewer.cpp | 3886 --------------- code/3rd_QGLViewer-2.6.3/qglviewer.h | 1300 ----- code/3rd_QGLViewer-2.6.3/quaternion.cpp | 552 --- code/3rd_QGLViewer-2.6.3/quaternion.h | 311 -- code/3rd_QGLViewer-2.6.3/saveSnapshot.cpp | 638 --- code/3rd_QGLViewer-2.6.3/vec.cpp | 164 - code/3rd_QGLViewer-2.6.3/vec.h | 390 -- code/3rd_QGLViewer/CHANGELOG | 6 + code/3rd_QGLViewer/CMakeLists.txt | 42 + code/3rd_QGLViewer/LICENCE | 1040 ++++ .../3rd_QGLViewer/QGLViewer/ImageInterface.ui | 297 ++ code/3rd_QGLViewer/QGLViewer/QGLViewer.pro | 318 ++ code/3rd_QGLViewer/QGLViewer/QGLViewer.vcproj | 493 ++ .../QGLViewer/VRender/AxisAlignedBox.h | 57 + .../QGLViewer}/VRender/BSPSortMethod.cpp | 130 +- .../VRender/BackFaceCullingOptimizer.cpp | 41 + .../QGLViewer}/VRender/EPSExporter.cpp | 48 +- .../QGLViewer/VRender/Exporter.cpp | 64 + .../QGLViewer}/VRender/Exporter.h | 44 - .../QGLViewer/VRender/FIGExporter.cpp | 125 + .../QGLViewer}/VRender/NVector3.cpp | 44 - .../QGLViewer}/VRender/NVector3.h | 44 - .../QGLViewer/VRender/Optimizer.h | 47 + .../QGLViewer}/VRender/ParserGL.cpp | 58 +- .../QGLViewer/VRender/ParserGL.h | 44 + .../QGLViewer}/VRender/Primitive.cpp | 44 - .../QGLViewer}/VRender/Primitive.h | 44 - .../VRender/PrimitivePositioning.cpp | 101 +- .../QGLViewer}/VRender/PrimitivePositioning.h | 44 - .../QGLViewer/VRender/SortMethod.h | 58 + .../VRender/TopologicalSortMethod.cpp | 50 +- code/3rd_QGLViewer/QGLViewer/VRender/Types.h | 30 + .../QGLViewer}/VRender/VRender.cpp | 75 +- .../QGLViewer}/VRender/VRender.h | 44 - .../QGLViewer}/VRender/Vector2.cpp | 53 - .../QGLViewer}/VRender/Vector2.h | 44 - .../QGLViewer}/VRender/Vector3.cpp | 54 - .../QGLViewer}/VRender/Vector3.h | 44 - .../VRender/VisibilityOptimizer.cpp | 64 +- .../3rd_QGLViewer/QGLViewer/VRender/copyright | 22 + .../QGLViewer}/VRender/gpc.cpp | 252 +- .../QGLViewer}/VRender/gpc.h | 44 - .../QGLViewer}/VRenderInterface.ui | 0 code/3rd_QGLViewer/QGLViewer/camera.cpp | 2244 +++++++++ code/3rd_QGLViewer/QGLViewer/camera.h | 550 +++ code/3rd_QGLViewer/QGLViewer/config.h | 77 + code/3rd_QGLViewer/QGLViewer/constraint.cpp | 258 + code/3rd_QGLViewer/QGLViewer/constraint.h | 345 ++ code/3rd_QGLViewer/QGLViewer/domUtils.h | 170 + code/3rd_QGLViewer/QGLViewer/frame.cpp | 1079 +++++ code/3rd_QGLViewer/QGLViewer/frame.h | 437 ++ .../QGLViewer/keyFrameInterpolator.cpp | 689 +++ .../QGLViewer/keyFrameInterpolator.h | 358 ++ .../QGLViewer/manipulatedCameraFrame.cpp | 502 ++ .../QGLViewer/manipulatedCameraFrame.h | 241 + .../QGLViewer/manipulatedFrame.cpp | 565 +++ .../QGLViewer/manipulatedFrame.h | 353 ++ code/3rd_QGLViewer/QGLViewer/mouseGrabber.cpp | 51 + code/3rd_QGLViewer/QGLViewer/mouseGrabber.h | 276 ++ .../QGLViewer}/qglviewer-icon.xpm | 0 code/3rd_QGLViewer/QGLViewer/qglviewer.cpp | 4217 +++++++++++++++++ code/3rd_QGLViewer/QGLViewer/qglviewer.h | 1450 ++++++ .../QGLViewer}/qglviewer.icns | Bin code/3rd_QGLViewer/QGLViewer/qglviewer.spec | 490 ++ .../QGLViewer}/qglviewer_fr.qm | Bin .../QGLViewer}/qglviewer_fr.ts | 2 +- code/3rd_QGLViewer/QGLViewer/quaternion.cpp | 503 ++ code/3rd_QGLViewer/QGLViewer/quaternion.h | 316 ++ code/3rd_QGLViewer/QGLViewer/saveSnapshot.cpp | 641 +++ code/3rd_QGLViewer/QGLViewer/vec.cpp | 142 + code/3rd_QGLViewer/QGLViewer/vec.h | 362 ++ code/3rd_QGLViewer/README | 41 + code/3rd_QGLViewer/README.md | 11 + code/CMakeLists.txt | 13 +- code/PolyFit/CMakeLists.txt | 71 +- code/PolyFit/dlg/weight_panel_click.cpp | 10 +- code/PolyFit/dlg/weight_panel_click.h | 5 +- code/PolyFit/dlg/weight_panel_manual.cpp | 8 +- code/PolyFit/dlg/weight_panel_manual.h | 5 +- code/PolyFit/dlg/wgt_render.cpp | 13 +- code/PolyFit/dlg/wgt_render.h | 5 +- code/PolyFit/dlg/wgt_render.ui | 30 +- code/PolyFit/main.cpp | 22 +- code/PolyFit/main_window.cpp | 37 +- code/PolyFit/main_window.h | 7 +- code/PolyFit/paint_canvas.cpp | 135 +- code/PolyFit/paint_canvas.h | 9 +- code/cmake/FindGUROBI.cmake | 18 +- code/cmake/UseQt.cmake | 75 + code/model/kdtree/PriorityQueue.h | 12 +- code/model/kdtree/kdTree.cpp | 24 +- 122 files changed, 19476 insertions(+), 18371 deletions(-) delete mode 100644 code/3rd_QGLViewer-2.6.3/CMakeLists.txt delete mode 100644 code/3rd_QGLViewer-2.6.3/ImageInterface.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/ImageInterface.h delete mode 100644 code/3rd_QGLViewer-2.6.3/ImageInterface.ui delete mode 100644 code/3rd_QGLViewer-2.6.3/VRender/AxisAlignedBox.h delete mode 100644 code/3rd_QGLViewer-2.6.3/VRender/BackFaceCullingOptimizer.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/VRender/Exporter.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/VRender/FIGExporter.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/VRender/Optimizer.h delete mode 100644 code/3rd_QGLViewer-2.6.3/VRender/ParserGL.h delete mode 100644 code/3rd_QGLViewer-2.6.3/VRender/SortMethod.h delete mode 100644 code/3rd_QGLViewer-2.6.3/VRender/Types.h delete mode 100644 code/3rd_QGLViewer-2.6.3/camera.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/camera.h delete mode 100644 code/3rd_QGLViewer-2.6.3/config.h delete mode 100644 code/3rd_QGLViewer-2.6.3/constraint.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/constraint.h delete mode 100644 code/3rd_QGLViewer-2.6.3/domUtils.h delete mode 100644 code/3rd_QGLViewer-2.6.3/frame.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/frame.h delete mode 100644 code/3rd_QGLViewer-2.6.3/keyFrameInterpolator.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/keyFrameInterpolator.h delete mode 100644 code/3rd_QGLViewer-2.6.3/manipulatedCameraFrame.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/manipulatedCameraFrame.h delete mode 100644 code/3rd_QGLViewer-2.6.3/manipulatedFrame.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/manipulatedFrame.h delete mode 100644 code/3rd_QGLViewer-2.6.3/mouseGrabber.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/mouseGrabber.h delete mode 100644 code/3rd_QGLViewer-2.6.3/qglviewer.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/qglviewer.h delete mode 100644 code/3rd_QGLViewer-2.6.3/quaternion.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/quaternion.h delete mode 100644 code/3rd_QGLViewer-2.6.3/saveSnapshot.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/vec.cpp delete mode 100644 code/3rd_QGLViewer-2.6.3/vec.h create mode 100644 code/3rd_QGLViewer/CHANGELOG create mode 100644 code/3rd_QGLViewer/CMakeLists.txt create mode 100644 code/3rd_QGLViewer/LICENCE create mode 100644 code/3rd_QGLViewer/QGLViewer/ImageInterface.ui create mode 100644 code/3rd_QGLViewer/QGLViewer/QGLViewer.pro create mode 100644 code/3rd_QGLViewer/QGLViewer/QGLViewer.vcproj create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/AxisAlignedBox.h rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/BSPSortMethod.cpp (75%) create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/BackFaceCullingOptimizer.cpp rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/EPSExporter.cpp (81%) create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/Exporter.cpp rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/Exporter.h (63%) create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/FIGExporter.cpp rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/NVector3.cpp (65%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/NVector3.h (60%) create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/Optimizer.h rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/ParserGL.cpp (84%) create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/ParserGL.h rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/Primitive.cpp (56%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/Primitive.h (72%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/PrimitivePositioning.cpp (81%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/PrimitivePositioning.h (53%) create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/SortMethod.h rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/TopologicalSortMethod.cpp (91%) create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/Types.h rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/VRender.cpp (68%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/VRender.h (51%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/Vector2.cpp (54%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/Vector2.h (67%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/Vector3.cpp (62%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/Vector3.h (70%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/VisibilityOptimizer.cpp (84%) create mode 100644 code/3rd_QGLViewer/QGLViewer/VRender/copyright rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/gpc.cpp (92%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRender/gpc.h (71%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/VRenderInterface.ui (100%) create mode 100644 code/3rd_QGLViewer/QGLViewer/camera.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/camera.h create mode 100644 code/3rd_QGLViewer/QGLViewer/config.h create mode 100644 code/3rd_QGLViewer/QGLViewer/constraint.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/constraint.h create mode 100644 code/3rd_QGLViewer/QGLViewer/domUtils.h create mode 100644 code/3rd_QGLViewer/QGLViewer/frame.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/frame.h create mode 100644 code/3rd_QGLViewer/QGLViewer/keyFrameInterpolator.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/keyFrameInterpolator.h create mode 100644 code/3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.h create mode 100644 code/3rd_QGLViewer/QGLViewer/manipulatedFrame.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/manipulatedFrame.h create mode 100644 code/3rd_QGLViewer/QGLViewer/mouseGrabber.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/mouseGrabber.h rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/qglviewer-icon.xpm (100%) create mode 100644 code/3rd_QGLViewer/QGLViewer/qglviewer.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/qglviewer.h rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/qglviewer.icns (100%) create mode 100644 code/3rd_QGLViewer/QGLViewer/qglviewer.spec rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/qglviewer_fr.qm (100%) rename code/{3rd_QGLViewer-2.6.3 => 3rd_QGLViewer/QGLViewer}/qglviewer_fr.ts (99%) create mode 100644 code/3rd_QGLViewer/QGLViewer/quaternion.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/quaternion.h create mode 100644 code/3rd_QGLViewer/QGLViewer/saveSnapshot.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/vec.cpp create mode 100644 code/3rd_QGLViewer/QGLViewer/vec.h create mode 100644 code/3rd_QGLViewer/README create mode 100644 code/3rd_QGLViewer/README.md create mode 100644 code/cmake/UseQt.cmake diff --git a/.gitattributes b/.gitattributes index 5f42182c..07a59b1a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -18,6 +18,6 @@ 3rd_lpsolve/* linguist-vendored -3rd_QGLViewer-2.6.3/* linguist-vendored +3rd_QGLViewer/* linguist-vendored *.h linguist-language=C++ *.c linguist-language=C++ diff --git a/.gitignore b/.gitignore index db7db4b5..946e88a8 100644 --- a/.gitignore +++ b/.gitignore @@ -50,8 +50,8 @@ Temporary Items x64 .vs/PolyFit/v15/.suo -3rd_QGLViewer-2.6.3/3rd_qglviewer.vcxproj.user -3rd_QGLViewer-2.6.3/GeneratedFiles +3rd_QGLViewer/3rd_qglviewer.vcxproj.user +3rd_QGLViewer/GeneratedFiles PolyFit/GeneratedFiles PolyFit/PolyFit.log PolyFit/PolyFit.vcxproj.user diff --git a/ReadMe.md b/ReadMe.md index 6b730406..446965ad 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -27,7 +27,10 @@ You can also build PolyFit from the source code: * Clone the repository or download the [source code](https://github.com/LiangliangNan/PolyFit). * Dependencies - - [Qt5](https://www.qt.io/) (v5.12.12, v5.10.1, v5.9.2, and v5.8.0 were tested). Qt is required by the [GUI demo](./code/PolyFit). You should still be able to build the [command-line example](./Example) without Qt. + - [Qt5 or Qt6](https://www.qt.io/download-qt-installer-oss) ([v5.14.2](https://download.qt.io/archive/qt/5.14/5.14.2/), + [v5.12.12](https://download.qt.io/archive/qt/5.12/5.12.12/), [v6.7.3](https://www.qt.io/download-qt-installer-oss) + have been tested). Qt is required by the [GUI demo](./code/PolyFit). You should still be able to build + the [command-line example](./Example) without Qt. - [CGAL](http://www.cgal.org/index.html) (v6.0, v5.5, v5.0, v4.11.1, and v4.10 were tested). * Build PolyFit diff --git a/code/3rd_QGLViewer-2.6.3/CMakeLists.txt b/code/3rd_QGLViewer-2.6.3/CMakeLists.txt deleted file mode 100644 index 16a4dd39..00000000 --- a/code/3rd_QGLViewer-2.6.3/CMakeLists.txt +++ /dev/null @@ -1,153 +0,0 @@ -get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) -project(${PROJECT_NAME}) - -# ------------------------------------------------------------------------------ -# Qt -# ------------------------------------------------------------------------------ - -## we will use cmake autouic/automoc/autorcc features -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set( QT5_ROOT_PATH CACHE PATH "Qt5 root directory (i.e. where the 'bin' folder lies)" ) -if ( QT5_ROOT_PATH ) - list( APPEND CMAKE_PREFIX_PATH ${QT5_ROOT_PATH} ) -endif() - -# find qt5 components -find_package(Qt5 COMPONENTS Core Gui OpenGL Widgets Xml QUIET) -if (Qt5Core_FOUND AND Qt5Gui_FOUND AND Qt5OpenGL_FOUND AND Qt5Widgets_FOUND AND Qt5Xml_FOUND) - set(QT5_FOUND TRUE) -else() - return() -endif () - -# in the case no Qt5Config.cmake file could be found, cmake will explicitly ask the user for the QT5_DIR containing it! -# thus no need to keep additional variables and checks - -# Starting with the QtCore lib, find the bin and root directories -get_target_property(QT5_LIB_LOCATION Qt5::Core LOCATION_${CMAKE_BUILD_TYPE}) -get_filename_component(QT_BINARY_DIR ${QT5_LIB_LOCATION} DIRECTORY) - -# Apple uses frameworks - move up until we get to the base directory to set the bin directory properly -if ( APPLE ) - get_filename_component(QT_BINARY_DIR ${QT_BINARY_DIR} DIRECTORY) - set(QT_BINARY_DIR "${QT_BINARY_DIR}/bin") - - set( MACDEPLOYQT "${QT_BINARY_DIR}/macdeployqt" ) -endif() - -# set QT5_ROOT_PATH if it wasn't set by the user -if ( NOT QT5_ROOT_PATH ) - get_filename_component(QT5_ROOT_PATH ${QT_BINARY_DIR} DIRECTORY) -endif() - -include_directories(${Qt5OpenGL_INCLUDE_DIRS} - ${Qt5Widgets_INCLUDE_DIRS} - ${Qt5Core_INCLUDE_DIRS} - ${Qt5Gui_INCLUDE_DIRS} - ${Qt5Xml_INCLUDE_DIRS} - ) - -# turn on QStringBuilder for more efficient string construction -# see https://doc.qt.io/qt-5/qstring.html#more-efficient-string-construction -add_definitions( -DQT_USE_QSTRINGBUILDER ) - - -set(qglviewer_HEADERS - camera.h - config.h - constraint.h - domUtils.h - frame.h - ImageInterface.h - keyFrameInterpolator.h - manipulatedCameraFrame.h - manipulatedFrame.h - mouseGrabber.h - qglviewer.h - quaternion.h - vec.h - VRender/AxisAlignedBox.h - VRender/Exporter.h - VRender/gpc.h - VRender/NVector3.h - VRender/Optimizer.h - VRender/ParserGL.h - VRender/Primitive.h - VRender/PrimitivePositioning.h - VRender/SortMethod.h - VRender/Types.h - VRender/Vector2.h - VRender/Vector3.h - VRender/VRender.h - ) - -set(qglviewer_SOURCES - camera.cpp - constraint.cpp - frame.cpp - ImageInterface.cpp - keyFrameInterpolator.cpp - manipulatedCameraFrame.cpp - manipulatedFrame.cpp - mouseGrabber.cpp - qglviewer.cpp - quaternion.cpp - saveSnapshot.cpp - vec.cpp - VRender/BackFaceCullingOptimizer.cpp - VRender/BSPSortMethod.cpp - VRender/EPSExporter.cpp - VRender/Exporter.cpp - VRender/FIGExporter.cpp - VRender/gpc.cpp - VRender/NVector3.cpp - VRender/ParserGL.cpp - VRender/Primitive.cpp - VRender/PrimitivePositioning.cpp - VRender/TopologicalSortMethod.cpp - VRender/Vector2.cpp - VRender/Vector3.cpp - VRender/VisibilityOptimizer.cpp - VRender/VRender.cpp - ) - - -add_library(${PROJECT_NAME} SHARED ${qglviewer_SOURCES} ${qglviewer_HEADERS}) -set_target_properties(${PROJECT_NAME} PROPERTIES - FOLDER "3rd_party") - - -target_include_directories(${PROJECT_NAME} PRIVATE - ${POLYFIT_qglviewer_DIR}/shared - ${POLYFIT_qglviewer_DIR}/bfp - ${POLYFIT_qglviewer_DIR}/bfp/bfp_LUSOL - ${POLYFIT_qglviewer_DIR}/bfp/bfp_LUSOL/LUSOL - ${POLYFIT_qglviewer_DIR}/colamd - ) - - -target_compile_definitions(${PROJECT_NAME} PRIVATE CREATE_QGLVIEWER_DLL) - -if (MSVC) - target_compile_definitions(${PROJECT_NAME} PRIVATE - _CRT_SECURE_NO_WARNINGS - _CRT_SECURE_NO_DEPRECATE - ) -endif() - - -target_link_libraries( ${PROJECT_NAME} Qt5::Core ) -target_link_libraries( ${PROJECT_NAME} Qt5::Gui ) -target_link_libraries( ${PROJECT_NAME} Qt5::Widgets ) -target_link_libraries( ${PROJECT_NAME} Qt5::OpenGL ) -target_link_libraries( ${PROJECT_NAME} Qt5::Xml ) - - -set (OpenGL_GL_PREFERENCE GLVND) -find_package(OpenGL REQUIRED) -message(STATUS "OpenGL libraries: ${OPENGL_LIBRARIES}") -target_link_libraries( ${PROJECT_NAME} ${OPENGL_LIBRARIES} ) diff --git a/code/3rd_QGLViewer-2.6.3/ImageInterface.cpp b/code/3rd_QGLViewer-2.6.3/ImageInterface.cpp deleted file mode 100644 index eb5b30a1..00000000 --- a/code/3rd_QGLViewer-2.6.3/ImageInterface.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/**************************************************************************** - -Copyright (C) 2002-2013 Gilles Debunne. All rights reserved. - -This file is part of the QGLViewer library version 2.4.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. - -*****************************************************************************/ - -#include "qglviewer.h" -#include "ImageInterface.h" - - -ImageInterface::ImageInterface(QWidget *parent) -: QDialog(parent), scale_(1) -{ - setupUi(this); - imgScale->setValue(scale_); - viewer_ = dynamic_cast(parent); - - imgHeight->setValue(viewer_->height() * scale_); - imgWidth->setValue(viewer_->width() * scale_); - - connect(imgScale, SIGNAL(valueChanged(int)), this, SLOT(scaleChanged(int))); -} - -void ImageInterface::scaleChanged(int s) { - scale_ = s; - int h = viewer_->height() * scale_; - int w = viewer_->width() * scale_; - - imgHeight->setValue(h); - imgWidth->setValue(w); -} \ No newline at end of file diff --git a/code/3rd_QGLViewer-2.6.3/ImageInterface.h b/code/3rd_QGLViewer-2.6.3/ImageInterface.h deleted file mode 100644 index cfd41914..00000000 --- a/code/3rd_QGLViewer-2.6.3/ImageInterface.h +++ /dev/null @@ -1,46 +0,0 @@ -/**************************************************************************** - -Copyright (C) 2002-2013 Gilles Debunne. All rights reserved. - -This file is part of the QGLViewer library version 2.4.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. - -*****************************************************************************/ - -#ifndef _SAVE_SNAPSHOT_H_ -#define _SAVE_SNAPSHOT_H_ - -#include "qglviewer.h" -#include "ui_ImageInterface.h" - - -class QGLVIEWER_EXPORT ImageInterface: public QDialog, public Ui::ImageInterface -{ - Q_OBJECT -public: - ImageInterface(QWidget *parent) ; - -private Q_SLOTS: - void scaleChanged(int s); - -private: - QGLViewer* viewer_; - - int scale_; -}; - -#endif - diff --git a/code/3rd_QGLViewer-2.6.3/ImageInterface.ui b/code/3rd_QGLViewer-2.6.3/ImageInterface.ui deleted file mode 100644 index 00d4b117..00000000 --- a/code/3rd_QGLViewer-2.6.3/ImageInterface.ui +++ /dev/null @@ -1,386 +0,0 @@ - - - ImageInterface - - - - 0 - 0 - 266 - 391 - - - - Image settings - - - - ../../../../../../Documents and Settings/debunne/Bureau/libQGLViewer-2.2.0-1/doc/images/qglviewer.icon.png../../../../../../Documents and Settings/debunne/Bureau/libQGLViewer-2.2.0-1/doc/images/qglviewer.icon.png - - - - - - Size - - - - - - Width - - - - - - - - 60 - 0 - - - - - 200 - 16777215 - - - - Width of the image (in pixels) - - - Qt::AlignCenter - - - px - - - 1 - - - 32000 - - - - - - - Height - - - - - - - - 60 - 0 - - - - - 200 - 16777215 - - - - Height of the image (in pixels) - - - Qt::AlignCenter - - - px - - - 1 - - - 32000 - - - - - - - Qt::Horizontal - - - - - - - Scale - - - - - - - Scale image - - - Qt::AlignCenter - - - x - - - 1 - - - 10 - - - 1 - - - - - - - - - - Qt::Vertical - - - - 17 - 13 - - - - - - - - Quality - - - - - - Image quality - - - - - - - - 60 - 0 - - - - - 200 - 16777215 - - - - Between 0 (smallest files) and 100 (highest quality) - - - Qt::AlignCenter - - - 0 - - - 100 - - - 100 - - - - - - - Oversampling - - - - - - - - 60 - 0 - - - - - 200 - 16777215 - - - - Antialiases image (when larger then 1.0) - - - Qt::AlignCenter - - - x - - - 0 - - - 0.000000000000000 - - - 64.000000000000000 - - - 1.000000000000000 - - - 1.000000000000000 - - - - - - - - - - Qt::Vertical - - - - 17 - 13 - - - - - - - - Options - - - - - - Use white as background color - - - Use white background - - - true - - - - - - - When image aspect ratio differs from viewer's one, expand frustum as needed. Fits inside current frustum otherwise. - - - Expand frustum if needed - - - - - - - - - - Qt::Vertical - - - - 17 - 13 - - - - - - - - - - Qt::Horizontal - - - - 80 - 22 - - - - - - - - OK - - - - - - - Cancel - - - - - - - - - imgWidth - imgHeight - imgScale - imgQuality - oversampling - whiteBackground - expandFrustum - okButton - cancelButton - - - - - okButton - clicked() - ImageInterface - accept() - - - 135 - 184 - - - 96 - 254 - - - - - cancelButton - clicked() - ImageInterface - reject() - - - 226 - 184 - - - 179 - 282 - - - - - diff --git a/code/3rd_QGLViewer-2.6.3/VRender/AxisAlignedBox.h b/code/3rd_QGLViewer-2.6.3/VRender/AxisAlignedBox.h deleted file mode 100644 index db401fd4..00000000 --- a/code/3rd_QGLViewer-2.6.3/VRender/AxisAlignedBox.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef _VRENDER_AXISALIGNEDBOX_H -#define _VRENDER_AXISALIGNEDBOX_H - -namespace vrender -{ - class Vector2; - class Vector3; - - template class AxisAlignedBox - { - public: - AxisAlignedBox() ; - AxisAlignedBox(const T& v) ; - AxisAlignedBox(const T& v,const T& w) ; - - const T& mini() const { return _min ; } - const T& maxi() const { return _max ; } - - void include(const T& v) ; - void include(const AxisAlignedBox& b) ; - private: - T _min ; - T _max ; - }; - - typedef AxisAlignedBox< Vector2 > AxisAlignedBox_xy ; - typedef AxisAlignedBox< Vector3 > AxisAlignedBox_xyz ; - - template AxisAlignedBox::AxisAlignedBox() - : _min(T::inf), _max(-T::inf) - { - } - - template AxisAlignedBox::AxisAlignedBox(const T& v) - : _min(v), _max(v) - { - } - - template AxisAlignedBox::AxisAlignedBox(const T& v,const T& w) - : _min(v), _max(v) - { - include(w) ; - } - - template void AxisAlignedBox::include(const T& v) - { - _min = T::mini(_min,v) ; - _max = T::maxi(_max,v) ; - } - - template void AxisAlignedBox::include(const AxisAlignedBox& b) - { - include(b._min) ; - include(b._max) ; - } -} -#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/BackFaceCullingOptimizer.cpp b/code/3rd_QGLViewer-2.6.3/VRender/BackFaceCullingOptimizer.cpp deleted file mode 100644 index 3cc08689..00000000 --- a/code/3rd_QGLViewer-2.6.3/VRender/BackFaceCullingOptimizer.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include -#include "VRender.h" -#include "Optimizer.h" -#include "Primitive.h" - -using namespace std ; -using namespace vrender ; - -// Over-simplified algorithm to check wether a polygon is front-facing or not. -// Only works for convex polygons. - -void BackFaceCullingOptimizer::optimize(std::vector& primitives_tab,VRenderParams&) -{ - Polygone *P ; - int nb_culled = 0 ; - - for(size_t i=0;i(primitives_tab[i])) != NULL) - { - for(unsigned int j=0;jnbVertices();++j) - if(( (P->vertex(j+2) - P->vertex(j+1))^(P->vertex(j+1) - P->vertex(j))).z() > 0.0 ) - { - delete primitives_tab[i] ; - primitives_tab[i] = NULL ; - ++nb_culled ; - break ; - } - } - - // Rule out gaps. This avoids testing for null primitives later. - - int j=0 ; - for(size_t k=0;k -#include - -using namespace vrender ; -using namespace std ; - -Exporter::Exporter() -{ - _xmin=_xmax=_ymin=_ymax=_zmin=_zmax = 0.0 ; - _pointSize=1 ; -} - -void Exporter::exportToFile(const QString& filename, - const vector& primitive_tab, - VRenderParams& vparams) -{ - QFile file(filename); - - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::warning(NULL, QGLViewer::tr("Exporter error", "Message box window title"), QGLViewer::tr("Unable to open file %1.").arg(filename)); - return; - } - - QTextStream out(&file); - - writeHeader(out) ; - - unsigned int N = primitive_tab.size()/200 + 1 ; - - for(unsigned int i=0;i(primitive_tab[i]) ; - Segment *s = dynamic_cast(primitive_tab[i]) ; - Polygone *P = dynamic_cast(primitive_tab[i]) ; - - if(p != NULL) spewPoint(p,out) ; - if(s != NULL) spewSegment(s,out) ; - if(P != NULL) spewPolygone(P,out) ; - - if(i%N == 0) - vparams.progress(i/(float)primitive_tab.size(),QGLViewer::tr("Exporting to file %1").arg(filename)) ; - } - - writeFooter(out) ; - - file.close(); -} - -void Exporter::setBoundingBox(float xmin,float ymin,float xmax,float ymax) -{ - _xmin = xmin ; - _ymin = ymin ; - _xmax = xmax ; - _ymax = ymax ; -} - -void Exporter::setClearColor(float r, float g, float b) { _clearR=r; _clearG=g; _clearB=b; } -void Exporter::setClearBackground(bool b) { _clearBG=b; } -void Exporter::setBlackAndWhite(bool b) { _blackAndWhite = b; } - diff --git a/code/3rd_QGLViewer-2.6.3/VRender/FIGExporter.cpp b/code/3rd_QGLViewer-2.6.3/VRender/FIGExporter.cpp deleted file mode 100644 index b7411586..00000000 --- a/code/3rd_QGLViewer-2.6.3/VRender/FIGExporter.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "Exporter.h" -#include "math.h" - -using namespace vrender ; -using namespace std ; - -int FIGExporter::FigCoordX(double x) const -{ - float MaxX = 12000 ; - float MaxY = MaxX * _sizeY/(float)_sizeX ; - - if(MaxY > 7000) - { - MaxX *= 7000/(float)MaxY ; - MaxY = 7000 ; - } - - return int(0.5f + x/_sizeX*MaxX) ; -} - -int FIGExporter::FigCoordY(double y) const -{ - float MaxX = 12000 ; - float MaxY = MaxX * _sizeY/(float)_sizeX ; - - if(MaxY > 7000) - { - MaxX *= 7000/(float)MaxY ; - MaxY = 7000 ; - } - - return int(0.5f + (1.0 - y/_sizeY)*MaxY) ; -} - -int FIGExporter::FigGrayScaleIndex(float red, float green, float blue) const -{ - float intensity = 0.3f*red+0.6f*green+0.1f*blue ; - - return int(intensity * 20.0) ; -} - -FIGExporter::FIGExporter() -{ -} - -void FIGExporter::writeHeader(QTextStream& out) const -{ - out << "#FIG 3.2\nPortrait\nCenter\nInches\nLetter\n100.00\nSingle\n0\n1200 2\n"; - _depth = 999 ; - _sizeX = int(0.5f + _xmax - _xmin) ; - _sizeY = int(0.5f + _ymax - _ymin) ; -} - -void FIGExporter::writeFooter(QTextStream& out) const -{ - Q_UNUSED(out); -} - -void FIGExporter::spewPoint(const Point *P, QTextStream& out) -{ - out << "2 1 0 5 0 7 " << (_depth--) << " 0 -1 0.000 0 1 -1 0 0 1\n"; - - out << "\t " << FigCoordX(P->vertex(0)[0]) << " " << FigCoordY(P->vertex(0)[1]) << "\n"; - if(_depth > 0) _depth = 0 ; -} - -void FIGExporter::spewSegment(const Segment *S, QTextStream& out) -{ - const Feedback3DColor& P1 = Feedback3DColor(S->sommet3DColor(0)) ; - const Feedback3DColor& P2 = Feedback3DColor(S->sommet3DColor(1)) ; - - GLdouble dx, dy; - GLfloat dr, dg, db, absR, absG, absB, colormax; - int steps; - GLdouble xstep, ystep; - GLfloat rstep, gstep, bstep; - GLdouble xnext, ynext, distance; - GLfloat rnext, gnext, bnext; - - dr = P2.red() - P1.red(); - dg = P2.green() - P1.green(); - db = P2.blue() - P1.blue(); - - if (dr != 0 || dg != 0 || db != 0) - { - /* Smooth shaded line. */ - - dx = P2.x() - P1.x(); - dy = P2.y() - P1.y(); - - distance = sqrt(dx * dx + dy * dy); - - absR = fabs(dr); - absG = fabs(dg); - absB = fabs(db); - - colormax = max(absR, max(absG, absB)); - steps = int(0.5f + max(1.0, colormax * distance * EPS_SMOOTH_LINE_FACTOR)); - - xstep = dx / steps; - ystep = dy / steps; - - rstep = dr / steps; - gstep = dg / steps; - bstep = db / steps; - - xnext = P1.x(); - ynext = P1.y(); - rnext = P1.red(); - gnext = P1.green(); - bnext = P1.blue(); - - /* Back up half a step; we want the end points to be - exactly the their endpoint colors. */ - - xnext -= xstep / 2.0; - ynext -= ystep / 2.0; - rnext -= rstep / 2.0f; - gnext -= gstep / 2.0f; - bnext -= bstep / 2.0f; - } - else - { - /* Single color line. */ - steps = 0; - } - - out << "2 1 0 1 0 7 " << (_depth--) << " 0 -1 0.000 0 0 -1 0 0 2\n"; - out << "\t " << FigCoordX(P1.x()) << " " << FigCoordY(P1.y()); - - out << " " << FigCoordX(P2.x()) << " " << FigCoordY(P2.y())<< "\n"; - if(_depth > 0) _depth = 0 ; -} - -void FIGExporter::spewPolygone(const Polygone *P, QTextStream& out) -{ - int nvertices; - GLfloat red, green, blue; - - nvertices = P->nbVertices() ; - - Feedback3DColor vertex(P->sommet3DColor(0)) ; - - if (nvertices > 0) - { - red = 0 ; - green = 0 ; - blue = 0 ; - - for(int i = 0; i < nvertices; i++) - { - red += P->sommet3DColor(i).red() ; - green += P->sommet3DColor(i).green() ; - blue += P->sommet3DColor(i).blue() ; - } - - red /= nvertices ; - green /= nvertices ; - blue /= nvertices ; - - /* Flat shaded polygon; all vertex colors the same. */ - - if(_blackAndWhite) - out << "2 3 0 0 0 7 " << (_depth--) << " 0 20 0.000 0 0 -1 0 0 " << (nvertices+1) << "\n"; - else - out << "2 3 0 0 0 7 " << (_depth--) << " 0 " << (FigGrayScaleIndex(red,green,blue)) << " 0.000 0 0 -1 0 0 " << (nvertices+1) << "\n"; - - /* Draw a filled triangle. */ - - out << "\t"; - - for (int j = 0; j < nvertices; j++) - out << " " << FigCoordX(P->sommet3DColor(j).x()) << " " << FigCoordY(P->sommet3DColor(j).y()); - - out << " " << FigCoordX(P->sommet3DColor(0).x()) << " " << FigCoordY(P->sommet3DColor(0).y()) << "\n"; - } - - if(_depth > 0) _depth = 0 ; -} - - diff --git a/code/3rd_QGLViewer-2.6.3/VRender/Optimizer.h b/code/3rd_QGLViewer-2.6.3/VRender/Optimizer.h deleted file mode 100644 index be344b4d..00000000 --- a/code/3rd_QGLViewer-2.6.3/VRender/Optimizer.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef _OPTIMIZER_H -#define _OPTIMIZER_H - -#include "Types.h" - -namespace vrender -{ - // Implements some global optimizations on the polygon sorting. - - class VRenderParams ; - class Optimizer - { - public: - virtual void optimize(std::vector&,VRenderParams&) = 0 ; - virtual ~Optimizer() {} ; - }; - - // Optimizes visibility by culling primitives which do not appear in the - // rendered image. Computations are done analytically rather than using an item - // buffer. - - class VisibilityOptimizer: public Optimizer - { - public: - virtual void optimize(std::vector&,VRenderParams&) ; - virtual ~VisibilityOptimizer() {} ; - }; - - // Optimizes by collapsing together primitives which can be, without - // perturbating the back to front painting algorithm. - - class PrimitiveSplitOptimizer: public Optimizer - { - public: - virtual void optimize(std::vector&,VRenderParams&) {} - virtual ~PrimitiveSplitOptimizer() {} ; - }; - - class BackFaceCullingOptimizer: public Optimizer - { - public: - virtual void optimize(std::vector&,VRenderParams&) ; - virtual ~BackFaceCullingOptimizer() {} ; - }; -} - -#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/ParserGL.h b/code/3rd_QGLViewer-2.6.3/VRender/ParserGL.h deleted file mode 100644 index 2374a787..00000000 --- a/code/3rd_QGLViewer-2.6.3/VRender/ParserGL.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef _VRENDER_PARSERGL_H -#define _VRENDER_PARSERGL_H - -// This class implements the conversion from OpenGL feedback buffer into more -// usable data structures such as points, segments, and polygons (See Primitive.h) - -#include -#include "Primitive.h" - -namespace vrender -{ - class ParserGL - { - public: - void parseFeedbackBuffer( GLfloat *, - int size, - std::vector& primitive_tab, - VRenderParams& vparams) ; - void printStats() const ; - - inline GLfloat xmin() const { return _xmin ; } - inline GLfloat ymin() const { return _ymin ; } - inline GLfloat zmin() const { return _zmin ; } - inline GLfloat xmax() const { return _xmax ; } - inline GLfloat ymax() const { return _ymax ; } - inline GLfloat zmax() const { return _zmax ; } - private: - int nb_lines ; - int nb_polys ; - int nb_points ; - int nb_degenerated_lines ; - int nb_degenerated_polys ; - int nb_degenerated_points ; - - GLfloat _xmin ; - GLfloat _ymin ; - GLfloat _zmin ; - GLfloat _xmax ; - GLfloat _ymax ; - GLfloat _zmax ; - }; -} - -#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/SortMethod.h b/code/3rd_QGLViewer-2.6.3/VRender/SortMethod.h deleted file mode 100644 index e326842c..00000000 --- a/code/3rd_QGLViewer-2.6.3/VRender/SortMethod.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef _SORTMETHOD_H -#define _SORTMETHOD_H - -#include -#include "Types.h" - -namespace vrender -{ - // Class which implements the sorting of the primitives. An object of - class VRenderParams ; - class SortMethod - { - public: - SortMethod() {} - virtual ~SortMethod() {} - - virtual void sortPrimitives(std::vector&,VRenderParams&) = 0 ; - - void SetZDepth(FLOAT s) { zSize = s ; } - FLOAT ZDepth() const { return zSize ; } - - protected: - FLOAT zSize ; - }; - - class DontSortMethod: public SortMethod - { - public: - DontSortMethod() {} - virtual ~DontSortMethod() {} - - virtual void sortPrimitives(std::vector&,VRenderParams&) {} - }; - - class BSPSortMethod: public SortMethod - { - public: - BSPSortMethod() {} ; - virtual ~BSPSortMethod() {} - - virtual void sortPrimitives(std::vector&,VRenderParams&) ; - }; - - class TopologicalSortMethod: public SortMethod - { - public: - TopologicalSortMethod() ; - virtual ~TopologicalSortMethod() {} - - virtual void sortPrimitives(std::vector&,VRenderParams&) ; - - void setBreakCycles(bool b) { _break_cycles = b ; } - private: - bool _break_cycles ; - }; -} - -#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/Types.h b/code/3rd_QGLViewer-2.6.3/VRender/Types.h deleted file mode 100644 index 561ec559..00000000 --- a/code/3rd_QGLViewer-2.6.3/VRender/Types.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef _VRENDER_TYPES_H -#define _VRENDER_TYPES_H - -#ifdef WIN32 -# include -#endif - -#ifdef __APPLE__ -# include -#else -# include -#endif - - -// Windows DLL export macros -#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64) -// Disable a warning message about dll. This is a temporary solution -// http://support.microsoft.com/default.aspx?scid=kb;EN-US;168958 -#pragma warning( disable : 4251 ) -#pragma warning( disable : 4996 ) -#pragma warning( disable : 4267 ) -#pragma warning( disable : 4091 ) -#pragma warning( disable : 4005 ) -#pragma warning( disable : 4244 ) -#pragma warning( disable : 4101 ) -#endif - - -namespace vrender -{ - typedef double FLOAT ; - typedef GLdouble GLFLOAT ; - -#ifdef A_VOIR - typedef T_Vect3 DVector3 ; - typedef T_Vect2 Vector2 ; -#endif - - class Primitive ; - typedef Primitive *PtrPrimitive ; - - const float FLAT_POLYGON_EPS = 1e-5f ; -} - -#endif diff --git a/code/3rd_QGLViewer-2.6.3/camera.cpp b/code/3rd_QGLViewer-2.6.3/camera.cpp deleted file mode 100644 index 176f0555..00000000 --- a/code/3rd_QGLViewer-2.6.3/camera.cpp +++ /dev/null @@ -1,2213 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "domUtils.h" -#include "camera.h" -#include "qglviewer.h" -#include "manipulatedCameraFrame.h" - -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 - QGLWidget::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 - QGLWidget::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& center) -{ - 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; -} - -float Camera::pixelGLRatio(float x, float y, float z) const { - return pixelGLRatio(Vec(x, y, z)); -} - -/*! 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& center, 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(); -} - -/*! 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); - gluProject(tmp.x,tmp.y,tmp.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z); - } - else - gluProject(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); - gluUnProject(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; -} diff --git a/code/3rd_QGLViewer-2.6.3/camera.h b/code/3rd_QGLViewer-2.6.3/camera.h deleted file mode 100644 index 9cca5030..00000000 --- a/code/3rd_QGLViewer-2.6.3/camera.h +++ /dev/null @@ -1,516 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_CAMERA_H -#define QGLVIEWER_CAMERA_H - -#include "../basic/canvas.h" -#include "keyFrameInterpolator.h" -class QGLViewer; - -namespace qglviewer { - -class ManipulatedCameraFrame; - -/*! \brief A perspective or orthographic camera. - \class Camera camera.h QGLViewer/camera.h - - A Camera defines some intrinsic parameters (fieldOfView(), position(), viewDirection(), - upVector()...) and useful positioning tools that ease its placement (showEntireScene(), - fitSphere(), lookAt()...). It exports its associated OpenGL projection and modelview matrices and - can interactively be modified using the mouse. - -

Mouse manipulation

- - The position() and orientation() of the Camera are defined by a ManipulatedCameraFrame (retrieved - using frame()). These methods are just convenient wrappers to the equivalent Frame methods. This - also means that the Camera frame() can be attached to a Frame::referenceFrame() which enables - complex Camera setups. - - Different displacements can be performed using the mouse. The list of possible actions is defined - by the QGLViewer::MouseAction enum. Use QGLViewer::setMouseBinding() to attach a specific action - to an arbitrary mouse button-state key binding. These actions are detailed in the mouse page. - - The default button binding are: QGLViewer::ROTATE (left), QGLViewer::ZOOM (middle) and - QGLViewer::TRANSLATE (right). With this configuration, the Camera \e observes a scene and rotates - around its pivotPoint(). You can switch between this mode and a fly mode using the - QGLViewer::CAMERA_MODE (see QGLViewer::toggleCameraMode()) keyboard shortcut (default is 'Space'). - -

Other functionalities

- - The type() of the Camera can be Camera::ORTHOGRAPHIC or Camera::PERSPECTIVE (see Type()). - fieldOfView() is meaningless with Camera::ORTHOGRAPHIC. - - The near and far planes of the Camera are fitted to the scene and determined from - QGLViewer::sceneRadius(), QGLViewer::sceneCenter() and zClippingCoefficient() by the zNear() and - zFar() methods. Reasonable values on the scene extends hence have to be provided to the QGLViewer - in order for the Camera to correctly display the scene. High level positioning methods also use - this information (showEntireScene(), centerScene()...). - - A Camera holds KeyFrameInterpolator that can be used to save Camera positions and paths. You can - interactively addKeyFrameToPath() to a given path using the default \c Alt+F[1-12] shortcuts. Use - playPath() to make the Camera follow the path (default shortcut is F[1-12]). See the keyboard page for details on key customization. - - Use cameraCoordinatesOf() and worldCoordinatesOf() to convert to and from the Camera frame() - coordinate system. projectedCoordinatesOf() and unprojectedCoordinatesOf() will convert from - screen to 3D coordinates. convertClickToLine() is very useful for analytical object selection. - - Stereo display is possible on machines with quad buffer capabilities (with Camera::PERSPECTIVE - type() only). Test the stereoViewer example to check. - - A Camera can also be used outside of a QGLViewer or even without OpenGL for its coordinate system - conversion capabilities. Note however that some of them explicitly rely on the presence of a - Z-buffer. \nosubgrouping */ -class QGLVIEWER_EXPORT Camera : public QObject, public CameraBase -{ -#ifndef DOXYGEN - friend class ::QGLViewer; -#endif - - Q_OBJECT - -public: - Camera(); - virtual ~Camera(); - - Camera(const Camera& camera); - Camera& operator=(const Camera& camera); - - - /*! Enumerates the two possible types of Camera. - - See type() and setType(). This type mainly defines different Camera projection matrix (see - loadProjectionMatrix()). Many other methods (pointUnderPixel(), convertClickToLine(), - projectedCoordinatesOf(), pixelGLRatio()...) are affected by this Type. */ - enum Type { PERSPECTIVE, ORTHOGRAPHIC }; - - /*! @name Position and orientation */ - //@{ -public: - Vec position() const; - Vec upVector() const; - Vec viewDirection() const; - Vec rightVector() const; - Quaternion orientation() const; - - void setFromModelViewMatrix(const GLdouble* const modelViewMatrix); - void setFromProjectionMatrix(const qreal matrix[12]); - -public Q_SLOTS: - void setPosition(const Vec& pos); - void setOrientation(const Quaternion& q); - void setOrientation(qreal theta, qreal phi); - void setUpVector(const Vec& up, bool noMove=true); - void setViewDirection(const Vec& direction); - //@} - - - /*! @name Positioning tools */ - //@{ -public Q_SLOTS: - void lookAt(const Vec& target); - void showEntireScene(); - void fitSphere(const Vec& center, qreal radius); - void fitBoundingBox(const Vec& min, const Vec& max); - void fitScreenRegion(const QRect& rectangle); - void centerScene(); - void interpolateToZoomOnPixel(const QPoint& pixel); - void interpolateToFitScene(); - void interpolateTo(const Frame& fr, qreal duration); - //@} - - - /*! @name Frustum */ - //@{ -public: - /*! Returns the Camera::Type of the Camera. - - Set by setType(). Mainly used by loadProjectionMatrix(). - - A Camera::PERSPECTIVE Camera uses a classical projection mainly defined by its fieldOfView(). - - With a Camera::ORTHOGRAPHIC type(), the fieldOfView() is meaningless and the width and height of - the Camera frustum are inferred from the distance to the pivotPoint() using - getOrthoWidthHeight(). - - Both types use zNear() and zFar() (to define their clipping planes) and aspectRatio() (for - frustum shape). */ - Type type() const { return type_; } - - /*! Returns the vertical field of view of the Camera (in radians). - - Value is set using setFieldOfView(). Default value is pi/4 radians. This value is meaningless if - the Camera type() is Camera::ORTHOGRAPHIC. - - The field of view corresponds the one used in \c gluPerspective (see manual). It sets the Y - (vertical) aperture of the Camera. The X (horizontal) angle is inferred from the window aspect - ratio (see aspectRatio() and horizontalFieldOfView()). - - Use setFOVToFitScene() to adapt the fieldOfView() to a given scene. */ - qreal fieldOfView() const { return fieldOfView_; } - - /*! Returns the horizontal field of view of the Camera (in radians). - - 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() ); } - - /*! Returns the Camera aspect ratio defined by screenWidth() / screenHeight(). - - When the Camera is attached to a QGLViewer, these values and hence the aspectRatio() are - automatically fitted to the viewer's window aspect ratio using setScreenWidthAndHeight(). */ - qreal aspectRatio() const { return screenWidth_ / static_cast(screenHeight_); } - /*! Returns the width (in pixels) of the Camera screen. - - Set using setScreenWidthAndHeight(). This value is automatically fitted to the QGLViewer's - window dimensions when the Camera is attached to a QGLViewer. See also QGLWidget::width() */ - int screenWidth() const { return screenWidth_; } - /*! Returns the height (in pixels) of the Camera screen. - - Set using setScreenWidthAndHeight(). This value is automatically fitted to the QGLViewer's - window dimensions when the Camera is attached to a QGLViewer. See also QGLWidget::height() */ - int screenHeight() const { return screenHeight_; } - void getViewport(GLint viewport[4]) const; - qreal pixelGLRatio(const Vec& position) const; - float pixelGLRatio(float x, float y, float z) const; - - /*! Returns the coefficient which is used to set zNear() when the Camera is inside the sphere - defined by sceneCenter() and zClippingCoefficient() * sceneRadius(). - - In that case, the zNear() value is set to zNearCoefficient() * zClippingCoefficient() * - sceneRadius(). See the zNear() documentation for details. - - Default value is 0.005, which is appropriate for most applications. In case you need a high - dynamic ZBuffer precision, you can increase this value (~0.1). A lower value will prevent - clipping of very close objects at the expense of a worst Z precision. - - Only meaningful when Camera type is Camera::PERSPECTIVE. */ - qreal zNearCoefficient() const { return zNearCoef_; } - /*! Returns the coefficient used to position the near and far clipping planes. - - The near (resp. far) clipping plane is positioned at a distance equal to zClippingCoefficient() * - sceneRadius() in front of (resp. behind) the sceneCenter(). This garantees an optimal use of - the z-buffer range and minimizes aliasing. See the zNear() and zFar() documentations. - - Default value is square root of 3.0 (so that a cube of size sceneRadius() is not clipped). - - However, since the sceneRadius() is used for other purposes (see showEntireScene(), flySpeed(), - ...) and you may want to change this value to define more precisely the location of the clipping - planes. See also zNearCoefficient(). - - For a total control on clipping planes' positions, an other option is to overload the zNear() - and zFar() methods. See the standardCamera example. - - \attention When QGLViewer::cameraPathAreEdited(), this value is set to 5.0 so that the Camera - paths are not clipped. The previous zClippingCoefficient() value is restored back when you leave - this mode. */ - qreal zClippingCoefficient() const { return zClippingCoef_; } - - virtual qreal zNear() const; - virtual qreal zFar() const; - virtual void getOrthoWidthHeight(GLdouble& halfWidth, GLdouble& halfHeight) const; - void getFrustumPlanesCoefficients(GLdouble coef[6][4]) const; - -public Q_SLOTS: - void setType(Type type); - - void setFieldOfView(qreal fov); - - /*! Sets the horizontalFieldOfView() of the Camera (in radians). - - horizontalFieldOfView() and fieldOfView() are linked by the aspectRatio(). 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 setFOVToFitScene(); - - /*! Defines the Camera aspectRatio(). - - This value is actually inferred from the screenWidth() / screenHeight() ratio. You should use - setScreenWidthAndHeight() instead. - - This method might however be convenient when the Camera is not associated with a QGLViewer. It - actually sets the screenHeight() to 100 and the screenWidth() accordingly. See also - setFOVToFitScene(). - - \note If you absolutely need an aspectRatio() that does not correspond to your viewer's window - dimensions, overload loadProjectionMatrix() or multiply the created GL_PROJECTION matrix by a - scaled diagonal matrix in your QGLViewer::draw() method. */ - void setAspectRatio(qreal aspect) { setScreenWidthAndHeight(int(100.0*aspect), 100); } - - void setScreenWidthAndHeight(int width, int height); - /*! Sets the zNearCoefficient() value. */ - void setZNearCoefficient(qreal coef) { zNearCoef_ = coef; projectionMatrixIsUpToDate_ = false; } - /*! Sets the zClippingCoefficient() value. */ - void setZClippingCoefficient(qreal coef) { zClippingCoef_ = coef; projectionMatrixIsUpToDate_ = false; } - //@} - - double orthoCoefficient() const { return orthoCoef_; } - void setOrthoCoefficient(double coef) { orthoCoef_ = coef; } - - /*! @name Scene radius and center */ - //@{ -public: - /*! Returns the radius of the scene observed by the Camera. - - You need to provide such an approximation of the scene dimensions so that the Camera can adapt - its zNear() and zFar() values. See the sceneCenter() documentation. - - See also setSceneBoundingBox(). - - Note that QGLViewer::sceneRadius() (resp. QGLViewer::setSceneRadius()) simply call this method - (resp. setSceneRadius()) on its associated QGLViewer::camera(). */ - qreal sceneRadius() const { return sceneRadius_; } - - /*! Returns the position of the scene center, defined in the world coordinate system. - - The scene observed by the Camera should be roughly centered on this position, and included in a - sceneRadius() sphere. This approximate description of the scene permits a zNear() and zFar() - clipping planes definition, and allows convenient positioning methods such as showEntireScene(). - - Default value is (0,0,0) (world origin). Use setSceneCenter() to change it. See also - setSceneBoundingBox(). - - Note that QGLViewer::sceneCenter() (resp. QGLViewer::setSceneCenter()) simply calls this method - (resp. setSceneCenter()) on its associated QGLViewer::camera(). */ - Vec sceneCenter() const { return sceneCenter_; } - qreal distanceToSceneCenter() const; - -public Q_SLOTS: - void setSceneRadius(qreal radius); - void setSceneCenter(const Vec& center); - bool setSceneCenterFromPixel(const QPoint& pixel); - void setSceneBoundingBox(const Vec& min, const Vec& max); - //@} - - - /*! @name Pivot Point */ - //@{ -public Q_SLOTS: - void setPivotPoint(const Vec& point); - bool setPivotPointFromPixel(const QPoint& pixel); - -public: - Vec pivotPoint() const; - -#ifndef DOXYGEN -public Q_SLOTS: - void setRevolveAroundPoint(const Vec& point); - bool setRevolveAroundPointFromPixel(const QPoint& pixel); -public: - Vec revolveAroundPoint() const; -#endif - //@} - - - /*! @name Associated frame */ - //@{ -public: - /*! Returns the ManipulatedCameraFrame attached to the Camera. - - This ManipulatedCameraFrame defines its position() and orientation() and can translate mouse - events into Camera displacement. Set using setFrame(). */ - ManipulatedCameraFrame* frame() const { return frame_; } -public Q_SLOTS: - void setFrame(ManipulatedCameraFrame* const mcf); - //@} - - - /*! @name KeyFramed paths */ - //@{ -public: - KeyFrameInterpolator* keyFrameInterpolator(unsigned int i) const; - -public Q_SLOTS: - void setKeyFrameInterpolator(unsigned int i, KeyFrameInterpolator* const kfi); - - virtual void addKeyFrameToPath(unsigned int i); - virtual void playPath(unsigned int i); - virtual void deletePath(unsigned int i); - virtual void resetPath(unsigned int i); - virtual void drawAllPaths(); - //@} - - - /*! @name OpenGL matrices */ - //@{ -public: - virtual void loadProjectionMatrix(bool reset=true) const; - virtual void loadModelViewMatrix(bool reset=true) const; - void computeProjectionMatrix() const; - void computeModelViewMatrix() const; - - virtual void loadProjectionMatrixStereo(bool leftBuffer=true) const; - virtual void loadModelViewMatrixStereo(bool leftBuffer=true) const; - - void getProjectionMatrix(GLfloat m[16]) const; - void getProjectionMatrix(GLdouble m[16]) const; - - void getModelViewMatrix(GLfloat m[16]) const; - void getModelViewMatrix(GLdouble m[16]) const; - - void getModelViewProjectionMatrix(GLfloat m[16]) const; - 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: - Vec cameraCoordinatesOf(const Vec& src) const; - Vec worldCoordinatesOf(const Vec& src) const; - void getCameraCoordinatesOf(const qreal src[3], qreal res[3]) const; - void getWorldCoordinatesOf(const qreal src[3], qreal res[3]) const; - //@} - - - /*! @name 2D screen to 3D world coordinate systems conversions */ - //@{ -public: - Vec projectedCoordinatesOf(const Vec& src, const Frame* frame=NULL) const; - Vec unprojectedCoordinatesOf(const Vec& src, const Frame* frame=NULL) const; - void getProjectedCoordinatesOf(const qreal src[3], qreal res[3], const Frame* frame=NULL) const; - void getUnprojectedCoordinatesOf(const qreal src[3], qreal res[3], const Frame* frame=NULL) const; - void convertClickToLine(const QPoint& pixel, Vec& orig, Vec& dir) const; - Vec pointUnderPixel(const QPoint& pixel, bool& found) const; - //@} - - - /*! @name Fly speed */ - //@{ -public: - qreal flySpeed() const; -public Q_SLOTS: - void setFlySpeed(qreal speed); - //@} - - - /*! @name Stereo parameters */ - //@{ -public: - /*! Returns the user's inter-ocular distance (in meters). Default value is 0.062m, which fits most people. - - loadProjectionMatrixStereo() uses this value to define the Camera offset and frustum. See - setIODistance(). */ - qreal IODistance() const { return IODistance_; } - - /*! Returns the physical distance between the user's eyes and the screen (in meters). - - physicalDistanceToScreen() and focusDistance() represent the same distance. The former is - expressed in physical real world units, while the latter is expressed in OpenGL virtual world - units. - - 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); } - - /*! Returns the physical screen width, in meters. Default value is 0.5m (average monitor width). - - Used for stereo display only (see loadModelViewMatrixStereo() and loadProjectionMatrixStereo()). - Set using setPhysicalScreenWidth(). */ - qreal physicalScreenWidth() const { return physicalScreenWidth_; } - - /*! Returns the focus distance used by stereo display, expressed in OpenGL units. - - This is the distance in the virtual world between the Camera and the plane where the horizontal - stereo parallax is null (the stereo left and right cameras' lines of sigth cross at this distance). - - This distance is the virtual world equivalent of the real-world physicalDistanceToScreen(). - - \attention This value is modified by QGLViewer::setSceneRadius(), setSceneRadius() and - setFieldOfView(). When one of these values is modified, focusDistance() is set to sceneRadius() - / tan(fieldOfView()/2), which provides good results. */ - qreal focusDistance() const { return focusDistance_; } -public Q_SLOTS: - /*! Sets the IODistance(). */ - void setIODistance(qreal distance) { IODistance_ = distance; } - -#ifndef DOXYGEN - /*! This method is deprecated. Use setPhysicalScreenWidth() instead. */ - void setPhysicalDistanceToScreen(qreal distance) { Q_UNUSED(distance); qWarning("setPhysicalDistanceToScreen is deprecated, use setPhysicalScreenWidth instead"); } -#endif - - /*! Sets the physical screen (monitor or projected wall) width (in meters). */ - void setPhysicalScreenWidth(qreal width) { physicalScreenWidth_ = width; } - - /*! Sets the focusDistance(), in OpenGL scene units. */ - void setFocusDistance(qreal distance) { focusDistance_ = distance; } - //@} - - - /*! @name XML representation */ - //@{ -public: - virtual QDomElement domElement(const QString& name, QDomDocument& document) const; -public Q_SLOTS: - virtual void initFromDOMElement(const QDomElement& element); - //@} - - -private Q_SLOTS: - void onFrameModified(); - -private: - // F r a m e - ManipulatedCameraFrame* frame_; - - // C a m e r a p a r a m e t e r s - int screenWidth_, screenHeight_; // size of the window, in pixels - qreal fieldOfView_; // in radians - Vec sceneCenter_; - qreal sceneRadius_; // OpenGL units - qreal zNearCoef_; - qreal zClippingCoef_; - qreal orthoCoef_; - Type type_; // PERSPECTIVE or ORTHOGRAPHIC - mutable GLdouble modelViewMatrix_[16]; // Buffered model view matrix. - mutable bool modelViewMatrixIsUpToDate_; - mutable GLdouble projectionMatrix_[16]; // Buffered projection matrix. - mutable bool projectionMatrixIsUpToDate_; - - // S t e r e o p a r a m e t e r s - qreal IODistance_; // inter-ocular distance, in meters - qreal focusDistance_; // in scene units - qreal physicalScreenWidth_; // in meters - - // P o i n t s o f V i e w s a n d K e y F r a m e s - QMap kfi_; - KeyFrameInterpolator* interpolationKfi_; -}; - -} // namespace qglviewer - -#endif // QGLVIEWER_CAMERA_H diff --git a/code/3rd_QGLViewer-2.6.3/config.h b/code/3rd_QGLViewer-2.6.3/config.h deleted file mode 100644 index da45cb3d..00000000 --- a/code/3rd_QGLViewer-2.6.3/config.h +++ /dev/null @@ -1,120 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -/////////////////////////////////////////////////////////////////// -// libQGLViewer configuration file // -// Modify these settings according to your local configuration // -/////////////////////////////////////////////////////////////////// - -#ifndef QGLVIEWER_CONFIG_H -#define QGLVIEWER_CONFIG_H - -#define QGLVIEWER_VERSION 0x020603 - -// Needed for Qt < 4 (?) -#ifndef QT_CLEAN_NAMESPACE -# define QT_CLEAN_NAMESPACE -#endif - -// Get QT_VERSION and other Qt flags -#include - -#if QT_VERSION < 0x040000 -Error : libQGLViewer requires a minimum Qt version of 4.0 -#endif - -// Win 32 DLL export macros -#ifdef Q_OS_WIN32 -# ifndef M_PI -# define M_PI 3.14159265358979323846 -# endif -# ifndef QGLVIEWER_STATIC -# ifdef CREATE_QGLVIEWER_DLL -# if QT_VERSION >= 0x040500 -# define QGLVIEWER_EXPORT Q_DECL_EXPORT -# else -# define QGLVIEWER_EXPORT __declspec(dllexport) -# endif -# else -# if QT_VERSION >= 0x040500 -# define QGLVIEWER_EXPORT Q_DECL_IMPORT -# else -# define QGLVIEWER_EXPORT __declspec(dllimport) -# endif -# endif -# endif -# ifndef __MINGW32__ -# pragma warning( disable : 4251 ) // DLL interface, needed with Visual 6 -# pragma warning( disable : 4786 ) // identifier truncated to 255 in browser information (Visual 6). -# endif -#endif // Q_OS_WIN32 - -// For other architectures, this macro is empty -#ifndef QGLVIEWER_EXPORT -# define QGLVIEWER_EXPORT -#endif - -// OpenGL includes - Included here and hence shared by all the files that need 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 -#include - -// For deprecated methods -// #define __WHERE__ "In file "<<__FILE__<<", line "<<__LINE__<<": " -// #define orientationAxisAngle(x,y,z,a) { std::cout << __WHERE__ << "getOrientationAxisAngle()." << std::endl; exit(0); } - -// Patch for gcc version <= 2.95. Seems to no longer be needed with recent Qt versions. -// Uncomment these lines if you have error message dealing with operator << on QStrings -// #if defined(__GNUC__) && defined(__GNUC_MINOR__) && (__GNUC__ < 3) && (__GNUC_MINOR__ < 96) -// # include -// # include -// std::ostream& operator<<(std::ostream& out, const QString& str) -// { out << str.latin1(); return out; } -// #endif - - - -// Windows DLL export macros -#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64) -// Disable a warning message about dll. This is a temporary solution -// http://support.microsoft.com/default.aspx?scid=kb;EN-US;168958 -#pragma warning( disable : 4251 ) -#pragma warning( disable : 4996 ) -#pragma warning( disable : 4267 ) -#pragma warning( disable : 4091 ) -#pragma warning( disable : 4005 ) -#pragma warning( disable : 4244 ) -#pragma warning( disable : 4101 ) -#endif - - -#endif // QGLVIEWER_CONFIG_H diff --git a/code/3rd_QGLViewer-2.6.3/constraint.cpp b/code/3rd_QGLViewer-2.6.3/constraint.cpp deleted file mode 100644 index fbb9a6c2..00000000 --- a/code/3rd_QGLViewer-2.6.3/constraint.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "constraint.h" -#include "frame.h" -#include "camera.h" -#include "manipulatedCameraFrame.h" - -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; - } -} diff --git a/code/3rd_QGLViewer-2.6.3/constraint.h b/code/3rd_QGLViewer-2.6.3/constraint.h deleted file mode 100644 index d90d8203..00000000 --- a/code/3rd_QGLViewer-2.6.3/constraint.h +++ /dev/null @@ -1,338 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_CONSTRAINT_H -#define QGLVIEWER_CONSTRAINT_H - -#include "vec.h" -#include "quaternion.h" - -namespace qglviewer { -class Frame; -class Camera; - -/*! \brief An interface class for Frame constraints. - \class Constraint constraint.h QGLViewer/constraint.h - - This class defines the interface for the Constraints that can be applied to a Frame to limit its - motion. Use Frame::setConstraint() to associate a Constraint to a Frame (default is a \c NULL - Frame::constraint()). - -

How does it work ?

- - The Constraint acts as a filter on the translation and rotation Frame increments. - constrainTranslation() and constrainRotation() should be overloaded to specify the constraint - behavior: the desired displacement is given as a parameter that can optionally be modified. - - Here is how the Frame::translate() and Frame::rotate() methods use the Constraint: - \code - Frame::translate(Vec& T) - { - if (constraint()) - constraint()->constrainTranslation(T, this); - t += T; - } - - Frame::rotate(Quaternion& Q) - { - if (constraint()) - constraint()->constrainRotation(Q, this); - q *= Q; - } - \endcode - - The default behavior of constrainTranslation() and constrainRotation() is empty (meaning no - filtering). - - The Frame which uses the Constraint is passed as a parameter to the constrainTranslation() and - constrainRotation() methods, so that they can have access to its current state (mainly - Frame::position() and Frame::orientation()). It is not \c const for versatility reasons, but - directly modifying it should be avoided. - - \attention Frame::setTranslation(), Frame::setRotation() and similar methods will actually indeed - set the frame position and orientation, without taking the constraint into account. Use the \e - WithConstraint versions of these methods to enforce the Constraint. - -

Implemented Constraints

- - Classical axial and plane Constraints are provided for convenience: see the LocalConstraint, - WorldConstraint and CameraConstraint classes' documentations. - - Try the constrainedFrame and constrainedCamera examples for an illustration. - -

Creating new Constraints

- - The implementation of a new Constraint class simply consists in overloading the filtering methods: - \code - // This Constraint enforces that the Frame cannot have a negative z world coordinate. - class myConstraint : public Constraint - { - public: - virtual void constrainTranslation(Vec& t, Frame * const fr) - { - // Express t in the world coordinate system. - const Vec tWorld = fr->inverseTransformOf(t); - if (fr->position().z + tWorld.z < 0.0) // check the new fr z coordinate - t.z = fr->transformOf(-fr->position().z); // t.z is clamped so that next z position is 0.0 - } - }; - \endcode - - Note that the translation (resp. rotation) parameter passed to constrainTranslation() (resp. - constrainRotation()) is expressed in the \e local Frame coordinate system. Here, we use the - Frame::transformOf() and Frame::inverseTransformOf() method to convert it to and from the world - coordinate system. - - Combined constraints can easily be achieved by creating a new class that applies the different - constraint filters: - \code - myConstraint::constrainTranslation(Vec& v, Frame* const fr) - { - constraint1->constrainTranslation(v, fr); - constraint2->constrainTranslation(v, fr); - // and so on, with possible branches, tests, loops... - } - \endcode - */ -class QGLVIEWER_EXPORT Constraint -{ -public: - /*! Virtual destructor. Empty. */ - virtual ~Constraint() {} - - /*! Filters the translation applied to the \p frame. This default implementation is empty (no - filtering). - - Overload this method in your own Constraint class to define a new translation constraint. \p - frame is the Frame to which is applied the translation. It is not defined \c const, but you - should refrain from directly changing its value in the constraint. Use its Frame::position() and - update the \p translation accordingly instead. - - \p translation is expressed in local frame coordinate system. Use Frame::inverseTransformOf() to - express it in the world coordinate system if needed. */ - virtual void constrainTranslation(Vec& translation, Frame* const frame) { Q_UNUSED(translation); Q_UNUSED(frame); } - /*! Filters the rotation applied to the \p frame. This default implementation is empty (no - filtering). - - Overload this method in your own Constraint class to define a new rotation constraint. See - constrainTranslation() for details. - - Use Frame::inverseTransformOf() on the \p rotation Quaternion::axis() to express \p rotation in - the world coordinate system if needed. */ - virtual void constrainRotation(Quaternion& rotation, Frame* const frame) { Q_UNUSED(rotation); Q_UNUSED(frame); } -}; - -/*! - \brief An abstract class for Frame Constraints defined by an axis or a plane. - \class AxisPlaneConstraint constraint.h QGLViewer/constraint.h - - AxisPlaneConstraint is an interface for (translation and/or rotation) Constraint that are defined - by a direction. translationConstraintType() and rotationConstraintType() define how this - direction should be interpreted: as an axis (AxisPlaneConstraint::AXIS) or as a plane normal - (AxisPlaneConstraint::PLANE). See the Type() documentation for details. - - The three implementations of this class: LocalConstraint, WorldConstraint and CameraConstraint - differ by the coordinate system in which this direction is expressed. - - Different implementations of this class are illustrated in the - contrainedCamera and - constrainedFrame examples. - - \attention When applied, the rotational Constraint may not intuitively follow the mouse - displacement. A solution would be to directly measure the rotation angle in screen coordinates, - but that would imply to know the QGLViewer::camera(), so that we can compute the projected - coordinates of the rotation center (as is done with the QGLViewer::SCREEN_ROTATE binding). - However, adding an extra pointer to the QGLViewer::camera() in all the AxisPlaneConstraint - derived classes (which the user would have to update in a multi-viewer application) was judged as - an overkill. */ -class QGLVIEWER_EXPORT AxisPlaneConstraint : public Constraint -{ -public: - AxisPlaneConstraint(); - /*! Virtual destructor. Empty. */ - virtual ~AxisPlaneConstraint() {} - - /*! Type lists the different types of translation and rotation constraints that are available. - - It specifies the meaning of the constraint direction (see translationConstraintDirection() and - rotationConstraintDirection()): as an axis direction (AxisPlaneConstraint::AXIS) or a plane - normal (AxisPlaneConstraint::PLANE). AxisPlaneConstraint::FREE means no constraint while - AxisPlaneConstraint::FORBIDDEN completely forbids the translation and/or the rotation. - - See translationConstraintType() and rotationConstraintType(). - - \attention The AxisPlaneConstraint::PLANE Type is not valid for rotational constraint. - - New derived classes can use their own extended enum for specific constraints: - \code - class MyAxisPlaneConstraint : public AxisPlaneConstraint - { - public: - enum MyType { FREE, AXIS, PLANE, FORBIDDEN, CUSTOM }; - virtual void constrainTranslation(Vec &translation, Frame *const frame) - { - // translationConstraintType() is simply an int. CUSTOM Type is handled seamlessly. - switch (translationConstraintType()) - { - case MyAxisPlaneConstraint::FREE: ... break; - case MyAxisPlaneConstraint::CUSTOM: ... break; - } - }; - - MyAxisPlaneConstraint* c = new MyAxisPlaneConstraint(); - // Note the Type conversion - c->setTranslationConstraintType(AxisPlaneConstraint::Type(MyAxisPlaneConstraint::CUSTOM)); - }; - \endcode */ - enum Type { FREE, AXIS, PLANE, FORBIDDEN }; - - /*! @name Translation constraint */ - //@{ - /*! Overloading of Constraint::constrainTranslation(). Empty */ - virtual void constrainTranslation(Vec& translation, Frame* const frame) { Q_UNUSED(translation); Q_UNUSED(frame); }; - - void setTranslationConstraint(Type type, const Vec& direction); - /*! Sets the Type() of the translationConstraintType(). Default is AxisPlaneConstraint::FREE. */ - void setTranslationConstraintType(Type type) { translationConstraintType_ = type; }; - void setTranslationConstraintDirection(const Vec& direction); - - /*! Returns the translation constraint Type(). - - Depending on this value, the Frame will freely translate (AxisPlaneConstraint::FREE), will only - be able to translate along an axis direction (AxisPlaneConstraint::AXIS), will be forced to stay - into a plane (AxisPlaneConstraint::PLANE) or will not able to translate at all - (AxisPlaneConstraint::FORBIDDEN). - - Use Frame::setPosition() to define the position of the constrained Frame before it gets - constrained. */ - Type translationConstraintType() const { return translationConstraintType_; }; - /*! Returns the direction used by the translation constraint. - - It represents the axis direction (AxisPlaneConstraint::AXIS) or the plane normal - (AxisPlaneConstraint::PLANE) depending on the translationConstraintType(). It is undefined for - AxisPlaneConstraint::FREE or AxisPlaneConstraint::FORBIDDEN. - - The AxisPlaneConstraint derived classes express this direction in different coordinate system - (camera for CameraConstraint, local for LocalConstraint, and world for WorldConstraint). This - value can be modified with setTranslationConstraintDirection(). */ - Vec translationConstraintDirection() const { return translationConstraintDir_; }; - //@} - - /*! @name Rotation constraint */ - //@{ - /*! Overloading of Constraint::constrainRotation(). Empty. */ - virtual void constrainRotation(Quaternion& rotation, Frame* const frame) { Q_UNUSED(rotation); Q_UNUSED(frame); }; - - void setRotationConstraint(Type type, const Vec& direction); - void setRotationConstraintType(Type type); - void setRotationConstraintDirection(const Vec& direction); - - /*! Returns the rotation constraint Type(). */ - Type rotationConstraintType() const { return rotationConstraintType_; }; - /*! Returns the axis direction used by the rotation constraint. - - This direction is defined only when rotationConstraintType() is AxisPlaneConstraint::AXIS. - - The AxisPlaneConstraint derived classes express this direction in different coordinate system - (camera for CameraConstraint, local for LocalConstraint, and world for WorldConstraint). This - value can be modified with setRotationConstraintDirection(). */ - Vec rotationConstraintDirection() const { return rotationConstraintDir_; }; - //@} - -private: - // int and not Type to allow for overloading and new types definition. - Type translationConstraintType_; - Type rotationConstraintType_; - - Vec translationConstraintDir_; - Vec rotationConstraintDir_; -}; - - -/*! \brief An AxisPlaneConstraint defined in the Frame local coordinate system. - \class LocalConstraint constraint.h QGLViewer/constraint.h - - The translationConstraintDirection() and rotationConstraintDirection() are expressed in the Frame - local coordinate system (see Frame::referenceFrame()). - - See the constrainedFrame example for an illustration. */ -class QGLVIEWER_EXPORT LocalConstraint : public AxisPlaneConstraint -{ -public: - /*! Virtual destructor. Empty. */ - virtual ~LocalConstraint() {}; - - virtual void constrainTranslation(Vec& translation, Frame* const frame); - virtual void constrainRotation (Quaternion& rotation, Frame* const frame); -}; - - - -/*! \brief An AxisPlaneConstraint defined in the world coordinate system. - \class WorldConstraint constraint.h QGLViewer/constraint.h - - The translationConstraintDirection() and rotationConstraintDirection() are expressed in world - coordinate system. - - See the constrainedFrame and multiView examples for an illustration. */ -class QGLVIEWER_EXPORT WorldConstraint : public AxisPlaneConstraint -{ -public: - /*! Virtual destructor. Empty. */ - virtual ~WorldConstraint() {}; - - virtual void constrainTranslation(Vec& translation, Frame* const frame); - virtual void constrainRotation (Quaternion& rotation, Frame* const frame); -}; - - - -/*! \brief An AxisPlaneConstraint defined in the camera coordinate system. - \class CameraConstraint constraint.h QGLViewer/constraint.h - - The translationConstraintDirection() and rotationConstraintDirection() are expressed in the - associated camera() coordinate system. - - See the constrainedFrame and constrainedCamera examples for an illustration. */ -class QGLVIEWER_EXPORT CameraConstraint : public AxisPlaneConstraint -{ -public: - explicit CameraConstraint(const Camera* const camera); - /*! Virtual destructor. Empty. */ - 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_; }; - -private: - const Camera* const camera_; -}; - -} // namespace qglviewer - -#endif // QGLVIEWER_CONSTRAINT_H diff --git a/code/3rd_QGLViewer-2.6.3/domUtils.h b/code/3rd_QGLViewer-2.6.3/domUtils.h deleted file mode 100644 index 39944683..00000000 --- a/code/3rd_QGLViewer-2.6.3/domUtils.h +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "config.h" - -#include -#include -#include -#include - -#include - -#ifndef DOXYGEN - -// QDomElement loading with syntax checking. -class DomUtils -{ -private: - static void warning(const QString& message) - { - qWarning("%s", message.toLatin1().constData()); - } - -public: - static qreal qrealFromDom(const QDomElement& e, const QString& attribute, qreal defValue) - { - qreal value = defValue; - if (e.hasAttribute(attribute)) { - const QString s = e.attribute(attribute); - bool ok; - value = s.toDouble(&ok); - if (!ok) { - warning(QString("'%1' is not a valid qreal syntax for attribute \"%2\" in initialization of \"%3\". Setting value to %4.") - .arg(s).arg(attribute).arg(e.tagName()).arg(QString::number(defValue))); - value = defValue; - } - } else { - warning(QString("\"%1\" attribute missing in initialization of \"%2\". Setting value to %3.") - .arg(attribute).arg(e.tagName()).arg(QString::number(value))); - } - -#if defined(isnan) - // The "isnan" method may not be available on all platforms. - // Find its equivalent or simply remove these two lines - if (isnan(value)) - warning(QString("Warning, attribute \"%1\" initialized to Not a Number in \"%2\"") - .arg(attribute).arg(e.tagName())); -#endif - - return value; - } - - static int intFromDom(const QDomElement& e, const QString& attribute, int defValue) - { - int value = defValue; - if (e.hasAttribute(attribute)) - { - const QString s = e.attribute(attribute); - bool ok; - value = s.toInt(&ok); - if (!ok) { - warning(QString("'%1' is not a valid integer syntax for attribute \"%2\" in initialization of \"%3\". Setting value to %4.") - .arg(s).arg(attribute).arg(e.tagName()).arg(QString::number(defValue))); - value = defValue; - } - } else { - warning(QString("\"%1\" attribute missing in initialization of \"%2\". Setting value to %3.") - .arg(attribute).arg(e.tagName()).arg(QString::number(value))); - } - - return value; - } - - static unsigned int uintFromDom(const QDomElement& e, const QString& attribute, unsigned int defValue) - { - unsigned int value = defValue; - if (e.hasAttribute(attribute)) - { - const QString s = e.attribute(attribute); - bool ok; - value = s.toUInt(&ok); - if (!ok) { - warning(QString("'%1' is not a valid unsigned integer syntax for attribute \"%2\" in initialization of \"%3\". Setting value to %4.") - .arg(s).arg(attribute).arg(e.tagName()).arg(QString::number(defValue))); - value = defValue; - } - } else { - warning(QString("\"%1\" attribute missing in initialization of \"%2\". Setting value to %3.") - .arg(attribute).arg(e.tagName()).arg(QString::number(value))); - } - - return value; - } - - static bool boolFromDom(const QDomElement& e, const QString& attribute, bool defValue) - { - bool value = defValue; - if (e.hasAttribute(attribute)) - { - const QString s = e.attribute(attribute); - if (s.toLower() == QString("true")) - value = true; - else if (s.toLower() == QString("false")) - value = false; - else - { - warning(QString("'%1' is not a valid boolean syntax for attribute \"%2\" in initialization of \"%3\". Setting value to %4.") - .arg(s).arg(attribute).arg(e.tagName()).arg(defValue?"true":"false")); - } - } else { - warning(QString("\"%1\" attribute missing in initialization of \"%2\". Setting value to %3.") - .arg(attribute).arg(e.tagName()).arg(value?"true":"false")); - } - - return value; - } - - static void setBoolAttribute(QDomElement& element, const QString& attribute, bool value) { - element.setAttribute(attribute, (value ? "true" : "false")); - } - - static QDomElement QColorDomElement(const QColor& color, const QString& name, QDomDocument& doc) - { - QDomElement de = doc.createElement(name); - de.setAttribute("red", QString::number(color.red())); - de.setAttribute("green", QString::number(color.green())); - de.setAttribute("blue", QString::number(color.blue())); - return de; - } - - static QColor QColorFromDom(const QDomElement& e) - { - int color[3]; - QStringList attribute; - attribute << "red" << "green" << "blue"; - for (int i=0; i - -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); -} diff --git a/code/3rd_QGLViewer-2.6.3/frame.h b/code/3rd_QGLViewer-2.6.3/frame.h deleted file mode 100644 index 2b88540a..00000000 --- a/code/3rd_QGLViewer-2.6.3/frame.h +++ /dev/null @@ -1,415 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_FRAME_H -#define QGLVIEWER_FRAME_H - -#include -#include - -#include "constraint.h" -// #include "GL/gl.h" is now included in config.h for ease of configuration - -namespace qglviewer { -/*! \brief The Frame class represents a coordinate system, defined by a position and an - orientation. \class Frame frame.h QGLViewer/frame.h - - A Frame is a 3D coordinate system, represented by a position() and an orientation(). The order of - these transformations is important: the Frame is first translated \e and \e then rotated around - the new translated origin. - - A Frame is useful to define the position and orientation of a 3D rigid object, using its matrix() - method, as shown below: - \code - // Builds a Frame at position (0.5,0,0) and oriented such that its Y axis is along the (1,1,1) - // direction. One could also have used setPosition() and setOrientation(). - Frame fr(Vec(0.5,0,0), Quaternion(Vec(0,1,0), Vec(1,1,1))); - glPushMatrix(); - glMultMatrixd(fr.matrix()); - // Draw your object here, in the local fr coordinate system. - glPopMatrix(); - \endcode - - Many functions are provided to transform a 3D point from one coordinate system (Frame) to an - other: see coordinatesOf(), inverseCoordinatesOf(), coordinatesOfIn(), coordinatesOfFrom()... - - You may also want to transform a 3D vector (such as a normal), which corresponds to applying only - the rotational part of the frame transformation: see transformOf() and inverseTransformOf(). See - the frameTransform example for an illustration. - - The translation() and the rotation() that are encapsulated in a Frame can also be used to - represent a \e rigid \e transformation of space. Such a transformation can also be interpreted as - a change of coordinate system, and the coordinate system conversion functions actually allow you - to use a Frame as a rigid transformation. Use inverseCoordinatesOf() (resp. coordinatesOf()) to - apply the transformation (resp. its inverse). Note the inversion. - -

Hierarchy of Frames

- - The position and the orientation of a Frame are actually defined with respect to a - referenceFrame(). The default referenceFrame() is the world coordinate system (represented by a \c - NULL referenceFrame()). If you setReferenceFrame() to a different Frame, you must then - differentiate: - - \arg the \e local translation() and rotation(), defined with respect to the referenceFrame(), - - \arg the \e global position() and orientation(), always defined with respect to the world - coordinate system. - - A Frame is actually defined by its translation() with respect to its referenceFrame(), and then by - a rotation() of the coordinate system around the new translated origin. - - This terminology for \e local (translation() and rotation()) and \e global (position() and - orientation()) definitions is used in all the methods' names and should be sufficient to prevent - ambiguities. These notions are obviously identical when the referenceFrame() is \c NULL, i.e. when - the Frame is defined in the world coordinate system (the one you are in at the beginning of the - QGLViewer::draw() method, see the introduction page). - - Frames can hence easily be organized in a tree hierarchy, which root is the world coordinate - system. A loop in the hierarchy would result in an inconsistent (multiple) Frame definition. - settingAsReferenceFrameWillCreateALoop() checks this and prevents setReferenceFrame() from - creating such a loop. - - This frame hierarchy is used in methods like coordinatesOfIn(), coordinatesOfFrom()... which allow - coordinates (or vector) conversions from a Frame to any other one (including the world coordinate - system). - - However, one must note that this hierarchical representation is internal to the Frame classes. - When the Frames represent OpenGL coordinates system, one should map this hierarchical - representation to the OpenGL GL_MODELVIEW matrix stack. See the matrix() documentation for - details. - -

Constraints

- - An interesting feature of Frames is that their displacements can be constrained. When a Constraint - is attached to a Frame, it filters the input of translate() and rotate(), and only the resulting - filtered motion is applied to the Frame. The default constraint() is \c NULL resulting in no - filtering. Use setConstraint() to attach a Constraint to a frame. - - Constraints are especially usefull for the ManipulatedFrame instances, in order to forbid some - mouse motions. See the constrainedFrame, constrainedCamera and luxo examples for an illustration. - - Classical constraints are provided for convenience (see LocalConstraint, WorldConstraint and - CameraConstraint) and new constraints can very easily be implemented. - -

Derived classes

- - The ManipulatedFrame class inherits Frame and implements a mouse motion convertion, so that a - Frame (and hence an object) can be manipulated in the scene with the mouse. - - \nosubgrouping */ -class QGLVIEWER_EXPORT Frame : public QObject -{ - Q_OBJECT - -public: - Frame(); - - /*! Virtual destructor. Empty. */ - virtual ~Frame() {} - - Frame(const Frame& frame); - Frame& operator=(const Frame& frame); - -Q_SIGNALS: - /*! This signal is emitted whenever the position() or the orientation() of the Frame is modified. - - Connect this signal to any object that must be notified: - \code - QObject::connect(myFrame, SIGNAL(modified()), myObject, SLOT(update())); - \endcode - Use the QGLViewer::QGLViewerPool() to connect the signal to all the viewers. - - \note If your Frame is part of a Frame hierarchy (see referenceFrame()), a modification of one - of the parents of this Frame will \e not emit this signal. Use code like this to change this - behavior (you can do this recursively for all the referenceFrame() until the \c NULL world root - frame is encountered): - \code - // Emits the Frame modified() signal when its referenceFrame() is modified(). - connect(myFrame->referenceFrame(), SIGNAL(modified()), myFrame, SIGNAL(modified())); - \endcode - - \attention Connecting this signal to a QGLWidget::update() slot (or a method that calls it) - will prevent you from modifying the Frame \e inside your QGLViewer::draw() method as it would - result in an infinite loop. However, QGLViewer::draw() should not modify the scene. - - \note Note that this signal might be emitted even if the Frame is not actually modified, for - instance after a translate(Vec(0,0,0)) or a setPosition(position()). */ - void modified(); - - /*! This signal is emitted when the Frame is interpolated by a KeyFrameInterpolator. - - See the KeyFrameInterpolator documentation for details. - - If a KeyFrameInterpolator is used to successively interpolate several Frames in your scene, - connect the KeyFrameInterpolator::interpolated() signal instead (identical, but independent of - the interpolated Frame). */ - void interpolated(); - -public: - /*! @name World coordinates position and orientation */ - //@{ - Frame(const Vec& position, const Quaternion& orientation); - - void setPosition(const Vec& position); - void setPosition(qreal x, qreal y, qreal z); - void setPositionWithConstraint(Vec& position); - - void setOrientation(const Quaternion& orientation); - void setOrientation(qreal q0, qreal q1, qreal q2, qreal q3); - void setOrientationWithConstraint(Quaternion& orientation); - - void setPositionAndOrientation(const Vec& position, const Quaternion& orientation); - void setPositionAndOrientationWithConstraint(Vec& position, Quaternion& orientation); - - Vec position() const; - Quaternion orientation() const; - - void getPosition(qreal& x, qreal& y, qreal& z) const; - void getOrientation(qreal& q0, qreal& q1, qreal& q2, qreal& q3) const; - //@} - - -public: - /*! @name Local translation and rotation w/r reference Frame */ - //@{ - /*! Sets the translation() of the frame, locally defined with respect to the referenceFrame(). - Emits the modified() signal. - - Use setPosition() to define the world coordinates position(). Use - setTranslationWithConstraint() to take into account the potential constraint() of the Frame. */ - void setTranslation(const Vec& translation) { t_ = translation; Q_EMIT modified(); } - void setTranslation(qreal x, qreal y, qreal z); - void setTranslationWithConstraint(Vec& translation); - - /*! Set the current rotation Quaternion. See rotation() and the different Quaternion - constructors. Emits the modified() signal. See also setTranslation() and - setRotationWithConstraint(). */ - - /*! Sets the rotation() of the Frame, locally defined with respect to the referenceFrame(). - Emits the modified() signal. - - Use setOrientation() to define the world coordinates orientation(). The potential - constraint() of the Frame is not taken into account, use setRotationWithConstraint() - instead. */ - void setRotation(const Quaternion& rotation) { q_ = rotation; Q_EMIT modified(); } - void setRotation(qreal q0, qreal q1, qreal q2, qreal q3); - void setRotationWithConstraint(Quaternion& rotation); - - void setTranslationAndRotation(const Vec& translation, const Quaternion& rotation); - void setTranslationAndRotationWithConstraint(Vec& translation, Quaternion& rotation); - - /*! Returns the Frame translation, defined with respect to the referenceFrame(). - - Use position() to get the result in the world coordinates. These two values are identical - when the referenceFrame() is \c NULL (default). - - See also setTranslation() and setTranslationWithConstraint(). */ - Vec translation() const { return t_; } - /*! Returns the Frame rotation, defined with respect to the referenceFrame(). - - Use orientation() to get the result in the world coordinates. These two values are identical - when the referenceFrame() is \c NULL (default). - - See also setRotation() and setRotationWithConstraint(). */ - - /*! Returns the current Quaternion orientation. See setRotation(). */ - Quaternion rotation() const { return q_; } - - void getTranslation(qreal& x, qreal& y, qreal& z) const; - void getRotation(qreal& q0, qreal& q1, qreal& q2, qreal& q3) const; - //@} - -public: - /*! @name Frame hierarchy */ - //@{ - /*! Returns the reference Frame, in which coordinates system the Frame is defined. - - The translation() and rotation() of the Frame are defined with respect to the referenceFrame() - coordinate system. A \c NULL referenceFrame() (default value) means that the Frame is defined in - the world coordinate system. - - Use position() and orientation() to recursively convert values along the referenceFrame() chain - and to get values expressed in the world coordinate system. The values match when the - referenceFrame() is \c NULL. - - Use setReferenceFrame() to set this value and create a Frame hierarchy. Convenient functions - allow you to convert 3D coordinates from one Frame to an other: see coordinatesOf(), - localCoordinatesOf(), coordinatesOfIn() and their inverse functions. - - Vectors can also be converted using transformOf(), transformOfIn, localTransformOf() and their - inverse functions. */ - const Frame* referenceFrame() const { return referenceFrame_; } - void setReferenceFrame(const Frame* const refFrame); - bool settingAsReferenceFrameWillCreateALoop(const Frame* const frame); - //@} - - - /*! @name Frame modification */ - //@{ - void translate(Vec& t); - void translate(const Vec& t); - // Some compilers complain about "overloading cannot distinguish from previous declaration" - // Simply comment out the following method and its associated implementation - void translate(qreal x, qreal y, qreal z); - void translate(qreal& x, qreal& y, qreal& z); - - void rotate(Quaternion& q); - void rotate(const Quaternion& q); - // Some compilers complain about "overloading cannot distinguish from previous declaration" - // Simply comment out the following method and its associated implementation - void rotate(qreal q0, qreal q1, qreal q2, qreal q3); - void rotate(qreal& q0, qreal& q1, qreal& q2, qreal& q3); - - void rotateAroundPoint(Quaternion& rotation, const Vec& point); - void rotateAroundPoint(const Quaternion& rotation, const Vec& point); - - void alignWithFrame(const Frame* const frame, bool move=false, qreal threshold=0.0); - void projectOnLine(const Vec& origin, const Vec& direction); - //@} - - - /*! @name Coordinate system transformation of 3D coordinates */ - //@{ - Vec coordinatesOf(const Vec& src) const; - Vec inverseCoordinatesOf(const Vec& src) const; - Vec localCoordinatesOf(const Vec& src) const; - Vec localInverseCoordinatesOf(const Vec& src) const; - Vec coordinatesOfIn(const Vec& src, const Frame* const in) const; - Vec coordinatesOfFrom(const Vec& src, const Frame* const from) const; - - void getCoordinatesOf(const qreal src[3], qreal res[3]) const; - void getInverseCoordinatesOf(const qreal src[3], qreal res[3]) const; - void getLocalCoordinatesOf(const qreal src[3], qreal res[3]) const; - void getLocalInverseCoordinatesOf(const qreal src[3], qreal res[3]) const; - void getCoordinatesOfIn(const qreal src[3], qreal res[3], const Frame* const in) const; - void getCoordinatesOfFrom(const qreal src[3], qreal res[3], const Frame* const from) const; - //@} - - /*! @name Coordinate system transformation of vectors */ - // A frame is as a new coordinate system, defined with respect to a reference frame (the world - // coordinate system by default, see the "Composition of frame" section). - - // The transformOf() (resp. inverseTransformOf()) functions transform a 3D vector from (resp. - // to) the world coordinates system. This section defines the 3D vector transformation - // functions. See the Coordinate system transformation of 3D points above for the transformation - // of 3D points. The difference between the two sets of functions is simple: for vectors, only - // the rotational part of the transformations is taken into account, while translation is also - // considered for 3D points. - - // The length of the resulting transformed vector is identical to the one of the source vector - // for all the described functions. - - // When local is prepended to the names of the functions, the functions simply transform from - // (and to) the reference frame. - - // When In (resp. From) is appended to the names, the functions transform from (resp. To) the - // frame that is given as an argument. The frame does not need to be in the same branch or the - // hierarchical tree, and can be \c NULL (the world coordinates system). - - // Combining any of these functions with its inverse (in any order) leads to the identity. - //@{ - Vec transformOf(const Vec& src) const; - Vec inverseTransformOf(const Vec& src) const; - Vec localTransformOf(const Vec& src) const; - Vec localInverseTransformOf(const Vec& src) const; - Vec transformOfIn(const Vec& src, const Frame* const in) const; - Vec transformOfFrom(const Vec& src, const Frame* const from) const; - - void getTransformOf(const qreal src[3], qreal res[3]) const; - void getInverseTransformOf(const qreal src[3], qreal res[3]) const; - void getLocalTransformOf(const qreal src[3], qreal res[3]) const; - void getLocalInverseTransformOf(const qreal src[3], qreal res[3]) const; - void getTransformOfIn(const qreal src[3], qreal res[3], const Frame* const in) const; - void getTransformOfFrom(const qreal src[3], qreal res[3], const Frame* const from) const; - //@} - - - /*! @name Constraint on the displacement */ - //@{ - /*! Returns the current constraint applied to the Frame. - - A \c NULL value (default) means that no Constraint is used to filter Frame translation and - rotation. See the Constraint class documentation for details. - - You may have to use a \c dynamic_cast to convert the result to a Constraint derived class. */ - Constraint* constraint() const { return constraint_; } - /*! Sets the constraint() attached to the Frame. - - A \c NULL value means no constraint. The previous constraint() should be deleted by the calling - method if needed. */ - void setConstraint(Constraint* const constraint) { constraint_ = constraint; } - //@} - - /*! @name Associated matrices */ - //@{ -public: - const GLdouble* matrix() const; - void getMatrix(GLdouble m[4][4]) const; - void getMatrix(GLdouble m[16]) const; - - const GLdouble* worldMatrix() const; - void getWorldMatrix(GLdouble m[4][4]) const; - void getWorldMatrix(GLdouble m[16]) const; - - void setFromMatrix(const GLdouble m[4][4]); - void setFromMatrix(const GLdouble m[16]); - //@} - - /*! @name Inversion of the transformation */ - //@{ - Frame inverse() const; - /*! Returns the inverse() of the Frame world transformation. - - The orientation() of the new Frame is the Quaternion::inverse() of the original orientation. - Its position() is the negated and inverse rotated image of the original position. - - The result Frame has a \c NULL referenceFrame() and a \c NULL constraint(). - - Use inverse() for a local (i.e. with respect to referenceFrame()) transformation inverse. */ - Frame worldInverse() const { return Frame(-(orientation().inverseRotate(position())), orientation().inverse()); } - //@} - - /*! @name XML representation */ - //@{ -public: - virtual QDomElement domElement(const QString& name, QDomDocument& document) const; -public Q_SLOTS: - virtual void initFromDOMElement(const QDomElement& element); - //@} - -private: - // P o s i t i o n a n d o r i e n t a t i o n - Vec t_; - Quaternion q_; - - // C o n s t r a i n t s - Constraint* constraint_; - - // F r a m e c o m p o s i t i o n - const Frame* referenceFrame_; -}; - -} // namespace qglviewer - -#endif // QGLVIEWER_FRAME_H diff --git a/code/3rd_QGLViewer-2.6.3/keyFrameInterpolator.cpp b/code/3rd_QGLViewer-2.6.3/keyFrameInterpolator.cpp deleted file mode 100644 index 37ca6950..00000000 --- a/code/3rd_QGLViewer-2.6.3/keyFrameInterpolator.cpp +++ /dev/null @@ -1,714 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "domUtils.h" -#include "qglviewer.h" // for QGLViewer::drawAxis and Camera::drawCamera - -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 sec). - - 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 sec). - - 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); - 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; iframe()) - 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 sec. - - 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 sec. - -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 sec. - -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 sec). 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 diff --git a/code/3rd_QGLViewer-2.6.3/keyFrameInterpolator.h b/code/3rd_QGLViewer-2.6.3/keyFrameInterpolator.h deleted file mode 100644 index 704cc17f..00000000 --- a/code/3rd_QGLViewer-2.6.3/keyFrameInterpolator.h +++ /dev/null @@ -1,357 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_KEY_FRAME_INTERPOLATOR_H -#define QGLVIEWER_KEY_FRAME_INTERPOLATOR_H - -#include -#include - -#include "quaternion.h" -// Not actually needed, but some bad compilers (Microsoft VS6) complain. -#include "frame.h" - -// 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 QGLViewer/keyFrameInterpolator.h - - A KeyFrameInterpolator holds keyFrames (that define a path) and a pointer to a Frame of your - application (which will be interpolated). When the user startInterpolation(), the - KeyFrameInterpolator regularly updates the frame() position and orientation along the path. - - Here is a typical utilization example (see also the keyFrames - example): - \code - - - init() - { - // The KeyFrameInterpolator kfi is given the Frame that it will drive over time. - kfi = new KeyFrameInterpolator( new Frame() ); - kfi->addKeyFrame( Frame( Vec(1,0,0), Quaternion() ) ); - kfi->addKeyFrame( new Frame( Vec(2,1,0), Quaternion() ) ); - // ...and so on for all the keyFrames. - - // Ask for a display update after each update of the KeyFrameInterpolator - connect(kfi, SIGNAL(interpolated()), SLOT(update())); - - kfi->startInterpolation(); - } - - draw() - { - glPushMatrix(); - glMultMatrixd( kfi->frame()->matrix() ); - // Draw your object here. Its position and orientation are interpolated. - glPopMatrix(); - } - \endcode - - The keyFrames are defined by a Frame and a time, expressed in sec. The Frame can be provided - as a const reference or as a pointer to a Frame (see the addKeyFrame() methods). In the latter - case, the path will automatically be updated when the Frame is modified (using the - Frame::modified() signal). - - The time has to be monotonously increasing over keyFrames. When interpolationSpeed() equals 1.0 - (default value), these times correspond to actual user's sec during interpolation (provided - that your main loop is fast enough). The interpolation is then real-time: the keyFrames will be - reached at their keyFrameTime(). - -

Interpolation details

- - When the user startInterpolation(), a timer is started which will update the frame()'s position - and orientation every interpolationPeriod() millisec. This update increases the - interpolationTime() by interpolationPeriod() * interpolationSpeed() millisec. - - Note that this mechanism ensures that the number of interpolation steps is constant and equal to - the total path duration() divided by the interpolationPeriod() * interpolationSpeed(). This is - especially useful for benchmarking or movie creation (constant number of snapshots). - - During the interpolation, the KeyFrameInterpolator emits an interpolated() signal, which will - usually be connected to the QGLViewer::update() slot. The interpolation is stopped when - interpolationTime() is greater than the lastTime() (unless loopInterpolation() is \c true) and the - endReached() signal is then emitted. - - Note that a Camera has Camera::keyFrameInterpolator(), that can be used to drive the Camera along a - path, or to restore a saved position (a path made of a single keyFrame). Press Alt+Fx to define a - new keyFrame for path x. Pressing Fx plays/pauses path interpolation. See QGLViewer::pathKey() and - the keyboard page for details. - - \attention If a Constraint is attached to the frame() (see Frame::constraint()), it should be - deactivated before interpolationIsStarted(), otherwise the interpolated motion (computed as if - there was no constraint) will probably be erroneous. - -

Retrieving interpolated values

- - This code defines a KeyFrameInterpolator, and displays the positions that will be followed by the - frame() along the path: - \code - KeyFrameInterpolator kfi( new Frame() ); - // calls to kfi.addKeyFrame() to define the path. - - const qreal deltaTime = 0.04; // output a position every deltaTime sec - for (qreal time=kfi.firstTime(); time<=kfi.lastTime(); time += deltaTime) - { - kfi.interpolateAtTime(time); - cout << "t=" << time << "\tpos=" << kfi.frame()->position() << endl; - } - \endcode - You may want to temporally disconnect the \c kfi interpolated() signal from the - QGLViewer::update() slot before calling this code. \nosubgrouping */ -class QGLVIEWER_EXPORT KeyFrameInterpolator : public QObject -{ - // todo closedPath, insertKeyFrames, deleteKeyFrame, replaceKeyFrame - Q_OBJECT - -public: - KeyFrameInterpolator(Frame* fr=NULL); - virtual ~KeyFrameInterpolator(); - -Q_SIGNALS: - /*! This signal is emitted whenever the frame() state is interpolated. - - The emission of this signal triggers the synchronous emission of the frame() - Frame::interpolated() signal, which may also be useful. - - This signal should especially be connected to your QGLViewer::update() slot, so that the display - is updated after every update of the KeyFrameInterpolator frame(): - \code - connect(myKeyFrameInterpolator, SIGNAL(interpolated()), SLOT(update())); - \endcode - Use the QGLViewer::QGLViewerPool() to connect the signal to all the viewers. - - Note that the QGLViewer::camera() Camera::keyFrameInterpolator() created using QGLViewer::pathKey() - have their interpolated() signals automatically connected to the QGLViewer::update() slot. */ - void interpolated(); - - /*! This signal is emitted when the interpolation reaches the first (when interpolationSpeed() - is negative) or the last keyFrame. - - When loopInterpolation() is \c true, interpolationTime() is reset and the interpolation - continues. It otherwise stops. */ - void endReached(); - - /*! @name Path creation */ - //@{ -public Q_SLOTS: - void addKeyFrame(const Frame& frame); - void addKeyFrame(const Frame& frame, qreal time); - - void addKeyFrame(const Frame* const frame); - void addKeyFrame(const Frame* const frame, qreal time); - - void deletePath(); - //@} - - /*! @name Associated Frame */ - //@{ -public: - /*! Returns the associated Frame and that is interpolated by the KeyFrameInterpolator. - - When interpolationIsStarted(), this Frame's position and orientation will regularly be updated - by a timer, so that they follow the KeyFrameInterpolator path. - - Set using setFrame() or with the KeyFrameInterpolator constructor. */ - Frame* frame() const { return frame_; } - -public Q_SLOTS: - void setFrame(Frame* const frame); - //@} - - /*! @name Path parameters */ - //@{ -public: - Frame keyFrame(int index) const; - qreal keyFrameTime(int index) const; - /*! Returns the number of keyFrames used by the interpolation. Use addKeyFrame() to add new keyFrames. */ - int numberOfKeyFrames() const { return keyFrame_.count(); } - qreal duration() const; - qreal firstTime() const; - qreal lastTime() const; - //@} - - /*! @name Interpolation parameters */ - //@{ -public: - /*! Returns the current interpolation time (in sec) along the KeyFrameInterpolator path. - - This time is regularly updated when interpolationIsStarted(). Can be set directly with - setInterpolationTime() or interpolateAtTime(). */ - qreal interpolationTime() const { return interpolationTime_; } - /*! Returns the current interpolation speed. - - Default value is 1.0, which means keyFrameTime() will be matched during the interpolation - (provided that your main loop is fast enough). - - A negative value will result in a reverse interpolation of the keyFrames. See also - interpolationPeriod(). */ - qreal interpolationSpeed() const { return interpolationSpeed_; } - /*! Returns the current interpolation period, expressed in millisec. - - The update of the frame() state will be done by a timer at this period when - interpolationIsStarted(). - - This period (multiplied by interpolationSpeed()) is added to the interpolationTime() at each - update, and the frame() state is modified accordingly (see interpolateAtTime()). Default value - is 40 millisec. */ - int interpolationPeriod() const { return period_; } - /*! Returns \c true when the interpolation is played in an infinite loop. - - When \c false (default), the interpolation stops when interpolationTime() reaches firstTime() - (with negative interpolationSpeed()) or lastTime(). - - interpolationTime() is otherwise reset to firstTime() (+ interpolationTime() - lastTime()) (and - inversely for negative interpolationSpeed()) and interpolation continues. - - In both cases, the endReached() signal is emitted. */ - bool loopInterpolation() const { return loopInterpolation_; } -#ifndef DOXYGEN - /*! Whether or not (default) the path defined by the keyFrames is a closed loop. When \c true, - the last and the first KeyFrame are linked by a new spline segment. - - Use setLoopInterpolation() to create a continuous animation over the entire path. - \attention The closed path feature is not yet implemented. */ - bool closedPath() const { return closedPath_; } -#endif -public Q_SLOTS: - /*! Sets the interpolationTime(). - - \attention The frame() state is not affected by this method. Use this function to define the - starting time of a future interpolation (see startInterpolation()). Use interpolateAtTime() to - actually interpolate at a given time. */ - void setInterpolationTime(qreal time) { interpolationTime_ = time; } - /*! Sets the interpolationSpeed(). Negative or null values are allowed. */ - void setInterpolationSpeed(qreal speed) { interpolationSpeed_ = speed; } - /*! Sets the interpolationPeriod(). */ - void setInterpolationPeriod(int period) { period_ = period; } - /*! Sets the loopInterpolation() value. */ - void setLoopInterpolation(bool loop=true) { loopInterpolation_ = loop; } -#ifndef DOXYGEN - /*! Sets the closedPath() value. \attention The closed path feature is not yet implemented. */ - void setClosedPath(bool closed=true) { closedPath_ = closed; } -#endif - //@} - - - /*! @name Interpolation */ - //@{ -public: - /*! Returns \c true when the interpolation is being performed. Use startInterpolation(), - stopInterpolation() or toggleInterpolation() to modify this state. */ - bool interpolationIsStarted() const { return interpolationStarted_; } -public Q_SLOTS: - void startInterpolation(int period = -1); - void stopInterpolation(); - void resetInterpolation(); - /*! Calls startInterpolation() or stopInterpolation(), depending on interpolationIsStarted(). */ - void toggleInterpolation() { if (interpolationIsStarted()) stopInterpolation(); else startInterpolation(); } - 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: - virtual QDomElement domElement(const QString& name, QDomDocument& document) const; - virtual void initFromDOMElement(const QDomElement& element); - //@} - -private Q_SLOTS: - virtual void update(); - virtual void invalidateValues() { valuesAreValid_ = false; pathIsValid_ = false; splineCacheIsValid_ = false; } - -private: - // Copy constructor and opertor= are declared private and undefined - // Prevents everyone from trying to use them - // KeyFrameInterpolator(const KeyFrameInterpolator& kfi); - // KeyFrameInterpolator& operator=(const KeyFrameInterpolator& kfi); - - void updateCurrentKeyFrameForTime(qreal time); - void updateModifiedFrameValues(); - void updateSplineCache(); - -#ifndef DOXYGEN - // Internal private KeyFrame representation - class KeyFrame - { - public: - KeyFrame(const Frame& fr, qreal t); - KeyFrame(const Frame* fr, qreal t); - - Vec position() const { return p_; } - Quaternion orientation() const { return q_; } - Vec tgP() const { return tgP_; } - Quaternion tgQ() const { return tgQ_; } - qreal time() const { return time_; } - const Frame* frame() const { return frame_; } - void updateValuesFromPointer(); - void flipOrientationIfNeeded(const Quaternion& prev); - void computeTangent(const KeyFrame* const prev, const KeyFrame* const next); - private: - Vec p_, tgP_; - Quaternion q_, tgQ_; - qreal time_; - const Frame* const frame_; - }; -#endif - - // K e y F r a m e s - mutable QList keyFrame_; - QMutableListIterator* currentFrame_[4]; - QList path_; - - // A s s o c i a t e d f r a m e - Frame* frame_; - - // R h y t h m - QTimer timer_; - int period_; - qreal interpolationTime_; - qreal interpolationSpeed_; - bool interpolationStarted_; - - // M i s c - bool closedPath_; - bool loopInterpolation_; - - // C a c h e d v a l u e s a n d f l a g s - bool pathIsValid_; - bool valuesAreValid_; - bool currentFrameValid_; - bool splineCacheIsValid_; - Vec v1, v2; -}; - -} // namespace qglviewer - -#endif // QGLVIEWER_KEY_FRAME_INTERPOLATOR_H diff --git a/code/3rd_QGLViewer-2.6.3/manipulatedCameraFrame.cpp b/code/3rd_QGLViewer-2.6.3/manipulatedCameraFrame.cpp deleted file mode 100644 index ac275747..00000000 --- a/code/3rd_QGLViewer-2.6.3/manipulatedCameraFrame.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "domUtils.h" -#include "manipulatedCameraFrame.h" -#include "qglviewer.h" - -#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_(true) -{ - 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), 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 millisec 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; -} diff --git a/code/3rd_QGLViewer-2.6.3/manipulatedCameraFrame.h b/code/3rd_QGLViewer-2.6.3/manipulatedCameraFrame.h deleted file mode 100644 index 1fe7aada..00000000 --- a/code/3rd_QGLViewer-2.6.3/manipulatedCameraFrame.h +++ /dev/null @@ -1,233 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_MANIPULATED_CAMERA_FRAME_H -#define QGLVIEWER_MANIPULATED_CAMERA_FRAME_H - -#include "manipulatedFrame.h" - -namespace qglviewer { -/*! \brief The ManipulatedCameraFrame class represents a ManipulatedFrame with Camera specific mouse bindings. - \class ManipulatedCameraFrame manipulatedCameraFrame.h QGLViewer/manipulatedCameraFrame.h - - A ManipulatedCameraFrame is a specialization of a ManipulatedFrame, designed to be set as the - Camera::frame(). Mouse motions are basically interpreted in a negated way: when the mouse goes to - the right, the ManipulatedFrame translation goes to the right, while the ManipulatedCameraFrame - has to go to the \e left, so that the \e scene seems to move to the right. - - A ManipulatedCameraFrame rotates around its pivotPoint(), which corresponds to the - associated Camera::pivotPoint(). - - 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. - - See the mouse page for a description of the possible actions that can - be performed using the mouse and their bindings. - \nosubgrouping */ -class QGLVIEWER_EXPORT ManipulatedCameraFrame : public qglviewer::ManipulatedFrame -{ -#ifndef DOXYGEN - friend class Camera; - friend class ::QGLViewer; -#endif - - Q_OBJECT - -public: - ManipulatedCameraFrame(); - /*! Virtual destructor. Empty. */ - virtual ~ManipulatedCameraFrame() {} - - ManipulatedCameraFrame(const ManipulatedCameraFrame& mcf); - ManipulatedCameraFrame& operator=(const ManipulatedCameraFrame& mcf); - - /*! @name Pivot point */ - //@{ -public: - /*! Returns the point the ManipulatedCameraFrame pivot point, around which the camera rotates. - - It is defined in the world coordinate system. Default value is (0,0,0). - - 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. */ - void setPivotPoint(const Vec& point) { pivotPoint_ = point; } - -#ifndef DOXYGEN - Vec revolveAroundPoint() const { qWarning("revolveAroundPoint() is deprecated, use pivotPoint() instead"); return pivotPoint(); } - void setRevolveArountPoint(const Vec& point) { qWarning("setRevolveAroundPoint() is deprecated, use setPivotPoint() instead"); setPivotPoint(point); } -#endif - //@} - - /*! @name Camera manipulation */ - //@{ -public: - /*! Returns \c true when the frame's rotation is constrained around the sceneUpVector(), - and \c false otherwise, when the rotation is completely free (default). - - In free mode, the associated camera can be arbitrarily rotated in the scene, along its - three axis, thus possibly leading to any arbitrary orientation. - - When you setRotatesAroundUpVector() to \c true, the sceneUpVector() defines a - 'vertical' direction around which the camera rotates. The camera can rotate left - or right, around this axis. It can also be moved up or down to show the 'top' and - 'bottom' views of the scene. As a result, the sceneUpVector() will always appear vertical - in the scene, and the horizon is preserved and stays projected along the camera's - horizontal axis. - - Note that setting this value to \c true when the sceneUpVector() is not already - vertically projected will break these invariants. It will also limit the possible movement - of the camera, possibly up to a lock when the sceneUpVector() is projected horizontally. - Use Camera::setUpVector() to define the sceneUpVector() and align the camera before calling - this method to ensure this does not happen. */ - bool rotatesAroundUpVector() const { return rotatesAroundUpVector_; } - /*! Sets the value of rotatesAroundUpVector(). - - Default value is false (free rotation). */ - void setRotatesAroundUpVector(bool constrained) { rotatesAroundUpVector_ = constrained; } - - /*! Returns whether or not the QGLViewer::ZOOM action zooms on the pivot point. - - When set to \c false (default), a zoom action will move the camera along its Camera::viewDirection(), - i.e. back and forth along a direction perpendicular to the projection screen. - - setZoomsOnPivotPoint() to \c true will move the camera along an axis defined by the - Camera::pivotPoint() and its current position instead. As a result, the projected position of the - pivot point on screen will stay the same during a zoom. */ - bool zoomsOnPivotPoint() const { return zoomsOnPivotPoint_; } - /*! Sets the value of zoomsOnPivotPoint(). - - Default value is false. */ - void setZoomsOnPivotPoint(bool enabled) { zoomsOnPivotPoint_ = enabled; } - -private: -#ifndef DOXYGEN - void zoom(qreal delta, const Camera * const camera); -#endif - //@} - - /*! @name Fly parameters */ - //@{ -public Q_SLOTS: - /*! Sets the flySpeed(), defined in OpenGL units. - - Default value is 0.0, but it is modified according to the QGLViewer::sceneRadius() when the - ManipulatedCameraFrame is set as the Camera::frame(). */ - void setFlySpeed(qreal speed) { flySpeed_ = speed; } - - /*! Sets the sceneUpVector(), defined in the world coordinate system. - - Default value is (0,1,0), but it is updated by the Camera when this object is set as its Camera::frame(). - Using Camera::setUpVector() instead is probably a better solution. */ - void setSceneUpVector(const Vec& up) { sceneUpVector_ = up; } - -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. - - \attention When the ManipulatedCameraFrame is set as the Camera::frame(), this value is set - according to the QGLViewer::sceneRadius() by QGLViewer::setSceneRadius(). */ - qreal flySpeed() const { return flySpeed_; } - - /*! 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 displacements of the mouse rotate - the ManipulatedCameraFrame around this vector. Vertical displacements rotate always around the - Camera \c X axis. - - This value is also used when setRotationIsConstrained() is set to \c true to define the up vector - (and incidentally the 'horizon' plane) around which the camera will rotate. - - Default value is (0,1,0), but it is updated by the Camera when this object is set as its Camera::frame(). - Camera::setOrientation() and Camera::setUpVector()) direclty modify this value and should be used - instead. */ - Vec sceneUpVector() const { return sceneUpVector_; } - -#ifndef DOXYGEN - Vec flyUpVector() const; - void setFlyUpVector(const Vec& up); -#endif - //@} - - /*! @name Mouse event handlers */ - //@{ -protected: - virtual void mouseReleaseEvent(QMouseEvent* const event, Camera* const camera); - virtual void mouseMoveEvent (QMouseEvent* const event, Camera* const camera); - virtual void wheelEvent (QWheelEvent* const event, Camera* const camera); - //@} - - /*! @name Spinning */ - //@{ -protected Q_SLOTS: - virtual void spin(); - //@} - - /*! @name XML representation */ - //@{ -public: - virtual QDomElement domElement(const QString& name, QDomDocument& document) const; -public Q_SLOTS: - virtual void initFromDOMElement(const QDomElement& element); - //@} - -#ifndef DOXYGEN -protected: - virtual void startAction(int ma, bool withConstraint=true); // int is really a QGLViewer::MouseAction -#endif - -private Q_SLOTS: - virtual void flyUpdate(); - -private: - void updateSceneUpVector(); - Quaternion turnQuaternion(int x, const Camera* const camera); - Quaternion pitchYawQuaternion(int x, int y, const Camera* const camera); - -private: - // Fly mode data - qreal flySpeed_; - qreal driveSpeed_; - Vec sceneUpVector_; - QTimer flyTimer_; - - bool rotatesAroundUpVector_; - // Inverse the direction of an horizontal mouse motion. Depends on the projected - // screen orientation of the vertical axis when the mouse button is pressed. - bool constrainedRotationIsReversed_; - - bool zoomsOnPivotPoint_; - - Vec pivotPoint_; -}; - -} // namespace qglviewer - -#endif // QGLVIEWER_MANIPULATED_CAMERA_FRAME_H diff --git a/code/3rd_QGLViewer-2.6.3/manipulatedFrame.cpp b/code/3rd_QGLViewer-2.6.3/manipulatedFrame.cpp deleted file mode 100644 index aba34420..00000000 --- a/code/3rd_QGLViewer-2.6.3/manipulatedFrame.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "domUtils.h" -#include "manipulatedFrame.h" -#include "manipulatedCameraFrame.h" -#include "qglviewer.h" -#include "camera.h" - -#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 millisec. 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/millisec. 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 diff --git a/code/3rd_QGLViewer-2.6.3/manipulatedFrame.h b/code/3rd_QGLViewer-2.6.3/manipulatedFrame.h deleted file mode 100644 index 7977bf50..00000000 --- a/code/3rd_QGLViewer-2.6.3/manipulatedFrame.h +++ /dev/null @@ -1,335 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_MANIPULATED_FRAME_H -#define QGLVIEWER_MANIPULATED_FRAME_H - -#include "frame.h" -#include "mouseGrabber.h" -#include "qglviewer.h" - -#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 - - It converts the mouse motion into a translation and an orientation updates. A ManipulatedFrame is - used to move an object in the scene. Combined with object selection, its MouseGrabber properties - and a dynamic update of the scene, the ManipulatedFrame introduces a great reactivity in your - applications. - - A ManipulatedFrame is attached to a QGLViewer using QGLViewer::setManipulatedFrame(): - \code - init() { setManipulatedFrame( new ManipulatedFrame() ); } - - draw() - { - glPushMatrix(); - glMultMatrixd(manipulatedFrame()->matrix()); - // draw the manipulated object here - glPopMatrix(); - } - \endcode - See the manipulatedFrame example for a complete - application. - - Mouse events are normally sent to the QGLViewer::camera(). You have to press the QGLViewer::FRAME - state key (default is \c Control) to move the QGLViewer::manipulatedFrame() instead. See the mouse page for a description of mouse button bindings. - -

Inherited functionalities

- - A ManipulatedFrame is an overloaded instance of a Frame. The powerful coordinate system - transformation functions (Frame::coordinatesOf(), Frame::transformOf(), ...) can hence be applied - to a ManipulatedFrame. - - A ManipulatedFrame is also a MouseGrabber. If the mouse cursor gets within a distance of 10 pixels - from the projected position of the ManipulatedFrame, the ManipulatedFrame becomes the new - QGLViewer::mouseGrabber(). It can then be manipulated directly, without any specific state key, - object selection or GUI intervention. This is very convenient to directly move some objects in the - scene (typically a light). See the mouseGrabber - example as an illustration. Note that QWidget::setMouseTracking() needs to be enabled in order - to use this feature (see the MouseGrabber documentation). - -

Advanced functionalities

- - A QGLViewer can handle at most one ManipulatedFrame at a time. If you want to move several objects - in the scene, you simply have to keep a list of the different ManipulatedFrames, and to activate - the right one (using QGLViewer::setManipulatedFrame()) when needed. This can for instance be done - according to an object selection: see the luxo example for an - illustration. - - When the ManipulatedFrame is being manipulated using the mouse (mouse pressed and not yet - released), isManipulated() returns \c true. This might be used to trigger a specific action or - display (as is done with QGLViewer::fastDraw()). - - The ManipulatedFrame also emits a manipulated() signal each time its state is modified by the - mouse. This signal is automatically connected to the QGLViewer::update() slot when the - ManipulatedFrame is attached to a viewer using QGLViewer::setManipulatedFrame(). - - You can make the ManipulatedFrame spin() if you release the rotation mouse button while moving the - mouse fast enough (see spinningSensitivity()). See also translationSensitivity() and - rotationSensitivity() for sensitivity tuning. \nosubgrouping */ -class QGLVIEWER_EXPORT ManipulatedFrame : public qglviewer::Frame, public MouseGrabber -{ -#ifndef DOXYGEN - friend class Camera; - friend class ::QGLViewer; -#endif - - Q_OBJECT - -public: - ManipulatedFrame(); - /*! Virtual destructor. Empty. */ - virtual ~ManipulatedFrame() {} - - ManipulatedFrame(const ManipulatedFrame& mf); - ManipulatedFrame& operator=(const ManipulatedFrame& mf); - -Q_SIGNALS: - /*! This signal is emitted when ever the ManipulatedFrame is manipulated (i.e. rotated or - translated) using the mouse. Connect this signal to any object that should be notified. - - Note that this signal is automatically connected to the QGLViewer::update() slot, when the - ManipulatedFrame is attached to a viewer using QGLViewer::setManipulatedFrame(), which is - probably all you need. - - Use the QGLViewer::QGLViewerPool() if you need to connect this signal to all the viewers. - - See also the spun(), modified(), interpolated() and KeyFrameInterpolator::interpolated() - signals' documentations. */ - void manipulated(); - - /*! This signal is emitted when the ManipulatedFrame isSpinning(). - - Note that for the QGLViewer::manipulatedFrame(), this signal is automatically connected to the - QGLViewer::update() slot. - - Connect this signal to any object that should be notified. Use the QGLViewer::QGLViewerPool() if - you need to connect this signal to all the viewers. - - See also the manipulated(), modified(), interpolated() and KeyFrameInterpolator::interpolated() - signals' documentations. */ - void spun(); - - /*! @name Manipulation sensitivity */ - //@{ -public Q_SLOTS: - /*! Defines the rotationSensitivity(). */ - void setRotationSensitivity(qreal sensitivity) { rotationSensitivity_ = sensitivity; } - /*! Defines the translationSensitivity(). */ - void setTranslationSensitivity(qreal sensitivity) { translationSensitivity_ = sensitivity; } - /*! Defines the spinningSensitivity(), in pixels per millisec. */ - void setSpinningSensitivity(qreal sensitivity) { spinningSensitivity_ = sensitivity; } - /*! Defines the wheelSensitivity(). */ - void setWheelSensitivity(qreal sensitivity) { wheelSensitivity_ = sensitivity; } - /*! Defines the zoomSensitivity(). */ - void setZoomSensitivity(qreal sensitivity) { zoomSensitivity_ = sensitivity; } - -public: - /*! Returns the influence of a mouse displacement on the ManipulatedFrame rotation. - - Default value is 1.0. With an identical mouse displacement, a higher value will generate a - larger rotation (and inversely for lower values). A 0.0 value will forbid ManipulatedFrame mouse - rotation (see also constraint()). - - See also setRotationSensitivity(), translationSensitivity(), spinningSensitivity() and - wheelSensitivity(). */ - qreal rotationSensitivity() const { return rotationSensitivity_; } - /*! Returns the influence of a mouse displacement on the ManipulatedFrame translation. - - Default value is 1.0. You should not have to modify this value, since with 1.0 the - ManipulatedFrame precisely stays under the mouse cursor. - - With an identical mouse displacement, a higher value will generate a larger translation (and - inversely for lower values). A 0.0 value will forbid ManipulatedFrame mouse translation (see - also constraint()). - - \note When the ManipulatedFrame is used to move a \e Camera (see the ManipulatedCameraFrame - class documentation), after zooming on a small region of your scene, the camera may translate - too fast. For a camera, it is the Camera::pivotPoint() that exactly matches the mouse - displacement. Hence, instead of changing the translationSensitivity(), solve the problem by - (temporarily) setting the Camera::pivotPoint() to a point on the zoomed region (see the - QGLViewer::RAP_FROM_PIXEL mouse binding in the mouse page). - - See also setTranslationSensitivity(), rotationSensitivity(), spinningSensitivity() and - wheelSensitivity(). */ - qreal translationSensitivity() const { return translationSensitivity_; } - /*! Returns the minimum mouse speed required (at button release) to make the ManipulatedFrame - spin(). - - See spin(), spinningQuaternion() and startSpinning() for details. - - Mouse speed is expressed in pixels per millisec. Default value is 0.3 (300 pixels per - second). Use setSpinningSensitivity() to tune this value. A higher value will make spinning more - difficult (a value of 100.0 forbids spinning in practice). - - See also setSpinningSensitivity(), translationSensitivity(), rotationSensitivity() and - wheelSensitivity(). */ - qreal spinningSensitivity() const { return spinningSensitivity_; } - - /*! Returns the zoom sensitivity. - - Default value is 1.0. A higher value will make the zoom faster. - Use a negative value to invert the zoom in and out directions. - - See also setZoomSensitivity(), translationSensitivity(), rotationSensitivity() wheelSensitivity() - and spinningSensitivity(). */ - qreal zoomSensitivity() const { return zoomSensitivity_; } - /*! Returns the mouse wheel sensitivity. - - Default value is 1.0. A higher value will make the wheel action more efficient (usually meaning - a faster zoom). Use a negative value to invert the zoom in and out directions. - - See also setWheelSensitivity(), translationSensitivity(), rotationSensitivity() zoomSensitivity() - and spinningSensitivity(). */ - qreal wheelSensitivity() const { return wheelSensitivity_; } - //@} - - - /*! @name Spinning */ - //@{ -public: - /*! Returns \c true when the ManipulatedFrame is spinning. - - During spinning, spin() rotates the ManipulatedFrame by its spinningQuaternion() at a frequency - defined when the ManipulatedFrame startSpinning(). - - Use startSpinning() and stopSpinning() to change this state. Default value is \c false. */ - bool isSpinning() const { return isSpinning_; } - /*! Returns the incremental rotation that is applied by spin() to the ManipulatedFrame - orientation when it isSpinning(). - - Default value is a null rotation (identity Quaternion). Use setSpinningQuaternion() to change - this value. - - The spinningQuaternion() axis is defined in the ManipulatedFrame coordinate system. You can use - Frame::transformOfFrom() to convert this axis from an other Frame coordinate system. */ - Quaternion spinningQuaternion() const { return spinningQuaternion_; } -public Q_SLOTS: - /*! Defines the spinningQuaternion(). Its axis is defined in the ManipulatedFrame coordinate - system. */ - void setSpinningQuaternion(const Quaternion& spinningQuaternion) { spinningQuaternion_ = spinningQuaternion; } - virtual void startSpinning(int updateInterval); - /*! Stops the spinning motion started using startSpinning(). isSpinning() will return \c false - after this call. */ - virtual void stopSpinning() { spinningTimer_.stop(); isSpinning_ = false; } -protected Q_SLOTS: - virtual void spin(); -private Q_SLOTS: - void spinUpdate(); - //@} - - /*! @name Mouse event handlers */ - //@{ -protected: - virtual void mousePressEvent (QMouseEvent* const event, Camera* const camera); - virtual void mouseMoveEvent (QMouseEvent* const event, Camera* const camera); - virtual void mouseReleaseEvent (QMouseEvent* const event, Camera* const camera); - virtual void mouseDoubleClickEvent(QMouseEvent* const event, Camera* const camera); - virtual void wheelEvent (QWheelEvent* const event, Camera* const camera); - //@} - -public: - /*! @name Current state */ - //@{ - bool isManipulated() const; - /*! Returns the \c MouseAction currently applied to this ManipulatedFrame. - - Will return QGLViewer::NO_MOUSE_ACTION unless a mouse button is being pressed - and has been bound to this QGLViewer::MouseHandler. - - The binding between mouse buttons and key modifiers and MouseAction is set using - QGLViewer::setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton buttons, MouseHandler handler, MouseAction action, bool withConstraint). - */ - QGLViewer::MouseAction currentMouseAction() const { return action_; } - //@} - - /*! @name MouseGrabber implementation */ - //@{ -public: - virtual void checkIfGrabsMouse(int x, int y, const Camera* const camera); - //@} - - /*! @name XML representation */ - //@{ -public: - virtual QDomElement domElement(const QString& name, QDomDocument& document) const; -public Q_SLOTS: - virtual void initFromDOMElement(const QDomElement& element); - //@} - -#ifndef DOXYGEN -protected: - Quaternion deformedBallQuaternion(int x, int y, qreal cx, qreal cy, const Camera* const camera); - - QGLViewer::MouseAction action_; - Constraint* previousConstraint_; // When manipulation is without Contraint. - - virtual void startAction(int ma, bool withConstraint=true); // int is really a QGLViewer::MouseAction - void computeMouseSpeed(const QMouseEvent* const e); - int mouseOriginalDirection(const QMouseEvent* const e); - - /*! Returns a screen scaled delta from event's position to prevPos_, along the - X or Y direction, whichever has the largest magnitude. */ - qreal deltaWithPrevPos(QMouseEvent* const event, Camera* const camera) const; - /*! Returns a normalized wheel delta, proportionnal to wheelSensitivity(). */ - qreal wheelDelta(const QWheelEvent* event) const; - - // Previous mouse position (used for incremental updates) and mouse press position. - QPoint prevPos_, pressPos_; - -private: - void zoom(qreal delta, const Camera * const camera); - -#endif // DOXYGEN - -private: - // Sensitivity - qreal rotationSensitivity_; - qreal translationSensitivity_; - qreal spinningSensitivity_; - qreal wheelSensitivity_; - qreal zoomSensitivity_; - - // Mouse speed and spinning - QTime last_move_time; - qreal mouseSpeed_; - int delay_; - bool isSpinning_; - QTimer spinningTimer_; - Quaternion spinningQuaternion_; - - // Whether the SCREEN_TRANS direction (horizontal or vertical) is fixed or not. - bool dirIsFixed_; - - // MouseGrabber - bool keepsGrabbingMouse_; -}; - -} // namespace qglviewer - -#endif // QGLVIEWER_MANIPULATED_FRAME_H diff --git a/code/3rd_QGLViewer-2.6.3/mouseGrabber.cpp b/code/3rd_QGLViewer-2.6.3/mouseGrabber.cpp deleted file mode 100644 index 38cfdd01..00000000 --- a/code/3rd_QGLViewer-2.6.3/mouseGrabber.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "mouseGrabber.h" - -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(); -} diff --git a/code/3rd_QGLViewer-2.6.3/mouseGrabber.h b/code/3rd_QGLViewer-2.6.3/mouseGrabber.h deleted file mode 100644 index e66a8b4e..00000000 --- a/code/3rd_QGLViewer-2.6.3/mouseGrabber.h +++ /dev/null @@ -1,264 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_MOUSE_GRABBER_H -#define QGLVIEWER_MOUSE_GRABBER_H - -#include "config.h" - -#include - -class QGLViewer; - -namespace qglviewer { -class Camera; - -/*! \brief Abstract class for objects that grab mouse focus in a QGLViewer. - \class MouseGrabber mouseGrabber.h QGLViewer/mouseGrabber.h - - MouseGrabber are objects which react to the mouse cursor, usually when it hovers over them. This - abstract class only provides an interface for all these objects: their actual behavior has to be - defined in a derived class. - -

How does it work ?

- - All the created MouseGrabber are grouped in a MouseGrabberPool(). The QGLViewers parse this pool, - calling all the MouseGrabbers' checkIfGrabsMouse() methods that setGrabsMouse() if desired. - - When a MouseGrabber grabsMouse(), it becomes the QGLViewer::mouseGrabber(). All the mouse events - (mousePressEvent(), mouseReleaseEvent(), mouseMoveEvent(), mouseDoubleClickEvent() and - wheelEvent()) are then transmitted to the QGLViewer::mouseGrabber() instead of being normally - processed. This continues while grabsMouse() (updated using checkIfGrabsMouse()) returns \c true. - - If you want to (temporarily) disable a specific MouseGrabbers, you can remove it from this pool - using removeFromMouseGrabberPool(). You can also disable a MouseGrabber in a specific QGLViewer - using QGLViewer::setMouseGrabberIsEnabled(). - -

Implementation details

- - In order to make MouseGrabber react to mouse events, mouse tracking has to be activated in the - QGLViewer which wants to use MouseGrabbers: - \code - init() { setMouseTracking(true); } - \endcode - Call \c QGLWidget::hasMouseTracking() to get the current state of this flag. - - The \p camera parameter of the different mouse event methods is a pointer to the - QGLViewer::camera() of the QGLViewer that uses the MouseGrabber. It can be used to compute 2D to - 3D coordinates conversion using Camera::projectedCoordinatesOf() and - Camera::unprojectedCoordinatesOf(). - - Very complex behaviors can be implemented using this framework: auto-selected objects (no need to - press a key to use them), automatic drop-down menus, 3D GUI, spinners using the wheelEvent(), and - whatever your imagination creates. See the mouseGrabber - example for an illustration. - - Note that ManipulatedFrame are MouseGrabber: see the keyFrame - example for an illustration. Every created ManipulatedFrame is hence present in the - MouseGrabberPool() (note however that ManipulatedCameraFrame are not inserted). - -

Example

- - Here is for instance a draft version of a MovableObject class. Instances of these class can freely - be moved on screen using the mouse, as movable post-it-like notes: - \code - class MovableObject : public MouseGrabber - { - public: - MovableObject() : pos(0,0), moved(false) {} - - void checkIfGrabsMouse(int x, int y, const qglviewer::Camera* const) - { - // MovableObject is active in a region of 5 pixels around its pos. - // May depend on the actual shape of the object. Customize as desired. - // Once clicked (moved = true), it keeps grabbing mouse until button is released. - setGrabsMouse( moved || ((pos-QPoint(x,y)).manhattanLength() < 5) ); - } - - void mousePressEvent( QMouseEvent* const e, Camera* const) { prevPos = e->pos(); moved = true; } - - void mouseMoveEvent(QMouseEvent* const e, const Camera* const) - { - if (moved) - { - // Add position delta to current pos - pos += e->pos() - prevPos; - prevPos = e->pos(); - } - } - - void mouseReleaseEvent(QMouseEvent* const, Camera* const) { moved = false; } - - void draw() - { - // The object is drawn centered on its pos, with different possible aspects: - if (grabsMouse()) - if (moved) - // Object being moved, maybe a transparent display - else - // Object ready to be moved, maybe a highlighted visual feedback - else - // Normal display - } - - private: - QPoint pos, prevPos; - bool moved; - }; - \endcode - Note that the different event callback methods are called only once the MouseGrabber grabsMouse(). - \nosubgrouping */ -class QGLVIEWER_EXPORT MouseGrabber -{ -#ifndef DOXYGEN - friend class ::QGLViewer; -#endif - -public: - MouseGrabber(); - /*! Virtual destructor. Removes the MouseGrabber from the MouseGrabberPool(). */ - virtual ~MouseGrabber() { MouseGrabber::MouseGrabberPool_.removeAll(this); } - - /*! @name Mouse grabbing detection */ - //@{ -public: - /*! Pure virtual method, called by the QGLViewers before they test if the MouseGrabber - grabsMouse(). Should setGrabsMouse() according to the mouse position. - - This is the core method of the MouseGrabber. It has to be overloaded in your derived class. - Its goal is to update the grabsMouse() flag according to the mouse and MouseGrabber current - positions, using setGrabsMouse(). - - grabsMouse() is usually set to \c true when the mouse cursor is close enough to the MouseGrabber - position. It should also be set to \c false when the mouse cursor leaves this region in order to - release the mouse focus. - - \p x and \p y are the mouse cursor coordinates (Qt coordinate system: (0,0) corresponds to the upper - left corner). - - A typical implementation will look like: - \code - // (posX,posY) is the position of the MouseGrabber on screen. - // Here, distance to mouse must be less than 10 pixels to activate the MouseGrabber. - setGrabsMouse( sqrt((x-posX)*(x-posX) + (y-posY)*(y-posY)) < 10); - \endcode - - If the MouseGrabber position is defined in 3D, use the \p camera parameter, corresponding to - the calling QGLViewer Camera. Project on screen and then compare the projected coordinates: - \code - Vec proj = camera->projectedCoordinatesOf(myMouseGrabber->frame()->position()); - setGrabsMouse((fabs(x-proj.x) < 5) && (fabs(y-proj.y) < 2)); // Rectangular region - \endcode - - See examples in the detailed description section and in the mouseGrabber example. */ - virtual void checkIfGrabsMouse(int x, int y, const Camera* const camera) = 0; - - /*! Returns \c true when the MouseGrabber grabs the QGLViewer's mouse events. - - This flag is set with setGrabsMouse() by the checkIfGrabsMouse() method. */ - bool grabsMouse() const { return grabsMouse_; } - -protected: - /*! Sets the grabsMouse() flag. Normally used by checkIfGrabsMouse(). */ - void setGrabsMouse(bool grabs) { grabsMouse_ = grabs; } - //@} - - - /*! @name MouseGrabber pool */ - //@{ -public: - /*! Returns a list containing pointers to all the active MouseGrabbers. - - Used by the QGLViewer to parse all the MouseGrabbers and to check if any of them grabsMouse() - using checkIfGrabsMouse(). - - 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_; } - - /*! Returns \c true if the MouseGrabber is currently in the MouseGrabberPool() list. - - Default value is \c true. When set to \c false using removeFromMouseGrabberPool(), the - QGLViewers no longer checkIfGrabsMouse() on this MouseGrabber. Use addInMouseGrabberPool() to - insert it back. */ - bool isInMouseGrabberPool() const { return MouseGrabber::MouseGrabberPool_.contains(const_cast(this)); } - void addInMouseGrabberPool(); - void removeFromMouseGrabberPool(); - void clearMouseGrabberPool(bool autoDelete=false); - //@} - - - /*! @name Mouse event handlers */ - //@{ -protected: - /*! Callback method called when the MouseGrabber grabsMouse() and a mouse button is pressed. - - - The MouseGrabber will typically start an action or change its state when a mouse button is - pressed. mouseMoveEvent() (called at each mouse displacement) will then update the MouseGrabber - accordingly and mouseReleaseEvent() (called when the mouse button is released) will terminate - this action. - - Use the \p event QMouseEvent::state() and QMouseEvent::button() to test the keyboard - and button state and possibly change the MouseGrabber behavior accordingly. - - See the detailed description section and the mouseGrabber example for examples. - - See the \c QGLWidget::mousePressEvent() and the \c QMouseEvent documentations for details. */ - virtual void mousePressEvent(QMouseEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); } - /*! Callback method called when the MouseGrabber grabsMouse() and a mouse button is double clicked. - - See the \c QGLWidget::mouseDoubleClickEvent() and the \c QMouseEvent documentations for details. */ - virtual void mouseDoubleClickEvent(QMouseEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); } - /*! Mouse release event callback method. See mousePressEvent(). */ - virtual void mouseReleaseEvent(QMouseEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); } - /*! Callback method called when the MouseGrabber grabsMouse() and the mouse is moved while a - button is pressed. - - This method will typically update the state of the MouseGrabber from the mouse displacement. See - the mousePressEvent() documentation for details. */ - virtual void mouseMoveEvent(QMouseEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); } - /*! Callback method called when the MouseGrabber grabsMouse() and the mouse wheel is used. - - See the \c QGLWidget::wheelEvent() and the \c QWheelEvent documentations for details. */ - virtual void wheelEvent(QWheelEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); } - //@} - -private: - // Copy constructor and opertor= are declared private and undefined - // Prevents everyone from trying to use them - MouseGrabber(const MouseGrabber&); - MouseGrabber& operator=(const MouseGrabber&); - - bool grabsMouse_; - - // Q G L V i e w e r p o o l - static QList MouseGrabberPool_; -}; - -} // namespace qglviewer - -#endif // QGLVIEWER_MOUSE_GRABBER_H diff --git a/code/3rd_QGLViewer-2.6.3/qglviewer.cpp b/code/3rd_QGLViewer-2.6.3/qglviewer.cpp deleted file mode 100644 index bfaac63b..00000000 --- a/code/3rd_QGLViewer-2.6.3/qglviewer.cpp +++ /dev/null @@ -1,3886 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "domUtils.h" -#include "qglviewer.h" -#include "camera.h" -#include "keyFrameInterpolator.h" -#include "manipulatedCameraFrame.h" - -# 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 screenshot saving, 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() -{ - // Test OpenGL context - // if (glGetString(GL_VERSION) == 0) - // qWarning("Unable to get OpenGL version, context may not be available - Check your configuration"); - - 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(); - - setSnapshotFileName(tr("snapshot", "Default snapshot file name")); - initializeSnapshotFormats(); - setSnapshotCounter(0); - setSnapshotQuality(95); - - fpsTime_.start(); - fpsCounter_ = 0; - f_p_s_ = 0.0; - fpsString_ = tr("%1Hz", "Frames per sec, 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); - - tileRegion_ = NULL; -} - -#if !defined QT3_SUPPORT -/*! 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, const QGLWidget* shareWidget, Qt::WindowFlags flags) - : QGLWidget(parent, shareWidget, flags) -{ defaultConstructor(); } - -/*! Same as QGLViewer(), but a \c QGLContext can be provided so that viewers share GL contexts, even -with \c QGLContext sub-classes (use \p shareWidget otherwise). */ -QGLViewer::QGLViewer(QGLContext *context, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags flags) - : QGLWidget(context, parent, shareWidget, flags) -{ defaultConstructor(); } - -/*! Same as QGLViewer(), but a specific \c QGLFormat can be provided. - -This is for instance needed to ask for a stencil buffer or for stereo display (as is illustrated in -the stereoViewer example). */ -QGLViewer::QGLViewer(const QGLFormat& format, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags flags) - : QGLWidget(format, parent, shareWidget, flags) -{ defaultConstructor(); } -#endif // QT3_SUPPORT - -/*! 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 or to create display -lists. - -To make beginners' life easier and to simplify the examples, this method slightly modifies the -standard OpenGL state: -\code -glEnable(GL_LIGHT0); -glEnable(GL_LIGHTING); -glEnable(GL_DEPTH_TEST); -glEnable(GL_COLOR_MATERIAL); -\endcode - -If you port an existing application to QGLViewer and your display changes, you probably want to -disable these flags in init() to get back to a standard OpenGL state. */ -void QGLViewer::initializeGL() -{ - glEnable(GL_LIGHT0); - glEnable(GL_LIGHTING); - glEnable(GL_DEPTH_TEST); - glEnable(GL_COLOR_MATERIAL); - - // Default colors - setForegroundColor(QColor(180, 180, 180)); - setBackgroundColor(QColor(51, 51, 51)); - - // Clear the buffer where we're going to draw - if (format().stereo()) - { - glDrawBuffer(GL_BACK_RIGHT); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glDrawBuffer(GL_BACK_LEFT); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - else - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Calls user defined method. Default emits a signal. - init(); - - // Give time to glInit to finish and then call setFullScreen(). - if (isFullScreen()) - QTimer::singleShot( 100, this, SLOT(delayedFullScreen()) ); -} - -/*! Main paint method, inherited from \c QGLWidget. - -Calls the following methods, in that order: -\arg preDraw() (or preDrawStereo() if viewer displaysInStereo()) : places the camera in the world coordinate system. -\arg draw() (or fastDraw() when the camera is manipulated) : main drawing method. Should be overloaded. -\arg postDraw() : display of visual hints (world axis, FPS...) */ -void QGLViewer::paintGL() -{ - if (displaysInStereo()) - { - for (int view=1; view>=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() -{ - // Reset model view matrix to world coordinates origin - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - camera()->loadModelViewMatrix(); - // TODO restore model loadProjectionMatrixStereo - - // Save OpenGL state - glPushAttrib(GL_ALL_ATTRIB_BITS); - - // Set neutral GL state - glDisable(GL_TEXTURE_1D); - glDisable(GL_TEXTURE_2D); -#ifdef GL_TEXTURE_3D // OpenGL 1.2 Only... - glDisable(GL_TEXTURE_3D); -#endif - - glDisable(GL_TEXTURE_GEN_Q); - glDisable(GL_TEXTURE_GEN_R); - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - -#ifdef GL_RESCALE_NORMAL // OpenGL 1.2 Only... - glEnable(GL_RESCALE_NORMAL); -#endif - - glDisable(GL_COLOR_MATERIAL); - qglColor(foregroundColor()); - - if (cameraIsEdited()) - camera()->drawAllPaths(); - - // Pivot point, line when camera rolls, zoom region - drawVisualHints(); - - if (gridIsDrawn()) { glLineWidth(1.0); drawGrid(camera()->sceneRadius()); } - if (axisIsDrawn()) { glLineWidth(2.0); drawAxis(camera()->sceneRadius()); } - - // FPS computation - const unsigned int maxCounter = 20; - if (++fpsCounter_ == maxCounter) - { - f_p_s_ = 1000.0 * maxCounter / fpsTime_.restart(); - fpsString_ = tr("%1Hz", "Frames per sec, 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(SAVE_SCREENSHOT, Qt::CTRL+Qt::Key_S); - 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); - setShortcut(SNAPSHOT_TO_CLIPBOARD, Qt::CTRL+Qt::Key_C); - - keyboardActionDescription_[DISPLAY_FPS] = tr("Toggles the display of the FPS", "DISPLAY_FPS action description"); - keyboardActionDescription_[SAVE_SCREENSHOT] = tr("Saves a screenshot", "SAVE_SCREENSHOT 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"); - keyboardActionDescription_[SNAPSHOT_TO_CLIPBOARD] = tr("Copies a snapshot to clipboard", "SNAPSHOT_TO_CLIPBOARD 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 -{ - static GLUquadric* quadric = gluNewQuadric(); - - const qreal length = sceneRadius() / 5.0 * scale; - - GLboolean lightIsOn; - glGetBooleanv(light, &lightIsOn); - - if (lightIsOn) - { - // All light values are given in eye coordinates - glPushMatrix(); - glLoadIdentity(); - - float color[4]; - glGetLightfv(light, GL_DIFFUSE, color); - glColor4fv(color); - - float pos[4]; - glGetLightfv(light, GL_POSITION, pos); - - if (pos[3] != 0.0) - { - glTranslatef(pos[0]/pos[3], pos[1]/pos[3], pos[2]/pos[3]); - - GLfloat cutOff; - glGetLightfv(light, GL_SPOT_CUTOFF, &cutOff); - if (cutOff != 180.0) - { - GLfloat dir[4]; - glGetLightfv(light, GL_SPOT_DIRECTION, dir); - glMultMatrixd(Quaternion(Vec(0,0,1), Vec(dir)).matrix()); - QGLViewer::drawArrow(length); - gluCylinder(quadric, 0.0, 0.7 * length * sin(cutOff * M_PI / 180.0), 0.7 * length * cos(cutOff * M_PI / 180.0), 12, 1); - } - else - gluSphere(quadric, 0.2*length, 10, 10); - } - else - { - // Directional light. - Vec dir(pos[0], pos[1], pos[2]); - dir.normalize(); - Frame fr=Frame(camera()->cameraCoordinatesOf(4.0 * length * camera()->frame()->inverseTransformOf(dir)), - Quaternion(Vec(0,0,-1), dir)); - glMultMatrixd(fr.matrix()); - drawArrow(length); - } - - glPopMatrix(); - } -} - - -/*! 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 QGLWidget::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. - -\attention This method uses display lists to render the characters, with an index that starts at -2000 by default (see the QGLWidget::renderText() documentation). If you use more than 2000 Display -Lists, they may overlap with these. Directly use QGLWidget::renderText() in that case, with a -higher \c listBase parameter (or overload fontDisplayListBase).*/ -void QGLViewer::drawText(int x, int y, const QString& text, const QFont& fnt) -{ - if (!textIsEnabled()) - return; - - if (tileRegion_ != NULL) { - renderText(int((x-tileRegion_->xMin) * width() / (tileRegion_->xMax - tileRegion_->xMin)), - int((y-tileRegion_->yMin) * height() / (tileRegion_->yMax - tileRegion_->yMin)), text, scaledFont(fnt)); - } else - 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 millisec (default is 2 sec) 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(20, 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 upward) const -{ - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - if (tileRegion_ != NULL) - if (upward) - glOrtho(tileRegion_->xMin, tileRegion_->xMax, tileRegion_->yMin, tileRegion_->yMax, 0.0, -1.0); - else - glOrtho(tileRegion_->xMin, tileRegion_->xMax, tileRegion_->yMax, tileRegion_->yMin, 0.0, -1.0); - else - if (upward) - glOrtho(0, width(), 0, height(), 0.0, -1.0); - else - glOrtho(0, width(), height(), 0, 0.0, -1.0); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); -} - -/*! 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 -{ - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); -} - -/*! 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 QGLWidget::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(); - QGLWidget::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(). - -The default implementation uses the \c GL_SELECT mode to perform a selection. It uses -selectBuffer() and selectBufferSize() to define a \c glSelectBuffer(). The \c GL_PROJECTION is then -set using \c gluPickMatrix(), with a window selection size defined by selectRegionWidth() and -selectRegionHeight(). Finally, the \c GL_MODELVIEW matrix is set to the world coordinate system -using qglviewer::Camera::loadModelViewMatrix(). See the gluPickMatrix() documentation for details. - -You should not need to redefine this method (if you use the \c GL_SELECT mode to perform your -selection), since this code is fairly classical and can be tuned. You are more likely to overload -endSelection() if you want to use a more complex select buffer structure. */ -void QGLViewer::beginSelection(const QPoint& point) -{ - // Make OpenGL context current (may be needed with several viewers ?) - makeCurrent(); - - // Prepare the selection mode - glSelectBuffer(selectBufferSize(), selectBuffer()); - glRenderMode(GL_SELECT); - glInitNames(); - - // Loads the matrices - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - static GLint viewport[4]; - camera()->getViewport(viewport); - gluPickMatrix(point.x(), point.y(), selectRegionWidth(), selectRegionHeight(), viewport); - - // loadProjectionMatrix() first resets the GL_PROJECTION matrix with a glLoadIdentity(). - // The false parameter prevents this and hence multiplies the matrices. - camera()->loadProjectionMatrix(false); - // Reset the original (world coordinates) modelview matrix - camera()->loadModelViewMatrix(); -} - -/*! This method is called by select() after scene elements were drawn by drawWithNames(). It should -analyze the selection result to determine which object is actually selected. - -The default implementation relies on \c GL_SELECT mode (see beginSelection()). It assumes that -names were pushed and popped in drawWithNames(), and analyzes the selectBuffer() to find the name -that corresponds to the closer (z min) object. It then setSelectedName() to this value, or to -1 if -the selectBuffer() is empty (no object drawn in selection region). Use selectedName() (probably in -the postSelection() method) to retrieve this value and update your data structure accordingly. - -This default implementation, although sufficient for many cases is however limited and you may have -to overload this method. This will be the case if drawWithNames() uses several push levels in the -name heap. A more precise depth selection, for instance privileging points over edges and -triangles to avoid z precision problems, will also require an overloading. A typical implementation -will look like: -\code -glFlush(); - -// Get the number of objects that were seen through the pick matrix frustum. -// Resets GL_RENDER mode. -GLint nbHits = glRenderMode(GL_RENDER); - -if (nbHits <= 0) -setSelectedName(-1); -else -{ -// Interpret results: each object created values in the selectBuffer(). -// See the glSelectBuffer() man page for details on the buffer structure. -// The following code depends on your selectBuffer() structure. -for (int i=0; imultiSelect example for -a multi-object selection implementation of this method. */ -void QGLViewer::endSelection(const QPoint& point) -{ - Q_UNUSED(point); - - // Flush GL buffers - glFlush(); - - // Get the number of objects that were seen through the pick matrix frustum. Reset GL_RENDER mode. - GLint nbHits = glRenderMode(GL_RENDER); - - if (nbHits <= 0) - setSelectedName(-1); - else - { - // Interpret results: each object created 4 values in the selectBuffer(). - // selectBuffer[4*i+1] is the object minimum depth value, while selectBuffer[4*i+3] is the id pushed on the stack. - // Of all the objects that were projected in the pick region, we select the closest one (zMin comparison). - // This code needs to be modified if you use several stack levels. See glSelectBuffer() man page. - GLuint zMin = (selectBuffer())[1]; - setSelectedName(int((selectBuffer())[3])); - for (int i=1; iinterpolateToZoomOnPixel(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 QGLWidget::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 QGLWidget::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 SAVE_SCREENSHOT : saveSnapshot(false, false); break; - case FULL_SCREEN : toggleFullScreen(); break; - case STEREO : toggleStereoDisplay(); break; - case ANIMATION : toggleAnimation(); break; - case HELP : help(); break; - case EDIT_CAMERA : toggleCameraIsEdited(); break; - case SNAPSHOT_TO_CLIPBOARD : snapshotToClipboard(); 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) -{ - QGLWidget::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() -{ - // Pivot point cross - if (visualHint_ & 1) - { - const qreal size = 15.0; - Vec proj = camera()->projectedCoordinatesOf(camera()->pivotPoint()); - startScreenCoordinatesSystem(); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - glLineWidth(3.0); - glBegin(GL_LINES); - glVertex2d(proj.x - size, proj.y); - glVertex2d(proj.x + size, proj.y); - glVertex2d(proj.x, proj.y - size); - glVertex2d(proj.x, proj.y + size); - glEnd(); - glEnable(GL_DEPTH_TEST); - stopScreenCoordinatesSystem(); - } - - // if (visualHint_ & 2) - // drawText(80, 10, "Play"); - - // Screen rotate line - ManipulatedFrame* mf = NULL; - Vec pnt; - if (camera()->frame()->action_ == SCREEN_ROTATE) - { - mf = camera()->frame(); - pnt = camera()->pivotPoint(); - } - if (manipulatedFrame() && (manipulatedFrame()->action_ == SCREEN_ROTATE)) - { - mf = manipulatedFrame(); - // Maybe useful if the mf is a manipCameraFrame... - // pnt = manipulatedFrame()->pivotPoint(); - pnt = manipulatedFrame()->position(); - } - - if (mf) - { - pnt = camera()->projectedCoordinatesOf(pnt); - startScreenCoordinatesSystem(); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - glLineWidth(3.0); - glBegin(GL_LINES); - glVertex2d(pnt.x, pnt.y); - glVertex2i(mf->prevPos_.x(), mf->prevPos_.y()); - glEnd(); - glEnable(GL_DEPTH_TEST); - stopScreenCoordinatesSystem(); - } - - // Zoom on region: draw a rectangle - if (camera()->frame()->action_ == ZOOM_ON_REGION) - { - startScreenCoordinatesSystem(); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - glLineWidth(2.0); - glBegin(GL_LINE_LOOP); - glVertex2i(camera()->frame()->pressPos_.x(), camera()->frame()->pressPos_.y()); - glVertex2i(camera()->frame()->prevPos_.x(), camera()->frame()->pressPos_.y()); - glVertex2i(camera()->frame()->prevPos_.x(), camera()->frame()->prevPos_.y()); - glVertex2i(camera()->frame()->pressPos_.x(), camera()->frame()->prevPos_.y()); - glEnd(); - glEnable(GL_DEPTH_TEST); - stopScreenCoordinatesSystem(); - } -} - -/*! 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 millisec (default is 2 sec). */ -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 sec 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 along the positive Z axis. - -\p length, \p radius and \p nbSubdivisions define its geometry. If \p radius is negative -(default), it is set to 0.05 * \p length. - -Use drawArrow(const Vec& from, const Vec& to, qreal radius, int nbSubdivisions) or change the \c -ModelView matrix to place the arrow in 3D. - -Uses current color and does not modify the OpenGL state. */ -void QGLViewer::drawArrow(qreal length, qreal radius, int nbSubdivisions) -{ - static GLUquadric* quadric = gluNewQuadric(); - - if (radius < 0.0) - radius = 0.05 * length; - - const qreal head = 2.5*(radius / length) + 0.1; - const qreal coneRadiusCoef = 4.0 - 5.0 * head; - - gluCylinder(quadric, radius, radius, length * (1.0 - head/coneRadiusCoef), nbSubdivisions, 1); - glTranslated(0.0, 0.0, length * (1.0 - head)); - gluCylinder(quadric, coneRadiusCoef * radius, 0.0, head * length, nbSubdivisions, 1); - glTranslated(0.0, 0.0, -length * (1.0 - head)); -} - -/*! Draws a 3D arrow between the 3D point \p from and the 3D point \p to, both defined in the -current ModelView coordinates system. - -See drawArrow(qreal length, qreal radius, int nbSubdivisions) for details. */ -void QGLViewer::drawArrow(const Vec& from, const Vec& to, qreal radius, int nbSubdivisions) -{ - glPushMatrix(); - glTranslated(from[0], from[1], from[2]); - const Vec dir = to-from; - glMultMatrixd(Quaternion(Vec(0,0,1), dir).matrix()); - QGLViewer::drawArrow(dir.norm(), radius, nbSubdivisions); - glPopMatrix(); -} - -/*! Draws an XYZ axis, with a given size (default is 1.0). - -The axis position and 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. - -Use the following code to display the current position and orientation of a qglviewer::Frame: -\code -glPushMatrix(); -glMultMatrixd(frame.matrix()); -QGLViewer::drawAxis(sceneRadius() / 5.0); // Or any scale -glPopMatrix(); -\endcode - -The current color and line width are used to draw the X, Y and Z characters at the extremities of -the three arrows. The OpenGL state is not modified by this method. - -axisIsDrawn() uses this method to draw a representation of the world coordinate system. See also -QGLViewer::drawArrow() and QGLViewer::drawGrid(). */ -void QGLViewer::drawAxis(qreal length) -{ - GLboolean lighting, colorMaterial; - glGetBooleanv(GL_LIGHTING, &lighting); - glGetBooleanv(GL_COLOR_MATERIAL, &colorMaterial); - - if (!lighting) - glEnable(GL_LIGHTING); - if (colorMaterial) - glDisable(GL_COLOR_MATERIAL); - - // X - float color[4]; - //color[0] = 1.0f; color[1] = 0.3f; color[2] = 0.3f; color[3] = 1.0f; - color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 1.0f; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); - glPushMatrix(); - glRotatef(90.0, 0.0, 1.0, 0.0); - QGLViewer::drawArrow(length, 0.03*length); - glPopMatrix(); - - // Y - //color[0] = 0.3f; color[1] = 1.0f; color[2] = 0.3f; color[3] = 1.0f; - color[0] = 0.0f; color[1] = 1.0f; color[2] = 0.0f; color[3] = 1.0f; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); - glPushMatrix(); - glRotatef(-90.0, 1.0, 0.0, 0.0); - QGLViewer::drawArrow(length, 0.03*length); - glPopMatrix(); - - // Z - //color[0] = 0.3f; color[1] = 0.3f; color[2] = 1.0f; color[3] = 1.0f; - color[0] = 0.0f; color[1] = 0.0f; color[2] = 1.0f; color[3] = 1.0f; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); - QGLViewer::drawArrow(length, 0.03*length); - - if (colorMaterial) - glEnable(GL_COLOR_MATERIAL); - if (!lighting) - glDisable(GL_LIGHTING); - - //const qreal charWidth = length / 40.0; - //const qreal charHeight = length / 30.0; - //const qreal charShift = 1.04 * length; - - //GLboolean lighting, colorMaterial; - //glGetBooleanv(GL_LIGHTING, &lighting); - //glGetBooleanv(GL_COLOR_MATERIAL, &colorMaterial); - - //glDisable(GL_LIGHTING); - - //glBegin(GL_LINES); - //// The X - //glVertex3d(charShift, charWidth, -charHeight); - //glVertex3d(charShift, -charWidth, charHeight); - //glVertex3d(charShift, -charWidth, -charHeight); - //glVertex3d(charShift, charWidth, charHeight); - //// The Y - //glVertex3d( charWidth, charShift, charHeight); - //glVertex3d(0.0, charShift, 0.0); - //glVertex3d(-charWidth, charShift, charHeight); - //glVertex3d(0.0, charShift, 0.0); - //glVertex3d(0.0, charShift, 0.0); - //glVertex3d(0.0, charShift, -charHeight); - //// The Z - //glVertex3d(-charWidth, charHeight, charShift); - //glVertex3d( charWidth, charHeight, charShift); - //glVertex3d( charWidth, charHeight, charShift); - //glVertex3d(-charWidth, -charHeight, charShift); - //glVertex3d(-charWidth, -charHeight, charShift); - //glVertex3d( charWidth, -charHeight, charShift); - //glEnd(); - - //glEnable(GL_LIGHTING); - //glDisable(GL_COLOR_MATERIAL); - - //float color[4]; - //color[0] = 0.7f; color[1] = 0.7f; color[2] = 1.0f; color[3] = 1.0f; - //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); - //QGLViewer::drawArrow(length, 0.01*length); - - //color[0] = 1.0f; color[1] = 0.7f; color[2] = 0.7f; color[3] = 1.0f; - //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); - //glPushMatrix(); - //glRotatef(90.0f, 0.0f, 1.0f, 0.0f); - //QGLViewer::drawArrow(length, 0.01*length); - //glPopMatrix(); - - //color[0] = 0.7f; color[1] = 1.0f; color[2] = 0.7f; color[3] = 1.0f; - //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); - //glPushMatrix(); - //glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); - //QGLViewer::drawArrow(length, 0.01*length); - //glPopMatrix(); - - //if (colorMaterial) - // glEnable(GL_COLOR_MATERIAL); - //if (!lighting) - // glDisable(GL_LIGHTING); -} - -/*! 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. Set the \c GL_MODELVIEW matrix to -place and orientate the grid in 3D space (see the drawAxis() documentation). - -The OpenGL state is not modified by this method. */ -void QGLViewer::drawGrid(qreal size, int nbSubdivisions) -{ - GLboolean lighting; - glGetBooleanv(GL_LIGHTING, &lighting); - - glDisable(GL_LIGHTING); - - glBegin(GL_LINES); - for (int i=0; i<=nbSubdivisions; ++i) - { - const qreal pos = size*(2.0*i/nbSubdivisions-1.0); - glVertex2d(pos, -size); - glVertex2d(pos, +size); - glVertex2d(-size, pos); - glVertex2d( size, pos); - } - glEnd(); - - if (lighting) - glEnable(GL_LIGHTING); -} - -//////////////////////////////////////////////////////////////////////////////// -// 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").lower() == "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 - -/*! Makes a copy of the current buffer into a texture. - -Creates a texture (when needed) and uses glCopyTexSubImage2D() to directly copy the buffer in it. - -Use \p internalFormat and \p format to define the texture format and hence which and how components -of the buffer are copied into the texture. See the glTexImage2D() documentation for details. - -When \p format is c GL_NONE (default), its value is set to \p internalFormat, which fits most -cases. Typical \p internalFormat (and \p format) values are \c GL_DEPTH_COMPONENT and \c GL_RGBA. -Use \c GL_LUMINANCE as the \p internalFormat and \c GL_RED, \c GL_GREEN or \c GL_BLUE as \p format -to capture a single color component as a luminance (grey scaled) value. Note that \c GL_STENCIL is -not supported as a format. - -The texture has dimensions which are powers of two. It is as small as possible while always being -larger or equal to the current size of the widget. The buffer image hence does not entirely fill -the texture: it is stuck to the lower left corner (corresponding to the (0,0) texture coordinates). -Use bufferTextureMaxU() and bufferTextureMaxV() to get the upper right corner maximum u and v -texture coordinates. Use bufferTextureId() to retrieve the id of the created texture. - -Here is how to display a grey-level image of the z-buffer: -\code -copyBufferToTexture(GL_DEPTH_COMPONENT); - -glMatrixMode(GL_TEXTURE); -glLoadIdentity(); - -glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -glEnable(GL_TEXTURE_2D); - -startScreenCoordinatesSystem(true); - -glBegin(GL_QUADS); -glTexCoord2f(0.0, 0.0); glVertex2i(0, 0); -glTexCoord2f(bufferTextureMaxU(), 0.0); glVertex2i(width(), 0); -glTexCoord2f(bufferTextureMaxU(), bufferTextureMaxV()); glVertex2i(width(), height()); -glTexCoord2f(0.0, bufferTextureMaxV()); glVertex2i(0, height()); -glEnd(); - -stopScreenCoordinatesSystem(); - -glDisable(GL_TEXTURE_2D); -\endcode - -Use glReadBuffer() to select which buffer is copied into the texture. See also \c -glPixelTransfer(), \c glPixelZoom() and \c glCopyPixel() for pixel color transformations during -copy. - -Call makeCurrent() before this method to make the OpenGL context active if needed. - -\note The \c GL_DEPTH_COMPONENT format may not be supported by all hardware. It may sometimes be -emulated in software, resulting in poor performances. - -\note The bufferTextureId() texture is binded at the end of this method. */ -void QGLViewer::copyBufferToTexture(GLint internalFormat, GLenum format) -{ - int h = 16; - int w = 16; - // Todo compare performance with qt code. - while (w < width()) - w <<= 1; - while (h < height()) - h <<= 1; - - bool init = false; - - if ((w != bufferTextureWidth_) || (h != bufferTextureHeight_)) - { - bufferTextureWidth_ = w; - bufferTextureHeight_ = h; - bufferTextureMaxU_ = width() / qreal(bufferTextureWidth_); - bufferTextureMaxV_ = height() / qreal(bufferTextureHeight_); - init = true; - } - - if (bufferTextureId() == 0) - { - glGenTextures(1, &bufferTextureId_); - glBindTexture(GL_TEXTURE_2D, bufferTextureId_); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - init = true; - } - else - glBindTexture(GL_TEXTURE_2D, bufferTextureId_); - - if ((format != previousBufferTextureFormat_) || - (internalFormat != previousBufferTextureInternalFormat_)) - { - previousBufferTextureFormat_ = format; - previousBufferTextureInternalFormat_ = internalFormat; - init = true; - } - - if (init) - { - if (format == GL_NONE) - format = GLenum(internalFormat); - - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, bufferTextureWidth_, bufferTextureHeight_, 0, format, GL_UNSIGNED_BYTE, NULL); - } - - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width(), height()); -} - -/*! 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 -{ - if (glIsTexture(bufferTextureId_)) - return bufferTextureId_; - else - return 0; -} diff --git a/code/3rd_QGLViewer-2.6.3/qglviewer.h b/code/3rd_QGLViewer-2.6.3/qglviewer.h deleted file mode 100644 index 346c4505..00000000 --- a/code/3rd_QGLViewer-2.6.3/qglviewer.h +++ /dev/null @@ -1,1300 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_QGLVIEWER_H -#define QGLVIEWER_QGLVIEWER_H - -#include "camera.h" -#include "../basic/canvas.h" -#include -#include -#include - -class QTabWidget; - -namespace qglviewer { -class MouseGrabber; -class ManipulatedFrame; -class ManipulatedCameraFrame; -} - -/*! \brief A versatile 3D OpenGL viewer based on QGLWidget. -\class QGLViewer qglviewer.h QGLViewer/qglviewer.h - -It features many classical viewer functionalities, such as a camera trackball, manipulated objects, -snapshot saving and much more. Its main goal is to ease the development -of new 3D applications. - -New users should read the introduction page to get familiar with -important notions such as sceneRadius(), sceneCenter() and the world coordinate system. Try the -numerous simple examples to discover the possibilities and -understand how it works. - -

Usage

- -To use a QGLViewer, derive you viewer class from the QGLViewer and overload its draw() virtual -method. See the simpleViewer example for an illustration. - -An other option is to connect your drawing methods to the signals emitted by the QGLViewer (Qt's -callback mechanism). See the callback example for a -complete implementation. - -\nosubgrouping */ -class QGLVIEWER_EXPORT QGLViewer : public QGLWidget, public Canvas -{ - Q_OBJECT - -public: - // Complete implementation is provided so that the constructor is defined with QT3_SUPPORT when .h is included. - // (Would not be available otherwise since lib is compiled without QT3_SUPPORT). -#ifdef QT3_SUPPORT - explicit QGLViewer(QWidget* parent=NULL, const char* name=0, const QGLWidget* shareWidget=0, Qt::WindowFlags flags=0) - : QGLWidget(parent, name, shareWidget, flags) - { defaultConstructor(); } - - explicit QGLViewer(const QGLFormat& format, QWidget* parent=0, const char* name=0, const QGLWidget* shareWidget=0,Qt::WindowFlags flags=0) - : QGLWidget(format, parent, name, shareWidget, flags) - { defaultConstructor(); } - - QGLViewer(QGLContext* context, QWidget* parent, const char* name=0, const QGLWidget* shareWidget=0, Qt::WindowFlags flags=0) - : QGLWidget(context, parent, name, shareWidget, flags) { - defaultConstructor(); } - -#else - - explicit QGLViewer(QWidget* parent=0, const QGLWidget* shareWidget=0, Qt::WindowFlags flags=0); - explicit QGLViewer(QGLContext *context, QWidget* parent=0, const QGLWidget* shareWidget=0, Qt::WindowFlags flags=0); - explicit QGLViewer(const QGLFormat& format, QWidget* parent=0, const QGLWidget* shareWidget=0, Qt::WindowFlags flags=0); -#endif - - virtual ~QGLViewer(); - - /*! @name Display of visual hints */ - //@{ -public: - /*! Returns \c true if the world axis is drawn by the viewer. - - Set by setAxisIsDrawn() or toggleAxisIsDrawn(). Default value is \c false. */ - bool axisIsDrawn() const { return axisIsDrawn_; } - /*! Returns \c true if a XY grid is drawn by the viewer. - - Set by setGridIsDrawn() or toggleGridIsDrawn(). Default value is \c false. */ - bool gridIsDrawn() const { return gridIsDrawn_; } - /*! Returns \c true if the viewer displays the current frame rate (Frames Per Second). - - Use QApplication::setFont() to define the display font (see drawText()). - - Set by setFPSIsDisplayed() or toggleFPSIsDisplayed(). Use currentFPS() to get the current FPS. - Default value is \c false. */ - bool FPSIsDisplayed() const { return FPSIsDisplayed_; } - /*! Returns \c true if text display (see drawText()) is enabled. - - Set by setTextIsEnabled() or toggleTextIsEnabled(). This feature conveniently removes all the - possibly displayed text, cleaning display. Default value is \c true. */ - bool textIsEnabled() const { return textIsEnabled_; } - - /*! Returns \c true if the camera() is being edited in the viewer. - - Set by setCameraIsEdited() or toggleCameraIsEdited(). Default value is \p false. - - The current implementation is limited: the defined camera() paths (see - qglviewer::Camera::keyFrameInterpolator()) are simply displayed using - qglviewer::Camera::drawAllPaths(). Actual camera and path edition will be implemented in the - future. */ - bool cameraIsEdited() const { return cameraIsEdited_; } - - -public Q_SLOTS: - /*! Sets the state of axisIsDrawn(). Emits the axisIsDrawnChanged() signal. See also toggleAxisIsDrawn(). */ - void setAxisIsDrawn(bool draw=true) { axisIsDrawn_ = draw; Q_EMIT axisIsDrawnChanged(draw); update(); } - /*! Sets the state of gridIsDrawn(). Emits the gridIsDrawnChanged() signal. See also toggleGridIsDrawn(). */ - void setGridIsDrawn(bool draw=true) { gridIsDrawn_ = draw; Q_EMIT gridIsDrawnChanged(draw); update(); } - /*! Sets the state of FPSIsDisplayed(). Emits the FPSIsDisplayedChanged() signal. See also toggleFPSIsDisplayed(). */ - void setFPSIsDisplayed(bool display=true) { FPSIsDisplayed_ = display; Q_EMIT FPSIsDisplayedChanged(display); update(); } - /*! Sets the state of textIsEnabled(). Emits the textIsEnabledChanged() signal. See also toggleTextIsEnabled(). */ - void setTextIsEnabled(bool enable=true) { textIsEnabled_ = enable; Q_EMIT textIsEnabledChanged(enable); update(); } - void setCameraIsEdited(bool edit=true); - - /*! Toggles the state of axisIsDrawn(). See also setAxisIsDrawn(). */ - void toggleAxisIsDrawn() { setAxisIsDrawn(!axisIsDrawn()); } - /*! Toggles the state of gridIsDrawn(). See also setGridIsDrawn(). */ - void toggleGridIsDrawn() { setGridIsDrawn(!gridIsDrawn()); } - /*! Toggles the state of FPSIsDisplayed(). See also setFPSIsDisplayed(). */ - void toggleFPSIsDisplayed() { setFPSIsDisplayed(!FPSIsDisplayed()); } - /*! Toggles the state of textIsEnabled(). See also setTextIsEnabled(). */ - void toggleTextIsEnabled() { setTextIsEnabled(!textIsEnabled()); } - /*! Toggles the state of cameraIsEdited(). See also setCameraIsEdited(). */ - void toggleCameraIsEdited() { setCameraIsEdited(!cameraIsEdited()); } - //@} - - - /*! @name Viewer's colors */ - //@{ -public: - /*! Returns the background color of the viewer. - - This method is provided for convenience since the background color is an OpenGL state variable - set with \c glClearColor(). However, this internal representation has the advantage that it is - saved (resp. restored) with saveStateToFile() (resp. restoreStateFromFile()). - - Use setBackgroundColor() to define and activate a background color. - - \attention Each QColor component is an integer ranging from 0 to 255. This differs from the qreal - values used by \c glClearColor() which are in the 0.0-1.0 range. Default value is (51, 51, 51) - (dark gray). You may have to change foregroundColor() accordingly. - - \attention This method does not return the current OpenGL clear color as \c glGet() does. Instead, - it returns the QGLViewer internal variable. If you directly use \c glClearColor() or \c - qglClearColor() instead of setBackgroundColor(), the two results will differ. */ - QColor backgroundColor() const { return backgroundColor_; } - - /*! Returns the foreground color used by the viewer. - - This color is used when FPSIsDisplayed(), gridIsDrawn(), to display the camera paths when the - cameraIsEdited(). - - \attention Each QColor component is an integer in the range 0-255. This differs from the qreal - values used by \c glColor3f() which are in the range 0-1. Default value is (180, 180, 180) (light - gray). - - Use \c qglColor(foregroundColor()) to set the current OpenGL color to the foregroundColor(). - - See also backgroundColor(). */ - QColor foregroundColor() const { return foregroundColor_; } -public Q_SLOTS: - /*! Sets the backgroundColor() of the viewer and calls \c qglClearColor(). See also - setForegroundColor(). */ - void setBackgroundColor(const QColor& color) { backgroundColor_=color; qglClearColor(color); } - /*! Sets the foregroundColor() of the viewer, used to draw visual hints. See also setBackgroundColor(). */ - void setForegroundColor(const QColor& color) { foregroundColor_ = color; } - //@} - - - /*! @name Scene dimensions */ - //@{ -public: - /*! Returns the scene radius. - - The entire displayed scene should be included in a sphere of radius sceneRadius(), centered on - sceneCenter(). - - This approximate value is used by the camera() to set qglviewer::Camera::zNear() and - qglviewer::Camera::zFar(). It is also used to showEntireScene() or to scale the world axis - display.. - - Default value is 1.0. This method is equivalent to camera()->sceneRadius(). See - setSceneRadius(). */ - qreal sceneRadius() const { return camera()->sceneRadius(); } - /*! Returns the scene center, defined in world coordinates. - - See sceneRadius() for details. - - Default value is (0,0,0). Simply a wrapper for camera()->sceneCenter(). Set using - setSceneCenter(). - - Do not mismatch this value (that only depends on the scene) with the qglviewer::Camera::pivotPoint(). */ - qglviewer::Vec sceneCenter() const { return camera()->sceneCenter(); } - -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); } - - /*! 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& center) { camera()->setSceneCenter(center); } - - /*! Convenient way to call setSceneCenter() and setSceneRadius() from a (world axis aligned) bounding box of the scene. - - This is equivalent to: - \code - setSceneCenter((min+max) / 2.0); - setSceneRadius((max-min).norm() / 2.0); - \endcode */ - void setSceneBoundingBox(const qglviewer::Vec& min, const qglviewer::Vec& max) { camera()->setSceneBoundingBox(min,max); } - - /*! Moves the camera so that the entire scene is visible. - - Simple wrapper around qglviewer::Camera::showEntireScene(). */ - void showEntireScene() { camera()->showEntireScene(); update(); } - //@} - - - /*! @name Associated objects */ - //@{ -public: - /*! Returns the associated qglviewer::Camera, never \c NULL. */ - qglviewer::Camera* camera() const { return camera_; } - - CameraBase* get_camera() const { return camera_; } - - /*! Returns the viewer's qglviewer::ManipulatedFrame. - - This qglviewer::ManipulatedFrame can be moved with the mouse when the associated mouse bindings - are used (default is when pressing the \c Control key with any mouse button). Use - setMouseBinding() to define new bindings. - - See the manipulatedFrame example for a complete - implementation. - - Default value is \c NULL, meaning that no qglviewer::ManipulatedFrame is set. */ - qglviewer::ManipulatedFrame* manipulatedFrame() const { return manipulatedFrame_; } - -public Q_SLOTS: - void setCamera(qglviewer::Camera* const camera); - void setManipulatedFrame(qglviewer::ManipulatedFrame* frame); - //@} - - - /*! @name Mouse grabbers */ - //@{ -public: - /*! Returns the current qglviewer::MouseGrabber, or \c NULL if no qglviewer::MouseGrabber - currently grabs mouse events. - - When qglviewer::MouseGrabber::grabsMouse(), the different mouse events are sent to the - mouseGrabber() instead of their usual targets (camera() or manipulatedFrame()). - - See the qglviewer::MouseGrabber documentation for details on MouseGrabber's mode of operation. - - In order to use MouseGrabbers, you need to enable mouse tracking (so that mouseMoveEvent() is - called even when no mouse button is pressed). Add this line in init() or in your viewer - constructor: - \code - setMouseTracking(true); - \endcode - Note that mouse tracking is disabled by default. Use QWidget::hasMouseTracking() to - retrieve current state. */ - qglviewer::MouseGrabber* mouseGrabber() const { return mouseGrabber_; } - - void setMouseGrabberIsEnabled(const qglviewer::MouseGrabber* const mouseGrabber, bool enabled=true); - /*! Returns \c true if \p mouseGrabber is enabled. - - Default value is \c true for all MouseGrabbers. When set to \c false using - setMouseGrabberIsEnabled(), the specified \p mouseGrabber will never become the mouseGrabber() of - this QGLViewer. This is useful when you use several viewers: some MouseGrabbers may only have a - meaning for some specific viewers and should not be selectable in others. - - You can also use qglviewer::MouseGrabber::removeFromMouseGrabberPool() to completely disable a - MouseGrabber in all the QGLViewers. */ - bool mouseGrabberIsEnabled(const qglviewer::MouseGrabber* const mouseGrabber) { return !disabledMouseGrabbers_.contains(reinterpret_cast(mouseGrabber)); } -public Q_SLOTS: - void setMouseGrabber(qglviewer::MouseGrabber* mouseGrabber); - //@} - - - /*! @name State of the viewer */ - //@{ -public: - /*! Returns the aspect ratio of the viewer's widget (width() / height()). */ - qreal aspectRatio() const { return width() / static_cast(height()); } - /*! Returns the current averaged viewer frame rate. - - This value is computed and averaged over 20 successive frames. It only changes every 20 draw() - (previously computed value is otherwise returned). - - This method is useful for true real-time applications that may adapt their computational load - accordingly in order to maintain a given frequency. - - This value is meaningful only when draw() is regularly called, either using a \c QTimer, when - animationIsStarted() or when the camera is manipulated with the mouse. */ - qreal currentFPS() { return f_p_s_; } - /*! Returns \c true if the viewer is in fullScreen mode. - - Default value is \c false. Set by setFullScreen() or toggleFullScreen(). - - Note that if the QGLViewer is embedded in an other QWidget, it returns \c true when the top level - widget is in full screen mode. */ - bool isFullScreen() const { return fullScreen_; } - /*! Returns \c true if the viewer displays in stereo. - - The QGLViewer object must be created with a stereo format to handle stereovision: - \code - QGLFormat format; - format.setStereoDisplay( TRUE ); - QGLViewer viewer(format); - \endcode - The hardware needs to support stereo display. Try the stereoViewer example to check. - - Set by setStereoDisplay() or toggleStereoDisplay(). Default value is \c false. - - Stereo is performed using the Parallel axis asymmetric frustum perspective projection method. - See Camera::loadProjectionMatrixStereo() and Camera::loadModelViewMatrixStereo(). - - The stereo parameters are defined by the camera(). See qglviewer::Camera::setIODistance(), - qglviewer::Camera::setPhysicalScreenWidth() and - qglviewer::Camera::setFocusDistance(). */ - bool displaysInStereo() const { return stereo_; } - /*! Returns the recommended size for the QGLViewer. Default value is 600x400 pixels. */ - virtual QSize sizeHint() const { return QSize(600, 400); } - -public Q_SLOTS: - void setFullScreen(bool fullScreen=true); - void setStereoDisplay(bool stereo=true); - /*! Toggles the state of isFullScreen(). See also setFullScreen(). */ - void toggleFullScreen() { setFullScreen(!isFullScreen()); } - /*! Toggles the state of displaysInStereo(). See setStereoDisplay(). */ - void toggleStereoDisplay() { setStereoDisplay(!stereo_); } - void toggleCameraMode(); - -private: - bool cameraIsInRotateMode() const; - //@} - - - /*! @name Display methods */ - //@{ -public: - static void drawArrow(qreal length=1.0, qreal radius=-1.0, int nbSubdivisions=12); - static void drawArrow(const qglviewer::Vec& from, const qglviewer::Vec& to, qreal radius=-1.0, int nbSubdivisions=12); - static void drawAxis(qreal length=1.0); - static void drawGrid(qreal size=1.0, int nbSubdivisions=10); - - virtual void startScreenCoordinatesSystem(bool upward=false) const; - virtual void stopScreenCoordinatesSystem() const; - - void drawText(int x, int y, const QString& text, const QFont& fnt=QFont()); - void displayMessage(const QString& message, int delay=2000); - // void draw3DText(const qglviewer::Vec& pos, const qglviewer::Vec& normal, const QString& string, GLfloat height=0.1); - -protected: - virtual void drawLight(GLenum light, qreal scale = 1.0) const; - -private: - void displayFPS(); - /*! Vectorial rendering callback method. */ - void drawVectorial() { paintGL(); } - -#ifndef DOXYGEN - friend void drawVectorial(void* param); -#endif - //@} - - -#ifdef DOXYGEN - /*! @name Useful inherited methods */ - //@{ -public: - /*! Returns viewer's widget width (in pixels). See QGLWidget documentation. */ - int width() const; - /*! Returns viewer's widget height (in pixels). See QGLWidget documentation. */ - int height() const; - /*! Updates the display. Do not call draw() directly, use this method instead. See QGLWidget documentation. */ - virtual void updateGL(); - /*! Converts \p image into the unnamed format expected by OpenGL methods such as glTexImage2D(). - See QGLWidget documentation. */ - static QImage convertToGLFormat(const QImage & image); - /*! Calls \c glColor3. See QGLWidget::qglColor(). */ - void qglColor(const QColor& color) const; - /*! Calls \c glClearColor. See QGLWidget documentation. */ - void qglClearColor(const QColor& color) const; - /*! Returns \c true if the widget has a valid GL rendering context. See QGLWidget - documentation. */ - bool isValid() const; - /*! Returns \c true if display list sharing with another QGLWidget was requested in the - constructor. See QGLWidget documentation. */ - bool isSharing() const; - /*! Makes this widget's rendering context the current OpenGL rendering context. Useful with - several viewers. See QGLWidget documentation. */ - virtual void makeCurrent(); - /*! Returns \c true if mouseMoveEvent() is called even when no mouse button is pressed. - - You need to setMouseTracking() to \c true in order to use MouseGrabber (see mouseGrabber()). See - details in the QWidget documentation. */ - bool hasMouseTracking () const; -public Q_SLOTS: - /*! Resizes the widget to size \p width by \p height pixels. See also width() and height(). */ - virtual void resize(int width, int height); - /*! Sets the hasMouseTracking() value. */ - virtual void setMouseTracking(bool enable); -protected: - /*! Returns \c true when buffers are automatically swapped (default). See details in the QGLWidget - documentation. */ - bool autoBufferSwap() const; -protected Q_SLOTS: - /*! Sets the autoBufferSwap() value. */ - void setAutoBufferSwap(bool on); - //@} -#endif - - - /*! @name Snapshots */ - //@{ -public: - /*! Returns the snapshot file name used by saveSnapshot(). - - This value is used in \p automatic mode (see saveSnapshot()). A dialog is otherwise popped-up to - set it. - - You can also directly provide a file name using saveSnapshot(const QString&, bool). - - If the file name is relative, the current working directory at the moment of the method call is - used. Set using setSnapshotFileName(). */ - const QString& snapshotFileName() const { return snapshotFileName_; } -#ifndef DOXYGEN - const QString& snapshotFilename() const; -#endif - /*! Returns the snapshot file format used by saveSnapshot(). - - This value is used when saveSnapshot() is passed the \p automatic flag. It is defined using a - saveAs pop-up dialog otherwise. - - The available formats are those handled by Qt. Classical values are \c "JPEG", \c "PNG", - \c "PPM", \c "BMP". Use the following code to get the actual list: - \code - QList formatList = QImageReader::supportedImageFormats(); - // or with Qt version 2 or 3: - QStringList formatList = QImage::outputFormatList(); - \endcode - - If the library was compiled with the vectorial rendering option (default), three additional - vectorial formats are available: \c "EPS", \c "PS" and \c "XFIG". \c "SVG" and \c "PDF" formats - should soon be available. The VRender library - was created by Cyril Soler. - - Note that the VRender library has some limitations: vertex shader effects are not reproduced and - \c PASS_THROUGH tokens are not handled so one can not change point and line size in the middle of - a drawing. - - Default value is the first supported among "JPEG, PNG, EPS, PS, PPM, BMP", in that order. - - This value is set using setSnapshotFormat() or with openSnapshotFormatDialog(). - - \attention No verification is performed on the provided format validity. The next call to - saveSnapshot() may fail if the format string is not supported. */ - const QString& snapshotFormat() const { return snapshotFormat_; } - /*! Returns the value of the counter used to name snapshots in saveSnapshot() when \p automatic is - \c true. - - Set using setSnapshotCounter(). Default value is 0, and it is incremented after each \p automatic - snapshot. See saveSnapshot() for details. */ - int snapshotCounter() const { return snapshotCounter_; } - /*! Defines the image quality of the snapshots produced with saveSnapshot(). - - Values must be in the range -1..100. Use 0 for lowest quality and 100 for highest quality (and - larger files). -1 means use Qt default quality. Default value is 95. - - Set using setSnapshotQuality(). See also the QImage::save() documentation. - - \note This value has no impact on the images produced in vectorial format. */ - int snapshotQuality() { return snapshotQuality_; } - - // Qt 2.3 does not support qreal default value parameters in slots. - // Remove "Q_SLOTS" from the following line to compile with Qt 2.3 -public Q_SLOTS: - void saveSnapshot(bool automatic=true, bool overwrite=false); - -public Q_SLOTS: - void saveSnapshot(const QString& fileName, bool overwrite=false); - void setSnapshotFileName(const QString& name); - - /*! Sets the snapshotFormat(). */ - void setSnapshotFormat(const QString& format) { snapshotFormat_ = format; } - /*! Sets the snapshotCounter(). */ - void setSnapshotCounter(int counter) { snapshotCounter_ = counter; } - /*! Sets the snapshotQuality(). */ - void setSnapshotQuality(int quality) { snapshotQuality_ = quality; } - bool openSnapshotFormatDialog(); - void snapshotToClipboard(); - -public: - bool saveImageSnapshot(const QString& fileName); - -#ifndef DOXYGEN - /* This class is used internally for screenshot that require tiling (image size size different - from window size). Only in that case, is the private tileRegion_ pointer non null. - It then contains the current tiled region, which is used by startScreenCoordinatesSystem - to adapt the coordinate system. Not using it would result in a tiled drawing of the parts - that use startScreenCoordinatesSystem. Also used by scaledFont for same purposes. */ - class TileRegion { public : qreal xMin, yMin, xMax, yMax, textScale; }; -#endif - -public: - /*! Return a possibly scaled version of \p font, used for snapshot rendering. - - From a user's point of view, this method simply returns \p font and can be used transparently. - - However when internally rendering a screen snapshot using saveSnapshot(), it returns a scaled version - of the font, so that the size of the rendered text on the snapshot is identical to what is displayed on screen, - even if the snapshot uses image tiling to create an image of dimensions different from those of the - current window. This scaled version will only be used when saveSnapshot() calls your draw() method - to generate the snapshot. - - All your calls to QGLWidget::renderText() function hence should use this method. - \code - renderText(x, y, z, "My Text", scaledFont(QFont())); - \endcode - will guarantee that this text will be properly displayed on arbitrary sized snapshots. - - Note that this method is not needed if you use drawText() which already calls it internally. */ - QFont scaledFont(const QFont& font) const { - if (tileRegion_ == NULL) - return font; - else { - QFont f(font); - if (f.pixelSize() == -1) - f.setPointSizeF(f.pointSizeF() * tileRegion_->textScale); - else - f.setPixelSize(int(f.pixelSize() * tileRegion_->textScale)); - return f; - } - } - //@} - - - /*! @name Buffer to texture */ - //@{ -public: - GLuint bufferTextureId() const; - /*! Returns the texture coordinate corresponding to the u extremum of the bufferTexture. - - The bufferTexture is created by copyBufferToTexture(). The texture size has powers of two - dimensions and the buffer image hence only fills a part of it. This value corresponds to the u - coordinate of the extremum right side of the buffer image. - - Use (0,0) to (bufferTextureMaxU(), bufferTextureMaxV()) texture coordinates to map the entire - texture on a quad. */ - qreal bufferTextureMaxU() const { return bufferTextureMaxU_; } - /*! Same as bufferTextureMaxU(), but for the v texture coordinate. */ - qreal bufferTextureMaxV() const { return bufferTextureMaxV_; } -public Q_SLOTS: - void copyBufferToTexture(GLint internalFormat, GLenum format=GL_NONE); - //@} - - /*! @name Animation */ - //@{ -public: - /*! Return \c true when the animation loop is started. - - During animation, an infinite loop calls animate() and draw() and then waits for animationPeriod() - millisec before calling animate() and draw() again. And again. - - Use startAnimation(), stopAnimation() or toggleAnimation() to change this value. - - See the animation example for illustration. */ - bool animationIsStarted() const { return animationStarted_; } - /*! The animation loop period, in millisec. - - When animationIsStarted(), this is delay waited after draw() to call animate() and draw() again. - Default value is 40 millisec (25 Hz). - - This value will define the currentFPS() when animationIsStarted() (provided that your animate() - and draw() methods are fast enough). - - If you want to know the maximum possible frame rate of your machine on a given scene, - setAnimationPeriod() to \c 0, and startAnimation() (keyboard shortcut is \c Enter). The display - will then be updated as often as possible, and the frame rate will be meaningful. - - \note This value is taken into account only the next time you call startAnimation(). If - animationIsStarted(), you should stopAnimation() first. */ - int animationPeriod() const { return animationPeriod_; } - -public Q_SLOTS: - /*! Sets the animationPeriod(), in millisec. */ - void setAnimationPeriod(int period) { animationPeriod_ = period; } - virtual void startAnimation(); - virtual void stopAnimation(); - /*! Scene animation method. - - When animationIsStarted(), this method is in charge of the scene update before each draw(). - Overload it to define how your scene evolves over time. The time should either be regularly - incremented in this method (frame-rate independent animation) or computed from actual time (for - instance using QTime::elapsed()) for real-time animations. - - Note that KeyFrameInterpolator (which regularly updates a Frame) does not use this method - to animate a Frame, but rather rely on a QTimer signal-slot mechanism. - - See the animation example for an illustration. */ - virtual void animate() { Q_EMIT animateNeeded(); } - /*! Calls startAnimation() or stopAnimation(), depending on animationIsStarted(). */ - void toggleAnimation() { if (animationIsStarted()) stopAnimation(); else startAnimation(); } - //@} - -public: -Q_SIGNALS: - /*! Signal emitted by the default init() method. - - Connect this signal to the methods that need to be called to initialize your viewer or overload init(). */ - void viewerInitialized(); - - /*! Signal emitted by the default draw() method. - - Connect this signal to your main drawing method or overload draw(). See the callback example for an illustration. */ - void drawNeeded(); - - /*! Signal emitted at the end of the QGLViewer::paintGL() method, when frame is drawn. - - Can be used to notify an image grabbing process that the image is ready. A typical example is to - connect this signal to the saveSnapshot() method, so that a (numbered) snapshot is generated after - each new display, in order to create a movie: - \code - connect(viewer, SIGNAL(drawFinished(bool)), SLOT(saveSnapshot(bool))); - \endcode - - The \p automatic bool variable is always \c true and has been added so that the signal can be - connected to saveSnapshot() with an \c automatic value set to \c true. */ - void drawFinished(bool automatic); - - /*! Signal emitted by the default animate() method. - - Connect this signal to your scene animation method or overload animate(). */ - void animateNeeded(); - - /*! Signal emitted by the default QGLViewer::help() method. - - Connect this signal to your own help method or overload help(). */ - void helpRequired(); - - /*! This signal is emitted whenever axisIsDrawn() changes value. */ - void axisIsDrawnChanged(bool drawn); - /*! This signal is emitted whenever gridIsDrawn() changes value. */ - void gridIsDrawnChanged(bool drawn); - /*! This signal is emitted whenever FPSIsDisplayed() changes value. */ - void FPSIsDisplayedChanged(bool displayed); - /*! This signal is emitted whenever textIsEnabled() changes value. */ - void textIsEnabledChanged(bool enabled); - /*! This signal is emitted whenever cameraIsEdited() changes value.. */ - void cameraIsEditedChanged(bool edited); - /*! This signal is emitted whenever displaysInStereo() changes value. */ - void stereoChanged(bool on); - /*! Signal emitted by select(). - - Connect this signal to your selection method or overload select(), or more probably simply - drawWithNames(). */ - void pointSelected(const QMouseEvent* e); - - /*! Signal emitted by setMouseGrabber() when the mouseGrabber() is changed. - - \p mouseGrabber is a pointer to the new MouseGrabber. Note that this signal is emitted with a \c - NULL parameter each time a MouseGrabber stops grabbing mouse. */ - void mouseGrabberChanged(qglviewer::MouseGrabber* mouseGrabber); - - /*! @name Help window */ - //@{ -public: - /*! Returns the QString displayed in the help() window main tab. - - Overload this method to define your own help string, which should shortly describe your - application and explain how it works. Rich-text (HTML) tags can be used (see QStyleSheet() - documentation for available tags): - \code - QString myViewer::helpString() const - { - QString text("

M y V i e w e r

"); - text += "Displays a Scene using OpenGL. Move the camera using the mouse."; - return text; - } - \endcode - - See also mouseString() and keyboardString(). */ - virtual QString helpString() const { return tr("No help available."); } - - virtual QString mouseString() const; - virtual QString keyboardString() const; - -#ifndef DOXYGEN - /*! This method is deprecated, use mouseString() instead. */ - virtual QString mouseBindingsString () const { return mouseString(); } - /*! This method is deprecated, use keyboardString() instead. */ - virtual QString shortcutBindingsString () const { return keyboardString(); } -#endif - -public Q_SLOTS: - virtual void help(); - virtual void aboutQGLViewer(); - -protected: - /*! Returns a pointer to the help widget. - - Use this only if you want to directly modify the help widget. Otherwise use helpString(), - setKeyDescription() and setMouseBindingDescription() to customize the text displayed in the help - window tabs. */ - QTabWidget* helpWidget() { return helpWidget_; } - //@} - - - /*! @name Drawing methods */ - //@{ -protected: - virtual void resizeGL(int width, int height); - virtual void initializeGL(); - - /*! Initializes the viewer OpenGL context. - - This method is called before the first drawing and should be overloaded to initialize some of the - OpenGL flags. The default implementation is empty. See initializeGL(). - - Typical usage include camera() initialization (showEntireScene()), previous viewer state - restoration (restoreStateFromFile()), OpenGL state modification and display list creation. - - Note that initializeGL() modifies the standard OpenGL context. These values can be restored back - in this method. - - \attention You should not call updateGL() (or any method that calls it) in this method, as it will - result in an infinite loop. The different QGLViewer set methods (setAxisIsDrawn(), - setFPSIsDisplayed()...) are protected against this problem and can safely be called. - - \note All the OpenGL specific initializations must be done in this method: the OpenGL context is - not yet available in your viewer constructor. */ - virtual void init() { Q_EMIT viewerInitialized(); } - - virtual void paintGL(); - virtual void preDraw(); - virtual void preDrawStereo(bool leftBuffer=true); - - /*! The core method of the viewer, that draws the scene. - - If you build a class that inherits from QGLViewer, this is the method you want to overload. See - the simpleViewer example for an illustration. - - The camera modelView matrix set in preDraw() converts from the world to the camera coordinate - systems. Vertices given in draw() can then be considered as being given in the world coordinate - system. The camera is moved in this world using the mouse. This representation is much more - intuitive than the default camera-centric OpenGL standard. - - \attention The \c GL_PROJECTION matrix should not be modified by this method, to correctly display - visual hints (axis, grid, FPS...) in postDraw(). Use push/pop or call - camera()->loadProjectionMatrix() at the end of draw() if you need to change the projection matrix - (unlikely). On the other hand, the \c GL_MODELVIEW matrix can be modified and left in a arbitrary - state. */ - virtual void draw() {} - virtual void fastDraw(); - virtual void postDraw(); - //@} - - /*! @name Mouse, keyboard and event handlers */ - //@{ -protected: - virtual void mousePressEvent(QMouseEvent *); - virtual void mouseMoveEvent(QMouseEvent *); - virtual void mouseReleaseEvent(QMouseEvent *); - virtual void mouseDoubleClickEvent(QMouseEvent *); - virtual void wheelEvent(QWheelEvent *); - virtual void keyPressEvent(QKeyEvent *); - virtual void keyReleaseEvent(QKeyEvent *); - virtual void timerEvent(QTimerEvent *); - virtual void closeEvent(QCloseEvent *); - //@} - - /*! @name Object selection */ - //@{ -public: - /*! Returns the name (an integer value) of the entity that was last selected by select(). This - value is set by endSelection(). See the select() documentation for details. - - As a convention, this method returns -1 if the selectBuffer() was empty, meaning that no object - was selected. - - Return value is -1 before the first call to select(). This value is modified using setSelectedName(). */ - int selectedName() const { return selectedObjectId_; } - /*! Returns the selectBuffer() size. - - See the select() documentation for details. Use setSelectBufferSize() to change this value. - - Default value is 4000 (i.e. 1000 objects in selection region, since each object pushes 4 values). - This size should be over estimated to prevent a buffer overflow when many objects are drawn under - the mouse cursor. */ - int selectBufferSize() const { return selectBufferSize_; } - - /*! Returns the width (in pixels) of a selection frustum, centered on the mouse cursor, that is - used to select objects. - - The height of the selection frustum is defined by selectRegionHeight(). - - The objects that will be drawn in this region by drawWithNames() will be recorded in the - selectBuffer(). endSelection() then analyzes this buffer and setSelectedName() to the name of the - closest object. See the gluPickMatrix() documentation for details. - - The default value is 3, which is adapted to standard applications. A smaller value results in a - more precise selection but the user has to be careful for small feature selection. - - See the multiSelect example for an illustration. */ - int selectRegionWidth() const { return selectRegionWidth_; } - /*! See the selectRegionWidth() documentation. Default value is 3 pixels. */ - int selectRegionHeight() const { return selectRegionHeight_; } - - /*! Returns a pointer to an array of \c GLuint. - - This buffer is used by the \c GL_SELECT mode in select() to perform object selection. The buffer - size can be modified using setSelectBufferSize(). If you overload endSelection(), you will analyze - the content of this buffer. See the \c glSelectBuffer() man page for details. */ - GLuint* selectBuffer() { return selectBuffer_; } - -public Q_SLOTS: - virtual void select(const QMouseEvent* event); - virtual void select(const QPoint& point); - - void setSelectBufferSize(int size); - /*! Sets the selectRegionWidth(). */ - void setSelectRegionWidth(int width) { selectRegionWidth_ = width; } - /*! Sets the selectRegionHeight(). */ - void setSelectRegionHeight(int height) { selectRegionHeight_ = height; } - /*! Set the selectedName() value. - - Used in endSelection() during a selection. You should only call this method if you overload the - endSelection() method. */ - void setSelectedName(int id) { selectedObjectId_=id; } - -protected: - virtual void beginSelection(const QPoint& point); - /*! This method is called by select() and should draw selectable entities. - - Default implementation is empty. Overload and draw the different elements of your scene you want - to be able to select. The default select() implementation relies on the \c GL_SELECT, and requires - that each selectable element is drawn within a \c glPushName() - \c glPopName() block. A typical - usage would be (see the select example): -\code -void Viewer::drawWithNames() { - for (int i=0; idraw(); - glPopName(); - } -} -\endcode - - The resulting selected name is computed by endSelection(), which setSelectedName() to the integer - id pushed by this method (a value of -1 means no selection). Use selectedName() to update your - selection, probably in the postSelection() method. - - \attention If your selected objects are points, do not use \c glBegin(GL_POINTS); and \c glVertex3fv() - in the above \c draw() method (not compatible with raster mode): use \c glRasterPos3fv() instead. */ - virtual void drawWithNames() {} - virtual void endSelection(const QPoint& point); - /*! This method is called at the end of the select() procedure. It should finalize the selection - process and update the data structure/interface/computation/display... according to the newly - selected entity. - - The default implementation is empty. Overload this method if needed, and use selectedName() to - retrieve the selected entity name (returns -1 if no object was selected). See the select example for an illustration. */ - virtual void postSelection(const QPoint& point) { Q_UNUSED(point); } - //@} - - - /*! @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, - SAVE_SCREENSHOT, 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, SNAPSHOT_TO_CLIPBOARD }; - - unsigned int shortcut(KeyboardAction action) const; -#ifndef DOXYGEN - // QGLViewer 1.x - unsigned int keyboardAccelerator(KeyboardAction action) const; - Qt::Key keyFrameKey(unsigned int index) const; - Qt::KeyboardModifiers playKeyFramePathStateKey() const; - // QGLViewer 2.0 without Qt4 support - Qt::KeyboardModifiers addKeyFrameStateKey() const; - Qt::KeyboardModifiers playPathStateKey() const; -#endif - Qt::Key pathKey(unsigned int index) const; - Qt::KeyboardModifiers addKeyFrameKeyboardModifiers() const; - Qt::KeyboardModifiers playPathKeyboardModifiers() const; - -public Q_SLOTS: - void setShortcut(KeyboardAction action, unsigned int key); -#ifndef DOXYGEN - void setKeyboardAccelerator(KeyboardAction action, unsigned int key); -#endif - void setKeyDescription(unsigned int key, QString description); - void clearShortcuts(); - - // Key Frames shortcut keys -#ifndef DOXYGEN - // QGLViewer 1.x compatibility methods - virtual void setKeyFrameKey(unsigned int index, int key); - virtual void setPlayKeyFramePathStateKey(unsigned int buttonState); - // QGLViewer 2.0 without Qt4 support - virtual void setPlayPathStateKey(unsigned int buttonState); - virtual void setAddKeyFrameStateKey(unsigned int buttonState); -#endif - virtual void setPathKey(int key, unsigned int index = 0); - virtual void setPlayPathKeyboardModifiers(Qt::KeyboardModifiers modifiers); - virtual void setAddKeyFrameKeyboardModifiers(Qt::KeyboardModifiers modifiers); - //@} - - -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; - int mouseHandler(unsigned int state) const; - int mouseButtonState(MouseHandler handler, MouseAction action, bool withConstraint=true) const; - ClickAction clickAction(unsigned int state, bool doubleClick, Qt::MouseButtons buttonsBefore) const; - void getClickButtonState(ClickAction action, unsigned int & state, bool& doubleClick, Qt::MouseButtons& buttonsBefore) const; - unsigned int wheelButtonState(MouseHandler handler, MouseAction action, bool withConstraint=true) const; -#endif - - 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, bool withConstraint, - Qt::Key& key, Qt::KeyboardModifiers& modifiers, Qt::MouseButton& button) const; - - 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, Qt::KeyboardModifiers& modifiers, - Qt::MouseButton& button, bool& doubleClick, Qt::MouseButtons& buttonsBefore) const; - - MouseAction wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) const; - int wheelHandler(Qt::Key key, Qt::KeyboardModifiers modifiers) const; - - void getWheelActionBinding(MouseHandler handler, 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, bool doubleClick=false, Qt::MouseButtons buttonsBefore=Qt::NoButton); - void setMouseBindingDescription(unsigned int state, QString description, bool doubleClick=false, Qt::MouseButtons buttonsBefore=Qt::NoButton); -#endif - - void setMouseBinding(Qt::KeyboardModifiers modifiers, Qt::MouseButton buttons, MouseHandler handler, MouseAction action, bool withConstraint=true); - void setMouseBinding(Qt::KeyboardModifiers modifiers, Qt::MouseButton button, ClickAction action, bool doubleClick=false, Qt::MouseButtons buttonsBefore=Qt::NoButton); - void setWheelBinding(Qt::KeyboardModifiers modifiers, MouseHandler handler, MouseAction action, bool withConstraint=true); - void setMouseBindingDescription(Qt::KeyboardModifiers modifiers, Qt::MouseButton button, QString description, bool doubleClick=false, Qt::MouseButtons buttonsBefore=Qt::NoButton); - - void setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton buttons, MouseHandler handler, MouseAction action, bool withConstraint=true); - void setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton button, ClickAction action, bool doubleClick=false, Qt::MouseButtons buttonsBefore=Qt::NoButton); - void setWheelBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, MouseHandler handler, MouseAction action, bool withConstraint=true); - void setMouseBindingDescription(Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton button, QString description, bool doubleClick=false, Qt::MouseButtons buttonsBefore=Qt::NoButton); - - void clearMouseBindings(); - -#ifndef DOXYGEN - MouseAction wheelAction(Qt::KeyboardModifiers modifiers) const; - int wheelHandler(Qt::KeyboardModifiers modifiers) const; - - void setHandlerKeyboardModifiers(MouseHandler handler, Qt::KeyboardModifiers modifiers); - void setHandlerStateKey(MouseHandler handler, unsigned int buttonState); - void setMouseStateKey(MouseHandler handler, unsigned int buttonState); -#endif - -private: - static QString mouseActionString(QGLViewer::MouseAction ma); - static QString clickActionString(QGLViewer::ClickAction ca); - //@} - - - /*! @name State persistence */ - //@{ -public: - QString stateFileName() const; - virtual QDomElement domElement(const QString& name, QDomDocument& document) const; - -public Q_SLOTS: - virtual void initFromDOMElement(const QDomElement& element); - virtual void saveStateToFile(); // cannot be const because of QMessageBox - virtual bool restoreStateFromFile(); - - /*! Defines the stateFileName() used by saveStateToFile() and restoreStateFromFile(). - - The file name can have an optional prefix directory (no prefix meaning current directory). If the - directory does not exist, it will be created by saveStateToFile(). - - \code - // Name depends on the displayed 3D model. Saved in current directory. - setStateFileName(3DModelName() + ".xml"); - - // Files are stored in a dedicated directory under user's home directory. - setStateFileName(QDir::homeDirPath + "/.config/myApp.xml"); - \endcode */ - void setStateFileName(const QString& name) { stateFileName_ = name; } - -#ifndef DOXYGEN - void saveToFile(const QString& fileName=QString::null); - bool restoreFromFile(const QString& fileName=QString::null); -#endif - -private: - static void saveStateToFileForAllViewers(); - //@} - - - /*! @name QGLViewer pool */ - //@{ -public: - /*! Returns a \c QList that contains pointers to all the created QGLViewers. - Note that this list may contain \c NULL pointers if the associated viewer has been deleted. - - Can be useful to apply a method or to connect a signal to all the viewers: - \code - 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_; } - - - /*! 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 (see stateFileName() for an application - example). - - When a QGLViewer is deleted, the QGLViewers' indexes are preserved and NULL is set for that index. - When a QGLViewer is created, it is placed in the first 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)); } - //@} - -#ifndef DOXYGEN - /*! @name Visual hints */ - //@{ -public: - virtual void setVisualHintsMask(int mask, int delay = 2000); - virtual void drawVisualHints(); - -public Q_SLOTS: - virtual void resetVisualHints(); - //@} -#endif - -private Q_SLOTS: - // Patch for a Qt bug with fullScreen on startup - void delayedFullScreen() { move(prevPos_); setFullScreen(); } - void hideMessage(); - -protected: // Liangliang: I made this protected - // Copy constructor and operator= are declared private and undefined - // Prevents everyone from trying to use them - QGLViewer(const QGLViewer& v); - QGLViewer& operator=(const QGLViewer& v); - - // Set parameters to their default values. Called by the constructors. - void defaultConstructor(); - - void handleKeyboardAction(KeyboardAction id); - - // C a m e r a - qglviewer::Camera* camera_; - bool cameraIsEdited_; - qreal previousCameraZClippingCoefficient_; - unsigned int previousPathId_; // double key press recognition - void connectAllCameraKFIInterpolatedSignals(bool connection=true); - - // C o l o r s - QColor backgroundColor_, foregroundColor_; - - // D i s p l a y f l a g s - bool axisIsDrawn_; // world axis - bool gridIsDrawn_; // world XY grid - bool FPSIsDisplayed_; // Frame Per Seconds - bool textIsEnabled_; // drawText() actually draws text or not - bool stereo_; // stereo display - bool fullScreen_; // full screen mode - QPoint prevPos_; // Previous window position, used for full screen mode - - // A n i m a t i o n - bool animationStarted_; // animation mode started - int animationPeriod_; // period in msecs - int animationTimerId_; - - // F P S d i s p l a y - QTime fpsTime_; - unsigned int fpsCounter_; - QString fpsString_; - qreal f_p_s_; - - // M e s s a g e s - QString message_; - bool displayMessage_; - QTimer messageTimer_; - - // M a n i p u l a t e d f r a m e - qglviewer::ManipulatedFrame* manipulatedFrame_; - bool manipulatedFrameIsACamera_; - - // M o u s e G r a b b e r - qglviewer::MouseGrabber* mouseGrabber_; - bool mouseGrabberIsAManipulatedFrame_; - bool mouseGrabberIsAManipulatedCameraFrame_; - QMap disabledMouseGrabbers_; - - // S e l e c t i o n - int selectRegionWidth_, selectRegionHeight_; - int selectBufferSize_; - GLuint* selectBuffer_; - int selectedObjectId_; - - // V i s u a l h i n t s - int visualHint_; - - // S h o r t c u t k e y s - void setDefaultShortcuts(); - QString cameraPathKeysString() const; - QMap keyboardActionDescription_; - QMap keyboardBinding_; - QMap keyDescription_; - - // K e y F r a m e s s h o r t c u t s - QMap pathIndex_; - Qt::KeyboardModifiers addKeyFrameKeyboardModifiers_, playPathKeyboardModifiers_; - - // B u f f e r T e x t u r e - GLuint bufferTextureId_; - qreal bufferTextureMaxU_, bufferTextureMaxV_; - int bufferTextureWidth_, bufferTextureHeight_; - unsigned int previousBufferTextureFormat_; - int previousBufferTextureInternalFormat_; - -#ifndef DOXYGEN - // M o u s e a c t i o n s - struct MouseActionPrivate { - MouseHandler handler; - MouseAction action; - bool withConstraint; - }; - - // M o u s e b i n d i n g s - struct MouseBindingPrivate { - const Qt::KeyboardModifiers modifiers; - const Qt::MouseButton button; - const Qt::Key key; - - MouseBindingPrivate(Qt::KeyboardModifiers m, Qt::MouseButton b, Qt::Key k) - : modifiers(m), button(b), key(k) {} - - // This sort order is used in mouseString() to display sorted mouse bindings - bool operator<(const MouseBindingPrivate& mbp) const - { - if (key != mbp.key) - return key < mbp.key; - if (modifiers != mbp.modifiers) - return modifiers < mbp.modifiers; - return button < mbp.button; - } - }; - - // W h e e l b i n d i n g s - struct WheelBindingPrivate { - const Qt::KeyboardModifiers modifiers; - const Qt::Key key; - - WheelBindingPrivate(Qt::KeyboardModifiers m, Qt::Key k) - : modifiers(m), key(k) {} - - // This sort order is used in mouseString() to display sorted wheel bindings - bool operator<(const WheelBindingPrivate& wbp) const - { - if (key != wbp.key) - return key < wbp.key; - return modifiers < wbp.modifiers; - } - }; - - // C l i c k b i n d i n g s - struct ClickBindingPrivate { - const Qt::KeyboardModifiers modifiers; - const Qt::MouseButton button; - const bool doubleClick; - const Qt::MouseButtons buttonsBefore; // only defined when doubleClick is true - const Qt::Key key; - - ClickBindingPrivate(Qt::KeyboardModifiers m, Qt::MouseButton b, bool dc, Qt::MouseButtons bb, Qt::Key k) - : modifiers(m), button(b), doubleClick(dc), buttonsBefore(bb), key(k) {} - - // This sort order is used in mouseString() to display sorted mouse bindings - bool operator<(const ClickBindingPrivate& cbp) const - { - if (key != cbp.key) - return key < cbp.key; - if (buttonsBefore != cbp.buttonsBefore) - return buttonsBefore < cbp.buttonsBefore; - if (modifiers != cbp.modifiers) - return modifiers < cbp.modifiers; - if (button != cbp.button) - return button < cbp.button; - return doubleClick != cbp.doubleClick; - } - }; -#endif - static QString formatClickActionPrivate(ClickBindingPrivate cbp); - static bool isValidShortcutKey(int key); - - QMap mouseDescription_; - - void setDefaultMouseBindings(); - void performClickAction(ClickAction ca, const QMouseEvent* const e); - QMap mouseBinding_; - QMap wheelBinding_; - QMap clickBinding_; - Qt::Key currentlyPressedKey_; - - // S n a p s h o t s - void initializeSnapshotFormats(); - QImage frameBufferSnapshot(); - QString snapshotFileName_, snapshotFormat_; - int snapshotCounter_, snapshotQuality_; - TileRegion* tileRegion_; - - // 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_; - - // H e l p w i n d o w - QTabWidget* helpWidget_; -}; - -#endif // QGLVIEWER_QGLVIEWER_H diff --git a/code/3rd_QGLViewer-2.6.3/quaternion.cpp b/code/3rd_QGLViewer-2.6.3/quaternion.cpp deleted file mode 100644 index 0c308094..00000000 --- a/code/3rd_QGLViewer-2.6.3/quaternion.cpp +++ /dev/null @@ -1,552 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "domUtils.h" -#include "quaternion.h" -#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. */ -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::setFromRotationMatrix(const float m[3][3]) -{ - qWarning("setFromRotationMatrix now expects a double[3][3] parameter"); - - qreal mat[3][3]; - for (int i=0; i<3; ++i) - for (int j=0; j<3; ++j) - mat[i][j] = qreal(m[i][j]); - - setFromRotationMatrix(mat); -} - -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 -#include - -namespace qglviewer { -/*! \brief The Quaternion class represents 3D rotations and orientations. - \class Quaternion quaternion.h QGLViewer/quaternion.h - - The Quaternion is an appropriate (although not very intuitive) representation for 3D rotations and - orientations. Many tools are provided to ease the definition of a Quaternion: see constructors, - setAxisAngle(), setFromRotationMatrix(), setFromRotatedBasis(). - - You can apply the rotation represented by the Quaternion to 3D points using rotate() and - inverseRotate(). See also the Frame class that represents a coordinate system and provides other - conversion functions like Frame::coordinatesOf() and Frame::transformOf(). - - You can apply the Quaternion \c q rotation to the OpenGL matrices using: - \code - glMultMatrixd(q.matrix()); - // equvalent to glRotate(q.angle()*180.0/M_PI, q.axis().x, q.axis().y, q.axis().z); - \endcode - - Quaternion is part of the \c qglviewer namespace, specify \c qglviewer::Quaternion or use the qglviewer - namespace: \code using namespace qglviewer; \endcode - -

Internal representation

- - The internal representation of a Quaternion corresponding to a rotation around axis \c axis, with an angle - \c alpha is made of four qreals (i.e. doubles) q[i]: - \code - {q[0],q[1],q[2]} = sin(alpha/2) * {axis[0],axis[1],axis[2]} - q[3] = cos(alpha/2) - \endcode - - Note that certain implementations place the cosine term in first position (instead of last here). - - The Quaternion is always normalized, so that its inverse() is actually its conjugate. - - See also the Vec and Frame classes' documentations. - \nosubgrouping */ -class QGLVIEWER_EXPORT Quaternion -{ -public: - /*! @name Defining a Quaternion */ - //@{ - /*! Default constructor, builds an identity rotation. */ - Quaternion() - { q[0]=q[1]=q[2]=0.0; q[3]=1.0; } - - /*! Constructor from rotation axis (non null) and angle (in radians). See also setAxisAngle(). */ - Quaternion(const Vec& axis, qreal angle) - { - setAxisAngle(axis, angle); - } - - Quaternion(const Vec& from, const Vec& to); - - /*! Constructor from the four values of a Quaternion. First three values are axis*sin(angle/2) and - last one is cos(angle/2). - - \attention The identity Quaternion is Quaternion(0,0,0,1) and \e not Quaternion(0,0,0,0) (which is - not unitary). The default Quaternion() creates such identity Quaternion. */ - Quaternion(qreal q0, qreal q1, qreal q2, qreal q3) - { q[0]=q0; q[1]=q1; q[2]=q2; q[3]=q3; } - - /*! Copy constructor. */ - Quaternion(const Quaternion& Q) - { for (int i=0; i<4; ++i) q[i] = Q.q[i]; } - - /*! Equal operator. */ - Quaternion& operator=(const Quaternion& Q) - { - for (int i=0; i<4; ++i) - q[i] = Q.q[i]; - return (*this); - } - - /*! Sets the Quaternion as a rotation of axis \p axis and angle \p angle (in radians). - - \p axis does not need to be normalized. A null \p axis will result in an identity Quaternion. */ - void setAxisAngle(const Vec& axis, qreal angle) - { - const qreal norm = axis.norm(); - if (norm < 1E-8) - { - // Null rotation - q[0] = 0.0; q[1] = 0.0; q[2] = 0.0; q[3] = 1.0; - } - else - { - const qreal sin_half_angle = sin(angle / 2.0); - q[0] = sin_half_angle*axis[0]/norm; - q[1] = sin_half_angle*axis[1]/norm; - q[2] = sin_half_angle*axis[2]/norm; - q[3] = cos(angle / 2.0); - } - } - - /*! Sets the Quaternion value. See the Quaternion(qreal, qreal, qreal, qreal) constructor documentation. */ - void setValue(qreal q0, qreal q1, qreal q2, qreal q3) - { q[0]=q0; q[1]=q1; q[2]=q2; q[3]=q3; } - -#ifndef DOXYGEN - void setFromRotationMatrix(const float m[3][3]); - void setFromRotatedBase(const Vec& X, const Vec& Y, const Vec& Z); -#endif - void setFromRotationMatrix(const qreal m[3][3]); - void setFromRotatedBasis(const Vec& X, const Vec& Y, const Vec& Z); - //@} - - - /*! @name Accessing values */ - //@{ - Vec axis() const; - qreal angle() const; - void getAxisAngle(Vec& axis, qreal& angle) const; - - /*! Bracket operator, with a constant return value. \p i must range in [0..3]. See the Quaternion(qreal, qreal, qreal, qreal) documentation. */ - qreal operator[](int i) const { return q[i]; } - - /*! Bracket operator returning an l-value. \p i must range in [0..3]. See the Quaternion(qreal, qreal, qreal, qreal) documentation. */ - qreal& operator[](int i) { return q[i]; } - //@} - - - /*! @name Rotation computations */ - //@{ - /*! Returns the composition of the \p a and \p b rotations. - - The order is important. When applied to a Vec \c v (see operator*(const Quaternion&, const Vec&) - and rotate()) the resulting Quaternion acts as if \p b was applied first and then \p a was - applied. This is obvious since the image \c v' of \p v by the composited rotation satisfies: \code - v'= (a*b) * v = a * (b*v) \endcode - - Note that a*b usually differs from b*a. - - \attention For efficiency reasons, the resulting Quaternion is not normalized. Use normalize() in - case of numerical drift with small rotation composition. */ - friend Quaternion operator*(const Quaternion& a, const Quaternion& b) - { - return Quaternion(a.q[3]*b.q[0] + b.q[3]*a.q[0] + a.q[1]*b.q[2] - a.q[2]*b.q[1], - a.q[3]*b.q[1] + b.q[3]*a.q[1] + a.q[2]*b.q[0] - a.q[0]*b.q[2], - a.q[3]*b.q[2] + b.q[3]*a.q[2] + a.q[0]*b.q[1] - a.q[1]*b.q[0], - a.q[3]*b.q[3] - b.q[0]*a.q[0] - a.q[1]*b.q[1] - a.q[2]*b.q[2]); - } - - /*! Quaternion rotation is composed with \p q. - - See operator*(), since this is equivalent to \c this = \c this * \p q. - - \note For efficiency reasons, the resulting Quaternion is not normalized. - You may normalize() it after each application in case of numerical drift. */ - Quaternion& operator*=(const Quaternion &q) - { - *this = (*this)*q; - return *this; - } - - /*! Returns the image of \p v by the rotation \p q. - - Same as q.rotate(v). See rotate() and inverseRotate(). */ - friend Vec operator*(const Quaternion& q, const Vec& v) - { - return q.rotate(v); - } - - Vec rotate(const Vec& v) const; - Vec inverseRotate(const Vec& v) const; - //@} - - - /*! @name Inversion */ - //@{ - /*! Returns the inverse Quaternion (inverse rotation). - - Result has a negated axis() direction and the same angle(). A composition (see operator*()) of a - Quaternion and its inverse() results in an identity function. - - Use invert() to actually modify the Quaternion. */ - Quaternion inverse() const { return Quaternion(-q[0], -q[1], -q[2], q[3]); } - - /*! Inverses the Quaternion (same rotation angle(), but negated axis()). - - See also inverse(). */ - void invert() { q[0] = -q[0]; q[1] = -q[1]; q[2] = -q[2]; } - - /*! Negates all the coefficients of the Quaternion. - - This results in an other representation of the \e same rotation (opposite rotation angle, but with - a negated axis direction: the two cancel out). However, note that the results of axis() and - angle() are unchanged after a call to this method since angle() always returns a value in [0,pi]. - - This method is mainly useful for Quaternion interpolation, so that the spherical - interpolation takes the shortest path on the unit sphere. See slerp() for details. */ - void negate() { invert(); q[3] = -q[3]; } - - /*! Normalizes the Quaternion coefficients. - - This method should not need to be called since we only deal with unit Quaternions. This is however - useful to prevent numerical drifts, especially with small rotational increments. See also - normalized(). */ - qreal normalize() - { - const qreal norm = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); - for (int i=0; i<4; ++i) - q[i] /= norm; - return norm; - } - - /*! Returns a normalized version of the Quaternion. - - See also normalize(). */ - Quaternion normalized() const - { - qreal Q[4]; - const qreal norm = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); - for (int i=0; i<4; ++i) - Q[i] = q[i] / norm; - return Quaternion(Q[0], Q[1], Q[2], Q[3]); - } - //@} - - - /*! @name Associated matrix */ - //@{ - const GLdouble* matrix() const; - void getMatrix(GLdouble m[4][4]) const; - void getMatrix(GLdouble m[16]) const; - - void getRotationMatrix(qreal m[3][3]) const; - - const GLdouble* inverseMatrix() const; - void getInverseMatrix(GLdouble m[4][4]) const; - void getInverseMatrix(GLdouble m[16]) const; - - void getInverseRotationMatrix(qreal m[3][3]) const; - //@} - - - /*! @name Slerp interpolation */ - //@{ - static Quaternion slerp(const Quaternion& a, const Quaternion& b, qreal t, bool allowFlip=true); - static Quaternion squad(const Quaternion& a, const Quaternion& tgA, const Quaternion& tgB, const Quaternion& b, qreal t); - /*! Returns the "dot" product of \p a and \p b: a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]. */ - static qreal dot(const Quaternion& a, const Quaternion& b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]; } - - Quaternion log(); - Quaternion exp(); - static Quaternion lnDif(const Quaternion& a, const Quaternion& b); - static Quaternion squadTangent(const Quaternion& before, const Quaternion& center, const Quaternion& after); - //@} - - /*! @name Random Quaternion */ - //@{ - static Quaternion randomQuaternion(); - //@} - - /*! @name XML representation */ - //@{ - explicit Quaternion(const QDomElement& element); - QDomElement domElement(const QString& name, QDomDocument& document) const; - void initFromDOMElement(const QDomElement& element); - //@} - -#ifdef DOXYGEN - /*! @name Output stream */ - //@{ - /*! Output stream operator. Enables debugging code like: - \code - Quaternion rot(...); - cout << "Rotation=" << rot << endl; - \endcode */ - std::ostream& operator<<(std::ostream& o, const qglviewer::Vec&); - //@} -#endif - -private: - /*! The internal data representation is private, use operator[] to access values. */ - qreal q[4]; -}; - -} // namespace - -QGLVIEWER_EXPORT std::ostream& operator<<(std::ostream& o, const qglviewer::Quaternion&); - -#endif // QGLVIEWER_QUATERNION_H diff --git a/code/3rd_QGLViewer-2.6.3/saveSnapshot.cpp b/code/3rd_QGLViewer-2.6.3/saveSnapshot.cpp deleted file mode 100644 index 932286b0..00000000 --- a/code/3rd_QGLViewer-2.6.3/saveSnapshot.cpp +++ /dev/null @@ -1,638 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "qglviewer.h" - -#ifndef NO_VECTORIAL_RENDER -# include "ui_VRenderInterface.h" -# include "VRender/VRender.h" -#endif - -#include "ImageInterface.h" - -// Output format list -# include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -////// Static global variables - local to this file ////// - -static bool snapshot_format_initialized = false; -// List of available output file formats, formatted for QFileDialog. -static QString formats; -// Converts QFileDialog resulting format to Qt snapshotFormat. -static QMap Qtformat; -// Converts Qt snapshotFormat to QFileDialog menu string. -static QMap FDFormatString; -// Converts snapshotFormat to file extension -static QMap extension; - - -/*! Sets snapshotFileName(). */ -void QGLViewer::setSnapshotFileName(const QString& name) -{ - snapshotFileName_ = QFileInfo(name).absoluteFilePath(); -} - -#ifndef DOXYGEN -const QString& QGLViewer::snapshotFilename() const -{ - qWarning("snapshotFilename is deprecated. Use snapshotFileName() (uppercase N) instead."); - return snapshotFileName(); -} -#endif - - -/*! Opens a dialog that displays the different available snapshot formats. - -Then calls setSnapshotFormat() with the selected one (unless the user cancels). - -Returns \c false if the user presses the Cancel button and \c true otherwise. */ -bool QGLViewer::openSnapshotFormatDialog() -{ - bool ok = false; - QStringList list = formats.split(";;", QString::SkipEmptyParts); - int current = list.indexOf(FDFormatString[snapshotFormat()]); - QString format = QInputDialog::getItem(this, "Snapshot format", "Select a snapshot format", list, current, false, &ok); - if (ok) - setSnapshotFormat(Qtformat[format]); - return ok; -} - - -// Finds all available Qt output formats, so that they can be available in -// saveSnapshot dialog. Initialize snapshotFormat() to the first one. -void QGLViewer::initializeSnapshotFormats() -{ - if (snapshot_format_initialized) - return; - QList list = QImageWriter::supportedImageFormats(); - QStringList formatList; - for (int i=0; i < list.size(); ++i) - formatList << QString(list.at(i).toUpper()); - // qWarning("Available image formats: "); - // QStringList::Iterator it = formatList.begin(); - // while( it != formatList.end() ) - // qWarning((*it++).); QT4 change this. qWarning no longer accepts QString - -#ifndef NO_VECTORIAL_RENDER - // We add the 3 vectorial formats to the list - formatList += "EPS"; - formatList += "PS"; - formatList += "XFIG"; -#endif - - // Check that the interesting formats are available and add them in "formats" - // Unused formats: XPM XBM PBM PGM - QStringList QtText, MenuText, Ext; - QtText += "PNG"; MenuText += "PNG (*.png)"; Ext += "png"; - QStringList::iterator itText = QtText.begin(); - QStringList::iterator itMenu = MenuText.begin(); - QStringList::iterator itExt = Ext.begin(); - - while (itText != QtText.end()) - { - //QMessageBox::information(this, "Snapshot ", "Trying format\n"+(*itText)); - if (formatList.contains((*itText))) - { - //QMessageBox::information(this, "Snapshot ", "Recognized format\n"+(*itText)); - if (formats.isEmpty()) - setSnapshotFormat(*itText); - else - formats += ";;"; - formats += (*itMenu); - Qtformat[(*itMenu)] = (*itText); - FDFormatString[(*itText)] = (*itMenu); - extension[(*itText)] = (*itExt); - } - // Synchronize parsing - itText++; - itMenu++; - itExt++; - } - snapshot_format_initialized = true; -} - -// Returns false if the user refused to use the fileName -static bool checkFileName(QString& fileName, QWidget* widget, const QString& snapshotFormat) -{ - if (fileName.isEmpty()) - return false; - - // Check that extension has been provided - QFileInfo info(fileName); - - if (info.suffix().isEmpty()) - { - // No extension given. Silently add one - if (fileName.right(1) != ".") - fileName += "."; - fileName += extension[snapshotFormat]; - info.setFile(fileName); - } - else if (info.suffix() != extension[snapshotFormat]) - { - // Extension is not appropriate. Propose a modification - QString modifiedName = info.absolutePath() + '/' + info.baseName() + "." + extension[snapshotFormat]; - QFileInfo modifInfo(modifiedName); - int i=(QMessageBox::warning(widget,"Wrong extension", - info.fileName()+" has a wrong extension.\nSave as "+modifInfo.fileName()+" instead ?", - QMessageBox::Yes, - QMessageBox::No, - QMessageBox::Cancel)); - if (i==QMessageBox::Cancel) - return false; - - if (i==QMessageBox::Yes) - { - fileName = modifiedName; - info.setFile(fileName); - } - } - - return true; -} - -#ifndef NO_VECTORIAL_RENDER -// static void drawVectorial(void* param) -void drawVectorial(void* param) -{ - ( (QGLViewer*) param )->drawVectorial(); -} - -#ifndef DOXYGEN -class ProgressDialog -{ -public: - static void showProgressDialog(QGLWidget* parent); - static void updateProgress(float progress, const QString& stepString); - static void hideProgressDialog(); - -private: - static QProgressDialog* progressDialog; -}; - -QProgressDialog* ProgressDialog::progressDialog = NULL; - -void ProgressDialog::showProgressDialog(QGLWidget* parent) -{ - progressDialog = new QProgressDialog(parent); - progressDialog->setWindowTitle("Image rendering progress"); - progressDialog->setMinimumSize(300, 40); - progressDialog->setCancelButton(NULL); - progressDialog->show(); -} - -void ProgressDialog::updateProgress(float progress, const QString& stepString) -{ - progressDialog->setValue(int(progress*100)); - QString message(stepString); - if (message.length() > 33) - message = message.left(17) + "..." + message.right(12); - progressDialog->setLabelText(message); - progressDialog->update(); - qApp->processEvents(); -} - -void ProgressDialog::hideProgressDialog() -{ - progressDialog->close(); - delete progressDialog; - progressDialog = NULL; -} - -class VRenderInterface: public QDialog, public Ui::VRenderInterface -{ -public: VRenderInterface(QWidget *parent) : QDialog(parent) { setupUi(this); } -}; - -#endif //DOXYGEN - -// Pops-up a vectorial output option dialog box and save to fileName -// Returns -1 in case of Cancel, 0 for success and (todo) error code in case of problem. -static int saveVectorialSnapshot(const QString& fileName, QGLWidget* widget, const QString& snapshotFormat) -{ - static VRenderInterface* VRinterface = NULL; - - if (!VRinterface) - VRinterface = new VRenderInterface(widget); - - - // Configure interface according to selected snapshotFormat - if (snapshotFormat == "XFIG") - { - VRinterface->tightenBBox->setEnabled(false); - VRinterface->colorBackground->setEnabled(false); - } - else - { - VRinterface->tightenBBox->setEnabled(true); - VRinterface->colorBackground->setEnabled(true); - } - - if (VRinterface->exec() == QDialog::Rejected) - return -1; - - vrender::VRenderParams vparams; - vparams.setFilename(fileName); - - if (snapshotFormat == "EPS") vparams.setFormat(vrender::VRenderParams::EPS); - if (snapshotFormat == "PS") vparams.setFormat(vrender::VRenderParams::PS); - if (snapshotFormat == "XFIG") vparams.setFormat(vrender::VRenderParams::XFIG); - - vparams.setOption(vrender::VRenderParams::CullHiddenFaces, !(VRinterface->includeHidden->isChecked())); - vparams.setOption(vrender::VRenderParams::OptimizeBackFaceCulling, VRinterface->cullBackFaces->isChecked()); - vparams.setOption(vrender::VRenderParams::RenderBlackAndWhite, VRinterface->blackAndWhite->isChecked()); - vparams.setOption(vrender::VRenderParams::AddBackground, VRinterface->colorBackground->isChecked()); - vparams.setOption(vrender::VRenderParams::TightenBoundingBox, VRinterface->tightenBBox->isChecked()); - - switch (VRinterface->sortMethod->currentIndex()) - { - case 0: vparams.setSortMethod(vrender::VRenderParams::NoSorting); break; - case 1: vparams.setSortMethod(vrender::VRenderParams::BSPSort); break; - case 2: vparams.setSortMethod(vrender::VRenderParams::TopologicalSort); break; - case 3: vparams.setSortMethod(vrender::VRenderParams::AdvancedTopologicalSort); break; - default: - qWarning("VRenderInterface::saveVectorialSnapshot: Unknown SortMethod"); - } - - vparams.setProgressFunction(&ProgressDialog::updateProgress); - ProgressDialog::showProgressDialog(widget); - widget->makeCurrent(); - widget->raise(); - vrender::VectorialRender(drawVectorial, (void*) widget, vparams); - ProgressDialog::hideProgressDialog(); - widget->setCursor(QCursor(Qt::ArrowCursor)); - - // Should return vparams.error(), but this is currently not set. - return 0; -} -#endif // NO_VECTORIAL_RENDER - - -//class ImageInterface: public QDialog, public Ui::ImageInterface -//{ -//public: ImageInterface(QWidget *parent) : QDialog(parent) { setupUi(this); } -//}; - - -// Pops-up an image settings dialog box and save to fileName. -// Returns false in case of problem. -bool QGLViewer::saveImageSnapshot(const QString& fileName) -{ - static ImageInterface* imageInterface = NULL; - - if (!imageInterface) - imageInterface = new ImageInterface(this); - else { - // imageInterface->imgScale->setValue(1); // assume the user wants to use the same scale - int scale = imageInterface->imgScale->value(); - imageInterface->imgWidth->setValue(width() * scale); - imageInterface->imgHeight->setValue(height() * scale); - imageInterface->imgQuality->setValue(snapshotQuality()); - } - - if (imageInterface->exec() == QDialog::Rejected) - return true; - - // Hide closed dialog - qApp->processEvents(); - - setSnapshotQuality(imageInterface->imgQuality->value()); - - QColor previousBGColor = backgroundColor(); - if (imageInterface->whiteBackground->isChecked()) - setBackgroundColor(Qt::white); - - QSize finalSize(imageInterface->imgWidth->value(), imageInterface->imgHeight->value()); - - qreal oversampling = imageInterface->oversampling->value(); - QSize subSize(int(this->width()/oversampling), int(this->height()/oversampling)); - - qreal aspectRatio = width() / static_cast(height()); - qreal newAspectRatio = finalSize.width() / static_cast(finalSize.height()); - - qreal zNear = camera()->zNear(); - qreal zFar = camera()->zFar(); - - qreal xMin, yMin; - bool expand = imageInterface->expandFrustum->isChecked(); - if (camera()->type() == qglviewer::Camera::PERSPECTIVE) - if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatiofieldOfView() / 2.0); - xMin = newAspectRatio * yMin; - } - else - { - xMin = zNear * tan(camera()->fieldOfView() / 2.0) * aspectRatio; - yMin = xMin / newAspectRatio; - } - else - { - camera()->getOrthoWidthHeight(xMin, yMin); - if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio(finalSize.width()); - qreal scaleY = subSize.height() / static_cast(finalSize.height()); - - qreal deltaX = 2.0 * xMin * scaleX; - qreal deltaY = 2.0 * yMin * scaleY; - - int nbX = finalSize.width() / subSize.width(); - int nbY = finalSize.height() / subSize.height(); - - // Extra subimage on the right/bottom border(s) if needed - if (nbX * subSize.width() < finalSize.width()) - nbX++; - if (nbY * subSize.height() < finalSize.height()) - nbY++; - - makeCurrent(); - - // tileRegion_ is used by startScreenCoordinatesSystem to appropriately set the local - // coordinate system when tiling - tileRegion_ = new TileRegion(); - qreal tileXMin, tileWidth, tileYMin, tileHeight; - if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatiotextScale = 1.0 / scaleY; - } - else - { - qreal tileTotalHeight = width() / newAspectRatio; - tileYMin = (height() - tileTotalHeight) / 2.0; - tileHeight = tileTotalHeight * scaleY; - tileXMin = 0.0; - tileWidth = width() * scaleX; - tileRegion_->textScale = 1.0 / scaleX; - } - - int count=0; - for (int i=0; itype() == qglviewer::Camera::PERSPECTIVE) - glFrustum(-xMin + i*deltaX, -xMin + (i+1)*deltaX, yMin - (j+1)*deltaY, yMin - j*deltaY, zNear, zFar); - else - glOrtho(-xMin + i*deltaX, -xMin + (i+1)*deltaX, yMin - (j+1)*deltaY, yMin - j*deltaY, zNear, zFar); - glMatrixMode(GL_MODELVIEW); - - tileRegion_->xMin = tileXMin + i * tileWidth; - tileRegion_->xMax = tileXMin + (i+1) * tileWidth; - tileRegion_->yMin = tileYMin + j * tileHeight; - tileRegion_->yMax = tileYMin + (j+1) * tileHeight; - - draw(); - postDraw(); - - // ProgressDialog::hideProgressDialog(); - // qApp->processEvents(); - - QImage snapshot = grabFrameBuffer(true); - - // ProgressDialog::showProgressDialog(this); - // ProgressDialog::updateProgress(count / (qreal)(nbX*nbY), - // "Generating image ["+QString::number(count)+"/"+QString::number(nbX*nbY)+"]"); - // qApp->processEvents(); - - QImage subImage = snapshot.scaled(subSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - - // Copy subImage in image - for (int ii=0; iiwhiteBackground->isChecked()) - setBackgroundColor(previousBGColor); - - return saveOK; -} - - -/*! Saves a snapshot of the current image displayed by the widget. - - Options are set using snapshotFormat(), snapshotFileName() and snapshotQuality(). For non vectorial - image formats, the image size is equal to the current viewer's dimensions (see width() and - height()). See snapshotFormat() for details on supported formats. - - If \p automatic is \c false (or if snapshotFileName() is empty), a file dialog is opened to ask for - the file name. - - When \p automatic is \c true, the file name is set to \c NAME-NUMBER, where \c NAME is - snapshotFileName() and \c NUMBER is snapshotCounter(). The snapshotCounter() is automatically - incremented after each snapshot saving. This is useful to create videos from your application: - \code - void Viewer::init() - { - resize(720, 576); // PAL DV format (use 720x480 for NTSC DV) - connect(this, SIGNAL(drawFinished(bool)), SLOT(saveSnapshot(bool))); - } - \endcode - Then call draw() in a loop (for instance using animate() and/or a camera() KeyFrameInterpolator - replay) to create your image sequence. - - If you want to create a Quicktime VR panoramic sequence, simply use code like this: - \code - void Viewer::createQuicktime() - { - const int nbImages = 36; - for (int i=0; isetOrientation(2.0*M_PI/nbImages, 0.0); // Theta-Phi orientation - showEntireScene(); - update(); // calls draw(), which emits drawFinished(), which calls saveSnapshot() - } - } - \endcode - - If snapshotCounter() is negative, no number is appended to snapshotFileName() and the - snapshotCounter() is not incremented. This is useful to force the creation of a file, overwriting - the previous one. - - When \p overwrite is set to \c false (default), a window asks for confirmation if the file already - exists. In \p automatic mode, the snapshotCounter() is incremented (if positive) until a - non-existing file name is found instead. Otherwise the file is overwritten without confirmation. - - The VRender library was written by Cyril Soler (Cyril dot Soler at imag dot fr). If the generated - PS or EPS file is not properly displayed, remove the anti-aliasing option in your postscript viewer. - - \note In order to correctly grab the frame buffer, the QGLViewer window is raised in front of - other windows by this method. */ -void QGLViewer::saveSnapshot(bool automatic, bool overwrite) -{ - // Ask for file name - if (snapshotFileName().isEmpty() || !automatic) - { - QString fileName; - QString selectedFormat = FDFormatString[snapshotFormat()]; - fileName = QFileDialog::getSaveFileName(this, "Choose a file name to save under", snapshotFileName(), formats, &selectedFormat, - overwrite?QFileDialog::DontConfirmOverwrite:QFlags(0)); - setSnapshotFormat(Qtformat[selectedFormat]); - - if (checkFileName(fileName, this, snapshotFormat())) - setSnapshotFileName(fileName); - else - return; - } - - QFileInfo fileInfo(snapshotFileName()); - - if ((automatic) && (snapshotCounter() >= 0)) - { - // In automatic mode, names have a number appended - const QString baseName = fileInfo.baseName(); - QString count; - count.sprintf("%.04d", snapshotCounter_++); - QString suffix; - suffix = fileInfo.suffix(); - if (suffix.isEmpty()) - suffix = extension[snapshotFormat()]; - fileInfo.setFile(fileInfo.absolutePath()+ '/' + baseName + '-' + count + '.' + suffix); - - if (!overwrite) - while (fileInfo.exists()) - { - count.sprintf("%.04d", snapshotCounter_++); - fileInfo.setFile(fileInfo.absolutePath() + '/' +baseName + '-' + count + '.' + fileInfo.suffix()); - } - } - - bool saveOK; -#ifndef NO_VECTORIAL_RENDER - if ( (snapshotFormat() == "EPS") || (snapshotFormat() == "PS") || (snapshotFormat() == "XFIG") ) - // Vectorial snapshot. -1 means cancel, 0 is ok, >0 (should be) an error - saveOK = (saveVectorialSnapshot(fileInfo.filePath(), this, snapshotFormat()) <= 0); - else -#endif - if (automatic) - { - QImage snapshot = frameBufferSnapshot(); - saveOK = snapshot.save(fileInfo.filePath(), snapshotFormat().toLatin1().constData(), snapshotQuality()); - } - else - saveOK = saveImageSnapshot(fileInfo.filePath()); - - if (!saveOK) - QMessageBox::warning(this, "Snapshot problem", "Unable to save snapshot in\n"+fileInfo.filePath()); -} - -QImage QGLViewer::frameBufferSnapshot() -{ - // Viewer must be on top of other windows. - makeCurrent(); - raise(); - // Hack: Qt has problems if the frame buffer is grabbed after QFileDialog is displayed. - // We grab the frame buffer before, even if it might be not necessary (vectorial rendering). - // The problem could not be reproduced on a simple example to submit a Qt bug. - // However, only grabs the backgroundImage in the eponym example. May come from the driver. - return grabFrameBuffer(true); -} - -/*! Same as saveSnapshot(), except that it uses \p fileName instead of snapshotFileName(). - - If \p fileName is empty, opens a file dialog to select the name. - - Snapshot settings are set from snapshotFormat() and snapshotQuality(). - - Asks for confirmation when \p fileName already exists and \p overwrite is \c false (default). - - \attention If \p fileName is a char* (as is "myFile.jpg"), it may be casted into a \c bool, and the - other saveSnapshot() method may be used instead. Pass QString("myFile.jpg") as a parameter to - prevent this. */ -void QGLViewer::saveSnapshot(const QString& fileName, bool overwrite) -{ - const QString previousName = snapshotFileName(); - const int previousCounter = snapshotCounter(); - setSnapshotFileName(fileName); - setSnapshotCounter(-1); - saveSnapshot(true, overwrite); - setSnapshotFileName(previousName); - setSnapshotCounter(previousCounter); -} - -/*! Takes a snapshot of the current display and pastes it to the clipboard. - -This action is activated by the KeyboardAction::SNAPSHOT_TO_CLIPBOARD enum, binded to \c Ctrl+C by default. -*/ -void QGLViewer::snapshotToClipboard() -{ - QClipboard *cb = QApplication::clipboard(); - cb->setImage(frameBufferSnapshot()); -} - diff --git a/code/3rd_QGLViewer-2.6.3/vec.cpp b/code/3rd_QGLViewer-2.6.3/vec.cpp deleted file mode 100644 index 669d6f71..00000000 --- a/code/3rd_QGLViewer-2.6.3/vec.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#include "domUtils.h" -#include "vec.h" - -// 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; ioperator[](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; -} - diff --git a/code/3rd_QGLViewer-2.6.3/vec.h b/code/3rd_QGLViewer-2.6.3/vec.h deleted file mode 100644 index b024d5e1..00000000 --- a/code/3rd_QGLViewer-2.6.3/vec.h +++ /dev/null @@ -1,390 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - -#ifndef QGLVIEWER_VEC_H -#define QGLVIEWER_VEC_H - -#include -#include - -# include - -// Included by all files as vec.h is at the end of the include hierarchy -#include "config.h" // Specific configuration options. - -namespace qglviewer { - -/*! \brief The Vec class represents 3D positions and 3D vectors. - \class Vec vec.h QGLViewer/vec.h - - Vec is used as a parameter and return type by many methods of the library. It provides classical - algebraic computational methods and is compatible with OpenGL: - - \code - // Draws a point located at 3.0 OpenGL units in front of the camera - Vec pos = camera()->position() + 3.0 * camera()->viewDirection(); - glBegin(GL_POINTS); - glVertex3fv(pos); - glEnd(); - \endcode - - This makes of Vec a good candidate for representing positions and vectors in your programs. Since - it is part of the \c qglviewer namespace, specify \c qglviewer::Vec or use the qglviewer - namespace: - \code - using namespace qglviewer; - \endcode - -

Interface with other vector classes

- - Vec implements a universal explicit converter, based on the \c [] \c operator. - Everywhere a \c const \c Vec& argument is expected, you can use your own vector type - instead, as long as it implements this operator (see the Vec(const C& c) documentation). - - See also the Quaternion and the Frame documentations. - \nosubgrouping */ -class QGLVIEWER_EXPORT Vec -{ - - // If your compiler complains the "The class "qglviewer::Vec" has no member "x"." - // Add your architecture Q_OS_XXXX flag (see qglobal.h) in this list. -#if defined (Q_OS_IRIX) || defined (Q_OS_AIX) || defined (Q_OS_HPUX) -# define QGLVIEWER_UNION_NOT_SUPPORTED -#endif - -public: - /*! The internal data representation is public. One can use v.x, v.y, v.z. See also operator[](). */ -#if defined (DOXYGEN) || defined (QGLVIEWER_UNION_NOT_SUPPORTED) - qreal x, y, z; -#else - union - { - struct { qreal x, y, z; }; - qreal v_[3]; - }; -#endif - - /*! @name Setting the value */ - //@{ - /*! Default constructor. Value is set to (0,0,0). */ - Vec() : x(0.0), y(0.0), z(0.0) {} - - /*! Standard constructor with the x, y and z values. */ - Vec(qreal X, qreal Y, qreal Z) : x(X), y(Y), z(Z) {} - - /*! Universal explicit converter from any class to Vec. You can use your own vector class everywhere - a \c const \c Vec& parameter is required, as long as it implements the \c operator[ ]: - - \code - class MyVec - { - // ... - qreal operator[](int i) const { returns x, y or z when i=0, 1 or 2; } - } - - MyVec v(...); - camera()->setPosition(v); - \endcode - - Note that standard vector types (STL, \c qreal[3], ...) implement this operator and can hence - be used in place of Vec. See also operator const qreal*() .*/ - template - explicit Vec(const C& c) : x(c[0]), y(c[1]), z(c[2]) {} - // Should NOT be explicit to prevent conflicts with operator<<. - - // ! Copy constructor - // Vec(const Vec& v) : x(v.x), y(v.y), z(v.z) {} - - /*! Equal operator. */ - Vec& operator=(const Vec& v) - { - x = v.x; y = v.y; z = v.z; - return *this; - } - - /*! Set the current value. May be faster than using operator=() with a temporary Vec(x,y,z). */ - void setValue(qreal X, qreal Y, qreal Z) - { x=X; y=Y; z=Z; } - - // Universal equal operator which allows the use of any type in place of Vec, - // as long as the [] operator is implemented (v[0]=v.x, v[1]=v.y, v[2]=v.z). - // template - // Vec& operator=(const C& c) - // { - // x=c[0]; y=c[1]; z=c[2]; - // return *this; - // } - //@} - - /*! @name Accessing the value */ - //@{ - /*! Bracket operator, with a constant return value. \p i must range in [0..2]. */ - qreal operator[](int i) const { -#ifdef QGLVIEWER_UNION_NOT_SUPPORTED - return (&x)[i]; -#else - return v_[i]; -#endif - } - - /*! Bracket operator returning an l-value. \p i must range in [0..2]. */ - qreal& operator[](int i) { -#ifdef QGLVIEWER_UNION_NOT_SUPPORTED - return (&x)[i]; -#else - return v_[i]; -#endif - } - -#ifndef DOXYGEN - /*! This method is deprecated since version 2.0. Use operator const double* instead. */ - const double* address() const { qWarning("Vec::address() is deprecated, use operator const double* instead."); return operator const double*(); } -#endif - - /*! Conversion operator returning the memory address of the vector. - - Very convenient to pass a Vec pointer as a parameter to \c GLdouble OpenGL functions: - \code - Vec pos, normal; - glNormal3dv(normal); - glVertex3dv(pos); - \endcode */ - operator const double*() const { -#ifdef QGLVIEWER_UNION_NOT_SUPPORTED - return &x; -#else - return v_; -#endif - } - - /*! Non const conversion operator returning the memory address of the vector. - - Useful to pass a Vec to a method that requires and fills a \c double*, as provided by certain libraries. */ - operator double*() { -#ifdef QGLVIEWER_UNION_NOT_SUPPORTED - return &x; -#else - return v_; -#endif - } - - /*! Conversion operator returning the memory address of the vector. - - Very convenient to pass a Vec pointer as a \c float parameter to OpenGL functions: - \code - Vec pos, normal; - glNormal3fv(normal); - glVertex3fv(pos); - \endcode - \note The returned float array is a static shared by all \c Vec instances. */ - operator const float*() const { - static float* const result = new float[3]; - result[0] = (float)x; - result[1] = (float)y; - result[2] = (float)z; - return result; - } - //@} - - /*! @name Algebraic computations */ - //@{ - /*! Returns the sum of the two vectors. */ - friend Vec operator+(const Vec &a, const Vec &b) - { - return Vec(a.x+b.x, a.y+b.y, a.z+b.z); - } - - /*! Returns the difference of the two vectors. */ - friend Vec operator-(const Vec &a, const Vec &b) - { - return Vec(a.x-b.x, a.y-b.y, a.z-b.z); - } - - /*! Unary minus operator. */ - friend Vec operator-(const Vec &a) - { - return Vec(-a.x, -a.y, -a.z); - } - - /*! Returns the product of the vector with a scalar. */ - friend Vec operator*(const Vec &a, qreal k) - { - return Vec(a.x*k, a.y*k, a.z*k); - } - - /*! Returns the product of a scalar with the vector. */ - friend Vec operator*(qreal k, const Vec &a) - { - return a*k; - } - - /*! Returns the division of the vector with a scalar. - - Too small \p k values are \e not tested (unless the library was compiled with the "debug" Qt \c - CONFIG flag) and may result in \c NaN values. */ - friend Vec operator/(const Vec &a, qreal k) - { -#ifndef QT_NO_DEBUG - if (fabs(k) < 1.0E-10) - qWarning("Vec::operator / : dividing by a null value (%f)", k); -#endif - return Vec(a.x/k, a.y/k, a.z/k); - } - - /*! Returns \c true only when the two vector are not equal (see operator==()). */ - friend bool operator!=(const Vec &a, const Vec &b) - { - return !(a==b); - } - - /*! Returns \c true when the squaredNorm() of the difference vector is lower than 1E-10. */ - friend bool operator==(const Vec &a, const Vec &b) - { - const qreal epsilon = 1.0E-10; - return (a-b).squaredNorm() < epsilon; - } - - /*! Adds \p a to the vector. */ - Vec& operator+=(const Vec &a) - { - x += a.x; y += a.y; z += a.z; - return *this; - } - - /*! Subtracts \p a to the vector. */ - Vec& operator-=(const Vec &a) - { - x -= a.x; y -= a.y; z -= a.z; - return *this; - } - - /*! Multiply the vector by a scalar \p k. */ - Vec& operator*=(qreal k) - { - x *= k; y *= k; z *= k; - return *this; - } - - /*! Divides the vector by a scalar \p k. - - An absolute \p k value lower than 1E-10 will print a warning if the library was compiled with the - "debug" Qt \c CONFIG flag. Otherwise, no test is performed for efficiency reasons. */ - Vec& operator/=(qreal k) - { -#ifndef QT_NO_DEBUG - if (fabs(k)<1.0E-10) - qWarning("Vec::operator /= : dividing by a null value (%f)", k); -#endif - x /= k; y /= k; z /= k; - return *this; - } - - /*! Dot product of the two Vec. */ - friend qreal operator*(const Vec &a, const Vec &b) - { - return a.x*b.x + a.y*b.y + a.z*b.z; - } - - /*! Cross product of the two vectors. Same as cross(). */ - friend Vec operator^(const Vec &a, const Vec &b) - { - return cross(a,b); - } - - /*! Cross product of the two Vec. Mind the order ! */ - friend Vec cross(const Vec &a, const Vec &b) - { - return Vec(a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x); - } - - Vec orthogonalVec() const; - //@} - - /*! @name Norm of the vector */ - //@{ -#ifndef DOXYGEN - /*! This method is deprecated since version 2.0. Use squaredNorm() instead. */ - qreal sqNorm() const { return x*x + y*y + z*z; } -#endif - - /*! Returns the \e squared norm of the Vec. */ - qreal squaredNorm() const { return x*x + y*y + z*z; } - - /*! Returns the norm of the vector. */ - qreal norm() const { return sqrt(x*x + y*y + z*z); } - - /*! Normalizes the Vec and returns its original norm. - - Normalizing a null vector will result in \c NaN values. */ - qreal normalize() - { - const qreal n = norm(); -#ifndef QT_NO_DEBUG - if (n < 1.0E-10) - qWarning("Vec::normalize: normalizing a null vector (norm=%f)", n); -#endif - *this /= n; - return n; - } - - /*! Returns a unitary (normalized) \e representation of the vector. The original Vec is not modified. */ - Vec unit() const - { - Vec v = *this; - v.normalize(); - return v; - } - //@} - - /*! @name Projection */ - //@{ - void projectOnAxis(const Vec& direction); - void projectOnPlane(const Vec& normal); - //@} - - /*! @name XML representation */ - //@{ - explicit Vec(const QDomElement& element); - QDomElement domElement(const QString& name, QDomDocument& document) const; - void initFromDOMElement(const QDomElement& element); - //@} - -#ifdef DOXYGEN - /*! @name Output stream */ - //@{ - /*! Output stream operator. Enables debugging code like: - \code - Vec pos(...); - cout << "Position=" << pos << endl; - \endcode */ - std::ostream& operator<<(std::ostream& o, const qglviewer::Vec&); - //@} -#endif -}; - -} // namespace - -QGLVIEWER_EXPORT std::ostream& operator<<(std::ostream& o, const qglviewer::Vec&); - -#endif // QGLVIEWER_VEC_H diff --git a/code/3rd_QGLViewer/CHANGELOG b/code/3rd_QGLViewer/CHANGELOG new file mode 100644 index 00000000..66aa28b4 --- /dev/null +++ b/code/3rd_QGLViewer/CHANGELOG @@ -0,0 +1,6 @@ +This is libQGLViewer version #VERSION#. Packaged on #DATE#. + +The complete change log is available in doc/changeLog.html + +The latest releases and changeLog are available at: +#WEBURL#/changeLog.html diff --git a/code/3rd_QGLViewer/CMakeLists.txt b/code/3rd_QGLViewer/CMakeLists.txt new file mode 100644 index 00000000..1e1c907f --- /dev/null +++ b/code/3rd_QGLViewer/CMakeLists.txt @@ -0,0 +1,42 @@ +if(POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif() + +project(libQGLViewer LANGUAGES CXX VERSION 2.9.1) +cmake_minimum_required(VERSION 3.16) + +include(../cmake/UseQt.cmake) + +find_package(OpenGL REQUIRED) + +# QGLViewer target. +set(QGLViewer_SRC + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/BackFaceCullingOptimizer.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/BSPSortMethod.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/EPSExporter.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/Exporter.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/FIGExporter.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/gpc.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/NVector3.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/ParserGL.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/Primitive.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/PrimitivePositioning.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/TopologicalSortMethod.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/Vector2.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/Vector3.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/VisibilityOptimizer.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/VRender/VRender.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/camera.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/constraint.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/frame.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/keyFrameInterpolator.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/manipulatedCameraFrame.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/manipulatedFrame.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/mouseGrabber.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/qglviewer.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/quaternion.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/saveSnapshot.cpp" + "${PROJECT_SOURCE_DIR}/QGLViewer/vec.cpp") +add_library(3rd_QGLViewer SHARED ${QGLViewer_SRC}) +target_include_directories(3rd_QGLViewer INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(3rd_QGLViewer PUBLIC ${QtLibs} OpenGL::GL OpenGL::GLU) \ No newline at end of file diff --git a/code/3rd_QGLViewer/LICENCE b/code/3rd_QGLViewer/LICENCE new file mode 100644 index 00000000..53747494 --- /dev/null +++ b/code/3rd_QGLViewer/LICENCE @@ -0,0 +1,1040 @@ + libQGLViewer is licensed under two licenses: the GNU General Public License (GPL) + and a commercial license. + + If you, your company or your organization use this library and do not wish to + release your complete source code, you are required to purchase a commercial license. + + Alternatively, if you are willing to follow the terms of the GPL (General Public License), + this Open source version of libQGLViewer allows you to develop, modify and distribute + your software freely. You must contribute all your source code to the open source + community, according to the terms of the applicable open source license. + + You may use, distribute and copy the QGLViewer library under the terms of + GNU General Public License version 2 or version 3, which are displayed below. + + In addition, as a special exception, Gilles Debunne gives you certain + additional rights, described in the file GPL_EXCEPTION in this package. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +------------------------------------------------------------------------- + + The QGLViewer library is Copyright (C) 2002-2023 Gilles Debunne. diff --git a/code/3rd_QGLViewer/QGLViewer/ImageInterface.ui b/code/3rd_QGLViewer/QGLViewer/ImageInterface.ui new file mode 100644 index 00000000..01691d38 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/ImageInterface.ui @@ -0,0 +1,297 @@ + + + ImageInterface + + + + 0 + 0 + 298 + 204 + + + + Image settings + + + + 6 + + + 9 + + + + + 6 + + + 0 + + + + + Width + + + + + + + Width of the image (in pixels) + + + px + + + 1 + + + 32000 + + + + + + + Qt::Horizontal + + + + 20 + 22 + + + + + + + + Height + + + + + + + Height of the image (in pixels) + + + px + + + 1 + + + 32000 + + + + + + + + + 6 + + + 0 + + + + + Image quality + + + + + + + Between 0 (smallest files) and 100 (highest quality) + + + 0 + + + 100 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 6 + + + 0 + + + + + Oversampling + + + + + + + Antialiases image (when larger then 1.0) + + + x + + + 1 + + + 0.100000000000000 + + + 64.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Use white as background color + + + Use white background + + + + + + + When image aspect ratio differs from viewer's one, expand frustum as needed. Fits inside current frustum otherwise. + + + Expand frustum if needed + + + + + + + Qt::Vertical + + + + 20 + 16 + + + + + + + + 6 + + + 0 + + + + + Qt::Horizontal + + + + 131 + 31 + + + + + + + + Cancel + + + + + + + OK + + + + + + + + + + + okButton + clicked() + ImageInterface + accept() + + + 135 + 184 + + + 96 + 254 + + + + + cancelButton + clicked() + ImageInterface + reject() + + + 226 + 184 + + + 179 + 282 + + + + + diff --git a/code/3rd_QGLViewer/QGLViewer/QGLViewer.pro b/code/3rd_QGLViewer/QGLViewer/QGLViewer.pro new file mode 100644 index 00000000..190d0e18 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/QGLViewer.pro @@ -0,0 +1,318 @@ +# l i b Q G L V i e w e r +# C o m p i l a t i o n c o n f i g u r a t i o n + +# Run "qmake; make; make install" to compile and install the library on Unix systems. +# Optional arguments can tune install paths (as in "qmake PREFIX=$HOME"). See doc/download.html for details. + +TEMPLATE = lib +TARGET = QGLViewer +VERSION = 2.9.1 +CONFIG *= qt opengl warn_on shared thread create_prl rtti no_keywords + +QGL_HEADERS = \ + qglviewer.h \ + camera.h \ + manipulatedFrame.h \ + manipulatedCameraFrame.h \ + frame.h \ + constraint.h \ + keyFrameInterpolator.h \ + mouseGrabber.h \ + quaternion.h \ + vec.h \ + domUtils.h \ + config.h + +SOURCES = \ + qglviewer.cpp \ + camera.cpp \ + manipulatedFrame.cpp \ + manipulatedCameraFrame.cpp \ + frame.cpp \ + saveSnapshot.cpp \ + constraint.cpp \ + keyFrameInterpolator.cpp \ + mouseGrabber.cpp \ + quaternion.cpp \ + vec.cpp + +HEADERS *= $${QGL_HEADERS} +DISTFILES *= qglviewer-icon.xpm +DESTDIR = $${PWD} + +TRANSLATIONS = qglviewer_fr.ts + +QT *= xml opengl + +equals (QT_MAJOR_VERSION, 5) { + QT *= gui widgets +} +equals (QT_MAJOR_VERSION, 6) { + QT *= gui widgets openglwidgets +} + +!isEmpty( QGLVIEWER_STATIC ) { + CONFIG *= staticlib +} + +# ----------------------------------- +# -- I m a g e I n t e r f a c e -- +# ----------------------------------- +FORMS *= ImageInterface.ui + +# --------------------------------------------- +# -- V e c t o r i a l R e n d e r i n g -- +# --------------------------------------------- +# In case of compilation troubles with vectorial rendering, uncomment this line +# DEFINES *= NO_VECTORIAL_RENDER + +contains( DEFINES, NO_VECTORIAL_RENDER ) { + message( Vectorial rendering disabled ) +} else { + FORMS *= VRenderInterface.ui + + SOURCES *= \ + VRender/BackFaceCullingOptimizer.cpp \ + VRender/BSPSortMethod.cpp \ + VRender/EPSExporter.cpp \ + VRender/Exporter.cpp \ + VRender/FIGExporter.cpp \ + VRender/gpc.cpp \ + VRender/ParserGL.cpp \ + VRender/Primitive.cpp \ + VRender/PrimitivePositioning.cpp \ + VRender/TopologicalSortMethod.cpp \ + VRender/VisibilityOptimizer.cpp \ + VRender/Vector2.cpp \ + VRender/Vector3.cpp \ + VRender/NVector3.cpp \ + VRender/VRender.cpp + + VRENDER_HEADERS = \ + VRender/AxisAlignedBox.h \ + VRender/Exporter.h \ + VRender/gpc.h \ + VRender/NVector3.h \ + VRender/Optimizer.h \ + VRender/ParserGL.h \ + VRender/Primitive.h \ + VRender/PrimitivePositioning.h \ + VRender/SortMethod.h \ + VRender/Types.h \ + VRender/Vector2.h \ + VRender/Vector3.h \ + VRender/VRender.h + + HEADERS *= $${VRENDER_HEADERS} +} + + +# --------------- +# -- U n i x -- +# --------------- +unix { + CONFIG -= debug debug_and_release + CONFIG *= release + + # INCLUDE_DIR and LIB_DIR specify where to install the include files and the library. + # Use qmake INCLUDE_DIR=... LIB_DIR=... , or qmake PREFIX=... to customize your installation. + isEmpty( PREFIX ) { + PREFIX_=/usr/local + } else { + PREFIX_=$${PREFIX} + } + isEmpty( LIB_DIR ) { + macx|darwin-g++ { + LIB_DIR_ = /Library/Frameworks + } else { + LIB_DIR_ = $${PREFIX_}/lib + } + } else { + LIB_DIR_ = $${LIB_DIR} + } + isEmpty( INCLUDE_DIR ) { + macx|darwin-g++ { + isEmpty( PREFIX ) { + INCLUDE_DIR_ = $${PWD}/Library/Developer/Headers + } else { + INCLUDE_DIR_ = $${PREFIX}/Headers + } + } else { + INCLUDE_DIR_ = $${PREFIX_}/include + } + } else { + INCLUDE_DIR_ = $${INCLUDE_DIR} + } + isEmpty( DOC_DIR ) { + macx|darwin-g++ { + isEmpty( PREFIX ) { + DOC_DIR = $${PWD}/Library/Developer/Shared/Documentation/QGLViewer + } else { + DOC_DIR = $${PREFIX}/Shared/Documentation/QGLViewer + } + } else { + DOC_DIR = $${PREFIX_}/share/doc/QGLViewer + } + } + + # GLUT for Unix architecture + !isEmpty( USE_GLUT ) { + QMAKE_LIBS_OPENGL *= -lglut + } + + macx|darwin-g++ { + # GLU is part of the OpenGL framework + } else { + QMAKE_LIBS_OPENGL *= -lGLU + + isEmpty( NO_QT_VERSION_SUFFIX ) { + equals (QT_MAJOR_VERSION, 4) { + TARGET = $$join(TARGET,,,-qt4) + } + equals (QT_MAJOR_VERSION, 5) { + TARGET = $$join(TARGET,,,-qt5) + } + equals (QT_MAJOR_VERSION, 6) { + TARGET = $$join(TARGET,,,-qt6) + } + } + } + + MOC_DIR = .moc + OBJECTS_DIR = .obj + + # Adds a -P option so that "make install" as root creates files owned by root and links are preserved. + # This is not a standard option, and it may have to be removed on old Unix flavors. + !hpux { + QMAKE_COPY_FILE = $${QMAKE_COPY_FILE} -P + } + + # Make much smaller libraries (and packages) by removing debugging informations + QMAKE_CFLAGS_RELEASE -= -g + QMAKE_CXXFLAGS_RELEASE -= -g + + # install header + include.path = $${INCLUDE_DIR_}/QGLViewer + # Should be $$replace(TRANSLATIONS, .ts, .qm), but 'replace' only appeared in Qt 4.3 + include.files = $${QGL_HEADERS} qglviewer_fr.qm + + # install documentation html + documentation.path = $${DOC_DIR} + documentation.files = ../doc/*.html ../doc/*.css ../doc/*.qch + + # install documentation images + docImages.path = $${DOC_DIR}/images + docImages.files = ../doc/images/* + + # install documentation examples + #docExamples.path = $${DOC_DIR}/examples + #docExamples.files = ../examples/*../examples/*/* + + # install documentation refManual + docRefManual.path = $${DOC_DIR}/refManual + docRefManual.files = ../doc/refManual/* + + # install static library + #staticlib.extra = make -f Makefile.Release staticlib + #staticlib.path = $${LIB_DIR_} + #staticlib.files = lib$${TARGET}.a + + # install library + target.path = $${LIB_DIR_} + + # "make install" configuration options + INSTALLS *= target include documentation docImages docRefManual + + # "make uninstall" for all targets + target.uninstall = @echo "uninstall" + include.uninstall = @echo "uninstall" + documentation.uninstall = @echo "uninstall" + docImages.uninstall = @echo "uninstall" + docRefManual.uninstall = @echo "uninstall" +} + + +# ------------------- +# -- M a c O S X -- +# ------------------- +macx|darwin-g++ { + # Default setting creates a Mac framework. Comment out this line to create a dylib instead. + !staticlib: CONFIG *= lib_bundle + + include.files *= qglviewer.icns + + # Or whatever exists in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ + #QMAKE_MAC_SDK = macosx10.15 + + lib_bundle { + FRAMEWORK_HEADERS.version = Versions + # Should be $$replace(TRANSLATIONS, .ts, .qm), but 'replace' is only available in Qt 4.3 + FRAMEWORK_HEADERS.files = $${QGL_HEADERS} qglviewer.icns qglviewer_fr.qm + FRAMEWORK_HEADERS.path = Headers + QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS + + # So that the path QGLViewer/*.h exists + QMAKE_POST_LINK=cd $$DESTDIR/QGLViewer.framework/Headers && (test -L QGLViewer || ln -s . QGLViewer) + + # Specific paths for the installation of the framework. + !isEmpty( LIB_DIR ) { + target.path = $${LIB_DIR} + } + + # Framework already contains includes + INSTALLS -= include + } + + # GLUT for Mac architecture + !isEmpty( USE_GLUT ) { + QMAKE_LIBS_OPENGL -= -lglut + QMAKE_LIBS_OPENGL *= -framework GLUT -lobjc + } +} + + +# --------------------- +# -- W i n d o w s -- +# --------------------- +win32 { + # Windows requires a debug lib version to link against debug applications + CONFIG *= debug_and_release build_all + + # Needed by Intel C++, (icl.exe) so that WINGDIAPI is a defined symbol in gl.h. + DEFINES *= WIN32 + + staticlib { + DEFINES *= QGLVIEWER_STATIC + } else { + DEFINES *= CREATE_QGLVIEWER_DLL + } + + CONFIG *= embed_manifest_dll + + # Use native OpenGL drivers with Qt5.5 + # No longer implicit since the ANGLE driver is now an alternative + LIBS += -lopengl32 -lglu32 + + # TP : C++ source code + # GR : Enables run-time type information (RTTI). + # Zi : Generates complete debugging information (removed) + # EHs : The exception-handling model that catches C++ exceptions only and tells the + # compiler to assume that functions declared as extern "C" may throw an exception. + # FS : Enable parallel compilation + # Any feedback on these flags is welcome. + !win32-g++ { + QMAKE_CXXFLAGS *= -TP -GR + DEFINES += NOMINMAX + win32-msvc { + QMAKE_CXXFLAGS *= -EH -FS + } else { + QMAKE_CXXFLAGS *= -EHs + } + } +} + + +build_pass:CONFIG(debug, debug|release) { + unix: TARGET = $$join(TARGET,,,_debug) + else: TARGET = $$join(TARGET,,,d) +} diff --git a/code/3rd_QGLViewer/QGLViewer/QGLViewer.vcproj b/code/3rd_QGLViewer/QGLViewer/QGLViewer.vcproj new file mode 100644 index 00000000..0f63cb12 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/QGLViewer.vcproj @@ -0,0 +1,493 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/AxisAlignedBox.h b/code/3rd_QGLViewer/QGLViewer/VRender/AxisAlignedBox.h new file mode 100644 index 00000000..456a2a3c --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/AxisAlignedBox.h @@ -0,0 +1,57 @@ +#ifndef _VRENDER_AXISALIGNEDBOX_H +#define _VRENDER_AXISALIGNEDBOX_H + +namespace vrender +{ + class Vector2; + class Vector3; + + template class AxisAlignedBox + { + public: + AxisAlignedBox() ; + AxisAlignedBox(const T& v) ; + AxisAlignedBox(const T& v,const T& w) ; + + const T& mini() const { return _min ; } + const T& maxi() const { return _max ; } + + void include(const T& v) ; + void include(const AxisAlignedBox& b) ; + private: + T _min ; + T _max ; + }; + + typedef AxisAlignedBox< Vector2 > AxisAlignedBox_xy ; + typedef AxisAlignedBox< Vector3 > AxisAlignedBox_xyz ; + + template AxisAlignedBox::AxisAlignedBox() + : _min(T::inf), _max(-T::inf) + { + } + + template AxisAlignedBox::AxisAlignedBox(const T& v) + : _min(v), _max(v) + { + } + + template AxisAlignedBox::AxisAlignedBox(const T& v,const T& w) + : _min(v), _max(v) + { + include(w) ; + } + + template void AxisAlignedBox::include(const T& v) + { + _min = T::mini(_min,v) ; + _max = T::maxi(_max,v) ; + } + + template void AxisAlignedBox::include(const AxisAlignedBox& b) + { + include(b._min) ; + include(b._max) ; + } +} +#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/BSPSortMethod.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/BSPSortMethod.cpp similarity index 75% rename from code/3rd_QGLViewer-2.6.3/VRender/BSPSortMethod.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/BSPSortMethod.cpp index 7d436815..e9c57884 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/BSPSortMethod.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/BSPSortMethod.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #include "VRender.h" #include "Primitive.h" #include "SortMethod.h" @@ -70,7 +26,7 @@ class BSPTree void recursFillPrimitiveArray(vector&) const; private: BSPNode *_root; - vector _segments; // these are for storing segments and points when _root is null + vector _segments; // these are for storing segments and points when _root is null vector _points; }; @@ -88,7 +44,7 @@ void BSPSortMethod::sortPrimitives(std::vector& primitive_tab,VRen // by the insertion and can not be dynamic_casted anymore. for(unsigned int i=0;i(primitive_tab[i])) != NULL) + if((P = dynamic_cast(primitive_tab[i])) != nullptr) tree.insert(P); else segments_and_points.push_back(primitive_tab[i]); @@ -104,9 +60,9 @@ void BSPSortMethod::sortPrimitives(std::vector& primitive_tab,VRen for(unsigned int j=0;j(segments_and_points[j])) != NULL) + if((S = dynamic_cast(segments_and_points[j])) != nullptr) tree.insert(S); - else if((p = dynamic_cast(segments_and_points[j])) != NULL) + else if((p = dynamic_cast(segments_and_points[j])) != nullptr) tree.insert(p); if(nbinserted%N==0) @@ -156,7 +112,7 @@ class BSPNode BSPTree::BSPTree() { - _root = NULL; + _root = nullptr; } BSPTree::~BSPTree() @@ -164,13 +120,13 @@ BSPTree::~BSPTree() delete _root; } -void BSPTree::insert(Point *P) { if(_root == NULL) _points.push_back(P) ; else _root->insert(P); } -void BSPTree::insert(Segment *S) { if(_root == NULL) _segments.push_back(S); else _root->insert(S); } -void BSPTree::insert(Polygone *P){ if(_root == NULL) _root = new BSPNode(P); else _root->insert(P); } +void BSPTree::insert(Point *P) { if(_root == nullptr) _points.push_back(P) ; else _root->insert(P); } +void BSPTree::insert(Segment *S) { if(_root == nullptr) _segments.push_back(S); else _root->insert(S); } +void BSPTree::insert(Polygone *P){ if(_root == nullptr) _root = new BSPNode(P); else _root->insert(P); } void BSPTree::recursFillPrimitiveArray(vector& tab) const { - if(_root != NULL) _root->recursFillPrimitiveArray(tab); + if(_root != nullptr) _root->recursFillPrimitiveArray(tab); for(unsigned int i=0;i<_points.size();++i) tab.push_back(_points[i]); for(unsigned int j=0;j<_segments.size();++j) tab.push_back(_segments[j]); @@ -220,7 +176,7 @@ void BSPNode::Classify(Segment *S, Segment * & moins_, Segment * & plus_) if(s1 == 0) { moins_ = S; - plus_ = NULL; + plus_ = nullptr; return; } else @@ -255,12 +211,12 @@ void BSPNode::Classify(Segment *S, Segment * & moins_, Segment * & plus_) if(s1 == -1) { moins_ = S; - plus_ = NULL; + plus_ = nullptr; return; } else { - moins_ = NULL; + moins_ = nullptr; plus_ = S; return; } @@ -269,14 +225,14 @@ void BSPNode::Classify(Segment *S, Segment * & moins_, Segment * & plus_) { if(s2 > 0) { - moins_ = NULL; + moins_ = nullptr; plus_ = S; return; } else { moins_ = S; - plus_ = NULL; + plus_ = nullptr; return; } } @@ -284,14 +240,14 @@ void BSPNode::Classify(Segment *S, Segment * & moins_, Segment * & plus_) { if(s1 > 0) { - moins_ = NULL; + moins_ = nullptr; plus_ = S; return; } else { moins_ = S; - plus_ = NULL; + plus_ = nullptr; return; } } @@ -304,12 +260,12 @@ void BSPNode::Classify(Polygone *P, Polygone * & moins_, Polygone * & plus_) static int Signs[100]; static double Zvals[100]; - moins_ = NULL; - plus_ = NULL; + moins_ = nullptr; + plus_ = nullptr; - if(P == NULL) + if(P == nullptr) { - //printf("BSPNode::Classify: Error. Null polygon.\n"); + //printf("BSPNode::Classify: Error. Null polygon.\n"); return; } @@ -342,7 +298,7 @@ void BSPNode::Classify(Polygone *P, Polygone * & moins_, Polygone * & plus_) if((Smin == 0)&&(Smax == 0)) { moins_ = P; - plus_ = NULL; + plus_ = nullptr; return; } @@ -351,7 +307,7 @@ void BSPNode::Classify(Polygone *P, Polygone * & moins_, Polygone * & plus_) if(Smin == 1) { plus_ = P; - moins_ = NULL; + moins_ = nullptr; return; } @@ -359,14 +315,14 @@ void BSPNode::Classify(Polygone *P, Polygone * & moins_, Polygone * & plus_) if(Smax == -1) { - plus_ = NULL; + plus_ = nullptr; moins_ = P; return; } if((Smin == -1)&&(Smax == 0)) { - plus_ = NULL; + plus_ = nullptr; moins_ = P; return; } @@ -374,7 +330,7 @@ void BSPNode::Classify(Polygone *P, Polygone * & moins_, Polygone * & plus_) if((Smin == 0)&&(Smax == 1)) { plus_ = P; - moins_ = NULL; + moins_ = nullptr; return; } @@ -404,7 +360,7 @@ void BSPNode::Classify(Polygone *P, Polygone * & moins_, Polygone * & plus_) // Ils y a des imprecisions numeriques dues au fait que le poly estpres du plan. moins_ = P; - plus_ = NULL; + plus_ = nullptr; return; } @@ -488,7 +444,7 @@ void BSPNode::Classify(Polygone *P, Polygone * & moins_, Polygone * & plus_) void BSPNode::insert(Polygone *P) { - Polygone *side_plus = NULL, *side_moins = NULL; + Polygone *side_plus = nullptr, *side_moins = nullptr; // 1 - Check on which size the polygon is, possibly split. @@ -496,15 +452,15 @@ void BSPNode::insert(Polygone *P) // 2 - insert polygons - if(side_plus != NULL) { - if(fils_plus == NULL) + if(side_plus != nullptr) { + if(fils_plus == nullptr) fils_plus = new BSPNode(side_plus); else fils_plus->insert(side_plus); } - if(side_moins != NULL) { - if(fils_moins == NULL) + if(side_moins != nullptr) { + if(fils_moins == nullptr) fils_moins = new BSPNode(side_moins); else fils_moins->insert(side_moins); @@ -513,7 +469,7 @@ void BSPNode::insert(Polygone *P) void BSPNode::recursFillPrimitiveArray(vector& primitive_tab) const { - if(fils_plus != NULL) + if(fils_plus != nullptr) fils_plus->recursFillPrimitiveArray(primitive_tab); for(unsigned int i=0;i& primitive_tab) cons for(unsigned int j=0;jrecursFillPrimitiveArray(primitive_tab); for(unsigned int i2=0;i2insert(P); } if(res == 1) { - if(fils_plus == NULL) + if(fils_plus == nullptr) pts_plus.push_back(P); else fils_plus->insert(P); @@ -554,19 +510,19 @@ void BSPNode::insert(Point *P) void BSPNode::insert(Segment *S) { - Segment *side_plus = NULL, *side_moins = NULL; + Segment *side_plus = nullptr, *side_moins = nullptr; Classify(S,side_moins,side_plus); - if(side_plus != NULL) { - if(fils_plus == NULL) + if(side_plus != nullptr) { + if(fils_plus == nullptr) seg_plus.push_back(side_plus); else fils_plus->insert(side_plus); } - if(side_moins != NULL) { - if(fils_moins == NULL) + if(side_moins != nullptr) { + if(fils_moins == nullptr) seg_moins.push_back(side_moins); else fils_moins->insert(side_moins); @@ -579,8 +535,8 @@ BSPNode::BSPNode(Polygone *P) initEquation(P,a,b,c,d); - fils_moins = NULL; - fils_plus = NULL; + fils_moins = nullptr; + fils_plus = nullptr; } void BSPNode::initEquation(const Polygone *P,double & a, double & b, double & c, double & d) diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/BackFaceCullingOptimizer.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/BackFaceCullingOptimizer.cpp new file mode 100644 index 00000000..b2f85b24 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/BackFaceCullingOptimizer.cpp @@ -0,0 +1,41 @@ +#include +#include "VRender.h" +#include "Optimizer.h" +#include "Primitive.h" + +using namespace std ; +using namespace vrender ; + +// Over-simplified algorithm to check wether a polygon is front-facing or not. +// Only works for convex polygons. + +void BackFaceCullingOptimizer::optimize(std::vector& primitives_tab,VRenderParams&) +{ + Polygone *P ; + int nb_culled = 0 ; + + for(size_t i=0;i(primitives_tab[i])) != nullptr) + { + for(unsigned int j=0;jnbVertices();++j) + if(( (P->vertex(j+2) - P->vertex(j+1))^(P->vertex(j+1) - P->vertex(j))).z() > 0.0 ) + { + delete primitives_tab[i] ; + primitives_tab[i] = nullptr ; + ++nb_culled ; + break ; + } + } + + // Rule out gaps. This avoids testing for null primitives later. + + int j=0 ; + for(size_t k=0;k #include "Primitive.h" #include "Exporter.h" @@ -90,7 +46,7 @@ void EPSExporter::writeHeader(QTextStream& out) const out << "/threshold " << EPS_GOURAUD_THRESHOLD << " def\n"; - for(int i = 0; GOURAUD_TRIANGLE_EPS[i] != NULL; i++) + for(int i = 0; GOURAUD_TRIANGLE_EPS[i] != nullptr; i++) out << GOURAUD_TRIANGLE_EPS[i] << "\n"; #ifdef A_VOIR out << "\n" << << " setlinewidth\n\n", _lineWidth; @@ -146,7 +102,7 @@ const char *EPSExporter::GOURAUD_TRIANGLE_EPS[] = "div 10 1 roll 7 index 5 index 3 index add add 3 div 10 1 roll 6 index 4 ", "index 2 index add add 3 div 10 1 roll 9 {pop} repeat 3 array astore ", "triangle } ifelse } bd", - NULL + nullptr }; void EPSExporter::spewPolygone(const Polygone *P, QTextStream& out) diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/Exporter.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/Exporter.cpp new file mode 100644 index 00000000..9c04f8fd --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Exporter.cpp @@ -0,0 +1,64 @@ +#include "VRender.h" +#include "Exporter.h" +#include "../qglviewer.h" + +#include +#include + +using namespace vrender ; +using namespace std ; + +Exporter::Exporter() +{ + _xmin=_xmax=_ymin=_ymax=_zmin=_zmax = 0.0 ; + _pointSize=1 ; +} + +void Exporter::exportToFile(const QString& filename, + const vector& primitive_tab, + VRenderParams& vparams) +{ + QFile file(filename); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::warning(nullptr, QGLViewer::tr("Exporter error", "Message box window title"), QGLViewer::tr("Unable to open file %1.").arg(filename)); + return; + } + + QTextStream out(&file); + + writeHeader(out) ; + + unsigned int N = primitive_tab.size()/200 + 1 ; + + for(unsigned int i=0;i(primitive_tab[i]) ; + Segment *s = dynamic_cast(primitive_tab[i]) ; + Polygone *P = dynamic_cast(primitive_tab[i]) ; + + if(p != nullptr) spewPoint(p,out) ; + if(s != nullptr) spewSegment(s,out) ; + if(P != nullptr) spewPolygone(P,out) ; + + if(i%N == 0) + vparams.progress(i/(float)primitive_tab.size(),QGLViewer::tr("Exporting to file %1").arg(filename)) ; + } + + writeFooter(out) ; + + file.close(); +} + +void Exporter::setBoundingBox(float xmin,float ymin,float xmax,float ymax) +{ + _xmin = xmin ; + _ymin = ymin ; + _xmax = xmax ; + _ymax = ymax ; +} + +void Exporter::setClearColor(float r, float g, float b) { _clearR=r; _clearG=g; _clearB=b; } +void Exporter::setClearBackground(bool b) { _clearBG=b; } +void Exporter::setBlackAndWhite(bool b) { _blackAndWhite = b; } + diff --git a/code/3rd_QGLViewer-2.6.3/VRender/Exporter.h b/code/3rd_QGLViewer/QGLViewer/VRender/Exporter.h similarity index 63% rename from code/3rd_QGLViewer-2.6.3/VRender/Exporter.h rename to code/3rd_QGLViewer/QGLViewer/VRender/Exporter.h index e01b7eea..352fa327 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/Exporter.h +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Exporter.h @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #ifndef _VRENDER_EXPORTER_H #define _VRENDER_EXPORTER_H diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/FIGExporter.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/FIGExporter.cpp new file mode 100644 index 00000000..4e1b5cb3 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/FIGExporter.cpp @@ -0,0 +1,125 @@ +#include "Exporter.h" +#include "math.h" + +using namespace vrender ; +using namespace std ; + +int FIGExporter::FigCoordX(double x) const +{ + float MaxX = 12000 ; + float MaxY = MaxX * _sizeY/(float)_sizeX ; + + if(MaxY > 7000) + { + MaxX *= 7000/(float)MaxY ; + MaxY = 7000 ; + } + + return int(0.5f + x/_sizeX*MaxX) ; +} + +int FIGExporter::FigCoordY(double y) const +{ + float MaxX = 12000 ; + float MaxY = MaxX * _sizeY/(float)_sizeX ; + + if(MaxY > 7000) + { + MaxX *= 7000/(float)MaxY ; + MaxY = 7000 ; + } + + return int(0.5f + (1.0 - y/_sizeY)*MaxY) ; +} + +int FIGExporter::FigGrayScaleIndex(float red, float green, float blue) const +{ + float intensity = 0.3f*red+0.6f*green+0.1f*blue ; + + return int(intensity * 20.0) ; +} + +FIGExporter::FIGExporter() +{ +} + +void FIGExporter::writeHeader(QTextStream& out) const +{ + out << "#FIG 3.2\nPortrait\nCenter\nInches\nLetter\n100.00\nSingle\n0\n1200 2\n"; + _depth = 999 ; + _sizeX = int(0.5f + _xmax - _xmin) ; + _sizeY = int(0.5f + _ymax - _ymin) ; +} + +void FIGExporter::writeFooter(QTextStream& out) const +{ + Q_UNUSED(out); +} + +void FIGExporter::spewPoint(const Point *P, QTextStream& out) +{ + out << "2 1 0 5 0 7 " << (_depth--) << " 0 -1 0.000 0 1 -1 0 0 1\n"; + + out << "\t " << FigCoordX(P->vertex(0)[0]) << " " << FigCoordY(P->vertex(0)[1]) << "\n"; + if(_depth > 0) _depth = 0 ; +} + +void FIGExporter::spewSegment(const Segment *S, QTextStream& out) +{ + const Feedback3DColor& P1 = Feedback3DColor(S->sommet3DColor(0)) ; + const Feedback3DColor& P2 = Feedback3DColor(S->sommet3DColor(1)) ; + + out << "2 1 0 1 0 7 " << (_depth--) << " 0 -1 0.000 0 0 -1 0 0 2\n"; + out << "\t " << FigCoordX(P1.x()) << " " << FigCoordY(P1.y()); + + out << " " << FigCoordX(P2.x()) << " " << FigCoordY(P2.y())<< "\n"; + if(_depth > 0) _depth = 0 ; +} + +void FIGExporter::spewPolygone(const Polygone *P, QTextStream& out) +{ + int nvertices; + GLfloat red, green, blue; + + nvertices = P->nbVertices() ; + + Feedback3DColor vertex(P->sommet3DColor(0)) ; + + if (nvertices > 0) + { + red = 0 ; + green = 0 ; + blue = 0 ; + + for(int i = 0; i < nvertices; i++) + { + red += P->sommet3DColor(i).red() ; + green += P->sommet3DColor(i).green() ; + blue += P->sommet3DColor(i).blue() ; + } + + red /= nvertices ; + green /= nvertices ; + blue /= nvertices ; + + /* Flat shaded polygon; all vertex colors the same. */ + + if(_blackAndWhite) + out << "2 3 0 0 0 7 " << (_depth--) << " 0 20 0.000 0 0 -1 0 0 " << (nvertices+1) << "\n"; + else + out << "2 3 0 0 0 7 " << (_depth--) << " 0 " << (FigGrayScaleIndex(red,green,blue)) << " 0.000 0 0 -1 0 0 " << (nvertices+1) << "\n"; + + /* Draw a filled triangle. */ + + out << "\t"; + + for (int j = 0; j < nvertices; j++) + out << " " << FigCoordX(P->sommet3DColor(j).x()) << " " << FigCoordY(P->sommet3DColor(j).y()); + + out << " " << FigCoordX(P->sommet3DColor(0).x()) << " " << FigCoordY(P->sommet3DColor(0).y()) << "\n"; + } + + if(_depth > 0) _depth = 0 ; +} + + diff --git a/code/3rd_QGLViewer-2.6.3/VRender/NVector3.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/NVector3.cpp similarity index 65% rename from code/3rd_QGLViewer-2.6.3/VRender/NVector3.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/NVector3.cpp index 80864a90..98613eee 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/NVector3.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/NVector3.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #include "NVector3.h" #include "Vector3.h" diff --git a/code/3rd_QGLViewer-2.6.3/VRender/NVector3.h b/code/3rd_QGLViewer/QGLViewer/VRender/NVector3.h similarity index 60% rename from code/3rd_QGLViewer-2.6.3/VRender/NVector3.h rename to code/3rd_QGLViewer/QGLViewer/VRender/NVector3.h index cac62e47..40b7f98b 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/NVector3.h +++ b/code/3rd_QGLViewer/QGLViewer/VRender/NVector3.h @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #ifndef _VRENDER_NVECTOR3_H #define _VRENDER_NVECTOR3_H diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/Optimizer.h b/code/3rd_QGLViewer/QGLViewer/VRender/Optimizer.h new file mode 100644 index 00000000..9a8b84d5 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Optimizer.h @@ -0,0 +1,47 @@ +#ifndef _OPTIMIZER_H +#define _OPTIMIZER_H + +#include "Types.h" + +namespace vrender +{ + // Implements some global optimizations on the polygon sorting. + + class VRenderParams ; + class Optimizer + { + public: + virtual void optimize(std::vector&,VRenderParams&) = 0 ; + virtual ~Optimizer() {} ; + }; + + // Optimizes visibility by culling primitives which do not appear in the + // rendered image. Computations are done analytically rather than using an item + // buffer. + + class VisibilityOptimizer: public Optimizer + { + public: + virtual void optimize(std::vector&,VRenderParams&) ; + virtual ~VisibilityOptimizer() {} ; + }; + + // Optimizes by collapsing together primitives which can be, without + // perturbating the back to front painting algorithm. + + class PrimitiveSplitOptimizer: public Optimizer + { + public: + virtual void optimize(std::vector&,VRenderParams&) {} + virtual ~PrimitiveSplitOptimizer() {} ; + }; + + class BackFaceCullingOptimizer: public Optimizer + { + public: + virtual void optimize(std::vector&,VRenderParams&) ; + virtual ~BackFaceCullingOptimizer() {} ; + }; +} + +#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/ParserGL.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/ParserGL.cpp similarity index 84% rename from code/3rd_QGLViewer-2.6.3/VRender/ParserGL.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/ParserGL.cpp index 4afe15db..e77fce10 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/ParserGL.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/ParserGL.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #include #include #include @@ -137,7 +93,7 @@ void ParserGL::parseFeedbackBuffer( GLfloat *buffer,int size, primitive_tab.push_back(ParserUtils::checkSegment(S)) ; - if(S == NULL) + if(S == nullptr) nb_degenerated_lines++ ; nb_lines++ ; @@ -159,7 +115,7 @@ void ParserGL::parseFeedbackBuffer( GLfloat *buffer,int size, primitive_tab.push_back(ParserUtils::checkPolygon(P)) ; - if(P == NULL) + if(P == nullptr) nb_degenerated_polys++ ; nb_polys++ ; @@ -172,7 +128,7 @@ void ParserGL::parseFeedbackBuffer( GLfloat *buffer,int size, primitive_tab.push_back(Pt);//ParserUtils::checkPoint(Pt)) ; - if(Pt == NULL) + if(Pt == nullptr) nb_degenerated_points++ ; nb_points++ ; @@ -200,7 +156,7 @@ PtrPrimitive ParserUtils::checkSegment(Segment *& P) { Point *pp = new Point(P->sommet3DColor(0)) ; delete P ; - P = NULL ; + P = nullptr ; return checkPoint(pp) ; } @@ -214,7 +170,7 @@ PtrPrimitive ParserUtils::checkPolygon(Polygone *& P) { cout << "unexpected case: Polygon with " << P->nbVertices() << " vertices !" << endl ; delete P ; - return NULL ; + return nullptr ; } if(P->FlatFactor() < FLAT_POLYGON_EPS) @@ -228,14 +184,14 @@ PtrPrimitive ParserUtils::checkPolygon(Polygone *& P) { Segment *pp = new Segment(P->sommet3DColor((i+1)%n),P->sommet3DColor((i+2)%n)) ; delete P ; - P = NULL ; + P = nullptr ; return checkSegment(pp) ; } Point *pp = new Point(P->sommet3DColor(0)) ; delete P ; - P = NULL ; + P = nullptr ; return checkPoint(pp) ; } diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/ParserGL.h b/code/3rd_QGLViewer/QGLViewer/VRender/ParserGL.h new file mode 100644 index 00000000..c158e5b5 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/ParserGL.h @@ -0,0 +1,44 @@ +#ifndef _VRENDER_PARSERGL_H +#define _VRENDER_PARSERGL_H + +// This class implements the conversion from OpenGL feedback buffer into more +// usable data structures such as points, segments, and polygons (See Primitive.h) + +#include +#include "Primitive.h" + +namespace vrender +{ + class ParserGL + { + public: + void parseFeedbackBuffer( GLfloat *, + int size, + std::vector& primitive_tab, + VRenderParams& vparams) ; + void printStats() const ; + + inline GLfloat xmin() const { return _xmin ; } + inline GLfloat ymin() const { return _ymin ; } + inline GLfloat zmin() const { return _zmin ; } + inline GLfloat xmax() const { return _xmax ; } + inline GLfloat ymax() const { return _ymax ; } + inline GLfloat zmax() const { return _zmax ; } + private: + int nb_lines ; + int nb_polys ; + int nb_points ; + int nb_degenerated_lines ; + int nb_degenerated_polys ; + int nb_degenerated_points ; + + GLfloat _xmin ; + GLfloat _ymin ; + GLfloat _zmin ; + GLfloat _xmax ; + GLfloat _ymax ; + GLfloat _zmax ; + }; +} + +#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/Primitive.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/Primitive.cpp similarity index 56% rename from code/3rd_QGLViewer-2.6.3/VRender/Primitive.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/Primitive.cpp index b7c27238..b24ab36e 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/Primitive.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Primitive.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #include #include #include "Primitive.h" diff --git a/code/3rd_QGLViewer-2.6.3/VRender/Primitive.h b/code/3rd_QGLViewer/QGLViewer/VRender/Primitive.h similarity index 72% rename from code/3rd_QGLViewer-2.6.3/VRender/Primitive.h rename to code/3rd_QGLViewer/QGLViewer/VRender/Primitive.h index 34d1f750..88ab11d0 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/Primitive.h +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Primitive.h @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #ifndef _PRIMITIVE_H_ #define _PRIMITIVE_H_ diff --git a/code/3rd_QGLViewer-2.6.3/VRender/PrimitivePositioning.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/PrimitivePositioning.cpp similarity index 81% rename from code/3rd_QGLViewer-2.6.3/VRender/PrimitivePositioning.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/PrimitivePositioning.cpp index e7084bc9..341cbe3b 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/PrimitivePositioning.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/PrimitivePositioning.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #include "Primitive.h" #include "AxisAlignedBox.h" #include "PrimitivePositioning.h" @@ -49,14 +5,7 @@ #include #include "Vector2.h" -#ifdef max -#undef max -#endif - -#ifdef min -#undef min -#endif - +#include using namespace vrender ; using namespace std ; @@ -406,8 +355,8 @@ gpc_polygon PrimitivePositioning::createGPCPolygon_XY(const Polygone *P) gpc_polygon p ; p.num_contours = 0 ; - p.hole = NULL ; - p.contour = NULL ; + p.hole = nullptr ; + p.contour = nullptr ; gpc_vertex_list *gpc_p_verts = new gpc_vertex_list ; @@ -428,7 +377,7 @@ gpc_polygon PrimitivePositioning::createGPCPolygon_XY(const Polygone *P) void PrimitivePositioning::getsigns(const Primitive *P,const NVector3& v,double C, vector& signs,vector& zvals,int& Smin,int& Smax,double I_EPS) { - if(P == NULL) + if(P == nullptr) throw runtime_error("Null primitive in getsigns !") ; size_t n = P->nbVertices() ; @@ -473,8 +422,8 @@ void PrimitivePositioning::split(Polygone *P,const NVector3& v,double C,Primitiv vector Signs ; vector Zvals ; - P_plus = NULL ; - P_moins = NULL ; + P_plus = nullptr ; + P_moins = nullptr ; int Smin = 1 ; int Smax = -1 ; @@ -483,12 +432,12 @@ void PrimitivePositioning::split(Polygone *P,const NVector3& v,double C,Primitiv size_t n = P->nbVertices() ; - if((Smin == 0)&&(Smax == 0)){ P_moins = P ; P_plus = NULL ; return ; } // Polygone inclus dans le plan - if(Smin == 1) { P_plus = P ; P_moins = NULL ; return ; } // Polygone tout positif - if(Smax == -1) { P_plus = NULL ; P_moins = P ; return ; } // Polygone tout negatif + if((Smin == 0)&&(Smax == 0)){ P_moins = P ; P_plus = nullptr ; return ; } // Polygone inclus dans le plan + if(Smin == 1) { P_plus = P ; P_moins = nullptr ; return ; } // Polygone tout positif + if(Smax == -1) { P_plus = nullptr ; P_moins = P ; return ; } // Polygone tout negatif - if((Smin == -1)&&(Smax == 0)) { P_plus = NULL ; P_moins = P ; return ; } // Polygone tout negatif ou null - if((Smin == 0)&&(Smax == 1)) { P_plus = P ; P_moins = NULL ; return ; } // Polygone tout positif ou null + if((Smin == -1)&&(Smax == 0)) { P_plus = nullptr ; P_moins = P ; return ; } // Polygone tout negatif ou null + if((Smin == 0)&&(Smax == 1)) { P_plus = P ; P_moins = nullptr ; return ; } // Polygone tout positif ou null // Reste le cas Smin = -1 et Smax = 1. Il faut couper @@ -512,7 +461,7 @@ void PrimitivePositioning::split(Polygone *P,const NVector3& v,double C,Primitiv } // Ils y a des imprecisions numeriques dues au fait que le poly estpres du plan. - if((nZero > 2)||(nconsZero > 0)) { P_moins = P ; P_plus = NULL ; return ; } + if((nZero > 2)||(nconsZero > 0)) { P_moins = P ; P_plus = nullptr ; return ; } int dep=0 ; while(Signs[dep] == 0) dep++ ; int prev_sign = Signs[dep] ; @@ -590,12 +539,12 @@ void PrimitivePositioning::split(Point *P,const NVector3& v,double C,Primitive * if(v*P->vertex(0)-C > -_EPS) { P_plus = P ; - P_moins = NULL ; + P_moins = nullptr ; } else { P_moins = P ; - P_plus = NULL ; + P_plus = nullptr ; } } @@ -604,8 +553,8 @@ void PrimitivePositioning::split(Segment *S,const NVector3& v,double C,Primitive vector Signs ; vector Zvals ; - P_plus = NULL ; - P_moins = NULL ; + P_plus = nullptr ; + P_moins = nullptr ; int Smin = 1 ; int Smax = -1 ; @@ -614,12 +563,12 @@ void PrimitivePositioning::split(Segment *S,const NVector3& v,double C,Primitive size_t n = S->nbVertices() ; - if((Smin == 0)&&(Smax == 0)) { P_moins = S ; P_plus = NULL ; return ; } // Polygone inclus dans le plan - if(Smin == 1) { P_plus = S ; P_moins = NULL ; return ; } // Polygone tout positif - if(Smax == -1) { P_plus = NULL ; P_moins = S ; return ; } // Polygone tout negatif + if((Smin == 0)&&(Smax == 0)) { P_moins = S ; P_plus = nullptr ; return ; } // Polygone inclus dans le plan + if(Smin == 1) { P_plus = S ; P_moins = nullptr ; return ; } // Polygone tout positif + if(Smax == -1) { P_plus = nullptr ; P_moins = S ; return ; } // Polygone tout negatif - if((Smin == -1)&&(Smax == 0)) { P_plus = NULL ; P_moins = S ; return ; } // Polygone tout negatif ou null - if((Smin == 0)&&(Smax == 1)) { P_plus = S ; P_moins = NULL ; return ; } // Polygone tout positif ou null + if((Smin == -1)&&(Smax == 0)) { P_plus = nullptr ; P_moins = S ; return ; } // Polygone tout negatif ou null + if((Smin == 0)&&(Smax == 1)) { P_plus = S ; P_moins = nullptr ; return ; } // Polygone tout positif ou null // Reste le cas Smin = -1 et Smax = 1. Il faut couper // On teste la coherence des signes. @@ -639,7 +588,7 @@ void PrimitivePositioning::split(Segment *S,const NVector3& v,double C,Primitive } // Ils y a des imprecisions numeriques dues au fait que le poly estpres du plan. - if((nZero > 2)||(nconsZero > 0)) { P_moins = S ; P_plus = NULL ; return ; } + if((nZero > 2)||(nconsZero > 0)) { P_moins = S ; P_plus = nullptr ; return ; } double Z1 = Zvals[0] ; double Z2 = Zvals[1] ; @@ -671,8 +620,8 @@ void PrimitivePositioning::split(Segment *S,const NVector3& v,double C,Primitive void PrimitivePositioning::splitPrimitive(Primitive *P,const NVector3& v,double c, Primitive *& prim_up,Primitive *& prim_lo) { - Polygone *p1 = dynamic_cast(P) ; if(p1 != NULL) PrimitivePositioning::split(p1,v,c,prim_up,prim_lo) ; - Segment *p2 = dynamic_cast(P) ; if(p2 != NULL) PrimitivePositioning::split(p2,v,c,prim_up,prim_lo) ; - Point *p3 = dynamic_cast(P) ; if(p3 != NULL) PrimitivePositioning::split(p3,v,c,prim_up,prim_lo) ; + Polygone *p1 = dynamic_cast(P) ; if(p1 != nullptr) PrimitivePositioning::split(p1,v,c,prim_up,prim_lo) ; + Segment *p2 = dynamic_cast(P) ; if(p2 != nullptr) PrimitivePositioning::split(p2,v,c,prim_up,prim_lo) ; + Point *p3 = dynamic_cast(P) ; if(p3 != nullptr) PrimitivePositioning::split(p3,v,c,prim_up,prim_lo) ; } diff --git a/code/3rd_QGLViewer-2.6.3/VRender/PrimitivePositioning.h b/code/3rd_QGLViewer/QGLViewer/VRender/PrimitivePositioning.h similarity index 53% rename from code/3rd_QGLViewer-2.6.3/VRender/PrimitivePositioning.h rename to code/3rd_QGLViewer/QGLViewer/VRender/PrimitivePositioning.h index 1d323a34..2ba2e75e 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/PrimitivePositioning.h +++ b/code/3rd_QGLViewer/QGLViewer/VRender/PrimitivePositioning.h @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #ifndef _PRIMITIVEPOSITIONING_H #define _PRIMITIVEPOSITIONING_H diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/SortMethod.h b/code/3rd_QGLViewer/QGLViewer/VRender/SortMethod.h new file mode 100644 index 00000000..e0066c93 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/SortMethod.h @@ -0,0 +1,58 @@ +#ifndef _SORTMETHOD_H +#define _SORTMETHOD_H + +#include +#include "Types.h" + +namespace vrender +{ + // Class which implements the sorting of the primitives. An object of + class VRenderParams ; + class SortMethod + { + public: + SortMethod() {} + virtual ~SortMethod() {} + + virtual void sortPrimitives(std::vector&,VRenderParams&) = 0 ; + + void SetZDepth(FLOAT s) { zSize = s ; } + FLOAT ZDepth() const { return zSize ; } + + protected: + FLOAT zSize ; + }; + + class DontSortMethod: public SortMethod + { + public: + DontSortMethod() {} + virtual ~DontSortMethod() {} + + virtual void sortPrimitives(std::vector&,VRenderParams&) {} + }; + + class BSPSortMethod: public SortMethod + { + public: + BSPSortMethod() {} ; + virtual ~BSPSortMethod() {} + + virtual void sortPrimitives(std::vector&,VRenderParams&) ; + }; + + class TopologicalSortMethod: public SortMethod + { + public: + TopologicalSortMethod() ; + virtual ~TopologicalSortMethod() {} + + virtual void sortPrimitives(std::vector&,VRenderParams&) ; + + void setBreakCycles(bool b) { _break_cycles = b ; } + private: + bool _break_cycles ; + }; +} + +#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/TopologicalSortMethod.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/TopologicalSortMethod.cpp similarity index 91% rename from code/3rd_QGLViewer-2.6.3/VRender/TopologicalSortMethod.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/TopologicalSortMethod.cpp index 420e8e0c..f5491c8e 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/TopologicalSortMethod.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/TopologicalSortMethod.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #include #include @@ -499,12 +455,12 @@ void TopologicalSortUtils::recursTopologicalSort( vector< vector >& prec bool prim_lower_prec_contains_ip1 = false ; bool prim_upper_prec_contains_ip1 = false ; - Primitive *prim_upper = NULL ; - Primitive *prim_lower = NULL ; + Primitive *prim_upper = nullptr ; + Primitive *prim_lower = nullptr ; PrimitivePositioning::splitPrimitive(primitive_tab[ancestors[i3]],normal,c,prim_upper,prim_lower) ; - if(prim_upper == NULL || prim_lower == NULL) + if(prim_upper == nullptr || prim_lower == nullptr) continue ; #ifdef DEBUG_TS cout << "Splitted primitive " << ancestors[i3] << endl ; diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/Types.h b/code/3rd_QGLViewer/QGLViewer/VRender/Types.h new file mode 100644 index 00000000..4b405ce1 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Types.h @@ -0,0 +1,30 @@ +#ifndef _VRENDER_TYPES_H +#define _VRENDER_TYPES_H + +#ifdef WIN32 +# include +#endif + +#ifdef __APPLE__ +# include +#else +# include +#endif + +namespace vrender +{ + typedef double FLOAT ; + typedef GLdouble GLFLOAT ; + +#ifdef A_VOIR + typedef T_Vect3 DVector3 ; + typedef T_Vect2 Vector2 ; +#endif + + class Primitive ; + typedef Primitive *PtrPrimitive ; + + const float FLAT_POLYGON_EPS = 1e-5f ; +} + +#endif diff --git a/code/3rd_QGLViewer-2.6.3/VRender/VRender.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/VRender.cpp similarity index 68% rename from code/3rd_QGLViewer-2.6.3/VRender/VRender.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/VRender.cpp index 60562fe1..3a9c3cd0 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/VRender.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/VRender.cpp @@ -1,52 +1,11 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. +#include - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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 WIN32 +#ifdef Q_OS_WIN32 # include #endif -#ifdef __APPLE__ +#ifdef Q_OS_MAC +# define GL_SILENCE_DEPRECATION # include #else # include @@ -68,9 +27,9 @@ using namespace std ; void vrender::VectorialRender(RenderCB render_callback, void *callback_params, VRenderParams& vparams) { - GLfloat *feedbackBuffer = NULL ; - SortMethod *sort_method = NULL ; - Exporter *exporter = NULL ; + GLfloat *feedbackBuffer = nullptr ; + SortMethod *sort_method = nullptr ; + Exporter *exporter = nullptr ; try { @@ -84,12 +43,12 @@ void vrender::VectorialRender(RenderCB render_callback, void *callback_params, V while(returned < 0) { - if(feedbackBuffer != NULL) + if(feedbackBuffer != nullptr) delete[] feedbackBuffer ; feedbackBuffer = new GLfloat[vparams.size()] ; - if(feedbackBuffer == NULL) + if(feedbackBuffer == nullptr) throw std::runtime_error("Out of memory during feedback buffer allocation.") ; glFeedbackBuffer(vparams.size(), GL_3D_COLOR, feedbackBuffer); @@ -127,10 +86,10 @@ void vrender::VectorialRender(RenderCB render_callback, void *callback_params, V ParserGL parserGL ; parserGL.parseFeedbackBuffer(feedbackBuffer,returned,primitive_tab,vparams) ; - if(feedbackBuffer != NULL) + if(feedbackBuffer != nullptr) { delete[] feedbackBuffer ; - feedbackBuffer = NULL ; + feedbackBuffer = nullptr ; } if(vparams.isEnabled(VRenderParams::OptimizeBackFaceCulling)) @@ -224,16 +183,16 @@ void vrender::VectorialRender(RenderCB render_callback, void *callback_params, V for(unsigned int i=0;i @@ -51,15 +7,6 @@ # include #endif -#ifdef max -#undef max -#endif - -#ifdef min -#undef min -#endif - - using namespace vrender; using namespace std; diff --git a/code/3rd_QGLViewer-2.6.3/VRender/Vector2.h b/code/3rd_QGLViewer/QGLViewer/VRender/Vector2.h similarity index 67% rename from code/3rd_QGLViewer-2.6.3/VRender/Vector2.h rename to code/3rd_QGLViewer/QGLViewer/VRender/Vector2.h index 9414768e..f6aaaf3e 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/Vector2.h +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Vector2.h @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #ifndef _VRENDER_VECTOR2_H #define _VRENDER_VECTOR2_H diff --git a/code/3rd_QGLViewer-2.6.3/VRender/Vector3.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/Vector3.cpp similarity index 62% rename from code/3rd_QGLViewer-2.6.3/VRender/Vector3.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/Vector3.cpp index 67be3919..4418f915 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/Vector3.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Vector3.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #include #include "Vector3.h" #include "NVector3.h" @@ -52,16 +8,6 @@ # include #endif - -#ifdef max -#undef max -#endif - -#ifdef min -#undef min -#endif - - using namespace vrender; using namespace std; diff --git a/code/3rd_QGLViewer-2.6.3/VRender/Vector3.h b/code/3rd_QGLViewer/QGLViewer/VRender/Vector3.h similarity index 70% rename from code/3rd_QGLViewer-2.6.3/VRender/Vector3.h rename to code/3rd_QGLViewer/QGLViewer/VRender/Vector3.h index f6a58535..32597e8c 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/Vector3.h +++ b/code/3rd_QGLViewer/QGLViewer/VRender/Vector3.h @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #ifndef _VRENDER_VECTOR3_H #define _VRENDER_VECTOR3_H diff --git a/code/3rd_QGLViewer-2.6.3/VRender/VisibilityOptimizer.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/VisibilityOptimizer.cpp similarity index 84% rename from code/3rd_QGLViewer-2.6.3/VRender/VisibilityOptimizer.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/VisibilityOptimizer.cpp index 62271b74..6f038976 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/VisibilityOptimizer.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/VisibilityOptimizer.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - #include #include "VRender.h" #include "Optimizer.h" @@ -97,12 +53,12 @@ void VisibilityOptimizer::optimize(vector& primitives,VRenderParam gpc_polygon cumulated_union ; cumulated_union.num_contours = 0 ; - cumulated_union.hole = NULL ; - cumulated_union.contour = NULL ; + cumulated_union.hole = nullptr ; + cumulated_union.contour = nullptr ; size_t nboptimised = 0 ; for(size_t pindex = primitives.size() - 1; long(pindex) >= 0;--pindex,++nboptimised) - if(primitives[pindex] != NULL) + if(primitives[pindex] != nullptr) { #ifdef A_FAIRE percentage_finished = pindex / (float)primitives.size() ; @@ -131,11 +87,11 @@ void VisibilityOptimizer::optimize(vector& primitives,VRenderParam gpc_polygon new_poly ; gpc_polygon new_poly_reduced ; new_poly.num_contours = 0 ; - new_poly.hole = NULL ; - new_poly.contour = NULL ; + new_poly.hole = nullptr ; + new_poly.contour = nullptr ; new_poly_reduced.num_contours = 0 ; - new_poly_reduced.hole = NULL ; - new_poly_reduced.contour = NULL ; + new_poly_reduced.hole = nullptr ; + new_poly_reduced.contour = nullptr ; // 1 - creates a gpc_polygon corresponding to the current primitive @@ -215,7 +171,7 @@ void VisibilityOptimizer::optimize(vector& primitives,VRenderParam { ++nb_culled ; delete p ; - primitives[pindex] = NULL ; + primitives[pindex] = nullptr ; continue ; } @@ -226,8 +182,8 @@ void VisibilityOptimizer::optimize(vector& primitives,VRenderParam { gpc_polygon cumulated_union_tmp ; cumulated_union_tmp.num_contours = 0 ; - cumulated_union_tmp.hole = NULL ; - cumulated_union_tmp.contour = NULL ; + cumulated_union_tmp.hole = nullptr ; + cumulated_union_tmp.contour = nullptr ; gpc_polygon_clip(GPC_UNION,&new_poly,&cumulated_union,&cumulated_union_tmp) ; diff --git a/code/3rd_QGLViewer/QGLViewer/VRender/copyright b/code/3rd_QGLViewer/QGLViewer/VRender/copyright new file mode 100644 index 00000000..42f8377d --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/VRender/copyright @@ -0,0 +1,22 @@ +/* + This file is part of the VRender library. + Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) + Version 1.0.0, released on June 27, 2005. + + http://artis.imag.fr/Members/Cyril.Soler/VRender + + VRender is free software; 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 2 of the License, or + (at your option) any later version. + + VRender is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VRender; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + diff --git a/code/3rd_QGLViewer-2.6.3/VRender/gpc.cpp b/code/3rd_QGLViewer/QGLViewer/VRender/gpc.cpp similarity index 92% rename from code/3rd_QGLViewer-2.6.3/VRender/gpc.cpp rename to code/3rd_QGLViewer/QGLViewer/VRender/gpc.cpp index 96ffa3ef..61d10bcb 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/gpc.cpp +++ b/code/3rd_QGLViewer/QGLViewer/VRender/gpc.cpp @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - /* =========================================================================== @@ -150,9 +106,9 @@ using namespace std ; #define MALLOC(p, b, s, t) {if ((b) > 0) { \ p= (t*)malloc(b); if (!(p)) { \ fprintf(stderr, "gpc malloc failure: %s\n", s); \ - exit(0);}} else p= NULL;} + exit(0);}} else p= nullptr;} -#define FREE(p) {if (p) {free(p); (p)= NULL;}} +#define FREE(p) {if (p) {free(p); (p)= nullptr;}} /* @@ -387,8 +343,8 @@ static edge_node **bound_list(lmt_node **lmt, double y) /* Add node onto the tail end of the LMT */ MALLOC(*lmt, sizeof(lmt_node), "LMT insertion", lmt_node); (*lmt)->y= y; - (*lmt)->first_bound= NULL; - (*lmt)->next= NULL; + (*lmt)->first_bound= nullptr; + (*lmt)->next= nullptr; return &((*lmt)->first_bound); } else @@ -398,7 +354,7 @@ static edge_node **bound_list(lmt_node **lmt, double y) existing_node= *lmt; MALLOC(*lmt, sizeof(lmt_node), "LMT insertion", lmt_node); (*lmt)->y= y; - (*lmt)->first_bound= NULL; + (*lmt)->first_bound= nullptr; (*lmt)->next= existing_node; return &((*lmt)->first_bound); } @@ -419,8 +375,8 @@ static void add_to_sbtree(int *entries, sb_tree **sbtree, double y) /* Add a new tree node here */ MALLOC(*sbtree, sizeof(sb_tree), "scanbeam tree insertion", sb_tree); (*sbtree)->y= y; - (*sbtree)->less= NULL; - (*sbtree)->more= NULL; + (*sbtree)->less= nullptr; + (*sbtree)->more= nullptr; (*entries)++; } else @@ -555,14 +511,14 @@ static edge_node *build_lmt(lmt_node **lmt, sb_tree **sbtree, e[i].dx= (edge_table[v].vertex.x - e[i].bot.x) / (e[i].top.y - e[i].bot.y); e[i].type= type; - e[i].outp[ABOVE]= NULL; - e[i].outp[BELOW]= NULL; - e[i].next= NULL; - e[i].prev= NULL; + e[i].outp[ABOVE]= nullptr; + e[i].outp[BELOW]= nullptr; + e[i].next= nullptr; + e[i].prev= nullptr; e[i].succ= ((num_edges > 1) && (i < (num_edges - 1))) ? - &(e[i + 1]) : NULL; - e[i].pred= ((num_edges > 1) && (i > 0)) ? &(e[i - 1]) : NULL; - e[i].next_bound= NULL; + &(e[i + 1]) : nullptr; + e[i].pred= ((num_edges > 1) && (i > 0)) ? &(e[i - 1]) : nullptr; + e[i].next_bound= nullptr; e[i].bside[CLIP]= (op == GPC_DIFF) ? RIGHT : LEFT; e[i].bside[SUBJ]= LEFT; } @@ -605,14 +561,14 @@ static edge_node *build_lmt(lmt_node **lmt, sb_tree **sbtree, e[i].dx= (edge_table[v].vertex.x - e[i].bot.x) / (e[i].top.y - e[i].bot.y); e[i].type= type; - e[i].outp[ABOVE]= NULL; - e[i].outp[BELOW]= NULL; - e[i].next= NULL; - e[i].prev= NULL; + e[i].outp[ABOVE]= nullptr; + e[i].outp[BELOW]= nullptr; + e[i].next= nullptr; + e[i].prev= nullptr; e[i].succ= ((num_edges > 1) && (i < (num_edges - 1))) ? - &(e[i + 1]) : NULL; - e[i].pred= ((num_edges > 1) && (i > 0)) ? &(e[i - 1]) : NULL; - e[i].next_bound= NULL; + &(e[i + 1]) : nullptr; + e[i].pred= ((num_edges > 1) && (i > 0)) ? &(e[i - 1]) : nullptr; + e[i].next_bound= nullptr; e[i].bside[CLIP]= (op == GPC_DIFF) ? RIGHT : LEFT; e[i].bside[SUBJ]= LEFT; } @@ -632,7 +588,7 @@ static void add_edge_to_aet(edge_node **aet, edge_node *edge, edge_node *prev) /* Append edge onto the tail end of the AET */ *aet= edge; edge->prev= prev; - edge->next= NULL; + edge->next= nullptr; } else { @@ -687,7 +643,7 @@ static void add_intersection(it_node **it, edge_node *edge0, edge_node *edge1, (*it)->ie[1]= edge1; (*it)->point.x= x; (*it)->point.y= y; - (*it)->next= NULL; + (*it)->next= nullptr; } else { @@ -723,7 +679,7 @@ static void add_st_edge(st_node **st, it_node **it, edge_node *edge, (*st)->xb= edge->xb; (*st)->xt= edge->xt; (*st)->dx= edge->dx; - (*st)->prev= NULL; + (*st)->prev= nullptr; } else { @@ -766,7 +722,7 @@ static void build_intersection_table(it_node **it, edge_node *aet, double dy) /* Build intersection table for the current scanbeam */ reset_it(it); - st= NULL; + st= nullptr; /* Process each AET edge */ for (edge= aet; edge; edge= edge->next) @@ -823,7 +779,7 @@ static void add_left(polygon_node *p, double x, double y) { vertex_node *nv; - if(p == NULL) throw runtime_error("GPC: Something's wrong.") ; + if(p == nullptr) throw runtime_error("GPC: Something's wrong.") ; /* Create a new vertex node and set its fields */ MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node); @@ -842,8 +798,8 @@ static void merge_left(polygon_node *p, polygon_node *q, polygon_node *list) { polygon_node *target; - if(p == NULL) throw runtime_error("GPC: Something's wrong.") ; - if(q == NULL) throw runtime_error("GPC: Something's wrong.") ; + if(p == nullptr) throw runtime_error("GPC: Something's wrong.") ; + if(q == nullptr) throw runtime_error("GPC: Something's wrong.") ; /* Label contour as a hole */ q->proxy->hole= TRUE; @@ -872,13 +828,13 @@ static void add_right(polygon_node *p, double x, double y) { vertex_node *nv = 0; - if(p == NULL) throw runtime_error("GPC: Something's wrong.") ; + if(p == nullptr) throw runtime_error("GPC: Something's wrong.") ; /* Create a new vertex node and set its fields */ MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node); nv->x= x; nv->y= y; - nv->next= NULL; + nv->next= nullptr; /* Add vertex nv to the right end of the polygon's vertex list */ p->proxy->v[RIGHT]->next= nv; @@ -892,8 +848,8 @@ static void merge_right(polygon_node *p, polygon_node *q, polygon_node *list) { polygon_node *target = 0; - if(p == NULL) throw runtime_error("GPC: Something's wrong.") ; - if(q == NULL) throw runtime_error("GPC: Something's wrong.") ; + if(p == nullptr) throw runtime_error("GPC: Something's wrong.") ; + if(q == nullptr) throw runtime_error("GPC: Something's wrong.") ; /* Label contour as external */ @@ -935,7 +891,7 @@ static void add_local_min(polygon_node **p, edge_node *edge, nv->x= x; nv->y= y; - nv->next= NULL; + nv->next= nullptr; /* Initialise proxy to point to p itself */ (*p)->proxy= (*p); @@ -969,7 +925,7 @@ static void add_vertex(vertex_node **t, double x, double y) MALLOC(*t, sizeof(vertex_node), "tristrip vertex creation", vertex_node); (*t)->x= x; (*t)->y= y; - (*t)->next= NULL; + (*t)->next= nullptr; } else /* Head further down the list */ @@ -985,9 +941,9 @@ static void new_tristrip(polygon_node **tn, edge_node *edge, MALLOC(*tn, sizeof(polygon_node), "tristrip node creation", polygon_node); **tn = polygon_node() ; - (*tn)->next= NULL; - (*tn)->v[LEFT]= NULL; - (*tn)->v[RIGHT]= NULL; + (*tn)->next= nullptr; + (*tn)->v[LEFT]= nullptr; + (*tn)->v[RIGHT]= nullptr; (*tn)->active= 1; add_vertex(&((*tn)->v[LEFT]), x, y); edge->outp[ABOVE]= *tn; @@ -1189,27 +1145,27 @@ void gpc_add_contour(gpc_polygon *p, gpc_vertex_list *new_contour, int hole) void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, gpc_polygon *result) { - sb_tree *sbtree= NULL; - it_node *it= NULL, *intersect=0; + sb_tree *sbtree= nullptr; + it_node *it= nullptr, *intersect=0; edge_node *edge=0, *prev_edge=0, *next_edge=0, *succ_edge=0, *e0=0, *e1=0; - edge_node *aet= NULL, *c_heap= NULL, *s_heap= NULL; - lmt_node *lmt= NULL, *local_min=0; - polygon_node *out_poly= NULL, *p=0, *q=0, *poly=0, *npoly=0, *cf= NULL; + edge_node *aet= nullptr, *c_heap= nullptr, *s_heap= nullptr; + lmt_node *lmt= nullptr, *local_min=0; + polygon_node *out_poly= nullptr, *p=0, *q=0, *poly=0, *npoly=0, *cf= nullptr; vertex_node *vtx=0, *nv=0; h_state horiz[2]; int in[2], exists[2], parity[2]= {LEFT, LEFT}; int c, v, contributing=0, search, scanbeam= 0, sbt_entries= 0; int vclass=0, bl=0, br=0, tl=0, tr=0; - double *sbt= NULL, xb, px, yb, yt=0.0, dy=0.0, ix, iy; + double *sbt= nullptr, xb, px, yb, yt=0.0, dy=0.0, ix, iy; - /* Test for trivial NULL result cases */ + /* Test for trivial nullptr result cases */ if (((subj->num_contours == 0) && (clip->num_contours == 0)) || ((subj->num_contours == 0) && ((op == GPC_INT) || (op == GPC_DIFF))) || ((clip->num_contours == 0) && (op == GPC_INT))) { result->num_contours= 0; - result->hole= NULL; - result->contour= NULL; + result->hole= nullptr; + result->contour= nullptr; return; } @@ -1224,12 +1180,12 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, if (clip->num_contours > 0) c_heap= build_lmt(&lmt, &sbtree, &sbt_entries, clip, CLIP, op); - /* Return a NULL result if no contours contribute */ - if (lmt == NULL) + /* Return a nullptr result if no contours contribute */ + if (lmt == nullptr) { result->num_contours= 0; - result->hole= NULL; - result->contour= NULL; + result->hole= nullptr; + result->contour= nullptr; reset_lmt(&lmt); FREE(s_heap); FREE(c_heap); @@ -1274,7 +1230,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { /* Add edges starting at this local minimum to the AET */ for (edge= local_min->first_bound; edge; edge= edge->next_bound) - add_edge_to_aet(&aet, edge, NULL); + add_edge_to_aet(&aet, edge, nullptr); local_min= local_min->next; } @@ -1415,7 +1371,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, px= xb; } edge->outp[ABOVE]= cf; - cf= NULL; + cf= nullptr; break; case ELI: add_left(edge->outp[BELOW], xb, yb); @@ -1429,7 +1385,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, px= xb; } merge_right(cf, edge->outp[BELOW], out_poly); - cf= NULL; + cf= nullptr; break; case ILI: if (xb != px) @@ -1438,13 +1394,13 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, px= xb; } edge->outp[ABOVE]= cf; - cf= NULL; + cf= nullptr; break; case IRI: add_right(edge->outp[BELOW], xb, yb); px= xb; cf= edge->outp[BELOW]; - edge->outp[BELOW]= NULL; + edge->outp[BELOW]= nullptr; break; case IMX: if (xb != px) @@ -1453,8 +1409,8 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, px= xb; } merge_left(cf, edge->outp[BELOW], out_poly); - cf= NULL; - edge->outp[BELOW]= NULL; + cf= nullptr; + edge->outp[BELOW]= nullptr; break; case IMM: if (xb != px) @@ -1463,7 +1419,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, px= xb; } merge_left(cf, edge->outp[BELOW], out_poly); - edge->outp[BELOW]= NULL; + edge->outp[BELOW]= nullptr; add_local_min(&out_poly, edge, xb, yb); cf= edge->outp[ABOVE]; break; @@ -1474,7 +1430,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, px= xb; } merge_right(cf, edge->outp[BELOW], out_poly); - edge->outp[BELOW]= NULL; + edge->outp[BELOW]= nullptr; add_local_min(&out_poly, edge, xb, yb); cf= edge->outp[ABOVE]; break; @@ -1612,7 +1568,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { add_right(p, ix, iy); e1->outp[ABOVE]= p; - e0->outp[ABOVE]= NULL; + e0->outp[ABOVE]= nullptr; } break; case ELI: @@ -1620,7 +1576,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { add_left(q, ix, iy); e0->outp[ABOVE]= q; - e1->outp[ABOVE]= NULL; + e1->outp[ABOVE]= nullptr; } break; case EMX: @@ -1628,8 +1584,8 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { add_left(p, ix, iy); merge_right(p, q, out_poly); - e0->outp[ABOVE]= NULL; - e1->outp[ABOVE]= NULL; + e0->outp[ABOVE]= nullptr; + e1->outp[ABOVE]= nullptr; } break; case IMN: @@ -1641,7 +1597,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { add_left(p, ix, iy); e1->outp[ABOVE]= p; - e0->outp[ABOVE]= NULL; + e0->outp[ABOVE]= nullptr; } break; case IRI: @@ -1649,7 +1605,7 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { add_right(q, ix, iy); e0->outp[ABOVE]= q; - e1->outp[ABOVE]= NULL; + e1->outp[ABOVE]= nullptr; } break; case IMX: @@ -1657,8 +1613,8 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { add_right(p, ix, iy); merge_left(p, q, out_poly); - e0->outp[ABOVE]= NULL; - e1->outp[ABOVE]= NULL; + e0->outp[ABOVE]= nullptr; + e1->outp[ABOVE]= nullptr; } break; case IMM: @@ -1727,8 +1683,8 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, e1->next= prev_edge->next; prev_edge->next= e0->next; } - if(e0->next == NULL) throw runtime_error("GPC internal error.") ; - if(e1->next == NULL) throw runtime_error("GPC internal error.") ; + if(e0->next == nullptr) throw runtime_error("GPC internal error.") ; + if(e1->next == nullptr) throw runtime_error("GPC internal error.") ; e0->next->prev= prev_edge; e1->next->prev= e1; e0->next= next_edge; @@ -1766,14 +1722,14 @@ void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ]; edge->xb= edge->xt; } - edge->outp[ABOVE]= NULL; + edge->outp[ABOVE]= nullptr; } } } /* === END OF SCANBEAM PROCESSING ================================== */ /* Generate result polygon from out_poly */ - result->contour= NULL; - result->hole= NULL; + result->contour= nullptr; + result->hole= nullptr; result->num_contours= count_contours(out_poly); if (result->num_contours > 0) { @@ -1840,8 +1796,8 @@ void gpc_polygon_to_tristrip(gpc_polygon *s, gpc_tristrip *t) gpc_polygon c; c.num_contours= 0; - c.hole= NULL; - c.contour= NULL; + c.hole= nullptr; + c.contour= nullptr; gpc_tristrip_clip(GPC_DIFF, s, &c, t); } @@ -1849,27 +1805,27 @@ void gpc_polygon_to_tristrip(gpc_polygon *s, gpc_tristrip *t) void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, gpc_tristrip *result) { - sb_tree *sbtree= NULL; - it_node *it= NULL, *intersect; + sb_tree *sbtree= nullptr; + it_node *it= nullptr, *intersect; edge_node *edge=0, *prev_edge=0, *next_edge=0, *succ_edge=0, *e0=0, *e1=0; - edge_node *aet= NULL, *c_heap= NULL, *s_heap= NULL, *cf=0; - lmt_node *lmt= NULL, *local_min; - polygon_node *tlist= NULL, *tn, *tnn, *p, *q; + edge_node *aet= nullptr, *c_heap= nullptr, *s_heap= nullptr, *cf=0; + lmt_node *lmt= nullptr, *local_min; + polygon_node *tlist= nullptr, *tn, *tnn, *p, *q; vertex_node *lt, *ltn, *rt, *rtn; h_state horiz[2]; vertex_type cft = NUL; int in[2], exists[2], parity[2]= {LEFT, LEFT}; int s, v, contributing=0, search, scanbeam= 0, sbt_entries= 0; int vclass=0, bl=0, br=0, tl=0, tr=0; - double *sbt= NULL, xb, px, nx, yb, yt=0.0, dy=0.0, ix, iy; + double *sbt= nullptr, xb, px, nx, yb, yt=0.0, dy=0.0, ix, iy; - /* Test for trivial NULL result cases */ + /* Test for trivial nullptr result cases */ if (((subj->num_contours == 0) && (clip->num_contours == 0)) || ((subj->num_contours == 0) && ((op == GPC_INT) || (op == GPC_DIFF))) || ((clip->num_contours == 0) && (op == GPC_INT))) { result->num_strips= 0; - result->strip= NULL; + result->strip= nullptr; return; } @@ -1884,11 +1840,11 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, if (clip->num_contours > 0) c_heap= build_lmt(&lmt, &sbtree, &sbt_entries, clip, CLIP, op); - /* Return a NULL result if no contours contribute */ - if (lmt == NULL) + /* Return a nullptr result if no contours contribute */ + if (lmt == nullptr) { result->num_strips= 0; - result->strip= NULL; + result->strip= nullptr; reset_lmt(&lmt); FREE(s_heap); FREE(c_heap); @@ -1927,7 +1883,7 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { /* Add edges starting at this local minimum to the AET */ for (edge= local_min->first_bound; edge; edge= edge->next_bound) - add_edge_to_aet(&aet, edge, NULL); + add_edge_to_aet(&aet, edge, nullptr); local_min= local_min->next; } @@ -2063,18 +2019,18 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, edge->outp[ABOVE]= cf->outp[ABOVE]; if (xb != cf->xb) VERTEX(edge, ABOVE, RIGHT, xb, yb); - cf= NULL; + cf= nullptr; break; case ELI: VERTEX(edge, BELOW, LEFT, xb, yb); - edge->outp[ABOVE]= NULL; + edge->outp[ABOVE]= nullptr; cf= edge; break; case EMX: if (xb != cf->xb) VERTEX(edge, BELOW, RIGHT, xb, yb); - edge->outp[ABOVE]= NULL; - cf= NULL; + edge->outp[ABOVE]= nullptr; + cf= nullptr; break; case IMN: if (cft == LED) @@ -2099,11 +2055,11 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, new_tristrip(&tlist, cf, cf->xb, yb); } VERTEX(edge, BELOW, RIGHT, xb, yb); - edge->outp[ABOVE]= NULL; + edge->outp[ABOVE]= nullptr; break; case IMX: VERTEX(edge, BELOW, LEFT, xb, yb); - edge->outp[ABOVE]= NULL; + edge->outp[ABOVE]= nullptr; cft= IMX; break; case IMM: @@ -2115,7 +2071,7 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, break; case EMM: VERTEX(edge, BELOW, RIGHT, xb, yb); - edge->outp[ABOVE]= NULL; + edge->outp[ABOVE]= nullptr; new_tristrip(&tlist, edge, xb, yb); cf= edge; break; @@ -2148,7 +2104,7 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, VERTEX(edge, BELOW, RIGHT, xb, yb); VERTEX(edge, ABOVE, RIGHT, xb, yb); } - cf= NULL; + cf= nullptr; break; default: break; @@ -2274,7 +2230,7 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, VERTEX(prev_edge, ABOVE, LEFT, px, iy); VERTEX(e0, ABOVE, RIGHT, ix, iy); e1->outp[ABOVE]= e0->outp[ABOVE]; - e0->outp[ABOVE]= NULL; + e0->outp[ABOVE]= nullptr; } break; case ELI: @@ -2284,15 +2240,15 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, VERTEX(e1, ABOVE, LEFT, ix, iy); VERTEX(next_edge, ABOVE, RIGHT, nx, iy); e0->outp[ABOVE]= e1->outp[ABOVE]; - e1->outp[ABOVE]= NULL; + e1->outp[ABOVE]= nullptr; } break; case EMX: if (p && q) { VERTEX(e0, ABOVE, LEFT, ix, iy); - e0->outp[ABOVE]= NULL; - e1->outp[ABOVE]= NULL; + e0->outp[ABOVE]= nullptr; + e1->outp[ABOVE]= nullptr; } break; case IMN: @@ -2314,7 +2270,7 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, N_EDGE(next_edge, e1, ABOVE, nx, iy); VERTEX(next_edge, ABOVE, RIGHT, nx, iy); e1->outp[ABOVE]= e0->outp[ABOVE]; - e0->outp[ABOVE]= NULL; + e0->outp[ABOVE]= nullptr; } break; case IRI: @@ -2324,7 +2280,7 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, P_EDGE(prev_edge, e0, ABOVE, px, iy); VERTEX(prev_edge, ABOVE, LEFT, px, iy); e0->outp[ABOVE]= e1->outp[ABOVE]; - e1->outp[ABOVE]= NULL; + e1->outp[ABOVE]= nullptr; } break; case IMX: @@ -2332,8 +2288,8 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, { VERTEX(e0, ABOVE, RIGHT, ix, iy); VERTEX(e1, ABOVE, LEFT, ix, iy); - e0->outp[ABOVE]= NULL; - e1->outp[ABOVE]= NULL; + e0->outp[ABOVE]= nullptr; + e1->outp[ABOVE]= nullptr; P_EDGE(prev_edge, e0, ABOVE, px, iy); VERTEX(prev_edge, ABOVE, LEFT, px, iy); new_tristrip(&tlist, prev_edge, px, iy); @@ -2453,13 +2409,13 @@ void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ]; edge->xb= edge->xt; } - edge->outp[ABOVE]= NULL; + edge->outp[ABOVE]= nullptr; } } } /* === END OF SCANBEAM PROCESSING ================================== */ /* Generate result tristrip from tlist */ - result->strip= NULL; + result->strip= nullptr; result->num_strips= count_tristrips(tlist); if (result->num_strips > 0) { diff --git a/code/3rd_QGLViewer-2.6.3/VRender/gpc.h b/code/3rd_QGLViewer/QGLViewer/VRender/gpc.h similarity index 71% rename from code/3rd_QGLViewer-2.6.3/VRender/gpc.h rename to code/3rd_QGLViewer/QGLViewer/VRender/gpc.h index 07841c65..31d421e5 100644 --- a/code/3rd_QGLViewer-2.6.3/VRender/gpc.h +++ b/code/3rd_QGLViewer/QGLViewer/VRender/gpc.h @@ -1,47 +1,3 @@ -/* - This file is part of the VRender library. - Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) - Version 1.0.0, released on June 27, 2005. - - http://artis.imag.fr/Members/Cyril.Soler/VRender - - VRender is free software; 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 2 of the License, or - (at your option) any later version. - - VRender is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VRender; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**************************************************************************** - - Copyright (C) 2002-2014 Gilles Debunne. All rights reserved. - - This file is part of the QGLViewer library version 2.6.3. - - 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. - -*****************************************************************************/ - /* =========================================================================== diff --git a/code/3rd_QGLViewer-2.6.3/VRenderInterface.ui b/code/3rd_QGLViewer/QGLViewer/VRenderInterface.ui similarity index 100% rename from code/3rd_QGLViewer-2.6.3/VRenderInterface.ui rename to code/3rd_QGLViewer/QGLViewer/VRenderInterface.ui diff --git a/code/3rd_QGLViewer/QGLViewer/camera.cpp b/code/3rd_QGLViewer/QGLViewer/camera.cpp new file mode 100644 index 00000000..bfe80354 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/camera.cpp @@ -0,0 +1,2244 @@ +#include "camera.h" +#include "domUtils.h" +#include "manipulatedCameraFrame.h" +#include "qglviewer.h" + +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_(nullptr), 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() + + // Default value + setDevicePixelRatio(1.0); + + // #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_(nullptr) { + // #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(nullptr); + 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; +} + +/*! Sets the screen's pixel ratio. + +See QScreen::devicePixelRatio() for a definition. +Automatically set by the associated QGLViewer. +*/ +void Camera::setDevicePixelRatio(qreal ratio) { + devicePixelRatio_ = ratio; +} + +/*! 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 nullptr \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; +} + +float Camera::pixelGLRatio(float x, float y, float z) const { + return pixelGLRatio(Vec(x, y, z)); +} + +/*! 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() * devicePixelRatio_, devicePixelRatio_ * (screenHeight() - pixel.y()) - 1, + 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); + found = static_cast(depth) < 1.0; + Vec point(pixel.x(), pixel.y(), static_cast(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(); +} + +/*! Returns the screen projected coordinates of a point \p src defined in the \p + frame coordinate system. + + When \p frame in \c nullptr (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); + gluProject(tmp.x, tmp.y, tmp.z, modelViewMatrix_, projectionMatrix_, + viewport, &x, &y, &z); + } else + gluProject(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 + nullptr (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); + gluUnProject(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 nullptr +pointer. */ +KeyFrameInterpolator *Camera::keyFrameInterpolator(unsigned int i) const { + if (kfi_.contains(i)) + return kfi_[i]; + else + return nullptr; +} + +/*! 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; +} diff --git a/code/3rd_QGLViewer/QGLViewer/camera.h b/code/3rd_QGLViewer/QGLViewer/camera.h new file mode 100644 index 00000000..0148532c --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/camera.h @@ -0,0 +1,550 @@ +#ifndef QGLVIEWER_CAMERA_H +#define QGLVIEWER_CAMERA_H + +#include "../basic/canvas.h" +#include +#include "keyFrameInterpolator.h" +class QGLViewer; + +namespace qglviewer { + +class ManipulatedCameraFrame; + +/*! \brief A perspective or orthographic camera. + \class Camera camera.h QGLViewer/camera.h + + A Camera defines some intrinsic parameters (fieldOfView(), position(), + viewDirection(), upVector()...) and useful positioning tools that ease its + placement (showEntireScene(), fitSphere(), lookAt()...). It exports its + associated OpenGL projection and modelview matrices and can interactively be + modified using the mouse. + +

Mouse manipulation

+ + The position() and orientation() of the Camera are defined by a + ManipulatedCameraFrame (retrieved using frame()). These methods are just + convenient wrappers to the equivalent Frame methods. This also means that the + Camera frame() can be attached to a Frame::referenceFrame() which enables + complex Camera setups. + + Different displacements can be performed using the mouse. The list of possible + actions is defined by the QGLViewer::MouseAction enum. Use + QGLViewer::setMouseBinding() to attach a specific action to an arbitrary mouse + button-state key binding. These actions are detailed in the mouse page. + + The default button binding are: QGLViewer::ROTATE (left), QGLViewer::ZOOM + (middle) and QGLViewer::TRANSLATE (right). With this configuration, the Camera + \e observes a scene and rotates around its pivotPoint(). You can switch + between this mode and a fly mode using the QGLViewer::CAMERA_MODE (see + QGLViewer::toggleCameraMode()) keyboard shortcut (default is 'Space'). + +

Other functionalities

+ + The type() of the Camera can be Camera::ORTHOGRAPHIC or Camera::PERSPECTIVE + (see Type()). fieldOfView() is meaningless with Camera::ORTHOGRAPHIC. + + The near and far planes of the Camera are fitted to the scene and determined + from QGLViewer::sceneRadius(), QGLViewer::sceneCenter() and + zClippingCoefficient() by the zNear() and zFar() methods. Reasonable values on + the scene extends hence have to be provided to the QGLViewer in order for the + Camera to correctly display the scene. High level positioning methods also use + this information (showEntireScene(), centerScene()...). + + A Camera holds KeyFrameInterpolator that can be used to save Camera positions + and paths. You can interactively addKeyFrameToPath() to a given path using the + default \c Alt+F[1-12] shortcuts. Use playPath() to make the Camera follow the + path (default shortcut is F[1-12]). See the keyboard page for details on key customization. + + Use cameraCoordinatesOf() and worldCoordinatesOf() to convert to and from the + Camera frame() coordinate system. projectedCoordinatesOf() and + unprojectedCoordinatesOf() will convert from screen to 3D coordinates. + convertClickToLine() is very useful for analytical object selection. + + Stereo display is possible on machines with quad buffer capabilities (with + Camera::PERSPECTIVE type() only). Test the stereoViewer example to check. + + A Camera can also be used outside of a QGLViewer or even without OpenGL for + its coordinate system conversion capabilities. Note however that some of them + explicitly rely on the presence of a Z-buffer. \nosubgrouping */ +class QGLVIEWER_EXPORT Camera : public QObject, public CameraBase{ +#ifndef DOXYGEN + friend class ::QGLViewer; +#endif + + Q_OBJECT + +public: + Camera(); + virtual ~Camera(); + + Camera(const Camera &camera); + Camera &operator=(const Camera &camera); + + /*! Enumerates the two possible types of Camera. + + See type() and setType(). This type mainly defines different Camera projection + matrix (see loadProjectionMatrix()). Many other methods (pointUnderPixel(), + convertClickToLine(), projectedCoordinatesOf(), pixelGLRatio()...) are + affected by this Type. */ + enum Type { PERSPECTIVE, ORTHOGRAPHIC }; + + /*! @name Position and orientation */ + //@{ +public: + Vec position() const; + Vec upVector() const; + Vec viewDirection() const; + Vec rightVector() const; + Quaternion orientation() const; + + void setFromModelViewMatrix(const GLdouble *const modelViewMatrix); + void setFromProjectionMatrix(const qreal matrix[12]); + +public Q_SLOTS: + void setPosition(const Vec &pos); + void setOrientation(const Quaternion &q); + void setOrientation(qreal theta, qreal phi); + void setUpVector(const Vec &up, bool noMove = true); + void setViewDirection(const Vec &direction); + //@} + + /*! @name Positioning tools */ + //@{ +public Q_SLOTS: + void lookAt(const Vec &target); + void showEntireScene(); + void fitSphere(const Vec ¢er, qreal radius); + void fitBoundingBox(const Vec &min, const Vec &max); + void fitScreenRegion(const QRect &rectangle); + void centerScene(); + void interpolateToZoomOnPixel(const QPoint &pixel); + void interpolateToFitScene(); + void interpolateTo(const Frame &fr, qreal duration); + //@} + + /*! @name Frustum */ + //@{ +public: + /*! Returns the Camera::Type of the Camera. + + Set by setType(). Mainly used by loadProjectionMatrix(). + + A Camera::PERSPECTIVE Camera uses a classical projection mainly defined by its + fieldOfView(). + + With a Camera::ORTHOGRAPHIC type(), the fieldOfView() is meaningless and the + width and height of the Camera frustum are inferred from the distance to the + pivotPoint() using getOrthoWidthHeight(). + + Both types use zNear() and zFar() (to define their clipping planes) and + aspectRatio() (for frustum shape). */ + Type type() const { return type_; } + + /*! Returns the vertical field of view of the Camera (in radians). + + Value is set using setFieldOfView(). Default value is pi/4 radians. This value + is meaningless if the Camera type() is Camera::ORTHOGRAPHIC. + + The field of view corresponds the one used in \c gluPerspective (see manual). + It sets the Y (vertical) aperture of the Camera. The X (horizontal) angle is + inferred from the window aspect ratio (see aspectRatio() and + horizontalFieldOfView()). + + Use setFOVToFitScene() to adapt the fieldOfView() to a given scene. */ + qreal fieldOfView() const { return fieldOfView_; } + + /*! Returns the horizontal field of view of the Camera (in radians). + + 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()); + } + + /*! Returns the Camera aspect ratio defined by screenWidth() / screenHeight(). + + When the Camera is attached to a QGLViewer, these values and hence the + aspectRatio() are automatically fitted to the viewer's window aspect ratio + using setScreenWidthAndHeight(). */ + qreal aspectRatio() const { + return screenWidth_ / static_cast(screenHeight_); + } + /*! Returns the width (in pixels) of the Camera screen. + + Set using setScreenWidthAndHeight(). This value is automatically fitted to the + QGLViewer's window dimensions when the Camera is attached to a QGLViewer. See + also QOpenGLWidget::width() */ + int screenWidth() const { return screenWidth_; } + /*! Returns the height (in pixels) of the Camera screen. + + Set using setScreenWidthAndHeight(). This value is automatically fitted to the + QGLViewer's window dimensions when the Camera is attached to a QGLViewer. See + also QOpenGLWidget::height() */ + int screenHeight() const { return screenHeight_; } + + /*! Returns the pixel ratio (physical to device independent pixel ratio) of the + Camera screen. + + Set using setDevicePixelRatio(). Defaults to 1.0. + + This value is automatically fitted to the QGLViewer's screen pixel ratio when the + Camera is attached to a QGLViewer. See also QWindow::devicePixelRatio() */ + qreal devicePixelRatio() const { return devicePixelRatio_; } + + void getViewport(GLint viewport[4]) const; + qreal pixelGLRatio(const Vec &position) const; + float pixelGLRatio(float x, float y, float z) const; + + /*! Returns the coefficient which is used to set zNear() when the Camera is + inside the sphere defined by sceneCenter() and zClippingCoefficient() * + sceneRadius(). + + In that case, the zNear() value is set to zNearCoefficient() * + zClippingCoefficient() * sceneRadius(). See the zNear() documentation for + details. + + Default value is 0.005, which is appropriate for most applications. In case + you need a high dynamic ZBuffer precision, you can increase this value (~0.1). + A lower value will prevent clipping of very close objects at the expense of a + worst Z precision. + + Only meaningful when Camera type is Camera::PERSPECTIVE. */ + qreal zNearCoefficient() const { return zNearCoef_; } + /*! Returns the coefficient used to position the near and far clipping planes. + + The near (resp. far) clipping plane is positioned at a distance equal to + zClippingCoefficient() * sceneRadius() in front of (resp. behind) the + sceneCenter(). This garantees an optimal use of the z-buffer range and + minimizes aliasing. See the zNear() and zFar() documentations. + + Default value is square root of 3.0 (so that a cube of size sceneRadius() is + not clipped). + + However, since the sceneRadius() is used for other purposes (see + showEntireScene(), flySpeed(), + ...) and you may want to change this value to define more precisely the + location of the clipping planes. See also zNearCoefficient(). + + For a total control on clipping planes' positions, an other option is to + overload the zNear() and zFar() methods. See the standardCamera example. + + \attention When QGLViewer::cameraPathAreEdited(), this value is set to 5.0 so + that the Camera paths are not clipped. The previous zClippingCoefficient() + value is restored back when you leave this mode. */ + qreal zClippingCoefficient() const { return zClippingCoef_; } + + virtual qreal zNear() const; + virtual qreal zFar() const; + virtual void getOrthoWidthHeight(GLdouble &halfWidth, + GLdouble &halfHeight) const; + void getFrustumPlanesCoefficients(GLdouble coef[6][4]) const; + +public Q_SLOTS: + void setType(Type type); + + void setFieldOfView(qreal fov); + + /*! Sets the horizontalFieldOfView() of the Camera (in radians). + + horizontalFieldOfView() and fieldOfView() are linked by the aspectRatio(). + 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 setFOVToFitScene(); + + /*! Defines the Camera aspectRatio(). + + This value is actually inferred from the screenWidth() / screenHeight() ratio. + You should use setScreenWidthAndHeight() instead. + + This method might however be convenient when the Camera is not associated with + a QGLViewer. It actually sets the screenHeight() to 100 and the screenWidth() + accordingly. See also setFOVToFitScene(). + + \note If you absolutely need an aspectRatio() that does not correspond to your + viewer's window dimensions, overload loadProjectionMatrix() or multiply the + created GL_PROJECTION matrix by a scaled diagonal matrix in your + QGLViewer::draw() method. */ + void setAspectRatio(qreal aspect) { + setScreenWidthAndHeight(int(100.0 * aspect), 100); + } + + void setScreenWidthAndHeight(int width, int height); + void setDevicePixelRatio(qreal ratio); + + /*! Sets the zNearCoefficient() value. */ + void setZNearCoefficient(qreal coef) { + zNearCoef_ = coef; + projectionMatrixIsUpToDate_ = false; + } + /*! Sets the zClippingCoefficient() value. */ + void setZClippingCoefficient(qreal coef) { + zClippingCoef_ = coef; + projectionMatrixIsUpToDate_ = false; + } + //@} + + /*! @name Scene radius and center */ + //@{ +public: + /*! Returns the radius of the scene observed by the Camera. + + You need to provide such an approximation of the scene dimensions so that the + Camera can adapt its zNear() and zFar() values. See the sceneCenter() + documentation. + + See also setSceneBoundingBox(). + + Note that QGLViewer::sceneRadius() (resp. QGLViewer::setSceneRadius()) simply + call this method (resp. setSceneRadius()) on its associated + QGLViewer::camera(). */ + qreal sceneRadius() const { return sceneRadius_; } + + /*! Returns the position of the scene center, defined in the world coordinate + system. + + The scene observed by the Camera should be roughly centered on this position, + and included in a sceneRadius() sphere. This approximate description of the + scene permits a zNear() and zFar() clipping planes definition, and allows + convenient positioning methods such as showEntireScene(). + + Default value is (0,0,0) (world origin). Use setSceneCenter() to change it. + See also setSceneBoundingBox(). + + Note that QGLViewer::sceneCenter() (resp. QGLViewer::setSceneCenter()) simply + calls this method (resp. setSceneCenter()) on its associated + QGLViewer::camera(). */ + Vec sceneCenter() const { return sceneCenter_; } + qreal distanceToSceneCenter() const; + +public Q_SLOTS: + void setSceneRadius(qreal radius); + void setSceneCenter(const Vec ¢er); + bool setSceneCenterFromPixel(const QPoint &pixel); + void setSceneBoundingBox(const Vec &min, const Vec &max); + //@} + + /*! @name Pivot Point */ + //@{ +public Q_SLOTS: + void setPivotPoint(const Vec &point); + bool setPivotPointFromPixel(const QPoint &pixel); + +public: + Vec pivotPoint() const; + +#ifndef DOXYGEN +public Q_SLOTS: + void setRevolveAroundPoint(const Vec &point); + bool setRevolveAroundPointFromPixel(const QPoint &pixel); + +public: + Vec revolveAroundPoint() const; +#endif + //@} + + /*! @name Associated frame */ + //@{ +public: + /*! Returns the ManipulatedCameraFrame attached to the Camera. + + This ManipulatedCameraFrame defines its position() and orientation() and can + translate mouse events into Camera displacement. Set using setFrame(). */ + ManipulatedCameraFrame *frame() const { return frame_; } +public Q_SLOTS: + void setFrame(ManipulatedCameraFrame *const mcf); + //@} + + /*! @name KeyFramed paths */ + //@{ +public: + KeyFrameInterpolator *keyFrameInterpolator(unsigned int i) const; + +public Q_SLOTS: + void setKeyFrameInterpolator(unsigned int i, KeyFrameInterpolator *const kfi); + + virtual void addKeyFrameToPath(unsigned int i); + virtual void playPath(unsigned int i); + virtual void deletePath(unsigned int i); + virtual void resetPath(unsigned int i); + virtual void drawAllPaths(); + //@} + + /*! @name OpenGL matrices */ + //@{ +public: + virtual void loadProjectionMatrix(bool reset = true) const; + virtual void loadModelViewMatrix(bool reset = true) const; + void computeProjectionMatrix() const; + void computeModelViewMatrix() const; + + virtual void loadProjectionMatrixStereo(bool leftBuffer = true) const; + virtual void loadModelViewMatrixStereo(bool leftBuffer = true) const; + + void getProjectionMatrix(GLfloat m[16]) const; + void getProjectionMatrix(GLdouble m[16]) const; + + void getModelViewMatrix(GLfloat m[16]) const; + void getModelViewMatrix(GLdouble m[16]) const; + + void getModelViewProjectionMatrix(GLfloat m[16]) const; + 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: + Vec cameraCoordinatesOf(const Vec &src) const; + Vec worldCoordinatesOf(const Vec &src) const; + void getCameraCoordinatesOf(const qreal src[3], qreal res[3]) const; + void getWorldCoordinatesOf(const qreal src[3], qreal res[3]) const; + //@} + + /*! @name 2D screen to 3D world coordinate systems conversions */ + //@{ +public: + Vec projectedCoordinatesOf(const Vec &src, const Frame *frame = nullptr) const; + Vec unprojectedCoordinatesOf(const Vec &src, const Frame *frame = nullptr) const; + void getProjectedCoordinatesOf(const qreal src[3], qreal res[3], + const Frame *frame = nullptr) const; + void getUnprojectedCoordinatesOf(const qreal src[3], qreal res[3], + const Frame *frame = nullptr) const; + void convertClickToLine(const QPoint &pixel, Vec &orig, Vec &dir) const; + Vec pointUnderPixel(const QPoint &pixel, bool &found) const; + //@} + + /*! @name Fly speed */ + //@{ +public: + qreal flySpeed() const; +public Q_SLOTS: + void setFlySpeed(qreal speed); + //@} + + /*! @name Stereo parameters */ + //@{ +public: + /*! Returns the user's inter-ocular distance (in meters). Default value is + 0.062m, which fits most people. + + loadProjectionMatrixStereo() uses this value to define the Camera offset and + frustum. See setIODistance(). */ + qreal IODistance() const { return IODistance_; } + + /*! Returns the physical distance between the user's eyes and the screen (in + meters). + + physicalDistanceToScreen() and focusDistance() represent the same distance. + The former is expressed in physical real world units, while the latter is + expressed in OpenGL virtual world units. + + 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); + } + + /*! Returns the physical screen width, in meters. Default value is 0.5m + (average monitor width). + + Used for stereo display only (see loadModelViewMatrixStereo() and + loadProjectionMatrixStereo()). Set using setPhysicalScreenWidth(). */ + qreal physicalScreenWidth() const { return physicalScreenWidth_; } + + /*! Returns the focus distance used by stereo display, expressed in OpenGL + units. + + This is the distance in the virtual world between the Camera and the plane + where the horizontal stereo parallax is null (the stereo left and right + cameras' lines of sigth cross at this distance). + + This distance is the virtual world equivalent of the real-world + physicalDistanceToScreen(). + + \attention This value is modified by QGLViewer::setSceneRadius(), + setSceneRadius() and setFieldOfView(). When one of these values is modified, + focusDistance() is set to sceneRadius() / tan(fieldOfView()/2), which provides + good results. */ + qreal focusDistance() const { return focusDistance_; } +public Q_SLOTS: + /*! Sets the IODistance(). */ + void setIODistance(qreal distance) { IODistance_ = distance; } + +#ifndef DOXYGEN + /*! This method is deprecated. Use setPhysicalScreenWidth() instead. */ + void setPhysicalDistanceToScreen(qreal distance) { + Q_UNUSED(distance); + qWarning("setPhysicalDistanceToScreen is deprecated, use " + "setPhysicalScreenWidth instead"); + } +#endif + + /*! Sets the physical screen (monitor or projected wall) width (in meters). */ + void setPhysicalScreenWidth(qreal width) { physicalScreenWidth_ = width; } + + /*! Sets the focusDistance(), in OpenGL scene units. */ + void setFocusDistance(qreal distance) { focusDistance_ = distance; } + //@} + + /*! @name XML representation */ + //@{ +public: + virtual QDomElement domElement(const QString &name, + QDomDocument &document) const; +public Q_SLOTS: + virtual void initFromDOMElement(const QDomElement &element); + //@} + +private Q_SLOTS: + void onFrameModified(); + +private: + // F r a m e + ManipulatedCameraFrame *frame_; + + // C a m e r a p a r a m e t e r s + int screenWidth_, screenHeight_; // size of the window, in pixels + qreal fieldOfView_; // in radians + Vec sceneCenter_; + qreal sceneRadius_; // OpenGL units + qreal zNearCoef_; + qreal zClippingCoef_; + qreal orthoCoef_; + qreal devicePixelRatio_; + Type type_; // PERSPECTIVE or ORTHOGRAPHIC + mutable GLdouble modelViewMatrix_[16]; // Buffered model view matrix. + mutable bool modelViewMatrixIsUpToDate_; + mutable GLdouble projectionMatrix_[16]; // Buffered projection matrix. + mutable bool projectionMatrixIsUpToDate_; + + // S t e r e o p a r a m e t e r s + qreal IODistance_; // inter-ocular distance, in meters + qreal focusDistance_; // in scene units + qreal physicalScreenWidth_; // in meters + + // P o i n t s o f V i e w s a n d K e y F r a m e s + QMap kfi_; + KeyFrameInterpolator *interpolationKfi_; +}; + +} // namespace qglviewer + +#endif // QGLVIEWER_CAMERA_H diff --git a/code/3rd_QGLViewer/QGLViewer/config.h b/code/3rd_QGLViewer/QGLViewer/config.h new file mode 100644 index 00000000..25f0e95e --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/config.h @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////// +// libQGLViewer configuration file // +// Modify these settings according to your local configuration // +/////////////////////////////////////////////////////////////////// + +#ifndef QGLVIEWER_CONFIG_H +#define QGLVIEWER_CONFIG_H + +#define QGLVIEWER_VERSION 0x020901 + +// Get QT_VERSION and other Qt flags +#include + +#if QT_VERSION < 0x050400 +Error : libQGLViewer requires a minimum Qt version of 5.4 Error + : Use a version prior to 2.7.0 to remove this constraint +#endif + +// Win 32 DLL export macros +#ifdef Q_OS_WIN32 +# ifndef M_PI +# define M_PI 3.14159265358979323846 +# endif // M_PI +# ifndef QGLVIEWER_STATIC +# ifdef CREATE_QGLVIEWER_DLL +# define QGLVIEWER_EXPORT Q_DECL_EXPORT +# else +# define QGLVIEWER_EXPORT Q_DECL_IMPORT +# endif +# endif // QGLVIEWER_STATIC + +# ifndef __MINGW32__ +# pragma warning(disable : 4251) // DLL interface, needed with Visual 6 +# pragma warning(disable : 4786) // identifier truncated to 255 in browser + // information (Visual 6). +# endif +#endif // Q_OS_WIN32 + +// For other architectures, this macro is empty +#ifndef QGLVIEWER_EXPORT +#define QGLVIEWER_EXPORT +#endif + +#ifdef Q_OS_MAC +# define GL_SILENCE_DEPRECATION +#endif + +// OpenGL includes - Included here and hence shared by all the files that need +// 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 +#include + +// For deprecated methods +// #define __WHERE__ "In file "<<__FILE__<<", line "<<__LINE__<<": " +// #define orientationAxisAngle(x,y,z,a) { std::cout << __WHERE__ << +// "getOrientationAxisAngle()." << std::endl; exit(0); } + +// Patch for gcc version <= 2.95. Seems to no longer be needed with recent Qt +// versions. Uncomment these lines if you have error message dealing with +// operator << on QStrings #if defined(__GNUC__) && defined(__GNUC_MINOR__) && +// (__GNUC__ < 3) && (__GNUC_MINOR__ < 96) # include # include +// std::ostream& operator<<(std::ostream& out, const QString& str) +// { out << str.latin1(); return out; } +// #endif + +#endif // QGLVIEWER_CONFIG_H diff --git a/code/3rd_QGLViewer/QGLViewer/constraint.cpp b/code/3rd_QGLViewer/QGLViewer/constraint.cpp new file mode 100644 index 00000000..d8635dea --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/constraint.cpp @@ -0,0 +1,258 @@ +#include "constraint.h" +#include "camera.h" +#include "frame.h" +#include "manipulatedCameraFrame.h" + +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; + } +} diff --git a/code/3rd_QGLViewer/QGLViewer/constraint.h b/code/3rd_QGLViewer/QGLViewer/constraint.h new file mode 100644 index 00000000..cdddc3da --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/constraint.h @@ -0,0 +1,345 @@ +#ifndef QGLVIEWER_CONSTRAINT_H +#define QGLVIEWER_CONSTRAINT_H + +#include "quaternion.h" +#include "vec.h" + +namespace qglviewer { +class Frame; +class Camera; + +/*! \brief An interface class for Frame constraints. + \class Constraint constraint.h QGLViewer/constraint.h + + This class defines the interface for the Constraints that can be applied to a + Frame to limit its motion. Use Frame::setConstraint() to associate a + Constraint to a Frame (default is a \c nullptr Frame::constraint()). + +

How does it work ?

+ + The Constraint acts as a filter on the translation and rotation Frame + increments. constrainTranslation() and constrainRotation() should be + overloaded to specify the constraint behavior: the desired displacement is + given as a parameter that can optionally be modified. + + Here is how the Frame::translate() and Frame::rotate() methods use the + Constraint: \code Frame::translate(Vec& T) + { + if (constraint()) + constraint()->constrainTranslation(T, this); + t += T; + } + + Frame::rotate(Quaternion& Q) + { + if (constraint()) + constraint()->constrainRotation(Q, this); + q *= Q; + } + \endcode + + The default behavior of constrainTranslation() and constrainRotation() is + empty (meaning no filtering). + + The Frame which uses the Constraint is passed as a parameter to the + constrainTranslation() and constrainRotation() methods, so that they can have + access to its current state (mainly Frame::position() and + Frame::orientation()). It is not \c const for versatility reasons, but + directly modifying it should be avoided. + + \attention Frame::setTranslation(), Frame::setRotation() and similar methods + will actually indeed set the frame position and orientation, without taking + the constraint into account. Use the \e WithConstraint versions of these + methods to enforce the Constraint. + +

Implemented Constraints

+ + Classical axial and plane Constraints are provided for convenience: see the + LocalConstraint, WorldConstraint and CameraConstraint classes' documentations. + + Try the constrainedFrame and + constrainedCamera examples + for an illustration. + +

Creating new Constraints

+ + The implementation of a new Constraint class simply consists in overloading + the filtering methods: \code + // This Constraint enforces that the Frame cannot have a negative z world + coordinate. class myConstraint : public Constraint + { + public: + virtual void constrainTranslation(Vec& t, Frame * const fr) + { + // Express t in the world coordinate system. + const Vec tWorld = fr->inverseTransformOf(t); + if (fr->position().z + tWorld.z < 0.0) // check the new fr z coordinate + t.z = fr->transformOf(-fr->position().z); // t.z is clamped so that + next z position is 0.0 + } + }; + \endcode + + Note that the translation (resp. rotation) parameter passed to + constrainTranslation() (resp. constrainRotation()) is expressed in the \e + local Frame coordinate system. Here, we use the Frame::transformOf() and + Frame::inverseTransformOf() method to convert it to and from the world + coordinate system. + + Combined constraints can easily be achieved by creating a new class that + applies the different constraint filters: \code + myConstraint::constrainTranslation(Vec& v, Frame* const fr) + { + constraint1->constrainTranslation(v, fr); + constraint2->constrainTranslation(v, fr); + // and so on, with possible branches, tests, loops... + } + \endcode + */ +class QGLVIEWER_EXPORT Constraint { +public: + /*! Virtual destructor. Empty. */ + virtual ~Constraint() {} + + /*! Filters the translation applied to the \p frame. This default + implementation is empty (no filtering). + + Overload this method in your own Constraint class to define a new translation + constraint. \p frame is the Frame to which is applied the translation. It is + not defined \c const, but you should refrain from directly changing its value + in the constraint. Use its Frame::position() and update the \p translation + accordingly instead. + + \p translation is expressed in local frame coordinate system. Use + Frame::inverseTransformOf() to express it in the world coordinate system if + needed. */ + virtual void constrainTranslation(Vec &translation, Frame *const frame) { + Q_UNUSED(translation); + Q_UNUSED(frame); + } + /*! Filters the rotation applied to the \p frame. This default implementation + is empty (no filtering). + + Overload this method in your own Constraint class to define a new rotation + constraint. See constrainTranslation() for details. + + Use Frame::inverseTransformOf() on the \p rotation Quaternion::axis() to + express \p rotation in the world coordinate system if needed. */ + virtual void constrainRotation(Quaternion &rotation, Frame *const frame) { + Q_UNUSED(rotation); + Q_UNUSED(frame); + } +}; + +/*! + \brief An abstract class for Frame Constraints defined by an axis or a plane. + \class AxisPlaneConstraint constraint.h QGLViewer/constraint.h + + AxisPlaneConstraint is an interface for (translation and/or rotation) + Constraint that are defined by a direction. translationConstraintType() and + rotationConstraintType() define how this direction should be interpreted: as + an axis (AxisPlaneConstraint::AXIS) or as a plane normal + (AxisPlaneConstraint::PLANE). See the Type() documentation for details. + + The three implementations of this class: LocalConstraint, WorldConstraint and + CameraConstraint differ by the coordinate system in which this direction is + expressed. + + Different implementations of this class are illustrated in the + contrainedCamera and + constrainedFrame examples. + + \attention When applied, the rotational Constraint may not intuitively follow + the mouse displacement. A solution would be to directly measure the rotation + angle in screen coordinates, but that would imply to know the + QGLViewer::camera(), so that we can compute the projected coordinates of the + rotation center (as is done with the QGLViewer::SCREEN_ROTATE binding). + However, adding an extra pointer to the QGLViewer::camera() in all the + AxisPlaneConstraint derived classes (which the user would have to update in a + multi-viewer application) was judged as an overkill. */ +class QGLVIEWER_EXPORT AxisPlaneConstraint : public Constraint { +public: + AxisPlaneConstraint(); + /*! Virtual destructor. Empty. */ + virtual ~AxisPlaneConstraint() {} + + /*! Type lists the different types of translation and rotation constraints + that are available. + + It specifies the meaning of the constraint direction (see + translationConstraintDirection() and rotationConstraintDirection()): as an + axis direction (AxisPlaneConstraint::AXIS) or a plane normal + (AxisPlaneConstraint::PLANE). AxisPlaneConstraint::FREE means no constraint + while AxisPlaneConstraint::FORBIDDEN completely forbids the translation and/or + the rotation. + + See translationConstraintType() and rotationConstraintType(). + + \attention The AxisPlaneConstraint::PLANE Type is not valid for rotational + constraint. + + New derived classes can use their own extended enum for specific constraints: + \code + class MyAxisPlaneConstraint : public AxisPlaneConstraint + { + public: + enum MyType { FREE, AXIS, PLANE, FORBIDDEN, CUSTOM }; + virtual void constrainTranslation(Vec &translation, Frame *const frame) + { + // translationConstraintType() is simply an int. CUSTOM Type is + handled seamlessly. switch (translationConstraintType()) + { + case MyAxisPlaneConstraint::FREE: ... break; + case MyAxisPlaneConstraint::CUSTOM: ... break; + } + }; + + MyAxisPlaneConstraint* c = new MyAxisPlaneConstraint(); + // Note the Type conversion + c->setTranslationConstraintType(AxisPlaneConstraint::Type(MyAxisPlaneConstraint::CUSTOM)); + }; + \endcode */ + enum Type { FREE, AXIS, PLANE, FORBIDDEN }; + + /*! @name Translation constraint */ + //@{ + /*! Overloading of Constraint::constrainTranslation(). Empty */ + virtual void constrainTranslation(Vec &translation, Frame *const frame) { + Q_UNUSED(translation); + Q_UNUSED(frame); + }; + + void setTranslationConstraint(Type type, const Vec &direction); + /*! Sets the Type() of the translationConstraintType(). Default is + * AxisPlaneConstraint::FREE. */ + void setTranslationConstraintType(Type type) { + translationConstraintType_ = type; + }; + void setTranslationConstraintDirection(const Vec &direction); + + /*! Returns the translation constraint Type(). + + Depending on this value, the Frame will freely translate + (AxisPlaneConstraint::FREE), will only be able to translate along an axis + direction (AxisPlaneConstraint::AXIS), will be forced to stay into a plane + (AxisPlaneConstraint::PLANE) or will not able to translate at all + (AxisPlaneConstraint::FORBIDDEN). + + Use Frame::setPosition() to define the position of the constrained Frame + before it gets constrained. */ + Type translationConstraintType() const { return translationConstraintType_; }; + /*! Returns the direction used by the translation constraint. + + It represents the axis direction (AxisPlaneConstraint::AXIS) or the plane + normal (AxisPlaneConstraint::PLANE) depending on the + translationConstraintType(). It is undefined for AxisPlaneConstraint::FREE or + AxisPlaneConstraint::FORBIDDEN. + + The AxisPlaneConstraint derived classes express this direction in different + coordinate system (camera for CameraConstraint, local for LocalConstraint, and + world for WorldConstraint). This value can be modified with + setTranslationConstraintDirection(). */ + Vec translationConstraintDirection() const { + return translationConstraintDir_; + }; + //@} + + /*! @name Rotation constraint */ + //@{ + /*! Overloading of Constraint::constrainRotation(). Empty. */ + virtual void constrainRotation(Quaternion &rotation, Frame *const frame) { + Q_UNUSED(rotation); + Q_UNUSED(frame); + }; + + void setRotationConstraint(Type type, const Vec &direction); + void setRotationConstraintType(Type type); + void setRotationConstraintDirection(const Vec &direction); + + /*! Returns the rotation constraint Type(). */ + Type rotationConstraintType() const { return rotationConstraintType_; }; + /*! Returns the axis direction used by the rotation constraint. + + This direction is defined only when rotationConstraintType() is + AxisPlaneConstraint::AXIS. + + The AxisPlaneConstraint derived classes express this direction in different + coordinate system (camera for CameraConstraint, local for LocalConstraint, and + world for WorldConstraint). This value can be modified with + setRotationConstraintDirection(). */ + Vec rotationConstraintDirection() const { return rotationConstraintDir_; }; + //@} + +private: + // int and not Type to allow for overloading and new types definition. + Type translationConstraintType_; + Type rotationConstraintType_; + + Vec translationConstraintDir_; + Vec rotationConstraintDir_; +}; + +/*! \brief An AxisPlaneConstraint defined in the Frame local coordinate system. + \class LocalConstraint constraint.h QGLViewer/constraint.h + + The translationConstraintDirection() and rotationConstraintDirection() are + expressed in the Frame local coordinate system (see Frame::referenceFrame()). + + See the constrainedFrame + example for an illustration. */ +class QGLVIEWER_EXPORT LocalConstraint : public AxisPlaneConstraint { +public: + /*! Virtual destructor. Empty. */ + virtual ~LocalConstraint(){}; + + virtual void constrainTranslation(Vec &translation, Frame *const frame); + virtual void constrainRotation(Quaternion &rotation, Frame *const frame); +}; + +/*! \brief An AxisPlaneConstraint defined in the world coordinate system. + \class WorldConstraint constraint.h QGLViewer/constraint.h + + The translationConstraintDirection() and rotationConstraintDirection() are + expressed in world coordinate system. + + See the constrainedFrame and + multiView examples for an + illustration. */ +class QGLVIEWER_EXPORT WorldConstraint : public AxisPlaneConstraint { +public: + /*! Virtual destructor. Empty. */ + virtual ~WorldConstraint(){}; + + virtual void constrainTranslation(Vec &translation, Frame *const frame); + virtual void constrainRotation(Quaternion &rotation, Frame *const frame); +}; + +/*! \brief An AxisPlaneConstraint defined in the camera coordinate system. + \class CameraConstraint constraint.h QGLViewer/constraint.h + + The translationConstraintDirection() and rotationConstraintDirection() are + expressed in the associated camera() coordinate system. + + See the constrainedFrame and + constrainedCamera examples + for an illustration. */ +class QGLVIEWER_EXPORT CameraConstraint : public AxisPlaneConstraint { +public: + explicit CameraConstraint(const Camera *const camera); + /*! Virtual destructor. Empty. */ + 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_; }; + +private: + const Camera *const camera_; +}; + +} // namespace qglviewer + +#endif // QGLVIEWER_CONSTRAINT_H diff --git a/code/3rd_QGLViewer/QGLViewer/domUtils.h b/code/3rd_QGLViewer/QGLViewer/domUtils.h new file mode 100644 index 00000000..5cd360c4 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/domUtils.h @@ -0,0 +1,170 @@ +#include "config.h" + +#include +#include +#include +#include + +#include + +#ifndef DOXYGEN + +// QDomElement loading with syntax checking. +class DomUtils { +private: + static void warning(const QString &message) { + qWarning("%s", message.toLatin1().constData()); + } + +public: + static qreal qrealFromDom(const QDomElement &e, const QString &attribute, + qreal defValue) { + qreal value = defValue; + if (e.hasAttribute(attribute)) { + const QString s = e.attribute(attribute); + bool ok; + value = s.toDouble(&ok); + if (!ok) { + warning(QString("'%1' is not a valid qreal syntax for attribute \"%2\" " + "in initialization of \"%3\". Setting value to %4.") + .arg(s) + .arg(attribute) + .arg(e.tagName()) + .arg(QString::number(defValue))); + value = defValue; + } + } else { + warning(QString("\"%1\" attribute missing in initialization of \"%2\". " + "Setting value to %3.") + .arg(attribute) + .arg(e.tagName()) + .arg(QString::number(value))); + } + +#if defined(isnan) + // The "isnan" method may not be available on all platforms. + // Find its equivalent or simply remove these two lines + if (isnan(value)) + warning( + QString( + "Warning, attribute \"%1\" initialized to Not a Number in \"%2\"") + .arg(attribute) + .arg(e.tagName())); +#endif + + return value; + } + + static int intFromDom(const QDomElement &e, const QString &attribute, + int defValue) { + int value = defValue; + if (e.hasAttribute(attribute)) { + const QString s = e.attribute(attribute); + bool ok; + value = s.toInt(&ok); + if (!ok) { + warning( + QString("'%1' is not a valid integer syntax for attribute \"%2\" " + "in initialization of \"%3\". Setting value to %4.") + .arg(s) + .arg(attribute) + .arg(e.tagName()) + .arg(QString::number(defValue))); + value = defValue; + } + } else { + warning(QString("\"%1\" attribute missing in initialization of \"%2\". " + "Setting value to %3.") + .arg(attribute) + .arg(e.tagName()) + .arg(QString::number(value))); + } + + return value; + } + + static unsigned int uintFromDom(const QDomElement &e, + const QString &attribute, + unsigned int defValue) { + unsigned int value = defValue; + if (e.hasAttribute(attribute)) { + const QString s = e.attribute(attribute); + bool ok; + value = s.toUInt(&ok); + if (!ok) { + warning( + QString("'%1' is not a valid unsigned integer syntax for attribute " + "\"%2\" in initialization of \"%3\". Setting value to %4.") + .arg(s) + .arg(attribute) + .arg(e.tagName()) + .arg(QString::number(defValue))); + value = defValue; + } + } else { + warning(QString("\"%1\" attribute missing in initialization of \"%2\". " + "Setting value to %3.") + .arg(attribute) + .arg(e.tagName()) + .arg(QString::number(value))); + } + + return value; + } + + static bool boolFromDom(const QDomElement &e, const QString &attribute, + bool defValue) { + bool value = defValue; + if (e.hasAttribute(attribute)) { + const QString s = e.attribute(attribute); + if (s.toLower() == QString("true")) + value = true; + else if (s.toLower() == QString("false")) + value = false; + else { + warning( + QString("'%1' is not a valid boolean syntax for attribute \"%2\" " + "in initialization of \"%3\". Setting value to %4.") + .arg(s) + .arg(attribute) + .arg(e.tagName()) + .arg(defValue ? "true" : "false")); + } + } else { + warning(QString("\"%1\" attribute missing in initialization of \"%2\". " + "Setting value to %3.") + .arg(attribute) + .arg(e.tagName()) + .arg(value ? "true" : "false")); + } + + return value; + } + + static void setBoolAttribute(QDomElement &element, const QString &attribute, + bool value) { + element.setAttribute(attribute, (value ? "true" : "false")); + } + + static QDomElement QColorDomElement(const QColor &color, const QString &name, + QDomDocument &doc) { + QDomElement de = doc.createElement(name); + de.setAttribute("red", QString::number(color.red())); + de.setAttribute("green", QString::number(color.green())); + de.setAttribute("blue", QString::number(color.blue())); + return de; + } + + static QColor QColorFromDom(const QDomElement &e) { + int color[3]; + QStringList attribute; + attribute << "red" + << "green" + << "blue"; + for (int i = 0; i < attribute.count(); ++i) + color[i] = DomUtils::intFromDom(e, attribute[i], 0); + return QColor(color[0], color[1], color[2]); + } +}; + +#endif // DOXYGEN diff --git a/code/3rd_QGLViewer/QGLViewer/frame.cpp b/code/3rd_QGLViewer/QGLViewer/frame.cpp new file mode 100644 index 00000000..2aaefffd --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/frame.cpp @@ -0,0 +1,1079 @@ +#include "frame.h" +#include "domUtils.h" +#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 nullptr. */ +Frame::Frame() : constraint_(nullptr), referenceFrame_(nullptr) {} + +/*! 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 + nullptr). It has a \c nullptr associated constraint(). */ +Frame::Frame(const Vec &position, const Quaternion &orientation) + : t_(position), q_(orientation), constraint_(nullptr), referenceFrame_(nullptr) {} + +/*! 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 nullptr. + + 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 nullptr + 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 nullptr. + + 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 != nullptr) { + 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 nullptr +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 != nullptr) { + 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 != nullptr) { + 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 != nullptr) && (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 != nullptr) { + 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 != nullptr) && (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 nullptr 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); +} diff --git a/code/3rd_QGLViewer/QGLViewer/frame.h b/code/3rd_QGLViewer/QGLViewer/frame.h new file mode 100644 index 00000000..c6fa5de9 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/frame.h @@ -0,0 +1,437 @@ +#ifndef QGLVIEWER_FRAME_H +#define QGLVIEWER_FRAME_H + +#include +#include + +#include "constraint.h" +// #include "GL/gl.h" is now included in config.h for ease of configuration + +namespace qglviewer { +/*! \brief The Frame class represents a coordinate system, defined by a position + and an orientation. \class Frame frame.h QGLViewer/frame.h + + A Frame is a 3D coordinate system, represented by a position() and an + orientation(). The order of these transformations is important: the Frame is + first translated \e and \e then rotated around the new translated origin. + + A Frame is useful to define the position and orientation of a 3D rigid object, + using its matrix() method, as shown below: \code + // Builds a Frame at position (0.5,0,0) and oriented such that its Y axis is + along the (1,1,1) + // direction. One could also have used setPosition() and setOrientation(). + Frame fr(Vec(0.5,0,0), Quaternion(Vec(0,1,0), Vec(1,1,1))); + glPushMatrix(); + glMultMatrixd(fr.matrix()); + // Draw your object here, in the local fr coordinate system. + glPopMatrix(); + \endcode + + Many functions are provided to transform a 3D point from one coordinate system + (Frame) to an other: see coordinatesOf(), inverseCoordinatesOf(), + coordinatesOfIn(), coordinatesOfFrom()... + + You may also want to transform a 3D vector (such as a normal), which + corresponds to applying only the rotational part of the frame transformation: + see transformOf() and inverseTransformOf(). See the frameTransform example for an + illustration. + + The translation() and the rotation() that are encapsulated in a Frame can also + be used to represent a \e rigid \e transformation of space. Such a + transformation can also be interpreted as a change of coordinate system, and + the coordinate system conversion functions actually allow you to use a Frame + as a rigid transformation. Use inverseCoordinatesOf() (resp. coordinatesOf()) + to apply the transformation (resp. its inverse). Note the inversion. + +

Hierarchy of Frames

+ + The position and the orientation of a Frame are actually defined with respect + to a referenceFrame(). The default referenceFrame() is the world coordinate + system (represented by a \c nullptr referenceFrame()). If you setReferenceFrame() + to a different Frame, you must then differentiate: + + \arg the \e local translation() and rotation(), defined with respect to the + referenceFrame(), + + \arg the \e global position() and orientation(), always defined with respect + to the world coordinate system. + + A Frame is actually defined by its translation() with respect to its + referenceFrame(), and then by a rotation() of the coordinate system around the + new translated origin. + + This terminology for \e local (translation() and rotation()) and \e global + (position() and orientation()) definitions is used in all the methods' names + and should be sufficient to prevent ambiguities. These notions are obviously + identical when the referenceFrame() is \c nullptr, i.e. when the Frame is defined + in the world coordinate system (the one you are in at the beginning of the + QGLViewer::draw() method, see the introduction + page). + + Frames can hence easily be organized in a tree hierarchy, which root is the + world coordinate system. A loop in the hierarchy would result in an + inconsistent (multiple) Frame definition. + settingAsReferenceFrameWillCreateALoop() checks this and prevents + setReferenceFrame() from creating such a loop. + + This frame hierarchy is used in methods like coordinatesOfIn(), + coordinatesOfFrom()... which allow coordinates (or vector) conversions from a + Frame to any other one (including the world coordinate system). + + However, one must note that this hierarchical representation is internal to + the Frame classes. When the Frames represent OpenGL coordinates system, one + should map this hierarchical representation to the OpenGL GL_MODELVIEW matrix + stack. See the matrix() documentation for details. + +

Constraints

+ + An interesting feature of Frames is that their displacements can be + constrained. When a Constraint is attached to a Frame, it filters the input of + translate() and rotate(), and only the resulting filtered motion is applied to + the Frame. The default constraint() is \c nullptr resulting in no filtering. Use + setConstraint() to attach a Constraint to a frame. + + Constraints are especially usefull for the ManipulatedFrame instances, in + order to forbid some mouse motions. See the constrainedFrame, constrainedCamera and luxo examples for an illustration. + + Classical constraints are provided for convenience (see LocalConstraint, + WorldConstraint and CameraConstraint) and new constraints can very easily be + implemented. + +

Derived classes

+ + The ManipulatedFrame class inherits Frame and implements a mouse motion + convertion, so that a Frame (and hence an object) can be manipulated in the + scene with the mouse. + + \nosubgrouping */ +class QGLVIEWER_EXPORT Frame : public QObject { + Q_OBJECT + +public: + Frame(); + + /*! Virtual destructor. Empty. */ + virtual ~Frame() {} + + Frame(const Frame &frame); + Frame &operator=(const Frame &frame); + +Q_SIGNALS: + /*! This signal is emitted whenever the position() or the orientation() of the + Frame is modified. + + Connect this signal to any object that must be notified: + \code + QObject::connect(myFrame, SIGNAL(modified()), myObject, SLOT(update())); + \endcode + Use the QGLViewer::QGLViewerPool() to connect the signal to all the viewers. + + \note If your Frame is part of a Frame hierarchy (see referenceFrame()), a + modification of one of the parents of this Frame will \e not emit this signal. + Use code like this to change this behavior (you can do this recursively for + all the referenceFrame() until the \c nullptr world root frame is encountered): + \code + // Emits the Frame modified() signal when its referenceFrame() is modified(). + connect(myFrame->referenceFrame(), SIGNAL(modified()), myFrame, + SIGNAL(modified())); \endcode + + \attention Connecting this signal to a QOpenGLWidget::update() slot (or a + method that calls it) will prevent you from modifying the Frame \e inside your + QGLViewer::draw() method as it would result in an infinite loop. However, + QGLViewer::draw() should not modify the scene. + + \note Note that this signal might be emitted even if the Frame is not actually + modified, for instance after a translate(Vec(0,0,0)) or a + setPosition(position()). */ + void modified(); + + /*! This signal is emitted when the Frame is interpolated by a + KeyFrameInterpolator. + + See the KeyFrameInterpolator documentation for details. + + If a KeyFrameInterpolator is used to successively interpolate several Frames + in your scene, connect the KeyFrameInterpolator::interpolated() signal instead + (identical, but independent of the interpolated Frame). */ + void interpolated(); + +public: + /*! @name World coordinates position and orientation */ + //@{ + Frame(const Vec &position, const Quaternion &orientation); + + void setPosition(const Vec &position); + void setPosition(qreal x, qreal y, qreal z); + void setPositionWithConstraint(Vec &position); + + void setOrientation(const Quaternion &orientation); + void setOrientation(qreal q0, qreal q1, qreal q2, qreal q3); + void setOrientationWithConstraint(Quaternion &orientation); + + void setPositionAndOrientation(const Vec &position, + const Quaternion &orientation); + void setPositionAndOrientationWithConstraint(Vec &position, + Quaternion &orientation); + + Vec position() const; + Quaternion orientation() const; + + void getPosition(qreal &x, qreal &y, qreal &z) const; + void getOrientation(qreal &q0, qreal &q1, qreal &q2, qreal &q3) const; + //@} + +public: + /*! @name Local translation and rotation w/r reference Frame */ + //@{ + /*! Sets the translation() of the frame, locally defined with respect to the + referenceFrame(). Emits the modified() signal. + + Use setPosition() to define the world coordinates position(). Use + setTranslationWithConstraint() to take into account the potential constraint() + of the Frame. */ + void setTranslation(const Vec &translation) { + t_ = translation; + Q_EMIT modified(); + } + void setTranslation(qreal x, qreal y, qreal z); + void setTranslationWithConstraint(Vec &translation); + + /*! Set the current rotation Quaternion. See rotation() and the different + Quaternion constructors. Emits the modified() signal. See also + setTranslation() and setRotationWithConstraint(). */ + + /*! Sets the rotation() of the Frame, locally defined with respect to the + referenceFrame(). Emits the modified() signal. + + Use setOrientation() to define the world coordinates orientation(). The + potential constraint() of the Frame is not taken into account, use + setRotationWithConstraint() instead. */ + void setRotation(const Quaternion &rotation) { + q_ = rotation; + Q_EMIT modified(); + } + void setRotation(qreal q0, qreal q1, qreal q2, qreal q3); + void setRotationWithConstraint(Quaternion &rotation); + + void setTranslationAndRotation(const Vec &translation, + const Quaternion &rotation); + void setTranslationAndRotationWithConstraint(Vec &translation, + Quaternion &rotation); + + /*! Returns the Frame translation, defined with respect to the + referenceFrame(). + + Use position() to get the result in the world coordinates. These two values + are identical when the referenceFrame() is \c nullptr (default). + + See also setTranslation() and setTranslationWithConstraint(). */ + Vec translation() const { return t_; } + /*! Returns the Frame rotation, defined with respect to the referenceFrame(). + + Use orientation() to get the result in the world coordinates. These two values + are identical when the referenceFrame() is \c nullptr (default). + + See also setRotation() and setRotationWithConstraint(). */ + + /*! Returns the current Quaternion orientation. See setRotation(). */ + Quaternion rotation() const { return q_; } + + void getTranslation(qreal &x, qreal &y, qreal &z) const; + void getRotation(qreal &q0, qreal &q1, qreal &q2, qreal &q3) const; + //@} + +public: + /*! @name Frame hierarchy */ + //@{ + /*! Returns the reference Frame, in which coordinates system the Frame is + defined. + + The translation() and rotation() of the Frame are defined with respect to the + referenceFrame() coordinate system. A \c nullptr referenceFrame() (default value) + means that the Frame is defined in the world coordinate system. + + Use position() and orientation() to recursively convert values along the + referenceFrame() chain and to get values expressed in the world coordinate + system. The values match when the referenceFrame() is \c nullptr. + + Use setReferenceFrame() to set this value and create a Frame hierarchy. + Convenient functions allow you to convert 3D coordinates from one Frame to an + other: see coordinatesOf(), localCoordinatesOf(), coordinatesOfIn() and their + inverse functions. + + Vectors can also be converted using transformOf(), transformOfIn, + localTransformOf() and their inverse functions. */ + const Frame *referenceFrame() const { return referenceFrame_; } + void setReferenceFrame(const Frame *const refFrame); + bool settingAsReferenceFrameWillCreateALoop(const Frame *const frame); + //@} + + /*! @name Frame modification */ + //@{ + void translate(Vec &t); + void translate(const Vec &t); + // Some compilers complain about "overloading cannot distinguish from previous + // declaration" Simply comment out the following method and its associated + // implementation + void translate(qreal x, qreal y, qreal z); + void translate(qreal &x, qreal &y, qreal &z); + + void rotate(Quaternion &q); + void rotate(const Quaternion &q); + // Some compilers complain about "overloading cannot distinguish from previous + // declaration" Simply comment out the following method and its associated + // implementation + void rotate(qreal q0, qreal q1, qreal q2, qreal q3); + void rotate(qreal &q0, qreal &q1, qreal &q2, qreal &q3); + + void rotateAroundPoint(Quaternion &rotation, const Vec &point); + void rotateAroundPoint(const Quaternion &rotation, const Vec &point); + + void alignWithFrame(const Frame *const frame, bool move = false, + qreal threshold = 0.0); + void projectOnLine(const Vec &origin, const Vec &direction); + //@} + + /*! @name Coordinate system transformation of 3D coordinates */ + //@{ + Vec coordinatesOf(const Vec &src) const; + Vec inverseCoordinatesOf(const Vec &src) const; + Vec localCoordinatesOf(const Vec &src) const; + Vec localInverseCoordinatesOf(const Vec &src) const; + Vec coordinatesOfIn(const Vec &src, const Frame *const in) const; + Vec coordinatesOfFrom(const Vec &src, const Frame *const from) const; + + void getCoordinatesOf(const qreal src[3], qreal res[3]) const; + void getInverseCoordinatesOf(const qreal src[3], qreal res[3]) const; + void getLocalCoordinatesOf(const qreal src[3], qreal res[3]) const; + void getLocalInverseCoordinatesOf(const qreal src[3], qreal res[3]) const; + void getCoordinatesOfIn(const qreal src[3], qreal res[3], + const Frame *const in) const; + void getCoordinatesOfFrom(const qreal src[3], qreal res[3], + const Frame *const from) const; + //@} + + /*! @name Coordinate system transformation of vectors */ + // A frame is as a new coordinate system, defined with respect to a reference + // frame (the world coordinate system by default, see the "Composition of + // frame" section). + + // The transformOf() (resp. inverseTransformOf()) functions transform a 3D + // vector from (resp. to) the world coordinates system. This section defines + // the 3D vector transformation functions. See the Coordinate system + // transformation of 3D points above for the transformation of 3D points. The + // difference between the two sets of functions is simple: for vectors, only + // the rotational part of the transformations is taken into account, while + // translation is also considered for 3D points. + + // The length of the resulting transformed vector is identical to the one of + // the source vector for all the described functions. + + // When local is prepended to the names of the functions, the functions simply + // transform from (and to) the reference frame. + + // When In (resp. From) is appended to the names, the functions transform from + // (resp. To) the frame that is given as an argument. The frame does not need + // to be in the same branch or the hierarchical tree, and can be \c nullptr (the + // world coordinates system). + + // Combining any of these functions with its inverse (in any order) leads to + // the identity. + //@{ + Vec transformOf(const Vec &src) const; + Vec inverseTransformOf(const Vec &src) const; + Vec localTransformOf(const Vec &src) const; + Vec localInverseTransformOf(const Vec &src) const; + Vec transformOfIn(const Vec &src, const Frame *const in) const; + Vec transformOfFrom(const Vec &src, const Frame *const from) const; + + void getTransformOf(const qreal src[3], qreal res[3]) const; + void getInverseTransformOf(const qreal src[3], qreal res[3]) const; + void getLocalTransformOf(const qreal src[3], qreal res[3]) const; + void getLocalInverseTransformOf(const qreal src[3], qreal res[3]) const; + void getTransformOfIn(const qreal src[3], qreal res[3], + const Frame *const in) const; + void getTransformOfFrom(const qreal src[3], qreal res[3], + const Frame *const from) const; + //@} + + /*! @name Constraint on the displacement */ + //@{ + /*! Returns the current constraint applied to the Frame. + + A \c nullptr value (default) means that no Constraint is used to filter Frame + translation and rotation. See the Constraint class documentation for details. + + You may have to use a \c dynamic_cast to convert the result to a Constraint + derived class. */ + Constraint *constraint() const { return constraint_; } + /*! Sets the constraint() attached to the Frame. + + A \c nullptr value means no constraint. The previous constraint() should be + deleted by the calling method if needed. */ + void setConstraint(Constraint *const constraint) { constraint_ = constraint; } + //@} + + /*! @name Associated matrices */ + //@{ +public: + const GLdouble *matrix() const; + void getMatrix(GLdouble m[4][4]) const; + void getMatrix(GLdouble m[16]) const; + + const GLdouble *worldMatrix() const; + void getWorldMatrix(GLdouble m[4][4]) const; + void getWorldMatrix(GLdouble m[16]) const; + + void setFromMatrix(const GLdouble m[4][4]); + void setFromMatrix(const GLdouble m[16]); + //@} + + /*! @name Inversion of the transformation */ + //@{ + Frame inverse() const; + /*! Returns the inverse() of the Frame world transformation. + + The orientation() of the new Frame is the Quaternion::inverse() of the + original orientation. Its position() is the negated and inverse rotated image + of the original position. + + The result Frame has a \c nullptr referenceFrame() and a \c nullptr constraint(). + + Use inverse() for a local (i.e. with respect to referenceFrame()) + transformation inverse. */ + Frame worldInverse() const { + return Frame(-(orientation().inverseRotate(position())), + orientation().inverse()); + } + //@} + + /*! @name XML representation */ + //@{ +public: + virtual QDomElement domElement(const QString &name, + QDomDocument &document) const; +public Q_SLOTS: + virtual void initFromDOMElement(const QDomElement &element); + //@} + +private: + // P o s i t i o n a n d o r i e n t a t i o n + Vec t_; + Quaternion q_; + + // C o n s t r a i n t s + Constraint *constraint_; + + // F r a m e c o m p o s i t i o n + const Frame *referenceFrame_; +}; + +} // namespace qglviewer + +#endif // QGLVIEWER_FRAME_H diff --git a/code/3rd_QGLViewer/QGLViewer/keyFrameInterpolator.cpp b/code/3rd_QGLViewer/QGLViewer/keyFrameInterpolator.cpp new file mode 100644 index 00000000..85b65437 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/keyFrameInterpolator.cpp @@ -0,0 +1,689 @@ +#include "domUtils.h" +#include "qglviewer.h" // for QGLViewer::drawAxis and Camera::drawCamera + +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_(nullptr), 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 nullptr \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) : nullptr; + index++; + kf_[3] = (index < keyFrame_.size()) ? keyFrame_.at(index) : nullptr; + + 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) : nullptr; + } + // 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) : nullptr; + 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(nullptr); + pathIsValid_ = false; + valuesAreValid_ = false; + currentFrameValid_ = false; + + stopInterpolation(); +} + +#ifndef DOXYGEN + +//////////// KeyFrame private class implementation ///////// +KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame &fr, qreal t) + : time_(t), frame_(nullptr) { + 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 diff --git a/code/3rd_QGLViewer/QGLViewer/keyFrameInterpolator.h b/code/3rd_QGLViewer/QGLViewer/keyFrameInterpolator.h new file mode 100644 index 00000000..beff0891 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/keyFrameInterpolator.h @@ -0,0 +1,358 @@ +#ifndef QGLVIEWER_KEY_FRAME_INTERPOLATOR_H +#define QGLVIEWER_KEY_FRAME_INTERPOLATOR_H + +#include + +#include "quaternion.h" +// Not actually needed, but some bad compilers (Microsoft VS6) complain. +#include "frame.h" + +// 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 + QGLViewer/keyFrameInterpolator.h + + A KeyFrameInterpolator holds keyFrames (that define a path) and a pointer to a + Frame of your application (which will be interpolated). When the user + startInterpolation(), the KeyFrameInterpolator regularly updates the frame() + position and orientation along the path. + + Here is a typical utilization example (see also the keyFrames example): \code + + + init() + { + // The KeyFrameInterpolator kfi is given the Frame that it will drive + over time. kfi = new KeyFrameInterpolator( new Frame() ); kfi->addKeyFrame( + Frame( Vec(1,0,0), Quaternion() ) ); kfi->addKeyFrame( new Frame( Vec(2,1,0), + Quaternion() ) ); + // ...and so on for all the keyFrames. + + // Ask for a display update after each update of the + KeyFrameInterpolator connect(kfi, SIGNAL(interpolated()), SLOT(update())); + + kfi->startInterpolation(); + } + + draw() + { + glPushMatrix(); + glMultMatrixd( kfi->frame()->matrix() ); + // Draw your object here. Its position and orientation are interpolated. + glPopMatrix(); + } + \endcode + + The keyFrames are defined by a Frame and a time, expressed in seconds. The + Frame can be provided as a const reference or as a pointer to a Frame (see the + addKeyFrame() methods). In the latter case, the path will automatically be + updated when the Frame is modified (using the Frame::modified() signal). + + The time has to be monotonously increasing over keyFrames. When + interpolationSpeed() equals 1.0 (default value), these times correspond to + actual user's seconds during interpolation (provided that your main loop is + fast enough). The interpolation is then real-time: the keyFrames will be + reached at their keyFrameTime(). + +

Interpolation details

+ + When the user startInterpolation(), a timer is started which will update the + frame()'s position and orientation every interpolationPeriod() milliseconds. + This update increases the interpolationTime() by interpolationPeriod() * + interpolationSpeed() milliseconds. + + Note that this mechanism ensures that the number of interpolation steps is + constant and equal to the total path duration() divided by the + interpolationPeriod() * interpolationSpeed(). This is especially useful for + benchmarking or movie creation (constant number of snapshots). + + During the interpolation, the KeyFrameInterpolator emits an interpolated() + signal, which will usually be connected to the QGLViewer::update() slot. The + interpolation is stopped when interpolationTime() is greater than the + lastTime() (unless loopInterpolation() is \c true) and the endReached() signal + is then emitted. + + Note that a Camera has Camera::keyFrameInterpolator(), that can be used to + drive the Camera along a path, or to restore a saved position (a path made of + a single keyFrame). Press Alt+Fx to define a new keyFrame for path x. Pressing + Fx plays/pauses path interpolation. See QGLViewer::pathKey() and the keyboard page for details. + + \attention If a Constraint is attached to the frame() (see + Frame::constraint()), it should be deactivated before + interpolationIsStarted(), otherwise the interpolated motion (computed as if + there was no constraint) will probably be erroneous. + +

Retrieving interpolated values

+ + This code defines a KeyFrameInterpolator, and displays the positions that will + be followed by the frame() along the path: \code KeyFrameInterpolator kfi( new + Frame() ); + // calls to kfi.addKeyFrame() to define the path. + + const qreal deltaTime = 0.04; // output a position every deltaTime seconds + for (qreal time=kfi.firstTime(); time<=kfi.lastTime(); time += deltaTime) + { + kfi.interpolateAtTime(time); + cout << "t=" << time << "\tpos=" << kfi.frame()->position() << endl; + } + \endcode + You may want to temporally disconnect the \c kfi interpolated() signal from + the QGLViewer::update() slot before calling this code. \nosubgrouping */ +class QGLVIEWER_EXPORT KeyFrameInterpolator : public QObject { + // todo closedPath, insertKeyFrames, deleteKeyFrame, replaceKeyFrame + Q_OBJECT + +public: + KeyFrameInterpolator(Frame *fr = nullptr); + virtual ~KeyFrameInterpolator(); + +Q_SIGNALS: + /*! This signal is emitted whenever the frame() state is interpolated. + + The emission of this signal triggers the synchronous emission of the frame() + Frame::interpolated() signal, which may also be useful. + + This signal should especially be connected to your QGLViewer::update() slot, + so that the display is updated after every update of the KeyFrameInterpolator + frame(): \code connect(myKeyFrameInterpolator, SIGNAL(interpolated()), + SLOT(update())); \endcode Use the QGLViewer::QGLViewerPool() to connect the + signal to all the viewers. + + Note that the QGLViewer::camera() Camera::keyFrameInterpolator() created using + QGLViewer::pathKey() have their interpolated() signals automatically connected + to the QGLViewer::update() slot. */ + void interpolated(); + + /*! This signal is emitted when the interpolation reaches the first (when + interpolationSpeed() is negative) or the last keyFrame. + + When loopInterpolation() is \c true, interpolationTime() is reset and the + interpolation continues. It otherwise stops. */ + void endReached(); + + /*! @name Path creation */ + //@{ +public Q_SLOTS: + void addKeyFrame(const Frame &frame); + void addKeyFrame(const Frame &frame, qreal time); + + void addKeyFrame(const Frame *const frame); + void addKeyFrame(const Frame *const frame, qreal time); + + void deletePath(); + //@} + + /*! @name Associated Frame */ + //@{ +public: + /*! Returns the associated Frame and that is interpolated by the + KeyFrameInterpolator. + + When interpolationIsStarted(), this Frame's position and orientation will + regularly be updated by a timer, so that they follow the KeyFrameInterpolator + path. + + Set using setFrame() or with the KeyFrameInterpolator constructor. */ + Frame *frame() const { return frame_; } + +public Q_SLOTS: + void setFrame(Frame *const frame); + //@} + + /*! @name Path parameters */ + //@{ +public: + Frame keyFrame(int index) const; + qreal keyFrameTime(int index) const; + /*! Returns the number of keyFrames used by the interpolation. Use + * addKeyFrame() to add new keyFrames. */ + int numberOfKeyFrames() const { return keyFrame_.count(); } + qreal duration() const; + qreal firstTime() const; + qreal lastTime() const; + //@} + + /*! @name Interpolation parameters */ + //@{ +public: + /*! Returns the current interpolation time (in seconds) along the + KeyFrameInterpolator path. + + This time is regularly updated when interpolationIsStarted(). Can be set + directly with setInterpolationTime() or interpolateAtTime(). */ + qreal interpolationTime() const { return interpolationTime_; } + /*! Returns the current interpolation speed. + + Default value is 1.0, which means keyFrameTime() will be matched during the + interpolation (provided that your main loop is fast enough). + + A negative value will result in a reverse interpolation of the keyFrames. See + also interpolationPeriod(). */ + qreal interpolationSpeed() const { return interpolationSpeed_; } + /*! Returns the current interpolation period, expressed in milliseconds. + + The update of the frame() state will be done by a timer at this period when + interpolationIsStarted(). + + This period (multiplied by interpolationSpeed()) is added to the + interpolationTime() at each update, and the frame() state is modified + accordingly (see interpolateAtTime()). Default value is 40 milliseconds. */ + int interpolationPeriod() const { return period_; } + /*! Returns \c true when the interpolation is played in an infinite loop. + + When \c false (default), the interpolation stops when interpolationTime() + reaches firstTime() (with negative interpolationSpeed()) or lastTime(). + + interpolationTime() is otherwise reset to firstTime() (+ interpolationTime() - + lastTime()) (and inversely for negative interpolationSpeed()) and + interpolation continues. + + In both cases, the endReached() signal is emitted. */ + bool loopInterpolation() const { return loopInterpolation_; } +#ifndef DOXYGEN + /*! Whether or not (default) the path defined by the keyFrames is a closed + loop. When \c true, the last and the first KeyFrame are linked by a new spline + segment. + + Use setLoopInterpolation() to create a continuous animation over the entire + path. \attention The closed path feature is not yet implemented. */ + bool closedPath() const { return closedPath_; } +#endif +public Q_SLOTS: + /*! Sets the interpolationTime(). + + \attention The frame() state is not affected by this method. Use this function + to define the starting time of a future interpolation (see + startInterpolation()). Use interpolateAtTime() to actually interpolate at a + given time. */ + void setInterpolationTime(qreal time) { interpolationTime_ = time; } + /*! Sets the interpolationSpeed(). Negative or null values are allowed. */ + void setInterpolationSpeed(qreal speed) { interpolationSpeed_ = speed; } + /*! Sets the interpolationPeriod(). */ + void setInterpolationPeriod(int period) { period_ = period; } + /*! Sets the loopInterpolation() value. */ + void setLoopInterpolation(bool loop = true) { loopInterpolation_ = loop; } +#ifndef DOXYGEN + /*! Sets the closedPath() value. \attention The closed path feature is not yet + * implemented. */ + void setClosedPath(bool closed = true) { closedPath_ = closed; } +#endif + //@} + + /*! @name Interpolation */ + //@{ +public: + /*! Returns \c true when the interpolation is being performed. Use + startInterpolation(), stopInterpolation() or toggleInterpolation() to modify + this state. */ + bool interpolationIsStarted() const { return interpolationStarted_; } +public Q_SLOTS: + void startInterpolation(int period = -1); + void stopInterpolation(); + void resetInterpolation(); + /*! Calls startInterpolation() or stopInterpolation(), depending on + * interpolationIsStarted(). */ + void toggleInterpolation() { + if (interpolationIsStarted()) + stopInterpolation(); + else + startInterpolation(); + } + 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: + virtual QDomElement domElement(const QString &name, + QDomDocument &document) const; + virtual void initFromDOMElement(const QDomElement &element); + //@} + +private Q_SLOTS: + virtual void update(); + virtual void invalidateValues() { + valuesAreValid_ = false; + pathIsValid_ = false; + splineCacheIsValid_ = false; + } + +private: + // Copy constructor and opertor= are declared private and undefined + // Prevents everyone from trying to use them + // KeyFrameInterpolator(const KeyFrameInterpolator& kfi); + // KeyFrameInterpolator& operator=(const KeyFrameInterpolator& kfi); + + void updateCurrentKeyFrameForTime(qreal time); + void updateModifiedFrameValues(); + void updateSplineCache(); + +#ifndef DOXYGEN + // Internal private KeyFrame representation + class KeyFrame { + public: + KeyFrame(const Frame &fr, qreal t); + KeyFrame(const Frame *fr, qreal t); + + Vec position() const { return p_; } + Quaternion orientation() const { return q_; } + Vec tgP() const { return tgP_; } + Quaternion tgQ() const { return tgQ_; } + qreal time() const { return time_; } + const Frame *frame() const { return frame_; } + void updateValuesFromPointer(); + void flipOrientationIfNeeded(const Quaternion &prev); + void computeTangent(const KeyFrame *const prev, const KeyFrame *const next); + + private: + Vec p_, tgP_; + Quaternion q_, tgQ_; + qreal time_; + const Frame *const frame_; + }; +#endif + + // K e y F r a m e s + mutable QList keyFrame_; + QMutableListIterator *currentFrame_[4]; + QList path_; + + // A s s o c i a t e d f r a m e + Frame *frame_; + + // R h y t h m + QTimer timer_; + int period_; + qreal interpolationTime_; + qreal interpolationSpeed_; + bool interpolationStarted_; + + // M i s c + bool closedPath_; + bool loopInterpolation_; + + // C a c h e d v a l u e s a n d f l a g s + bool pathIsValid_; + bool valuesAreValid_; + bool currentFrameValid_; + bool splineCacheIsValid_; + Vec v1, v2; +}; + +} // namespace qglviewer + +#endif // QGLVIEWER_KEY_FRAME_INTERPOLATOR_H diff --git a/code/3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.cpp b/code/3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.cpp new file mode 100644 index 00000000..a9f2fb05 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.cpp @@ -0,0 +1,502 @@ +#include "manipulatedCameraFrame.h" +#include "domUtils.h" +#include "qglviewer.h" + +#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: { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); +#else + Quaternion rot = pitchYawQuaternion(event->position().x(), event->position().y(), camera); +#endif + 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: { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); +#else + Quaternion rot = pitchYawQuaternion(event->position().x(), event->position().y(), camera); +#endif + rotate(rot); + // actual translation is made in flyUpdate(). + // translate(inverseTransformOf(Vec(0.0, 0.0, flySpeed()))); + break; + } + + case QGLViewer::DRIVE: { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + Quaternion rot = turnQuaternion(event->x(), camera); +#else + Quaternion rot = turnQuaternion(event->position().x(), camera); +#endif + rotate(rot); + // actual translation is made in flyUpdate(). +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + driveSpeed_ = 0.01 * (event->y() - pressPos_.y()); +#else + driveSpeed_ = 0.01 * (event->position().y() - pressPos_.y()); +#endif + break; + } + + case QGLViewer::ZOOM: { + zoom(deltaWithPrevPos(event, camera), camera); + break; + } + + case QGLViewer::LOOK_AROUND: { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera); +#else + Quaternion rot = pitchYawQuaternion(event->position().x(), event->position().y(), camera); +#endif + 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 +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + qreal dx = 2.0 * rotationSensitivity() * (prevPos_.x() - event->x()) / + camera->screenWidth(); + qreal dy = 2.0 * rotationSensitivity() * (prevPos_.y() - event->y()) / + camera->screenHeight(); +#else + qreal dx = 2.0 * rotationSensitivity() * (prevPos_.x() - event->position().x()) / + camera->screenWidth(); + qreal dy = 2.0 * rotationSensitivity() * (prevPos_.y() - event->position().y()) / + camera->screenHeight(); +#endif + 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()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], camera); +#else + rot = deformedBallQuaternion(event->position().x(), event->position().y(), trans[0], trans[1], camera); +#endif + } + //#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()); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]) - + atan2(prevPos_.y() - trans[1], prevPos_.x() - trans[0]); +#else + const qreal angle = atan2(event->position().y() - trans[1], event->position().x() - trans[0]) - + atan2(prevPos_.y() - trans[1], prevPos_.x() - trans[0]); +#endif + + 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: { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + const qreal angle = M_PI * (event->x() - prevPos_.x()) / camera->screenWidth(); +#else + const qreal angle = M_PI * (event->position().x() - prevPos_.x()) / camera->screenWidth(); +#endif + 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) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + trans.setValue(prevPos_.x() - event->x(), 0.0, 0.0); +#else + trans.setValue(prevPos_.x() - event->position().x(), 0.0, 0.0); +#endif + else if (dir == -1) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + trans.setValue(0.0, event->y() - prevPos_.y(), 0.0); +#else + trans.setValue(0.0, event->position().y() - prevPos_.y(), 0.0); +#endif + + 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( +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + inverseTransformOf(Vec(0.0, 0.0, 0.2 * flySpeed() * event->delta()))); +#else + inverseTransformOf(Vec(0.0, 0.0, 0.2 * flySpeed() * event->angleDelta().y()))); +#endif + 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; +} diff --git a/code/3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.h b/code/3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.h new file mode 100644 index 00000000..a027832c --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.h @@ -0,0 +1,241 @@ +#ifndef QGLVIEWER_MANIPULATED_CAMERA_FRAME_H +#define QGLVIEWER_MANIPULATED_CAMERA_FRAME_H + +#include "manipulatedFrame.h" + +namespace qglviewer { +/*! \brief The ManipulatedCameraFrame class represents a ManipulatedFrame with + Camera specific mouse bindings. \class ManipulatedCameraFrame + manipulatedCameraFrame.h QGLViewer/manipulatedCameraFrame.h + + A ManipulatedCameraFrame is a specialization of a ManipulatedFrame, designed + to be set as the Camera::frame(). Mouse motions are basically interpreted in a + negated way: when the mouse goes to the right, the ManipulatedFrame + translation goes to the right, while the ManipulatedCameraFrame has to go to + the \e left, so that the \e scene seems to move to the right. + + A ManipulatedCameraFrame rotates around its pivotPoint(), which corresponds to + the associated Camera::pivotPoint(). + + 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. + + See the mouse page for a description of the + possible actions that can be performed using the mouse and their bindings. + \nosubgrouping */ +class QGLVIEWER_EXPORT ManipulatedCameraFrame : public ManipulatedFrame { +#ifndef DOXYGEN + friend class Camera; + friend class ::QGLViewer; +#endif + + Q_OBJECT + +public: + ManipulatedCameraFrame(); + /*! Virtual destructor. Empty. */ + virtual ~ManipulatedCameraFrame() {} + + ManipulatedCameraFrame(const ManipulatedCameraFrame &mcf); + ManipulatedCameraFrame &operator=(const ManipulatedCameraFrame &mcf); + + /*! @name Pivot point */ + //@{ +public: + /*! Returns the point the ManipulatedCameraFrame pivot point, around which the + camera rotates. + + It is defined in the world coordinate system. Default value is (0,0,0). + + 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. */ + void setPivotPoint(const Vec &point) { pivotPoint_ = point; } + +#ifndef DOXYGEN + Vec revolveAroundPoint() const { + qWarning("revolveAroundPoint() is deprecated, use pivotPoint() instead"); + return pivotPoint(); + } + void setRevolveAroundPoint(const Vec &point) { + qWarning( + "setRevolveAroundPoint() is deprecated, use setPivotPoint() instead"); + setPivotPoint(point); + } +#endif + //@} + + /*! @name Camera manipulation */ + //@{ +public: + /*! Returns \c true when the frame's rotation is constrained around the + sceneUpVector(), and \c false otherwise, when the rotation is completely + free (default). + + In free mode, the associated camera can be arbitrarily rotated in the + scene, along its three axis, thus possibly leading to any arbitrary + orientation. + + When you setRotatesAroundUpVector() to \c true, the sceneUpVector() + defines a 'vertical' direction around which the camera rotates. The camera + can rotate left or right, around this axis. It can also be moved up or down + to show the 'top' and 'bottom' views of the scene. As a result, the + sceneUpVector() will always appear vertical in the scene, and the horizon + is preserved and stays projected along the camera's horizontal axis. + + Note that setting this value to \c true when the sceneUpVector() is + not already vertically projected will break these invariants. It will also + limit the possible movement of the camera, possibly up to a lock when the + sceneUpVector() is projected horizontally. Use Camera::setUpVector() to + define the sceneUpVector() and align the camera before calling this method + to ensure this does not happen. */ + bool rotatesAroundUpVector() const { return rotatesAroundUpVector_; } + /*! Sets the value of rotatesAroundUpVector(). + + Default value is false (free rotation). */ + void setRotatesAroundUpVector(bool constrained) { + rotatesAroundUpVector_ = constrained; + } + + /*! Returns whether or not the QGLViewer::ZOOM action zooms on the pivot + point. + + When set to \c false (default), a zoom action will move the camera along its + Camera::viewDirection(), i.e. back and forth along a direction perpendicular + to the projection screen. + + setZoomsOnPivotPoint() to \c true will move the camera along an axis defined + by the Camera::pivotPoint() and its current position instead. As a result, + the projected position of the pivot point on screen will stay the same + during a zoom. */ + bool zoomsOnPivotPoint() const { return zoomsOnPivotPoint_; } + /*! Sets the value of zoomsOnPivotPoint(). + + Default value is false. */ + void setZoomsOnPivotPoint(bool enabled) { zoomsOnPivotPoint_ = enabled; } + +private: +#ifndef DOXYGEN + void zoom(qreal delta, const Camera *const camera); +#endif + //@} + + /*! @name Fly parameters */ + //@{ +public Q_SLOTS: + /*! Sets the flySpeed(), defined in OpenGL units. + + Default value is 0.0, but it is modified according to the + QGLViewer::sceneRadius() when the ManipulatedCameraFrame is set as the + Camera::frame(). */ + void setFlySpeed(qreal speed) { flySpeed_ = speed; } + + /*! Sets the sceneUpVector(), defined in the world coordinate system. + + Default value is (0,1,0), but it is updated by the Camera when this object is + set as its Camera::frame(). Using Camera::setUpVector() instead is probably a + better solution. */ + void setSceneUpVector(const Vec &up) { sceneUpVector_ = up; } + +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. + + \attention When the ManipulatedCameraFrame is set as the Camera::frame(), this + value is set according to the QGLViewer::sceneRadius() by + QGLViewer::setSceneRadius(). */ + qreal flySpeed() const { return flySpeed_; } + + /*! 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 + displacements of the mouse rotate the ManipulatedCameraFrame around this + vector. Vertical displacements rotate always around the Camera \c X axis. + + This value is also used when setRotationIsConstrained() is set to \c true to + define the up vector (and incidentally the 'horizon' plane) around which the + camera will rotate. + + Default value is (0,1,0), but it is updated by the Camera when this object is + set as its Camera::frame(). Camera::setOrientation() and + Camera::setUpVector()) direclty modify this value and should be used instead. +*/ + Vec sceneUpVector() const { return sceneUpVector_; } + +#ifndef DOXYGEN + Vec flyUpVector() const; + void setFlyUpVector(const Vec &up); +#endif + //@} + + /*! @name Mouse event handlers */ + //@{ +protected: + virtual void mouseReleaseEvent(QMouseEvent *const event, + Camera *const camera); + virtual void mouseMoveEvent(QMouseEvent *const event, Camera *const camera); + virtual void wheelEvent(QWheelEvent *const event, Camera *const camera); + //@} + + /*! @name Spinning */ + //@{ +protected Q_SLOTS: + virtual void spin(); + //@} + + /*! @name XML representation */ + //@{ +public: + virtual QDomElement domElement(const QString &name, + QDomDocument &document) const; +public Q_SLOTS: + virtual void initFromDOMElement(const QDomElement &element); +//@} + +#ifndef DOXYGEN +protected: + virtual void startAction( + int ma, + bool withConstraint = true); // int is really a QGLViewer::MouseAction +#endif + +private Q_SLOTS: + virtual void flyUpdate(); + +private: + void updateSceneUpVector(); + Quaternion turnQuaternion(int x, const Camera *const camera); + Quaternion pitchYawQuaternion(int x, int y, const Camera *const camera); + +private: + // Fly mode data + qreal flySpeed_; + qreal driveSpeed_; + Vec sceneUpVector_; + QTimer flyTimer_; + + bool rotatesAroundUpVector_; + // Inverse the direction of an horizontal mouse motion. Depends on the + // projected screen orientation of the vertical axis when the mouse button is + // pressed. + bool constrainedRotationIsReversed_; + + bool zoomsOnPivotPoint_; + + Vec pivotPoint_; +}; + +} // namespace qglviewer + +#endif // QGLVIEWER_MANIPULATED_CAMERA_FRAME_H diff --git a/code/3rd_QGLViewer/QGLViewer/manipulatedFrame.cpp b/code/3rd_QGLViewer/QGLViewer/manipulatedFrame.cpp new file mode 100644 index 00000000..dd74fb60 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/manipulatedFrame.cpp @@ -0,0 +1,565 @@ +#include "manipulatedFrame.h" +#include "camera.h" +#include "domUtils.h" +#include "manipulatedCameraFrame.h" +#include "qglviewer.h" + +#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_ = nullptr; + + 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 nullptr. + // *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_ = static_cast(ma); + + // #CONNECTION# manipulatedFrame::wheelEvent, + // manipulatedCameraFrame::wheelEvent and mouseReleaseEvent() restore previous + // constraint + if (withConstraint) + previousConstraint_ = nullptr; + else { + previousConstraint_ = constraint(); + setConstraint(nullptr); + } + + 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 { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + qreal dx = qreal(event->x() - prevPos_.x()) / camera->screenWidth(); + qreal dy = qreal(event->y() - prevPos_.y()) / camera->screenHeight(); +#else + qreal dx = qreal(event->position().x() - prevPos_.x()) / camera->screenWidth(); + qreal dy = qreal(event->position().y() - prevPos_.y()) / camera->screenHeight(); +#endif + + 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; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + return event->delta() * wheelSensitivity() * WHEEL_SENSITIVITY_COEF; +#else + return event->angleDelta().y() * wheelSensitivity() * WHEEL_SENSITIVITY_COEF; +#endif +} + +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]); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]); +#else + const qreal angle = atan2(event->position().y() - trans[1], event->position().x() - trans[0]); +#endif + + 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) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + trans.setValue(event->x() - prevPos_.x(), 0.0, 0.0); +#else + trans.setValue(event->position().x() - prevPos_.x(), 0.0, 0.0); +#endif + else if (dir == -1) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + trans.setValue(0.0, prevPos_.y() - event->y(), 0.0); +#else + trans.setValue(0.0, prevPos_.y() - event->position().y(), 0.0); +#endif + + 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()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + Quaternion rot = deformedBallQuaternion(event->x(), event->y(), trans[0], + trans[1], camera); +#else + Quaternion rot = deformedBallQuaternion(event->position().x(), event->position().y(), + trans[0], trans[1], camera); +#endif + 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 diff --git a/code/3rd_QGLViewer/QGLViewer/manipulatedFrame.h b/code/3rd_QGLViewer/QGLViewer/manipulatedFrame.h new file mode 100644 index 00000000..898d3862 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/manipulatedFrame.h @@ -0,0 +1,353 @@ +#ifndef QGLVIEWER_MANIPULATED_FRAME_H +#define QGLVIEWER_MANIPULATED_FRAME_H + +#include "frame.h" +#include "mouseGrabber.h" +#include "qglviewer.h" + +#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 + + It converts the mouse motion into a translation and an orientation updates. A + ManipulatedFrame is used to move an object in the scene. Combined with object + selection, its MouseGrabber properties and a dynamic update of the scene, the + ManipulatedFrame introduces a great reactivity in your applications. + + A ManipulatedFrame is attached to a QGLViewer using + QGLViewer::setManipulatedFrame(): \code init() { setManipulatedFrame( new + ManipulatedFrame() ); } + + draw() + { + glPushMatrix(); + glMultMatrixd(manipulatedFrame()->matrix()); + // draw the manipulated object here + glPopMatrix(); + } + \endcode + See the manipulatedFrame + example for a complete application. + + Mouse events are normally sent to the QGLViewer::camera(). You have to press + the QGLViewer::FRAME state key (default is \c Control) to move the + QGLViewer::manipulatedFrame() instead. See the mouse + page for a description of mouse button bindings. + +

Inherited functionalities

+ + A ManipulatedFrame is an overloaded instance of a Frame. The powerful + coordinate system transformation functions (Frame::coordinatesOf(), + Frame::transformOf(), ...) can hence be applied to a ManipulatedFrame. + + A ManipulatedFrame is also a MouseGrabber. If the mouse cursor gets within a + distance of 10 pixels from the projected position of the ManipulatedFrame, the + ManipulatedFrame becomes the new QGLViewer::mouseGrabber(). It can then be + manipulated directly, without any specific state key, object selection or GUI + intervention. This is very convenient to directly move some objects in the + scene (typically a light). See the mouseGrabber example as an + illustration. Note that QWidget::setMouseTracking() needs to be enabled in + order to use this feature (see the MouseGrabber documentation). + +

Advanced functionalities

+ + A QGLViewer can handle at most one ManipulatedFrame at a time. If you want to + move several objects in the scene, you simply have to keep a list of the + different ManipulatedFrames, and to activate the right one (using + QGLViewer::setManipulatedFrame()) when needed. This can for instance be done + according to an object selection: see the luxo + example for an illustration. + + When the ManipulatedFrame is being manipulated using the mouse (mouse pressed + and not yet released), isManipulated() returns \c true. This might be used to + trigger a specific action or display (as is done with QGLViewer::fastDraw()). + + The ManipulatedFrame also emits a manipulated() signal each time its state is + modified by the mouse. This signal is automatically connected to the + QGLViewer::update() slot when the ManipulatedFrame is attached to a viewer + using QGLViewer::setManipulatedFrame(). + + You can make the ManipulatedFrame spin() if you release the rotation mouse + button while moving the mouse fast enough (see spinningSensitivity()). See + also translationSensitivity() and rotationSensitivity() for sensitivity + tuning. \nosubgrouping */ +class QGLVIEWER_EXPORT ManipulatedFrame : public Frame, public MouseGrabber { +#ifndef DOXYGEN + friend class Camera; + friend class ::QGLViewer; +#endif + + Q_OBJECT + +public: + ManipulatedFrame(); + /*! Virtual destructor. Empty. */ + virtual ~ManipulatedFrame() {} + + ManipulatedFrame(const ManipulatedFrame &mf); + ManipulatedFrame &operator=(const ManipulatedFrame &mf); + +Q_SIGNALS: + /*! This signal is emitted when ever the ManipulatedFrame is manipulated (i.e. + rotated or translated) using the mouse. Connect this signal to any object that + should be notified. + + Note that this signal is automatically connected to the QGLViewer::update() + slot, when the ManipulatedFrame is attached to a viewer using + QGLViewer::setManipulatedFrame(), which is probably all you need. + + Use the QGLViewer::QGLViewerPool() if you need to connect this signal to all + the viewers. + + See also the spun(), modified(), interpolated() and + KeyFrameInterpolator::interpolated() signals' documentations. */ + void manipulated(); + + /*! This signal is emitted when the ManipulatedFrame isSpinning(). + + Note that for the QGLViewer::manipulatedFrame(), this signal is automatically + connected to the QGLViewer::update() slot. + + Connect this signal to any object that should be notified. Use the + QGLViewer::QGLViewerPool() if you need to connect this signal to all the + viewers. + + See also the manipulated(), modified(), interpolated() and + KeyFrameInterpolator::interpolated() signals' documentations. */ + void spun(); + + /*! @name Manipulation sensitivity */ + //@{ +public Q_SLOTS: + /*! Defines the rotationSensitivity(). */ + void setRotationSensitivity(qreal sensitivity) { + rotationSensitivity_ = sensitivity; + } + /*! Defines the translationSensitivity(). */ + void setTranslationSensitivity(qreal sensitivity) { + translationSensitivity_ = sensitivity; + } + /*! Defines the spinningSensitivity(), in pixels per milliseconds. */ + void setSpinningSensitivity(qreal sensitivity) { + spinningSensitivity_ = sensitivity; + } + /*! Defines the wheelSensitivity(). */ + void setWheelSensitivity(qreal sensitivity) { + wheelSensitivity_ = sensitivity; + } + /*! Defines the zoomSensitivity(). */ + void setZoomSensitivity(qreal sensitivity) { zoomSensitivity_ = sensitivity; } + +public: + /*! Returns the influence of a mouse displacement on the ManipulatedFrame + rotation. + + Default value is 1.0. With an identical mouse displacement, a higher value + will generate a larger rotation (and inversely for lower values). A 0.0 value + will forbid ManipulatedFrame mouse rotation (see also constraint()). + + See also setRotationSensitivity(), translationSensitivity(), + spinningSensitivity() and wheelSensitivity(). */ + qreal rotationSensitivity() const { return rotationSensitivity_; } + /*! Returns the influence of a mouse displacement on the ManipulatedFrame + translation. + + Default value is 1.0. You should not have to modify this value, since with 1.0 + the ManipulatedFrame precisely stays under the mouse cursor. + + With an identical mouse displacement, a higher value will generate a larger + translation (and inversely for lower values). A 0.0 value will forbid + ManipulatedFrame mouse translation (see also constraint()). + + \note When the ManipulatedFrame is used to move a \e Camera (see the + ManipulatedCameraFrame class documentation), after zooming on a small region + of your scene, the camera may translate too fast. For a camera, it is the + Camera::pivotPoint() that exactly matches the mouse displacement. Hence, + instead of changing the translationSensitivity(), solve the problem by + (temporarily) setting the Camera::pivotPoint() to a point on the zoomed region + (see the QGLViewer::RAP_FROM_PIXEL mouse binding in the mouse page). + + See also setTranslationSensitivity(), rotationSensitivity(), + spinningSensitivity() and wheelSensitivity(). */ + qreal translationSensitivity() const { return translationSensitivity_; } + /*! Returns the minimum mouse speed required (at button release) to make the + ManipulatedFrame spin(). + + See spin(), spinningQuaternion() and startSpinning() for details. + + Mouse speed is expressed in pixels per milliseconds. Default value is 0.3 (300 + pixels per second). Use setSpinningSensitivity() to tune this value. A higher + value will make spinning more difficult (a value of 100.0 forbids spinning in + practice). + + See also setSpinningSensitivity(), translationSensitivity(), + rotationSensitivity() and wheelSensitivity(). */ + qreal spinningSensitivity() const { return spinningSensitivity_; } + + /*! Returns the zoom sensitivity. + + Default value is 1.0. A higher value will make the zoom faster. + Use a negative value to invert the zoom in and out directions. + + See also setZoomSensitivity(), translationSensitivity(), rotationSensitivity() + wheelSensitivity() and spinningSensitivity(). */ + qreal zoomSensitivity() const { return zoomSensitivity_; } + /*! Returns the mouse wheel sensitivity. + + Default value is 1.0. A higher value will make the wheel action more efficient + (usually meaning a faster zoom). Use a negative value to invert the zoom in + and out directions. + + See also setWheelSensitivity(), translationSensitivity(), + rotationSensitivity() zoomSensitivity() and spinningSensitivity(). */ + qreal wheelSensitivity() const { return wheelSensitivity_; } + //@} + + /*! @name Spinning */ + //@{ +public: + /*! Returns \c true when the ManipulatedFrame is spinning. + + During spinning, spin() rotates the ManipulatedFrame by its + spinningQuaternion() at a frequency defined when the ManipulatedFrame + startSpinning(). + + Use startSpinning() and stopSpinning() to change this state. Default value is + \c false. */ + bool isSpinning() const { return isSpinning_; } + /*! Returns the incremental rotation that is applied by spin() to the + ManipulatedFrame orientation when it isSpinning(). + + Default value is a null rotation (identity Quaternion). Use + setSpinningQuaternion() to change this value. + + The spinningQuaternion() axis is defined in the ManipulatedFrame coordinate + system. You can use Frame::transformOfFrom() to convert this axis from an + other Frame coordinate system. */ + Quaternion spinningQuaternion() const { return spinningQuaternion_; } +public Q_SLOTS: + /*! Defines the spinningQuaternion(). Its axis is defined in the + ManipulatedFrame coordinate system. */ + void setSpinningQuaternion(const Quaternion &spinningQuaternion) { + spinningQuaternion_ = spinningQuaternion; + } + virtual void startSpinning(int updateInterval); + /*! Stops the spinning motion started using startSpinning(). isSpinning() will + return \c false after this call. */ + virtual void stopSpinning() { + spinningTimer_.stop(); + isSpinning_ = false; + } +protected Q_SLOTS: + virtual void spin(); +private Q_SLOTS: + void spinUpdate(); + //@} + + /*! @name Mouse event handlers */ + //@{ +protected: + virtual void mousePressEvent(QMouseEvent *const event, Camera *const camera); + virtual void mouseMoveEvent(QMouseEvent *const event, Camera *const camera); + virtual void mouseReleaseEvent(QMouseEvent *const event, + Camera *const camera); + virtual void mouseDoubleClickEvent(QMouseEvent *const event, + Camera *const camera); + virtual void wheelEvent(QWheelEvent *const event, Camera *const camera); + //@} + +public: + /*! @name Current state */ + //@{ + bool isManipulated() const; + /*! Returns the \c MouseAction currently applied to this ManipulatedFrame. + + Will return QGLViewer::NO_MOUSE_ACTION unless a mouse button is being + pressed and has been bound to this QGLViewer::MouseHandler. + + The binding between mouse buttons and key modifiers and MouseAction is set + using QGLViewer::setMouseBinding(Qt::Key key, Qt::KeyboardModifiers + modifiers, Qt::MouseButton buttons, MouseHandler handler, MouseAction action, + bool withConstraint). + */ + QGLViewer::MouseAction currentMouseAction() const { return action_; } + //@} + + /*! @name MouseGrabber implementation */ + //@{ +public: + virtual void checkIfGrabsMouse(int x, int y, const Camera *const camera); + //@} + + /*! @name XML representation */ + //@{ +public: + virtual QDomElement domElement(const QString &name, + QDomDocument &document) const; +public Q_SLOTS: + virtual void initFromDOMElement(const QDomElement &element); +//@} + +#ifndef DOXYGEN +protected: + Quaternion deformedBallQuaternion(int x, int y, qreal cx, qreal cy, + const Camera *const camera); + + QGLViewer::MouseAction action_; + Constraint *previousConstraint_; // When manipulation is without Contraint. + + virtual void startAction( + int ma, + bool withConstraint = true); // int is really a QGLViewer::MouseAction + void computeMouseSpeed(const QMouseEvent *const e); + int mouseOriginalDirection(const QMouseEvent *const e); + + /*! Returns a screen scaled delta from event's position to prevPos_, along the + X or Y direction, whichever has the largest magnitude. */ + qreal deltaWithPrevPos(QMouseEvent *const event, Camera *const camera) const; + /*! Returns a normalized wheel delta, proportionnal to wheelSensitivity(). */ + qreal wheelDelta(const QWheelEvent *event) const; + + // Previous mouse position (used for incremental updates) and mouse press + // position. + QPoint prevPos_, pressPos_; + +private: + void zoom(qreal delta, const Camera *const camera); + +#endif // DOXYGEN + +private: + // Sensitivity + qreal rotationSensitivity_; + qreal translationSensitivity_; + qreal spinningSensitivity_; + qreal wheelSensitivity_; + qreal zoomSensitivity_; + + // Mouse speed and spinning + QElapsedTimer last_move_time; + qreal mouseSpeed_; + int delay_; + bool isSpinning_; + QTimer spinningTimer_; + Quaternion spinningQuaternion_; + + // Whether the SCREEN_TRANS direction (horizontal or vertical) is fixed or + // not. + bool dirIsFixed_; + + // MouseGrabber + bool keepsGrabbingMouse_; +}; + +} // namespace qglviewer + +#endif // QGLVIEWER_MANIPULATED_FRAME_H diff --git a/code/3rd_QGLViewer/QGLViewer/mouseGrabber.cpp b/code/3rd_QGLViewer/QGLViewer/mouseGrabber.cpp new file mode 100644 index 00000000..e9fe6e16 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/mouseGrabber.cpp @@ -0,0 +1,51 @@ +#include "mouseGrabber.h" + +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(); +} diff --git a/code/3rd_QGLViewer/QGLViewer/mouseGrabber.h b/code/3rd_QGLViewer/QGLViewer/mouseGrabber.h new file mode 100644 index 00000000..76cc463d --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/mouseGrabber.h @@ -0,0 +1,276 @@ +#ifndef QGLVIEWER_MOUSE_GRABBER_H +#define QGLVIEWER_MOUSE_GRABBER_H + +#include "config.h" + +#include + +class QGLViewer; + +namespace qglviewer { +class Camera; + +/*! \brief Abstract class for objects that grab mouse focus in a QGLViewer. + \class MouseGrabber mouseGrabber.h QGLViewer/mouseGrabber.h + + MouseGrabber are objects which react to the mouse cursor, usually when it + hovers over them. This abstract class only provides an interface for all these + objects: their actual behavior has to be defined in a derived class. + +

How does it work ?

+ + All the created MouseGrabber are grouped in a MouseGrabberPool(). The + QGLViewers parse this pool, calling all the MouseGrabbers' checkIfGrabsMouse() + methods that setGrabsMouse() if desired. + + When a MouseGrabber grabsMouse(), it becomes the QGLViewer::mouseGrabber(). + All the mouse events (mousePressEvent(), mouseReleaseEvent(), + mouseMoveEvent(), mouseDoubleClickEvent() and wheelEvent()) are then + transmitted to the QGLViewer::mouseGrabber() instead of being normally + processed. This continues while grabsMouse() (updated using + checkIfGrabsMouse()) returns \c true. + + If you want to (temporarily) disable a specific MouseGrabbers, you can remove + it from this pool using removeFromMouseGrabberPool(). You can also disable a + MouseGrabber in a specific QGLViewer using + QGLViewer::setMouseGrabberIsEnabled(). + +

Implementation details

+ + In order to make MouseGrabber react to mouse events, mouse tracking has to be + activated in the QGLViewer which wants to use MouseGrabbers: \code init() { + setMouseTracking(true); } \endcode Call \c QOpenGLWidget::hasMouseTracking() + to get the current state of this flag. + + The \p camera parameter of the different mouse event methods is a pointer to + the QGLViewer::camera() of the QGLViewer that uses the MouseGrabber. It can be + used to compute 2D to 3D coordinates conversion using + Camera::projectedCoordinatesOf() and Camera::unprojectedCoordinatesOf(). + + Very complex behaviors can be implemented using this framework: auto-selected + objects (no need to press a key to use them), automatic drop-down menus, 3D + GUI, spinners using the wheelEvent(), and whatever your imagination creates. + See the mouseGrabber example for + an illustration. + + Note that ManipulatedFrame are MouseGrabber: see the keyFrame example for an illustration. + Every created ManipulatedFrame is hence present in the MouseGrabberPool() + (note however that ManipulatedCameraFrame are not inserted). + +

Example

+ + Here is for instance a draft version of a MovableObject class. Instances of + these class can freely be moved on screen using the mouse, as movable + post-it-like notes: \code class MovableObject : public MouseGrabber + { + public: + MovableObject() : pos(0,0), moved(false) {} + + void checkIfGrabsMouse(int x, int y, const qglviewer::Camera* const) + { + // MovableObject is active in a region of 5 pixels around its pos. + // May depend on the actual shape of the object. Customize as desired. + // Once clicked (moved = true), it keeps grabbing mouse until button is + released. setGrabsMouse( moved || ((pos-QPoint(x,y)).manhattanLength() < 5) ); + } + + void mousePressEvent( QMouseEvent* const e, Camera* const) { prevPos = + e->pos(); moved = true; } + + void mouseMoveEvent(QMouseEvent* const e, const Camera* const) + { + if (moved) + { + // Add position delta to current pos + pos += e->pos() - prevPos; + prevPos = e->pos(); + } + } + + void mouseReleaseEvent(QMouseEvent* const, Camera* const) { moved = false; } + + void draw() + { + // The object is drawn centered on its pos, with different possible aspects: + if (grabsMouse()) + if (moved) + // Object being moved, maybe a transparent display + else + // Object ready to be moved, maybe a highlighted visual feedback + else + // Normal display + } + + private: + QPoint pos, prevPos; + bool moved; + }; + \endcode + Note that the different event callback methods are called only once the + MouseGrabber grabsMouse(). \nosubgrouping */ +class QGLVIEWER_EXPORT MouseGrabber { +#ifndef DOXYGEN + friend class ::QGLViewer; +#endif + +public: + MouseGrabber(); + /*! Virtual destructor. Removes the MouseGrabber from the MouseGrabberPool(). + */ + virtual ~MouseGrabber() { MouseGrabber::MouseGrabberPool_.removeAll(this); } + + /*! @name Mouse grabbing detection */ + //@{ +public: + /*! Pure virtual method, called by the QGLViewers before they test if the + MouseGrabber grabsMouse(). Should setGrabsMouse() according to the mouse + position. + + This is the core method of the MouseGrabber. It has to be overloaded in your + derived class. Its goal is to update the grabsMouse() flag according to the + mouse and MouseGrabber current positions, using setGrabsMouse(). + + grabsMouse() is usually set to \c true when the mouse cursor is close enough + to the MouseGrabber position. It should also be set to \c false when the mouse + cursor leaves this region in order to release the mouse focus. + + \p x and \p y are the mouse cursor coordinates (Qt coordinate system: (0,0) + corresponds to the upper left corner). + + A typical implementation will look like: + \code + // (posX,posY) is the position of the MouseGrabber on screen. + // Here, distance to mouse must be less than 10 pixels to activate the + MouseGrabber. setGrabsMouse( sqrt((x-posX)*(x-posX) + (y-posY)*(y-posY)) < + 10); \endcode + + If the MouseGrabber position is defined in 3D, use the \p camera parameter, + corresponding to the calling QGLViewer Camera. Project on screen and then + compare the projected coordinates: \code Vec proj = + camera->projectedCoordinatesOf(myMouseGrabber->frame()->position()); + setGrabsMouse((fabs(x-proj.x) < 5) && (fabs(y-proj.y) < 2)); // Rectangular + region \endcode + + See examples in the detailed description section and + in the mouseGrabber example. */ + virtual void checkIfGrabsMouse(int x, int y, const Camera *const camera) = 0; + + /*! Returns \c true when the MouseGrabber grabs the QGLViewer's mouse events. + + This flag is set with setGrabsMouse() by the checkIfGrabsMouse() method. */ + bool grabsMouse() const { return grabsMouse_; } + +protected: + /*! Sets the grabsMouse() flag. Normally used by checkIfGrabsMouse(). */ + void setGrabsMouse(bool grabs) { grabsMouse_ = grabs; } + //@} + + /*! @name MouseGrabber pool */ + //@{ +public: + /*! Returns a list containing pointers to all the active MouseGrabbers. + + Used by the QGLViewer to parse all the MouseGrabbers and to check if any of + them grabsMouse() using checkIfGrabsMouse(). + + 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_; + } + + /*! Returns \c true if the MouseGrabber is currently in the MouseGrabberPool() + list. + + Default value is \c true. When set to \c false using + removeFromMouseGrabberPool(), the QGLViewers no longer checkIfGrabsMouse() on + this MouseGrabber. Use addInMouseGrabberPool() to insert it back. */ + bool isInMouseGrabberPool() const { + return MouseGrabber::MouseGrabberPool_.contains( + const_cast(this)); + } + void addInMouseGrabberPool(); + void removeFromMouseGrabberPool(); + void clearMouseGrabberPool(bool autoDelete = false); + //@} + + /*! @name Mouse event handlers */ + //@{ +protected: + /*! Callback method called when the MouseGrabber grabsMouse() and a mouse + button is pressed. + + + The MouseGrabber will typically start an action or change its state when a + mouse button is pressed. mouseMoveEvent() (called at each mouse displacement) + will then update the MouseGrabber accordingly and mouseReleaseEvent() (called + when the mouse button is released) will terminate this action. + + Use the \p event QMouseEvent::state() and QMouseEvent::button() to test the + keyboard and button state and possibly change the MouseGrabber behavior + accordingly. + + See the detailed description section and the mouseGrabber example for examples. + + See the \c QOpenGLWidget::mousePressEvent() and the \c QMouseEvent + documentations for details. */ + virtual void mousePressEvent(QMouseEvent *const event, Camera *const camera) { + Q_UNUSED(event); + Q_UNUSED(camera); + } + /*! Callback method called when the MouseGrabber grabsMouse() and a mouse + button is double clicked. + + See the \c QOpenGLWidget::mouseDoubleClickEvent() and the \c QMouseEvent + documentations for details. */ + virtual void mouseDoubleClickEvent(QMouseEvent *const event, + Camera *const camera) { + Q_UNUSED(event); + Q_UNUSED(camera); + } + /*! Mouse release event callback method. See mousePressEvent(). */ + virtual void mouseReleaseEvent(QMouseEvent *const event, + Camera *const camera) { + Q_UNUSED(event); + Q_UNUSED(camera); + } + /*! Callback method called when the MouseGrabber grabsMouse() and the mouse is + moved while a button is pressed. + + This method will typically update the state of the MouseGrabber from the mouse + displacement. See the mousePressEvent() documentation for details. */ + virtual void mouseMoveEvent(QMouseEvent *const event, Camera *const camera) { + Q_UNUSED(event); + Q_UNUSED(camera); + } + /*! Callback method called when the MouseGrabber grabsMouse() and the mouse + wheel is used. + + See the \c QOpenGLWidget::wheelEvent() and the \c QWheelEvent documentations + for details. */ + virtual void wheelEvent(QWheelEvent *const event, Camera *const camera) { + Q_UNUSED(event); + Q_UNUSED(camera); + } + //@} + +private: + // Copy constructor and opertor= are declared private and undefined + // Prevents everyone from trying to use them + MouseGrabber(const MouseGrabber &); + MouseGrabber &operator=(const MouseGrabber &); + + bool grabsMouse_; + + // Q G L V i e w e r p o o l + static QList MouseGrabberPool_; +}; + +} // namespace qglviewer + +#endif // QGLVIEWER_MOUSE_GRABBER_H diff --git a/code/3rd_QGLViewer-2.6.3/qglviewer-icon.xpm b/code/3rd_QGLViewer/QGLViewer/qglviewer-icon.xpm similarity index 100% rename from code/3rd_QGLViewer-2.6.3/qglviewer-icon.xpm rename to code/3rd_QGLViewer/QGLViewer/qglviewer-icon.xpm diff --git a/code/3rd_QGLViewer/QGLViewer/qglviewer.cpp b/code/3rd_QGLViewer/QGLViewer/qglviewer.cpp new file mode 100644 index 00000000..8073aa58 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/qglviewer.cpp @@ -0,0 +1,4217 @@ +#include "qglviewer.h" +#include "camera.h" +#include "domUtils.h" +#include "keyFrameInterpolator.h" +#include "manipulatedCameraFrame.h" + +#include +#include +#include +#include +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +# define MidButton MiddleButton +#endif + +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 +screenshot saving, 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() { + // Test OpenGL context + // if (glGetString(GL_VERSION) == 0) + // qWarning("Unable to get OpenGL version, context may not be available - + // Check your configuration"); + + int poolIndex = QGLViewer::QGLViewerPool_.indexOf(nullptr); + setFocusPolicy(Qt::StrongFocus); + + if (poolIndex >= 0) + QGLViewer::QGLViewerPool_.replace(poolIndex, this); + else + QGLViewer::QGLViewerPool_.append(this); + + camera_ = new Camera(); + setCamera(camera()); + + setDefaultShortcuts(); + setDefaultMouseBindings(); + + setSnapshotFileName(tr("snapshot", "Default snapshot file name")); + initializeSnapshotFormats(); + setSnapshotCounter(0); + setSnapshotQuality(95); + + 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_ = nullptr; + manipulatedFrameIsACamera_ = false; + mouseGrabberIsAManipulatedFrame_ = false; + mouseGrabberIsAManipulatedCameraFrame_ = false; + displayMessage_ = false; + connect(&messageTimer_, SIGNAL(timeout()), SLOT(hideMessage())); + messageTimer_.setSingleShot(true); + helpWidget_ = nullptr; + setMouseGrabber(nullptr); + + 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_ = nullptr; + 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); + + tileRegion_ = nullptr; +} + +/*! 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. */ +QGLViewer::QGLViewer(QWidget *parent, Qt::WindowFlags flags) + : QOpenGLWidget(parent, flags) { + defaultConstructor(); +} + +#ifndef DOXYGEN + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +/*! 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, const QGLWidget *shareWidget, + Qt::WindowFlags flags) + : QOpenGLWidget(parent, flags) { + Q_UNUSED(shareWidget) + qWarning("The constructor with a shareWidget is deprecated, use the regular " + "contructor instead."); + defaultConstructor(); +} + +/*! Same as QGLViewer(), but a \c QGLContext can be provided so that viewers +share GL contexts, even with \c QGLContext sub-classes (use \p shareWidget +otherwise). */ +QGLViewer::QGLViewer(QGLContext *context, QWidget *parent, + const QGLWidget *shareWidget, Qt::WindowFlags flags) + : QOpenGLWidget(parent, flags) { + Q_UNUSED(context) + Q_UNUSED(shareWidget) + qWarning("The constructor with a QGLContext is deprecated, use the regular " + "contructor instead."); + defaultConstructor(); +} + +/*! Same as QGLViewer(), but a specific \c QGLFormat can be provided. + +This is for instance needed to ask for a stencil buffer or for stereo display +(as is illustrated in the stereoViewer +example). */ +QGLViewer::QGLViewer(const QGLFormat &format, QWidget *parent, + const QGLWidget *shareWidget, Qt::WindowFlags flags) + : QOpenGLWidget(parent, flags) { + Q_UNUSED(format) + Q_UNUSED(shareWidget) + qWarning("The constructor with a QGLFormat is deprecated, use the regular " + "contructor instead."); + defaultConstructor(); +} +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#endif // DOXYGEN + +/*! Virtual destructor. + +The viewer is replaced by \c nullptr 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), + nullptr); + + 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 or +to create display lists. + +To make beginners' life easier and to simplify the examples, this method +slightly modifies the standard OpenGL state: \code glEnable(GL_LIGHT0); +glEnable(GL_LIGHTING); +glEnable(GL_DEPTH_TEST); +glEnable(GL_COLOR_MATERIAL); +\endcode + +If you port an existing application to QGLViewer and your display changes, you +probably want to disable these flags in init() to get back to a standard OpenGL +state. */ +void QGLViewer::initializeGL() { + glEnable(GL_LIGHT0); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + glEnable(GL_COLOR_MATERIAL); + + // Default colors + setForegroundColor(QColor(180, 180, 180)); + setBackgroundColor(QColor(51, 51, 51)); + + // Clear the buffer where we're going to draw + if (format().stereo()) { + glDrawBuffer(GL_BACK_RIGHT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDrawBuffer(GL_BACK_LEFT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } else + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Calls user defined method. Default emits a signal. + init(); + + // Give time to glInit to finish and then call setFullScreen(). + if (isFullScreen()) + QTimer::singleShot(100, this, SLOT(delayedFullScreen())); +} + +/*! Main paint method, inherited from \c QOpenGLWidget. + +Calls the following methods, in that order: +\arg preDraw() (or preDrawStereo() if viewer displaysInStereo()) : places the +camera in the world coordinate system. \arg draw() (or fastDraw() when the +camera is manipulated) : main drawing method. Should be overloaded. \arg +postDraw() : display of visual hints (world axis, FPS...) */ +void QGLViewer::paintGL() { + if (displaysInStereo()) { + for (int view = 1; view >= 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() { + // Reset model view matrix to world coordinates origin + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + camera()->loadModelViewMatrix(); + // TODO restore model loadProjectionMatrixStereo + + // Save OpenGL state + glPushAttrib(GL_ALL_ATTRIB_BITS); + + // Set neutral GL state + glDisable(GL_TEXTURE_1D); + glDisable(GL_TEXTURE_2D); +#ifdef GL_TEXTURE_3D // OpenGL 1.2 Only... + glDisable(GL_TEXTURE_3D); +#endif + + glDisable(GL_TEXTURE_GEN_Q); + glDisable(GL_TEXTURE_GEN_R); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + +#ifdef GL_RESCALE_NORMAL // OpenGL 1.2 Only... + glEnable(GL_RESCALE_NORMAL); +#endif + + glDisable(GL_COLOR_MATERIAL); + glColor4f(foregroundColor().redF(), foregroundColor().greenF(), + foregroundColor().blueF(), foregroundColor().alphaF()); + + if (cameraIsEdited()) + camera()->drawAllPaths(); + + // Pivot point, line when camera rolls, zoom region + drawVisualHints(); + + if (gridIsDrawn()) { + glLineWidth(1.0); + drawGrid(camera()->sceneRadius()); + } + if (axisIsDrawn()) { + glLineWidth(2.0); + drawAxis(camera()->sceneRadius()); + } + + // 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(SAVE_SCREENSHOT, Qt::CTRL | Qt::Key_S); + 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); + setShortcut(SNAPSHOT_TO_CLIPBOARD, Qt::CTRL | Qt::Key_C); + + keyboardActionDescription_[DISPLAY_FPS] = + tr("Toggles the display of the FPS", "DISPLAY_FPS action description"); + keyboardActionDescription_[SAVE_SCREENSHOT] = + tr("Saves a screenshot", "SAVE_SCREENSHOT 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"); + keyboardActionDescription_[SNAPSHOT_TO_CLIPBOARD] = + tr("Copies a snapshot to clipboard", + "SNAPSHOT_TO_CLIPBOARD 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 = static_cast(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 nullptr \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; + + // Disconnect current camera from this viewer. + disconnect(this->camera()->frame(), SIGNAL(manipulated()), this, + SLOT(update())); + disconnect(this->camera()->frame(), SIGNAL(spun()), this, SLOT(update())); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) // The screen() function was introduced in the QWidget class starting from Qt 5.14. + disconnect(screen(), SIGNAL(physicalDotsPerInchChanged(qreal)), this->camera(), SLOT(setDevicePixelRatio(qreal))); +#endif + connectAllCameraKFIInterpolatedSignals(false); + + camera_ = camera; + + camera->setSceneRadius(sceneRadius()); + camera->setSceneCenter(sceneCenter()); + camera->setScreenWidthAndHeight(width(), height()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) // The screen() function was introduced in the QWidget class starting from Qt 5.14. + camera->setDevicePixelRatio(screen()->devicePixelRatio()); +#else + camera->setDevicePixelRatio(devicePixelRatioF()); +#endif + + // Connect camera frame to this viewer. + connect(camera->frame(), SIGNAL(manipulated()), SLOT(update())); + connect(camera->frame(), SIGNAL(spun()), SLOT(update())); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) // The screen() function was introduced in the QWidget class starting from Qt 5.14. + connect(screen(), SIGNAL(physicalDotsPerInchChanged(qreal)), camera, SLOT(setDevicePixelRatio(qreal))); +#endif + 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 { + static GLUquadric *quadric = gluNewQuadric(); + + const qreal length = sceneRadius() / 5.0 * scale; + + GLboolean lightIsOn; + glGetBooleanv(light, &lightIsOn); + + if (lightIsOn) { + // All light values are given in eye coordinates + glPushMatrix(); + glLoadIdentity(); + + float color[4]; + glGetLightfv(light, GL_DIFFUSE, color); + glColor4fv(color); + + float pos[4]; + glGetLightfv(light, GL_POSITION, pos); + + if (static_cast(pos[3]) != 0.0) { + glTranslatef(pos[0] / pos[3], pos[1] / pos[3], pos[2] / pos[3]); + + GLfloat cutOff; + glGetLightfv(light, GL_SPOT_CUTOFF, &cutOff); + if (static_cast(cutOff) != 180.0) { + GLfloat dir[4]; + glGetLightfv(light, GL_SPOT_DIRECTION, dir); + glMultMatrixd(Quaternion(Vec(0, 0, 1), Vec(dir)).matrix()); + QGLViewer::drawArrow(length); + gluCylinder(quadric, 0.0, 0.7 * length * sin(static_cast(cutOff) * M_PI / 180.0), + 0.7 * length * cos(static_cast(cutOff) * M_PI / 180.0), 12, 1); + } else + gluSphere(quadric, 0.2 * length, 10, 10); + } else { + // Directional light. + Vec dir(static_cast(pos[0]), static_cast(pos[1]), static_cast(pos[2])); + dir.normalize(); + Frame fr = + Frame(camera()->cameraCoordinatesOf( + 4.0 * length * camera()->frame()->inverseTransformOf(dir)), + Quaternion(Vec(0, 0, -1), dir)); + glMultMatrixd(fr.matrix()); + drawArrow(length); + } + + glPopMatrix(); + } +} + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) +void QGLViewer::renderText(int x, int y, const QString &str, + const QFont &font) { + // Retrieve last OpenGL color to use as a font color + GLdouble glColor[4]; + glGetDoublev(GL_CURRENT_COLOR, glColor); + QColor fontColor = QColor(255 * glColor[0], 255 * glColor[1], + 255 * glColor[2], 255 * glColor[3]); + + // 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; + + if (tileRegion_ != nullptr) { + renderText(int((x - tileRegion_->xMin) * width() / + (tileRegion_->xMax - tileRegion_->xMin)), + int((y - tileRegion_->yMin) * height() / + (tileRegion_->yMax - tileRegion_->yMin)), + text, scaledFont(fnt)); + } else + 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 upward) const { + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + if (tileRegion_ != nullptr) + if (upward) + glOrtho(tileRegion_->xMin, tileRegion_->xMax, tileRegion_->yMin, + tileRegion_->yMax, 0.0, -1.0); + else + glOrtho(tileRegion_->xMin, tileRegion_->xMax, tileRegion_->yMax, + tileRegion_->yMin, 0.0, -1.0); + else if (upward) + glOrtho(0, width(), 0, height(), 0.0, -1.0); + else + glOrtho(0, width(), height(), 0, 0.0, -1.0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); +} + +/*! 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 { + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +/*! 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(). + +The default implementation uses the \c GL_SELECT mode to perform a selection. It +uses selectBuffer() and selectBufferSize() to define a \c glSelectBuffer(). The +\c GL_PROJECTION is then set using \c gluPickMatrix(), with a window selection +size defined by selectRegionWidth() and selectRegionHeight(). Finally, the \c +GL_MODELVIEW matrix is set to the world coordinate system using +qglviewer::Camera::loadModelViewMatrix(). See the gluPickMatrix() documentation +for details. + +You should not need to redefine this method (if you use the \c GL_SELECT mode to +perform your selection), since this code is fairly classical and can be tuned. +You are more likely to overload endSelection() if you want to use a more complex +select buffer structure. */ +void QGLViewer::beginSelection(const QPoint &point) { + // Make OpenGL context current (may be needed with several viewers ?) + makeCurrent(); + + // Prepare the selection mode + glSelectBuffer(selectBufferSize(), selectBuffer()); + glRenderMode(GL_SELECT); + glInitNames(); + + // Loads the matrices + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + static GLint viewport[4]; + camera()->getViewport(viewport); + gluPickMatrix(point.x(), point.y(), selectRegionWidth(), selectRegionHeight(), + viewport); + + // loadProjectionMatrix() first resets the GL_PROJECTION matrix with a + // glLoadIdentity(). The false parameter prevents this and hence multiplies + // the matrices. + camera()->loadProjectionMatrix(false); + // Reset the original (world coordinates) modelview matrix + camera()->loadModelViewMatrix(); +} + +/*! This method is called by select() after scene elements were drawn by +drawWithNames(). It should analyze the selection result to determine which +object is actually selected. + +The default implementation relies on \c GL_SELECT mode (see beginSelection()). +It assumes that names were pushed and popped in drawWithNames(), and analyzes +the selectBuffer() to find the name that corresponds to the closer (z min) +object. It then setSelectedName() to this value, or to -1 if the selectBuffer() +is empty (no object drawn in selection region). Use selectedName() (probably in +the postSelection() method) to retrieve this value and update your data +structure accordingly. + +This default implementation, although sufficient for many cases is however +limited and you may have to overload this method. This will be the case if +drawWithNames() uses several push levels in the name heap. A more precise depth +selection, for instance privileging points over edges and triangles to avoid z +precision problems, will also require an overloading. A typical implementation +will look like: +\code +glFlush(); + +// Get the number of objects that were seen through the pick matrix frustum. +// Resets GL_RENDER mode. +GLint nbHits = glRenderMode(GL_RENDER); + +if (nbHits <= 0) +setSelectedName(-1); +else +{ +// Interpret results: each object created values in the selectBuffer(). +// See the glSelectBuffer() man page for details on the buffer structure. +// The following code depends on your selectBuffer() structure. +for (int i=0; imultiSelect example for +a multi-object selection implementation of this method. */ +void QGLViewer::endSelection(const QPoint &point) { + Q_UNUSED(point) + + // Flush GL buffers + glFlush(); + + // Get the number of objects that were seen through the pick matrix frustum. + // Reset GL_RENDER mode. + GLint nbHits = glRenderMode(GL_RENDER); + + if (nbHits <= 0) + setSelectedName(-1); + else { + // Interpret results: each object created 4 values in the selectBuffer(). + // selectBuffer[4*i+1] is the object minimum depth value, while + // selectBuffer[4*i+3] is the id pushed on the stack. Of all the objects + // that were projected in the pick region, we select the closest one (zMin + // comparison). This code needs to be modified if you use several stack + // levels. See glSelectBuffer() man page. + GLuint zMin = (selectBuffer())[1]; + setSelectedName(int((selectBuffer())[3])); + for (int i = 1; i < nbHits; ++i) + if ((selectBuffer())[4 * i + 1] < zMin) { + zMin = (selectBuffer())[4 * i + 1]; + setSelectedName(int((selectBuffer())[4 * i + 3])); + } + } +} + +/*! 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: + makeCurrent(); + 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, + static_cast((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(nullptr); + 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(nullptr); + // 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, + static_cast(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) != nullptr); + mouseGrabberIsAManipulatedCameraFrame_ = + ((dynamic_cast(mouseGrabber) != nullptr) && + (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(); + 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(); +} + +QString QGLViewer::clickActionString(QGLViewer::ClickAction ca) { + switch (ca) { + case QGLViewer::NO_CLICK_ACTION: + return QString(); + 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(); +} + +static QString keyString(unsigned int key) { + return QKeySequence(int(key)).toString(QKeySequence::NativeText); +} + +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(); + + QVector keys; + keys.reserve(pathIndex_.count()); + for (QMap::ConstIterator i = pathIndex_.begin(), + endi = pathIndex_.end(); + i != endi; ++i) + keys.push_back(i.key()); + std::sort(keys.begin(), keys.end()); + + 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 nullptr parent... + helpWidget_ = new QTabWidget(nullptr); + helpWidget()->setWindowTitle(tr("Help", "Help window title")); + + resize = true; + for (int i = 0; i < 4; ++i) { + QTextEdit *tab = new QTextEdit(nullptr); + 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("2023") + .arg("http://www.libqglviewer.com") + + QString("
"); + break; + default: + break; + } + + QTextEdit *textEdit = static_cast(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 QElapsedTimer 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) == nullptr); + 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 SAVE_SCREENSHOT: + saveSnapshot(false, false); + break; + case FULL_SCREEN: + toggleFullScreen(); + break; + case STEREO: + toggleStereoDisplay(); + break; + case ANIMATION: + toggleAnimation(); + break; + case HELP: + help(); + break; + case EDIT_CAMERA: + toggleCameraIsEdited(); + break; + case SNAPSHOT_TO_CLIPBOARD: + snapshotToClipboard(); + 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 static_cast(it.key().modifiers) | static_cast(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()) != nullptr)); + + 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() { + // Pivot point cross + if (visualHint_ & 1) { + const qreal size = 15.0; + Vec proj = camera()->projectedCoordinatesOf(camera()->pivotPoint()); + startScreenCoordinatesSystem(); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glLineWidth(3.0); + glBegin(GL_LINES); + glVertex2d(proj.x - size, proj.y); + glVertex2d(proj.x + size, proj.y); + glVertex2d(proj.x, proj.y - size); + glVertex2d(proj.x, proj.y + size); + glEnd(); + glEnable(GL_DEPTH_TEST); + stopScreenCoordinatesSystem(); + } + + // if (visualHint_ & 2) + // drawText(80, 10, "Play"); + + // Screen rotate line + ManipulatedFrame *mf = nullptr; + Vec pnt; + if (camera()->frame()->action_ == SCREEN_ROTATE) { + mf = camera()->frame(); + pnt = camera()->pivotPoint(); + } + if (manipulatedFrame() && (manipulatedFrame()->action_ == SCREEN_ROTATE)) { + mf = manipulatedFrame(); + // Maybe useful if the mf is a manipCameraFrame... + // pnt = manipulatedFrame()->pivotPoint(); + pnt = manipulatedFrame()->position(); + } + + if (mf) { + pnt = camera()->projectedCoordinatesOf(pnt); + startScreenCoordinatesSystem(); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glLineWidth(3.0); + glBegin(GL_LINES); + glVertex2d(pnt.x, pnt.y); + glVertex2i(mf->prevPos_.x(), mf->prevPos_.y()); + glEnd(); + glEnable(GL_DEPTH_TEST); + stopScreenCoordinatesSystem(); + } + + // Zoom on region: draw a rectangle + if (camera()->frame()->action_ == ZOOM_ON_REGION) { + startScreenCoordinatesSystem(); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glLineWidth(2.0); + glBegin(GL_LINE_LOOP); + glVertex2i(camera()->frame()->pressPos_.x(), + camera()->frame()->pressPos_.y()); + glVertex2i(camera()->frame()->prevPos_.x(), + camera()->frame()->pressPos_.y()); + glVertex2i(camera()->frame()->prevPos_.x(), + camera()->frame()->prevPos_.y()); + glVertex2i(camera()->frame()->pressPos_.x(), + camera()->frame()->prevPos_.y()); + glEnd(); + glEnable(GL_DEPTH_TEST); + stopScreenCoordinatesSystem(); + } +} + +/*! 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 along the positive Z axis. + +\p length, \p radius and \p nbSubdivisions define its geometry. If \p radius is +negative (default), it is set to 0.05 * \p length. + +Use drawArrow(const Vec& from, const Vec& to, qreal radius, int nbSubdivisions) +or change the \c ModelView matrix to place the arrow in 3D. + +Uses current color and does not modify the OpenGL state. */ +void QGLViewer::drawArrow(qreal length, qreal radius, int nbSubdivisions) { + static GLUquadric *quadric = gluNewQuadric(); + + if (radius < 0.0) + radius = 0.05 * length; + + const qreal head = 2.5 * (radius / length) + 0.1; + const qreal coneRadiusCoef = 4.0 - 5.0 * head; + + gluCylinder(quadric, radius, radius, length * (1.0 - head / coneRadiusCoef), + nbSubdivisions, 1); + glTranslated(0.0, 0.0, length * (1.0 - head)); + gluCylinder(quadric, coneRadiusCoef * radius, 0.0, head * length, + nbSubdivisions, 1); + glTranslated(0.0, 0.0, -length * (1.0 - head)); +} + +/*! Draws a 3D arrow between the 3D point \p from and the 3D point \p to, both +defined in the current ModelView coordinates system. + +See drawArrow(qreal length, qreal radius, int nbSubdivisions) for details. */ +void QGLViewer::drawArrow(const Vec &from, const Vec &to, qreal radius, + int nbSubdivisions) { + glPushMatrix(); + glTranslated(from[0], from[1], from[2]); + const Vec dir = to - from; + glMultMatrixd(Quaternion(Vec(0, 0, 1), dir).matrix()); + QGLViewer::drawArrow(dir.norm(), radius, nbSubdivisions); + glPopMatrix(); +} + +/*! Draws an XYZ axis, with a given size (default is 1.0). + +The axis position and 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. + +Use the following code to display the current position and orientation of a +qglviewer::Frame: \code glPushMatrix(); glMultMatrixd(frame.matrix()); +QGLViewer::drawAxis(sceneRadius() / 5.0); // Or any scale +glPopMatrix(); +\endcode + +The current color and line width are used to draw the X, Y and Z characters at +the extremities of the three arrows. The OpenGL state is not modified by this +method. + +axisIsDrawn() uses this method to draw a representation of the world coordinate +system. See also QGLViewer::drawArrow() and QGLViewer::drawGrid(). */ +void QGLViewer::drawAxis(qreal length) { + GLboolean lighting, colorMaterial; + glGetBooleanv(GL_LIGHTING, &lighting); + glGetBooleanv(GL_COLOR_MATERIAL, &colorMaterial); + + if (!lighting) + glEnable(GL_LIGHTING); + if (colorMaterial) + glDisable(GL_COLOR_MATERIAL); + + // X + float color[4]; + //color[0] = 1.0f; color[1] = 0.3f; color[2] = 0.3f; color[3] = 1.0f; + color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 1.0f; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + glPushMatrix(); + glRotatef(90.0, 0.0, 1.0, 0.0); + QGLViewer::drawArrow(length, 0.03*length); + glPopMatrix(); + + // Y + //color[0] = 0.3f; color[1] = 1.0f; color[2] = 0.3f; color[3] = 1.0f; + color[0] = 0.0f; color[1] = 1.0f; color[2] = 0.0f; color[3] = 1.0f; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + glPushMatrix(); + glRotatef(-90.0, 1.0, 0.0, 0.0); + QGLViewer::drawArrow(length, 0.03*length); + glPopMatrix(); + + // Z + //color[0] = 0.3f; color[1] = 0.3f; color[2] = 1.0f; color[3] = 1.0f; + color[0] = 0.0f; color[1] = 0.0f; color[2] = 1.0f; color[3] = 1.0f; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + QGLViewer::drawArrow(length, 0.03*length); + + if (colorMaterial) + glEnable(GL_COLOR_MATERIAL); + if (!lighting) + glDisable(GL_LIGHTING); + + + // const qreal charWidth = length / 40.0; + // const qreal charHeight = length / 30.0; + // const qreal charShift = 1.04 * length; + // + // GLboolean lighting, colorMaterial; + // glGetBooleanv(GL_LIGHTING, &lighting); + // glGetBooleanv(GL_COLOR_MATERIAL, &colorMaterial); + // + // glDisable(GL_LIGHTING); + // + // glBegin(GL_LINES); + // // The X + // glVertex3d(charShift, charWidth, -charHeight); + // glVertex3d(charShift, -charWidth, charHeight); + // glVertex3d(charShift, -charWidth, -charHeight); + // glVertex3d(charShift, charWidth, charHeight); + // // The Y + // glVertex3d(charWidth, charShift, charHeight); + // glVertex3d(0.0, charShift, 0.0); + // glVertex3d(-charWidth, charShift, charHeight); + // glVertex3d(0.0, charShift, 0.0); + // glVertex3d(0.0, charShift, 0.0); + // glVertex3d(0.0, charShift, -charHeight); + // // The Z + // glVertex3d(-charWidth, charHeight, charShift); + // glVertex3d(charWidth, charHeight, charShift); + // glVertex3d(charWidth, charHeight, charShift); + // glVertex3d(-charWidth, -charHeight, charShift); + // glVertex3d(-charWidth, -charHeight, charShift); + // glVertex3d(charWidth, -charHeight, charShift); + // glEnd(); + // + // glEnable(GL_LIGHTING); + // glDisable(GL_COLOR_MATERIAL); + // + // float color[4]; + // color[0] = 0.7f; + // color[1] = 0.7f; + // color[2] = 1.0f; + // color[3] = 1.0f; + // glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + // QGLViewer::drawArrow(length, 0.01 * length); + // + // color[0] = 1.0f; + // color[1] = 0.7f; + // color[2] = 0.7f; + // color[3] = 1.0f; + // glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + // glPushMatrix(); + // glRotatef(90.0f, 0.0f, 1.0f, 0.0f); + // QGLViewer::drawArrow(length, 0.01 * length); + // glPopMatrix(); + // + // color[0] = 0.7f; + // color[1] = 1.0f; + // color[2] = 0.7f; + // color[3] = 1.0f; + // glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + // glPushMatrix(); + // glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); + // QGLViewer::drawArrow(length, 0.01 * length); + // glPopMatrix(); + // + // if (colorMaterial) + // glEnable(GL_COLOR_MATERIAL); + // if (!lighting) + // glDisable(GL_LIGHTING); +} + +/*! 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. Set the \c +GL_MODELVIEW matrix to place and orientate the grid in 3D space (see the +drawAxis() documentation). + +The OpenGL state is not modified by this method. */ +void QGLViewer::drawGrid(qreal size, int nbSubdivisions) { + GLboolean lighting; + glGetBooleanv(GL_LIGHTING, &lighting); + + glDisable(GL_LIGHTING); + + glBegin(GL_LINES); + for (int i = 0; i <= nbSubdivisions; ++i) { + const qreal pos = size * (2.0 * i / nbSubdivisions - 1.0); + glVertex2d(pos, -size); + glVertex2d(pos, +size); + glVertex2d(-size, pos); + glVertex2d(size, pos); + } + glEnd(); + + if (lighting) + glEnable(GL_LIGHTING); +} + +//////////////////////////////////////////////////////////////////////////////// +// 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 + +/*! Makes a copy of the current buffer into a texture. + +Creates a texture (when needed) and uses glCopyTexSubImage2D() to directly copy +the buffer in it. + +Use \p internalFormat and \p format to define the texture format and hence which +and how components of the buffer are copied into the texture. See the +glTexImage2D() documentation for details. + +When \p format is c GL_NONE (default), its value is set to \p internalFormat, +which fits most cases. Typical \p internalFormat (and \p format) values are \c +GL_DEPTH_COMPONENT and \c GL_RGBA. Use \c GL_LUMINANCE as the \p internalFormat +and \c GL_RED, \c GL_GREEN or \c GL_BLUE as \p format to capture a single color +component as a luminance (grey scaled) value. Note that \c GL_STENCIL is not +supported as a format. + +The texture has dimensions which are powers of two. It is as small as possible +while always being larger or equal to the current size of the widget. The buffer +image hence does not entirely fill the texture: it is stuck to the lower left +corner (corresponding to the (0,0) texture coordinates). Use bufferTextureMaxU() +and bufferTextureMaxV() to get the upper right corner maximum u and v texture +coordinates. Use bufferTextureId() to retrieve the id of the created texture. + +Here is how to display a grey-level image of the z-buffer: +\code +copyBufferToTexture(GL_DEPTH_COMPONENT); + +glMatrixMode(GL_TEXTURE); +glLoadIdentity(); + +glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +glEnable(GL_TEXTURE_2D); + +startScreenCoordinatesSystem(true); + +glBegin(GL_QUADS); +glTexCoord2f(0.0, 0.0); glVertex2i(0, 0); +glTexCoord2f(bufferTextureMaxU(), 0.0); glVertex2i(width(), 0); +glTexCoord2f(bufferTextureMaxU(), bufferTextureMaxV()); glVertex2i(width(), +height()); glTexCoord2f(0.0, bufferTextureMaxV()); glVertex2i(0, +height()); glEnd(); + +stopScreenCoordinatesSystem(); + +glDisable(GL_TEXTURE_2D); +\endcode + +Use glReadBuffer() to select which buffer is copied into the texture. See also +\c glPixelTransfer(), \c glPixelZoom() and \c glCopyPixel() for pixel color +transformations during copy. + +Call makeCurrent() before this method to make the OpenGL context active if +needed. + +\note The \c GL_DEPTH_COMPONENT format may not be supported by all hardware. It +may sometimes be emulated in software, resulting in poor performances. + +\note The bufferTextureId() texture is binded at the end of this method. */ +void QGLViewer::copyBufferToTexture(GLint internalFormat, GLenum format) { + int h = 16; + int w = 16; + // Todo compare performance with qt code. + while (w < width()) + w <<= 1; + while (h < height()) + h <<= 1; + + bool init = false; + + if ((w != bufferTextureWidth_) || (h != bufferTextureHeight_)) { + bufferTextureWidth_ = w; + bufferTextureHeight_ = h; + bufferTextureMaxU_ = width() / qreal(bufferTextureWidth_); + bufferTextureMaxV_ = height() / qreal(bufferTextureHeight_); + init = true; + } + + if (bufferTextureId() == 0) { + glGenTextures(1, &bufferTextureId_); + glBindTexture(GL_TEXTURE_2D, bufferTextureId_); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + init = true; + } else + glBindTexture(GL_TEXTURE_2D, bufferTextureId_); + + if ((format != previousBufferTextureFormat_) || + (internalFormat != previousBufferTextureInternalFormat_)) { + previousBufferTextureFormat_ = format; + previousBufferTextureInternalFormat_ = internalFormat; + init = true; + } + + if (init) { + if (format == GL_NONE) + format = GLenum(internalFormat); + + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, bufferTextureWidth_, + bufferTextureHeight_, 0, format, GL_UNSIGNED_BYTE, nullptr); + } + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width(), height()); +} + +/*! 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 { + if (glIsTexture(bufferTextureId_)) + return bufferTextureId_; + else + return 0; +} diff --git a/code/3rd_QGLViewer/QGLViewer/qglviewer.h b/code/3rd_QGLViewer/QGLViewer/qglviewer.h new file mode 100644 index 00000000..0b276214 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/qglviewer.h @@ -0,0 +1,1450 @@ +#ifndef QGLVIEWER_QGLVIEWER_H +#define QGLVIEWER_QGLVIEWER_H + +#include "camera.h" +#include "../basic/canvas.h" + +#include +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +# include +#endif +#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 + +It features many classical viewer functionalities, such as a camera trackball, +manipulated objects, snapshot saving and much more. Its main goal is to ease the development of +new 3D applications. + +New users should read the introduction page +to get familiar with important notions such as sceneRadius(), sceneCenter() and +the world coordinate system. Try the numerous simple examples to discover the possibilities and +understand how it works. + +

Usage

+ +To use a QGLViewer, derive you viewer class from the QGLViewer and overload its +draw() virtual method. See the simpleViewer example for an +illustration. + +An other option is to connect your drawing methods to the signals emitted by the +QGLViewer (Qt's callback mechanism). See the callback example for a complete +implementation. + +\nosubgrouping */ +class QGLVIEWER_EXPORT QGLViewer : public QOpenGLWidget, public Canvas { + Q_OBJECT + +public: + explicit QGLViewer(QWidget *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + explicit QGLViewer(QWidget *parent, const QGLWidget *shareWidget, + Qt::WindowFlags flags = 0); + explicit QGLViewer(QGLContext *context, QWidget *parent = 0, + const QGLWidget *shareWidget = 0, + Qt::WindowFlags flags = 0); + explicit QGLViewer(const QGLFormat &format, QWidget *parent = 0, + const QGLWidget *shareWidget = 0, + Qt::WindowFlags flags = 0); +#endif + virtual ~QGLViewer(); + + /*! @name Display of visual hints */ + //@{ +public: + /*! Returns \c true if the world axis is drawn by the viewer. + + Set by setAxisIsDrawn() or toggleAxisIsDrawn(). Default value is \c false. */ + bool axisIsDrawn() const { return axisIsDrawn_; } + /*! Returns \c true if a XY grid is drawn by the viewer. + + Set by setGridIsDrawn() or toggleGridIsDrawn(). Default value is \c false. */ + bool gridIsDrawn() const { return gridIsDrawn_; } + /*! Returns \c true if the viewer displays the current frame rate (Frames Per + Second). + + Use QApplication::setFont() to define the display font (see drawText()). + + Set by setFPSIsDisplayed() or toggleFPSIsDisplayed(). Use currentFPS() to get + the current FPS. Default value is \c false. */ + bool FPSIsDisplayed() const { return FPSIsDisplayed_; } + /*! Returns \c true if text display (see drawText()) is enabled. + + Set by setTextIsEnabled() or toggleTextIsEnabled(). This feature conveniently + removes all the possibly displayed text, cleaning display. Default value is \c + true. */ + bool textIsEnabled() const { return textIsEnabled_; } + + /*! Returns \c true if the camera() is being edited in the viewer. + + Set by setCameraIsEdited() or toggleCameraIsEdited(). Default value is \p + false. + + The current implementation is limited: the defined camera() paths (see + qglviewer::Camera::keyFrameInterpolator()) are simply displayed using + qglviewer::Camera::drawAllPaths(). Actual camera and path edition will be + implemented in the future. */ + bool cameraIsEdited() const { return cameraIsEdited_; } + +public Q_SLOTS: + /*! Sets the state of axisIsDrawn(). Emits the axisIsDrawnChanged() signal. + * See also toggleAxisIsDrawn(). */ + void setAxisIsDrawn(bool draw = true) { + axisIsDrawn_ = draw; + Q_EMIT axisIsDrawnChanged(draw); + update(); + } + /*! Sets the state of gridIsDrawn(). Emits the gridIsDrawnChanged() signal. + * See also toggleGridIsDrawn(). */ + void setGridIsDrawn(bool draw = true) { + gridIsDrawn_ = draw; + Q_EMIT gridIsDrawnChanged(draw); + update(); + } + /*! Sets the state of FPSIsDisplayed(). Emits the FPSIsDisplayedChanged() + * signal. See also toggleFPSIsDisplayed(). */ + void setFPSIsDisplayed(bool display = true) { + FPSIsDisplayed_ = display; + Q_EMIT FPSIsDisplayedChanged(display); + update(); + } + /*! Sets the state of textIsEnabled(). Emits the textIsEnabledChanged() + * signal. See also toggleTextIsEnabled(). */ + void setTextIsEnabled(bool enable = true) { + textIsEnabled_ = enable; + Q_EMIT textIsEnabledChanged(enable); + update(); + } + void setCameraIsEdited(bool edit = true); + + /*! Toggles the state of axisIsDrawn(). See also setAxisIsDrawn(). */ + void toggleAxisIsDrawn() { setAxisIsDrawn(!axisIsDrawn()); } + /*! Toggles the state of gridIsDrawn(). See also setGridIsDrawn(). */ + void toggleGridIsDrawn() { setGridIsDrawn(!gridIsDrawn()); } + /*! Toggles the state of FPSIsDisplayed(). See also setFPSIsDisplayed(). */ + void toggleFPSIsDisplayed() { setFPSIsDisplayed(!FPSIsDisplayed()); } + /*! Toggles the state of textIsEnabled(). See also setTextIsEnabled(). */ + void toggleTextIsEnabled() { setTextIsEnabled(!textIsEnabled()); } + /*! Toggles the state of cameraIsEdited(). See also setCameraIsEdited(). */ + void toggleCameraIsEdited() { setCameraIsEdited(!cameraIsEdited()); } + //@} + + /*! @name Viewer's colors */ + //@{ +public: + /*! Returns the background color of the viewer. + + This method is provided for convenience since the background color is an + OpenGL state variable set with \c glClearColor(). However, this internal + representation has the advantage that it is saved (resp. restored) with + saveStateToFile() (resp. restoreStateFromFile()). + + Use setBackgroundColor() to define and activate a background color. + + \attention Each QColor component is an integer ranging from 0 to 255. This + differs from the qreal values used by \c glClearColor() which are in the + 0.0-1.0 range. Default value is (51, 51, 51) (dark gray). You may have to + change foregroundColor() accordingly. + + \attention This method does not return the current OpenGL clear color as \c + glGet() does. Instead, it returns the QGLViewer internal variable. If you + directly use \c glClearColor() or \c qglClearColor() instead of + setBackgroundColor(), the two results will differ. */ + QColor backgroundColor() const { return backgroundColor_; } + + /*! Returns the foreground color used by the viewer. + + This color is used when FPSIsDisplayed(), gridIsDrawn(), to display the camera + paths when the cameraIsEdited(). + + \attention Each QColor component is an integer in the range 0-255. This + differs from the qreal values used by \c glColor3f() which are in the range + 0-1. Default value is (180, 180, 180) (light gray). + + Use \c qglColor(foregroundColor()) to set the current OpenGL color to the + foregroundColor(). + + See also backgroundColor(). */ + QColor foregroundColor() const { return foregroundColor_; } +public Q_SLOTS: + /*! Sets the backgroundColor() of the viewer and calls \c qglClearColor(). See + also setForegroundColor(). */ + void setBackgroundColor(const QColor &color) { + backgroundColor_ = color; + glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); + } + /*! Sets the foregroundColor() of the viewer, used to draw visual hints. See + * also setBackgroundColor(). */ + void setForegroundColor(const QColor &color) { foregroundColor_ = color; } + //@} + + /*! @name Scene dimensions */ + //@{ +public: + /*! Returns the scene radius. + + The entire displayed scene should be included in a sphere of radius + sceneRadius(), centered on sceneCenter(). + + This approximate value is used by the camera() to set + qglviewer::Camera::zNear() and qglviewer::Camera::zFar(). It is also used to + showEntireScene() or to scale the world axis display.. + + Default value is 1.0. This method is equivalent to camera()->sceneRadius(). + See setSceneRadius(). */ + qreal sceneRadius() const { return camera()->sceneRadius(); } + /*! Returns the scene center, defined in world coordinates. + + See sceneRadius() for details. + + Default value is (0,0,0). Simply a wrapper for camera()->sceneCenter(). Set + using setSceneCenter(). + + Do not mismatch this value (that only depends on the scene) with the + qglviewer::Camera::pivotPoint(). */ + qglviewer::Vec sceneCenter() const { return camera()->sceneCenter(); } + +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); + } + + /*! 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); + } + + /*! Convenient way to call setSceneCenter() and setSceneRadius() from a (world + axis aligned) bounding box of the scene. + + This is equivalent to: + \code + setSceneCenter((min+max) / 2.0); + setSceneRadius((max-min).norm() / 2.0); + \endcode */ + void setSceneBoundingBox(const qglviewer::Vec &min, + const qglviewer::Vec &max) { + camera()->setSceneBoundingBox(min, max); + } + + /*! Moves the camera so that the entire scene is visible. + + Simple wrapper around qglviewer::Camera::showEntireScene(). */ + void showEntireScene() { + camera()->showEntireScene(); + update(); + } + //@} + + /*! @name Associated objects */ + //@{ +public: + /*! Returns the associated qglviewer::Camera, never \c nullptr. */ + qglviewer::Camera *camera() const { return camera_; } + + CameraBase* get_camera() const { return camera_; } + + /*! Returns the viewer's qglviewer::ManipulatedFrame. + + This qglviewer::ManipulatedFrame can be moved with the mouse when the + associated mouse bindings are used (default is when pressing the \c Control + key with any mouse button). Use setMouseBinding() to define new bindings. + + See the manipulatedFrame + example for a complete implementation. + + Default value is \c nullptr, meaning that no qglviewer::ManipulatedFrame is set. +*/ + qglviewer::ManipulatedFrame *manipulatedFrame() const { + return manipulatedFrame_; + } + +public Q_SLOTS: + void setCamera(qglviewer::Camera *const camera); + void setManipulatedFrame(qglviewer::ManipulatedFrame *frame); + //@} + + /*! @name Mouse grabbers */ + //@{ +public: + /*! Returns the current qglviewer::MouseGrabber, or \c nullptr if no + qglviewer::MouseGrabber currently grabs mouse events. + + When qglviewer::MouseGrabber::grabsMouse(), the different mouse events are + sent to the mouseGrabber() instead of their usual targets (camera() or + manipulatedFrame()). + + See the qglviewer::MouseGrabber documentation for details on MouseGrabber's + mode of operation. + + In order to use MouseGrabbers, you need to enable mouse tracking (so that + mouseMoveEvent() is called even when no mouse button is pressed). Add this + line in init() or in your viewer constructor: \code setMouseTracking(true); + \endcode + Note that mouse tracking is disabled by default. Use + QWidget::hasMouseTracking() to retrieve current state. */ + qglviewer::MouseGrabber *mouseGrabber() const { return mouseGrabber_; } + + void + setMouseGrabberIsEnabled(const qglviewer::MouseGrabber *const mouseGrabber, + bool enabled = true); + /*! Returns \c true if \p mouseGrabber is enabled. + + Default value is \c true for all MouseGrabbers. When set to \c false using + setMouseGrabberIsEnabled(), the specified \p mouseGrabber will never become + the mouseGrabber() of this QGLViewer. This is useful when you use several + viewers: some MouseGrabbers may only have a meaning for some specific viewers + and should not be selectable in others. + + You can also use qglviewer::MouseGrabber::removeFromMouseGrabberPool() to + completely disable a MouseGrabber in all the QGLViewers. */ + bool + mouseGrabberIsEnabled(const qglviewer::MouseGrabber *const mouseGrabber) { + return !disabledMouseGrabbers_.contains( + reinterpret_cast(mouseGrabber)); + } +public Q_SLOTS: + void setMouseGrabber(qglviewer::MouseGrabber *mouseGrabber); + //@} + + /*! @name State of the viewer */ + //@{ +public: + /*! Returns the aspect ratio of the viewer's widget (width() / height()). */ + qreal aspectRatio() const { return width() / static_cast(height()); } + /*! Returns the current averaged viewer frame rate. + + This value is computed and averaged over 20 successive frames. It only changes + every 20 draw() (previously computed value is otherwise returned). + + This method is useful for true real-time applications that may adapt their + computational load accordingly in order to maintain a given frequency. + + This value is meaningful only when draw() is regularly called, either using a + \c QTimer, when animationIsStarted() or when the camera is manipulated with + the mouse. */ + qreal currentFPS() { return f_p_s_; } + /*! Returns \c true if the viewer is in fullScreen mode. + + Default value is \c false. Set by setFullScreen() or toggleFullScreen(). + + Note that if the QGLViewer is embedded in an other QWidget, it returns \c true + when the top level widget is in full screen mode. */ + bool isFullScreen() const { return fullScreen_; } + /*! Returns \c true if the viewer displays in stereo. + + The QGLViewer object must be created with a stereo format to handle + stereovision: \code QGLFormat format; format.setStereoDisplay( TRUE ); + QGLViewer viewer(format); + \endcode + The hardware needs to support stereo display. Try the stereoViewer example to check. + + Set by setStereoDisplay() or toggleStereoDisplay(). Default value is \c false. + + Stereo is performed using the Parallel axis asymmetric frustum perspective + projection method. See Camera::loadProjectionMatrixStereo() and + Camera::loadModelViewMatrixStereo(). + + The stereo parameters are defined by the camera(). See + qglviewer::Camera::setIODistance(), + qglviewer::Camera::setPhysicalScreenWidth() and + qglviewer::Camera::setFocusDistance(). */ + bool displaysInStereo() const { return stereo_; } + /*! Returns the recommended size for the QGLViewer. Default value is 600x400 + * pixels. */ + virtual QSize sizeHint() const { return QSize(600, 400); } + +public Q_SLOTS: + void setFullScreen(bool fullScreen = true); + void setStereoDisplay(bool stereo = true); + /*! Toggles the state of isFullScreen(). See also setFullScreen(). */ + void toggleFullScreen() { setFullScreen(!isFullScreen()); } + /*! Toggles the state of displaysInStereo(). See setStereoDisplay(). */ + void toggleStereoDisplay() { setStereoDisplay(!stereo_); } + void toggleCameraMode(); + +private: + bool cameraIsInRotateMode() const; + //@} + + /*! @name Display methods */ + //@{ +public: + static void drawArrow(qreal length = 1.0, qreal radius = -1.0, + int nbSubdivisions = 12); + static void drawArrow(const qglviewer::Vec &from, const qglviewer::Vec &to, + qreal radius = -1.0, int nbSubdivisions = 12); + static void drawAxis(qreal length = 1.0); + static void drawGrid(qreal size = 1.0, int nbSubdivisions = 10); + + virtual void startScreenCoordinatesSystem(bool upward = false) const; + virtual void stopScreenCoordinatesSystem() const; + + void drawText(int x, int y, const QString &text, const QFont &fnt = QFont()); + void displayMessage(const QString &message, int delay = 2000); + // void draw3DText(const qglviewer::Vec& pos, const qglviewer::Vec& normal, + // const QString& string, GLfloat height=0.1); + +protected: + virtual void drawLight(GLenum light, qreal scale = 1.0) const; + +private: + void displayFPS(); + /*! Vectorial rendering callback method. */ + void drawVectorial() { paintGL(); } + +#ifndef DOXYGEN + friend void drawVectorial(void *param); +#endif +//@} + +#ifdef DOXYGEN + /*! @name Useful inherited methods */ + //@{ +public: + /*! Returns viewer's widget width (in pixels). See QOpenGLWidget + * documentation. */ + int width() const; + /*! Returns viewer's widget height (in pixels). See QOpenGLWidget + * documentation. */ + int height() const; + /*! Updates the display. Do not call draw() directly, use this method instead. + * See QOpenGLWidget documentation. */ + virtual void update(); + /*! Returns \c true if the widget has a valid GL rendering context. See + QOpenGLWidget documentation. */ + bool isValid() const; + /*! Makes this widget's rendering context the current OpenGL rendering + context. Useful with several viewers. See QOpenGLWidget documentation. */ + virtual void makeCurrent(); + /*! Returns \c true if mouseMoveEvent() is called even when no mouse button is + pressed. + + You need to setMouseTracking() to \c true in order to use MouseGrabber (see + mouseGrabber()). See details in the QWidget documentation. */ + bool hasMouseTracking() const; +public Q_SLOTS: + /*! Resizes the widget to size \p width by \p height pixels. See also width() + * and height(). */ + virtual void resize(int width, int height); + /*! Sets the hasMouseTracking() value. */ + virtual void setMouseTracking(bool enable); +#endif + + /*! @name Snapshots */ + //@{ +public: + /*! Returns the snapshot file name used by saveSnapshot(). + + This value is used in \p automatic mode (see saveSnapshot()). A dialog is + otherwise popped-up to set it. + + You can also directly provide a file name using saveSnapshot(const QString&, + bool). + + If the file name is relative, the current working directory at the moment of + the method call is used. Set using setSnapshotFileName(). */ + const QString &snapshotFileName() const { return snapshotFileName_; } +#ifndef DOXYGEN + const QString &snapshotFilename() const; +#endif + /*! Returns the snapshot file format used by saveSnapshot(). + + This value is used when saveSnapshot() is passed the \p automatic flag. It is + defined using a saveAs pop-up dialog otherwise. + + The available formats are those handled by Qt. Classical values are \c "JPEG", + \c "PNG", \c "PPM", \c "BMP". Use the following code to get the actual list: + \code + QList formatList = QImageReader::supportedImageFormats(); + // or with Qt version 2 or 3: + QStringList formatList = QImage::outputFormatList(); + \endcode + + If the library was compiled with the vectorial rendering option (default), + three additional vectorial formats are available: \c "EPS", \c "PS" and \c + "XFIG". \c "SVG" and \c "PDF" formats should soon be available. The VRender library was created + by Cyril Soler. + + Note that the VRender library has some limitations: vertex shader effects are + not reproduced and \c PASS_THROUGH tokens are not handled so one can not + change point and line size in the middle of a drawing. + + Default value is the first supported among "JPEG, PNG, EPS, PS, PPM, BMP", in + that order. + + This value is set using setSnapshotFormat() or with + openSnapshotFormatDialog(). + + \attention No verification is performed on the provided format validity. The + next call to saveSnapshot() may fail if the format string is not supported. */ + const QString &snapshotFormat() const { return snapshotFormat_; } + /*! Returns the value of the counter used to name snapshots in saveSnapshot() + when \p automatic is \c true. + + Set using setSnapshotCounter(). Default value is 0, and it is incremented + after each \p automatic snapshot. See saveSnapshot() for details. */ + int snapshotCounter() const { return snapshotCounter_; } + /*! Defines the image quality of the snapshots produced with saveSnapshot(). + + Values must be in the range -1..100. Use 0 for lowest quality and 100 for + highest quality (and larger files). -1 means use Qt default quality. Default + value is 95. + + Set using setSnapshotQuality(). See also the QImage::save() documentation. + + \note This value has no impact on the images produced in vectorial format. */ + int snapshotQuality() { return snapshotQuality_; } + + // Qt 2.3 does not support qreal default value parameters in slots. + // Remove "Q_SLOTS" from the following line to compile with Qt 2.3 +public Q_SLOTS: + void saveSnapshot(bool automatic = true, bool overwrite = false); + +public Q_SLOTS: + void saveSnapshot(const QString &fileName, bool overwrite = false); + void setSnapshotFileName(const QString &name); + + /*! Sets the snapshotFormat(). */ + void setSnapshotFormat(const QString &format) { snapshotFormat_ = format; } + /*! Sets the snapshotCounter(). */ + void setSnapshotCounter(int counter) { snapshotCounter_ = counter; } + /*! Sets the snapshotQuality(). */ + void setSnapshotQuality(int quality) { snapshotQuality_ = quality; } + bool openSnapshotFormatDialog(); + void snapshotToClipboard(); + +protected: + bool saveImageSnapshot(const QString &fileName); + +#ifndef DOXYGEN + /* This class is used internally for screenshot that require tiling (image + size size different from window size). Only in that case, is the private + tileRegion_ pointer non null. It then contains the current tiled region, which + is used by startScreenCoordinatesSystem to adapt the coordinate system. Not + using it would result in a tiled drawing of the parts that use + startScreenCoordinatesSystem. Also used by scaledFont for same purposes. */ + class TileRegion { + public: + qreal xMin, yMin, xMax, yMax, textScale; + }; +#endif + +public: + /*! Return a possibly scaled version of \p font, used for snapshot rendering. + + From a user's point of view, this method simply returns \p font and can be + used transparently. + + However when internally rendering a screen snapshot using saveSnapshot(), it + returns a scaled version of the font, so that the size of the rendered text on + the snapshot is identical to what is displayed on screen, even if the snapshot + uses image tiling to create an image of dimensions different from those of the + current window. This scaled version will only be used when saveSnapshot() + calls your draw() method to generate the snapshot. + + All your calls to renderText() function hence should use this method. + \code + renderText(x, y, z, "My Text", scaledFont(QFont())); + \endcode + will guarantee that this text will be properly displayed on arbitrary sized + snapshots. + + Note that this method is not needed if you use drawText() which already calls + it internally. */ + QFont scaledFont(const QFont &font) const { + if (tileRegion_ == nullptr) + return font; + else { + QFont f(font); + if (f.pixelSize() == -1) + f.setPointSizeF(f.pointSizeF() * tileRegion_->textScale); + else + f.setPixelSize(int(f.pixelSize() * tileRegion_->textScale)); + return f; + } + } + //@} + + /*! @name Buffer to texture */ + //@{ +public: + GLuint bufferTextureId() const; + /*! Returns the texture coordinate corresponding to the u extremum of the + bufferTexture. + + The bufferTexture is created by copyBufferToTexture(). The texture size has + powers of two dimensions and the buffer image hence only fills a part of it. + This value corresponds to the u coordinate of the extremum right side of the + buffer image. + + Use (0,0) to (bufferTextureMaxU(), bufferTextureMaxV()) texture coordinates to + map the entire texture on a quad. */ + qreal bufferTextureMaxU() const { return bufferTextureMaxU_; } + /*! Same as bufferTextureMaxU(), but for the v texture coordinate. */ + qreal bufferTextureMaxV() const { return bufferTextureMaxV_; } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) + // These methods are part of the QGLWidget public API. + // As of version 2.7.0, the use of QOpenGLWidget instead means that they have + // to be provided for backward compatibility. + void renderText(int x, int y, const QString &str, + const QFont &font = QFont()); + void renderText(double x, double y, double z, const QString &str, + const QFont &font = QFont()); +#endif + +public Q_SLOTS: + void copyBufferToTexture(GLint internalFormat, GLenum format = GL_NONE); + //@} + + /*! @name Animation */ + //@{ +public: + /*! Return \c true when the animation loop is started. + + During animation, an infinite loop calls animate() and draw() and then waits + for animationPeriod() milliseconds before calling animate() and draw() again. + And again. + + Use startAnimation(), stopAnimation() or toggleAnimation() to change this + value. + + See the animation example for + illustration. */ + bool animationIsStarted() const { return animationStarted_; } + /*! The animation loop period, in milliseconds. + + When animationIsStarted(), this is delay waited after draw() to call animate() + and draw() again. Default value is 40 milliseconds (25 Hz). + + This value will define the currentFPS() when animationIsStarted() (provided + that your animate() and draw() methods are fast enough). + + If you want to know the maximum possible frame rate of your machine on a given + scene, setAnimationPeriod() to \c 0, and startAnimation() (keyboard shortcut + is \c Enter). The display will then be updated as often as possible, and the + frame rate will be meaningful. + + \note This value is taken into account only the next time you call + startAnimation(). If animationIsStarted(), you should stopAnimation() first. +*/ + int animationPeriod() const { return animationPeriod_; } + +public Q_SLOTS: + /*! Sets the animationPeriod(), in milliseconds. */ + void setAnimationPeriod(int period) { animationPeriod_ = period; } + virtual void startAnimation(); + virtual void stopAnimation(); + /*! Scene animation method. + + When animationIsStarted(), this method is in charge of the scene update + before each draw(). Overload it to define how your scene evolves over time. + The time should either be regularly incremented in this method (frame-rate + independent animation) or computed from actual time (for instance using + QTime::elapsed()) for real-time animations. + + Note that KeyFrameInterpolator (which regularly updates a Frame) does + not use this method to animate a Frame, but rather rely on a QTimer + signal-slot mechanism. + + See the animation example for an + illustration. */ + virtual void animate() { Q_EMIT animateNeeded(); } + /*! Calls startAnimation() or stopAnimation(), depending on + * animationIsStarted(). */ + void toggleAnimation() { + if (animationIsStarted()) + stopAnimation(); + else + startAnimation(); + } + //@} + +public: +Q_SIGNALS: + /*! Signal emitted by the default init() method. + + Connect this signal to the methods that need to be called to initialize your + viewer or overload init(). */ + void viewerInitialized(); + + /*! Signal emitted by the default draw() method. + + Connect this signal to your main drawing method or overload draw(). See the callback example for an illustration. */ + void drawNeeded(); + + /*! Signal emitted at the end of the QGLViewer::paintGL() method, when frame + is drawn. + + Can be used to notify an image grabbing process that the image is ready. A + typical example is to connect this signal to the saveSnapshot() method, so + that a (numbered) snapshot is generated after each new display, in order to + create a movie: \code connect(viewer, SIGNAL(drawFinished(bool)), + SLOT(saveSnapshot(bool))); \endcode + + The \p automatic bool variable is always \c true and has been added so that + the signal can be connected to saveSnapshot() with an \c automatic value set + to \c true. */ + void drawFinished(bool automatic); + + /*! Signal emitted by the default animate() method. + + Connect this signal to your scene animation method or overload animate(). */ + void animateNeeded(); + + /*! Signal emitted by the default QGLViewer::help() method. + + Connect this signal to your own help method or overload help(). */ + void helpRequired(); + + /*! This signal is emitted whenever axisIsDrawn() changes value. */ + void axisIsDrawnChanged(bool drawn); + /*! This signal is emitted whenever gridIsDrawn() changes value. */ + void gridIsDrawnChanged(bool drawn); + /*! This signal is emitted whenever FPSIsDisplayed() changes value. */ + void FPSIsDisplayedChanged(bool displayed); + /*! This signal is emitted whenever textIsEnabled() changes value. */ + void textIsEnabledChanged(bool enabled); + /*! This signal is emitted whenever cameraIsEdited() changes value.. */ + void cameraIsEditedChanged(bool edited); + /*! This signal is emitted whenever displaysInStereo() changes value. */ + void stereoChanged(bool on); + /*! Signal emitted by select(). + + Connect this signal to your selection method or overload select(), or more + probably simply drawWithNames(). */ + void pointSelected(const QMouseEvent *e); + + /*! Signal emitted by setMouseGrabber() when the mouseGrabber() is changed. + + \p mouseGrabber is a pointer to the new MouseGrabber. Note that this signal is + emitted with a \c nullptr parameter each time a MouseGrabber stops grabbing + mouse. */ + void mouseGrabberChanged(qglviewer::MouseGrabber *mouseGrabber); + + /*! @name Help window */ + //@{ +public: + /*! Returns the QString displayed in the help() window main tab. + + Overload this method to define your own help string, which should shortly + describe your application and explain how it works. Rich-text (HTML) tags can + be used (see QStyleSheet() documentation for available tags): \code QString + myViewer::helpString() const + { + QString text("

M y V i e w e r

"); + text += "Displays a Scene using OpenGL. Move the camera using the + mouse."; return text; + } + \endcode + + See also mouseString() and keyboardString(). */ + virtual QString helpString() const { return tr("No help available."); } + + virtual QString mouseString() const; + virtual QString keyboardString() const; + +#ifndef DOXYGEN + /*! This method is deprecated, use mouseString() instead. */ + virtual QString mouseBindingsString() const { return mouseString(); } + /*! This method is deprecated, use keyboardString() instead. */ + virtual QString shortcutBindingsString() const { return keyboardString(); } +#endif + +public Q_SLOTS: + virtual void help(); + virtual void aboutQGLViewer(); + +protected: + /*! Returns a pointer to the help widget. + + Use this only if you want to directly modify the help widget. Otherwise use + helpString(), setKeyDescription() and setMouseBindingDescription() to + customize the text displayed in the help window tabs. */ + QTabWidget *helpWidget() { return helpWidget_; } + //@} + + /*! @name Drawing methods */ + //@{ +protected: + virtual void resizeGL(int width, int height); + virtual void initializeGL(); + + /*! Initializes the viewer OpenGL context. + + This method is called before the first drawing and should be overloaded to + initialize some of the OpenGL flags. The default implementation is empty. See + initializeGL(). + + Typical usage include camera() initialization (showEntireScene()), previous + viewer state restoration (restoreStateFromFile()), OpenGL state modification + and display list creation. + + Note that initializeGL() modifies the standard OpenGL context. These values + can be restored back in this method. + + \attention You should not call updateGL() (or any method that calls it) in + this method, as it will result in an infinite loop. The different QGLViewer + set methods (setAxisIsDrawn(), setFPSIsDisplayed()...) are protected against + this problem and can safely be called. + + \note All the OpenGL specific initializations must be done in this method: the + OpenGL context is not yet available in your viewer constructor. */ + virtual void init() { Q_EMIT viewerInitialized(); } + + virtual void paintGL(); + virtual void preDraw(); + virtual void preDrawStereo(bool leftBuffer = true); + + /*! The core method of the viewer, that draws the scene. + + If you build a class that inherits from QGLViewer, this is the method you want + to overload. See the simpleViewer + example for an illustration. + + The camera modelView matrix set in preDraw() converts from the world to the + camera coordinate systems. Vertices given in draw() can then be considered as + being given in the world coordinate system. The camera is moved in this world + using the mouse. This representation is much more intuitive than the default + camera-centric OpenGL standard. + + \attention The \c GL_PROJECTION matrix should not be modified by this method, + to correctly display visual hints (axis, grid, FPS...) in postDraw(). Use + push/pop or call camera()->loadProjectionMatrix() at the end of draw() if you + need to change the projection matrix (unlikely). On the other hand, the \c + GL_MODELVIEW matrix can be modified and left in a arbitrary state. */ + virtual void draw() {} + virtual void fastDraw(); + virtual void postDraw(); + //@} + + /*! @name Mouse, keyboard and event handlers */ + //@{ +protected: + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void mouseDoubleClickEvent(QMouseEvent *); + virtual void wheelEvent(QWheelEvent *); + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + virtual void timerEvent(QTimerEvent *); + virtual void closeEvent(QCloseEvent *); + //@} + + /*! @name Object selection */ + //@{ +public: + /*! Returns the name (an integer value) of the entity that was last selected + by select(). This value is set by endSelection(). See the select() + documentation for details. + + As a convention, this method returns -1 if the selectBuffer() was empty, + meaning that no object was selected. + + Return value is -1 before the first call to select(). This value is modified + using setSelectedName(). */ + int selectedName() const { return selectedObjectId_; } + /*! Returns the selectBuffer() size. + + See the select() documentation for details. Use setSelectBufferSize() to + change this value. + + Default value is 4000 (i.e. 1000 objects in selection region, since each + object pushes 4 values). This size should be over estimated to prevent a + buffer overflow when many objects are drawn under the mouse cursor. */ + int selectBufferSize() const { return selectBufferSize_; } + + /*! Returns the width (in pixels) of a selection frustum, centered on the + mouse cursor, that is used to select objects. + + The height of the selection frustum is defined by selectRegionHeight(). + + The objects that will be drawn in this region by drawWithNames() will be + recorded in the selectBuffer(). endSelection() then analyzes this buffer and + setSelectedName() to the name of the closest object. See the gluPickMatrix() + documentation for details. + + The default value is 3, which is adapted to standard applications. A smaller + value results in a more precise selection but the user has to be careful for + small feature selection. + + See the multiSelect example for an + illustration. */ + int selectRegionWidth() const { return selectRegionWidth_; } + /*! See the selectRegionWidth() documentation. Default value is 3 pixels. */ + int selectRegionHeight() const { return selectRegionHeight_; } + + /*! Returns a pointer to an array of \c GLuint. + + This buffer is used by the \c GL_SELECT mode in select() to perform object + selection. The buffer size can be modified using setSelectBufferSize(). If you + overload endSelection(), you will analyze the content of this buffer. See the + \c glSelectBuffer() man page for details. */ + GLuint *selectBuffer() { return selectBuffer_; } + +public Q_SLOTS: + virtual void select(const QMouseEvent *event); + virtual void select(const QPoint &point); + + void setSelectBufferSize(int size); + /*! Sets the selectRegionWidth(). */ + void setSelectRegionWidth(int width) { selectRegionWidth_ = width; } + /*! Sets the selectRegionHeight(). */ + void setSelectRegionHeight(int height) { selectRegionHeight_ = height; } + /*! Set the selectedName() value. + + Used in endSelection() during a selection. You should only call this method + if you overload the endSelection() method. */ + void setSelectedName(int id) { selectedObjectId_ = id; } + +protected: + virtual void beginSelection(const QPoint &point); + /*! This method is called by select() and should draw selectable entities. + + Default implementation is empty. Overload and draw the different elements of +your scene you want to be able to select. The default select() implementation +relies on the \c GL_SELECT, and requires that each selectable element is drawn +within a \c glPushName() - \c glPopName() block. A typical usage would be (see +the select example): \code void +Viewer::drawWithNames() { for (int i=0; idraw(); + glPopName(); + } +} +\endcode + + The resulting selected name is computed by endSelection(), which +setSelectedName() to the integer id pushed by this method (a value of -1 means +no selection). Use selectedName() to update your selection, probably in the +postSelection() method. + + \attention If your selected objects are points, do not use \c +glBegin(GL_POINTS); and \c glVertex3fv() in the above \c draw() method (not +compatible with raster mode): use \c glRasterPos3fv() instead. */ + virtual void drawWithNames() {} + virtual void endSelection(const QPoint &point); + /*! This method is called at the end of the select() procedure. It should + finalize the selection process and update the data + structure/interface/computation/display... according to the newly selected + entity. + + The default implementation is empty. Overload this method if needed, and use + selectedName() to retrieve the selected entity name (returns -1 if no object + was selected). See the select example + for an illustration. */ + virtual void postSelection(const QPoint &point) { Q_UNUSED(point); } + //@} + + /*! @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, + SAVE_SCREENSHOT, + 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, + SNAPSHOT_TO_CLIPBOARD + }; + + unsigned int shortcut(KeyboardAction action) const; +#ifndef DOXYGEN + // QGLViewer 1.x + unsigned int keyboardAccelerator(KeyboardAction action) const; + Qt::Key keyFrameKey(unsigned int index) const; + Qt::KeyboardModifiers playKeyFramePathStateKey() const; + // QGLViewer 2.0 without Qt4 support + Qt::KeyboardModifiers addKeyFrameStateKey() const; + Qt::KeyboardModifiers playPathStateKey() const; +#endif + Qt::Key pathKey(unsigned int index) const; + Qt::KeyboardModifiers addKeyFrameKeyboardModifiers() const; + Qt::KeyboardModifiers playPathKeyboardModifiers() const; + +public Q_SLOTS: + void setShortcut(KeyboardAction action, unsigned int key); +#ifndef DOXYGEN + void setKeyboardAccelerator(KeyboardAction action, unsigned int key); +#endif + void setKeyDescription(unsigned int key, QString description); + void clearShortcuts(); + +// Key Frames shortcut keys +#ifndef DOXYGEN + // QGLViewer 1.x compatibility methods + virtual void setKeyFrameKey(unsigned int index, int key); + virtual void setPlayKeyFramePathStateKey(unsigned int buttonState); + // QGLViewer 2.0 without Qt4 support + virtual void setPlayPathStateKey(unsigned int buttonState); + virtual void setAddKeyFrameStateKey(unsigned int buttonState); +#endif + virtual void setPathKey(int key, unsigned int index = 0); + virtual void setPlayPathKeyboardModifiers(Qt::KeyboardModifiers modifiers); + virtual void setAddKeyFrameKeyboardModifiers(Qt::KeyboardModifiers modifiers); + //@} + +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; + int mouseHandler(unsigned int state) const; + int mouseButtonState(MouseHandler handler, MouseAction action, + bool withConstraint = true) const; + ClickAction clickAction(unsigned int state, bool doubleClick, + Qt::MouseButtons buttonsBefore) const; + void getClickButtonState(ClickAction action, unsigned int &state, + bool &doubleClick, + Qt::MouseButtons &buttonsBefore) const; + unsigned int wheelButtonState(MouseHandler handler, MouseAction action, + bool withConstraint = true) const; +#endif + + 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, + bool withConstraint, Qt::Key &key, + Qt::KeyboardModifiers &modifiers, + Qt::MouseButton &button) const; + + 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, + Qt::KeyboardModifiers &modifiers, + Qt::MouseButton &button, bool &doubleClick, + Qt::MouseButtons &buttonsBefore) const; + + MouseAction wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) const; + int wheelHandler(Qt::Key key, Qt::KeyboardModifiers modifiers) const; + + void getWheelActionBinding(MouseHandler handler, 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, + bool doubleClick = false, + Qt::MouseButtons buttonsBefore = Qt::NoButton); + void + setMouseBindingDescription(unsigned int state, QString description, + bool doubleClick = false, + Qt::MouseButtons buttonsBefore = Qt::NoButton); +#endif + + void setMouseBinding(Qt::KeyboardModifiers modifiers, Qt::MouseButton buttons, + MouseHandler handler, MouseAction action, + bool withConstraint = true); + void setMouseBinding(Qt::KeyboardModifiers modifiers, Qt::MouseButton button, + ClickAction action, bool doubleClick = false, + Qt::MouseButtons buttonsBefore = Qt::NoButton); + void setWheelBinding(Qt::KeyboardModifiers modifiers, MouseHandler handler, + MouseAction action, bool withConstraint = true); + void + setMouseBindingDescription(Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, QString description, + bool doubleClick = false, + Qt::MouseButtons buttonsBefore = Qt::NoButton); + + void setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, + Qt::MouseButton buttons, MouseHandler handler, + MouseAction action, bool withConstraint = true); + void setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, ClickAction action, + bool doubleClick = false, + Qt::MouseButtons buttonsBefore = Qt::NoButton); + void setWheelBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, + MouseHandler handler, MouseAction action, + bool withConstraint = true); + void + setMouseBindingDescription(Qt::Key key, Qt::KeyboardModifiers modifiers, + Qt::MouseButton button, QString description, + bool doubleClick = false, + Qt::MouseButtons buttonsBefore = Qt::NoButton); + + void clearMouseBindings(); + +#ifndef DOXYGEN + MouseAction wheelAction(Qt::KeyboardModifiers modifiers) const; + int wheelHandler(Qt::KeyboardModifiers modifiers) const; + + void setHandlerKeyboardModifiers(MouseHandler handler, + Qt::KeyboardModifiers modifiers); + void setHandlerStateKey(MouseHandler handler, unsigned int buttonState); + void setMouseStateKey(MouseHandler handler, unsigned int buttonState); +#endif + +private: + static QString mouseActionString(QGLViewer::MouseAction ma); + static QString clickActionString(QGLViewer::ClickAction ca); + //@} + + /*! @name State persistence */ + //@{ +public: + QString stateFileName() const; + virtual QDomElement domElement(const QString &name, + QDomDocument &document) const; + +public Q_SLOTS: + virtual void initFromDOMElement(const QDomElement &element); + virtual void saveStateToFile(); // cannot be const because of QMessageBox + virtual bool restoreStateFromFile(); + + /*! Defines the stateFileName() used by saveStateToFile() and + restoreStateFromFile(). + + The file name can have an optional prefix directory (no prefix meaning + current directory). If the directory does not exist, it will be created by + saveStateToFile(). + + \code + // Name depends on the displayed 3D model. Saved in current directory. + setStateFileName(3DModelName() + ".xml"); + + // Files are stored in a dedicated directory under user's home directory. + setStateFileName(QDir::homeDirPath + "/.config/myApp.xml"); + \endcode */ + void setStateFileName(const QString &name) { stateFileName_ = name; } + +#ifndef DOXYGEN + void saveToFile(const QString &fileName = QString()); + bool restoreFromFile(const QString &fileName = QString()); +#endif + +private: + static void saveStateToFileForAllViewers(); + //@} + + /*! @name QGLViewer pool */ + //@{ +public: + /*! Returns a \c QList that contains pointers to all the created QGLViewers. + Note that this list may contain \c nullptr pointers if the associated viewer + has been deleted. + + Can be useful to apply a method or to connect a signal to all the viewers: + \code + 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_; + } + + /*! 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 + (see stateFileName() for an application example). + + When a QGLViewer is deleted, the QGLViewers' indexes are preserved and nullptr is + set for that index. When a QGLViewer is created, it is placed in the first + 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)); + } +//@} + +#ifndef DOXYGEN + /*! @name Visual hints */ + //@{ +public: + virtual void setVisualHintsMask(int mask, int delay = 2000); + virtual void drawVisualHints(); + +public Q_SLOTS: + virtual void resetVisualHints(); +//@} +#endif + +private Q_SLOTS: + // Patch for a Qt bug with fullScreen on startup + void delayedFullScreen() { + move(prevPos_); + setFullScreen(); + } + void hideMessage(); + +private: + // Copy constructor and operator= are declared private and undefined + // Prevents everyone from trying to use them + QGLViewer(const QGLViewer &v); + QGLViewer &operator=(const QGLViewer &v); + + // Set parameters to their default values. Called by the constructors. + void defaultConstructor(); + + void handleKeyboardAction(KeyboardAction id); + + // C a m e r a + qglviewer::Camera *camera_; + bool cameraIsEdited_; + qreal previousCameraZClippingCoefficient_; + unsigned int previousPathId_; // double key press recognition + void connectAllCameraKFIInterpolatedSignals(bool connection = true); + + // C o l o r s + QColor backgroundColor_, foregroundColor_; + + // D i s p l a y f l a g s + bool axisIsDrawn_; // world axis + bool gridIsDrawn_; // world XY grid + bool FPSIsDisplayed_; // Frame Per Seconds + bool textIsEnabled_; // drawText() actually draws text or not + bool stereo_; // stereo display + bool fullScreen_; // full screen mode + QPoint prevPos_; // Previous window position, used for full screen mode + + // A n i m a t i o n + bool animationStarted_; // animation mode started + int animationPeriod_; // period in msecs + int animationTimerId_; + + // F P S d i s p l a y + QElapsedTimer fpsTime_; + unsigned int fpsCounter_; + QString fpsString_; + qreal f_p_s_; + + // M e s s a g e s + QString message_; + bool displayMessage_; + QTimer messageTimer_; + + // M a n i p u l a t e d f r a m e + qglviewer::ManipulatedFrame *manipulatedFrame_; + bool manipulatedFrameIsACamera_; + + // M o u s e G r a b b e r + qglviewer::MouseGrabber *mouseGrabber_; + bool mouseGrabberIsAManipulatedFrame_; + bool mouseGrabberIsAManipulatedCameraFrame_; + QMap disabledMouseGrabbers_; + + // S e l e c t i o n + int selectRegionWidth_, selectRegionHeight_; + int selectBufferSize_; + GLuint *selectBuffer_; + int selectedObjectId_; + + // V i s u a l h i n t s + int visualHint_; + + // S h o r t c u t k e y s + void setDefaultShortcuts(); + QString cameraPathKeysString() const; + QMap keyboardActionDescription_; + QMap keyboardBinding_; + QMap keyDescription_; + + // K e y F r a m e s s h o r t c u t s + QMap pathIndex_; + Qt::KeyboardModifiers addKeyFrameKeyboardModifiers_, + playPathKeyboardModifiers_; + + // B u f f e r T e x t u r e + GLuint bufferTextureId_; + qreal bufferTextureMaxU_, bufferTextureMaxV_; + int bufferTextureWidth_, bufferTextureHeight_; + unsigned int previousBufferTextureFormat_; + int previousBufferTextureInternalFormat_; + +#ifndef DOXYGEN + // M o u s e a c t i o n s + struct MouseActionPrivate { + MouseHandler handler; + MouseAction action; + bool withConstraint; + }; + + // M o u s e b i n d i n g s + struct MouseBindingPrivate { + const Qt::KeyboardModifiers modifiers; + const Qt::MouseButton button; + const Qt::Key key; + + MouseBindingPrivate(Qt::KeyboardModifiers m, Qt::MouseButton b, Qt::Key k) + : modifiers(m), button(b), key(k) {} + + // This sort order is used in mouseString() to display sorted mouse bindings + bool operator<(const MouseBindingPrivate &mbp) const { + if (key != mbp.key) + return key < mbp.key; + if (modifiers != mbp.modifiers) + return modifiers < mbp.modifiers; + return button < mbp.button; + } + }; + + // W h e e l b i n d i n g s + struct WheelBindingPrivate { + const Qt::KeyboardModifiers modifiers; + const Qt::Key key; + + WheelBindingPrivate(Qt::KeyboardModifiers m, Qt::Key k) + : modifiers(m), key(k) {} + + // This sort order is used in mouseString() to display sorted wheel bindings + bool operator<(const WheelBindingPrivate &wbp) const { + if (key != wbp.key) + return key < wbp.key; + return modifiers < wbp.modifiers; + } + }; + + // C l i c k b i n d i n g s + struct ClickBindingPrivate { + const Qt::KeyboardModifiers modifiers; + const Qt::MouseButton button; + const bool doubleClick; + const Qt::MouseButtons + buttonsBefore; // only defined when doubleClick is true + const Qt::Key key; + + ClickBindingPrivate(Qt::KeyboardModifiers m, Qt::MouseButton b, bool dc, + Qt::MouseButtons bb, Qt::Key k) + : modifiers(m), button(b), doubleClick(dc), buttonsBefore(bb), key(k) {} + + // This sort order is used in mouseString() to display sorted mouse bindings + bool operator<(const ClickBindingPrivate &cbp) const { + if (key != cbp.key) + return key < cbp.key; + if (buttonsBefore != cbp.buttonsBefore) + return buttonsBefore < cbp.buttonsBefore; + if (modifiers != cbp.modifiers) + return modifiers < cbp.modifiers; + if (button != cbp.button) + return button < cbp.button; + return doubleClick < cbp.doubleClick; + } + }; +#endif + static QString formatClickActionPrivate(ClickBindingPrivate cbp); + static bool isValidShortcutKey(int key); + + QMap mouseDescription_; + + void setDefaultMouseBindings(); + void performClickAction(ClickAction ca, const QMouseEvent *const e); + QMap mouseBinding_; + QMap wheelBinding_; + QMap clickBinding_; + Qt::Key currentlyPressedKey_; + + // S n a p s h o t s + void initializeSnapshotFormats(); + QImage frameBufferSnapshot(); + QString snapshotFileName_, snapshotFormat_; + int snapshotCounter_, snapshotQuality_; + TileRegion *tileRegion_; + + // 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_; + + // H e l p w i n d o w + QTabWidget *helpWidget_; +}; + +#endif // QGLVIEWER_QGLVIEWER_H diff --git a/code/3rd_QGLViewer-2.6.3/qglviewer.icns b/code/3rd_QGLViewer/QGLViewer/qglviewer.icns similarity index 100% rename from code/3rd_QGLViewer-2.6.3/qglviewer.icns rename to code/3rd_QGLViewer/QGLViewer/qglviewer.icns diff --git a/code/3rd_QGLViewer/QGLViewer/qglviewer.spec b/code/3rd_QGLViewer/QGLViewer/qglviewer.spec new file mode 100644 index 00000000..8a273fd2 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/qglviewer.spec @@ -0,0 +1,490 @@ +%define version_major 2 +%define version_minor 9 +%define version_revision 1 + +Name: libQGLViewer +Version: %{version_major}.%{version_minor}.%{version_revision} +Release: 1 + +Summary: Qt based OpenGL generic 3D viewer library. +License: GPL +Group: Development/C++ +Source: %{name}-%{version}.tar.gz +URL: http://www.libqglviewer.com +Buildroot: %{_tmppath}/%{name}-%{version}-buildroot +Vendor: Alma +Requires: libqt >= 4.0.0 + +%description +libQGLViewer is a C++ library based on Qt that eases the creation of OpenGL 3D viewers. It provides +some of the typical 3D viewer functionalities, such as the possibility to move the camera using the +mouse, which lacks in most of the other APIs. Other features include mouse manipulated frames, +interpolated keyFrames, object selection, stereo display, screenshot saving and much more. It is +used by OpenGL beginners as well as to create complex applications, being fully customizable and +easy to extend. + +%package devel +Summary: The libQGLViewer header files, documentation and examples. +Group: Development/Libraries +Requires: %{name} = 0:%{version} + + +%description devel +This package contains the header files for libQGLViewer. Install this package to develop programs +that uses libQGLViewer. A reference documentation and pedagogical examples are included. + +%prep +%define docDir %{_defaultdocdir}/QGLViewer +%define includeDir %{_includedir}/QGLViewer +%define libDir %{_libdir} +%setup -q -n %{name}-%{version} + +%build +# if [[ -z "${QTDIR}" ]] +# then + # echo "QTDIR undefined - Trying to autodetect..." + # autoDetect=$(locate lib/libqt | head -1 | sed s:"/lib/libqt.*":"":) + # if [[ -d $autoDetect ]] + # then + # export QTDIR=$autoDetect + # else + # echo "Compilation error - QTDIR is undefined, unable to run qmake" + # echo "Use export QTDIR=... ([ba]sh) or setenv QTDIR ... ([t]csh) and re-run" + # exit 1 + # fi +# fi + +# export PATH=${PATH}:${QTDIR}/bin +# export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${QTDIR}/lib + +cd QGLViewer +qmake-qt4 +make %{?_smp_mflags} +make staticlib +make clean +#cd ../designerPlugin +#qmake +#make %{?_smp_mflags} + + +%install +rm -rf $RPM_BUILD_ROOT +%{__install} -d $RPM_BUILD_ROOT%{includeDir} +%{__install} --mode=644 QGLViewer/*.h $RPM_BUILD_ROOT%{includeDir} +#%{__install} --mode=644 QGLViewer/qglviewer.cw $RPM_BUILD_ROOT%{includeDir} + +%{__install} -d $RPM_BUILD_ROOT%{libDir} +%{__install} --mode=644 QGLViewer/libQGLViewer.so.%{version} $RPM_BUILD_ROOT%{libDir} +ln -s libQGLViewer.so.%{version} $RPM_BUILD_ROOT%{libDir}/libQGLViewer.so.%{version_major}.%{version_minor} +ln -s libQGLViewer.so.%{version} $RPM_BUILD_ROOT%{libDir}/libQGLViewer.so.%{version_major} +ln -s libQGLViewer.so.%{version} $RPM_BUILD_ROOT%{libDir}/libQGLViewer.so +%{__install} --mode=644 QGLViewer/libQGLViewer.a $RPM_BUILD_ROOT%{libDir} + +%{__install} -d $RPM_BUILD_ROOT%{includeDir}/designerPlugin +%{__install} --mode=644 designerPlugin/* $RPM_BUILD_ROOT%{includeDir}/designerPlugin + +# %{__install} -d $RPM_BUILD_ROOT%{_mandir}/man3 +%{__install} -d $RPM_BUILD_ROOT%{docDir} +%{__install} -d $RPM_BUILD_ROOT%{docDir}/refManual +%{__install} -d $RPM_BUILD_ROOT%{docDir}/images +# %{__install} -d $RPM_BUILD_ROOT%{docDir}/examples +# %{__install} -d $RPM_BUILD_ROOT%{docDir}/examples/contribs +# %{__install} doc/man/man3/QGLViewer.3 $RPM_BUILD_ROOT%{_mandir}/man3/ +# %{__install} doc/man/man3/qglviewer_* $RPM_BUILD_ROOT%{_mandir}/man3/ +%{__install} --mode=644 doc/*.html doc/*.css $RPM_BUILD_ROOT%{docDir} +%{__install} --mode=644 INSTALL README LICENCE CHANGELOG $RPM_BUILD_ROOT%{docDir} +%{__install} --mode=644 doc/refManual/* $RPM_BUILD_ROOT%{docDir}/refManual +%{__install} --mode=644 doc/images/* $RPM_BUILD_ROOT%{docDir}/images +# %{__install} --mode=644 doc/examples/*.html $RPM_BUILD_ROOT%{docDir}/examples +# %{__install} --mode=644 examples/examples.pro $RPM_BUILD_ROOT%{docDir}/examples +# %{__install} --mode=644 examples/contribs/contribs.pro $RPM_BUILD_ROOT%{docDir}/examples/contribs +for dir in $(find examples -type d) +do + %{__install} -d $RPM_BUILD_ROOT%{docDir}/$dir + for file in $(find $dir -maxdepth 1 -type f) + do + %{__install} --mode=644 $file $RPM_BUILD_ROOT%{docDir}/$dir + done +done + +# Which ROBUST path should be used ? +#%{__install} -d /usr/lib/qt4/plugins/designer/ +#%{__install} --mode=644 designerPlugin/*.so /usr/lib/qt4/plugins/designer/ + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%{libDir}/%{name}.so +%{libDir}/%{name}.so.%{version_major}.%{version_minor} +%{libDir}/%{name}.so.%{version_major} +%{libDir}/%{name}.so.%{version} +%{libDir}/%{name}.a + +%files devel +%defattr(-,root,root) +%dir %{includeDir} +%{includeDir}/*.h +#%{includeDir}/qglviewer.cw + +%dir %{includeDir}/designerPlugin +%{includeDir}/designerPlugin/* + +# %doc %{_mandir}/man3/QGLViewer.3.bz2 +# %doc %{_mandir}/man3/qglviewer_* + +%dir %{docDir} +%doc %{docDir}/*.html +%doc %{docDir}/*.css +%doc %{docDir}/README +%doc %{docDir}/LICENCE +%doc %{docDir}/INSTALL +%doc %{docDir}/CHANGELOG +%docdir %{docDir}/refManual +%doc %{docDir}/refManual/* +%dir %{docDir}/images +%{docDir}/images/* +%dir %{docDir}/examples +%dir %{docDir}/examples/* +%{docDir}/examples/*/* + +%changelog +* Sat Dec 31 2022 Gilles Debunne 2.9.1 +- Qt5 compilation error fix. + +* Sat Dec 31 2022 Gilles Debunne 2.9.0 +- CMake compilation, Qt6 fixes, high dpi fixes. + +* Sun Mar 13 2022 Gilles Debunne 2.8.0 +- Updates for Qt6 compatibility. + +* Fri Nov 17 2019 Gilles Debunne 2.7.2 +- Update include. + +* Fri Nov 17 2017 Gilles Debunne 2.7.1 +- Fix deprecated message in constructor. + +* Wed Jun 14 2017 Gilles Debunne 2.7.0 +- QGLViewer extends QOpenGLWidget instead of the deprecated QGLWidget. + +* Tue Sep 27 2016 Gilles Debunne 2.6.4 +- Fix ARM build, remove contribs examples' compilation. + +* Thu Jul 16 2015 Gilles Debunne 2.6.3 +- Build library in QGLViewer directory. Simplified examples.pri. + +* Fri Jan 23 2015 Gilles Debunne 2.6.2 +- Fix qreal issue in Quaternion::FromRotationMatrix(). + +* Thu Jan 22 2015 Gilles Debunne 2.6.1 +- Bug fix in Camera::pointUnderPixel introduced by the switch to qreal. + +* Mon Nov 17 2014 Gilles Debunne 2.6.0 +- The entire API now consistently uses the qreal (i.e double) type for floating point numbers. + +* Thu Nov 6 2014 Gilles Debunne 2.5.4 +- More minor compilation warning and error fixes. + +* Mon Sep 1 2014 Gilles Debunne 2.5.3 +- Minor compilation warning and error fixes for recent compilers. + +* Wed Mar 5 2014 Gilles Debunne 2.5.2 +- New sceneUpVector and camera's rotation around it. + +* Wed Jan 22 2014 Gilles Debunne 2.5.1 +- Revolve around point reset to scene center. + +* Thu Dec 19 2013 Gilles Debunne 2.5.0 +- Refactor of all mouse bindings methods. New clear bindings methods. + +* Sun Nov 17 2013 Gilles Debunne 2.4.1 +- Using update() instead of updateGL(). Matrix computation is cached. Full reindent. + +* Sat May 25 2013 Gilles Debunne 2.4.0 +- Supports Qt5 (as well as Qt3 and Qt4), source available on github. + +* Thu May 24 2012 Gilles Debunne 2.3.17 +- Compilation of the examples using a framework fixed on Mac. + +* Tue Apr 10 2012 Gilles Debunne 2.3.16 +- Added -lGLU library when linking on Linux. + +* Thu Mar 8 2012 Gilles Debunne 2.3.15 +- Added a QGLViewer prefix so that headers are correctly when using the framework on mac. + +* Fri Mar 2 2012 Gilles Debunne 2.3.14 +- Added symbol export for designer on windows. Framework installed in user's library folder on mac. + +* Fri Feb 24 2012 Gilles Debunne 2.3.13 +- Handle lower case compilation folder names on windows/Qt 4.8. + +* Tue Feb 14 2012 Gilles Debunne 2.3.12 +- Fixed compilation for windows/Qt 4.8, better designer installation, removed setPhysicalDistanceToScreen. + +* Thu Nov 17 2011 Gilles Debunne 2.3.11 +- Unneeded Qt3 reference removed. + +* Wed Jun 14 2011 Gilles Debunne 2.3.10 +- Patches in designerPlugin and Qt3 support with Visual 2008. + +* Sat Dec 4 2010 Gilles Debunne 2.3.9 +- Vec switched to double. + +* Sun Nov 7 2010 Gilles Debunne 2.3.8 +- Vec operations return double, Support for Qt3 signal/slot cw file removed. + +* Thu Oct 28 2010 Gilles Debunne 2.3.7 +- Fixes for compilation on windows. + +* Sat May 29 2010 Gilles Debunne 2.3.6 +- Minor fixes for Qt3 and gcc 4.3 compilation. No more LD_LIBRARY_PATH tuning. + +* Sun Mar 1 2010 Gilles Debunne 2.3.5 +- Mac compilation improvements, no_keywords CONFIG option. + +* Tue Sep 1 2009 Gilles Debunne 2.3.4 +- Patches for Qt3 compilation. + +* Tue Jul 14 2009 Gilles Debunne 2.3.3 +- New snapshotToClipboard method. + +* Tue Jul 7 2009 Gilles Debunne 2.3.2 +- Fixed tiled snapshot rendering when using screen coordinates. + +* Wed Oct 1 2008 Gilles Debunne 2.3.1 +- Exceptions added to the GPL Open Source license, Qt2 no longer supported. + +* Mon Sep 1 2008 Gilles Debunne 2.3.0 +- New examples, default package compilation using Qt4, new version numbering. + +* Tue Aug 28 2007 Gilles Debunne 2.2.6-3 +- make install problem with static lib compilation fixed (and bug submitted to Trolltech). + +* Tue Jul 25 2007 Gilles Debunne 2.2.6-2 +- Missing event includes in examples (qt4). + +* Tue Jul 4 2007 Gilles Debunne 2.2.6-1 +- Misspelling in ui files (qt3) and missing include in keyboardAndMouse example (qt4). + +* Tue May 29 2007 Gilles Debunne 2.2.6-0 +- Camera saves and restores scene center and radius. Camera::getModelViewProjectionMatrix + +* Sun Apr 1 2007 Gilles Debunne 2.2.5-1 +- A new clippingPlane example. + +* Wed Jan 31 2007 Gilles Debunne 2.2.5-0 +- 1px shift in pointUnderPixel fixed. Minor bugs fixed. + +* Wed Jan 23 2007 Gilles Debunne 2.2.4-2 +- Qt4 include naming convention. + +* Tue Dec 12 2006 Gilles Debunne 2.2.4-1 +- Black screen bug introduced by DRIVE mode fixed. + +* Tue Nov 28 2006 Gilles Debunne 2.2.4-0 +- New DRIVE mouse mode, standardCamera example, minor bug fixes. + +* Wed Jul 26 2006 Gilles Debunne 2.2.3-1 +- Missing brace in .pro added. + +* Wed Jul 12 2006 Gilles Debunne 2.2.3-0 +- Camera::interpolateTo(). Constraint bug fix. + +* Mon Jun 7 2006 Gilles Debunne 2.2.2-3 +- Path in examples' .pro for minGW debug compilation. + +* Mon May 29 2006 Gilles Debunne 2.2.2-2 +- Documentation problem fixed. Compiles with Qt 2.3. + +* Wed May 15 2006 Gilles Debunne 2.2.2-1 +- Missing .ui file added. + +* Wed May 14 2006 Gilles Debunne 2.2.2-0 +- Snapshot can be created at an arbitrary size with optional oversampling anti-aliassing. + +* Wed Mar 29 2006 Gilles Debunne 2.2.1-1 +- libQGLViewer version added in documentation pages footers. + +* Wed Mar 8 2006 Gilles Debunne 2.2.1-0 +- Minor bug fixes in saveSnapshot. Designer plugin installation directory can now be changed. + +* Thu Feb 23 2006 Gilles Debunne 2.2.0-2 +- Truncation warnings on windows fixed, patch for a moc bug with Qt 4.1.1 on VC 6 (thanks Juergen). + +* Wed Feb 22 2006 Gilles Debunne 2.2.0-1 +- Warning with Qt 2.3 fixed. Selection problem with certain compilers fixed on BlobWar example. + +* Wed Feb 8 2006 Gilles Debunne 2.2.0-0 +- New Camera methods, new examples, many major improvements and bug fixes. + +* Mon Nov 14 2005 Gilles Debunne 2.1.1-8 +- VRender dialog bug with Qt4 fixed. + +* Fri Oct 21 2005 Gilles Debunne 2.1.1-7 +- API documentation restored. + +* Fri Oct 7 2005 Gilles Debunne 2.1.1-6 +- "weak definition" error message fixed with gcc4/Qt4. + +* Thu Oct 6 2005 Gilles Debunne 2.1.1-5 +- Spurious QTDIR in .pro caused error messages with Qt4. + +* Tue Oct 4 2005 Gilles Debunne 2.1.1-4 +- Agora example fixed. Minor typos fixed. + +* Wed Sep 21 2005 Gilles Debunne 2.1.1-3 +- Compilation options fixed for Visual 6. Qt2.3 patches in help window. + +* Tue Sep 20 2005 Gilles Debunne 2.1.1-2 +- Compilation options fixed, especially for MinGW. + +* Mon Sep 19 2005 Gilles Debunne 2.1.1-1 +- shared compilation option added for MinGW. + +* Sun Sep 18 2005 Gilles Debunne 2.1.1-0 +- saveSnapshot() file dialog problem fixed. + +* Wed Sep 14 2005 Gilles Debunne 2.1.0-0 +- Now runs with Qt version 2, 3 and 4. Dual licensing. + +* Wed Aug 10 2005 Gilles Debunne 2.0.1-0 +- Bounding box warning when viewing EPS fixed. + +* Thu Jul 25 2005 Gilles Debunne 2.0.0-5 +- Unclosed parenthesis fixed in saveSnapshot with Qt 2.3. + +* Thu Jul 21 2005 Gilles Debunne 2.0.0-4 +- Division by 0 in manipulatedFrame fixed. terrain and agora examples fixed. + +* Thu Jul 7 2005 Gilles Debunne 2.0.0-3 +- Patches for Qt 3.1, 64 bits architectures and gcc 4.0 compilation. + +* Wed Jun 29 2005 Gilles Debunne 2.0.0-2 +- float/double warnings fixed for .NET windows compilation. + +* Wed Jun 28 2005 Gilles Debunne 2.0.0-1 +- VRender code patched for windows compilation. + +* Wed Jun 22 2005 Gilles Debunne 2.0.0-0 +- New major release. API cleaned up and documentation entirely rewritten. + +* Wed Apr 28 2005 Gilles Debunne 1.3.9-6 +- zClippingCoef method, .NET compilation, better help window. + +* Wed Mar 2 2005 Gilles Debunne 1.3.9-5 +- saveToFile and restoreFromFile renamed. New setStateFileName method. + +* Wed Feb 9 2005 Gilles Debunne 1.3.9-4 +- Minor bug fixes in restoreFromFile. + +* Wed Jan 13 2005 Gilles Debunne 1.3.9-3 +- Minor bug fixes. displaymessage(), Vec class. New startScreenCoordinatesSystem() z range. + +* Wed Nov 23 2004 Gilles Debunne 1.3.9-2 +- Minor bug fixes. Better MouseGrabber and MultiView examples. New sizeHint() method. + +* Wed Nov 17 2004 Gilles Debunne 1.3.9-1 +- Many new utility functions. Many new examples. Binary distribution. Fully tested on Linux, Windows and Mac. + +* Wed Jul 7 2004 Gilles Debunne 1.3.8-3 +- QAccel abandonned for key bindings. Library installed in /usr instead of /usr/local. + +* Mon Jun 14 2004 Gilles Debunne 1.3.8-2 +- Patches for compilation on HP UX architecture. Anaglyph example. + +* Wed Jun 9 2004 Gilles Debunne 1.3.8-1 +- Key customization modified. New key and mouse descriptions can be added in the help window. + +* Wed May 5 2004 Gilles Debunne 1.3.7-1 +- New select procedure based on GL_SELECT. Frame inverse() method. + +* Wed Mar 17 2004 Gilles Debunne 1.3.6-2 +- Doc installed in /usr/local/share/doc instead of /usr/share/doc. Patch for gcc 2.96. + +* Wed Feb 3 2004 Gilles Debunne 1.3.6-1 +- Signal mechanism modified, MouseMotion renamed MouseAction, new project URL. + +* Wed Dec 24 2003 Gilles Debunne 1.3.5-8 +- New dom syntax, startScreenCoordinatesSystem orientation, bug fixes. + +* Wed Nov 26 2003 Gilles Debunne 1.3.5-7 +- ZOOM_ON_REGION added. + +* Fri Nov 17 2003 Gilles Debunne 1.3.5-6 +- Minor bug fixes with MouseGrabber. + +* Fri Nov 7 2003 Gilles Debunne 1.3.5-5 +- keyboardAndMouse example, minor bug fixes. + +* Fri Oct 31 2003 Gilles Debunne 1.3.5-4 +- QTextEdit patch with Qt 2.3. + +* Wed Oct 24 2003 Gilles Debunne 1.3.5-3 +- Patch for nVidia bug with anti-aliased fonts. + +* Wed Oct 22 2003 Gilles Debunne 1.3.5-2 +- Bug fixes, better help window. + +* Thu Oct 2 2003 Gilles Debunne 1.3.5-1 +- GLUT dependency removed. drawText uses Qt. + +* Wed Sep 24 2003 Gilles Debunne 1.3.4-2 +- Seg fault on exit fixed. Minor fixes. + +* Wed Jul 16 2003 Gilles Debunne 1.3.4-1 +- Mouse bindings configuration + +* Wed Jun 25 2003 Gilles Debunne 1.3.3-1 +- Doxygen search engine, FAQ page, Z-buffer display, constraints in KFI. + +* Mon May 5 2003 Gilles Debunne 1.3.2-2 +- /usr/lib changed to /usr/local/lib + +* Thu Apr 17 2003 Gilles Debunne 1.3.2-1 +- help() uses popup windows. DLL created for windows. + +* Wed Apr 10 2003 Gilles Debunne 1.3.1-1 +- A ManipulatedCameraFrame class. double in Quaternion. + +* Wed Mar 26 2003 Gilles Debunne 1.3.0-1 +- Many changes in the API. Documentation updated. A new MouseGrabber class. + +* Wed Mar 19 2003 Gilles Debunne 1.2.9-3 +- ORTHO camera improvements, better default help(). + +* Wed Mar 5 2003 Gilles Debunne 1.2.9-2 +- Slerp interpolation fixed. Tiny Camera matrix improvements. + +* Wed Feb 26 2003 Gilles Debunne 1.2.9-1 +- No more camera referenceFrame, slerp interpolation and new install paths. + +* Wed Jan 29 2003 Gilles Debunne 1.2.8-3 +- pixelGLRatio function, minor changes. + +* Wed Jan 22 2003 Gilles Debunne 1.2.8-2 +- Minor bug fixes. GL state saving optimized. + +* Wed Jan 15 2003 Gilles Debunne 1.2.8-1 +- SPECIAL key disappears. New trackball features. + +* Thu Dec 12 2002 Gilles Debunne 1.2.7-2 +- Minor improvements, draw3DText. Mac and Windows compatible release. +- Documentation and examples added to the distribution. + +* Wed Dec 4 2002 Gilles Debunne 1.2.7-1 +- KeyFrameInterpolator and EPSRender. Bug fixes and new trackball + +* Thu Sep 10 2002 Gilles Debunne 1.2.6-1 +- New features and bug fixes. See CHANGELOG for details. Cleaner spec. + +* Thu Jul 25 2002 Xavier Decoret 1.2.5-2 +- Links with qt-mt (multithread) so it works fine with Mandrake libqt3-devel +- fix the spec file: files were copied in /usr directories during rebuild! + +* Tue Jul 16 2002 Gilles Debunne 1.2.5-1 +- First rpm release diff --git a/code/3rd_QGLViewer-2.6.3/qglviewer_fr.qm b/code/3rd_QGLViewer/QGLViewer/qglviewer_fr.qm similarity index 100% rename from code/3rd_QGLViewer-2.6.3/qglviewer_fr.qm rename to code/3rd_QGLViewer/QGLViewer/qglviewer_fr.qm diff --git a/code/3rd_QGLViewer-2.6.3/qglviewer_fr.ts b/code/3rd_QGLViewer/QGLViewer/qglviewer_fr.ts similarity index 99% rename from code/3rd_QGLViewer-2.6.3/qglviewer_fr.ts rename to code/3rd_QGLViewer/QGLViewer/qglviewer_fr.ts index cd8a9b6a..72372d1f 100644 --- a/code/3rd_QGLViewer-2.6.3/qglviewer_fr.ts +++ b/code/3rd_QGLViewer/QGLViewer/qglviewer_fr.ts @@ -81,7 +81,7 @@ %1Hz - Frames per sec, in Hertz + Frames per seconds, in Hertz %1Hz diff --git a/code/3rd_QGLViewer/QGLViewer/quaternion.cpp b/code/3rd_QGLViewer/QGLViewer/quaternion.cpp new file mode 100644 index 00000000..aecc1f56 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/quaternion.cpp @@ -0,0 +1,503 @@ +#include "quaternion.h" +#include "domUtils.h" +#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. */ +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); +} diff --git a/code/3rd_QGLViewer/QGLViewer/quaternion.h b/code/3rd_QGLViewer/QGLViewer/quaternion.h new file mode 100644 index 00000000..9a06d424 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/quaternion.h @@ -0,0 +1,316 @@ +#ifndef QGLVIEWER_QUATERNION_H +#define QGLVIEWER_QUATERNION_H + +#include "vec.h" +#include +#include + +namespace qglviewer { +/*! \brief The Quaternion class represents 3D rotations and orientations. + \class Quaternion quaternion.h QGLViewer/quaternion.h + + The Quaternion is an appropriate (although not very intuitive) + representation for 3D rotations and orientations. Many tools are provided to + ease the definition of a Quaternion: see constructors, setAxisAngle(), + setFromRotationMatrix(), setFromRotatedBasis(). + + You can apply the rotation represented by the Quaternion to 3D points + using rotate() and inverseRotate(). See also the Frame class that represents + a coordinate system and provides other conversion functions like + Frame::coordinatesOf() and Frame::transformOf(). + + You can apply the Quaternion \c q rotation to the OpenGL matrices using: + \code glMultMatrixd(q.matrix()); + // equivalent to glRotate(q.angle()*180.0/M_PI, q.axis().x, q.axis().y, q.axis().z); \endcode + + Quaternion is part of the \c qglviewer namespace, specify \c + qglviewer::Quaternion or use the qglviewer namespace: + \code using namespace qglviewer; \endcode + +

Internal representation

+ + The internal representation of a Quaternion corresponding to a rotation + around axis \c axis, with an angle \c alpha is made of four qreals (i.e. + doubles) q[i]: \code {q[0],q[1],q[2]} = sin(alpha/2) * + {axis[0],axis[1],axis[2]} q[3] = cos(alpha/2) \endcode + + Note that certain implementations place the cosine term in first + position (instead of last here). + + The Quaternion is always normalized, so that its inverse() is actually + its conjugate. + + See also the Vec and Frame classes' documentations. + \nosubgrouping */ +class QGLVIEWER_EXPORT Quaternion { +public: + /*! @name Defining a Quaternion */ + //@{ + /*! Default constructor, builds an identity rotation. */ + Quaternion() { + q[0] = q[1] = q[2] = 0.0; + q[3] = 1.0; + } + + /*! Constructor from rotation axis (non null) and angle (in radians). See also + * setAxisAngle(). */ + Quaternion(const Vec &axis, qreal angle) { setAxisAngle(axis, angle); } + + Quaternion(const Vec &from, const Vec &to); + + /*! Constructor from the four values of a Quaternion. First three values are + axis*sin(angle/2) and last one is cos(angle/2). + + \attention The identity Quaternion is Quaternion(0,0,0,1) and \e not + Quaternion(0,0,0,0) (which is not unitary). The default Quaternion() + creates such identity Quaternion. */ + Quaternion(qreal q0, qreal q1, qreal q2, qreal q3) { + q[0] = q0; + q[1] = q1; + q[2] = q2; + q[3] = q3; + } + + /*! Copy constructor. */ + Quaternion(const Quaternion &Q) { + for (int i = 0; i < 4; ++i) + q[i] = Q.q[i]; + } + + /*! Equal operator. */ + Quaternion &operator=(const Quaternion &Q) { + for (int i = 0; i < 4; ++i) + q[i] = Q.q[i]; + return (*this); + } + + /*! Sets the Quaternion as a rotation of axis \p axis and angle \p angle (in + radians). + + \p axis does not need to be normalized. A null \p axis will result in + an identity Quaternion. */ + void setAxisAngle(const Vec &axis, qreal angle) { + const qreal norm = axis.norm(); + if (norm < 1E-8) { + // Null rotation + q[0] = 0.0; + q[1] = 0.0; + q[2] = 0.0; + q[3] = 1.0; + } else { + const qreal sin_half_angle = sin(angle / 2.0); + q[0] = sin_half_angle * axis[0] / norm; + q[1] = sin_half_angle * axis[1] / norm; + q[2] = sin_half_angle * axis[2] / norm; + q[3] = cos(angle / 2.0); + } + } + + /*! Sets the Quaternion value. See the Quaternion(qreal, qreal, qreal, qreal) + * constructor documentation. */ + void setValue(qreal q0, qreal q1, qreal q2, qreal q3) { + q[0] = q0; + q[1] = q1; + q[2] = q2; + q[3] = q3; + } + +#ifndef DOXYGEN + void setFromRotatedBase(const Vec &X, const Vec &Y, const Vec &Z); +#endif + void setFromRotationMatrix(const qreal m[3][3]); + void setFromRotatedBasis(const Vec &X, const Vec &Y, const Vec &Z); + //@} + + /*! @name Accessing values */ + //@{ + Vec axis() const; + qreal angle() const; + void getAxisAngle(Vec &axis, qreal &angle) const; + + /*! Bracket operator, with a constant return value. \p i must range in [0..3]. + * See the Quaternion(qreal, qreal, qreal, qreal) documentation. */ + qreal operator[](int i) const { return q[i]; } + + /*! Bracket operator returning an l-value. \p i must range in [0..3]. See the + * Quaternion(qreal, qreal, qreal, qreal) documentation. */ + qreal &operator[](int i) { return q[i]; } + //@} + + /*! @name Rotation computations */ + //@{ + /*! Returns the composition of the \p a and \p b rotations. + + The order is important. When applied to a Vec \c v (see + operator*(const Quaternion&, const Vec&) and rotate()) the resulting + Quaternion acts as if \p b was applied first and then \p a was applied. + This is obvious since the image \c v' of \p v by the composited rotation + satisfies: \code v'= (a*b) * v = a * (b*v) \endcode + + Note that a*b usually differs from b*a. + + \attention For efficiency reasons, the resulting Quaternion is not + normalized. Use normalize() in case of numerical drift with small rotation + composition. */ + friend Quaternion operator*(const Quaternion &a, const Quaternion &b) { + return Quaternion( + a.q[3] * b.q[0] + b.q[3] * a.q[0] + a.q[1] * b.q[2] - a.q[2] * b.q[1], + a.q[3] * b.q[1] + b.q[3] * a.q[1] + a.q[2] * b.q[0] - a.q[0] * b.q[2], + a.q[3] * b.q[2] + b.q[3] * a.q[2] + a.q[0] * b.q[1] - a.q[1] * b.q[0], + a.q[3] * b.q[3] - b.q[0] * a.q[0] - a.q[1] * b.q[1] - a.q[2] * b.q[2]); + } + + /*! Quaternion rotation is composed with \p q. + + See operator*(), since this is equivalent to \c this = \c this * \p q. + + \note For efficiency reasons, the resulting Quaternion is not + normalized. You may normalize() it after each application in case of + numerical drift. */ + Quaternion &operator*=(const Quaternion &q) { + *this = (*this) * q; + return *this; + } + + /*! Returns the image of \p v by the rotation \p q. + + Same as q.rotate(v). See rotate() and inverseRotate(). */ + friend Vec operator*(const Quaternion &q, const Vec &v) { + return q.rotate(v); + } + + Vec rotate(const Vec &v) const; + Vec inverseRotate(const Vec &v) const; + //@} + + /*! @name Inversion */ + //@{ + /*! Returns the inverse Quaternion (inverse rotation). + + Result has a negated axis() direction and the same angle(). A + composition (see operator*()) of a Quaternion and its inverse() results in + an identity function. + + Use invert() to actually modify the Quaternion. */ + Quaternion inverse() const { return Quaternion(-q[0], -q[1], -q[2], q[3]); } + + /*! Inverses the Quaternion (same rotation angle(), but negated axis()). + + See also inverse(). */ + void invert() { + q[0] = -q[0]; + q[1] = -q[1]; + q[2] = -q[2]; + } + + /*! Negates all the coefficients of the Quaternion. + + This results in an other representation of the \e same rotation + (opposite rotation angle, but with a negated axis direction: the two cancel + out). However, note that the results of axis() and angle() are unchanged + after a call to this method since angle() always returns a value in [0,pi]. + + This method is mainly useful for Quaternion interpolation, so that the + spherical interpolation takes the shortest path on the unit sphere. See + slerp() for details. */ + void negate() { + invert(); + q[3] = -q[3]; + } + + /*! Normalizes the Quaternion coefficients. + + This method should not need to be called since we only deal with unit + Quaternions. This is however useful to prevent numerical drifts, especially + with small rotational increments. See also normalized(). */ + qreal normalize() { + const qreal norm = + sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); + for (int i = 0; i < 4; ++i) + q[i] /= norm; + return norm; + } + + /*! Returns a normalized version of the Quaternion. + + See also normalize(). */ + Quaternion normalized() const { + qreal Q[4]; + const qreal norm = + sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); + for (int i = 0; i < 4; ++i) + Q[i] = q[i] / norm; + return Quaternion(Q[0], Q[1], Q[2], Q[3]); + } + //@} + + /*! @name Associated matrix */ + //@{ + const GLdouble *matrix() const; + void getMatrix(GLdouble m[4][4]) const; + void getMatrix(GLdouble m[16]) const; + + void getRotationMatrix(qreal m[3][3]) const; + + const GLdouble *inverseMatrix() const; + void getInverseMatrix(GLdouble m[4][4]) const; + void getInverseMatrix(GLdouble m[16]) const; + + void getInverseRotationMatrix(qreal m[3][3]) const; + //@} + + /*! @name Slerp interpolation */ + //@{ + static Quaternion slerp(const Quaternion &a, const Quaternion &b, qreal t, + bool allowFlip = true); + static Quaternion squad(const Quaternion &a, const Quaternion &tgA, + const Quaternion &tgB, const Quaternion &b, qreal t); + /*! Returns the "dot" product of \p a and \p b: a[0]*b[0] + a[1]*b[1] + + * a[2]*b[2] + a[3]*b[3]. */ + static qreal dot(const Quaternion &a, const Quaternion &b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + + Quaternion log(); + Quaternion exp(); + static Quaternion lnDif(const Quaternion &a, const Quaternion &b); + static Quaternion squadTangent(const Quaternion &before, + const Quaternion ¢er, + const Quaternion &after); + //@} + + /*! @name Random Quaternion */ + //@{ + static Quaternion randomQuaternion(); + //@} + + /*! @name XML representation */ + //@{ + explicit Quaternion(const QDomElement &element); + QDomElement domElement(const QString &name, QDomDocument &document) const; + void initFromDOMElement(const QDomElement &element); +//@} + +#ifdef DOXYGEN + /*! @name Output stream */ + //@{ + /*! Output stream operator. Enables debugging code like: + \code + Quaternion rot(...); + cout << "Rotation=" << rot << endl; + \endcode */ + std::ostream &operator<<(std::ostream &o, const qglviewer::Vec &); +//@} +#endif + +private: + /*! The internal data representation is private, use operator[] to access + * values. */ + qreal q[4]; +}; + +} // namespace qglviewer + +std::ostream &operator<<(std::ostream &o, const qglviewer::Quaternion &); + +#endif // QGLVIEWER_QUATERNION_H diff --git a/code/3rd_QGLViewer/QGLViewer/saveSnapshot.cpp b/code/3rd_QGLViewer/QGLViewer/saveSnapshot.cpp new file mode 100644 index 00000000..4ea90a10 --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/saveSnapshot.cpp @@ -0,0 +1,641 @@ +#include "qglviewer.h" + +#ifndef NO_VECTORIAL_RENDER +#include "VRender/VRender.h" +#include "ui_VRenderInterface.h" +#endif + +#include "ui_ImageInterface.h" + +// Output format list +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +////// Static global variables - local to this file ////// +// List of available output file formats, formatted for QFileDialog. +static QString formats; +// Converts QFileDialog resulting format to Qt snapshotFormat. +static QMap Qtformat; +// Converts Qt snapshotFormat to QFileDialog menu string. +static QMap FDFormatString; +// Converts snapshotFormat to file extension +static QMap extension; + +/*! Sets snapshotFileName(). */ +void QGLViewer::setSnapshotFileName(const QString &name) { + snapshotFileName_ = QFileInfo(name).absoluteFilePath(); +} + +#ifndef DOXYGEN +const QString &QGLViewer::snapshotFilename() const { + qWarning("snapshotFilename is deprecated. Use snapshotFileName() (uppercase " + "N) instead."); + return snapshotFileName(); +} +#endif + +/*! Opens a dialog that displays the different available snapshot formats. + +Then calls setSnapshotFormat() with the selected one (unless the user cancels). + +Returns \c false if the user presses the Cancel button and \c true otherwise. */ +bool QGLViewer::openSnapshotFormatDialog() { + bool ok = false; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QStringList list = formats.split(";;", QString::SkipEmptyParts); +#else + QStringList list = formats.split(";;", Qt::SkipEmptyParts); +#endif + int current = list.indexOf(FDFormatString[snapshotFormat()]); + QString format = + QInputDialog::getItem(this, "Snapshot format", "Select a snapshot format", + list, current, false, &ok); + if (ok) + setSnapshotFormat(Qtformat[format]); + return ok; +} + +// Finds all available Qt output formats, so that they can be available in +// saveSnapshot dialog. Initialize snapshotFormat() to the first one. +void QGLViewer::initializeSnapshotFormats() { + QList list = QImageWriter::supportedImageFormats(); + QStringList formatList; + for (int i = 0; i < list.size(); ++i) + formatList << QString(list.at(i).toUpper()); +// qWarning("Available image formats: "); +// QStringList::Iterator it = formatList.begin(); +// while( it != formatList.end() ) +// qWarning((*it++).); QT4 change this. qWarning no longer accepts +// QString + +#ifndef NO_VECTORIAL_RENDER + // We add the 3 vectorial formats to the list + formatList += "EPS"; + formatList += "PS"; + formatList += "XFIG"; +#endif + + // Check that the interesting formats are available and add them in "formats" + // Unused formats: XPM XBM PBM PGM + QStringList QtText, MenuText, Ext; + QtText += "JPEG"; + MenuText += "JPEG (*.jpg)"; + Ext += "jpg"; + QtText += "PNG"; + MenuText += "PNG (*.png)"; + Ext += "png"; + QtText += "EPS"; + MenuText += "Encapsulated Postscript (*.eps)"; + Ext += "eps"; + QtText += "PS"; + MenuText += "Postscript (*.ps)"; + Ext += "ps"; + QtText += "PPM"; + MenuText += "24bit RGB Bitmap (*.ppm)"; + Ext += "ppm"; + QtText += "BMP"; + MenuText += "Windows Bitmap (*.bmp)"; + Ext += "bmp"; + QtText += "XFIG"; + MenuText += "XFig (*.fig)"; + Ext += "fig"; + + QStringList::iterator itText = QtText.begin(); + QStringList::iterator itMenu = MenuText.begin(); + QStringList::iterator itExt = Ext.begin(); + + while (itText != QtText.end()) { + // QMessageBox::information(this, "Snapshot ", "Trying format\n"+(*itText)); + if (formatList.contains((*itText))) { + // QMessageBox::information(this, "Snapshot ", "Recognized + // format\n"+(*itText)); + if (formats.isEmpty()) + setSnapshotFormat(*itText); + else + formats += ";;"; + formats += (*itMenu); + Qtformat[(*itMenu)] = (*itText); + FDFormatString[(*itText)] = (*itMenu); + extension[(*itText)] = (*itExt); + } + // Synchronize parsing + itText++; + itMenu++; + itExt++; + } +} + +// Returns false if the user refused to use the fileName +static bool checkFileName(QString &fileName, QWidget *widget, + const QString &snapshotFormat) { + if (fileName.isEmpty()) + return false; + + // Check that extension has been provided + QFileInfo info(fileName); + + if (info.suffix().isEmpty()) { + // No extension given. Silently add one + if (fileName.right(1) != ".") + fileName += "."; + fileName += extension[snapshotFormat]; + info.setFile(fileName); + } else if (info.suffix() != extension[snapshotFormat]) { + // Extension is not appropriate. Propose a modification + QString modifiedName = info.absolutePath() + '/' + info.baseName() + "." + + extension[snapshotFormat]; + QFileInfo modifInfo(modifiedName); + int i = (QMessageBox::warning( + widget, "Wrong extension", + info.fileName() + " has a wrong extension.\nSave as " + + modifInfo.fileName() + " instead ?", + QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel)); + if (i == QMessageBox::Cancel) + return false; + + if (i == QMessageBox::Yes) { + fileName = modifiedName; + info.setFile(fileName); + } + } + + return true; +} + +#ifndef NO_VECTORIAL_RENDER +// static void drawVectorial(void* param) +void drawVectorial(void *param) { ((QGLViewer *)param)->drawVectorial(); } + +#ifndef DOXYGEN +class ProgressDialog { +public: + static void showProgressDialog(QOpenGLWidget *parent); + static void updateProgress(float progress, const QString &stepString); + static void hideProgressDialog(); + +private: + static QProgressDialog *progressDialog; +}; + +QProgressDialog *ProgressDialog::progressDialog = nullptr; + +void ProgressDialog::showProgressDialog(QOpenGLWidget *parent) { + progressDialog = new QProgressDialog(parent); + progressDialog->setWindowTitle("Image rendering progress"); + progressDialog->setMinimumSize(300, 40); + progressDialog->setCancelButton(nullptr); + progressDialog->show(); +} + +void ProgressDialog::updateProgress(float progress, const QString &stepString) { + progressDialog->setValue(int(progress * 100)); + QString message(stepString); + if (message.length() > 33) + message = message.left(17) + "..." + message.right(12); + progressDialog->setLabelText(message); + progressDialog->update(); + qApp->processEvents(); +} + +void ProgressDialog::hideProgressDialog() { + progressDialog->close(); + delete progressDialog; + progressDialog = nullptr; +} + +class VRenderInterface : public QDialog, public Ui::VRenderInterface { +public: + VRenderInterface(QWidget *parent) : QDialog(parent) { setupUi(this); } +}; + +#endif // DOXYGEN + +// Pops-up a vectorial output option dialog box and save to fileName +// Returns -1 in case of Cancel, 0 for success and (todo) error code in case of +// problem. +static int saveVectorialSnapshot(const QString &fileName, QOpenGLWidget *widget, + const QString &snapshotFormat) { + static VRenderInterface *VRinterface = nullptr; + + if (!VRinterface) + VRinterface = new VRenderInterface(widget); + + // Configure interface according to selected snapshotFormat + if (snapshotFormat == "XFIG") { + VRinterface->tightenBBox->setEnabled(false); + VRinterface->colorBackground->setEnabled(false); + } else { + VRinterface->tightenBBox->setEnabled(true); + VRinterface->colorBackground->setEnabled(true); + } + + if (VRinterface->exec() == QDialog::Rejected) + return -1; + + vrender::VRenderParams vparams; + vparams.setFilename(fileName); + + if (snapshotFormat == "EPS") + vparams.setFormat(vrender::VRenderParams::EPS); + if (snapshotFormat == "PS") + vparams.setFormat(vrender::VRenderParams::PS); + if (snapshotFormat == "XFIG") + vparams.setFormat(vrender::VRenderParams::XFIG); + + vparams.setOption(vrender::VRenderParams::CullHiddenFaces, + !(VRinterface->includeHidden->isChecked())); + vparams.setOption(vrender::VRenderParams::OptimizeBackFaceCulling, + VRinterface->cullBackFaces->isChecked()); + vparams.setOption(vrender::VRenderParams::RenderBlackAndWhite, + VRinterface->blackAndWhite->isChecked()); + vparams.setOption(vrender::VRenderParams::AddBackground, + VRinterface->colorBackground->isChecked()); + vparams.setOption(vrender::VRenderParams::TightenBoundingBox, + VRinterface->tightenBBox->isChecked()); + + switch (VRinterface->sortMethod->currentIndex()) { + case 0: + vparams.setSortMethod(vrender::VRenderParams::NoSorting); + break; + case 1: + vparams.setSortMethod(vrender::VRenderParams::BSPSort); + break; + case 2: + vparams.setSortMethod(vrender::VRenderParams::TopologicalSort); + break; + case 3: + vparams.setSortMethod(vrender::VRenderParams::AdvancedTopologicalSort); + break; + default: + qWarning("VRenderInterface::saveVectorialSnapshot: Unknown SortMethod"); + } + + vparams.setProgressFunction(&ProgressDialog::updateProgress); + ProgressDialog::showProgressDialog(widget); + widget->makeCurrent(); + widget->raise(); + vrender::VectorialRender(drawVectorial, (void *)widget, vparams); + ProgressDialog::hideProgressDialog(); + widget->setCursor(QCursor(Qt::ArrowCursor)); + + // Should return vparams.error(), but this is currently not set. + return 0; +} +#endif // NO_VECTORIAL_RENDER + +class ImageInterface : public QDialog, public Ui::ImageInterface { +public: + ImageInterface(QWidget *parent) : QDialog(parent) { setupUi(this); } +}; + +// Pops-up an image settings dialog box and save to fileName. +// Returns false in case of problem. +bool QGLViewer::saveImageSnapshot(const QString &fileName) { + static ImageInterface *imageInterface = nullptr; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) // The screen() function was introduced in the QWidget class starting from Qt 5.14. + qreal devicePixelRatio = screen()->devicePixelRatio(); +#else + qreal devicePixelRatio = devicePixelRatioF(); +#endif + qreal dipWidth = devicePixelRatio * width(); + qreal dipHeight = devicePixelRatio * height(); + + if (!imageInterface) + imageInterface = new ImageInterface(this); + + imageInterface->imgWidth->setValue(dipWidth); + imageInterface->imgHeight->setValue(dipHeight); + + imageInterface->imgQuality->setValue(snapshotQuality()); + + if (imageInterface->exec() == QDialog::Rejected) + return true; + + // Hide closed dialog + qApp->processEvents(); + + setSnapshotQuality(imageInterface->imgQuality->value()); + + QColor previousBGColor = backgroundColor(); + if (imageInterface->whiteBackground->isChecked()) + setBackgroundColor(Qt::white); + + QSize finalSize(imageInterface->imgWidth->value(), + imageInterface->imgHeight->value()); + + qreal oversampling = imageInterface->oversampling->value(); + QSize subSize(int(dipWidth / oversampling), int(dipHeight / oversampling)); + + qreal aspectRatio = dipWidth / static_cast(dipHeight); + qreal newAspectRatio = finalSize.width() / static_cast(finalSize.height()); + + qreal zNear = camera()->zNear(); + qreal zFar = camera()->zFar(); + + qreal xMin, yMin; + bool expand = imageInterface->expandFrustum->isChecked(); + if (camera()->type() == qglviewer::Camera::PERSPECTIVE) + if ((expand && (newAspectRatio > aspectRatio)) || + (!expand && (newAspectRatio < aspectRatio))) { + yMin = zNear * tan(camera()->fieldOfView() / 2.0); + xMin = newAspectRatio * yMin; + } else { + xMin = zNear * tan(camera()->fieldOfView() / 2.0) * aspectRatio; + yMin = xMin / newAspectRatio; + } + else { + GLdouble width, height; + camera()->getOrthoWidthHeight(width, height); + xMin = qreal(width); + yMin = qreal(height); + if ((expand && (newAspectRatio > aspectRatio)) || + (!expand && (newAspectRatio < aspectRatio))) + xMin = newAspectRatio * yMin; + else + yMin = xMin / newAspectRatio; + } + + QImage image(finalSize.width(), finalSize.height(), QImage::Format_ARGB32); + + if (image.isNull()) { + QMessageBox::warning(this, "Image saving error", + "Unable to create resulting image", QMessageBox::Ok, + QMessageBox::NoButton); + return false; + } + + // ProgressDialog disabled since it interfers with the screen grabing mecanism + // on some platforms. Too bad. ProgressDialog::showProgressDialog(this); + + qreal scaleX = subSize.width() / static_cast(finalSize.width()); + qreal scaleY = subSize.height() / static_cast(finalSize.height()); + + qreal deltaX = 2.0 * xMin * scaleX; + qreal deltaY = 2.0 * yMin * scaleY; + + int nbX = finalSize.width() / subSize.width(); + int nbY = finalSize.height() / subSize.height(); + + // Extra subimage on the right/bottom border(s) if needed + if (nbX * subSize.width() < finalSize.width()) + nbX++; + if (nbY * subSize.height() < finalSize.height()) + nbY++; + + makeCurrent(); + + // tileRegion_ is used by startScreenCoordinatesSystem to appropriately set + // the local coordinate system when tiling + tileRegion_ = new TileRegion(); + qreal tileXMin, tileWidth, tileYMin, tileHeight; + if ((expand && (newAspectRatio > aspectRatio)) || + (!expand && (newAspectRatio < aspectRatio))) { + qreal tileTotalWidth = newAspectRatio * dipHeight; + tileXMin = (dipWidth - tileTotalWidth) / 2.0; + tileWidth = tileTotalWidth * scaleX; + tileYMin = 0.0; + tileHeight = dipHeight * scaleY; + tileRegion_->textScale = 1.0 / scaleY; + } else { + qreal tileTotalHeight = dipWidth / newAspectRatio; + tileYMin = (dipHeight - tileTotalHeight) / 2.0; + tileHeight = tileTotalHeight * scaleY; + tileXMin = 0.0; + tileWidth = dipWidth * scaleX; + tileRegion_->textScale = 1.0 / scaleX; + } + + int count = 0; + for (int i = 0; i < nbX; i++) + for (int j = 0; j < nbY; j++) { + preDraw(); + + // Change projection matrix + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if (camera()->type() == qglviewer::Camera::PERSPECTIVE) + glFrustum(-xMin + i * deltaX, -xMin + (i + 1) * deltaX, + yMin - (j + 1) * deltaY, yMin - j * deltaY, zNear, zFar); + else + glOrtho(-xMin + i * deltaX, -xMin + (i + 1) * deltaX, + yMin - (j + 1) * deltaY, yMin - j * deltaY, zNear, zFar); + glMatrixMode(GL_MODELVIEW); + + tileRegion_->xMin = tileXMin + i * tileWidth; + tileRegion_->xMax = tileXMin + (i + 1) * tileWidth; + tileRegion_->yMin = tileYMin + j * tileHeight; + tileRegion_->yMax = tileYMin + (j + 1) * tileHeight; + + draw(); + postDraw(); + + // ProgressDialog::hideProgressDialog(); + // qApp->processEvents(); + + QImage snapshot = QOpenGLWidget::grabFramebuffer(); + + // ProgressDialog::showProgressDialog(this); + // ProgressDialog::updateProgress(count / (qreal)(nbX*nbY), + // "Generating image + // ["+QString::number(count)+"/"+QString::number(nbX*nbY)+"]"); + // qApp->processEvents(); + + QImage subImage = snapshot.scaled(subSize, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + + // Copy subImage in image + for (int ii = 0; ii < subSize.width(); ii++) { + int fi = i * subSize.width() + ii; + if (fi == image.width()) + break; + for (int jj = 0; jj < subSize.height(); jj++) { + int fj = j * subSize.height() + jj; + if (fj == image.height()) + break; + image.setPixel(fi, fj, subImage.pixel(ii, jj)); + } + } + count++; + } + + bool saveOK = image.save(fileName, snapshotFormat().toLatin1().constData(), + snapshotQuality()); + + // ProgressDialog::hideProgressDialog(); + // setCursor(QCursor(Qt::ArrowCursor)); + + delete tileRegion_; + tileRegion_ = nullptr; + + if (imageInterface->whiteBackground->isChecked()) + setBackgroundColor(previousBGColor); + + return saveOK; +} + +/*! Saves a snapshot of the current image displayed by the widget. + + Options are set using snapshotFormat(), snapshotFileName() and + snapshotQuality(). For non vectorial image formats, the image size is equal to + the current viewer's dimensions (see width() and height()). See + snapshotFormat() for details on supported formats. + + If \p automatic is \c false (or if snapshotFileName() is empty), a file dialog + is opened to ask for the file name. + + When \p automatic is \c true, the file name is set to \c NAME-NUMBER, where \c + NAME is snapshotFileName() and \c NUMBER is snapshotCounter(). The + snapshotCounter() is automatically incremented after each snapshot saving. This + is useful to create videos from your application: \code void Viewer::init() + { + resize(720, 576); // PAL DV format (use 720x480 for NTSC DV) + connect(this, SIGNAL(drawFinished(bool)), SLOT(saveSnapshot(bool))); + } + \endcode + Then call draw() in a loop (for instance using animate() and/or a camera() + KeyFrameInterpolator replay) to create your image sequence. + + If you want to create a Quicktime VR panoramic sequence, simply use code like + this: \code void Viewer::createQuicktime() + { + const int nbImages = 36; + for (int i=0; isetOrientation(2.0*M_PI/nbImages, 0.0); // Theta-Phi + orientation showEntireScene(); update(); // calls draw(), which emits + drawFinished(), which calls saveSnapshot() + } + } + \endcode + + If snapshotCounter() is negative, no number is appended to snapshotFileName() + and the snapshotCounter() is not incremented. This is useful to force the + creation of a file, overwriting the previous one. + + When \p overwrite is set to \c false (default), a window asks for confirmation + if the file already exists. In \p automatic mode, the snapshotCounter() is + incremented (if positive) until a non-existing file name is found instead. + Otherwise the file is overwritten without confirmation. + + The VRender library was written by Cyril Soler (Cyril dot Soler at imag dot + fr). If the generated PS or EPS file is not properly displayed, remove the + anti-aliasing option in your postscript viewer. + + \note In order to correctly grab the frame buffer, the QGLViewer window is + raised in front of other windows by this method. */ +void QGLViewer::saveSnapshot(bool automatic, bool overwrite) { + // Ask for file name + if (snapshotFileName().isEmpty() || !automatic) { + QString fileName; + QString selectedFormat = FDFormatString[snapshotFormat()]; + fileName = QFileDialog::getSaveFileName( + this, "Choose a file name to save under", snapshotFileName(), formats, + &selectedFormat, + overwrite ? QFileDialog::DontConfirmOverwrite + : QFlags(0)); + setSnapshotFormat(Qtformat[selectedFormat]); + + if (checkFileName(fileName, this, snapshotFormat())) + setSnapshotFileName(fileName); + else + return; + } + + QFileInfo fileInfo(snapshotFileName()); + + if ((automatic) && (snapshotCounter() >= 0)) { + // In automatic mode, names have a number appended + const QString baseName = fileInfo.baseName(); + QString count = QString("%1").arg(snapshotCounter_++, 4, 10, QChar('0')); + QString suffix; + suffix = fileInfo.suffix(); + if (suffix.isEmpty()) + suffix = extension[snapshotFormat()]; + fileInfo.setFile(fileInfo.absolutePath() + '/' + baseName + '-' + count + + '.' + suffix); + + if (!overwrite) + while (fileInfo.exists()) { + count = QString("%1").arg(snapshotCounter_++, 4, 10, QChar('0')); + fileInfo.setFile(fileInfo.absolutePath() + '/' + baseName + '-' + + count + '.' + fileInfo.suffix()); + } + } + + bool saveOK; +#ifndef NO_VECTORIAL_RENDER + if ((snapshotFormat() == "EPS") || (snapshotFormat() == "PS") || + (snapshotFormat() == "XFIG")) + // Vectorial snapshot. -1 means cancel, 0 is ok, >0 (should be) an error + saveOK = (saveVectorialSnapshot(fileInfo.filePath(), this, + snapshotFormat()) <= 0); + else +#endif + if (automatic) { + QImage snapshot = frameBufferSnapshot(); + saveOK = snapshot.save(fileInfo.filePath(), + snapshotFormat().toLatin1().constData(), + snapshotQuality()); + } else + saveOK = saveImageSnapshot(fileInfo.filePath()); + + if (!saveOK) + QMessageBox::warning(this, "Snapshot problem", + "Unable to save snapshot in\n" + fileInfo.filePath()); +} + +QImage QGLViewer::frameBufferSnapshot() { + // Viewer must be on top of other windows. + makeCurrent(); + raise(); + // Hack: Qt has problems if the frame buffer is grabbed after QFileDialog is + // displayed. We grab the frame buffer before, even if it might be not + // necessary (vectorial rendering). The problem could not be reproduced on a + // simple example to submit a Qt bug. However, only grabs the backgroundImage + // in the eponym example. May come from the driver. + return QOpenGLWidget::grabFramebuffer(); +} + +/*! Same as saveSnapshot(), except that it uses \p fileName instead of + snapshotFileName(). + + If \p fileName is empty, opens a file dialog to select the name. + + Snapshot settings are set from snapshotFormat() and snapshotQuality(). + + Asks for confirmation when \p fileName already exists and \p overwrite is \c + false (default). + + \attention If \p fileName is a char* (as is "myFile.jpg"), it may be casted + into a \c bool, and the other saveSnapshot() method may be used instead. Pass + QString("myFile.jpg") as a parameter to prevent this. */ +void QGLViewer::saveSnapshot(const QString &fileName, bool overwrite) { + const QString previousName = snapshotFileName(); + const int previousCounter = snapshotCounter(); + setSnapshotFileName(fileName); + setSnapshotCounter(-1); + saveSnapshot(true, overwrite); + setSnapshotFileName(previousName); + setSnapshotCounter(previousCounter); +} + +/*! Takes a snapshot of the current display and pastes it to the clipboard. + +This action is activated by the KeyboardAction::SNAPSHOT_TO_CLIPBOARD enum, +binded to \c Ctrl+C by default. +*/ +void QGLViewer::snapshotToClipboard() { + QClipboard *cb = QApplication::clipboard(); + cb->setImage(frameBufferSnapshot()); +} diff --git a/code/3rd_QGLViewer/QGLViewer/vec.cpp b/code/3rd_QGLViewer/QGLViewer/vec.cpp new file mode 100644 index 00000000..dbb68c8d --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/vec.cpp @@ -0,0 +1,142 @@ +#include "vec.h" +#include "domUtils.h" + +// 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; +} diff --git a/code/3rd_QGLViewer/QGLViewer/vec.h b/code/3rd_QGLViewer/QGLViewer/vec.h new file mode 100644 index 00000000..048a7b4f --- /dev/null +++ b/code/3rd_QGLViewer/QGLViewer/vec.h @@ -0,0 +1,362 @@ +#ifndef QGLVIEWER_VEC_H +#define QGLVIEWER_VEC_H + +#include +#include + +#include + +// Included by all files as vec.h is at the end of the include hierarchy +#include "config.h" // Specific configuration options. + +namespace qglviewer { + +/*! \brief The Vec class represents 3D positions and 3D vectors. + \class Vec vec.h QGLViewer/vec.h + + Vec is used as a parameter and return type by many methods of the library. It + provides classical algebraic computational methods and is compatible with + OpenGL: + + \code + // Draws a point located at 3.0 OpenGL units in front of the camera + Vec pos = camera()->position() + 3.0 * camera()->viewDirection(); + glBegin(GL_POINTS); + glVertex3fv(pos); + glEnd(); + \endcode + + This makes of Vec a good candidate for representing positions and vectors in + your programs. Since it is part of the \c qglviewer namespace, specify \c + qglviewer::Vec or use the qglviewer namespace: \code using namespace + qglviewer; \endcode + +

Interface with other vector classes

+ + Vec implements a universal explicit converter, based on the \c [] \c operator. + Everywhere a \c const \c Vec& argument is expected, you can use your own + vector type instead, as long as it implements this operator (see the Vec(const + C& c) documentation). + + See also the Quaternion and the Frame documentations. + \nosubgrouping */ +class QGLVIEWER_EXPORT Vec { + +// If your compiler complains the "The class "qglviewer::Vec" has no member +// "x"." Add your architecture Q_OS_XXXX flag (see qglobal.h) in this list. +#if defined(Q_OS_IRIX) || defined(Q_OS_AIX) || defined(Q_OS_HPUX) +#define QGLVIEWER_UNION_NOT_SUPPORTED +#endif + +public: +/*! The internal data representation is public. One can use v.x, v.y, v.z. See + * also operator[](). */ +#if defined(DOXYGEN) || defined(QGLVIEWER_UNION_NOT_SUPPORTED) + qreal x, y, z; +#else + union { + struct { + qreal x, y, z; + }; + qreal v_[3]; + }; +#endif + + /*! @name Setting the value */ + //@{ + /*! Default constructor. Value is set to (0,0,0). */ + Vec() : x(0.0), y(0.0), z(0.0) {} + + /*! Standard constructor with the x, y and z values. */ + Vec(qreal X, qreal Y, qreal Z) : x(X), y(Y), z(Z) {} + + /*! Universal explicit converter from any class to Vec. You can use your own +vector class everywhere a \c const \c Vec& parameter is required, as long as it +implements the \c operator[ ]: + +\code +class MyVec +{ + // ... + qreal operator[](int i) const { returns x, y or z when i=0, 1 or 2; } +} + +MyVec v(...); +camera()->setPosition(v); +\endcode + +Note that standard vector types (STL, \c qreal[3], ...) implement this operator +and can hence be used in place of Vec. See also operator const qreal*() .*/ + template explicit Vec(const C &c) : x(c[0]), y(c[1]), z(c[2]) {} + // Should NOT be explicit to prevent conflicts with operator<<. + + // ! Copy constructor + // Vec(const Vec& v) : x(v.x), y(v.y), z(v.z) {} + + /*! Equal operator. + Vec &operator=(const Vec &v) { + x = v.x; + y = v.y; + z = v.z; + return *this; + } + */ + + /*! Set the current value. May be faster than using operator=() with a + * temporary Vec(x,y,z). */ + void setValue(qreal X, qreal Y, qreal Z) { + x = X; + y = Y; + z = Z; + } + + // Universal equal operator which allows the use of any type in place of Vec, + // as long as the [] operator is implemented (v[0]=v.x, v[1]=v.y, v[2]=v.z). + // template + // Vec& operator=(const C& c) + // { + // x=c[0]; y=c[1]; z=c[2]; + // return *this; + // } + //@} + + /*! @name Accessing the value */ + //@{ + /*! Bracket operator, with a constant return value. \p i must range in [0..2]. + */ + qreal operator[](int i) const { +#ifdef QGLVIEWER_UNION_NOT_SUPPORTED + return (&x)[i]; +#else + return v_[i]; +#endif + } + + /*! Bracket operator returning an l-value. \p i must range in [0..2]. */ + qreal &operator[](int i) { +#ifdef QGLVIEWER_UNION_NOT_SUPPORTED + return (&x)[i]; +#else + return v_[i]; +#endif + } + +#ifndef DOXYGEN + /*! This method is deprecated since version 2.0. Use operator const qreal* + * instead. */ + const qreal *address() const { + qWarning( + "Vec::address() is deprecated, use operator const qreal* instead."); + return operator const qreal *(); + } +#endif + + /*! Conversion operator returning the memory address of the vector. + +Very convenient to pass a Vec pointer as a parameter to \c GLdouble OpenGL +functions: \code Vec pos, normal; glNormal3dv(normal); glVertex3dv(pos); +\endcode */ + operator const qreal *() const { +#ifdef QGLVIEWER_UNION_NOT_SUPPORTED + return &x; +#else + return v_; +#endif + } + + /*! Non const conversion operator returning the memory address of the vector. + +Useful to pass a Vec to a method that requires and fills a \c qreal*, as +provided by certain libraries. */ + operator qreal *() { +#ifdef QGLVIEWER_UNION_NOT_SUPPORTED + return &x; +#else + return v_; +#endif + } + + /*! Conversion operator returning the memory address of the vector. + +Very convenient to pass a Vec pointer as a \c float parameter to OpenGL +functions: \code Vec pos, normal; glNormal3fv(normal); glVertex3fv(pos); +\endcode +\note The returned float array is a static shared by all \c Vec instances. */ + operator const float *() const { + static float *const result = new float[3]; + result[0] = (float)x; + result[1] = (float)y; + result[2] = (float)z; + return result; + } + //@} + + /*! @name Algebraic computations */ + //@{ + /*! Returns the sum of the two vectors. */ + friend Vec operator+(const Vec &a, const Vec &b) { + return Vec(a.x + b.x, a.y + b.y, a.z + b.z); + } + + /*! Returns the difference of the two vectors. */ + friend Vec operator-(const Vec &a, const Vec &b) { + return Vec(a.x - b.x, a.y - b.y, a.z - b.z); + } + + /*! Unary minus operator. */ + friend Vec operator-(const Vec &a) { return Vec(-a.x, -a.y, -a.z); } + + /*! Returns the product of the vector with a scalar. */ + friend Vec operator*(const Vec &a, qreal k) { + return Vec(a.x * k, a.y * k, a.z * k); + } + + /*! Returns the product of a scalar with the vector. */ + friend Vec operator*(qreal k, const Vec &a) { return a * k; } + + /*! Returns the division of the vector with a scalar. + +Too small \p k values are \e not tested (unless the library was compiled with +the "debug" Qt \c CONFIG flag) and may result in \c NaN values. */ + friend Vec operator/(const Vec &a, qreal k) { +#ifndef QT_NO_DEBUG + if (fabs(k) < 1.0E-10) + qWarning("Vec::operator / : dividing by a null value (%f)", k); +#endif + return Vec(a.x / k, a.y / k, a.z / k); + } + + /*! Returns \c true only when the two vector are not equal (see operator==()). + */ + friend bool operator!=(const Vec &a, const Vec &b) { return !(a == b); } + + /*! Returns \c true when the squaredNorm() of the difference vector is lower + * than 1E-10. */ + friend bool operator==(const Vec &a, const Vec &b) { + const qreal epsilon = 1.0E-10; + return (a - b).squaredNorm() < epsilon; + } + + /*! Adds \p a to the vector. */ + Vec &operator+=(const Vec &a) { + x += a.x; + y += a.y; + z += a.z; + return *this; + } + + /*! Subtracts \p a to the vector. */ + Vec &operator-=(const Vec &a) { + x -= a.x; + y -= a.y; + z -= a.z; + return *this; + } + + /*! Multiply the vector by a scalar \p k. */ + Vec &operator*=(qreal k) { + x *= k; + y *= k; + z *= k; + return *this; + } + + /*! Divides the vector by a scalar \p k. + +An absolute \p k value lower than 1E-10 will print a warning if the library was +compiled with the "debug" Qt \c CONFIG flag. Otherwise, no test is performed for +efficiency reasons. */ + Vec &operator/=(qreal k) { +#ifndef QT_NO_DEBUG + if (fabs(k) < 1.0E-10) + qWarning("Vec::operator /= : dividing by a null value (%f)", k); +#endif + x /= k; + y /= k; + z /= k; + return *this; + } + + /*! Dot product of the two Vec. */ + friend qreal operator*(const Vec &a, const Vec &b) { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + /*! Cross product of the two vectors. Same as cross(). */ + friend Vec operator^(const Vec &a, const Vec &b) { return cross(a, b); } + + /*! Cross product of the two Vec. Mind the order ! */ + friend Vec cross(const Vec &a, const Vec &b) { + return Vec(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x); + } + + Vec orthogonalVec() const; +//@} + +/*! @name Norm of the vector */ +//@{ +#ifndef DOXYGEN + /*! This method is deprecated since version 2.0. Use squaredNorm() instead. */ + qreal sqNorm() const { return x * x + y * y + z * z; } +#endif + + /*! Returns the \e squared norm of the Vec. */ + qreal squaredNorm() const { return x * x + y * y + z * z; } + + /*! Returns the norm of the vector. */ + qreal norm() const { return sqrt(x * x + y * y + z * z); } + + /*! Normalizes the Vec and returns its original norm. + +Normalizing a null vector will result in \c NaN values. */ + qreal normalize() { + const qreal n = norm(); +#ifndef QT_NO_DEBUG + if (n < 1.0E-10) + qWarning("Vec::normalize: normalizing a null vector (norm=%f)", n); +#endif + *this /= n; + return n; + } + + /*! Returns a unitary (normalized) \e representation of the vector. The + * original Vec is not modified. */ + Vec unit() const { + Vec v = *this; + v.normalize(); + return v; + } + //@} + + /*! @name Projection */ + //@{ + void projectOnAxis(const Vec &direction); + void projectOnPlane(const Vec &normal); + //@} + + /*! @name XML representation */ + //@{ + explicit Vec(const QDomElement &element); + QDomElement domElement(const QString &name, QDomDocument &document) const; + void initFromDOMElement(const QDomElement &element); +//@} + +#ifdef DOXYGEN + /*! @name Output stream */ + //@{ + /*! Output stream operator. Enables debugging code like: +\code +Vec pos(...); +cout << "Position=" << pos << endl; +\endcode */ + std::ostream &operator<<(std::ostream &o, const qglviewer::Vec &); +//@} +#endif +}; + +} // namespace qglviewer + +std::ostream &operator<<(std::ostream &o, const qglviewer::Vec &); + +#endif // QGLVIEWER_VEC_H diff --git a/code/3rd_QGLViewer/README b/code/3rd_QGLViewer/README new file mode 100644 index 00000000..cefab8b7 --- /dev/null +++ b/code/3rd_QGLViewer/README @@ -0,0 +1,41 @@ + + l i b Q G L V i e w e r + + Version #VERSION#. Packaged on #DATE# + + + Copyright (C) 2002-2023 Gilles Debunne. All rights reserved. + #WEBURL# + Send e-mail to contact@libqglviewer.com + + +libQGLViewer is a C++ library based on Qt that eases the creation of OpenGL 3D viewers. + +It provides some of the typical 3D viewer functionalities, such as the possibility to +move the camera using the mouse, which lacks in most of the other APIs. Other features +include mouse manipulated frames, interpolated keyFrames, object selection, stereo +display, screenshot saving and much more. It can be used by OpenGL beginners as well as +to create complex applications, being fully customizable and easy to extend. + +Based on the Qt toolkit, it compiles on any architecture (Unix-Linux, Mac, Windows). +Full reference documentation and many examples are provided. libQGLViewer does not +display 3D scenes in various formats, but it can be the base for the coding of such a +viewer. + +libQGLViewer uses dual licensing: it is freely available under the terms of the GNU-GPL +license for open source software development, while commercial applications can apply +for a commercial license. + + +The library's main functionalities are: + + - A camera trackball to intuitively move the camera in the scene. + - Screenshot saving in different file formats (JPG, PNG, EPS...). + - Easy scene object selection and manipulation using the mouse. + - Definition and replay of keyFrame paths. + - Stereo display (provided that your hardware supports it). + - Clean, well-designed and easily extendable API. + - Many examples and a complete reference documentation. + +See the doc/index.html page for details. + diff --git a/code/3rd_QGLViewer/README.md b/code/3rd_QGLViewer/README.md new file mode 100644 index 00000000..26fbc3b5 --- /dev/null +++ b/code/3rd_QGLViewer/README.md @@ -0,0 +1,11 @@ +libQGLViewer +============ + +libQGLViewer is an open source C++ library based on Qt that eases the creation of OpenGL 3D viewers. + +libQGLViewer provides some of the typical 3D viewer functionalities, such as the possibility to move the camera using the mouse, which lacks in most of the other APIs. Other features include mouse manipulated frames, interpolated keyFrames, object selection, stereo display, screenshot saving and much more. It can be used by OpenGL beginners as well as to create complex applications, being fully customizable and easy to extend. +Based on the Qt toolkit, it compiles on any architecture (Unix-Linux, Mac, Windows). Full reference documentation and many examples are provided. libQGLViewer does not display 3D scenes in various formats, but it can be the base for the coding of such a viewer. + +libQGLViewer uses dual licensing: it is freely available under the terms of the GNU-GPL license for open source software development, while commercial applications can apply for a commercial license. + +http://www.libqglviewer.com diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index b635d8b3..964ffa56 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12) if (APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.12" CACHE STRING "Minimum OS X deployment version" FORCE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version" FORCE) message(STATUS "Minimum OS X deployment version: ${CMAKE_OSX_DEPLOYMENT_TARGET}") endif () @@ -19,7 +19,8 @@ endif() ################################################################################ -set (CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) ################################################################################ @@ -39,10 +40,9 @@ set(POLYFIT_ROOT ${CMAKE_CURRENT_LIST_DIR}) set(POLYFIT_INCLUDE_DIR ${POLYFIT_ROOT} ${CMAKE_CURRENT_BINARY_DIR}) set(POLYFIT_SOURCE_DIR ${POLYFIT_ROOT}) - set(POLYFIT_glpk_DIR ${POLYFIT_ROOT}/3rd_glpk) set(POLYFIT_lpsolve_DIR ${POLYFIT_ROOT}/3rd_lpsolve) -set(POLYFIT_qglviewer_DIR ${POLYFIT_ROOT}/3rd_QGLViewer-2.6.3) +set(POLYFIT_qglviewer_DIR ${POLYFIT_ROOT}/3rd_QGLViewer) set(POLYFIT_scip_DIR ${POLYFIT_ROOT}/3rd_scip) set(POLYFIT_soplex_DIR ${POLYFIT_ROOT}/3rd_soplex) set(POLYFIT_glew_DIR ${POLYFIT_ROOT}/3rd_glew) @@ -50,6 +50,9 @@ set(POLYFIT_glew_DIR ${POLYFIT_ROOT}/3rd_glew) ### conditionally compile certain modules depending on libraries found on the system list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) +if (POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) +endif () find_package(Boost REQUIRED) ################################################################################ @@ -65,7 +68,7 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY add_subdirectory(3rd_glpk) add_subdirectory(3rd_lpsolve) -add_subdirectory(3rd_QGLViewer-2.6.3) +add_subdirectory(3rd_QGLViewer) add_subdirectory(3rd_scip) add_subdirectory(3rd_soplex) add_subdirectory(3rd_glew) diff --git a/code/PolyFit/CMakeLists.txt b/code/PolyFit/CMakeLists.txt index 1dd169c4..6fce6dd8 100644 --- a/code/PolyFit/CMakeLists.txt +++ b/code/PolyFit/CMakeLists.txt @@ -1,67 +1,7 @@ get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) project(${PROJECT_NAME}) - -# ------------------------------------------------------------------------------ -# Qt -# ------------------------------------------------------------------------------ - -## we will use cmake autouic/automoc/autorcc features -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set( QT5_ROOT_PATH CACHE PATH "Qt5 root directory (i.e. where the 'bin' folder lies)" ) -if ( QT5_ROOT_PATH ) - list( APPEND CMAKE_PREFIX_PATH ${QT5_ROOT_PATH} ) -endif() - -# find qt5 components -# find_package(Qt5 COMPONENTS Core Gui OpenGL Widgets REQUIRED) -find_package(Qt5Core QUIET) -find_package(Qt5Gui QUIET) -find_package(Qt5OpenGL QUIET) -find_package(Qt5Widgets QUIET) -find_package(Qt5Xml QUIET) - -if (Qt5Core_FOUND AND Qt5Gui_FOUND AND Qt5OpenGL_FOUND AND Qt5Widgets_FOUND) - set(QT5_FOUND TRUE) -endif () - -if (NOT QT5_FOUND) - message(WARNING "The demo program '${PROJECT_NAME}' requires Qt but Qt was not found. " - "You can set 'Qt5_DIR' to the directory containing 'Qt5Config.cmake' or 'qt5-config.cmake'. " - "Optionally, you can set the Qt5 root directory 'QT5_ROOT_PATH' to the directory " - "containing the 'bin' folder.") - return() -endif () - - -# in the case no Qt5Config.cmake file could be found, cmake will explicitly ask the user for the QT5_DIR containing it! -# thus no need to keep additional variables and checks - -# Starting with the QtCore lib, find the bin and root directories -get_target_property(QT5_LIB_LOCATION Qt5::Core LOCATION_${CMAKE_BUILD_TYPE}) -get_filename_component(QT_BINARY_DIR ${QT5_LIB_LOCATION} DIRECTORY) - -# Apple uses frameworks - move up until we get to the base directory to set the bin directory properly -if ( APPLE ) - get_filename_component(QT_BINARY_DIR ${QT_BINARY_DIR} DIRECTORY) - set(QT_BINARY_DIR "${QT_BINARY_DIR}/bin") - - set( MACDEPLOYQT "${QT_BINARY_DIR}/macdeployqt" ) -endif() - -# set QT5_ROOT_PATH if it wasn't set by the user -if ( NOT QT5_ROOT_PATH ) - get_filename_component(QT5_ROOT_PATH ${QT_BINARY_DIR} DIRECTORY) -endif() - - -# turn on QStringBuilder for more efficient string construction -# see https://doc.qt.io/qt-5/qstring.html#more-efficient-string-construction -add_definitions( -DQT_USE_QSTRINGBUILDER ) +include(../cmake/UseQt.cmake) set(${PROJECT_NAME}_HEADERS main_window.h @@ -126,11 +66,6 @@ set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "PolyFit") # deprecated API in order to know how to port your code away from it. target_compile_definitions(${PROJECT_NAME} PRIVATE QT_DEPRECATED_WARNINGS) +target_link_libraries( ${PROJECT_NAME} ${QtLibs}) -target_link_libraries( ${PROJECT_NAME} Qt5::Core ) -target_link_libraries( ${PROJECT_NAME} Qt5::Gui ) -target_link_libraries( ${PROJECT_NAME} Qt5::Widgets ) -target_link_libraries( ${PROJECT_NAME} Qt5::OpenGL ) -target_link_libraries( ${PROJECT_NAME} Qt5::Xml ) - -target_link_libraries( ${PROJECT_NAME} basic math model method renderer 3rd_glew 3rd_QGLViewer-2.6.3) \ No newline at end of file +target_link_libraries( ${PROJECT_NAME} basic math model method renderer 3rd_glew 3rd_QGLViewer) \ No newline at end of file diff --git a/code/PolyFit/dlg/weight_panel_click.cpp b/code/PolyFit/dlg/weight_panel_click.cpp index 631298f1..f0af17c8 100644 --- a/code/PolyFit/dlg/weight_panel_click.cpp +++ b/code/PolyFit/dlg/weight_panel_click.cpp @@ -18,12 +18,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "weight_panel_click.h" + #include #include #include -#include "main_window.h" + #include "../math/math_types.h" #include "../method/method_global.h" +#include "main_window.h" static QPointF pointFromWeights( @@ -73,9 +75,9 @@ static QGradient gradient(const QColor &color, const QRectF &rect) { QColor c = color; c.setAlpha(160); QLinearGradient result(rect.topLeft(), rect.bottomRight()); - result.setColorAt(0, c.dark(150)); - result.setColorAt(0.5, c.light(200)); - result.setColorAt(1, c.dark(150)); + result.setColorAt(0, c.darker(150)); + result.setColorAt(0.5, c.lighter(200)); + result.setColorAt(1, c.darker(150)); return result; } diff --git a/code/PolyFit/dlg/weight_panel_click.h b/code/PolyFit/dlg/weight_panel_click.h index 0b54e797..c11d2339 100644 --- a/code/PolyFit/dlg/weight_panel_click.h +++ b/code/PolyFit/dlg/weight_panel_click.h @@ -21,9 +21,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #ifndef WEIGHT_PANEL_CLICK_H #define WEIGHT_PANEL_CLICK_H -#include -#include "ui_weight_panel_click.h" -#include +#include +#include class MainWindow; diff --git a/code/PolyFit/dlg/weight_panel_manual.cpp b/code/PolyFit/dlg/weight_panel_manual.cpp index e3acda17..1e46bb74 100644 --- a/code/PolyFit/dlg/weight_panel_manual.cpp +++ b/code/PolyFit/dlg/weight_panel_manual.cpp @@ -18,11 +18,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "weight_panel_manual.h" -#include "main_window.h" -#include "paint_canvas.h" + #include "../math/math_types.h" #include "../method/method_global.h" +#include "main_window.h" +#include "paint_canvas.h" + +#include "ui_weight_panel_manual.h" + WeightPanelManual::WeightPanelManual(QWidget *parent) : QDialog(parent) diff --git a/code/PolyFit/dlg/weight_panel_manual.h b/code/PolyFit/dlg/weight_panel_manual.h index 4af437b3..576dd306 100644 --- a/code/PolyFit/dlg/weight_panel_manual.h +++ b/code/PolyFit/dlg/weight_panel_manual.h @@ -21,9 +21,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #ifndef WEIGHT_PANEL_MANUAL_H #define WEIGHT_PANEL_MANUAL_H -#include -#include "ui_weight_panel_manual.h" - +#include +#include class MainWindow; diff --git a/code/PolyFit/dlg/wgt_render.cpp b/code/PolyFit/dlg/wgt_render.cpp index d732e5db..8f3d7ffb 100644 --- a/code/PolyFit/dlg/wgt_render.cpp +++ b/code/PolyFit/dlg/wgt_render.cpp @@ -18,17 +18,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "wgt_render.h" -#include "main_window.h" -#include "paint_canvas.h" -#include "../basic/file_utils.h" -#include "../renderer/surface_render.h" -#include "../renderer/point_set_render.h" + #include #include +#include "../renderer/surface_render.h" +#include "../renderer/point_set_render.h" + +#include "main_window.h" +#include "paint_canvas.h" -WgtRender::WgtRender(QWidget *parent) +WgtRender::WgtRender(QWidget *parent) { setupUi(this); diff --git a/code/PolyFit/dlg/wgt_render.h b/code/PolyFit/dlg/wgt_render.h index a9e87ee5..5afe40ce 100644 --- a/code/PolyFit/dlg/wgt_render.h +++ b/code/PolyFit/dlg/wgt_render.h @@ -20,13 +20,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #ifndef WGT_RENDER_H #define WGT_RENDER_H +#include + #include "ui_wgt_render.h" + class MainWindow; class SurfaceRender; class PointSetRender; - class WgtRender : public QWidget, public Ui::WidgetRender { Q_OBJECT @@ -68,7 +70,6 @@ private Q_SLOTS: SurfaceRender* mesh_render_; PointSetRender* point_set_render_; - }; #endif diff --git a/code/PolyFit/dlg/wgt_render.ui b/code/PolyFit/dlg/wgt_render.ui index 4402eb5f..c6e40274 100644 --- a/code/PolyFit/dlg/wgt_render.ui +++ b/code/PolyFit/dlg/wgt_render.ui @@ -7,7 +7,7 @@ 0 0 336 - 228 + 280 @@ -74,7 +74,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -106,7 +106,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -191,7 +191,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -223,7 +223,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -286,7 +286,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -324,7 +324,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -362,7 +362,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -408,7 +408,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -421,7 +421,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -522,7 +522,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -535,7 +535,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -595,7 +595,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -671,7 +671,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -684,7 +684,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal diff --git a/code/PolyFit/main.cpp b/code/PolyFit/main.cpp index d43f6720..44ad7572 100644 --- a/code/PolyFit/main.cpp +++ b/code/PolyFit/main.cpp @@ -18,10 +18,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include +#include + #include #include #include -#include #include #include "main_window.h" @@ -45,10 +46,27 @@ int main(int argc, char **argv) setlocale(LC_NUMERIC, "C"); #endif -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif + // Note: Calling QSurfaceFormat::setDefaultFormat() before constructing the + // QApplication instance is mandatory on some platforms(for example, macOS) + // when an OpenGL core profile context is requested. This is to ensure + // that resource sharing between contexts stays functional as all internal + // contexts are created using the correct version and profile. + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + format.setVersion(4, 3); + format.setProfile(QSurfaceFormat::CompatibilityProfile); + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + format.setSamples(4); +#ifndef NDEBUG + format.setOption(QSurfaceFormat::DebugContext); +#endif + QSurfaceFormat::setDefaultFormat(format); + QApplication app(argc, argv); MainWindow window; diff --git a/code/PolyFit/main_window.cpp b/code/PolyFit/main_window.cpp index 1f90d426..85c97c3a 100644 --- a/code/PolyFit/main_window.cpp +++ b/code/PolyFit/main_window.cpp @@ -17,19 +17,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "main_window.h" + #include #include -#include #include #include -#include -#include #include -#include #include #include +#include -#include "main_window.h" #include "paint_canvas.h" #include "dlg/wgt_render.h" @@ -46,8 +44,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "../basic/attribute_serializer.h" -MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) -: QMainWindow(parent, flags) +MainWindow::MainWindow(QWidget *parent) +: QMainWindow(parent) , curDataDirectory_(".") { setupUi(this); @@ -92,15 +90,7 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) ////////////////////////////////////////////////////////////////////////// - // Setup the format to allow anti-aliasing if the graphic driver allows this. - QGLFormat format = QGLFormat::defaultFormat(); - format.setProfile(QGLFormat::CompatibilityProfile); - format.setSampleBuffers(true); // you can also call setOption(QGL::SampleBuffers) - format.setSamples(8); // 8 is enough - - mainCanvas_ = new PaintCanvas(this, format); - mainCanvas_->setAttribute(Qt::WA_MouseTracking); - mainCanvas_->setMouseTracking(true); + mainCanvas_ = new PaintCanvas(this); layoutCanvas->addWidget(mainCanvas_); ////////////////////////////////////////////////////////////////////////// @@ -174,21 +164,6 @@ void MainWindow::notify_progress(std::size_t value) { mainCanvas_->update_all(); } - -void MainWindow::dragEnterEvent(QDragEnterEvent *e) { - if (e->mimeData()->hasUrls()) { - e->acceptProposedAction(); - } -} - -void MainWindow::dropEvent(QDropEvent *e) { - foreach (const QUrl &url, e->mimeData()->urls()) { - const QString &fileName = url.toLocalFile(); - doOpen(fileName); - } -} - - void MainWindow::createActions() { connect(actionOpen, SIGNAL(triggered()), this, SLOT(open())); diff --git a/code/PolyFit/main_window.h b/code/PolyFit/main_window.h index 96dc27a8..0ae86e5a 100644 --- a/code/PolyFit/main_window.h +++ b/code/PolyFit/main_window.h @@ -23,12 +23,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include -#include "ui_main_window.h" #include "../math/math_types.h" #include "../basic/logger.h" #include "../basic/progress.h" #include "../math/linear_program_solver.h" +#include "ui_main_window.h" + class QLabel; class QComboBox; class PaintCanvas; @@ -46,7 +47,7 @@ class MainWindow Q_OBJECT public: - MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0); + MainWindow(QWidget *parent = 0); ~MainWindow(); PaintCanvas* canvas() { return mainCanvas_; } @@ -97,8 +98,6 @@ public Q_SLOTS: QString strippedName(const QString &fullFileName); protected: - void dragEnterEvent(QDragEnterEvent *e); - void dropEvent(QDropEvent *e); void closeEvent(QCloseEvent *e); private: diff --git a/code/PolyFit/paint_canvas.cpp b/code/PolyFit/paint_canvas.cpp index 1fdc4b6f..13176313 100644 --- a/code/PolyFit/paint_canvas.cpp +++ b/code/PolyFit/paint_canvas.cpp @@ -18,10 +18,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "paint_canvas.h" -#include "main_window.h" -#include "dlg/weight_panel_manual.h" -#include "../3rd_QGLViewer-2.6.3/manipulatedCameraFrame.h" +#include + +#include +#include + +#include "../3rd_QGLViewer/QGLViewer/manipulatedCameraFrame.h" #include "../basic/file_utils.h" #include "../basic/stop_watch.h" #include "../model/map_editor.h" @@ -32,17 +35,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "../method/hypothesis_generator.h" #include "../method/face_selection.h" -#include -#include - -#include -#include +#include "main_window.h" using namespace qglviewer; -PaintCanvas::PaintCanvas(QWidget *parent, QGLFormat format) - : QGLViewer(format, parent) +PaintCanvas::PaintCanvas(QWidget *parent) + : QGLViewer(parent) , coord_system_region_size_(150) , show_coord_sys_(true) , point_set_(nil) @@ -207,7 +206,6 @@ void PaintCanvas::init() setFPSIsDisplayed(false); } - void PaintCanvas::draw() { if (fatal_opengl_error) { return; @@ -252,6 +250,11 @@ void PaintCanvas::draw() { drawText(30, 180, " - Pan: right button", font); drawText(30, 210, " - Zoom: wheel", font); } + + // Liangliang: It seems the renderText() func disables multi-sample and depth test + // Is this a bug in Qt ? + glEnable(GL_MULTISAMPLE); + glEnable(GL_DEPTH_TEST); } @@ -276,12 +279,12 @@ void PaintCanvas::snapshotScreen(const QString& fileName) { if (need_hide) show_coord_sys_ = true; - updateGL(); + update(); } void PaintCanvas::update_graphics() { - updateGL(); + update(); // This approach has significant drawbacks. For example, imagine you wanted to perform two such loops // in parallel-calling one of them would effectively halt the other until the first one is finished @@ -293,7 +296,7 @@ void PaintCanvas::update_graphics() { } void PaintCanvas::update_all() { - updateGL(); + update(); main_window_->updateStatusBar(); // This approach has significant drawbacks. For example, imagine you wanted to perform two such loops @@ -308,7 +311,7 @@ void PaintCanvas::update_all() { void PaintCanvas::showCoordinateSystem(bool b) { show_coord_sys_ = b; - updateGL(); + update(); } @@ -328,7 +331,7 @@ void PaintCanvas::fitScreen() { setSceneBoundingBox(vmin, vmax); showEntireScene(); - updateGL(); + update(); } @@ -372,15 +375,6 @@ void PaintCanvas::drawCornerAxis() // Draw text id glColor3f(0, 0, 0); - // Liangliang: It seems the renderText() func disables multi-sample. - // Is this a bug in Qt ? - GLboolean anti_alias = glIsEnabled(GL_MULTISAMPLE); - const_cast(this)->renderText(axis_size, 0, 0, "X"); - const_cast(this)->renderText(0, axis_size, 0, "Y"); - const_cast(this)->renderText(0, 0, axis_size, "Z"); - if (anti_alias) - glEnable(GL_MULTISAMPLE); - glMatrixMode(GL_PROJECTION); glPopMatrix(); @@ -465,97 +459,6 @@ void PaintCanvas::setShowResult(bool b) { update_all(); } -void PaintCanvas::saveStateAsMappleFormat() { - std::string str = FileUtils::replace_extension(stateFileName().toStdString(), "state"); - QString name = QString::fromStdString(str); - if (name.isEmpty()) - return; - - // Write the state to file - std::ofstream output(name.toStdString().c_str()); - if (output.fail()) { - QMessageBox::warning(window(), tr("Save state to file error"), tr("Unable to create file %1").arg(name)); - return; - } - - //----------------------------------------------------- - - // first line is just a comment - output << "" << std::endl << std::endl; - - //----------------------------------------------------- - - // write foreground and background colors - output << "" << std::endl; - QColor fc = foregroundColor(); - output << "\t foreground: " << fc.red() << " " << fc.green() << " " << fc.blue() << std::endl; - QColor bc = backgroundColor(); - output << "\t background: " << bc.red() << " " << bc.green() << " " << bc.blue() << std::endl; - output << "" << std::endl << std::endl; - - //----------------------------------------------------- - - // Revolve or fly camera mode is not saved - // ... - - //----------------------------------------------------- - - output << "" << std::endl; - output << "\t cameraIsEdited: " << cameraIsEdited() << std::endl; - output << "\t gridIsDrawn: " << gridIsDrawn() << std::endl; - output << "\t axisIsDrawn: " << axisIsDrawn() << std::endl; - output << "\t FPSIsDisplayed: " << FPSIsDisplayed() << std::endl; - output << "" << std::endl << std::endl; - - //----------------------------------------------------- - - output << "" << std::endl; - output << "\t state: " << window()->windowState() << std::endl;; - if (window()->windowState() == Qt::WindowNoState) { - output << "\t size: " << window()->width() << " " << window()->height() << std::endl; - output << "\t position: " << window()->pos().x() << " " << window()->pos().y() << std::endl; - } - output << "" << std::endl << std::endl; - - //----------------------------------------------------- - - output << "" << std::endl; - // Restore original QCamera zClippingCoefficient before saving. - if (cameraIsEdited()) - camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_); - - switch (camera()->type()) { - case Camera::PERSPECTIVE: output << "\t type: " << "PERSPECTIVE" << std::endl; break; - case Camera::ORTHOGRAPHIC: output << "\t type: " << "ORTHOGRAPHIC" << std::endl; break; - } - output << "\t zClippingCoefficient: " << QString::number(camera()->zClippingCoefficient()).toStdString() << std::endl; - output << "\t zNearCoefficient: " << QString::number(camera()->zNearCoefficient()).toStdString() << std::endl; - output << "\t sceneRadius: " << QString::number(camera()->sceneRadius()).toStdString() << std::endl; - output << "\t orthoCoefficient: " << QString::number(camera()->orthoCoefficient()).toStdString() << std::endl; - output << "\t fieldOfView: " << QString::number(camera()->fieldOfView()).toStdString() << std::endl; - output << "\t sceneCenter: " << camera()->sceneCenter() << std::endl; - - // ManipulatedCameraFrame - output << "\t position: " << camera()->frame()->position() << std::endl; - output << "\t orientation: " << camera()->frame()->orientation() << std::endl; - output << "\t wheelSens: " << camera()->frame()->wheelSensitivity() << std::endl; - output << "\t rotSens: " << camera()->frame()->rotationSensitivity() << std::endl; - output << "\t zoomSens: " << camera()->frame()->zoomSensitivity() << std::endl; - output << "\t spinSens: " << camera()->frame()->spinningSensitivity() << std::endl; - output << "\t transSens: " << camera()->frame()->translationSensitivity() << std::endl; - - output << "\t zoomsOnPivotPoint: " << camera()->frame()->zoomsOnPivotPoint() << std::endl; - output << "\t pivotPoint: " << camera()->frame()->pivotPoint() << std::endl; - output << "\t rotatesAroundUpVector: " << camera()->frame()->rotatesAroundUpVector() << std::endl; - output << "\t flySpeed: " << camera()->frame()->flySpeed() << std::endl; - output << "\t sceneUpVector: " << camera()->frame()->sceneUpVector() << std::endl; - - if (cameraIsEdited()) - // #CONNECTION# 5.0 from setCameraIsEdited() - camera()->setZClippingCoefficient(5.0); - output << "" << std::endl << std::endl; -} - void PaintCanvas::refinePlanes() { if (!pointSet()) { diff --git a/code/PolyFit/paint_canvas.h b/code/PolyFit/paint_canvas.h index 8c1ba693..22a4fe23 100644 --- a/code/PolyFit/paint_canvas.h +++ b/code/PolyFit/paint_canvas.h @@ -22,14 +22,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include -#include "../3rd_QGLViewer-2.6.3/qglviewer.h" +#include "../3rd_QGLViewer/QGLViewer/qglviewer.h" #include "../basic/color.h" #include "../math/math_types.h" -#include "../basic/canvas.h" #include "../model/point_set.h" #include "../model/map.h" -#include "../math/linear_program_solver.h" - class MainWindow; class SurfaceRender; @@ -41,7 +38,7 @@ class PaintCanvas : public QGLViewer Q_OBJECT public: - PaintCanvas(QWidget *parent, QGLFormat format); + PaintCanvas(QWidget *parent); ~PaintCanvas(); public: @@ -98,8 +95,6 @@ public Q_SLOTS: void setShowCandidates(bool); void setShowResult(bool); - void saveStateAsMappleFormat(); - private : void drawCornerAxis(); diff --git a/code/cmake/FindGUROBI.cmake b/code/cmake/FindGUROBI.cmake index d66bb670..cfa2863c 100755 --- a/code/cmake/FindGUROBI.cmake +++ b/code/cmake/FindGUROBI.cmake @@ -48,41 +48,43 @@ if (NOT GUROBI_FOUND) # Hardcoded search paths set(SEARCH_PATHS_FOR_HEADERS "$ENV{GUROBI_HOME}/include" + "/Library/gurobi1200/macos_universal2/include" "/Library/gurobi1003/macos_universal2/include" "/Library/gurobi952/macos_universal2/include" "/home/liangliang/dev/gurobi952/include" "D:\\dev\\Gurobi-10.0.3\\win64\\include" "D:\\dev\\Gurobi-9.5.2\\win64\\include" - ) + ) set(SEARCH_PATHS_FOR_LIBRARIES "$ENV{GUROBI_HOME}/lib" + "/Library/gurobi1200/macos_universal2/lib" "/Library/gurobi1003/macos_universal2/lib" "/Library/gurobi952/macos_universal2/lib" "/home/liangliang/dev/gurobi952/lib" "D:\\dev\\Gurobi-10.0.3\\win64\\lib" "D:\\dev\\Gurobi-9.5.2\\win64\\lib" - ) + ) find_path(GUROBI_INCLUDE_DIR gurobi_c++.h PATHS ${SEARCH_PATHS_FOR_HEADERS} - ) + ) find_library(GUROBI_C_LIBRARY - NAMES gurobi100 libgurobi + NAMES gurobi120 gurobi100 libgurobi PATHS ${SEARCH_PATHS_FOR_LIBRARIES} - ) + ) find_library(GUROBI_CXX_LIBRARY_DEBUG NAMES gurobi_c++ gurobi_c++mdd2017 PATHS ${SEARCH_PATHS_FOR_LIBRARIES} - ) + ) find_library(GUROBI_CXX_LIBRARY_RELEASE NAMES gurobi_c++ gurobi_c++md2017 PATHS ${SEARCH_PATHS_FOR_LIBRARIES} - ) + ) # setup header file directories set(GUROBI_INCLUDE_DIRS ${GUROBI_INCLUDE_DIR}) @@ -92,7 +94,7 @@ if (NOT GUROBI_FOUND) debug ${GUROBI_CXX_LIBRARY_DEBUG} optimized ${GUROBI_CXX_LIBRARY_RELEASE} ${GUROBI_C_LIBRARY} - ) + ) endif () diff --git a/code/cmake/UseQt.cmake b/code/cmake/UseQt.cmake new file mode 100644 index 00000000..79fe3d72 --- /dev/null +++ b/code/cmake/UseQt.cmake @@ -0,0 +1,75 @@ +# ************************************************************************* +# Copyright (C) 2015 Liangliang Nan +# https://3d.bk.tudelft.nl/liangliang/ +# +# This file is part of Easy3D. If it is useful in your research/work, +# I would be grateful if you show your appreciation by citing it: +# ------------------------------------------------------------------ +# Liangliang Nan. +# Easy3D: a lightweight, easy-to-use, and efficient C++ library +# for processing and rendering 3D data. +# Journal of Open Source Software, 6(64), 3255, 2021. +# ------------------------------------------------------------------ +# +# Easy3D is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 3 +# as published by the Free Software Foundation. +# +# Easy3D is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# ************************************************************************* + + +# ------------------------------------------------------------------------------ +# This file sets up Qt for CMake. When Qt6 was setup successfully, Qt6_FOUND +# will be set. If Qt6 is not found, it will try to find Qt5. If Qt5 is found, +# Qt5_FOUND will be set. If both Qt6 and Qt5 are not found, it will stop the +# configuration and show an error message. +# If either Qt6 or Qt5 is found, it will set QtLibs to the corresponding Qt +# libraries, e.g., Qt5Core, Qt5Gui, Qt5Widgets, Qt5OpenGL, Qt5Xml, etc. +# +# To use Qt in your program, you only need to include this file and specifying +# Qt libraries to link against, e.g., +# ------------------------------------------------------------------------ +# project(${PROJECT_NAME}) +# include( ../cmake/UseQt5.cmake ) +# add_executable(${PROJECT_NAME}, main.cpp) +# target_link_libraries(${PROJECT_NAME} ${QtLibs}) +# ------------------------------------------------------------------------ +# NOTE: 'UseQt.cmake' must be included after you define your project but before +# 'add_executable()' or 'add_library()'. +# +# The recommended way to specify libraries and headers with CMake is to use the +# target_link_libraries command. This command automatically adds appropriate +# include directories, compile definitions, the position-independent-code flag, +# and links to the qtmain.lib library on Windows. +# ------------------------------------------------------------------------------ + +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) +# Instruct CMake to run uic automatically when needed. +set(CMAKE_AUTOUIC ON) +# Instruct CMake to run rcc automatically when needed. +set(CMAKE_AUTORCC ON) + +# This will find the Qt files. +find_package(Qt6 COMPONENTS Core Widgets OpenGL OpenGLWidgets Xml QUIET) +if (Qt6_FOUND) + message(STATUS "Found Qt6 version: ${Qt6Core_VERSION}") + set(QtLibs Qt::Core Qt::Widgets Qt::OpenGL Qt::OpenGLWidgets Qt::Xml) +else() + find_package(Qt5 COMPONENTS Core Widgets OpenGL Xml QUIET) + if (Qt5_FOUND) + message(STATUS "Found Qt5 version: ${Qt5Core_VERSION}") + set(QtLibs Qt5::Core Qt5::Widgets Qt5::OpenGL Qt5::Xml) + else() + message(FATAL_ERROR "Qt is required, either Qt6 or Qt5, but both cannot be found") + endif() +endif() \ No newline at end of file diff --git a/code/model/kdtree/PriorityQueue.h b/code/model/kdtree/PriorityQueue.h index 20919a7e..7ee23ef7 100644 --- a/code/model/kdtree/PriorityQueue.h +++ b/code/model/kdtree/PriorityQueue.h @@ -113,7 +113,7 @@ namespace kdtree { m_current++; m_queue[m_current].index = index; m_queue[m_current].weight = weight; - register int i=m_current; + int i=m_current; while(i>1 && (m_queue[i].weight > m_queue[i>>1].weight)) { swapElements(i, i>>1); i >>= 1; @@ -164,8 +164,8 @@ namespace kdtree { protected: - inline void restore(register int L, register int R) { - register int i, j; + inline void restore(int L, int R) { + int i, j; i = L; while (i <= (R>>1)) { if( 2*i < R && m_queue[2*i+1].weight > m_queue[2*i].weight) { @@ -282,7 +282,7 @@ namespace kdtree { m_current++; m_queue[m_current].index = index; m_queue[m_current].weight = weight; - register int i=m_current; + int i=m_current; while(i>1 && (m_queue[i].weight < m_queue[i>>1].weight)) { swapElements(i, i>>1); i >>= 1; @@ -333,8 +333,8 @@ namespace kdtree { protected: - inline void restore(register int L, register int R) { - register int i, j; + inline void restore(int L, int R) { + int i, j; i = L; while (i <= (R>>1)) { if( 2*i < R && m_queue[2*i+1].weight < m_queue[2*i].weight) { diff --git a/code/model/kdtree/kdTree.cpp b/code/model/kdtree/kdTree.cpp index dabefa8c..5231f9a6 100644 --- a/code/model/kdtree/kdTree.cpp +++ b/code/model/kdtree/kdTree.cpp @@ -440,8 +440,8 @@ namespace kdtree { } float BaseKdNode::computeBoxDistance(const Vector3D &q, const Vector3D &lo, const Vector3D &hi) { - register float dist = 0.0; - register float t; + float dist = 0.0; + float t; if (q[0] < lo[0]) { t = lo[0] - q[0]; @@ -472,8 +472,8 @@ namespace kdtree { } float BaseKdNode::computeBoxMaxDistance(const Vector3D &q, const Vector3D &lo, const Vector3D &hi) { - register float dist; - register float t1, t2; + float dist; + float t1, t2; t1 = fabsf( q[0] - lo[0] ); t2 = fabsf( q[0] - hi[0] ); @@ -546,7 +546,7 @@ namespace kdtree { KdBoxFace( vert[2], vert[3], vert[7], vert[6] ), KdBoxFace( vert[0], vert[4], vert[5], vert[1] ), }; - register int i, j; + int i, j; unsigned char bOutside[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Check two end points against all 6 face planes, @@ -613,7 +613,7 @@ namespace kdtree { float absb = ( face.b > 0 ? face.b : -face.b ); float absc = ( face.c > 0 ? face.c : -face.c ); - register int i; + int i; Vector2D facePoints[4], point; if( absa > absb && absa > absc ) @@ -702,8 +702,8 @@ namespace kdtree { } void KdNode::queryNode(float rd, PQueue* queryPriorityQueue) { - register float old_off = g_queryOffsets[m_dim]; - register float new_off = g_queryPosition[m_dim] - m_cutVal; + float old_off = g_queryOffsets[m_dim]; + float new_off = g_queryPosition[m_dim] - m_cutVal; if (new_off < 0) { m_children[0]->queryNode(rd, queryPriorityQueue); rd = rd - SQR(old_off) + SQR(new_off); @@ -728,7 +728,7 @@ namespace kdtree { { KdTreePoint points[4]; Vector3D vlow, vhigh; - for( register unsigned int i = 0; i < 2; i++ ) { + for( unsigned int i = 0; i < 2; i++ ) { m_children[i]->createBoundingBox( vlow, vhigh ); points[2*i].pos = vlow; points[2*i+1].pos = vhigh; @@ -763,7 +763,7 @@ namespace kdtree { float sqrDist; //use pointer arithmetic to speed up the linear traversing KdTreePoint* point = m_points; - for (register unsigned int i=0; ipos - g_queryPosition).getSquaredLength(); if (sqrDist < queryPriorityQueue->getMaxWeight()) { queryPriorityQueue->insert(point->index, sqrDist, g_queryAll); @@ -787,7 +787,7 @@ namespace kdtree { float sqrDist, sqrDistLine, sqrDistVert; KdTreePoint* point = m_points; // check points individually - for( register unsigned int i = 0; i < m_nOfElements; i++ ) { + for( unsigned int i = 0; i < m_nOfElements; i++ ) { vc = point->pos - g_queryLine[0]; sqrDist = vc.getSquaredLength(); sqrDistLine = Vector3D::dotProduct( vc, g_queryLineDir ); @@ -823,7 +823,7 @@ namespace kdtree { float sqrDist, distLine, sqrDistVert, cosAngle; KdTreePoint* point = m_points; // check points individually - for( register unsigned int i = 0; i < m_nOfElements; i++ ) { + for( unsigned int i = 0; i < m_nOfElements; i++ ) { vc = point->pos - g_queryEye; sqrDist = vc.getSquaredLength(); if( sqrDist < g_queryMinSqrRange ) continue;