Lab 1 End Version

This commit is contained in:
Riku Avelar 2016-10-28 16:36:45 -04:00
parent fda113767d
commit 0977688583
47 changed files with 16849 additions and 0 deletions

43
.gitignore vendored Normal file
View 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
View 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>

View 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

File diff suppressed because it is too large Load Diff

505
QGLViewer/camera.h Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

415
QGLViewer/frame.h Normal file
View 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

View 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

View 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

View 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;
}

View 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

View 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

View 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

View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

1278
QGLViewer/qglviewer.h Normal file

File diff suppressed because it is too large Load Diff

BIN
QGLViewer/qglviewer.icns Normal file

Binary file not shown.

BIN
QGLViewer/qglviewer_fr.qm Normal file

Binary file not shown.

608
QGLViewer/qglviewer_fr.ts Normal file
View 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&apos;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&apos;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&apos;image (en pixels)</translation>
</message>
<message>
<source>Image quality</source>
<translation>Qualité d&apos;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&apos;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&apos;s one, expand frustum as needed. Fits inside current frustum otherwise.</source>
<translation>Lorsque le rapport de dimensions de l&apos;image diffère de celui de la fenêtre, étendre la pyramide de vue (frustum) en conséquence. L&apos;image est ajustée à l&apos;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&apos;affichage de la fréquence d&apos;affiichage</translation>
</message>
<message>
<source>Saves a screenshot</source>
<comment>SAVE_SCREENSHOT action description</comment>
<translation>Sauvegarde une capture d&apos;é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&apos;aide</translation>
</message>
<message>
<source>Starts/stops the animation</source>
<comment>ANIMATION action description</comment>
<translation>Démarre/arrête l&apos;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&apos;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&apos;é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 &lt;i&gt;Fx&lt;/i&gt; 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 &lt;i&gt;Fx&lt;/i&gt; 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>&amp;Help</source>
<comment>Help window tab title</comment>
<translation>&amp;Aide</translation>
</message>
<message>
<source>&amp;Keyboard</source>
<comment>Help window tab title</comment>
<translation>&amp;Clavier</translation>
</message>
<message>
<source>&amp;Mouse</source>
<comment>Help window tab title</comment>
<translation>&amp;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&apos;é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&apos;état</translation>
</message>
<message>
<source>File %1 is not readable.</source>
<translation>Le fichier %1 n&apos;est pas lisible.</translation>
</message>
<message>
<source>Open file error</source>
<comment>Message box window title</comment>
<translation>Erreur d&apos;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&apos;aide disponible.</translation>
</message>
<message>
<source>Exporter error</source>
<comment>Message box window title</comment>
<translation>Erreur d&apos;export</translation>
</message>
<message>
<source>Unable to open file %1.</source>
<translation>Impossible d&apos;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>&amp;About</source>
<comment>Help window about title</comment>
<translation>À &amp;propos</translation>
</message>
<message>
<source>&lt;h1&gt;libQGLViewer&lt;/h1&gt;&lt;h3&gt;Version %1&lt;/h3&gt;&lt;br&gt;A versatile 3D viewer based on OpenGL and Qt&lt;br&gt;Copyright 2002-%2 Gilles Debunne&lt;br&gt;&lt;code&gt;%3&lt;/code&gt;</source>
<translation>&lt;h1&gt;libQGLViewer&lt;/h1&gt;&lt;h3&gt;Version %1&lt;/h3&gt;&lt;br&gt;Un afficheur 3D généraliste basé sur OpenGL et Qt&lt;br&gt;Copyright 2002-%2 Gilles Debunne&lt;br&gt;&lt;code&gt;%3&lt;/code&gt;</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&apos;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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>&amp;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>&amp;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
View 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
View 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

View 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
View 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
View 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
View 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

View 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
View 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
View 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();
}

View File

@ -0,0 +1,9 @@
#version 400 core
in vec4 ifColor;
out vec4 fColor;
void
main()
{
fColor = ifColor;
}

View 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
View 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
View 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
View 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
View 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