mirror of
https://github.com/ConjureETS/LOG750-LAB2.git
synced 2026-03-23 19:11:20 +00:00
Lab 1 End Version
This commit is contained in:
parent
fda113767d
commit
0977688583
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# Generated Bin and debug
|
||||
debug/
|
||||
object_script.*.Debug
|
||||
object_script.*.Release
|
||||
|
||||
# C++ objects and libs
|
||||
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.a
|
||||
*.la
|
||||
*.lai
|
||||
*.so
|
||||
*.dll
|
||||
*.dylib
|
||||
|
||||
# Qt-es
|
||||
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*.qbs.user
|
||||
*.qbs.user.*
|
||||
*.moc
|
||||
moc_*.cpp
|
||||
qrc_*.cpp
|
||||
ui_*.h
|
||||
Makefile*
|
||||
*build-*
|
||||
|
||||
# QtCreator
|
||||
|
||||
*.autosave
|
||||
|
||||
# QtCtreator Qml
|
||||
*.qmlproject.user
|
||||
*.qmlproject.user.*
|
||||
|
||||
# QtCtreator CMake
|
||||
CMakeLists.txt.user*
|
||||
|
||||
297
QGLViewer/ImageInterface.ui
Normal file
297
QGLViewer/ImageInterface.ui
Normal file
@ -0,0 +1,297 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ImageInterface</class>
|
||||
<widget class="QDialog" name="ImageInterface">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>298</width>
|
||||
<height>204</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Image settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="imgWidth">
|
||||
<property name="toolTip">
|
||||
<string>Width of the image (in pixels)</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>32000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="imgHeight">
|
||||
<property name="toolTip">
|
||||
<string>Height of the image (in pixels)</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>32000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Image quality</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="imgQuality">
|
||||
<property name="toolTip">
|
||||
<string>Between 0 (smallest files) and 100 (highest quality)</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Oversampling</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="oversampling">
|
||||
<property name="toolTip">
|
||||
<string>Antialiases image (when larger then 1.0)</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>x </string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>64.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="whiteBackground">
|
||||
<property name="toolTip">
|
||||
<string>Use white as background color</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use white background</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="expandFrustum">
|
||||
<property name="toolTip">
|
||||
<string>When image aspect ratio differs from viewer's one, expand frustum as needed. Fits inside current frustum otherwise.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Expand frustum if needed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>131</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="okButton">
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>okButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ImageInterface</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>135</x>
|
||||
<y>184</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>96</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>cancelButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ImageInterface</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>226</x>
|
||||
<y>184</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>179</x>
|
||||
<y>282</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
217
QGLViewer/VRenderInterface.ui
Normal file
217
QGLViewer/VRenderInterface.ui
Normal file
@ -0,0 +1,217 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VRenderInterface</class>
|
||||
<widget class="QDialog" name="VRenderInterface">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>309</width>
|
||||
<height>211</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Vectorial rendering options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="includeHidden">
|
||||
<property name="toolTip">
|
||||
<string>Hidden polygons are also included in the output (usually twice bigger)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Include hidden parts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cullBackFaces">
|
||||
<property name="toolTip">
|
||||
<string>Back faces (non clockwise point ordering) are removed from the output</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cull back faces</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="blackAndWhite">
|
||||
<property name="toolTip">
|
||||
<string>Black and white rendering</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Black and white</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="colorBackground">
|
||||
<property name="toolTip">
|
||||
<string>Use current background color instead of white</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Color background</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="tightenBBox">
|
||||
<property name="toolTip">
|
||||
<string>Fit output bounding box to current display</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tighten bounding box</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="leftMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="sortLabel">
|
||||
<property name="toolTip">
|
||||
<string>Polygon depth sorting method</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sort method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="sortMethod">
|
||||
<property name="toolTip">
|
||||
<string>Polygon depth sorting method</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>No sorting</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BSP</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Topological</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Advanced topological</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>31</width>
|
||||
<height>41</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="SaveButton">
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="CancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<tabstops>
|
||||
<tabstop>SaveButton</tabstop>
|
||||
<tabstop>CancelButton</tabstop>
|
||||
<tabstop>includeHidden</tabstop>
|
||||
<tabstop>cullBackFaces</tabstop>
|
||||
<tabstop>blackAndWhite</tabstop>
|
||||
<tabstop>colorBackground</tabstop>
|
||||
<tabstop>tightenBBox</tabstop>
|
||||
<tabstop>sortMethod</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>SaveButton</sender>
|
||||
<signal>released()</signal>
|
||||
<receiver>VRenderInterface</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>16</x>
|
||||
<y>210</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>35</x>
|
||||
<y>176</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>CancelButton</sender>
|
||||
<signal>released()</signal>
|
||||
<receiver>VRenderInterface</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>225</x>
|
||||
<y>198</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>211</x>
|
||||
<y>180</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
2073
QGLViewer/camera.cpp
Normal file
2073
QGLViewer/camera.cpp
Normal file
File diff suppressed because it is too large
Load Diff
505
QGLViewer/camera.h
Normal file
505
QGLViewer/camera.h
Normal file
@ -0,0 +1,505 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <QMap>
|
||||
#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.
|
||||
|
||||
<h3>Mouse manipulation</h3>
|
||||
|
||||
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 <a
|
||||
href="../mouse.html">mouse page</a>.
|
||||
|
||||
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').
|
||||
|
||||
<h3>Other functionalities</h3>
|
||||
|
||||
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 <a
|
||||
href="../keyboard.html">keyboard page</a> 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 <a href="../examples/stereoViewer.html">stereoViewer example</a> 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
|
||||
{
|
||||
#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<qreal>(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() [TODO Update with QOpenGLWidget] */
|
||||
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() [TODO Update with QOpenGLWidget] */
|
||||
int screenHeight() const { return screenHeight_; }
|
||||
void getViewport(GLint viewport[4]) const;
|
||||
qreal pixelGLRatio(const Vec& position) 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 <a href="../examples/standardCamera.html">standardCamera example</a>.
|
||||
|
||||
\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; }
|
||||
//@}
|
||||
|
||||
|
||||
/*! @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);
|
||||
//@}
|
||||
|
||||
|
||||
/*! @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 loadModelViewMatrixStereo(bool leftBuffer=true) const;
|
||||
|
||||
void getProjectionMatrix(GLfloat m[16]) const;
|
||||
void getProjectionMatrix(GLdouble m[16]) const;
|
||||
void getProjectionMatrix(QMatrix4x4& m) const;
|
||||
|
||||
void getModelViewMatrix(GLfloat m[16]) const;
|
||||
void getModelViewMatrix(GLdouble m[16]) const;
|
||||
void getModelViewMatrix(QMatrix4x4& m) const;
|
||||
|
||||
void getModelViewProjectionMatrix(GLfloat m[16]) const;
|
||||
void getModelViewProjectionMatrix(GLdouble m[16]) const;
|
||||
void getModelViewProjectionMatrix(QMatrix4x4& m) 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<unsigned int, KeyFrameInterpolator*> kfi_;
|
||||
KeyFrameInterpolator* interpolationKfi_;
|
||||
};
|
||||
|
||||
} // namespace qglviewer
|
||||
|
||||
#endif // QGLVIEWER_CAMERA_H
|
||||
97
QGLViewer/config.h
Normal file
97
QGLViewer/config.h
Normal file
@ -0,0 +1,97 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <qglobal.h>
|
||||
|
||||
#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 <QOpenGLWidget>
|
||||
|
||||
// Container classes interfaces changed a lot in Qt.
|
||||
// Compatibility patches are all grouped here.
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
|
||||
// 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 <iostream>
|
||||
// # include <qstring.h>
|
||||
// std::ostream& operator<<(std::ostream& out, const QString& str)
|
||||
// { out << str.latin1(); return out; }
|
||||
// #endif
|
||||
|
||||
#endif // QGLVIEWER_CONFIG_H
|
||||
291
QGLViewer/constraint.cpp
Normal file
291
QGLViewer/constraint.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
/****************************************************************************
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
338
QGLViewer/constraint.h
Normal file
338
QGLViewer/constraint.h
Normal file
@ -0,0 +1,338 @@
|
||||
/****************************************************************************
|
||||
|
||||
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()).
|
||||
|
||||
<h3>How does it work ?</h3>
|
||||
|
||||
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.
|
||||
|
||||
<h3>Implemented Constraints</h3>
|
||||
|
||||
Classical axial and plane Constraints are provided for convenience: see the LocalConstraint,
|
||||
WorldConstraint and CameraConstraint classes' documentations.
|
||||
|
||||
Try the <a href="../examples/constrainedFrame.html">constrainedFrame</a> and <a
|
||||
href="../examples/constrainedCamera.html">constrainedCamera</a> examples for an illustration.
|
||||
|
||||
<h3>Creating new Constraints</h3>
|
||||
|
||||
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
|
||||
<a href="../examples/constrainedCamera.html">contrainedCamera</a> and
|
||||
<a href="../examples/constrainedFrame.html">constrainedFrame</a> 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 <a href="../examples/constrainedFrame.html">constrainedFrame</a> 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 <a href="../examples/constrainedFrame.html">constrainedFrame</a> and <a
|
||||
href="../examples/multiView.html">multiView</a> 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 <a href="../examples/constrainedFrame.html">constrainedFrame</a> and <a
|
||||
href="../examples/constrainedCamera.html">constrainedCamera</a> 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
|
||||
161
QGLViewer/domUtils.h
Normal file
161
QGLViewer/domUtils.h
Normal file
@ -0,0 +1,161 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <QDomElement>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QColor>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#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
|
||||
1144
QGLViewer/frame.cpp
Normal file
1144
QGLViewer/frame.cpp
Normal file
File diff suppressed because it is too large
Load Diff
415
QGLViewer/frame.h
Normal file
415
QGLViewer/frame.h
Normal file
@ -0,0 +1,415 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <QObject>
|
||||
#include <QString>
|
||||
|
||||
#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 <a href="../examples/frameTransform.html">frameTransform example</a> 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.
|
||||
|
||||
<h3>Hierarchy of Frames</h3>
|
||||
|
||||
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 <a href="../introduction.html">introduction page</a>).
|
||||
|
||||
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.
|
||||
|
||||
<h3>Constraints</h3>
|
||||
|
||||
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 <a href="../examples/constrainedFrame.html">constrainedFrame</a>, <a
|
||||
href="../examples/constrainedCamera.html">constrainedCamera</a> and <a
|
||||
href="../examples/luxo.html">luxo</a> examples for an illustration.
|
||||
|
||||
Classical constraints are provided for convenience (see LocalConstraint, WorldConstraint and
|
||||
CameraConstraint) and new constraints can very easily be implemented.
|
||||
|
||||
<h3>Derived classes</h3>
|
||||
|
||||
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) [TODO Update with QOpenGLWidget]
|
||||
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
|
||||
549
QGLViewer/keyFrameInterpolator.cpp
Normal file
549
QGLViewer/keyFrameInterpolator.cpp
Normal file
@ -0,0 +1,549 @@
|
||||
/****************************************************************************
|
||||
|
||||
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*>(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 <a href="../examples/keyFrames.html">keyFrames example</a> 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 <a href="../examples/keyFrames.html">keyFrames example</a> for an
|
||||
illustration.
|
||||
|
||||
\c NULL \p frame pointers are silently ignored. The keyFrameTime() has to be monotonously
|
||||
increasing over keyFrames.
|
||||
|
||||
Use addKeyFrame(const Frame&, qreal) to add keyFrame by values. */
|
||||
void KeyFrameInterpolator::addKeyFrame(const Frame* const frame, qreal time)
|
||||
{
|
||||
if (!frame)
|
||||
return;
|
||||
|
||||
if (keyFrame_.isEmpty())
|
||||
interpolationTime_ = time;
|
||||
|
||||
if ( (!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time) )
|
||||
qWarning("Error in KeyFrameInterpolator::addKeyFrame: time is not monotone");
|
||||
else
|
||||
keyFrame_.append(new KeyFrame(frame, time));
|
||||
connect(frame, SIGNAL(modified()), SLOT(invalidateValues()));
|
||||
valuesAreValid_ = false;
|
||||
pathIsValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
resetInterpolation();
|
||||
}
|
||||
|
||||
/*! Appends a new keyFrame to the path, with its associated \p time (in seconds).
|
||||
|
||||
The path will use the current \p frame state. If you want the path to change when \p frame is
|
||||
modified, you need to pass a \e pointer to the Frame instead (see addKeyFrame(const Frame*,
|
||||
qreal)).
|
||||
|
||||
The keyFrameTime() have to be monotonously increasing over keyFrames. */
|
||||
void KeyFrameInterpolator::addKeyFrame(const Frame& frame, qreal time)
|
||||
{
|
||||
if (keyFrame_.isEmpty())
|
||||
interpolationTime_ = time;
|
||||
|
||||
if ( (!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time) )
|
||||
qWarning("Error in KeyFrameInterpolator::addKeyFrame: time is not monotone");
|
||||
else
|
||||
keyFrame_.append(new KeyFrame(frame, time));
|
||||
|
||||
valuesAreValid_ = false;
|
||||
pathIsValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
resetInterpolation();
|
||||
}
|
||||
|
||||
|
||||
/*! Appends a new keyFrame to the path.
|
||||
|
||||
Same as addKeyFrame(const Frame* frame, qreal), except that the keyFrameTime() is set to the
|
||||
previous keyFrameTime() plus one second (or 0.0 if there is no previous keyFrame). */
|
||||
void KeyFrameInterpolator::addKeyFrame(const Frame* const frame)
|
||||
{
|
||||
qreal time;
|
||||
if (keyFrame_.isEmpty())
|
||||
time = 0.0;
|
||||
else
|
||||
time = lastTime() + 1.0;
|
||||
|
||||
addKeyFrame(frame, time);
|
||||
}
|
||||
|
||||
/*! Appends a new keyFrame to the path.
|
||||
|
||||
Same as addKeyFrame(const Frame& frame, qreal), except that the keyFrameTime() is automatically set
|
||||
to previous keyFrameTime() plus one second (or 0.0 if there is no previous keyFrame). */
|
||||
void KeyFrameInterpolator::addKeyFrame(const Frame& frame)
|
||||
{
|
||||
qreal time;
|
||||
if (keyFrame_.isEmpty())
|
||||
time = 0.0;
|
||||
else
|
||||
time = keyFrame_.last()->time() + 1.0;
|
||||
|
||||
addKeyFrame(frame, time);
|
||||
}
|
||||
|
||||
/*! Removes all keyFrames from the path. The numberOfKeyFrames() is set to 0. */
|
||||
void KeyFrameInterpolator::deletePath()
|
||||
{
|
||||
stopInterpolation();
|
||||
qDeleteAll(keyFrame_);
|
||||
keyFrame_.clear();
|
||||
pathIsValid_ = false;
|
||||
valuesAreValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::updateModifiedFrameValues()
|
||||
{
|
||||
Quaternion prevQ = keyFrame_.first()->orientation();
|
||||
KeyFrame* kf;
|
||||
for (int i=0; i<keyFrame_.size(); ++i)
|
||||
{
|
||||
kf = keyFrame_.at(i);
|
||||
if (kf->frame())
|
||||
kf->updateValuesFromPointer();
|
||||
kf->flipOrientationIfNeeded(prevQ);
|
||||
prevQ = kf->orientation();
|
||||
}
|
||||
|
||||
KeyFrame* prev = keyFrame_.first();
|
||||
kf = keyFrame_.first();
|
||||
int index = 1;
|
||||
while (kf)
|
||||
{
|
||||
KeyFrame* next = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL;
|
||||
index++;
|
||||
if (next)
|
||||
kf->computeTangent(prev, next);
|
||||
else
|
||||
kf->computeTangent(prev, kf);
|
||||
prev = kf;
|
||||
kf = next;
|
||||
}
|
||||
valuesAreValid_ = true;
|
||||
}
|
||||
|
||||
/*! Returns the Frame associated with the keyFrame at index \p index.
|
||||
|
||||
See also keyFrameTime(). \p index has to be in the range 0..numberOfKeyFrames()-1.
|
||||
|
||||
\note If this keyFrame was defined using a pointer to a Frame (see addKeyFrame(const Frame*
|
||||
const)), the \e current pointed Frame state is returned. */
|
||||
Frame KeyFrameInterpolator::keyFrame(int index) const
|
||||
{
|
||||
const KeyFrame* const kf = keyFrame_.at(index);
|
||||
return Frame(kf->position(), kf->orientation());
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the \p index keyFrame.
|
||||
|
||||
See also keyFrame(). \p index has to be in the range 0..numberOfKeyFrames()-1. */
|
||||
qreal KeyFrameInterpolator::keyFrameTime(int index) const
|
||||
{
|
||||
return keyFrame_.at(index)->time();
|
||||
}
|
||||
|
||||
/*! Returns the duration of the KeyFrameInterpolator path, expressed in seconds.
|
||||
|
||||
Simply corresponds to lastTime() - firstTime(). Returns 0.0 if the path has less than 2 keyFrames.
|
||||
See also keyFrameTime(). */
|
||||
qreal KeyFrameInterpolator::duration() const
|
||||
{
|
||||
return lastTime() - firstTime();
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the first keyFrame, expressed in seconds.
|
||||
|
||||
Returns 0.0 if the path is empty. See also lastTime(), duration() and keyFrameTime(). */
|
||||
qreal KeyFrameInterpolator::firstTime() const
|
||||
{
|
||||
if (keyFrame_.isEmpty())
|
||||
return 0.0;
|
||||
else
|
||||
return keyFrame_.first()->time();
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the last keyFrame, expressed in seconds.
|
||||
|
||||
Returns 0.0 if the path is empty. See also firstTime(), duration() and keyFrameTime(). */
|
||||
qreal KeyFrameInterpolator::lastTime() const
|
||||
{
|
||||
if (keyFrame_.isEmpty())
|
||||
return 0.0;
|
||||
else
|
||||
return keyFrame_.last()->time();
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::updateCurrentKeyFrameForTime(qreal time)
|
||||
{
|
||||
// Assertion: times are sorted in monotone order.
|
||||
// Assertion: keyFrame_ is not empty
|
||||
|
||||
// TODO: Special case for loops when closed path is implemented !!
|
||||
if (!currentFrameValid_)
|
||||
// Recompute everything from scrach
|
||||
currentFrame_[1]->toFront();
|
||||
|
||||
while (currentFrame_[1]->peekNext()->time() > time)
|
||||
{
|
||||
currentFrameValid_ = false;
|
||||
if (!currentFrame_[1]->hasPrevious())
|
||||
break;
|
||||
currentFrame_[1]->previous();
|
||||
}
|
||||
|
||||
if (!currentFrameValid_)
|
||||
*currentFrame_[2] = *currentFrame_[1];
|
||||
|
||||
while (currentFrame_[2]->peekNext()->time() < time)
|
||||
{
|
||||
currentFrameValid_ = false;
|
||||
if (!currentFrame_[2]->hasNext())
|
||||
break;
|
||||
currentFrame_[2]->next();
|
||||
}
|
||||
|
||||
if (!currentFrameValid_)
|
||||
{
|
||||
*currentFrame_[1] = *currentFrame_[2];
|
||||
if ((currentFrame_[1]->hasPrevious()) && (time < currentFrame_[2]->peekNext()->time()))
|
||||
currentFrame_[1]->previous();
|
||||
|
||||
*currentFrame_[0] = *currentFrame_[1];
|
||||
if (currentFrame_[0]->hasPrevious())
|
||||
currentFrame_[0]->previous();
|
||||
|
||||
*currentFrame_[3] = *currentFrame_[2];
|
||||
if (currentFrame_[3]->hasNext())
|
||||
currentFrame_[3]->next();
|
||||
|
||||
currentFrameValid_ = true;
|
||||
splineCacheIsValid_ = false;
|
||||
}
|
||||
|
||||
// cout << "Time = " << time << " : " << currentFrame_[0]->peekNext()->time() << " , " <<
|
||||
// currentFrame_[1]->peekNext()->time() << " , " << currentFrame_[2]->peekNext()->time() << " , " << currentFrame_[3]->peekNext()->time() << endl;
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::updateSplineCache()
|
||||
{
|
||||
Vec delta = currentFrame_[2]->peekNext()->position() - currentFrame_[1]->peekNext()->position();
|
||||
v1 = 3.0 * delta - 2.0 * currentFrame_[1]->peekNext()->tgP() - currentFrame_[2]->peekNext()->tgP();
|
||||
v2 = -2.0 * delta + currentFrame_[1]->peekNext()->tgP() + currentFrame_[2]->peekNext()->tgP();
|
||||
splineCacheIsValid_ = true;
|
||||
}
|
||||
|
||||
/*! Interpolate frame() at time \p time (expressed in seconds). interpolationTime() is set to \p
|
||||
time and frame() is set accordingly.
|
||||
|
||||
If you simply want to change interpolationTime() but not the frame() state, use
|
||||
setInterpolationTime() instead.
|
||||
|
||||
Emits the interpolated() signal and makes the frame() emit the Frame::interpolated() signal. */
|
||||
void KeyFrameInterpolator::interpolateAtTime(qreal time)
|
||||
{
|
||||
setInterpolationTime(time);
|
||||
|
||||
if ((keyFrame_.isEmpty()) || (!frame()))
|
||||
return;
|
||||
|
||||
if (!valuesAreValid_)
|
||||
updateModifiedFrameValues();
|
||||
|
||||
updateCurrentKeyFrameForTime(time);
|
||||
|
||||
if (!splineCacheIsValid_)
|
||||
updateSplineCache();
|
||||
|
||||
qreal alpha;
|
||||
qreal dt = currentFrame_[2]->peekNext()->time() - currentFrame_[1]->peekNext()->time();
|
||||
if (dt == 0.0)
|
||||
alpha = 0.0;
|
||||
else
|
||||
alpha = (time - currentFrame_[1]->peekNext()->time()) / dt;
|
||||
|
||||
// Linear interpolation - debug
|
||||
// Vec pos = alpha*(currentFrame_[2]->peekNext()->position()) + (1.0-alpha)*(currentFrame_[1]->peekNext()->position());
|
||||
Vec pos = currentFrame_[1]->peekNext()->position() + alpha * (currentFrame_[1]->peekNext()->tgP() + alpha * (v1+alpha*v2));
|
||||
Quaternion q = Quaternion::squad(currentFrame_[1]->peekNext()->orientation(), currentFrame_[1]->peekNext()->tgQ(),
|
||||
currentFrame_[2]->peekNext()->tgQ(), currentFrame_[2]->peekNext()->orientation(), alpha);
|
||||
frame()->setPositionAndOrientationWithConstraint(pos, q);
|
||||
|
||||
Q_EMIT interpolated();
|
||||
}
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the KeyFrameInterpolator.
|
||||
|
||||
The resulting QDomElement holds the KeyFrameInterpolator parameters as well as the path keyFrames
|
||||
(if the keyFrame is defined by a pointer to a Frame, use its current value).
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument factory used to create
|
||||
QDomElement.
|
||||
|
||||
Use initFromDOMElement() to restore the ManipulatedFrame state from the resulting QDomElement.
|
||||
|
||||
See Vec::domElement() for a complete example. See also Quaternion::domElement(),
|
||||
Camera::domElement()...
|
||||
|
||||
Note that the Camera::keyFrameInterpolator() are automatically saved by QGLViewer::saveStateToFile()
|
||||
when a QGLViewer is closed. */
|
||||
QDomElement KeyFrameInterpolator::domElement(const QString& name, QDomDocument& document) const
|
||||
{
|
||||
QDomElement de = document.createElement(name);
|
||||
int count = 0;
|
||||
Q_FOREACH (KeyFrame* kf, keyFrame_)
|
||||
{
|
||||
Frame fr(kf->position(), kf->orientation());
|
||||
QDomElement kfNode = fr.domElement("KeyFrame", document);
|
||||
kfNode.setAttribute("index", QString::number(count));
|
||||
kfNode.setAttribute("time", QString::number(kf->time()));
|
||||
de.appendChild(kfNode);
|
||||
++count;
|
||||
}
|
||||
de.setAttribute("nbKF", QString::number(keyFrame_.count()));
|
||||
de.setAttribute("time", QString::number(interpolationTime()));
|
||||
de.setAttribute("speed", QString::number(interpolationSpeed()));
|
||||
de.setAttribute("period", QString::number(interpolationPeriod()));
|
||||
DomUtils::setBoolAttribute(de, "closedPath", closedPath());
|
||||
DomUtils::setBoolAttribute(de, "loop", loopInterpolation());
|
||||
return de;
|
||||
}
|
||||
|
||||
/*! Restores the KeyFrameInterpolator state from a \c QDomElement created by domElement().
|
||||
|
||||
Note that the frame() pointer is not included in the domElement(): you need to setFrame() after
|
||||
this method to attach a Frame to the KeyFrameInterpolator.
|
||||
|
||||
See Vec::initFromDOMElement() for a complete code example.
|
||||
|
||||
See also Camera::initFromDOMElement() and Frame::initFromDOMElement(). */
|
||||
void KeyFrameInterpolator::initFromDOMElement(const QDomElement& element)
|
||||
{
|
||||
qDeleteAll(keyFrame_);
|
||||
keyFrame_.clear();
|
||||
QDomElement child=element.firstChild().toElement();
|
||||
while (!child.isNull())
|
||||
{
|
||||
if (child.tagName() == "KeyFrame")
|
||||
{
|
||||
Frame fr;
|
||||
fr.initFromDOMElement(child);
|
||||
qreal time = DomUtils::qrealFromDom(child, "time", 0.0);
|
||||
addKeyFrame(fr, time);
|
||||
}
|
||||
|
||||
child = child.nextSibling().toElement();
|
||||
}
|
||||
|
||||
// #CONNECTION# Values cut pasted from constructor
|
||||
setInterpolationTime(DomUtils::qrealFromDom(element, "time", 0.0));
|
||||
setInterpolationSpeed(DomUtils::qrealFromDom(element, "speed", 1.0));
|
||||
setInterpolationPeriod(DomUtils::intFromDom(element, "period", 40));
|
||||
setClosedPath(DomUtils::boolFromDom(element, "closedPath", false));
|
||||
setLoopInterpolation(DomUtils::boolFromDom(element, "loop", false));
|
||||
|
||||
// setFrame(NULL);
|
||||
pathIsValid_ = false;
|
||||
valuesAreValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
|
||||
stopInterpolation();
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
//////////// KeyFrame private class implementation /////////
|
||||
KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame& fr, qreal t)
|
||||
: time_(t), frame_(NULL)
|
||||
{
|
||||
p_ = fr.position();
|
||||
q_ = fr.orientation();
|
||||
}
|
||||
|
||||
KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame* fr, qreal t)
|
||||
: time_(t), frame_(fr)
|
||||
{
|
||||
updateValuesFromPointer();
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::KeyFrame::updateValuesFromPointer()
|
||||
{
|
||||
p_ = frame()->position();
|
||||
q_ = frame()->orientation();
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::KeyFrame::computeTangent(const KeyFrame* const prev, const KeyFrame* const next)
|
||||
{
|
||||
tgP_ = 0.5 * (next->position() - prev->position());
|
||||
tgQ_ = Quaternion::squadTangent(prev->orientation(), q_, next->orientation());
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::KeyFrame::flipOrientationIfNeeded(const Quaternion& prev)
|
||||
{
|
||||
if (Quaternion::dot(prev, q_) < 0.0)
|
||||
q_.negate();
|
||||
}
|
||||
|
||||
#endif //DOXYGEN
|
||||
351
QGLViewer/keyFrameInterpolator.h
Normal file
351
QGLViewer/keyFrameInterpolator.h
Normal file
@ -0,0 +1,351 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#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 <a href="../examples/keyFrames.html">keyFrames
|
||||
example</a>):
|
||||
\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().
|
||||
|
||||
<h3>Interpolation details</h3>
|
||||
|
||||
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 <a href="../keyboard.html">keyboard page</a> 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.
|
||||
|
||||
<h3>Retrieving interpolated values</h3>
|
||||
|
||||
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=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 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 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*> keyFrame_;
|
||||
QMutableListIterator<KeyFrame*>* currentFrame_[4];
|
||||
QList<Frame> 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
|
||||
469
QGLViewer/manipulatedCameraFrame.cpp
Normal file
469
QGLViewer/manipulatedCameraFrame.cpp
Normal file
@ -0,0 +1,469 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <QMouseEvent>
|
||||
|
||||
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), 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 <a href="../mouse.html">mouse page</a> for details). The
|
||||
resulting displacements are basically inverted from those of a ManipulatedFrame. */
|
||||
void ManipulatedCameraFrame::mouseMoveEvent(QMouseEvent* const event, Camera* const camera)
|
||||
{
|
||||
// #CONNECTION# QGLViewer::mouseMoveEvent does the update().
|
||||
switch (action_)
|
||||
{
|
||||
case QGLViewer::TRANSLATE:
|
||||
{
|
||||
const QPoint delta = prevPos_ - event->pos();
|
||||
Vec trans(delta.x(), -delta.y(), 0.0);
|
||||
// Scale to fit the screen mouse displacement
|
||||
switch (camera->type())
|
||||
{
|
||||
case Camera::PERSPECTIVE :
|
||||
trans *= 2.0 * tan(camera->fieldOfView()/2.0) *
|
||||
fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / camera->screenHeight();
|
||||
break;
|
||||
case Camera::ORTHOGRAPHIC :
|
||||
{
|
||||
GLdouble w,h;
|
||||
camera->getOrthoWidthHeight(w, h);
|
||||
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
translate(inverseTransformOf(translationSensitivity()*trans));
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::MOVE_FORWARD:
|
||||
{
|
||||
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||
rotate(rot);
|
||||
//#CONNECTION# wheelEvent MOVE_FORWARD case
|
||||
// actual translation is made in flyUpdate().
|
||||
//translate(inverseTransformOf(Vec(0.0, 0.0, -flySpeed())));
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::MOVE_BACKWARD:
|
||||
{
|
||||
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||
rotate(rot);
|
||||
// actual translation is made in flyUpdate().
|
||||
//translate(inverseTransformOf(Vec(0.0, 0.0, flySpeed())));
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::DRIVE:
|
||||
{
|
||||
Quaternion rot = turnQuaternion(event->x(), camera);
|
||||
rotate(rot);
|
||||
// actual translation is made in flyUpdate().
|
||||
driveSpeed_ = 0.01 * (event->y() - pressPos_.y());
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::ZOOM:
|
||||
{
|
||||
zoom(deltaWithPrevPos(event, camera), camera);
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::LOOK_AROUND:
|
||||
{
|
||||
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||
rotate(rot);
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::ROTATE:
|
||||
{
|
||||
Quaternion rot;
|
||||
if (rotatesAroundUpVector_) {
|
||||
// Multiply by 2.0 to get on average about the same speed as with the deformed ball
|
||||
qreal dx = 2.0 * rotationSensitivity() * (prevPos_.x() - event->x()) / camera->screenWidth();
|
||||
qreal dy = 2.0 * rotationSensitivity() * (prevPos_.y() - event->y()) / camera->screenHeight();
|
||||
if (constrainedRotationIsReversed_) dx = -dx;
|
||||
Vec verticalAxis = transformOf(sceneUpVector_);
|
||||
rot = Quaternion(verticalAxis, dx) * Quaternion(Vec(1.0, 0.0, 0.0), dy);
|
||||
} else {
|
||||
Vec trans = camera->projectedCoordinatesOf(pivotPoint());
|
||||
rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], camera);
|
||||
}
|
||||
//#CONNECTION# These two methods should go together (spinning detection and activation)
|
||||
computeMouseSpeed(event);
|
||||
setSpinningQuaternion(rot);
|
||||
spin();
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::SCREEN_ROTATE:
|
||||
{
|
||||
Vec trans = camera->projectedCoordinatesOf(pivotPoint());
|
||||
|
||||
const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]) - atan2(prevPos_.y()-trans[1], prevPos_.x()-trans[0]);
|
||||
|
||||
Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
|
||||
//#CONNECTION# These two methods should go together (spinning detection and activation)
|
||||
computeMouseSpeed(event);
|
||||
setSpinningQuaternion(rot);
|
||||
spin();
|
||||
updateSceneUpVector();
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::ROLL:
|
||||
{
|
||||
const qreal angle = M_PI * (event->x() - prevPos_.x()) / camera->screenWidth();
|
||||
Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
|
||||
rotate(rot);
|
||||
setSpinningQuaternion(rot);
|
||||
updateSceneUpVector();
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::SCREEN_TRANSLATE:
|
||||
{
|
||||
Vec trans;
|
||||
int dir = mouseOriginalDirection(event);
|
||||
if (dir == 1)
|
||||
trans.setValue(prevPos_.x() - event->x(), 0.0, 0.0);
|
||||
else if (dir == -1)
|
||||
trans.setValue(0.0, event->y() - prevPos_.y(), 0.0);
|
||||
|
||||
switch (camera->type())
|
||||
{
|
||||
case Camera::PERSPECTIVE :
|
||||
trans *= 2.0 * tan(camera->fieldOfView()/2.0) *
|
||||
fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / camera->screenHeight();
|
||||
break;
|
||||
case Camera::ORTHOGRAPHIC :
|
||||
{
|
||||
GLdouble w,h;
|
||||
camera->getOrthoWidthHeight(w, h);
|
||||
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
translate(inverseTransformOf(translationSensitivity()*trans));
|
||||
break;
|
||||
}
|
||||
|
||||
case QGLViewer::ZOOM_ON_REGION:
|
||||
case QGLViewer::NO_MOUSE_ACTION:
|
||||
break;
|
||||
}
|
||||
|
||||
if (action_ != QGLViewer::NO_MOUSE_ACTION)
|
||||
{
|
||||
prevPos_ = event->pos();
|
||||
if (action_ != QGLViewer::ZOOM_ON_REGION)
|
||||
// ZOOM_ON_REGION should not emit manipulated().
|
||||
// prevPos_ is used to draw rectangle feedback.
|
||||
Q_EMIT manipulated();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! This is an overload of ManipulatedFrame::mouseReleaseEvent(). The QGLViewer::MouseAction is
|
||||
terminated. */
|
||||
void ManipulatedCameraFrame::mouseReleaseEvent(QMouseEvent* const event, Camera* const camera)
|
||||
{
|
||||
if ((action_ == QGLViewer::MOVE_FORWARD) || (action_ == QGLViewer::MOVE_BACKWARD) || (action_ == QGLViewer::DRIVE))
|
||||
flyTimer_.stop();
|
||||
|
||||
if (action_ == QGLViewer::ZOOM_ON_REGION)
|
||||
camera->fitScreenRegion(QRect(pressPos_, event->pos()));
|
||||
|
||||
ManipulatedFrame::mouseReleaseEvent(event, camera);
|
||||
}
|
||||
|
||||
/*! This is an overload of ManipulatedFrame::wheelEvent().
|
||||
|
||||
The wheel behavior depends on the wheel binded action. Current possible actions are QGLViewer::ZOOM,
|
||||
QGLViewer::MOVE_FORWARD, QGLViewer::MOVE_BACKWARD. QGLViewer::ZOOM speed depends on
|
||||
wheelSensitivity() while QGLViewer::MOVE_FORWARD and QGLViewer::MOVE_BACKWARD depend on flySpeed().
|
||||
See QGLViewer::setWheelBinding() to customize the binding. */
|
||||
void ManipulatedCameraFrame::wheelEvent(QWheelEvent* const event, Camera* const camera)
|
||||
{
|
||||
//#CONNECTION# QGLViewer::setWheelBinding, ManipulatedFrame::wheelEvent.
|
||||
switch (action_)
|
||||
{
|
||||
case QGLViewer::ZOOM:
|
||||
{
|
||||
zoom(wheelDelta(event), camera);
|
||||
Q_EMIT manipulated();
|
||||
break;
|
||||
}
|
||||
case QGLViewer::MOVE_FORWARD:
|
||||
case QGLViewer::MOVE_BACKWARD:
|
||||
//#CONNECTION# mouseMoveEvent() MOVE_FORWARD case
|
||||
translate(inverseTransformOf(Vec(0.0, 0.0, 0.2*flySpeed()*event->delta())));
|
||||
Q_EMIT manipulated();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// #CONNECTION# startAction should always be called before
|
||||
if (previousConstraint_)
|
||||
setConstraint(previousConstraint_);
|
||||
|
||||
// The wheel triggers a fastDraw. A final update() is needed after the last wheel event to
|
||||
// polish the rendering using draw(). Since the last wheel event does not say its name, we use
|
||||
// the flyTimer_ to trigger flyUpdate(), which emits manipulated. Two wheel events
|
||||
// separated by more than this delay milliseconds will trigger a draw().
|
||||
const int finalDrawAfterWheelEventDelay = 400;
|
||||
|
||||
// Starts (or prolungates) the timer.
|
||||
flyTimer_.setSingleShot(true);
|
||||
flyTimer_.start(finalDrawAfterWheelEventDelay);
|
||||
|
||||
// This could also be done *before* manipulated is emitted, so that isManipulated() returns false.
|
||||
// But then fastDraw would not be used with wheel.
|
||||
// Detecting the last wheel event and forcing a final draw() is done using the timer_.
|
||||
action_ = QGLViewer::NO_MOUSE_ACTION;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Returns a Quaternion that is a rotation around current camera Y, proportionnal to the horizontal mouse position. */
|
||||
Quaternion ManipulatedCameraFrame::turnQuaternion(int x, const Camera* const camera)
|
||||
{
|
||||
return Quaternion(Vec(0.0, 1.0, 0.0), rotationSensitivity()*(prevPos_.x()-x)/camera->screenWidth());
|
||||
}
|
||||
|
||||
/*! Returns a Quaternion that is the composition of two rotations, inferred from the
|
||||
mouse pitch (X axis) and yaw (sceneUpVector() axis). */
|
||||
Quaternion ManipulatedCameraFrame::pitchYawQuaternion(int x, int y, const Camera* const camera)
|
||||
{
|
||||
const Quaternion rotX(Vec(1.0, 0.0, 0.0), rotationSensitivity()*(prevPos_.y()-y)/camera->screenHeight());
|
||||
const Quaternion rotY(transformOf(sceneUpVector()), rotationSensitivity()*(prevPos_.x()-x)/camera->screenWidth());
|
||||
return rotY * rotX;
|
||||
}
|
||||
233
QGLViewer/manipulatedCameraFrame.h
Normal file
233
QGLViewer/manipulatedCameraFrame.h
Normal file
@ -0,0 +1,233 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <a href="../mouse.html">mouse page</a> 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 <a href="../mouse.html">mouse page</a>). */
|
||||
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
|
||||
550
QGLViewer/manipulatedFrame.cpp
Normal file
550
QGLViewer/manipulatedFrame.cpp
Normal file
@ -0,0 +1,550 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <cstdlib>
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
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 <a href="../examples/mouseGrabber.html">mouseGrabber example</a> for an illustration. */
|
||||
void ManipulatedFrame::checkIfGrabsMouse(int x, int y, const Camera* const camera)
|
||||
{
|
||||
const int thresold = 10;
|
||||
const Vec proj = camera->projectedCoordinatesOf(position());
|
||||
setGrabsMouse(keepsGrabbingMouse_ || ((fabs(x-proj.x) < thresold) && (fabs(y-proj.y) < thresold)));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// S t a t e s a v i n g a n d r e s t o r i n g //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the ManipulatedFrame.
|
||||
|
||||
Adds to the Frame::domElement() the ManipulatedFrame specific informations in a \c
|
||||
ManipulatedParameters child QDomElement.
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument factory used to create
|
||||
QDomElement.
|
||||
|
||||
Use initFromDOMElement() to restore the ManipulatedFrame state from the resulting \c QDomElement.
|
||||
|
||||
See Vec::domElement() for a complete example. See also Quaternion::domElement(),
|
||||
Camera::domElement()... */
|
||||
QDomElement ManipulatedFrame::domElement(const QString& name, QDomDocument& document) const
|
||||
{
|
||||
QDomElement e = Frame::domElement(name, document);
|
||||
QDomElement mp = document.createElement("ManipulatedParameters");
|
||||
mp.setAttribute("rotSens", QString::number(rotationSensitivity()));
|
||||
mp.setAttribute("transSens", QString::number(translationSensitivity()));
|
||||
mp.setAttribute("spinSens", QString::number(spinningSensitivity()));
|
||||
mp.setAttribute("wheelSens", QString::number(wheelSensitivity()));
|
||||
mp.setAttribute("zoomSens", QString::number(zoomSensitivity()));
|
||||
e.appendChild(mp);
|
||||
return e;
|
||||
}
|
||||
|
||||
/*! Restores the ManipulatedFrame state from a \c QDomElement created by domElement().
|
||||
|
||||
Fields that are not described in \p element are set to their default values (see
|
||||
ManipulatedFrame()).
|
||||
|
||||
First calls Frame::initFromDOMElement() and then initializes ManipulatedFrame specific parameters.
|
||||
Note that constraint() and referenceFrame() are not restored and are left unchanged.
|
||||
|
||||
See Vec::initFromDOMElement() for a complete code example. */
|
||||
void ManipulatedFrame::initFromDOMElement(const QDomElement& element)
|
||||
{
|
||||
// Not called since it would set constraint() and referenceFrame() to NULL.
|
||||
// *this = ManipulatedFrame();
|
||||
Frame::initFromDOMElement(element);
|
||||
|
||||
stopSpinning();
|
||||
|
||||
QDomElement child=element.firstChild().toElement();
|
||||
while (!child.isNull())
|
||||
{
|
||||
if (child.tagName() == "ManipulatedParameters")
|
||||
{
|
||||
// #CONNECTION# constructor default values and accessor docs
|
||||
setRotationSensitivity (DomUtils::qrealFromDom(child, "rotSens", 1.0));
|
||||
setTranslationSensitivity(DomUtils::qrealFromDom(child, "transSens", 1.0));
|
||||
setSpinningSensitivity (DomUtils::qrealFromDom(child, "spinSens", 0.3));
|
||||
setWheelSensitivity (DomUtils::qrealFromDom(child, "wheelSens", 1.0));
|
||||
setZoomSensitivity (DomUtils::qrealFromDom(child, "zoomSens", 1.0));
|
||||
}
|
||||
child = child.nextSibling().toElement();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// M o u s e h a n d l i n g //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Returns \c true when the ManipulatedFrame is being manipulated with the mouse.
|
||||
|
||||
Can be used to change the display of the manipulated object during manipulation.
|
||||
|
||||
When Camera::frame() of the QGLViewer::camera() isManipulated(), QGLViewer::fastDraw() is used in
|
||||
place of QGLViewer::draw() for scene rendering. A simplified drawing will then allow for
|
||||
interactive camera displacements. */
|
||||
bool ManipulatedFrame::isManipulated() const
|
||||
{
|
||||
return action_ != QGLViewer::NO_MOUSE_ACTION;
|
||||
}
|
||||
|
||||
/*! Starts the spinning of the ManipulatedFrame.
|
||||
|
||||
This method starts a timer that will call spin() every \p updateInterval milliseconds. The
|
||||
ManipulatedFrame isSpinning() until you call stopSpinning(). */
|
||||
void ManipulatedFrame::startSpinning(int updateInterval)
|
||||
{
|
||||
isSpinning_ = true;
|
||||
spinningTimer_.start(updateInterval);
|
||||
}
|
||||
|
||||
/*! Rotates the ManipulatedFrame by its spinningQuaternion(). Called by a timer when the
|
||||
ManipulatedFrame isSpinning(). */
|
||||
void ManipulatedFrame::spin()
|
||||
{
|
||||
rotate(spinningQuaternion());
|
||||
}
|
||||
|
||||
/* spin() and spinUpdate() differ since spin can be used by itself (for instance by
|
||||
QGLViewer::SCREEN_ROTATE) without a spun emission. Much nicer to use the spinningQuaternion() and
|
||||
hence spin() for these incremental updates. Nothing special to be done for continuous spinning
|
||||
with this design. */
|
||||
void ManipulatedFrame::spinUpdate()
|
||||
{
|
||||
spin();
|
||||
Q_EMIT spun();
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/*! Protected internal method used to handle mouse events. */
|
||||
void ManipulatedFrame::startAction(int ma, bool withConstraint)
|
||||
{
|
||||
action_ = (QGLViewer::MouseAction)(ma);
|
||||
|
||||
// #CONNECTION# manipulatedFrame::wheelEvent, manipulatedCameraFrame::wheelEvent and mouseReleaseEvent()
|
||||
// restore previous constraint
|
||||
if (withConstraint)
|
||||
previousConstraint_ = NULL;
|
||||
else
|
||||
{
|
||||
previousConstraint_ = constraint();
|
||||
setConstraint(NULL);
|
||||
}
|
||||
|
||||
switch (action_)
|
||||
{
|
||||
case QGLViewer::ROTATE:
|
||||
case QGLViewer::SCREEN_ROTATE:
|
||||
mouseSpeed_ = 0.0;
|
||||
stopSpinning();
|
||||
break;
|
||||
|
||||
case QGLViewer::SCREEN_TRANSLATE:
|
||||
dirIsFixed_ = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Updates mouse speed, measured in pixels/milliseconds. Should be called by any method which wants to
|
||||
use mouse speed. Currently used to trigger spinning in mouseReleaseEvent(). */
|
||||
void ManipulatedFrame::computeMouseSpeed(const QMouseEvent* const e)
|
||||
{
|
||||
const QPoint delta = (e->pos() - prevPos_);
|
||||
const qreal dist = sqrt(qreal(delta.x()*delta.x() + delta.y()*delta.y()));
|
||||
delay_ = last_move_time.restart();
|
||||
if (delay_ == 0)
|
||||
// Less than a millisecond: assume delay = 1ms
|
||||
mouseSpeed_ = dist;
|
||||
else
|
||||
mouseSpeed_ = dist/delay_;
|
||||
}
|
||||
|
||||
/*! Return 1 if mouse motion was started horizontally and -1 if it was more vertical. Returns 0 if
|
||||
this could not be determined yet (perfect diagonal motion, rare). */
|
||||
int ManipulatedFrame::mouseOriginalDirection(const QMouseEvent* const e)
|
||||
{
|
||||
static bool horiz = true; // Two simultaneous manipulatedFrame require two mice !
|
||||
|
||||
if (!dirIsFixed_)
|
||||
{
|
||||
const QPoint delta = e->pos() - pressPos_;
|
||||
dirIsFixed_ = abs(delta.x()) != abs(delta.y());
|
||||
horiz = abs(delta.x()) > abs(delta.y());
|
||||
}
|
||||
|
||||
if (dirIsFixed_)
|
||||
if (horiz)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
qreal ManipulatedFrame::deltaWithPrevPos(QMouseEvent* const event, Camera* const camera) const {
|
||||
qreal dx = qreal(event->x() - prevPos_.x()) / camera->screenWidth();
|
||||
qreal dy = qreal(event->y() - prevPos_.y()) / camera->screenHeight();
|
||||
|
||||
qreal value = fabs(dx) > fabs(dy) ? dx : dy;
|
||||
return value * zoomSensitivity();
|
||||
}
|
||||
|
||||
qreal ManipulatedFrame::wheelDelta(const QWheelEvent* event) const {
|
||||
static const qreal WHEEL_SENSITIVITY_COEF = 8E-4;
|
||||
return event->delta() * wheelSensitivity() * WHEEL_SENSITIVITY_COEF;
|
||||
}
|
||||
|
||||
void ManipulatedFrame::zoom(qreal delta, const Camera * const camera) {
|
||||
Vec trans(0.0, 0.0, (camera->position() - position()).norm() * delta);
|
||||
|
||||
trans = camera->frame()->orientation().rotate(trans);
|
||||
if (referenceFrame())
|
||||
trans = referenceFrame()->transformOf(trans);
|
||||
translate(trans);
|
||||
}
|
||||
|
||||
#endif // DOXYGEN
|
||||
|
||||
/*! Initiates the ManipulatedFrame mouse manipulation.
|
||||
|
||||
Overloading of MouseGrabber::mousePressEvent(). See also mouseMoveEvent() and mouseReleaseEvent().
|
||||
|
||||
The mouse behavior depends on which button is pressed. See the <a href="../mouse.html">QGLViewer
|
||||
mouse page</a> 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 <a
|
||||
href="../mouse.html">QGLViewer mouse page</a> 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
|
||||
335
QGLViewer/manipulatedFrame.h
Normal file
335
QGLViewer/manipulatedFrame.h
Normal file
@ -0,0 +1,335 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <QString>
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
|
||||
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 <a href="../examples/manipulatedFrame.html">manipulatedFrame example</a> 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 <a
|
||||
href="../mouse.html">mouse page</a> for a description of mouse button bindings.
|
||||
|
||||
<h3>Inherited functionalities</h3>
|
||||
|
||||
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 <a href="../examples/mouseGrabber.html">mouseGrabber
|
||||
example</a> as an illustration. Note that QWidget::setMouseTracking() needs to be enabled in order
|
||||
to use this feature (see the MouseGrabber documentation).
|
||||
|
||||
<h3>Advanced functionalities</h3>
|
||||
|
||||
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 <a href="../examples/luxo.html">luxo example</a> 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 <a href="../mouse.html">mouse page</a>).
|
||||
|
||||
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
|
||||
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
|
||||
76
QGLViewer/mouseGrabber.cpp
Normal file
76
QGLViewer/mouseGrabber.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/****************************************************************************
|
||||
|
||||
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*> 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<MouseGrabber*>(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();
|
||||
}
|
||||
264
QGLViewer/mouseGrabber.h
Normal file
264
QGLViewer/mouseGrabber.h
Normal file
@ -0,0 +1,264 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <QEvent>
|
||||
|
||||
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.
|
||||
|
||||
<h3>How does it work ?</h3>
|
||||
|
||||
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().
|
||||
|
||||
<h3>Implementation details</h3>
|
||||
|
||||
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. [TODO Update with QOpenGLWidget]
|
||||
|
||||
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 <a href="../examples/mouseGrabber.html">mouseGrabber
|
||||
example</a> for an illustration.
|
||||
|
||||
Note that ManipulatedFrame are MouseGrabber: see the <a href="../examples/keyFrames.html">keyFrame
|
||||
example</a> for an illustration. Every created ManipulatedFrame is hence present in the
|
||||
MouseGrabberPool() (note however that ManipulatedCameraFrame are not inserted).
|
||||
|
||||
<h3>Example</h3>
|
||||
|
||||
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 <a href="#_details">detailed description</a> section and in the <a
|
||||
href="../examples/mouseGrabber.html">mouseGrabber example</a>. */
|
||||
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<MouseGrabber> with Qt 3 and a \c QList<MouseGrabber> with Qt 2. */
|
||||
static const QList<MouseGrabber*>& 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<MouseGrabber*>(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 <a href="#_details">detailed description section</a> and the <a
|
||||
href="../examples/mouseGrabber.html">mouseGrabber example</a> for examples.
|
||||
|
||||
See the \c QGLWidget::mousePressEvent() and the \c QMouseEvent documentations for details. [TODO Update with QOpenGLWidget] */
|
||||
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. [TODO Update with QOpenGLWidget] */
|
||||
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. [TODO Update with QOpenGLWidget] */
|
||||
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<MouseGrabber*> MouseGrabberPool_;
|
||||
};
|
||||
|
||||
} // namespace qglviewer
|
||||
|
||||
#endif // QGLVIEWER_MOUSE_GRABBER_H
|
||||
359
QGLViewer/qglviewer-icon.xpm
Normal file
359
QGLViewer/qglviewer-icon.xpm
Normal file
@ -0,0 +1,359 @@
|
||||
/* XPM */
|
||||
static const char * qglviewer_icon[] = {
|
||||
"100 100 256 2",
|
||||
" c None",
|
||||
". c #0A0B27",
|
||||
"+ c #090B2C",
|
||||
"@ c #150C12",
|
||||
"# c #080F34",
|
||||
"$ c #1A0E1A",
|
||||
"% c #220D0B",
|
||||
"& c #260B0B",
|
||||
"* c #230E1C",
|
||||
"= c #12113E",
|
||||
"- c #2C0D10",
|
||||
"; c #2F0D09",
|
||||
"> c #310D14",
|
||||
", c #1A123A",
|
||||
"' c #261025",
|
||||
") c #17134B",
|
||||
"! c #151453",
|
||||
"~ c #1B1733",
|
||||
"{ c #14135E",
|
||||
"] c #281430",
|
||||
"^ c #0E1867",
|
||||
"/ c #3B1215",
|
||||
"( c #32132A",
|
||||
"_ c #1D1849",
|
||||
": c #121C58",
|
||||
"< c #431014",
|
||||
"[ c #141974",
|
||||
"} c #3A152E",
|
||||
"| c #201A5E",
|
||||
"1 c #1D204C",
|
||||
"2 c #401431",
|
||||
"3 c #261960",
|
||||
"4 c #48171C",
|
||||
"5 c #411B21",
|
||||
"6 c #371B45",
|
||||
"7 c #2A1C6B",
|
||||
"8 c #0E229B",
|
||||
"9 c #52171D",
|
||||
"0 c #441A35",
|
||||
"a c #1B2582",
|
||||
"b c #65150F",
|
||||
"c c #2D2076",
|
||||
"d c #4D1B3A",
|
||||
"e c #57182F",
|
||||
"f c #3E1F54",
|
||||
"g c #5E1924",
|
||||
"h c #2E2567",
|
||||
"i c #002BD1",
|
||||
"j c #002AD9",
|
||||
"k c #6D1A13",
|
||||
"l c #36237A",
|
||||
"m c #002FCD",
|
||||
"n c #701A0D",
|
||||
"o c #462060",
|
||||
"p c #651C23",
|
||||
"q c #322581",
|
||||
"r c #552429",
|
||||
"s c #581E41",
|
||||
"t c #671E1B",
|
||||
"u c #771911",
|
||||
"v c #6C1B2B",
|
||||
"w c #562341",
|
||||
"x c #342A79",
|
||||
"y c #3C2484",
|
||||
"z c #062FE6",
|
||||
"A c #68212B",
|
||||
"B c #4D236B",
|
||||
"C c #612045",
|
||||
"D c #42267B",
|
||||
"E c #811B10",
|
||||
"F c #7C1E0E",
|
||||
"G c #0033F1",
|
||||
"H c #0032F9",
|
||||
"I c #3A298E",
|
||||
"J c #472872",
|
||||
"K c #72202A",
|
||||
"L c #4F2774",
|
||||
"M c #652449",
|
||||
"N c #442890",
|
||||
"O c #1F37A8",
|
||||
"P c #87200E",
|
||||
"Q c #852014",
|
||||
"R c #402B98",
|
||||
"S c #6C244D",
|
||||
"T c #7D2230",
|
||||
"U c #8F1F12",
|
||||
"V c #70254A",
|
||||
"W c #542A7E",
|
||||
"X c #212FF2",
|
||||
"Y c #6E2E28",
|
||||
"Z c #442E9B",
|
||||
"` c #772834",
|
||||
" . c #971E15",
|
||||
".. c #552B86",
|
||||
"+. c #693132",
|
||||
"@. c #702A46",
|
||||
"#. c #7B2645",
|
||||
"$. c #862435",
|
||||
"%. c #79264E",
|
||||
"&. c #5A2B88",
|
||||
"*. c #1B3AE9",
|
||||
"=. c #9A2211",
|
||||
"-. c #4E2EA0",
|
||||
";. c #1E3BDE",
|
||||
">. c #722F3D",
|
||||
",. c #5E2C91",
|
||||
"'. c #592F91",
|
||||
"). c #7A322A",
|
||||
"!. c #1D42D7",
|
||||
"~. c #902539",
|
||||
"{. c #7D2A52",
|
||||
"]. c #862E26",
|
||||
"^. c #822853",
|
||||
"/. c #922B20",
|
||||
"(. c #4E34AA",
|
||||
"_. c #A62411",
|
||||
":. c #5D309A",
|
||||
"<. c #59387B",
|
||||
"[. c #5631AB",
|
||||
"}. c #5533A6",
|
||||
"|. c #912840",
|
||||
"1. c #8C2B3F",
|
||||
"2. c #AE2215",
|
||||
"3. c #862C57",
|
||||
"4. c #5D32A8",
|
||||
"5. c #882C53",
|
||||
"6. c #5C34A2",
|
||||
"7. c #6231A2",
|
||||
"8. c #A32A18",
|
||||
"9. c #5C398B",
|
||||
"0. c #4D4480",
|
||||
"a. c #982A3F",
|
||||
"b. c #8D2B5A",
|
||||
"c. c #962C44",
|
||||
"d. c #902C56",
|
||||
"e. c #7A3B43",
|
||||
"f. c #3249C3",
|
||||
"g. c #B42812",
|
||||
"h. c #583E9B",
|
||||
"i. c #BA2516",
|
||||
"j. c #524297",
|
||||
"k. c #55448B",
|
||||
"l. c #5E3BA3",
|
||||
"m. c #9E2C47",
|
||||
"n. c #5A4676",
|
||||
"o. c #873847",
|
||||
"p. c #404E94",
|
||||
"q. c #932F59",
|
||||
"r. c #BE2810",
|
||||
"s. c #992D5B",
|
||||
"t. c #5941A4",
|
||||
"u. c #8A3F36",
|
||||
"v. c #9D2F58",
|
||||
"w. c #78415D",
|
||||
"x. c #A72D4D",
|
||||
"y. c #A2304B",
|
||||
"z. c #C72815",
|
||||
"A. c #C12C13",
|
||||
"B. c #654298",
|
||||
"C. c #654780",
|
||||
"D. c #8F385F",
|
||||
"E. c #98345D",
|
||||
"F. c #2F51E3",
|
||||
"G. c #A93724",
|
||||
"H. c #A0325B",
|
||||
"I. c #CA2B10",
|
||||
"J. c #A93054",
|
||||
"K. c #A6305D",
|
||||
"L. c #D42A00",
|
||||
"M. c #D22914",
|
||||
"N. c #89415D",
|
||||
"O. c #6546A5",
|
||||
"P. c #AB3255",
|
||||
"Q. c #DC2806",
|
||||
"R. c #A3355D",
|
||||
"S. c #AA325A",
|
||||
"T. c #A13F2F",
|
||||
"U. c #6A4A8E",
|
||||
"V. c #A53754",
|
||||
"W. c #DE2A00",
|
||||
"X. c #CF300A",
|
||||
"Y. c #D62C0E",
|
||||
"Z. c #A8385B",
|
||||
"`. c #4B54C3",
|
||||
" + c #964356",
|
||||
".+ c #E82903",
|
||||
"++ c #8F4757",
|
||||
"@+ c #DF2C14",
|
||||
"#+ c #D93011",
|
||||
"$+ c #A23D62",
|
||||
"%+ c #8C4E44",
|
||||
"&+ c #E32F00",
|
||||
"*+ c #6E4CA5",
|
||||
"=+ c #6E4F9B",
|
||||
"-+ c #A04259",
|
||||
";+ c #9D4266",
|
||||
">+ c #D03716",
|
||||
",+ c #E3300D",
|
||||
"'+ c #6A51A7",
|
||||
")+ c #BA3F2F",
|
||||
"!+ c #EC2E07",
|
||||
"~+ c #A9405F",
|
||||
"{+ c #EB2E13",
|
||||
"]+ c #EF3100",
|
||||
"^+ c #4A5CDC",
|
||||
"/+ c #984A68",
|
||||
"(+ c #B54536",
|
||||
"_+ c #4563C9",
|
||||
":+ c #F62E03",
|
||||
"<+ c #EF310B",
|
||||
"[+ c #E73411",
|
||||
"}+ c #AA4B3E",
|
||||
"|+ c #F62E10",
|
||||
"1+ c #CE401F",
|
||||
"2+ c #D33D23",
|
||||
"3+ c #7455A7",
|
||||
"4+ c #C54426",
|
||||
"5+ c #A5496A",
|
||||
"6+ c #F93106",
|
||||
"7+ c #A45048",
|
||||
"8+ c #CD412C",
|
||||
"9+ c #F2350E",
|
||||
"0+ c #AA4A64",
|
||||
"a+ c #5266C7",
|
||||
"b+ c #DE411E",
|
||||
"c+ c #EF3C18",
|
||||
"d+ c #755FAD",
|
||||
"e+ c #C94C34",
|
||||
"f+ c #C14F3F",
|
||||
"g+ c #DB452E",
|
||||
"h+ c #AB526E",
|
||||
"i+ c #EB421F",
|
||||
"j+ c #AA566A",
|
||||
"k+ c #A55B6B",
|
||||
"l+ c #AA5873",
|
||||
"m+ c #E94824",
|
||||
"n+ c #D94D35",
|
||||
"o+ c #DB4F31",
|
||||
"p+ c #5B71DE",
|
||||
"q+ c #A85F76",
|
||||
"r+ c #EC4C31",
|
||||
"s+ c #E2522E",
|
||||
"t+ c #E84F30",
|
||||
"u+ c #CD5D42",
|
||||
"v+ c #6976CD",
|
||||
"w+ c #D9583F",
|
||||
"x+ c #E55537",
|
||||
"y+ c #E7573E",
|
||||
"z+ c #D65F4C",
|
||||
"A+ c #CA6457",
|
||||
"B+ c #E35C3E",
|
||||
"C+ c #D1634E",
|
||||
"D+ c #E26447",
|
||||
"E+ c #E1674E",
|
||||
"F+ c #DE6F57",
|
||||
"G+ c #DE705E",
|
||||
" ",
|
||||
" ",
|
||||
" C+ ",
|
||||
" ` >.o. 1+X.u+ ",
|
||||
" e.A K K T $.T ++ 1+L.L.X. ",
|
||||
" A A v ` T T T 1.~.++ X.L.L.L.W.1+ ",
|
||||
" g A K K T T $.$.1.$.1. >+L.L.L.L.L.L. ",
|
||||
" r g A v v T T $.1.$.$.|.|. + u+X.L.L.L.L.W.W.W.2+ ",
|
||||
" f r g g K K v T T T |.|.1.1.c.-+ u+X.L.L.L.Q.L.Q.Q.W.W. ",
|
||||
" 6 o B C. 4 9 p p v T T T 1.~.$.1.~.a.~.1. u+X.L.L.L.Q.L.W.L.W.W.W.o+ ",
|
||||
" f o B C. 4 9 9 g v v T T $.1.1.~.|.c.c.c.m. 4+L.L.L.Q.L.Q.Q.W.&+W.W.,+b+ ",
|
||||
" f o J L U. 9 9 A v K T $.1.1.$.|.|.c.c.c.a.a.q+ 4+L.L.L.L.L.Q.W.W.&+L.&+&+&+W. ",
|
||||
" f o B W L <. < 9 g A e.` ` $.$.|.$.1.~.a.a.c.c.a.k+ 1+L.L.Q.Q.W.L.W.L.&+&+&+,+&+&+w+ ",
|
||||
" 6 f B L L .. 4 4 9 o.$.|.$.c.a.c.c.m.m.y.c.q+ u+L.W.Q.L.W.&+&+W.&+,+&+&+&+.+s+ ",
|
||||
" p. f o L W W .. / < r 1.|.|.c.c.c.c.y.m.y.a.k+ X.L.W.L.,+,+&+,+&+&+&+[+&+&+b+ ",
|
||||
" 3 6 B L W W &.9. / o.|.|.c.a.m.y.a.a.y.y.q+ e+W.W.&+W.W.,+&+&+&+&+.+&+!+b+ ",
|
||||
" a : f o L L ....&.B. / ++a.a.m.c.a.y.y.a.y.y.q+ u+W.W.W.&+.+&+{+&+[+&+!+&+]+&+ ",
|
||||
" O ^ 1 f J W ....&.'.U. / 5 k+c.m.c.m.y.m.y.y.y.y. L.&+&+&+&+&+&+!+]+!+[+!+!+!+ ",
|
||||
" f.[ ) $ <.L W ..&.'.'. / k+c.m.a.m.y.a.y.y.y.y. b+&+,+&+.+&+&+&+[+]+&+!+&+!+E+ ",
|
||||
" `.8 ! = $ <.W ..&.'.'.B. - k+y.a.y.y.a.y.x.y.x.V. b+.+&+!+&+[+!+!+]+!+!+!+9+!+B+ ",
|
||||
" a+m { = # $ ....'.'.'.,.=+ - k+m.y.y.y.V.y.x.y.x.V. w+!+&+!+]+&+[+&+!+[+<+!+]+&+B+ ",
|
||||
" v+m 8 ) # $ U...'.'.'.,.:. - -+m.m.y.V.x.y.y.x.m.0+ s+&+!+[+&+!+]+<+!+]+]+[+<+9+B+ ",
|
||||
" ;.m ) = + $ 9.&.'.l.l.,.*+ & -+y.y.x.x.J.x.y.x.y.0+ B+&+]+!+<+!+!+[+]+!+]+<+]+<+B+ ",
|
||||
" ;.i [ = + . $ &.'.,.,.,.:.*+ & 0+y.y.x.y.y.y.J.V.x.l+ s+!+!+!+[+<+]+]+!+]+]+]+!+]+B+ ",
|
||||
" ;.z ^+1 # + $ =+l.'.l.7.:.'. % y.x.y.x.x.P.y.x.P.y. B+.+[+]+!+[+]+]+<+]+]+]+]+<+B+ ",
|
||||
" ;.z ;. + + . $ '.:.:.:.:.:.3+ & V.y.x.y.x.J.P.P.V.Z. x+<+]+]+]+:+]+]+]+9+<+9+9+]+ ",
|
||||
" p+*.G G p+ + . @ U.:.:.7.:.6.7. % h+P.V.P.P.y.J.P.x.y.0+ s+]+9+]+]+9+9+]+]+]+]+]+9+<+ ",
|
||||
" f. p+F.H H G F. . . . $ l.:.:.:.6.:.O. & 0+x.J.J.V.P.x.x.P.P.h+ x+<+]+<+]+]+]+9+]+]+]+9+]+<+ ",
|
||||
" i G X X X G H ;. . . $ =+:.:.6.l.6.:.3+ % V.y.y.x.P.V.V.P.P.J. m+!+]+]+]+9+]+|+9+|+]+9+]+c+ ",
|
||||
" m z G H G H G p+ . . * O.6.6.:.7.6.:. & V.P.P.P.x.P.P.V.V.V. i+]+]+<+|+:+]+]+:+]+:+:+]+m+ ",
|
||||
" i z G G G G F. . . * 3+7.7.6.6.6.6.3+ % 0+x.P.x.V.x.x.x.x.P.0+ [+9+]+]+]+|+]+]+]+9+9+9+]+m+ ",
|
||||
" i z G G H *. . . * :.6.6.7.6.7.:. & 0+P.P.P.P.P.P.P.P.V. :+:+]+]+9+]+9+9+|+:+:+:+]+B+ ",
|
||||
" i z G G G p+ . * *+7.}.6.4.6.6.*+ % V.y.P.x.P.x.P.P.P.V. F+]+|+9+:+]+:+:+]+]+:+|+|+:+B+ ",
|
||||
" i z G G ^+ . ' n.4.6.7.4.7.4.6. & 0+x.P.P.P.P.P.P.P.P.~+ s+9+]+:+]+|+6+]+]+|+9+]+]+9+ ",
|
||||
" i z G F. . * O.4.6.6.6.6.4.*+ & ~+P.P.P.P.P.P.P.P.Z.l+ s+6+9+|+]+9+|+|+]+6+:+6+6+9+ ",
|
||||
" j z !. . ] J 4.6.4.[.4.[.l. & P.P.P.P.P.P.P.P.P.V. c+9+6+]+|+6+9+]+6+9+|+9+|+i+ ",
|
||||
" a+i ;. . ' l.[.(.6.6.6.4.*+ & h+P.P.S.P.S.P.P.P.P.0+ :+|+]+]+|+6+|+9+6+]+6+]+9+t+ ",
|
||||
" a+!. . ] t.(.6.7.4.4.[.6. & S.P.P.P.P.P.S.S.S.S.q+ D+9+9+|+6+]+9+]+6+|+6+9+6+:+E+ ",
|
||||
" . 3+7.[.6.(.6.6.6.*+ & h+S.S.P.S.~+P.P.P.P.~+ r+6+]+9+6+6+6+9+]+9+6+9+6+9+ ",
|
||||
" . 6.(.6.4.4.4.(.6. & ~+P.P.P.P.P.P.P.P.P.h+ c+6+|+6+9+6+9+6+|+6+|+6+9+9+ ",
|
||||
" . l.[.[.(.6.(.4.(.3+ % l+S.V.~+P.P.P.~+P.P.S. 9+]+|+6+9+6+|+9+9+]+|+6+9+t+ ",
|
||||
" ~ *+(.6.4.4.4.6.4.l. % ~+P.S.P.S.P.S.P.S.S.0+ B+9+6+9+9+6+|+9+6+6+|+]+]+|+D+ ",
|
||||
" ~ '+(.[.(.(.(.4.4.6.d+ & S.P.S.P.S.P.S.P.P.~+ i+9+6+6+9+6+6+:+9+:+6+|+6+c+ ",
|
||||
" = '+(.[.(.(.[.4.[.(.O. % ~+~+S.Z.S.Z.S.P.Z.S.~+ 9+6+6+9+6+9+:+9+6+6+9+6+]+m+ ",
|
||||
" _ '+Z }.}.4.6.(.(.[.4. & q+S.S.S.~+S.~+S.S.S.S.l+ x+]+9+6+9+6+9+6+9+9+6+9+|+6+B+ ",
|
||||
" ) h h.Z -.-.}.(.(.[.[.(.'+ & R.Z.Z.S.S.S.S.~+Z.Z.R. r+|+6+|+6+9+6+|+6+6+9+6+9+]+F+ ",
|
||||
" _ 3 x k.j.I Z -.Z Z }.4.(.(.4.t. % l+S.S.Z.Z.Z.S.S.S.S.Z.h+ F+]+9+6+9+6+9+6+9+9+6+9+6+6+c+ ",
|
||||
" ) 3 7 q I I Z N }.}.}.(.}.}.(. & Z.Z.S.Z.Z.S.Z.Z.Z.Z.~+ B+6+6+9+6+|+6+9+6+6+|+6+6+9+y+ ",
|
||||
" = ! 7 c q I N Z Z Z Z -.}.}.}.'+ % l+S.Z.S.S.Z.S.S.S.S.Z.h+ c+9+6+9+6+9+6+|+9+6+9+9+6+6+ ",
|
||||
" _ ) 3 l q N N Z N -.}.Z -.(.O. & R.R.R.S.K.S.Z.Z.Z.V.$+ D+6+6+9+6+9+6+9+6+6+9+6+6+9+m+ ",
|
||||
" _ 3 3 l q I N Z Z -.-.}.}.Z & $+S.S.Z.Z.Z.K.K.K.K.S.h+ i+9+6+9+6+9+6+9+9+6+9+9+6+9+B+ ",
|
||||
" _ | 7 l y I I N N Z -.(.-.d+ % l+R.R.R.R.S.K.Z.S.S.S.~+ F+9+6+9+|+6+6+9+6+6+9+|+6+6+9+ ",
|
||||
" _ 3 7 c q I R Z :.Z Z -.'+ & R.K.K.K.R.Z.S.Z.Z.Z.K.l+ r+9+6+6+9+6+6+6+9+6+6+9+9+6+t+ ",
|
||||
" ) | 7 l y y I N R Z -.D & ;+R.Z.Z.S.K.K.R.Z.S.S.5+ G+6+6+9+9+6+9+9+9+6+6+9+6+6+]+E+ ",
|
||||
" 3 x c I y I Z Z Z h.' & 5+K.K.R.R.S.K.S.K.R.R.S. t+9+6+|+9+6+6+6+6+9+6+9+|+|+i+ ",
|
||||
" | c l q I R I Z h. $ & $+R.s.K.R.R.Z.R.K.K.K.5+ 9+6+9+6+6+6+c+6+9+6+9+|+6+6+x+ ",
|
||||
" h 7 l q y N N j. * & H.H.R.s.K.H.H.K.Z.K.S.Z. r+9+6+9+6+6+6+6+6+6+6+9+9+9+c+ ",
|
||||
" 0.l q y I y '+ ' ; ;+v.v.K.R.H.K.K.v.H.Z.R.5+ G+9+|+6+c+6+c+6+c+6+6+9+6+|+6+B+ ",
|
||||
" 0.x D j. ' & ;+s.R.s.K.H.s.s.R.K.K.K.$+ m+9+9+|+9+6+9+6+6+c+6+6+9+9+6+ ",
|
||||
" ( ( ; ;+v.$+s.R.v.H.R.K.K.R.s.R. E+9+|+9+6+6+c+6+c+6+6+9+9+6+6+t+ ",
|
||||
" ( } & ; /+5.v.v.v.s.H.R.H.H.s.K.R.5+ i+9+|+9+9+6+6+6+c+6+6+6+9+6+9+ ",
|
||||
" ( } - ; /+q.s.s.s.E.H.v.K.v.R.K.R.$+ y+|+|+9+9+|+9+c+6+6+c+9+9+|+9+x+ ",
|
||||
" ( 2 0 - ; D.d.q.q.E.v.v.s.s.H.s.R.v.$+ 9+9+|+|+9+6+6+9+c+6+6+6+c+6+9+ ",
|
||||
" } 0 0 ; ; 3.q.b.q.q.s.E.s.H.H.v.K.v.H.5+ r+9+|+9+9+6+c+9+6+6+c+9+9+|+|+x+ ",
|
||||
" } 0 d d ; < N.5.3.q.q.q.q.s.E.E.E.s.v.s.H.5+ D+9+9+|+9+9+6+c+9+6+|+6+9+9+9+9+ ",
|
||||
" } 2 s s s M w.w.e @.{.^.3.3.b.d.q.q.q.s.v.v.s.H.s.$+ i+9+|+9+|+c+6+|+6+9+9+9+9+6+|+y+ ",
|
||||
" 0 0 d s C M V V V ^.^.5.5.5.3.d.q.q.q.q.s.v.E.R.E. r+9+c+|+9+|+9+c+c+|+|+9+6+9+6+i+ ",
|
||||
" 2 2 s w C C V V %.{.{.5.b.5.q.3.d.q.q.s.E.E.v.s.q+ w+9+9+9+9+9+9+|+|+|+9+9+|+9+|+9+E+ ",
|
||||
" 0 0 d w C C S V V {.%.#.3.5.d.d.d.q.q.s.s.E.E.q+ E+c+9+9+c+9+c+9+|+9+|+9+9+9+9+|+i+ ",
|
||||
" } d s s C M V S %.%.^.3.3.3.3.3.d.d.q.d.q.s./+ i+!+c+9+9+9+|+9+9+|+9+|+9+|+|+9+G+ ",
|
||||
" 0 d d e M M V S {.{.^.3.3.d.d.D.d.d.q.q.v./+ r+[+|+{+9+c+9+9+9+9+9+9+|+9+9+9+B+ ",
|
||||
" d s s s V S V S %.{.{.5.3.3.d.d.q.q.q./+ o+[+!+c+9+|+9+c+9+c+9+c+9+9+|+9+m+ ",
|
||||
" 0 s C s V V %.V {.^.{.5.5.3.3.D.d.q./+ n+{+[+{+[+c+c+|+9+9+9+|+9+c+9+|+|+G+ ",
|
||||
" d w s M M M V %.%.{.3.{.3.d.d.d.q./+ w+[+[+{+&+{+{+{+9+c+9+c+9+c+|+9+c+B+ ",
|
||||
" w s @.S S S %.%.{.5.5.^.3.b.D. G+@+[+[+[+c+[+<+[+9+c+|+c+9+9+c+|+m+ ",
|
||||
" w s C M V V S %.^.{.{.3.5.N. C+@+[+{+&+{+&+<+[+{+{+{+9+9+9+c+9+c+G+ ",
|
||||
" w M M S V {.V ^.5.5.{. w+,+,+@+[+[+{+[+{+c+9+<+[+9+9+9+9+{+D+ ",
|
||||
" M @.V V {.{.#.++ w+@+@+@+[+{+[+[+{+[+&+[+[+{+c+{+{+{+x+ ",
|
||||
" w.>.p t b ). 8+@+,+[+,+@+{+[+[+&+{+{+{+[+!+[+9+9+b+ ",
|
||||
" +.b b n u %+ 2+#+,+@+Q.@+[+@+,+[+[+[+[+[+<+[+<+{+i+ ",
|
||||
" b b k u u u+M.M.#+#+@+,+@+[+@+[+{+[+[+{+[+[+[+[+{+z+ ",
|
||||
" b b n u u /. A+2+M.#+#+#+,+@+@+,+,+[+@+,+[+{+[+{+{+[+<+w+ ",
|
||||
" t k k F u P /.7+ A+e+z.#+#+@+M.#+#+,+#+@+Q.[+@+@+,+{+&+[+{+[+B+ ",
|
||||
" Y n u u E Q U U T.7+ f+4+I.M.>+M.M.Y.@+M.@+#+@+,+@+@+[+[+[+[+[+[+[+n+ ",
|
||||
" k n F Q Q U .=._.8.G.(+(+)+g.i.z.r.I.M.I.>+I.#+#+#+#+#+#+,+,+Q.,+@+,+@+{+{+o+ ",
|
||||
" k u u E U U U =.=._.2.g.i.i.i.A.r.z.z.I.M.Y.#+>+#+#+#+Q.@+@+@+@+,+[+@+[+,+s+ ",
|
||||
" ).n E E P U =.=.=.8._._.g.g.i.i.A.A.z.I.z.M.Y.M.M.M.#+#+#+,+@+@+@+[+,+@+g+ ",
|
||||
" u u Q U U U =._._.2.g.g.g.A.A.A.z.A.z.M.I.I.Y.#+#+#+#+@+#+,+,+,+,+@+o+ ",
|
||||
" ).F Q U U =.=.=.8._.2.2.g.r.r.r.I.I.I.z.M.I.M.#+M.#+#+#+#+@+@+@+,+w+ ",
|
||||
" Q Q P U U =._._.2._.g.g.g.i.A.A.A.A.I.I.M.X.M.#+@+#+@+@+@+,+#+z+ ",
|
||||
" u.U /.U U 8._.8._.2.2.g.g.i.A.A.I.z.I.I.M.I.Y.>+@+#+#+#+#+#+A+ ",
|
||||
" ].U U =.=.=.2.2._.g.i.i.A.i.A.A.I.z.I.>+Y.>+Y.Y.#+#+#+n+ ",
|
||||
" /.U .=.=._._.2.2.g.i.A.A.z.A.I.I.M.I.M.M.M.M.Y.M.z+ ",
|
||||
" /.=.8._._.2._.g.g.g.g.i.A.z.A.z.I.I.Y.I.X.#+8+ ",
|
||||
" /.8.8._._.2.2.i.i.A.i.A.z.z.I.I.I.M.M.8+A+ ",
|
||||
" }+8._.2.2.g.2.A.i.A.A.A.>+z.z.z.4+A+ ",
|
||||
" T.)+_.g.2.A.r.A.A.r.A.4+z+ ",
|
||||
" }+}+(+(+f+f+A+ ",
|
||||
" ",
|
||||
" "};
|
||||
3171
QGLViewer/qglviewer.cpp
Normal file
3171
QGLViewer/qglviewer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1278
QGLViewer/qglviewer.h
Normal file
1278
QGLViewer/qglviewer.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
QGLViewer/qglviewer.icns
Normal file
BIN
QGLViewer/qglviewer.icns
Normal file
Binary file not shown.
BIN
QGLViewer/qglviewer_fr.qm
Normal file
BIN
QGLViewer/qglviewer_fr.qm
Normal file
Binary file not shown.
608
QGLViewer/qglviewer_fr.ts
Normal file
608
QGLViewer/qglviewer_fr.ts
Normal file
@ -0,0 +1,608 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="fr_FR">
|
||||
<context>
|
||||
<name>ImageInterface</name>
|
||||
<message>
|
||||
<source>Image settings</source>
|
||||
<translation>Réglages d'image</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Width</source>
|
||||
<translation>Largeur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> px</source>
|
||||
<translation> px</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Width of the image (in pixels)</source>
|
||||
<translation>Largeur de l'image (en pixels)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Height</source>
|
||||
<translation>Hauteur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Height of the image (in pixels)</source>
|
||||
<translation>Hauteur de l'image (en pixels)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Image quality</source>
|
||||
<translation>Qualité d'image</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Between 0 (smallest files) and 100 (highest quality)</source>
|
||||
<translation>Entre 0 (taille de fichier minimale) et 100 (qualité maximale)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Oversampling</source>
|
||||
<translation>Suréchantillonage</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>x </source>
|
||||
<translation>x </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Antialiases image (when larger then 1.0)</source>
|
||||
<translation>Anti-alliassage de l'image (si supérieur à 1.0)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use white background</source>
|
||||
<translation>Fond blanc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use white as background color</source>
|
||||
<translation>Mettre du blanc en couleur de fond</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expand frustum if needed</source>
|
||||
<translation>Etendre la pyramide de vue ( frustum) si nécessaire</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>When image aspect ratio differs from viewer's one, expand frustum as needed. Fits inside current frustum otherwise.</source>
|
||||
<translation>Lorsque le rapport de dimensions de l'image diffère de celui de la fenêtre, étendre la pyramide de vue (frustum) en conséquence. L'image est ajustée à l'intérieur de la vue actuelle sinon. </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OK</source>
|
||||
<translation>Ok</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation>Annuler</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QGLViewer</name>
|
||||
<message>
|
||||
<source>snapshot</source>
|
||||
<comment>Default snapshot file name</comment>
|
||||
<translation>capture</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1Hz</source>
|
||||
<comment>Frames per seconds, in Hertz</comment>
|
||||
<translation>%1Hz</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggles the display of the FPS</source>
|
||||
<comment>DISPLAY_FPS action description</comment>
|
||||
<translation>Active ou non l'affichage de la fréquence d'affiichage</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saves a screenshot</source>
|
||||
<comment>SAVE_SCREENSHOT action description</comment>
|
||||
<translation>Sauvegarde une capture d'écran</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggles full screen display</source>
|
||||
<comment>FULL_SCREEN action description</comment>
|
||||
<translation>Passe ou non en mode plein écran</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggles the display of the world axis</source>
|
||||
<comment>DRAW_AXIS action description</comment>
|
||||
<translation>Affiche ou non le repère du monde</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggles the display of the XY grid</source>
|
||||
<comment>DRAW_GRID action description</comment>
|
||||
<translation>Affiche ou non la grille XY</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Changes camera mode (observe or fly)</source>
|
||||
<comment>CAMERA_MODE action description</comment>
|
||||
<translation>Change le mode de la caméra (observateur ou vol)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggles stereo display</source>
|
||||
<comment>STEREO action description</comment>
|
||||
<translation>Affiche ou non en stéréo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Opens this help window</source>
|
||||
<comment>HELP action description</comment>
|
||||
<translation>Ouvre la fenêtre d'aide</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts/stops the animation</source>
|
||||
<comment>ANIMATION action description</comment>
|
||||
<translation>Démarre/arrête l'animation</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggles camera paths display</source>
|
||||
<comment>EDIT_CAMERA action description</comment>
|
||||
<translation>Affiche ou non les chemins de caméra</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggles the display of the text</source>
|
||||
<comment>ENABLE_TEXT action description</comment>
|
||||
<translation>Affiche ou non les textes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exits program</source>
|
||||
<comment>EXIT_VIEWER action description</comment>
|
||||
<translation>Quitte l'application</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Moves camera left</source>
|
||||
<comment>MOVE_CAMERA_LEFT action description</comment>
|
||||
<translation>Déplace la caméra sur la gauche</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Moves camera right</source>
|
||||
<comment>MOVE_CAMERA_RIGHT action description</comment>
|
||||
<translation>Déplace la caméra sur la droite</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Moves camera up</source>
|
||||
<comment>MOVE_CAMERA_UP action description</comment>
|
||||
<translation>Déplace la caméra vers le haut</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Moves camera down</source>
|
||||
<comment>MOVE_CAMERA_DOWN action description</comment>
|
||||
<translation>Déplace la caméra vers le bas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Increases fly speed</source>
|
||||
<comment>INCREASE_FLYSPEED action description</comment>
|
||||
<translation>Augmente la vitesse de vol</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decreases fly speed</source>
|
||||
<comment>DECREASE_FLYSPEED action description</comment>
|
||||
<translation>Diminue la vitesse de vol</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copies a snapshot to clipboard</source>
|
||||
<comment>SNAPSHOT_TO_CLIPBOARD action description</comment>
|
||||
<translation>Place une capture d'écran dans le presse-papier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stereo not supported</source>
|
||||
<comment>Message box window title</comment>
|
||||
<translation>Stéréo non supportée</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stereo is not supported on this display.</source>
|
||||
<translation>Affichage en stéréo non supporté sur cette machine.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rotates</source>
|
||||
<comment>ROTATE mouse action</comment>
|
||||
<translation>Tourne</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Zooms</source>
|
||||
<comment>ZOOM mouse action</comment>
|
||||
<translation>Zoome</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Translates</source>
|
||||
<comment>TRANSLATE mouse action</comment>
|
||||
<translation>Translate</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Moves backward</source>
|
||||
<comment>MOVE_BACKWARD mouse action</comment>
|
||||
<translation>Recule</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Horizontally/Vertically translates</source>
|
||||
<comment>SCREEN_TRANSLATE mouse action</comment>
|
||||
<translation>Translate horizontalement/verticalement</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Moves forward</source>
|
||||
<comment>MOVE_FORWARD mouse action</comment>
|
||||
<translation>Avance</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Looks around</source>
|
||||
<comment>LOOK_AROUND mouse action</comment>
|
||||
<translation>Regarde</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rotates in screen plane</source>
|
||||
<comment>SCREEN_ROTATE mouse action</comment>
|
||||
<translation>Pivote dans le plan écran</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rolls</source>
|
||||
<comment>ROLL mouse action</comment>
|
||||
<translation>Pivote</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Drives</source>
|
||||
<comment>DRIVE mouse action</comment>
|
||||
<translation>Avance</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Zooms on region for</source>
|
||||
<comment>ZOOM_ON_REGION mouse action</comment>
|
||||
<translation>Zoome sur la région pour</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Zooms on pixel</source>
|
||||
<comment>ZOOM_ON_PIXEL click action</comment>
|
||||
<translation>Zoome sur le pixel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Zooms to fit scene</source>
|
||||
<comment>ZOOM_TO_FIT click action</comment>
|
||||
<translation>Zoome pour ajuster à la scène</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Selects</source>
|
||||
<comment>SELECT click action</comment>
|
||||
<translation>Sélectionne</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sets pivot point</source>
|
||||
<comment>RAP_FROM_PIXEL click action</comment>
|
||||
<translation>Définit le point de rotation</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Resets pivot point</source>
|
||||
<comment>RAP_IS_CENTER click action</comment>
|
||||
<translation>Restaure le point de rotation</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Centers manipulated frame</source>
|
||||
<comment>CENTER_FRAME click action</comment>
|
||||
<translation>Centre le repère manipulé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Centers scene</source>
|
||||
<comment>CENTER_SCENE click action</comment>
|
||||
<translation>Centre la scène</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Shows entire scene</source>
|
||||
<comment>SHOW_ENTIRE_SCENE click action</comment>
|
||||
<translation>Affiche toute la scène</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Aligns manipulated frame</source>
|
||||
<comment>ALIGN_FRAME click action</comment>
|
||||
<translation>Aligne le repère manipulé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Aligns camera</source>
|
||||
<comment>ALIGN_CAMERA click action</comment>
|
||||
<translation>Aligne la caméra</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Camera paths are controlled using the %1 keys (noted <i>Fx</i> below):</source>
|
||||
<comment>Help window key tab camera keys</comment>
|
||||
<translation>Les chemins de caméra sont contrôlés avec les touches %1 (notées <i>Fx</i> ci-dessous) :</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key(s)</source>
|
||||
<comment>Keys column header in help window mouse tab</comment>
|
||||
<translation>Touche(s)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Description</source>
|
||||
<comment>Description column header in help window mouse tab</comment>
|
||||
<translation>Description</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Standard viewer keys</source>
|
||||
<comment>In help window keys tab</comment>
|
||||
<translation>Raccourcis standards</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fx</source>
|
||||
<comment>Generic function key (F1..F12)</comment>
|
||||
<translation>Fx</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Plays path (or resets saved position)</source>
|
||||
<translation>Joue le chemin (ou restaure la position sauvegardée)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adds a key frame to path (or defines a position)</source>
|
||||
<translation>Ajoute une position clef au chemin (ou définit une position)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Deletes path (or saved position)</source>
|
||||
<translation>Supprime le chemin (ou la position)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Button(s)</source>
|
||||
<comment>Buttons column header in help window mouse tab</comment>
|
||||
<translation>Bouton(s)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Standard mouse bindings</source>
|
||||
<comment>In help window mouse tab</comment>
|
||||
<translation>Actions souris standards</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wheel</source>
|
||||
<comment>Mouse wheel</comment>
|
||||
<translation>Molette</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Help</source>
|
||||
<comment>Help window tab title</comment>
|
||||
<translation>&Aide</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Keyboard</source>
|
||||
<comment>Help window tab title</comment>
|
||||
<translation>&Clavier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Mouse</source>
|
||||
<comment>Help window tab title</comment>
|
||||
<translation>&Souris</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Help</source>
|
||||
<comment>Help window title</comment>
|
||||
<translation>Aide</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path %1 deleted</source>
|
||||
<comment>Feedback message</comment>
|
||||
<translation>Chemin %1 supprimé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Position %1 deleted</source>
|
||||
<comment>Feedback message</comment>
|
||||
<translation>Position %1 supprimée</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path %1, position %2 added</source>
|
||||
<comment>Feedback message</comment>
|
||||
<translation>Chemin %1, position %2 ajoutée</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Position %1 saved</source>
|
||||
<comment>Feedback message</comment>
|
||||
<translation>Position %1 sauvegardée</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Camera in observer mode</source>
|
||||
<comment>Feedback message</comment>
|
||||
<translation>Caméra en mode observateur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Camera in fly mode</source>
|
||||
<comment>Feedback message</comment>
|
||||
<translation>Caméra en mode vol</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save to file error</source>
|
||||
<comment>Message box window title</comment>
|
||||
<translation>Erreur lors de la sauvegarde</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>State file name (%1) references a directory instead of a file.</source>
|
||||
<translation>Le nom du fichier d'état (%1) référence un répértoire et non un fichier.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to create directory %1</source>
|
||||
<translation>Le répértoire %1 ne peut être créé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to save to file %1</source>
|
||||
<translation>Impossible de sauvegarder le fichier %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Problem in state restoration</source>
|
||||
<comment>Message box window title</comment>
|
||||
<translation>Problème lors de la restauration de l'état</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File %1 is not readable.</source>
|
||||
<translation>Le fichier %1 n'est pas lisible.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open file error</source>
|
||||
<comment>Message box window title</comment>
|
||||
<translation>Erreur d'ouverture de fichier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to open file %1</source>
|
||||
<translation>Le fichier %1 ne peut être ouvert</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Left</source>
|
||||
<comment>left mouse button</comment>
|
||||
<translation>Gauche</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Middle</source>
|
||||
<comment>middle mouse button</comment>
|
||||
<translation>Milieu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Right</source>
|
||||
<comment>right mouse button</comment>
|
||||
<translation>Droit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> double click</source>
|
||||
<comment>Suffix after mouse button</comment>
|
||||
<translation>Double clic </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>camera</source>
|
||||
<comment>Suffix after action</comment>
|
||||
<translation>la caméra</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>manipulated frame</source>
|
||||
<comment>Suffix after action</comment>
|
||||
<translation>le repère manipulé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> with </source>
|
||||
<comment>As in : Left button with Ctrl pressed</comment>
|
||||
<translation> avec </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> pressed</source>
|
||||
<comment>As in : Left button with Ctrl pressed</comment>
|
||||
<translation> enfoncé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1%2%3%4%5%6</source>
|
||||
<comment>Modifier / button or wheel / double click / with / button / pressed</comment>
|
||||
<translation>%1%3%2%4%5%6</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No help available.</source>
|
||||
<translation>Pas d'aide disponible.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exporter error</source>
|
||||
<comment>Message box window title</comment>
|
||||
<translation>Erreur d'export</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to open file %1.</source>
|
||||
<translation>Impossible d'ouvrir le ficher %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>BSP Construction</source>
|
||||
<translation>Construction du BSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exporting to file %1</source>
|
||||
<translation>Export vers le fichier %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Parsing feedback buffer.</source>
|
||||
<translation>Parcours du feedback buffer.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Topological sort</source>
|
||||
<translation>Tri topologique</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Advanced topological sort</source>
|
||||
<translation>Tri topologique avancé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Rendering...</source>
|
||||
<translation>Rendu...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Visibility optimization</source>
|
||||
<translation>Optimisation de la visibilité</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&About</source>
|
||||
<comment>Help window about title</comment>
|
||||
<translation>À &propos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><h1>libQGLViewer</h1><h3>Version %1</h3><br>A versatile 3D viewer based on OpenGL and Qt<br>Copyright 2002-%2 Gilles Debunne<br><code>%3</code></source>
|
||||
<translation><h1>libQGLViewer</h1><h3>Version %1</h3><br>Un afficheur 3D généraliste basé sur OpenGL et Qt<br>Copyright 2002-%2 Gilles Debunne<br><code>%3</code></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>VRenderInterface</name>
|
||||
<message>
|
||||
<source>Vectorial rendering options</source>
|
||||
<translation>Options de rendu vectoriel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Include hidden parts</source>
|
||||
<translation>Inclure les parties cachées</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cull back faces</source>
|
||||
<translation>Supprimer les faces arrières</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Back faces (non clockwise point ordering) are removed from the output</source>
|
||||
<translation>Les faces orientées vers l'arrière (points ordonnés dans le sens anti-horaire) sont supprimées du résultat (Back Face Culling)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Black and white</source>
|
||||
<translation>Noir et blanc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Black and white rendering</source>
|
||||
<translation>Rendu en noir et blanc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Color background</source>
|
||||
<translation>Fond avec une couleur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use current background color instead of white</source>
|
||||
<translation>Utiliser la couleur de fond actuelle à la place du blanc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tighten bounding box</source>
|
||||
<translation>Ajuster la boîte englobante</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fit output bounding box to current display</source>
|
||||
<translation>Ajuster la boîte englobante de la sortie à ce qui est actuellement affiché</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Polygon depth sorting method</source>
|
||||
<translation>Méthode de tri de la profondeur des polygônes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No sorting</source>
|
||||
<translation>Pas de tri</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Topological</source>
|
||||
<translation>Topologique</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Advanced topological</source>
|
||||
<translation>Topologique avancé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save</source>
|
||||
<translation>Sauvegarder</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation>Annuler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hidden polygons are also included in the output (usually twice bigger)</source>
|
||||
<translation>Inclure les polygônes cachés dans le résultat (alors habituellement deux fois plus gros)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort method:</source>
|
||||
<translation>Méthode de tri :</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>BSP</source>
|
||||
<translation>BSP</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
552
QGLViewer/quaternion.cpp
Normal file
552
QGLViewer/quaternion.cpp
Normal file
@ -0,0 +1,552 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <stdlib.h> // 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
|
||||
<name q0=".." q1=".." q2=".." q3=".." />
|
||||
\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& center, 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);
|
||||
}
|
||||
311
QGLViewer/quaternion.h
Normal file
311
QGLViewer/quaternion.h
Normal file
@ -0,0 +1,311 @@
|
||||
/****************************************************************************
|
||||
|
||||
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_QUATERNION_H
|
||||
#define QGLVIEWER_QUATERNION_H
|
||||
|
||||
#include "vec.h"
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
|
||||
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
|
||||
|
||||
<h3>Internal representation</h3>
|
||||
|
||||
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
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const qglviewer::Quaternion&);
|
||||
|
||||
#endif // QGLVIEWER_QUATERNION_H
|
||||
494
QGLViewer/saveSnapshot.cpp
Normal file
494
QGLViewer/saveSnapshot.cpp
Normal file
@ -0,0 +1,494 @@
|
||||
/****************************************************************************
|
||||
|
||||
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"
|
||||
|
||||
#include "ui_ImageInterface.h"
|
||||
|
||||
// Output format list
|
||||
# include <QImageWriter>
|
||||
|
||||
#include <qfileinfo.h>
|
||||
#include <qfiledialog.h>
|
||||
#include <qmessagebox.h>
|
||||
#include <qapplication.h>
|
||||
#include <qmap.h>
|
||||
#include <qinputdialog.h>
|
||||
#include <qprogressdialog.h>
|
||||
#include <qcursor.h>
|
||||
|
||||
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<QString, QString> Qtformat;
|
||||
// Converts Qt snapshotFormat to QFileDialog menu string.
|
||||
static QMap<QString, QString> FDFormatString;
|
||||
// Converts snapshotFormat to file extension
|
||||
static QMap<QString, QString> 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()
|
||||
{
|
||||
QList<QByteArray> 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
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
imageInterface->imgWidth->setValue(width());
|
||||
imageInterface->imgHeight->setValue(height());
|
||||
|
||||
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<qreal>(height());
|
||||
qreal newAspectRatio = finalSize.width() / static_cast<qreal>(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
|
||||
{
|
||||
camera()->getOrthoWidthHeight(xMin, yMin);
|
||||
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<qreal>(finalSize.width());
|
||||
qreal scaleY = subSize.height() / static_cast<qreal>(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 * height();
|
||||
tileXMin = (width() - tileTotalWidth) / 2.0;
|
||||
tileWidth = tileTotalWidth * scaleX;
|
||||
tileYMin = 0.0;
|
||||
tileHeight = height() * scaleY;
|
||||
tileRegion_->textScale = 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; i<nbX; i++)
|
||||
for (int j=0; j<nbY; j++)
|
||||
{
|
||||
preDraw();
|
||||
|
||||
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();
|
||||
|
||||
// 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_ = NULL;
|
||||
|
||||
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; i<nbImages; ++i)
|
||||
{
|
||||
camera()->setOrientation(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<QFileDialog::Option>(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;
|
||||
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();
|
||||
}
|
||||
|
||||
/*! 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());
|
||||
}
|
||||
|
||||
164
QGLViewer/vec.cpp
Normal file
164
QGLViewer/vec.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
/****************************************************************************
|
||||
|
||||
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; 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
|
||||
<name x=".." y=".." z=".." />
|
||||
\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;
|
||||
}
|
||||
|
||||
390
QGLViewer/vec.h
Normal file
390
QGLViewer/vec.h
Normal file
@ -0,0 +1,390 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 <math.h>
|
||||
#include <iostream>
|
||||
|
||||
# include <QDomElement>
|
||||
|
||||
// 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
|
||||
|
||||
<h3>Interface with other vector classes</h3>
|
||||
|
||||
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 <class C>
|
||||
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 <class C>
|
||||
// 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
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const qglviewer::Vec&);
|
||||
|
||||
#endif // QGLVIEWER_VEC_H
|
||||
73
log750-lab.pro
Normal file
73
log750-lab.pro
Normal file
@ -0,0 +1,73 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui xml opengl
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = simpleViewer
|
||||
TEMPLATE = app
|
||||
|
||||
QMAKE_CXXFLAGS += -std=c++0x
|
||||
|
||||
SOURCES += QGLViewer/camera.cpp \
|
||||
QGLViewer/constraint.cpp \
|
||||
QGLViewer/frame.cpp \
|
||||
QGLViewer/keyFrameInterpolator.cpp \
|
||||
QGLViewer/manipulatedCameraFrame.cpp \
|
||||
QGLViewer/manipulatedFrame.cpp \
|
||||
QGLViewer/mouseGrabber.cpp \
|
||||
QGLViewer/qglviewer.cpp \
|
||||
QGLViewer/quaternion.cpp \
|
||||
QGLViewer/saveSnapshot.cpp \
|
||||
QGLViewer/vec.cpp \
|
||||
src/main.cpp \
|
||||
src/window/mainwindow.cpp \
|
||||
src/viewer/simpleViewer.cpp \
|
||||
src/glnodes/glnode.cpp \
|
||||
src/glnodes/shapes.cpp \
|
||||
src/glnodes/scenegroup.cpp
|
||||
|
||||
HEADERS += QGLViewer/camera.h \
|
||||
QGLViewer/config.h \
|
||||
QGLViewer/constraint.h \
|
||||
QGLViewer/domUtils.h \
|
||||
QGLViewer/frame.h \
|
||||
QGLViewer/keyFrameInterpolator.h \
|
||||
QGLViewer/manipulatedCameraFrame.h \
|
||||
QGLViewer/manipulatedFrame.h \
|
||||
QGLViewer/mouseGrabber.h \
|
||||
QGLViewer/qglviewer.h \
|
||||
QGLViewer/quaternion.h \
|
||||
QGLViewer/vec.h \
|
||||
src/window/mainwindow.h \
|
||||
src/viewer/simpleViewer.h \
|
||||
src/glnodes/glnode.h \
|
||||
src/glnodes/shapes.h \
|
||||
src/interfaces/ivisitable.h \
|
||||
src/interfaces/visitor.h \
|
||||
src/glnodes/scenegroup.h
|
||||
|
||||
DISTFILES += src/shaders/basicShader.vert \
|
||||
src/shaders/basicShader.frag
|
||||
|
||||
FORMS += QGLViewer/ImageInterface.ui mainwindow.ui
|
||||
|
||||
|
||||
CONFIG *= debug_and_release console qt opengl warn_on thread create_prl rtti
|
||||
|
||||
DEFINES *= QGLVIEWER_STATIC
|
||||
win32 {
|
||||
DEFINES *= NOMINMAX
|
||||
}
|
||||
|
||||
win32 {
|
||||
contains ( QT_MAJOR_VERSION, 5 ) {
|
||||
greaterThan( QT_MINOR_VERSION, 4) {
|
||||
LIBS *= -lopengl32
|
||||
}
|
||||
}
|
||||
}
|
||||
176
mainwindow.ui
Normal file
176
mainwindow.ui
Normal file
@ -0,0 +1,176 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>588</width>
|
||||
<height>499</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Forme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Couleur</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Vide</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Triangle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Carré</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Cercle</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Couleur</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>588</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_File">
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<addaction name="action_Quit"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<action name="action_Quit">
|
||||
<property name="text">
|
||||
<string>&Quit</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Esc</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>action_Quit</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
17
src/glnodes/glnode.cpp
Normal file
17
src/glnodes/glnode.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "glnode.h"
|
||||
#include <qopengl.h>
|
||||
|
||||
|
||||
//GlNode& GlNode::getChild() {
|
||||
// GlNode* tempNode = new GlNode();
|
||||
// return *tempNode;
|
||||
//}
|
||||
|
||||
//bool GlNode::hasNext(){
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//void GlNode::accept(Visitor &v) {
|
||||
|
||||
// //v.visit(this);
|
||||
//}
|
||||
14
src/glnodes/glnode.h
Normal file
14
src/glnodes/glnode.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef GLNODE
|
||||
#define GLNODE
|
||||
#include <QMatrix4x4>
|
||||
#include "../interfaces/ivisitable.h"
|
||||
#include "../interfaces/visitor.h"
|
||||
class Viewer;
|
||||
class GlNode : public IVisitable {
|
||||
public:
|
||||
|
||||
QMatrix4x4 transform;
|
||||
};
|
||||
|
||||
#endif // GLNODE
|
||||
|
||||
42
src/glnodes/scenegroup.cpp
Normal file
42
src/glnodes/scenegroup.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "scenegroup.h"
|
||||
|
||||
SceneGroup::SceneGroup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::vector<GlNode*>* SceneGroup::getChildren() {
|
||||
return &children;
|
||||
}
|
||||
|
||||
GlNode* SceneGroup::childAt(int i) {
|
||||
return children.at(i);
|
||||
}
|
||||
|
||||
GlNode* SceneGroup::getChild(){
|
||||
// Automatically loops
|
||||
if(childIndex >= children.size() || childIndex < 0){ //just in case
|
||||
childIndex = 0;
|
||||
}
|
||||
return children.at(childIndex++);
|
||||
}
|
||||
|
||||
bool SceneGroup::hasNext()
|
||||
{
|
||||
if(childIndex >= children.size() || childIndex < 0) {
|
||||
childIndex = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneGroup::addChild(GlNode* child){
|
||||
|
||||
children.push_back(child);
|
||||
//children.push_back(std::shared_ptr<GlNode>(child));
|
||||
}
|
||||
|
||||
void SceneGroup::accept(Visitor &v) {
|
||||
v.visit(*this);
|
||||
}
|
||||
26
src/glnodes/scenegroup.h
Normal file
26
src/glnodes/scenegroup.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef SCENEGROUP_H
|
||||
#define SCENEGROUP_H
|
||||
|
||||
#include "glnode.h"
|
||||
#include "../interfaces/ivisitable.h"
|
||||
#include "../interfaces/visitor.h"
|
||||
|
||||
class SceneGroup : public GlNode
|
||||
{
|
||||
private:
|
||||
std::vector<GlNode*> children;
|
||||
int childIndex = 0;
|
||||
public:
|
||||
void addChild(GlNode* c);
|
||||
GlNode* getChild();
|
||||
bool hasNext();
|
||||
|
||||
std::vector<GlNode*>* getChildren();
|
||||
|
||||
GlNode* childAt(int i);
|
||||
|
||||
void accept(Visitor& v) override;
|
||||
SceneGroup();
|
||||
};
|
||||
|
||||
#endif // SCENEGROUP_H
|
||||
44
src/glnodes/shapes.cpp
Normal file
44
src/glnodes/shapes.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "shapes.h"
|
||||
#include <QColor>
|
||||
#include <QVector4D>
|
||||
|
||||
void Square::accept(Visitor &v) {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
void Circle::accept(Visitor &v) {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
void Triangle::accept(Visitor &v) {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
// ***
|
||||
|
||||
void Square::setColor(QColor& c) {
|
||||
color = QColor(c);
|
||||
}
|
||||
|
||||
void Circle::setColor(QColor& c) {
|
||||
color = QColor(c);
|
||||
}
|
||||
|
||||
void Triangle::setColor(QColor& c) {
|
||||
color = QColor(c);
|
||||
}
|
||||
|
||||
|
||||
QColor Triangle::getColor(){
|
||||
return color;
|
||||
};
|
||||
|
||||
|
||||
QColor Circle::getColor(){
|
||||
return color;
|
||||
};
|
||||
|
||||
|
||||
QColor Square::getColor(){
|
||||
return color;
|
||||
};
|
||||
41
src/glnodes/shapes.h
Normal file
41
src/glnodes/shapes.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef SHAPES_H
|
||||
#define SHAPES_H
|
||||
#include "glnode.h"
|
||||
#include <QVector4D>
|
||||
#include <QColor>
|
||||
#include "../interfaces/visitor.h"
|
||||
class Shape : public GlNode
|
||||
{
|
||||
public:
|
||||
virtual void setColor(QColor& c) = 0;
|
||||
virtual QColor getColor() = 0;
|
||||
};
|
||||
|
||||
class Circle : public Shape
|
||||
{
|
||||
public:
|
||||
Circle(){}
|
||||
QColor color;
|
||||
void accept(Visitor& v) override;
|
||||
void setColor(QColor& c);
|
||||
QColor getColor();
|
||||
};
|
||||
class Triangle : public Shape
|
||||
{
|
||||
public:
|
||||
Triangle(){}
|
||||
QColor color;
|
||||
void accept(Visitor& v) override;
|
||||
void setColor(QColor& c);
|
||||
QColor getColor();
|
||||
};
|
||||
class Square : public Shape
|
||||
{
|
||||
public:
|
||||
Square(){}
|
||||
QColor color;
|
||||
void accept(Visitor& v) override;
|
||||
void setColor(QColor& c);
|
||||
QColor getColor();
|
||||
};
|
||||
#endif // SHAPES_H
|
||||
10
src/interfaces/ivisitable.h
Normal file
10
src/interfaces/ivisitable.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef IVISITABLE_H
|
||||
#define IVISITABLE_H
|
||||
class Visitor;
|
||||
|
||||
class IVisitable {
|
||||
public:
|
||||
virtual void accept(Visitor& v) = 0;
|
||||
};
|
||||
|
||||
#endif // IVISITABLE_H
|
||||
15
src/interfaces/visitor.h
Normal file
15
src/interfaces/visitor.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef VISITOR_H
|
||||
#define VISITOR_H
|
||||
class SceneGroup;
|
||||
class Square;
|
||||
class Circle;
|
||||
class Triangle;
|
||||
|
||||
class Visitor {
|
||||
public:
|
||||
virtual void visit(SceneGroup &n) = 0;
|
||||
virtual void visit(Square &s) = 0;
|
||||
virtual void visit(Circle &s) = 0;
|
||||
virtual void visit(Triangle &s) = 0;
|
||||
};
|
||||
#endif // VISITOR_H
|
||||
29
src/main.cpp
Normal file
29
src/main.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "src/viewer/simpleViewer.h"
|
||||
#include "src/window/mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QColorDialog>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
QDesktopWidget dw;
|
||||
|
||||
// Set the core profile and version of OpenGL shaders
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setVersion(4, 0);
|
||||
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
|
||||
MainWindow w;
|
||||
|
||||
// Instantiate and layout the viewer.
|
||||
Viewer *v = new Viewer();
|
||||
w.addViewer(v);
|
||||
|
||||
//w.setFixedSize(dw.width() * 0.7, dw.height() * 0.7);
|
||||
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
9
src/shaders/basicShader.frag
Normal file
9
src/shaders/basicShader.frag
Normal file
@ -0,0 +1,9 @@
|
||||
#version 400 core
|
||||
in vec4 ifColor;
|
||||
out vec4 fColor;
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
fColor = ifColor;
|
||||
}
|
||||
14
src/shaders/basicShader.vert
Normal file
14
src/shaders/basicShader.vert
Normal file
@ -0,0 +1,14 @@
|
||||
#version 400 core
|
||||
uniform mat4 mvMatrix;
|
||||
uniform mat4 projMatrix;
|
||||
uniform vec4 color;
|
||||
in vec4 vPosition;
|
||||
out vec4 ifColor;
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
gl_Position = projMatrix * mvMatrix * vPosition;
|
||||
ifColor = color;
|
||||
}
|
||||
|
||||
444
src/viewer/simpleViewer.cpp
Normal file
444
src/viewer/simpleViewer.cpp
Normal file
@ -0,0 +1,444 @@
|
||||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.3.6.
|
||||
|
||||
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 "simpleViewer.h"
|
||||
#include "../interfaces/visitor.h"
|
||||
#include "../glnodes/scenegroup.h"
|
||||
#include "../glnodes/shapes.h"
|
||||
|
||||
#include <qopengl.h>
|
||||
#include <QOpenGLShaderProgram>
|
||||
|
||||
#include <qopengl.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <stack>
|
||||
#include <QMouseEvent>
|
||||
#include <typeinfo>
|
||||
using namespace std;
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
#define GRID_SIZE 10;
|
||||
|
||||
namespace
|
||||
{
|
||||
const int numVerticesSquare = 5;
|
||||
const int numVerticesTriangle = 3;
|
||||
const int numVerticesCircle = 26;
|
||||
const double frame_limit = 5;
|
||||
const double inc_mult = 5;
|
||||
const double inc_offset = 1.05;
|
||||
}
|
||||
|
||||
Viewer::Viewer()
|
||||
{
|
||||
activeColor = new QColor(255, 255, 255, 255);
|
||||
activeCell = nullptr;
|
||||
activeShape = 0;
|
||||
}
|
||||
|
||||
Viewer::~Viewer()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Viewer::cleanup()
|
||||
{
|
||||
makeCurrent();
|
||||
|
||||
// Delete shaders
|
||||
delete m_program;
|
||||
m_program = 0;
|
||||
|
||||
// Delete buffers
|
||||
glDeleteBuffers(NumBuffers, m_Buffers);
|
||||
glDeleteVertexArrays(NumVAOs, m_VAOs);
|
||||
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
void Viewer::draw()
|
||||
{
|
||||
// Bind our vertex/fragment shaders
|
||||
m_program->bind();
|
||||
|
||||
// Get projection and camera transformations
|
||||
QMatrix4x4 projectionMatrix;
|
||||
QMatrix4x4 modelViewMatrix;
|
||||
camera()->getProjectionMatrix(projectionMatrix);
|
||||
camera()->getModelViewMatrix(modelViewMatrix);
|
||||
|
||||
// Prepare a transformation stack
|
||||
|
||||
// stack<QMatrix4x4> modelStack;
|
||||
|
||||
modelViewMatrix.translate(-4.5, -4.5);
|
||||
modelViewMatrix.scale(0.95);
|
||||
|
||||
m_program->setUniformValue(m_projMatrixLocation, projectionMatrix);
|
||||
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
|
||||
|
||||
// Traverse the Scene in order to draw its components
|
||||
|
||||
modelStack.push(modelViewMatrix);
|
||||
root.accept(*this);
|
||||
}
|
||||
|
||||
void Viewer::mouseMoveEvent(QMouseEvent* e) {
|
||||
cout << "Viewer::mouseMoveEvent(QMouseEvent* e)" << endl;
|
||||
// Normal QGLViewer behavior.
|
||||
//QGLViewer::mouseMoveEvent(e);
|
||||
}
|
||||
|
||||
void Viewer::mousePressEvent(QMouseEvent* e) {
|
||||
|
||||
cout << "Viewer::mouseMoveEvent(QMouseEvent* e) : " << e->button() << endl;
|
||||
|
||||
if(e->button() == 1){ // LMB
|
||||
|
||||
int truY = 10 - (e->y()) / 500.0 * GRID_SIZE;
|
||||
int truX = (e->x() / 500.0) * GRID_SIZE;
|
||||
|
||||
cout << " -->Getting cell at " << truX << " : " << truY << endl;
|
||||
|
||||
SceneGroup* row = dynamic_cast<SceneGroup*> (root.childAt(truY));
|
||||
cout << " -->" << row << endl;
|
||||
SceneGroup* cell = dynamic_cast<SceneGroup*> (row->childAt(truX));
|
||||
cout << " -->" << cell << endl;
|
||||
|
||||
if(e->modifiers() & Qt::ShiftModifier){
|
||||
// add a shape
|
||||
if(!cell->getChildren()->size()){
|
||||
|
||||
// WARNING: CODE DEGEULASSE
|
||||
|
||||
Shape* s = nullptr;
|
||||
if(activeShape == 1){
|
||||
s = new Triangle();
|
||||
}else if(activeShape == 2){
|
||||
s = new Square();
|
||||
}else if(activeShape == 3){
|
||||
s = new Circle();
|
||||
}
|
||||
|
||||
// WARNING: END OF CODE DEGEULASSE
|
||||
|
||||
//activeCell->getChildren()->at(0) = s;
|
||||
if(s != nullptr){
|
||||
s->setColor(*activeColor);
|
||||
cell->addChild(s);
|
||||
this->update();
|
||||
deselect();
|
||||
activeCell = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(e->modifiers() & Qt::ControlModifier){
|
||||
// select a shape
|
||||
deselect();
|
||||
activeCell = cell;
|
||||
if(activeCell != nullptr && activeCell->getChildren()->size()){
|
||||
std::cout << "Cell has children..." << endl;
|
||||
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
|
||||
QColor newColor = shape->getColor();
|
||||
std::cout << newColor.Rgb << endl;
|
||||
newColor.setAlpha(255);
|
||||
shape->setColor(newColor);
|
||||
//emit shapeSelected(getTypeIndex(typeof activeCell->childAt(0)));
|
||||
int shapeId = 0;
|
||||
if(typeid(*(activeCell->getChildren()->at(0))) == typeid(Triangle))
|
||||
shapeId = 1;
|
||||
if(typeid(*(activeCell->getChildren()->at(0))) == typeid(Square))
|
||||
shapeId = 2;
|
||||
if(typeid(*(activeCell->getChildren()->at(0))) == typeid(Circle))
|
||||
shapeId = 3;
|
||||
emit shapeSelected(shapeId);
|
||||
}else{
|
||||
emit shapeSelected(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Viewer::deselect(){
|
||||
std::cout << "Deselecting cell " << activeCell << endl;
|
||||
if(activeCell != nullptr && activeCell->getChildren()->size()){
|
||||
std::cout << "Cell has children..." << endl;
|
||||
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
|
||||
QColor newColor = shape->getColor();
|
||||
std::cout << newColor.Rgb << endl;
|
||||
newColor.setAlpha(180);
|
||||
shape->setColor(newColor);
|
||||
}else{
|
||||
std::cout << "Cell has no children, moving on" << endl;
|
||||
}
|
||||
this->update();
|
||||
}
|
||||
|
||||
void Viewer::mouseReleaseEvent(QMouseEvent* e) {
|
||||
cout << "Viewer::mouseReleaseEvent(QMouseEvent* e)" << endl;
|
||||
//QGLViewer::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
void Viewer::init()
|
||||
{
|
||||
// We want to restrict ourselves to a 2D viewer.
|
||||
camera()->setType(qglviewer::Camera::ORTHOGRAPHIC);
|
||||
/*setMouseBinding(Qt::NoModifier, Qt::LeftButton, CAMERA, SCREEN_ROTATE);
|
||||
setMouseBinding(Qt::AltModifier, Qt::LeftButton, CAMERA, NO_MOUSE_ACTION);*/
|
||||
setMouseBinding(Qt::NoModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
|
||||
setMouseBinding(Qt::ControlModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
|
||||
setMouseBinding(Qt::ShiftModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
|
||||
|
||||
// Our scene will be from -5 to 5 in X and Y (the grid will be 10x10).
|
||||
setSceneRadius(5);
|
||||
showEntireScene();
|
||||
|
||||
// Init OpenGL objects
|
||||
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &Viewer::cleanup);
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
// Init shaders & geometry
|
||||
initShaders();
|
||||
initGeometries();
|
||||
initGrid();
|
||||
}
|
||||
|
||||
void Viewer::initShaders()
|
||||
{
|
||||
// Load vertex and fragment shaders
|
||||
m_program = new QOpenGLShaderProgram;
|
||||
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, "src/shaders/basicShader.vert")) {
|
||||
cerr << "Unable to load Shader" << endl
|
||||
<< "Log file:" << endl;
|
||||
qDebug() << m_program->log();
|
||||
}
|
||||
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, "src/shaders/basicShader.frag")) {
|
||||
cerr << "Unable to load Shader" << endl
|
||||
<< "Log file:" << endl;
|
||||
qDebug() << m_program->log();
|
||||
}
|
||||
m_program->link();
|
||||
m_program->bind(); // Note: This is equivalent to glUseProgram(programId());
|
||||
|
||||
// Specify shader input paramters
|
||||
// The strings "vPosition", "mvMatrix", etc. have to match an attribute name in the vertex shader.
|
||||
if ((m_vPositionLocation = m_program->attributeLocation("vPosition")) < 0)
|
||||
qDebug() << "Unable to find shader location for " << "vPosition";
|
||||
|
||||
if ((m_colorLocation = m_program->uniformLocation("color")) < 0)
|
||||
qDebug() << "Unable to find shader location for " << "color";
|
||||
|
||||
if ((m_mvMatrixLocation = m_program->uniformLocation("mvMatrix")) < 0)
|
||||
qDebug() << "Unable to find shader location for " << "mvMatrix";
|
||||
|
||||
if ((m_projMatrixLocation = m_program->uniformLocation("projMatrix")) < 0)
|
||||
qDebug() << "Unable to find shader location for " << "projMatrix";
|
||||
}
|
||||
|
||||
// Creates the basic shapes in memory. We only have 3, so we just prep them all in advance.
|
||||
void Viewer::initGeometries()
|
||||
{
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable( GL_BLEND ); glClearColor(0.0,0.0,0.0,0.0);
|
||||
|
||||
// Create our VertexArrays Objects and VertexBuffer Objects
|
||||
glGenVertexArrays(NumVAOs, m_VAOs);
|
||||
glGenBuffers(NumBuffers, m_Buffers);
|
||||
|
||||
// Create our pentagone object, store its vertices on the graphic card, and
|
||||
// bind the data to the vPosition attribute of the shader
|
||||
GLfloat verticesSquare[numVerticesSquare][3] = {
|
||||
{ -0.5, 0.5, 0 },
|
||||
{ -0.5, -0.5, 0 },
|
||||
{ 0.5, -0.5, 0 },
|
||||
{ 0.5, 0.5, 0 },
|
||||
{ -0.5, 0.5, 0 }
|
||||
};
|
||||
|
||||
glBindVertexArray(m_VAOs[VAO_Square]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[VBO_Square]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesSquare), verticesSquare, GL_STATIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(m_vPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
|
||||
glEnableVertexAttribArray(m_vPositionLocation);
|
||||
|
||||
// Create our triangle object, store its vertices on the graphic card, and
|
||||
// bind the data to the vPosition attribute of the shader
|
||||
GLfloat verticesTriangle[numVerticesTriangle][3] = {
|
||||
{ -0.5, -0.5, 0.0 },
|
||||
{ 0.5, -0.5, 0.0 },
|
||||
{ 0.0, 0.5, 0.0 }
|
||||
};
|
||||
|
||||
glBindVertexArray(m_VAOs[VAO_Triangle]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[VBO_Triangle]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesTriangle), verticesTriangle, GL_STATIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(m_vPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
|
||||
glEnableVertexAttribArray(m_vPositionLocation);
|
||||
|
||||
// Create our Circle Object, and store its vertices with its bindings
|
||||
|
||||
GLfloat verticesCircle[numVerticesCircle][3];
|
||||
|
||||
const float PI = 3.1415926f;
|
||||
double increment = 2.0f * PI / (numVerticesCircle - 2);
|
||||
|
||||
verticesCircle[0][0] = 0;
|
||||
verticesCircle[0][1] = 0;
|
||||
verticesCircle[0][2] = 0;
|
||||
|
||||
for(int i = 1; i < numVerticesCircle; i++) {
|
||||
double angle = increment * (i);
|
||||
|
||||
verticesCircle[i][0] = 0.5 * cos(angle);
|
||||
verticesCircle[i][1] = 0.5 * sin(angle);
|
||||
verticesCircle[i][2] = 0;
|
||||
}
|
||||
|
||||
glBindVertexArray(m_VAOs[VAO_Circle]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[VBO_Circle]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesCircle), verticesCircle, GL_STATIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(m_vPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
|
||||
glEnableVertexAttribArray(m_vPositionLocation);
|
||||
}
|
||||
|
||||
void Viewer::initGrid()
|
||||
{
|
||||
// Prepare construction loop. Also contains necessary translations
|
||||
int i,j, limit;
|
||||
limit = GRID_SIZE;
|
||||
|
||||
for(i = 0; i < limit ; i++) {
|
||||
SceneGroup *row = new SceneGroup();
|
||||
|
||||
row->transform.translate(0, inc_offset * i);
|
||||
|
||||
|
||||
for(j = 0; j < limit ; j++) {
|
||||
SceneGroup *cell = new SceneGroup();
|
||||
|
||||
cell->transform.translate(inc_offset * j, 0);
|
||||
|
||||
row->addChild(cell);
|
||||
}
|
||||
|
||||
root.addChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
void Viewer::visit(Square &s)
|
||||
{
|
||||
QMatrix4x4 modelViewMatrix = modelStack.top() * QMatrix4x4(s.transform);
|
||||
|
||||
glBindVertexArray(m_VAOs[VAO_Square]);
|
||||
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
|
||||
m_program->setUniformValue(m_colorLocation, s.color);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, numVerticesSquare);
|
||||
}
|
||||
void Viewer::visit(Circle &s)
|
||||
{
|
||||
QMatrix4x4 modelViewMatrix = modelStack.top() * QMatrix4x4(s.transform);
|
||||
|
||||
glBindVertexArray(m_VAOs[VAO_Circle]);
|
||||
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
|
||||
m_program->setUniformValue(m_colorLocation, s.color);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, numVerticesCircle);
|
||||
}
|
||||
void Viewer::visit(Triangle &s)
|
||||
{
|
||||
QMatrix4x4 modelViewMatrix = modelStack.top() * QMatrix4x4(s.transform);
|
||||
|
||||
glBindVertexArray(m_VAOs[VAO_Triangle]);
|
||||
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
|
||||
m_program->setUniformValue(m_colorLocation, s.color);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, numVerticesTriangle);
|
||||
}
|
||||
|
||||
void Viewer::visit(SceneGroup &s)
|
||||
{
|
||||
// Build compound transformation matrix
|
||||
QMatrix4x4 currentMatrix = modelStack.top() * QMatrix4x4(s.transform);
|
||||
modelStack.push(currentMatrix);
|
||||
|
||||
while(s.hasNext()) {
|
||||
// Get next leaf
|
||||
GlNode* current = s.getChild();
|
||||
|
||||
// Draw/Traverse child
|
||||
current->accept(*this);
|
||||
}
|
||||
|
||||
// Return model matrix to previous state
|
||||
modelStack.pop();
|
||||
}
|
||||
|
||||
void Viewer::changeColor(QColor c){
|
||||
if(activeCell->getChildren()->size()){
|
||||
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
|
||||
shape->setColor(c);
|
||||
}
|
||||
activeColor = new QColor(c);
|
||||
this->update();
|
||||
}
|
||||
|
||||
void Viewer::changeShape(int s){
|
||||
std::cout << "Chaging active shape from " << activeShape << " to " << s << endl;
|
||||
|
||||
if(activeCell != nullptr && activeCell->getChildren()->size()){
|
||||
std::cout << "Chaging cell-bound shape... " << endl;
|
||||
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
|
||||
QColor c = shape->getColor();
|
||||
|
||||
// WARNING: CODE DEGEULASSE
|
||||
|
||||
Shape* newShape = nullptr;
|
||||
if(s == 1){
|
||||
newShape = new Triangle();
|
||||
}else if(s == 2){
|
||||
newShape = new Square();
|
||||
}else if(s == 3){
|
||||
newShape = new Circle();
|
||||
}
|
||||
|
||||
if(newShape != nullptr){
|
||||
newShape->setColor(c);
|
||||
activeCell->getChildren()->at(0) = newShape;
|
||||
}else{
|
||||
activeCell->getChildren()->clear();
|
||||
}
|
||||
|
||||
// WARNING: END OF CODE DEGEULASSE
|
||||
|
||||
this->update();
|
||||
}else{
|
||||
std::cout << "Cell has no children, ignoring change." << endl;
|
||||
}
|
||||
|
||||
activeShape = s;
|
||||
};
|
||||
95
src/viewer/simpleViewer.h
Normal file
95
src/viewer/simpleViewer.h
Normal file
@ -0,0 +1,95 @@
|
||||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.3.6.
|
||||
|
||||
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 SIMPLEVIEWER_H
|
||||
#define SIMPLEVIEWER_H
|
||||
|
||||
#include <QOpenGLFunctions_4_0_Core>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QMatrix4x4>
|
||||
#include <QGLViewer/qglviewer.h>
|
||||
#include "../interfaces/visitor.h"
|
||||
#include "../glnodes/glnode.h"
|
||||
#include "../glnodes/scenegroup.h"
|
||||
#include "../glnodes/shapes.h"
|
||||
#include <stack>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
|
||||
|
||||
class Viewer : public QGLViewer, protected QOpenGLFunctions_4_0_Core, public Visitor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Viewer();
|
||||
~Viewer();
|
||||
|
||||
virtual void visit(SceneGroup &s);
|
||||
virtual void visit(Square &s);
|
||||
virtual void visit(Circle &s);
|
||||
virtual void visit(Triangle &s);
|
||||
|
||||
public slots:
|
||||
void cleanup();
|
||||
void changeColor(QColor);
|
||||
void changeShape(int);
|
||||
|
||||
signals:
|
||||
int shapeSelected(int);
|
||||
|
||||
protected :
|
||||
virtual void draw();
|
||||
virtual void init();
|
||||
|
||||
virtual void mouseMoveEvent(QMouseEvent* e);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* e);
|
||||
virtual void mousePressEvent(QMouseEvent *e);
|
||||
|
||||
SceneGroup root;
|
||||
std::stack<QMatrix4x4> modelStack;
|
||||
|
||||
private:
|
||||
void initShaders();
|
||||
void initGeometries();
|
||||
void initGrid();
|
||||
void deselect();
|
||||
|
||||
QOpenGLShaderProgram *m_program;
|
||||
int m_vPositionLocation;
|
||||
int m_colorLocation;
|
||||
int m_projMatrixLocation;
|
||||
int m_mvMatrixLocation;
|
||||
|
||||
SceneGroup* activeCell;
|
||||
QColor* activeColor;
|
||||
int activeShape;
|
||||
|
||||
enum VAO_IDs { VAO_Square, VAO_Triangle, VAO_Circle, NumVAOs };
|
||||
enum Buffer_IDs { VBO_Square, VBO_Triangle, VBO_Circle, NumBuffers };
|
||||
|
||||
GLuint m_VAOs[NumVAOs];
|
||||
GLuint m_Buffers[NumBuffers];
|
||||
|
||||
Shape* generateShapeFromIndex(int);
|
||||
};
|
||||
|
||||
#endif // SIMPLEVIEWER_H
|
||||
35
src/window/mainwindow.cpp
Normal file
35
src/window/mainwindow.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include <QBoxLayout>
|
||||
#include <QColorDialog>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow),
|
||||
colordialog(new QColorDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->pushButton, SIGNAL(clicked(bool)), this, SLOT(onColorPickerActivate()));
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::addViewer(Viewer* viewer)
|
||||
{
|
||||
QLayout *layout = new QHBoxLayout;
|
||||
layout->addWidget(viewer);
|
||||
ui->frame->setLayout(layout);
|
||||
|
||||
connect(colordialog, SIGNAL(colorSelected(QColor)), viewer, SLOT(changeColor(QColor)));
|
||||
connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), viewer, SLOT(changeShape(int)));
|
||||
connect(viewer, SIGNAL(shapeSelected(int)), ui->comboBox, SLOT(setCurrentIndex(int)));
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::onColorPickerActivate(){
|
||||
colordialog->open();
|
||||
}
|
||||
30
src/window/mainwindow.h
Normal file
30
src/window/mainwindow.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QColorDialog>
|
||||
#include "src/viewer/simpleViewer.h"
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
void addViewer(Viewer* viewer);
|
||||
|
||||
public slots:
|
||||
void onColorPickerActivate();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
QColorDialog *colordialog;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
Loading…
x
Reference in New Issue
Block a user