mirror of https://github.com/CGAL/cgal
WIP Header_only
This commit is contained in:
parent
cbc0ce1130
commit
0c74a68952
|
|
@ -49,7 +49,9 @@ if(CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND)
|
|||
"${CMAKE_CURRENT_BINARY_DIR}/Viewer_moc.cpp"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/Scene_moc.cpp" )
|
||||
|
||||
add_executable ( AABB_demo AABB_demo.cpp ${UI_FILES} ${CGAL_Qt5_RESOURCE_FILES} ${CGAL_Qt5_MOC_FILES})
|
||||
add_executable ( AABB_demo AABB_demo.cpp ${UI_FILES} ${CGAL_Qt5_RESOURCE_FILES}
|
||||
#${CGAL_Qt5_MOC_FILES}
|
||||
)
|
||||
# Link with Qt libraries
|
||||
target_link_libraries( AABB_demo PRIVATE
|
||||
Qt5::OpenGL Qt5::Gui Qt5::Xml
|
||||
|
|
|
|||
|
|
@ -411,11 +411,7 @@ void MainWindow::on_actionRefine_loop_triggered()
|
|||
|
||||
void MainWindow::on_actionSave_snapshot_triggered()
|
||||
{
|
||||
// save snapshot to file
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
QString filename = QFileDialog::getSaveFileName(this,tr("Save snapshot to file..."),"snapshot00.png","*.png");
|
||||
m_pViewer->saveSnapshot(filename);
|
||||
QApplication::restoreOverrideCursor();
|
||||
return;
|
||||
}
|
||||
void MainWindow::on_actionCopy_snapshot_triggered()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class Refiner
|
|||
typedef typename Polyhedron::Edge_iterator Edge_iterator;
|
||||
typedef std::priority_queue<Edge,
|
||||
std::vector<Edge>,
|
||||
less<Edge> > PQueue;
|
||||
::less<Edge> > PQueue;
|
||||
// data
|
||||
PQueue m_queue;
|
||||
Polyhedron* m_pMesh;
|
||||
|
|
|
|||
|
|
@ -1,360 +1 @@
|
|||
// Copyright (c) 2008 GeometryFactory Sarl (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
// You can redistribute it and/or modify it under the terms of the GNU
|
||||
// General Public License as published by the Free Software Foundation,
|
||||
// either version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// Licensees holding a valid commercial license may use this file in
|
||||
// accordance with the commercial license agreement provided with the software.
|
||||
//
|
||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
//
|
||||
//
|
||||
// Author(s) : Andreas Fabri <Andreas.Fabri@geometryfactory.com>
|
||||
// Laurent Rineau <Laurent.Rineau@geometryfactory.com>
|
||||
|
||||
#ifndef CGAL_QT_ALPHA_SHAPE_GRAPHICS_ITEM_H
|
||||
#define CGAL_QT_ALPHA_SHAPE_GRAPHICS_ITEM_H
|
||||
|
||||
#include <CGAL/license/GraphicsView.h>
|
||||
|
||||
|
||||
#include <CGAL/Bbox_2.h>
|
||||
#include <CGAL/apply_to_range.h>
|
||||
#include <CGAL/Qt/PainterOstream.h>
|
||||
#include <CGAL/Qt/GraphicsItem.h>
|
||||
#include <CGAL/Qt/Converter.h>
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Qt {
|
||||
|
||||
template <typename T>
|
||||
class AlphaShapeGraphicsItem : public GraphicsItem
|
||||
{
|
||||
typedef typename T::Geom_traits Geom_traits;
|
||||
public:
|
||||
AlphaShapeGraphicsItem(T* t_);
|
||||
|
||||
void modelChanged();
|
||||
|
||||
public:
|
||||
|
||||
QRectF boundingRect() const;
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||
|
||||
virtual void operator()(typename T::Face_handle fh);
|
||||
|
||||
const QPen& verticesPen() const
|
||||
{
|
||||
return vertices_pen;
|
||||
}
|
||||
|
||||
const QPen& edgesPen() const
|
||||
{
|
||||
return edges_pen;
|
||||
}
|
||||
|
||||
const QPen& regularEdgesPen() const
|
||||
{
|
||||
return regular_edges_pen;
|
||||
}
|
||||
|
||||
const QPen& singularEdgesPen() const
|
||||
{
|
||||
return singular_edges_pen;
|
||||
}
|
||||
|
||||
const QBrush& regularFacesBrush() const
|
||||
{
|
||||
return regular_faces_brush;
|
||||
}
|
||||
|
||||
void setVerticesPen(const QPen& pen)
|
||||
{
|
||||
vertices_pen = pen;
|
||||
}
|
||||
|
||||
void setEdgesPen(const QPen& pen)
|
||||
{
|
||||
edges_pen = pen;
|
||||
}
|
||||
|
||||
void setRegularEdgesPen(const QPen& pen)
|
||||
{
|
||||
regular_edges_pen = pen;
|
||||
}
|
||||
|
||||
void setSingularEdgesPen(const QPen& pen)
|
||||
{
|
||||
singular_edges_pen = pen;
|
||||
}
|
||||
|
||||
void setRegularFacesBrush(const QBrush& b)
|
||||
{
|
||||
regular_faces_brush = b;
|
||||
}
|
||||
|
||||
bool visibleVertices() const
|
||||
{
|
||||
return visible_vertices;
|
||||
}
|
||||
|
||||
void setVisibleVertices(const bool b)
|
||||
{
|
||||
visible_vertices = b;
|
||||
update();
|
||||
}
|
||||
|
||||
bool visibleEdges() const
|
||||
{
|
||||
return visible_edges;
|
||||
}
|
||||
|
||||
void setVisibleEdges(const bool b)
|
||||
{
|
||||
visible_edges = b;
|
||||
update();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void drawAll(QPainter *painter);
|
||||
void paintVertices(QPainter *painter);
|
||||
void paintOneVertex(const typename T::Point& point);
|
||||
virtual void paintVertex(typename T::Vertex_handle vh);
|
||||
void updateBoundingBox();
|
||||
|
||||
T * t;
|
||||
QPainter* m_painter;
|
||||
PainterOstream<Geom_traits> painterostream;
|
||||
|
||||
typename T::Vertex_handle vh;
|
||||
typename T::Point p;
|
||||
CGAL::Bbox_2 bb;
|
||||
bool bb_initialized;
|
||||
QRectF bounding_rect;
|
||||
|
||||
QPen vertices_pen;
|
||||
QPen edges_pen;
|
||||
QPen regular_edges_pen;
|
||||
QPen singular_edges_pen;
|
||||
QBrush regular_faces_brush;
|
||||
bool visible_edges;
|
||||
bool visible_vertices;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
AlphaShapeGraphicsItem<T>::AlphaShapeGraphicsItem(T * t_)
|
||||
: t(t_), painterostream(0),
|
||||
bb(0,0,0,0), bb_initialized(false),
|
||||
visible_edges(true), visible_vertices(true)
|
||||
{
|
||||
setVerticesPen(QPen(::Qt::red, 3.));
|
||||
if(t->number_of_vertices() == 0){
|
||||
this->hide();
|
||||
}
|
||||
updateBoundingBox();
|
||||
setZValue(3);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QRectF
|
||||
AlphaShapeGraphicsItem<T>::boundingRect() const
|
||||
{
|
||||
return bounding_rect;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
AlphaShapeGraphicsItem<T>::operator()(typename T::Face_handle fh)
|
||||
{
|
||||
if(visible_edges) {
|
||||
for (int i=0; i<3; i++) {
|
||||
if (fh < fh->neighbor(i) || t->is_infinite(fh->neighbor(i))){
|
||||
m_painter->setPen(this->edgesPen());
|
||||
painterostream << t->segment(fh,i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(visible_vertices) {
|
||||
for (int i=0; i<3; i++) {
|
||||
paintVertex(fh->vertex(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
AlphaShapeGraphicsItem<T>::drawAll(QPainter *painter)
|
||||
{
|
||||
painterostream = PainterOstream<Geom_traits>(painter);
|
||||
|
||||
painter->setBrush(regularFacesBrush());
|
||||
typedef typename T::Gt::Triangle_2 Triangle_2;
|
||||
for(typename T::Finite_faces_iterator fit = t->finite_faces_begin();
|
||||
fit != t->finite_faces_end();
|
||||
++fit){
|
||||
|
||||
if(t->classify(fit) == T::INTERIOR){
|
||||
Triangle_2 triangle = t->triangle(fit);
|
||||
painterostream << triangle;
|
||||
}
|
||||
}
|
||||
|
||||
if(visibleEdges()) {
|
||||
for(typename T::Finite_edges_iterator eit = t->finite_edges_begin();
|
||||
eit != t->finite_edges_end();
|
||||
++eit){
|
||||
switch (t->classify(*eit)) {
|
||||
case T::REGULAR :
|
||||
painter->setPen(regularEdgesPen());
|
||||
break;
|
||||
case T::SINGULAR :
|
||||
painter->setPen(singularEdgesPen());
|
||||
break;
|
||||
default:
|
||||
painter->setPen(edgesPen());
|
||||
}
|
||||
painterostream << t->segment(*eit);
|
||||
}
|
||||
}
|
||||
paintVertices(painter);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
AlphaShapeGraphicsItem<T>::paintVertices(QPainter *painter)
|
||||
{
|
||||
if(visibleVertices()) {
|
||||
Converter<Geom_traits> convert;
|
||||
|
||||
painter->setPen(verticesPen());
|
||||
QMatrix matrix = painter->matrix();
|
||||
painter->resetMatrix();
|
||||
for(typename T::Finite_vertices_iterator it = t->finite_vertices_begin();
|
||||
it != t->finite_vertices_end();
|
||||
it++){
|
||||
QPointF point = matrix.map(convert(it->point()));
|
||||
painter->drawPoint(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
AlphaShapeGraphicsItem<T>::paintOneVertex(const typename T::Point& point)
|
||||
{
|
||||
Converter<Geom_traits> convert;
|
||||
|
||||
m_painter->setPen(this->verticesPen());
|
||||
QMatrix matrix = m_painter->matrix();
|
||||
m_painter->resetMatrix();
|
||||
m_painter->drawPoint(matrix.map(convert(point)));
|
||||
m_painter->setMatrix(matrix);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
AlphaShapeGraphicsItem<T>::paintVertex(typename T::Vertex_handle vh)
|
||||
{
|
||||
Converter<Geom_traits> convert;
|
||||
|
||||
m_painter->setPen(this->verticesPen());
|
||||
QMatrix matrix = m_painter->matrix();
|
||||
m_painter->resetMatrix();
|
||||
m_painter->drawPoint(matrix.map(convert(vh->point())));
|
||||
m_painter->setMatrix(matrix);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
AlphaShapeGraphicsItem<T>::paint(QPainter *painter,
|
||||
const QStyleOptionGraphicsItem * /*option*/,
|
||||
QWidget * /*widget*/)
|
||||
{
|
||||
painter->setPen(this->edgesPen());
|
||||
// painter->drawRect(boundingRect());
|
||||
// if ( t->dimension()<2 || option->exposedRect.contains(boundingRect()) ) {
|
||||
drawAll(painter);
|
||||
/*
|
||||
} else {
|
||||
m_painter = painter;
|
||||
painterostream = PainterOstream<Geom_traits>(painter);
|
||||
CGAL::apply_to_range (*t,
|
||||
typename T::Point(option->exposedRect.left(),
|
||||
option->exposedRect.bottom()),
|
||||
typename T::Point(option->exposedRect.right(),
|
||||
option->exposedRect.top()),
|
||||
*this);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// We let the bounding box only grow, so that when vertices get removed
|
||||
// the maximal bbox gets refreshed in the GraphicsView
|
||||
template <typename T>
|
||||
void
|
||||
AlphaShapeGraphicsItem<T>::updateBoundingBox()
|
||||
{
|
||||
prepareGeometryChange();
|
||||
if(t->number_of_vertices() == 0){
|
||||
bb = Bbox_2(0,0,0,0);
|
||||
bb_initialized = false;
|
||||
return;
|
||||
} else if(! bb_initialized){
|
||||
bb = t->finite_vertices_begin()->point().bbox();
|
||||
bb_initialized = true;
|
||||
}
|
||||
|
||||
if(t->dimension() <2){
|
||||
for(typename T::Finite_vertices_iterator it = t->finite_vertices_begin();
|
||||
it != t->finite_vertices_end();
|
||||
++it){
|
||||
bb = bb + it->point().bbox();
|
||||
}
|
||||
} else {
|
||||
typename T::Vertex_handle inf = t->infinite_vertex();
|
||||
typename T::Vertex_circulator vc = t->incident_vertices(inf), done(vc);
|
||||
do {
|
||||
bb = bb + vc->point().bbox();
|
||||
++vc;
|
||||
} while(vc != done);
|
||||
}
|
||||
bounding_rect = QRectF(bb.xmin(),
|
||||
bb.ymin(),
|
||||
bb.xmax()-bb.xmin(),
|
||||
bb.ymax()-bb.ymin());
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
AlphaShapeGraphicsItem<T>::modelChanged()
|
||||
{
|
||||
if((t->number_of_vertices() == 0) ){
|
||||
this->hide();
|
||||
} else if((t->number_of_vertices() > 0) && (! this->isVisible())){
|
||||
this->show();
|
||||
}
|
||||
updateBoundingBox();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
} // namespace Qt
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_QT_ALPHA_SHAPE_GRAPHICS_ITEM_H
|
||||
|
|
|
|||
|
|
@ -22,15 +22,21 @@
|
|||
|
||||
#ifndef QGLVIEWER_CAMERA_H
|
||||
#define QGLVIEWER_CAMERA_H
|
||||
|
||||
#include <QMap>
|
||||
#include <CGAL/Qt/keyFrameInterpolator.h>
|
||||
#include <QDomElement>
|
||||
#include <CGAL/Qt/vec.h>
|
||||
#include <CGAL/Qt/quaternion.h>
|
||||
#include <CGAL/Qt/config.h>
|
||||
#include <QOpenGLFunctions_2_1>
|
||||
class QGLViewer;
|
||||
|
||||
namespace qglviewer {
|
||||
|
||||
class KeyFrameInterpolator;
|
||||
class Frame;
|
||||
class ManipulatedCameraFrame;
|
||||
|
||||
|
||||
/*! \brief A perspective or orthographic camera.
|
||||
\class Camera camera.h QGLViewer/camera.h
|
||||
|
||||
|
|
@ -98,7 +104,7 @@ class QGLVIEWER_EXPORT Camera : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Camera();
|
||||
Camera(QObject *parent);
|
||||
virtual ~Camera();
|
||||
|
||||
Camera(const Camera &camera);
|
||||
|
|
@ -182,9 +188,7 @@ public:
|
|||
Value is set using setHorizontalFieldOfView() or setFieldOfView(). These
|
||||
values are always linked by: \code horizontalFieldOfView() = 2.0 * atan (
|
||||
tan(fieldOfView()/2.0) * aspectRatio() ). \endcode */
|
||||
qreal horizontalFieldOfView() const {
|
||||
return 2.0 * atan(tan(fieldOfView() / 2.0) * aspectRatio());
|
||||
}
|
||||
qreal horizontalFieldOfView() const;
|
||||
|
||||
/*! Returns the Camera aspect ratio defined by screenWidth() / screenHeight().
|
||||
|
||||
|
|
@ -265,9 +269,7 @@ public Q_SLOTS:
|
|||
This method actually calls setFieldOfView(( 2.0 * atan (tan(hfov / 2.0) /
|
||||
aspectRatio()) )) so that a call to horizontalFieldOfView() returns the
|
||||
expected value. */
|
||||
void setHorizontalFieldOfView(qreal hfov) {
|
||||
setFieldOfView(2.0 * atan(tan(hfov / 2.0) / aspectRatio()));
|
||||
}
|
||||
void setHorizontalFieldOfView(qreal hfov);
|
||||
|
||||
void setFOVToFitScene();
|
||||
|
||||
|
|
@ -384,7 +386,6 @@ public Q_SLOTS:
|
|||
virtual void playPath(unsigned int i);
|
||||
virtual void deletePath(unsigned int i);
|
||||
virtual void resetPath(unsigned int i);
|
||||
virtual void drawAllPaths();
|
||||
//@}
|
||||
|
||||
/*! @name OpenGL matrices */
|
||||
|
|
@ -408,15 +409,6 @@ public:
|
|||
void getModelViewProjectionMatrix(GLdouble m[16]) const;
|
||||
//@}
|
||||
|
||||
/*! @name Drawing */
|
||||
//@{
|
||||
#ifndef DOXYGEN
|
||||
static void drawCamera(qreal scale = 1.0, qreal aspectRatio = 1.33,
|
||||
qreal fieldOfView = qreal(M_PI) / 4.0);
|
||||
#endif
|
||||
virtual void draw(bool drawFarPlane = true, qreal scale = 1.0) const;
|
||||
//@}
|
||||
|
||||
/*! @name World to Camera coordinate systems conversions */
|
||||
//@{
|
||||
public:
|
||||
|
|
@ -466,9 +458,7 @@ public:
|
|||
|
||||
This is a helper function. It simply returns physicalScreenWidth() / 2.0 /
|
||||
tan(horizontalFieldOfView() / 2.0); */
|
||||
qreal physicalDistanceToScreen() const {
|
||||
return physicalScreenWidth() / 2.0 / tan(horizontalFieldOfView() / 2.0);
|
||||
}
|
||||
qreal physicalDistanceToScreen() const;
|
||||
|
||||
/*! Returns the physical screen width, in meters. Default value is 0.5m
|
||||
(average monitor width).
|
||||
|
|
@ -525,6 +515,7 @@ private Q_SLOTS:
|
|||
void onFrameModified();
|
||||
|
||||
private:
|
||||
QOpenGLFunctions_2_1* gl() const{ return dynamic_cast<QOpenGLFunctions_2_1*>(parent()); }
|
||||
// F r a m e
|
||||
ManipulatedCameraFrame *frame_;
|
||||
|
||||
|
|
@ -553,5 +544,4 @@ private:
|
|||
};
|
||||
|
||||
} // namespace qglviewer
|
||||
|
||||
#endif // QGLVIEWER_CAMERA_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -48,7 +48,8 @@ Error : libQGLViewer requires a minimum Qt version of 4.0
|
|||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#ifndef QGLVIEWER_STATIC
|
||||
#ifdef CREATE_QGLVIEWER_DLL
|
||||
#if ( defined(CGAL_BUILD_SHARED_LIBS) && ( ! defined(CGAL_HEADER_ONLY) ) ) \
|
||||
|| defined(CGAL_USE_Qt5_RESOURCES)
|
||||
#if QT_VERSION >= 0x040500
|
||||
#define QGLVIEWER_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
|
|
@ -78,13 +79,6 @@ Error : libQGLViewer requires a minimum Qt version of 4.0
|
|||
// OpenGL headers.
|
||||
#include <QOpenGLWidget>
|
||||
|
||||
// GLU was removed from Qt in version 4.8
|
||||
#ifdef Q_OS_MAC
|
||||
#include <OpenGL/glu.h>
|
||||
#else
|
||||
#include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
// Container classes interfaces changed a lot in Qt.
|
||||
// Compatibility patches are all grouped here.
|
||||
#include <QList>
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@
|
|||
#include <CGAL/Qt/quaternion.h>
|
||||
#include <CGAL/Qt/vec.h>
|
||||
|
||||
|
||||
namespace qglviewer {
|
||||
class Frame;
|
||||
class Camera;
|
||||
|
||||
/*! \brief An interface class for Frame constraints.
|
||||
\class Constraint constraint.h QGLViewer/constraint.h
|
||||
|
||||
|
|
@ -349,14 +349,14 @@ class QGLVIEWER_EXPORT CameraConstraint : public AxisPlaneConstraint {
|
|||
public:
|
||||
explicit CameraConstraint(const Camera *const camera);
|
||||
/*! Virtual destructor. Empty. */
|
||||
virtual ~CameraConstraint(){};
|
||||
virtual ~CameraConstraint(){}
|
||||
|
||||
virtual void constrainTranslation(Vec &translation, Frame *const frame);
|
||||
virtual void constrainRotation(Quaternion &rotation, Frame *const frame);
|
||||
|
||||
/*! Returns the associated Camera. Set using the CameraConstraint constructor.
|
||||
*/
|
||||
const Camera *camera() const { return camera_; };
|
||||
const Camera *camera() const { return camera_; }
|
||||
|
||||
private:
|
||||
const Camera *const camera_;
|
||||
|
|
@ -364,4 +364,7 @@ private:
|
|||
|
||||
} // namespace qglviewer
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
//#include <CGAL/Qt/qglviewer_impl_list.h>
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
#endif // QGLVIEWER_CONSTRAINT_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,301 @@
|
|||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.7.0.
|
||||
|
||||
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||
|
||||
This file may be used under the terms of the GNU General Public License
|
||||
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||
appearing in the LICENSE file included in the packaging of this file.
|
||||
In addition, as a special exception, Gilles Debunne gives you certain
|
||||
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||
|
||||
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||
purchase a libQGLViewer Commercial License.
|
||||
|
||||
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
#define CGAL_INLINE_FUNCTION inline
|
||||
|
||||
#include <CGAL/license/GraphicsView.h>
|
||||
|
||||
#else
|
||||
#define CGAL_INLINE_FUNCTION
|
||||
#endif
|
||||
|
||||
#include <CGAL/Qt/constraint.h>
|
||||
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
||||
#include <CGAL/Qt/camera.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). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
AxisPlaneConstraint::AxisPlaneConstraint()
|
||||
: translationConstraintType_(FREE), rotationConstraintType_(FREE) {
|
||||
// Do not use set since setRotationConstraintType needs a read.
|
||||
}
|
||||
|
||||
/*! Simply calls setTranslationConstraintType() and
|
||||
* setTranslationConstraintDirection(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void AxisPlaneConstraint::setTranslationConstraint(Type type,
|
||||
const Vec &direction) {
|
||||
setTranslationConstraintType(type);
|
||||
setTranslationConstraintDirection(direction);
|
||||
}
|
||||
|
||||
/*! Defines the translationConstraintDirection(). The coordinate system where \p
|
||||
* direction is expressed depends on your class implementation. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void AxisPlaneConstraint::setTranslationConstraintDirection(
|
||||
const Vec &direction) {
|
||||
if ((translationConstraintType() != AxisPlaneConstraint::FREE) &&
|
||||
(translationConstraintType() != AxisPlaneConstraint::FORBIDDEN)) {
|
||||
const qreal norm = direction.norm();
|
||||
if (norm < 1E-8) {
|
||||
qWarning("AxisPlaneConstraint::setTranslationConstraintDir: null vector "
|
||||
"for translation constraint");
|
||||
translationConstraintType_ = AxisPlaneConstraint::FREE;
|
||||
} else
|
||||
translationConstraintDir_ = direction / norm;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Simply calls setRotationConstraintType() and
|
||||
* setRotationConstraintDirection(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void AxisPlaneConstraint::setRotationConstraint(Type type,
|
||||
const Vec &direction) {
|
||||
setRotationConstraintType(type);
|
||||
setRotationConstraintDirection(direction);
|
||||
}
|
||||
|
||||
/*! Defines the rotationConstraintDirection(). The coordinate system where \p
|
||||
* direction is expressed depends on your class implementation. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void AxisPlaneConstraint::setRotationConstraintDirection(const Vec &direction) {
|
||||
if ((rotationConstraintType() != AxisPlaneConstraint::FREE) &&
|
||||
(rotationConstraintType() != AxisPlaneConstraint::FORBIDDEN)) {
|
||||
const qreal norm = direction.norm();
|
||||
if (norm < 1E-8) {
|
||||
qWarning("AxisPlaneConstraint::setRotationConstraintDir: null vector for "
|
||||
"rotation constraint");
|
||||
rotationConstraintType_ = AxisPlaneConstraint::FREE;
|
||||
} else
|
||||
rotationConstraintDir_ = direction / norm;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Set the Type() of the rotationConstraintType(). Default is
|
||||
AxisPlaneConstraint::FREE.
|
||||
|
||||
Depending on this value, the Frame will freely rotate
|
||||
(AxisPlaneConstraint::FREE), will only be able to rotate around an axis
|
||||
(AxisPlaneConstraint::AXIS), or will not able to rotate at all
|
||||
(AxisPlaneConstraint::FORBIDDEN).
|
||||
|
||||
Use Frame::setOrientation() to define the orientation of the constrained Frame
|
||||
before it gets constrained.
|
||||
|
||||
\attention An AxisPlaneConstraint::PLANE Type() is not meaningful for
|
||||
rotational constraints and will be ignored. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void AxisPlaneConstraint::setRotationConstraintType(Type type) {
|
||||
if (rotationConstraintType() == AxisPlaneConstraint::PLANE) {
|
||||
qWarning("AxisPlaneConstraint::setRotationConstraintType: the PLANE type "
|
||||
"cannot be used for a rotation constraints");
|
||||
return;
|
||||
}
|
||||
|
||||
rotationConstraintType_ = type;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// LocalConstraint //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Depending on translationConstraintType(), constrain \p translation to be
|
||||
along an axis or limited to a plane defined in the Frame local coordinate
|
||||
system by translationConstraintDirection(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void LocalConstraint::constrainTranslation(Vec &translation,
|
||||
Frame *const frame) {
|
||||
Vec proj;
|
||||
switch (translationConstraintType()) {
|
||||
case AxisPlaneConstraint::FREE:
|
||||
break;
|
||||
case AxisPlaneConstraint::PLANE:
|
||||
proj = frame->rotation().rotate(translationConstraintDirection());
|
||||
translation.projectOnPlane(proj);
|
||||
break;
|
||||
case AxisPlaneConstraint::AXIS:
|
||||
proj = frame->rotation().rotate(translationConstraintDirection());
|
||||
translation.projectOnAxis(proj);
|
||||
break;
|
||||
case AxisPlaneConstraint::FORBIDDEN:
|
||||
translation = Vec(0.0, 0.0, 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p
|
||||
rotation to be a rotation around an axis whose direction is defined in the
|
||||
Frame local coordinate system by rotationConstraintDirection(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void LocalConstraint::constrainRotation(Quaternion &rotation, Frame *const) {
|
||||
switch (rotationConstraintType()) {
|
||||
case AxisPlaneConstraint::FREE:
|
||||
break;
|
||||
case AxisPlaneConstraint::PLANE:
|
||||
break;
|
||||
case AxisPlaneConstraint::AXIS: {
|
||||
Vec axis = rotationConstraintDirection();
|
||||
Vec quat = Vec(rotation[0], rotation[1], rotation[2]);
|
||||
quat.projectOnAxis(axis);
|
||||
rotation = Quaternion(quat, 2.0 * acos(rotation[3]));
|
||||
} break;
|
||||
case AxisPlaneConstraint::FORBIDDEN:
|
||||
rotation = Quaternion(); // identity
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// WorldConstraint //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Depending on translationConstraintType(), constrain \p translation to be
|
||||
along an axis or limited to a plane defined in the world coordinate system by
|
||||
translationConstraintDirection(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void WorldConstraint::constrainTranslation(Vec &translation,
|
||||
Frame *const frame) {
|
||||
Vec proj;
|
||||
switch (translationConstraintType()) {
|
||||
case AxisPlaneConstraint::FREE:
|
||||
break;
|
||||
case AxisPlaneConstraint::PLANE:
|
||||
if (frame->referenceFrame()) {
|
||||
proj = frame->referenceFrame()->transformOf(
|
||||
translationConstraintDirection());
|
||||
translation.projectOnPlane(proj);
|
||||
} else
|
||||
translation.projectOnPlane(translationConstraintDirection());
|
||||
break;
|
||||
case AxisPlaneConstraint::AXIS:
|
||||
if (frame->referenceFrame()) {
|
||||
proj = frame->referenceFrame()->transformOf(
|
||||
translationConstraintDirection());
|
||||
translation.projectOnAxis(proj);
|
||||
} else
|
||||
translation.projectOnAxis(translationConstraintDirection());
|
||||
break;
|
||||
case AxisPlaneConstraint::FORBIDDEN:
|
||||
translation = Vec(0.0, 0.0, 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p
|
||||
rotation to be a rotation around an axis whose direction is defined in the
|
||||
world coordinate system by rotationConstraintDirection(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void WorldConstraint::constrainRotation(Quaternion &rotation,
|
||||
Frame *const frame) {
|
||||
switch (rotationConstraintType()) {
|
||||
case AxisPlaneConstraint::FREE:
|
||||
break;
|
||||
case AxisPlaneConstraint::PLANE:
|
||||
break;
|
||||
case AxisPlaneConstraint::AXIS: {
|
||||
Vec quat(rotation[0], rotation[1], rotation[2]);
|
||||
Vec axis = frame->transformOf(rotationConstraintDirection());
|
||||
quat.projectOnAxis(axis);
|
||||
rotation = Quaternion(quat, 2.0 * acos(rotation[3]));
|
||||
break;
|
||||
}
|
||||
case AxisPlaneConstraint::FORBIDDEN:
|
||||
rotation = Quaternion(); // identity
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// CameraConstraint //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Creates a CameraConstraint, whose constrained directions are defined in the
|
||||
\p camera coordinate system. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
CameraConstraint::CameraConstraint(const Camera *const camera)
|
||||
: AxisPlaneConstraint(), camera_(camera) {}
|
||||
|
||||
/*! Depending on translationConstraintType(), constrain \p translation to be
|
||||
along an axis or limited to a plane defined in the camera() coordinate system
|
||||
by translationConstraintDirection(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void CameraConstraint::constrainTranslation(Vec &translation,
|
||||
Frame *const frame) {
|
||||
Vec proj;
|
||||
switch (translationConstraintType()) {
|
||||
case AxisPlaneConstraint::FREE:
|
||||
break;
|
||||
case AxisPlaneConstraint::PLANE:
|
||||
proj =
|
||||
camera()->frame()->inverseTransformOf(translationConstraintDirection());
|
||||
if (frame->referenceFrame())
|
||||
proj = frame->referenceFrame()->transformOf(proj);
|
||||
translation.projectOnPlane(proj);
|
||||
break;
|
||||
case AxisPlaneConstraint::AXIS:
|
||||
proj =
|
||||
camera()->frame()->inverseTransformOf(translationConstraintDirection());
|
||||
if (frame->referenceFrame())
|
||||
proj = frame->referenceFrame()->transformOf(proj);
|
||||
translation.projectOnAxis(proj);
|
||||
break;
|
||||
case AxisPlaneConstraint::FORBIDDEN:
|
||||
translation = Vec(0.0, 0.0, 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p
|
||||
rotation to be a rotation around an axis whose direction is defined in the
|
||||
camera() coordinate system by rotationConstraintDirection(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void CameraConstraint::constrainRotation(Quaternion &rotation,
|
||||
Frame *const frame) {
|
||||
switch (rotationConstraintType()) {
|
||||
case AxisPlaneConstraint::FREE:
|
||||
break;
|
||||
case AxisPlaneConstraint::PLANE:
|
||||
break;
|
||||
case AxisPlaneConstraint::AXIS: {
|
||||
Vec axis = frame->transformOf(
|
||||
camera()->frame()->inverseTransformOf(rotationConstraintDirection()));
|
||||
Vec quat = Vec(rotation[0], rotation[1], rotation[2]);
|
||||
quat.projectOnAxis(axis);
|
||||
rotation = Quaternion(quat, 2.0 * acos(rotation[3]));
|
||||
} break;
|
||||
case AxisPlaneConstraint::FORBIDDEN:
|
||||
rotation = Quaternion(); // identity
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,11 +25,13 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <CGAL/Qt/constraint.h>
|
||||
// #include "GL/gl.h" is now included in config.h for ease of configuration
|
||||
#include <CGAL/Qt/config.h>
|
||||
#include <CGAL/Qt/vec.h>
|
||||
#include <CGAL/Qt/quaternion.h>
|
||||
|
||||
namespace qglviewer {
|
||||
class Constraint;
|
||||
|
||||
/*! \brief The Frame class represents a coordinate system, defined by a position
|
||||
and an orientation. \class Frame frame.h QGLViewer/frame.h
|
||||
|
||||
|
|
@ -456,4 +458,8 @@ private:
|
|||
|
||||
} // namespace qglviewer
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
//#include <CGAL/Qt/qglviewer_impl_list.h>
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
||||
#endif // QGLVIEWER_FRAME_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -28,14 +28,13 @@
|
|||
|
||||
#include <CGAL/Qt/quaternion.h>
|
||||
// Not actually needed, but some bad compilers (Microsoft VS6) complain.
|
||||
#include <CGAL/Qt/frame.h>
|
||||
//#include <CGAL/Qt/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
|
||||
|
|
@ -292,12 +291,6 @@ public Q_SLOTS:
|
|||
virtual void interpolateAtTime(qreal time);
|
||||
//@}
|
||||
|
||||
/*! @name Path drawing */
|
||||
//@{
|
||||
public:
|
||||
virtual void drawPath(int mask = 1, int nbFrames = 6, qreal scale = 1.0);
|
||||
//@}
|
||||
|
||||
/*! @name XML representation */
|
||||
//@{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,587 @@
|
|||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.7.0.
|
||||
|
||||
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||
|
||||
This file may be used under the terms of the GNU General Public License
|
||||
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||
appearing in the LICENSE file included in the packaging of this file.
|
||||
In addition, as a special exception, Gilles Debunne gives you certain
|
||||
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||
|
||||
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||
purchase a libQGLViewer Commercial License.
|
||||
|
||||
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
#define CGAL_INLINE_FUNCTION inline
|
||||
|
||||
#include <CGAL/license/GraphicsView.h>
|
||||
|
||||
#else
|
||||
#define CGAL_INLINE_FUNCTION
|
||||
#endif
|
||||
|
||||
#include <CGAL/Qt/keyFrameInterpolator.h>
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
|
||||
using namespace qglviewer;
|
||||
using namespace std;
|
||||
|
||||
/*! Creates a KeyFrameInterpolator, with \p frame as associated frame().
|
||||
|
||||
The frame() can be set or changed using setFrame().
|
||||
|
||||
interpolationTime(), interpolationSpeed() and interpolationPeriod() are set to
|
||||
their default values. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
KeyFrameInterpolator::KeyFrameInterpolator(Frame *frame)
|
||||
: frame_(NULL), period_(40), interpolationTime_(0.0),
|
||||
interpolationSpeed_(1.0), interpolationStarted_(false),
|
||||
closedPath_(false), loopInterpolation_(false), pathIsValid_(false),
|
||||
valuesAreValid_(true), currentFrameValid_(false)
|
||||
// #CONNECTION# Values cut pasted initFromDOMElement()
|
||||
{
|
||||
setFrame(frame);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
currentFrame_[i] = new QMutableListIterator<KeyFrame *>(keyFrame_);
|
||||
connect(&timer_, SIGNAL(timeout()), SLOT(update()));
|
||||
}
|
||||
|
||||
/*! Virtual destructor. Clears the keyFrame path. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
KeyFrameInterpolator::~KeyFrameInterpolator() {
|
||||
deletePath();
|
||||
for (int i = 0; i < 4; ++i)
|
||||
delete currentFrame_[i];
|
||||
}
|
||||
|
||||
/*! Sets the frame() associated to the KeyFrameInterpolator. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::setFrame(Frame *const frame) {
|
||||
if (this->frame())
|
||||
disconnect(this, SIGNAL(interpolated()), this->frame(),
|
||||
SIGNAL(interpolated()));
|
||||
|
||||
frame_ = frame;
|
||||
|
||||
if (this->frame())
|
||||
connect(this, SIGNAL(interpolated()), this->frame(),
|
||||
SIGNAL(interpolated()));
|
||||
}
|
||||
|
||||
/*! Updates frame() state according to current interpolationTime(). Then adds
|
||||
interpolationPeriod()*interpolationSpeed() to interpolationTime().
|
||||
|
||||
This internal method is called by a timer when interpolationIsStarted(). It
|
||||
can be used for debugging purpose. stopInterpolation() is called when
|
||||
interpolationTime() reaches firstTime() or lastTime(), unless
|
||||
loopInterpolation() is \c true. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::update() {
|
||||
interpolateAtTime(interpolationTime());
|
||||
|
||||
interpolationTime_ += interpolationSpeed() * interpolationPeriod() / 1000.0;
|
||||
|
||||
if (interpolationTime() > keyFrame_.last()->time()) {
|
||||
if (loopInterpolation())
|
||||
setInterpolationTime(keyFrame_.first()->time() + interpolationTime_ -
|
||||
keyFrame_.last()->time());
|
||||
else {
|
||||
// Make sure last KeyFrame is reached and displayed
|
||||
interpolateAtTime(keyFrame_.last()->time());
|
||||
stopInterpolation();
|
||||
}
|
||||
Q_EMIT endReached();
|
||||
} else if (interpolationTime() < keyFrame_.first()->time()) {
|
||||
if (loopInterpolation())
|
||||
setInterpolationTime(keyFrame_.last()->time() -
|
||||
keyFrame_.first()->time() + interpolationTime_);
|
||||
else {
|
||||
// Make sure first KeyFrame is reached and displayed
|
||||
interpolateAtTime(keyFrame_.first()->time());
|
||||
stopInterpolation();
|
||||
}
|
||||
Q_EMIT endReached();
|
||||
}
|
||||
}
|
||||
|
||||
/*! Starts the interpolation process.
|
||||
|
||||
A timer is started with an interpolationPeriod() period that updates the
|
||||
frame()'s position and orientation. interpolationIsStarted() will return \c
|
||||
true until stopInterpolation() or toggleInterpolation() is called.
|
||||
|
||||
If \p period is positive, it is set as the new interpolationPeriod(). The
|
||||
previous interpolationPeriod() is used otherwise (default).
|
||||
|
||||
If interpolationTime() is larger than lastTime(), interpolationTime() is reset
|
||||
to firstTime() before interpolation starts (and inversely for negative
|
||||
interpolationSpeed()).
|
||||
|
||||
Use setInterpolationTime() before calling this method to change the starting
|
||||
interpolationTime().
|
||||
|
||||
See the <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. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::startInterpolation(int period) {
|
||||
if (period >= 0)
|
||||
setInterpolationPeriod(period);
|
||||
|
||||
if (!keyFrame_.isEmpty()) {
|
||||
if ((interpolationSpeed() > 0.0) &&
|
||||
(interpolationTime() >= keyFrame_.last()->time()))
|
||||
setInterpolationTime(keyFrame_.first()->time());
|
||||
if ((interpolationSpeed() < 0.0) &&
|
||||
(interpolationTime() <= keyFrame_.first()->time()))
|
||||
setInterpolationTime(keyFrame_.last()->time());
|
||||
timer_.start(interpolationPeriod());
|
||||
interpolationStarted_ = true;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
/*! Stops an interpolation started with startInterpolation(). See
|
||||
* interpolationIsStarted() and toggleInterpolation(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::stopInterpolation() {
|
||||
timer_.stop();
|
||||
interpolationStarted_ = false;
|
||||
}
|
||||
|
||||
/*! Stops the interpolation and resets interpolationTime() to the firstTime().
|
||||
|
||||
If desired, call interpolateAtTime() after this method to actually move the
|
||||
frame() to firstTime(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::resetInterpolation() {
|
||||
stopInterpolation();
|
||||
setInterpolationTime(firstTime());
|
||||
}
|
||||
|
||||
/*! Appends a new keyFrame to the path, with its associated \p time (in
|
||||
seconds).
|
||||
|
||||
The keyFrame is given as a pointer to a Frame, which will be connected to the
|
||||
KeyFrameInterpolator: when \p frame is modified, the KeyFrameInterpolator path
|
||||
is updated accordingly. This allows for dynamic paths, where keyFrame can be
|
||||
edited, even during the interpolation. See the <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. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::addKeyFrame(const Frame *const frame, qreal time) {
|
||||
if (!frame)
|
||||
return;
|
||||
|
||||
if (keyFrame_.isEmpty())
|
||||
interpolationTime_ = time;
|
||||
|
||||
if ((!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time))
|
||||
qWarning(
|
||||
"Error in KeyFrameInterpolator::addKeyFrame: time is not monotone");
|
||||
else
|
||||
keyFrame_.append(new KeyFrame(frame, time));
|
||||
connect(frame, SIGNAL(modified()), SLOT(invalidateValues()));
|
||||
valuesAreValid_ = false;
|
||||
pathIsValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
resetInterpolation();
|
||||
}
|
||||
|
||||
/*! Appends a new keyFrame to the path, with its associated \p time (in
|
||||
seconds).
|
||||
|
||||
The path will use the current \p frame state. If you want the path to change
|
||||
when \p frame is modified, you need to pass a \e pointer to the Frame instead
|
||||
(see addKeyFrame(const Frame*, qreal)).
|
||||
|
||||
The keyFrameTime() have to be monotonously increasing over keyFrames. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::addKeyFrame(const Frame &frame, qreal time) {
|
||||
if (keyFrame_.isEmpty())
|
||||
interpolationTime_ = time;
|
||||
|
||||
if ((!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time))
|
||||
qWarning(
|
||||
"Error in KeyFrameInterpolator::addKeyFrame: time is not monotone");
|
||||
else
|
||||
keyFrame_.append(new KeyFrame(frame, time));
|
||||
|
||||
valuesAreValid_ = false;
|
||||
pathIsValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
resetInterpolation();
|
||||
}
|
||||
|
||||
/*! Appends a new keyFrame to the path.
|
||||
|
||||
Same as addKeyFrame(const Frame* frame, qreal), except that the keyFrameTime()
|
||||
is set to the previous keyFrameTime() plus one second (or 0.0 if there is no
|
||||
previous keyFrame). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::addKeyFrame(const Frame *const frame) {
|
||||
qreal time;
|
||||
if (keyFrame_.isEmpty())
|
||||
time = 0.0;
|
||||
else
|
||||
time = lastTime() + 1.0;
|
||||
|
||||
addKeyFrame(frame, time);
|
||||
}
|
||||
|
||||
/*! Appends a new keyFrame to the path.
|
||||
|
||||
Same as addKeyFrame(const Frame& frame, qreal), except that the keyFrameTime()
|
||||
is automatically set to previous keyFrameTime() plus one second (or 0.0 if
|
||||
there is no previous keyFrame). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::addKeyFrame(const Frame &frame) {
|
||||
qreal time;
|
||||
if (keyFrame_.isEmpty())
|
||||
time = 0.0;
|
||||
else
|
||||
time = keyFrame_.last()->time() + 1.0;
|
||||
|
||||
addKeyFrame(frame, time);
|
||||
}
|
||||
|
||||
/*! Removes all keyFrames from the path. The numberOfKeyFrames() is set to 0. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::deletePath() {
|
||||
stopInterpolation();
|
||||
qDeleteAll(keyFrame_);
|
||||
keyFrame_.clear();
|
||||
pathIsValid_ = false;
|
||||
valuesAreValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
}
|
||||
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::updateModifiedFrameValues() {
|
||||
Quaternion prevQ = keyFrame_.first()->orientation();
|
||||
KeyFrame *kf;
|
||||
for (int i = 0; i < keyFrame_.size(); ++i) {
|
||||
kf = keyFrame_.at(i);
|
||||
if (kf->frame())
|
||||
kf->updateValuesFromPointer();
|
||||
kf->flipOrientationIfNeeded(prevQ);
|
||||
prevQ = kf->orientation();
|
||||
}
|
||||
|
||||
KeyFrame *prev = keyFrame_.first();
|
||||
kf = keyFrame_.first();
|
||||
int index = 1;
|
||||
while (kf) {
|
||||
KeyFrame *next = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL;
|
||||
index++;
|
||||
if (next)
|
||||
kf->computeTangent(prev, next);
|
||||
else
|
||||
kf->computeTangent(prev, kf);
|
||||
prev = kf;
|
||||
kf = next;
|
||||
}
|
||||
valuesAreValid_ = true;
|
||||
}
|
||||
|
||||
/*! Returns the Frame associated with the keyFrame at index \p index.
|
||||
|
||||
See also keyFrameTime(). \p index has to be in the range
|
||||
0..numberOfKeyFrames()-1.
|
||||
|
||||
\note If this keyFrame was defined using a pointer to a Frame (see
|
||||
addKeyFrame(const Frame* const)), the \e current pointed Frame state is
|
||||
returned. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Frame KeyFrameInterpolator::keyFrame(int index) const {
|
||||
const KeyFrame *const kf = keyFrame_.at(index);
|
||||
return Frame(kf->position(), kf->orientation());
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the \p index keyFrame.
|
||||
|
||||
See also keyFrame(). \p index has to be in the range 0..numberOfKeyFrames()-1.
|
||||
*/
|
||||
CGAL_INLINE_FUNCTION
|
||||
qreal KeyFrameInterpolator::keyFrameTime(int index) const {
|
||||
return keyFrame_.at(index)->time();
|
||||
}
|
||||
|
||||
/*! Returns the duration of the KeyFrameInterpolator path, expressed in seconds.
|
||||
|
||||
Simply corresponds to lastTime() - firstTime(). Returns 0.0 if the path has
|
||||
less than 2 keyFrames. See also keyFrameTime(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
qreal KeyFrameInterpolator::duration() const {
|
||||
return lastTime() - firstTime();
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the first keyFrame, expressed in seconds.
|
||||
|
||||
Returns 0.0 if the path is empty. See also lastTime(), duration() and
|
||||
keyFrameTime(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
qreal KeyFrameInterpolator::firstTime() const {
|
||||
if (keyFrame_.isEmpty())
|
||||
return 0.0;
|
||||
else
|
||||
return keyFrame_.first()->time();
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the last keyFrame, expressed in seconds.
|
||||
|
||||
Returns 0.0 if the path is empty. See also firstTime(), duration() and
|
||||
keyFrameTime(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
qreal KeyFrameInterpolator::lastTime() const {
|
||||
if (keyFrame_.isEmpty())
|
||||
return 0.0;
|
||||
else
|
||||
return keyFrame_.last()->time();
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::updateCurrentKeyFrameForTime(qreal time) {
|
||||
// Assertion: times are sorted in monotone order.
|
||||
// Assertion: keyFrame_ is not empty
|
||||
|
||||
// TODO: Special case for loops when closed path is implemented !!
|
||||
if (!currentFrameValid_)
|
||||
// Recompute everything from scrach
|
||||
currentFrame_[1]->toFront();
|
||||
|
||||
while (currentFrame_[1]->peekNext()->time() > time) {
|
||||
currentFrameValid_ = false;
|
||||
if (!currentFrame_[1]->hasPrevious())
|
||||
break;
|
||||
currentFrame_[1]->previous();
|
||||
}
|
||||
|
||||
if (!currentFrameValid_)
|
||||
*currentFrame_[2] = *currentFrame_[1];
|
||||
|
||||
while (currentFrame_[2]->peekNext()->time() < time) {
|
||||
currentFrameValid_ = false;
|
||||
if (!currentFrame_[2]->hasNext())
|
||||
break;
|
||||
currentFrame_[2]->next();
|
||||
}
|
||||
|
||||
if (!currentFrameValid_) {
|
||||
*currentFrame_[1] = *currentFrame_[2];
|
||||
if ((currentFrame_[1]->hasPrevious()) &&
|
||||
(time < currentFrame_[2]->peekNext()->time()))
|
||||
currentFrame_[1]->previous();
|
||||
|
||||
*currentFrame_[0] = *currentFrame_[1];
|
||||
if (currentFrame_[0]->hasPrevious())
|
||||
currentFrame_[0]->previous();
|
||||
|
||||
*currentFrame_[3] = *currentFrame_[2];
|
||||
if (currentFrame_[3]->hasNext())
|
||||
currentFrame_[3]->next();
|
||||
|
||||
currentFrameValid_ = true;
|
||||
splineCacheIsValid_ = false;
|
||||
}
|
||||
|
||||
// cout << "Time = " << time << " : " << currentFrame_[0]->peekNext()->time()
|
||||
// << " , " << currentFrame_[1]->peekNext()->time() << " , " <<
|
||||
// currentFrame_[2]->peekNext()->time() << " , " <<
|
||||
// currentFrame_[3]->peekNext()->time() << endl;
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::updateSplineCache() {
|
||||
Vec delta = currentFrame_[2]->peekNext()->position() -
|
||||
currentFrame_[1]->peekNext()->position();
|
||||
v1 = 3.0 * delta - 2.0 * currentFrame_[1]->peekNext()->tgP() -
|
||||
currentFrame_[2]->peekNext()->tgP();
|
||||
v2 = -2.0 * delta + currentFrame_[1]->peekNext()->tgP() +
|
||||
currentFrame_[2]->peekNext()->tgP();
|
||||
splineCacheIsValid_ = true;
|
||||
}
|
||||
|
||||
/*! Interpolate frame() at time \p time (expressed in seconds).
|
||||
interpolationTime() is set to \p time and frame() is set accordingly.
|
||||
|
||||
If you simply want to change interpolationTime() but not the frame() state,
|
||||
use setInterpolationTime() instead.
|
||||
|
||||
Emits the interpolated() signal and makes the frame() emit the
|
||||
Frame::interpolated() signal. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::interpolateAtTime(qreal time) {
|
||||
setInterpolationTime(time);
|
||||
|
||||
if ((keyFrame_.isEmpty()) || (!frame()))
|
||||
return;
|
||||
|
||||
if (!valuesAreValid_)
|
||||
updateModifiedFrameValues();
|
||||
|
||||
updateCurrentKeyFrameForTime(time);
|
||||
|
||||
if (!splineCacheIsValid_)
|
||||
updateSplineCache();
|
||||
|
||||
qreal alpha;
|
||||
qreal dt = currentFrame_[2]->peekNext()->time() -
|
||||
currentFrame_[1]->peekNext()->time();
|
||||
if (dt == 0.0)
|
||||
alpha = 0.0;
|
||||
else
|
||||
alpha = (time - currentFrame_[1]->peekNext()->time()) / dt;
|
||||
|
||||
// Linear interpolation - debug
|
||||
// Vec pos = alpha*(currentFrame_[2]->peekNext()->position()) +
|
||||
// (1.0-alpha)*(currentFrame_[1]->peekNext()->position());
|
||||
Vec pos =
|
||||
currentFrame_[1]->peekNext()->position() +
|
||||
alpha * (currentFrame_[1]->peekNext()->tgP() + alpha * (v1 + alpha * v2));
|
||||
Quaternion q = Quaternion::squad(
|
||||
currentFrame_[1]->peekNext()->orientation(),
|
||||
currentFrame_[1]->peekNext()->tgQ(), currentFrame_[2]->peekNext()->tgQ(),
|
||||
currentFrame_[2]->peekNext()->orientation(), alpha);
|
||||
frame()->setPositionAndOrientationWithConstraint(pos, q);
|
||||
|
||||
Q_EMIT interpolated();
|
||||
}
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the KeyFrameInterpolator.
|
||||
|
||||
The resulting QDomElement holds the KeyFrameInterpolator parameters as well as
|
||||
the path keyFrames (if the keyFrame is defined by a pointer to a Frame, use its
|
||||
current value).
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument
|
||||
factory used to create QDomElement.
|
||||
|
||||
Use initFromDOMElement() to restore the ManipulatedFrame state from the
|
||||
resulting QDomElement.
|
||||
|
||||
|
||||
See Vec::domElement() for a complete example. See also
|
||||
Quaternion::domElement(), Camera::domElement()...
|
||||
|
||||
Note that the Camera::keyFrameInterpolator() are automatically saved by
|
||||
QGLViewer::saveStateToFile() when a QGLViewer is closed. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
QDomElement KeyFrameInterpolator::domElement(const QString &name,
|
||||
QDomDocument &document) const {
|
||||
QDomElement de = document.createElement(name);
|
||||
int count = 0;
|
||||
Q_FOREACH (KeyFrame *kf, keyFrame_) {
|
||||
Frame fr(kf->position(), kf->orientation());
|
||||
QDomElement kfNode = fr.domElement("KeyFrame", document);
|
||||
kfNode.setAttribute("index", QString::number(count));
|
||||
kfNode.setAttribute("time", QString::number(kf->time()));
|
||||
de.appendChild(kfNode);
|
||||
++count;
|
||||
}
|
||||
de.setAttribute("nbKF", QString::number(keyFrame_.count()));
|
||||
de.setAttribute("time", QString::number(interpolationTime()));
|
||||
de.setAttribute("speed", QString::number(interpolationSpeed()));
|
||||
de.setAttribute("period", QString::number(interpolationPeriod()));
|
||||
DomUtils::setBoolAttribute(de, "closedPath", closedPath());
|
||||
DomUtils::setBoolAttribute(de, "loop", loopInterpolation());
|
||||
return de;
|
||||
}
|
||||
|
||||
/*! Restores the KeyFrameInterpolator state from a \c QDomElement created by
|
||||
domElement().
|
||||
|
||||
Note that the frame() pointer is not included in the domElement(): you need to
|
||||
setFrame() after this method to attach a Frame to the KeyFrameInterpolator.
|
||||
|
||||
See Vec::initFromDOMElement() for a complete code example.
|
||||
|
||||
See also Camera::initFromDOMElement() and Frame::initFromDOMElement(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::initFromDOMElement(const QDomElement &element) {
|
||||
qDeleteAll(keyFrame_);
|
||||
keyFrame_.clear();
|
||||
QDomElement child = element.firstChild().toElement();
|
||||
while (!child.isNull()) {
|
||||
if (child.tagName() == "KeyFrame") {
|
||||
Frame fr;
|
||||
fr.initFromDOMElement(child);
|
||||
qreal time = DomUtils::qrealFromDom(child, "time", 0.0);
|
||||
addKeyFrame(fr, time);
|
||||
}
|
||||
|
||||
child = child.nextSibling().toElement();
|
||||
}
|
||||
|
||||
// #CONNECTION# Values cut pasted from constructor
|
||||
setInterpolationTime(DomUtils::qrealFromDom(element, "time", 0.0));
|
||||
setInterpolationSpeed(DomUtils::qrealFromDom(element, "speed", 1.0));
|
||||
setInterpolationPeriod(DomUtils::intFromDom(element, "period", 40));
|
||||
setClosedPath(DomUtils::boolFromDom(element, "closedPath", false));
|
||||
setLoopInterpolation(DomUtils::boolFromDom(element, "loop", false));
|
||||
|
||||
// setFrame(NULL);
|
||||
pathIsValid_ = false;
|
||||
valuesAreValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
|
||||
stopInterpolation();
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
//////////// KeyFrame private class implementation /////////
|
||||
CGAL_INLINE_FUNCTION
|
||||
KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame &fr, qreal t)
|
||||
: time_(t), frame_(NULL) {
|
||||
p_ = fr.position();
|
||||
q_ = fr.orientation();
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame *fr, qreal t)
|
||||
: time_(t), frame_(fr) {
|
||||
updateValuesFromPointer();
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::KeyFrame::updateValuesFromPointer() {
|
||||
p_ = frame()->position();
|
||||
q_ = frame()->orientation();
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::KeyFrame::computeTangent(
|
||||
const KeyFrame *const prev, const KeyFrame *const next) {
|
||||
tgP_ = 0.5 * (next->position() - prev->position());
|
||||
tgQ_ = Quaternion::squadTangent(prev->orientation(), q_, next->orientation());
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void KeyFrameInterpolator::KeyFrame::flipOrientationIfNeeded(
|
||||
const Quaternion &prev) {
|
||||
if (Quaternion::dot(prev, q_) < 0.0)
|
||||
q_.negate();
|
||||
}
|
||||
|
||||
#endif // DOXYGEN
|
||||
|
|
@ -22,8 +22,11 @@
|
|||
|
||||
#ifndef QGLVIEWER_MANIPULATED_CAMERA_FRAME_H
|
||||
#define QGLVIEWER_MANIPULATED_CAMERA_FRAME_H
|
||||
#include <QTimer>
|
||||
|
||||
#include <CGAL/Qt/config.h>
|
||||
#include <CGAL/Qt/manipulatedFrame.h>
|
||||
#include <CGAL/Qt/camera.h>
|
||||
|
||||
namespace qglviewer {
|
||||
/*! \brief The ManipulatedCameraFrame class represents a ManipulatedFrame with
|
||||
|
|
@ -41,8 +44,8 @@ namespace qglviewer {
|
|||
|
||||
A ManipulatedCameraFrame can also "fly" in the scene. It basically moves
|
||||
forward, and turns according to the mouse motion. See flySpeed(),
|
||||
sceneUpVector() and the QGLViewer::MOVE_FORWARD and QGLViewer::MOVE_BACKWARD
|
||||
QGLViewer::MouseAction.
|
||||
sceneUpVector() and the MOVE_FORWARD and MOVE_BACKWARD
|
||||
MouseAction.
|
||||
|
||||
See the <a href="../mouse.html">mouse page</a> for a description of the
|
||||
possible actions that can be performed using the mouse and their bindings.
|
||||
|
|
@ -74,7 +77,7 @@ public:
|
|||
When the ManipulatedCameraFrame is associated to a Camera,
|
||||
Camera::pivotPoint() also returns this value. This point can interactively be
|
||||
changed using the mouse (see Camera::setPivotPointFromPixel() and
|
||||
QGLViewer::RAP_FROM_PIXEL and QGLViewer::RAP_IS_CENTER in the <a
|
||||
RAP_FROM_PIXEL and 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. */
|
||||
|
|
@ -125,7 +128,7 @@ public:
|
|||
rotatesAroundUpVector_ = constrained;
|
||||
}
|
||||
|
||||
/*! Returns whether or not the QGLViewer::ZOOM action zooms on the pivot
|
||||
/*! Returns whether or not the ZOOM action zooms on the pivot
|
||||
point.
|
||||
|
||||
When set to \c false (default), a zoom action will move the camera along its
|
||||
|
|
@ -169,8 +172,8 @@ public:
|
|||
/*! Returns the fly speed, expressed in OpenGL units.
|
||||
|
||||
It corresponds to the incremental displacement that is periodically applied to
|
||||
the ManipulatedCameraFrame position when a QGLViewer::MOVE_FORWARD or
|
||||
QGLViewer::MOVE_BACKWARD QGLViewer::MouseAction is proceeded.
|
||||
the ManipulatedCameraFrame position when a MOVE_FORWARD or
|
||||
MOVE_BACKWARD MouseAction is proceeded.
|
||||
|
||||
\attention When the ManipulatedCameraFrame is set as the Camera::frame(), this
|
||||
value is set according to the QGLViewer::sceneRadius() by
|
||||
|
|
@ -180,8 +183,8 @@ public:
|
|||
/*! Returns the up vector of the scene, expressed in the world coordinate
|
||||
system.
|
||||
|
||||
In 'fly mode' (corresponding to the QGLViewer::MOVE_FORWARD and
|
||||
QGLViewer::MOVE_BACKWARD QGLViewer::MouseAction bindings), horizontal
|
||||
In 'fly mode' (corresponding to the MOVE_FORWARD and
|
||||
MOVE_BACKWARD MouseAction bindings), horizontal
|
||||
displacements of the mouse rotate the ManipulatedCameraFrame around this
|
||||
vector. Vertical displacements rotate always around the Camera \c X axis.
|
||||
|
||||
|
|
@ -229,7 +232,7 @@ public Q_SLOTS:
|
|||
protected:
|
||||
virtual void startAction(
|
||||
int ma,
|
||||
bool withConstraint = true); // int is really a QGLViewer::MouseAction
|
||||
bool withConstraint = true); // int is really a MouseAction
|
||||
#endif
|
||||
|
||||
private Q_SLOTS:
|
||||
|
|
@ -260,4 +263,7 @@ private:
|
|||
|
||||
} // namespace qglviewer
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
//#include <CGAL/Qt/qglviewer_impl_list.h>
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
#endif // QGLVIEWER_MANIPULATED_CAMERA_FRAME_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,500 @@
|
|||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.7.0.
|
||||
|
||||
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||
|
||||
This file may be used under the terms of the GNU General Public License
|
||||
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||
appearing in the LICENSE file included in the packaging of this file.
|
||||
In addition, as a special exception, Gilles Debunne gives you certain
|
||||
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||
|
||||
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||
purchase a libQGLViewer Commercial License.
|
||||
|
||||
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
#define CGAL_INLINE_FUNCTION inline
|
||||
|
||||
#include <CGAL/license/GraphicsView.h>
|
||||
|
||||
#else
|
||||
#define CGAL_INLINE_FUNCTION
|
||||
#endif
|
||||
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
||||
#include <CGAL/Qt/camera.h>
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
#include <CGAL/Qt/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(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
ManipulatedCameraFrame::ManipulatedCameraFrame()
|
||||
: driveSpeed_(0.0), sceneUpVector_(0.0, 1.0, 0.0),
|
||||
rotatesAroundUpVector_(false), zoomsOnPivotPoint_(false) {
|
||||
setFlySpeed(0.0);
|
||||
removeFromMouseGrabberPool();
|
||||
connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate()));
|
||||
}
|
||||
|
||||
/*! Equal operator. Calls ManipulatedFrame::operator=() and then copy
|
||||
* attributes. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
ManipulatedCameraFrame &ManipulatedCameraFrame::
|
||||
operator=(const ManipulatedCameraFrame &mcf) {
|
||||
ManipulatedFrame::operator=(mcf);
|
||||
|
||||
setFlySpeed(mcf.flySpeed());
|
||||
setSceneUpVector(mcf.sceneUpVector());
|
||||
setRotatesAroundUpVector(mcf.rotatesAroundUpVector_);
|
||||
setZoomsOnPivotPoint(mcf.zoomsOnPivotPoint_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! Copy constructor. Performs a deep copy of all members using operator=(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
ManipulatedCameraFrame::ManipulatedCameraFrame(
|
||||
const ManipulatedCameraFrame &mcf)
|
||||
: ManipulatedFrame(mcf) {
|
||||
removeFromMouseGrabberPool();
|
||||
connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate()));
|
||||
(*this) = (mcf);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Overloading of ManipulatedFrame::spin().
|
||||
|
||||
Rotates the ManipulatedCameraFrame around its pivotPoint() instead of its
|
||||
origin. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::spin() {
|
||||
rotateAroundPoint(spinningQuaternion(), pivotPoint());
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/*! Called for continuous frame motion in fly mode (see
|
||||
MOVE_FORWARD). Emits manipulated(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::flyUpdate() {
|
||||
static Vec flyDisp(0.0, 0.0, 0.0);
|
||||
switch (action_) {
|
||||
case MOVE_FORWARD:
|
||||
flyDisp.z = -flySpeed();
|
||||
translate(localInverseTransformOf(flyDisp));
|
||||
break;
|
||||
case MOVE_BACKWARD:
|
||||
flyDisp.z = flySpeed();
|
||||
translate(localInverseTransformOf(flyDisp));
|
||||
break;
|
||||
case DRIVE:
|
||||
flyDisp.z = flySpeed() * driveSpeed_;
|
||||
translate(localInverseTransformOf(flyDisp));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Needs to be out of the switch since ZOOM/fastDraw()/wheelEvent use this
|
||||
// callback to trigger a final draw(). #CONNECTION# wheelEvent.
|
||||
Q_EMIT manipulated();
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
Vec ManipulatedCameraFrame::flyUpVector() const {
|
||||
qWarning("flyUpVector() is deprecated. Use sceneUpVector() instead.");
|
||||
return sceneUpVector();
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::setFlyUpVector(const Vec &up) {
|
||||
qWarning("setFlyUpVector() is deprecated. Use setSceneUpVector() instead.");
|
||||
setSceneUpVector(up);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*! This method will be called by the Camera when its orientation is changed, so
|
||||
that the sceneUpVector (private) is changed accordingly. You should not need to
|
||||
call this method. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::updateSceneUpVector() {
|
||||
sceneUpVector_ = inverseTransformOf(Vec(0.0, 1.0, 0.0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// S t a t e s a v i n g a n d r e s t o r i n g //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the ManipulatedCameraFrame.
|
||||
|
||||
Adds to the ManipulatedFrame::domElement() the ManipulatedCameraFrame specific
|
||||
informations in a \c ManipulatedCameraParameters child QDomElement.
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument
|
||||
factory used to create QDomElement.
|
||||
|
||||
Use initFromDOMElement() to restore the ManipulatedCameraFrame state from the
|
||||
resulting \c QDomElement.
|
||||
|
||||
See Vec::domElement() for a complete example. See also
|
||||
Quaternion::domElement(), Frame::domElement(), Camera::domElement()... */
|
||||
CGAL_INLINE_FUNCTION
|
||||
QDomElement ManipulatedCameraFrame::domElement(const QString &name,
|
||||
QDomDocument &document) const {
|
||||
QDomElement e = ManipulatedFrame::domElement(name, document);
|
||||
QDomElement mcp = document.createElement("ManipulatedCameraParameters");
|
||||
mcp.setAttribute("flySpeed", QString::number(flySpeed()));
|
||||
DomUtils::setBoolAttribute(mcp, "rotatesAroundUpVector",
|
||||
rotatesAroundUpVector());
|
||||
DomUtils::setBoolAttribute(mcp, "zoomsOnPivotPoint", zoomsOnPivotPoint());
|
||||
mcp.appendChild(sceneUpVector().domElement("sceneUpVector", document));
|
||||
e.appendChild(mcp);
|
||||
return e;
|
||||
}
|
||||
|
||||
/*! Restores the ManipulatedCameraFrame state from a \c QDomElement created by
|
||||
domElement().
|
||||
|
||||
First calls ManipulatedFrame::initFromDOMElement() and then initializes
|
||||
ManipulatedCameraFrame specific parameters. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::initFromDOMElement(const QDomElement &element) {
|
||||
// No need to initialize, since default sceneUpVector and flySpeed are not
|
||||
// meaningful. It's better to keep current ones. And it would destroy
|
||||
// constraint() and referenceFrame(). *this = ManipulatedCameraFrame();
|
||||
ManipulatedFrame::initFromDOMElement(element);
|
||||
|
||||
QDomElement child = element.firstChild().toElement();
|
||||
while (!child.isNull()) {
|
||||
if (child.tagName() == "ManipulatedCameraParameters") {
|
||||
setFlySpeed(DomUtils::qrealFromDom(child, "flySpeed", flySpeed()));
|
||||
setRotatesAroundUpVector(
|
||||
DomUtils::boolFromDom(child, "rotatesAroundUpVector", false));
|
||||
setZoomsOnPivotPoint(
|
||||
DomUtils::boolFromDom(child, "zoomsOnPivotPoint", false));
|
||||
|
||||
QDomElement schild = child.firstChild().toElement();
|
||||
while (!schild.isNull()) {
|
||||
if (schild.tagName() == "sceneUpVector")
|
||||
setSceneUpVector(Vec(schild));
|
||||
|
||||
schild = schild.nextSibling().toElement();
|
||||
}
|
||||
}
|
||||
child = child.nextSibling().toElement();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// M o u s e h a n d l i n g //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/*! Protected internal method used to handle mouse events. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::startAction(int ma, bool withConstraint) {
|
||||
ManipulatedFrame::startAction(ma, withConstraint);
|
||||
|
||||
switch (action_) {
|
||||
case MOVE_FORWARD:
|
||||
case MOVE_BACKWARD:
|
||||
case DRIVE:
|
||||
flyTimer_.setSingleShot(false);
|
||||
flyTimer_.start(10);
|
||||
break;
|
||||
case ROTATE:
|
||||
constrainedRotationIsReversed_ = transformOf(sceneUpVector_).y < 0.0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::zoom(qreal delta, const Camera *const camera) {
|
||||
const qreal sceneRadius = camera->sceneRadius();
|
||||
if (zoomsOnPivotPoint_) {
|
||||
Vec direction = position() - camera->pivotPoint();
|
||||
if (direction.norm() > 0.02 * sceneRadius || delta > 0.0)
|
||||
translate(delta * direction);
|
||||
} else {
|
||||
const qreal coef =
|
||||
qMax(fabs((camera->frame()->coordinatesOf(camera->pivotPoint())).z),
|
||||
qreal(0.2) * sceneRadius);
|
||||
Vec trans(0.0, 0.0, -coef * delta);
|
||||
translate(inverseTransformOf(trans));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*! Overloading of ManipulatedFrame::mouseMoveEvent().
|
||||
|
||||
Motion depends on mouse binding (see <a href="../mouse.html">mouse page</a> for
|
||||
details). The resulting displacements are basically inverted from those of a
|
||||
ManipulatedFrame. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::mouseMoveEvent(QMouseEvent *const event,
|
||||
Camera *const camera) {
|
||||
// #CONNECTION# mouseMoveEvent does the update().
|
||||
switch (action_) {
|
||||
case TRANSLATE: {
|
||||
const QPoint delta = prevPos_ - event->pos();
|
||||
Vec trans(delta.x(), -delta.y(), 0.0);
|
||||
// Scale to fit the screen mouse displacement
|
||||
switch (camera->type()) {
|
||||
case Camera::PERSPECTIVE:
|
||||
trans *= 2.0 * tan(camera->fieldOfView() / 2.0) *
|
||||
fabs((camera->frame()->coordinatesOf(pivotPoint())).z) /
|
||||
camera->screenHeight();
|
||||
break;
|
||||
case Camera::ORTHOGRAPHIC: {
|
||||
GLdouble w, h;
|
||||
camera->getOrthoWidthHeight(w, h);
|
||||
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
translate(inverseTransformOf(translationSensitivity() * trans));
|
||||
break;
|
||||
}
|
||||
|
||||
case MOVE_FORWARD: {
|
||||
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||
rotate(rot);
|
||||
//#CONNECTION# wheelEvent MOVE_FORWARD case
|
||||
// actual translation is made in flyUpdate().
|
||||
// translate(inverseTransformOf(Vec(0.0, 0.0, -flySpeed())));
|
||||
break;
|
||||
}
|
||||
|
||||
case MOVE_BACKWARD: {
|
||||
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||
rotate(rot);
|
||||
// actual translation is made in flyUpdate().
|
||||
// translate(inverseTransformOf(Vec(0.0, 0.0, flySpeed())));
|
||||
break;
|
||||
}
|
||||
|
||||
case DRIVE: {
|
||||
Quaternion rot = turnQuaternion(event->x(), camera);
|
||||
rotate(rot);
|
||||
// actual translation is made in flyUpdate().
|
||||
driveSpeed_ = 0.01 * (event->y() - pressPos_.y());
|
||||
break;
|
||||
}
|
||||
|
||||
case ZOOM: {
|
||||
zoom(deltaWithPrevPos(event, camera), camera);
|
||||
break;
|
||||
}
|
||||
|
||||
case LOOK_AROUND: {
|
||||
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||
rotate(rot);
|
||||
break;
|
||||
}
|
||||
|
||||
case ROTATE: {
|
||||
Quaternion rot;
|
||||
if (rotatesAroundUpVector_) {
|
||||
// Multiply by 2.0 to get on average about the same speed as with the
|
||||
// deformed ball
|
||||
qreal dx = 2.0 * rotationSensitivity() * (prevPos_.x() - event->x()) /
|
||||
camera->screenWidth();
|
||||
qreal dy = 2.0 * rotationSensitivity() * (prevPos_.y() - event->y()) /
|
||||
camera->screenHeight();
|
||||
if (constrainedRotationIsReversed_)
|
||||
dx = -dx;
|
||||
Vec verticalAxis = transformOf(sceneUpVector_);
|
||||
rot = Quaternion(verticalAxis, dx) * Quaternion(Vec(1.0, 0.0, 0.0), dy);
|
||||
} else {
|
||||
Vec trans = camera->projectedCoordinatesOf(pivotPoint());
|
||||
rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1],
|
||||
camera);
|
||||
}
|
||||
//#CONNECTION# These two methods should go together (spinning detection and
|
||||
// activation)
|
||||
computeMouseSpeed(event);
|
||||
setSpinningQuaternion(rot);
|
||||
spin();
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_ROTATE: {
|
||||
Vec trans = camera->projectedCoordinatesOf(pivotPoint());
|
||||
|
||||
const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]) -
|
||||
atan2(prevPos_.y() - trans[1], prevPos_.x() - trans[0]);
|
||||
|
||||
Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
|
||||
//#CONNECTION# These two methods should go together (spinning detection and
|
||||
// activation)
|
||||
computeMouseSpeed(event);
|
||||
setSpinningQuaternion(rot);
|
||||
spin();
|
||||
updateSceneUpVector();
|
||||
break;
|
||||
}
|
||||
|
||||
case ROLL: {
|
||||
const qreal angle =
|
||||
M_PI * (event->x() - prevPos_.x()) / camera->screenWidth();
|
||||
Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
|
||||
rotate(rot);
|
||||
setSpinningQuaternion(rot);
|
||||
updateSceneUpVector();
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_TRANSLATE: {
|
||||
Vec trans;
|
||||
int dir = mouseOriginalDirection(event);
|
||||
if (dir == 1)
|
||||
trans.setValue(prevPos_.x() - event->x(), 0.0, 0.0);
|
||||
else if (dir == -1)
|
||||
trans.setValue(0.0, event->y() - prevPos_.y(), 0.0);
|
||||
|
||||
switch (camera->type()) {
|
||||
case Camera::PERSPECTIVE:
|
||||
trans *= 2.0 * tan(camera->fieldOfView() / 2.0) *
|
||||
fabs((camera->frame()->coordinatesOf(pivotPoint())).z) /
|
||||
camera->screenHeight();
|
||||
break;
|
||||
case Camera::ORTHOGRAPHIC: {
|
||||
GLdouble w, h;
|
||||
camera->getOrthoWidthHeight(w, h);
|
||||
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
translate(inverseTransformOf(translationSensitivity() * trans));
|
||||
break;
|
||||
}
|
||||
|
||||
case ZOOM_ON_REGION:
|
||||
case NO_MOUSE_ACTION:
|
||||
break;
|
||||
}
|
||||
|
||||
if (action_ != NO_MOUSE_ACTION) {
|
||||
prevPos_ = event->pos();
|
||||
if (action_ != ZOOM_ON_REGION)
|
||||
// ZOOM_ON_REGION should not emit manipulated().
|
||||
// prevPos_ is used to draw rectangle feedback.
|
||||
Q_EMIT manipulated();
|
||||
}
|
||||
}
|
||||
|
||||
/*! This is an overload of ManipulatedFrame::mouseReleaseEvent(). The
|
||||
MouseAction is terminated. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::mouseReleaseEvent(QMouseEvent *const event,
|
||||
Camera *const camera) {
|
||||
if ((action_ == MOVE_FORWARD) ||
|
||||
(action_ == MOVE_BACKWARD) || (action_ == DRIVE))
|
||||
flyTimer_.stop();
|
||||
|
||||
if (action_ == ZOOM_ON_REGION)
|
||||
camera->fitScreenRegion(QRect(pressPos_, event->pos()));
|
||||
|
||||
ManipulatedFrame::mouseReleaseEvent(event, camera);
|
||||
}
|
||||
|
||||
/*! This is an overload of ManipulatedFrame::wheelEvent().
|
||||
|
||||
The wheel behavior depends on the wheel binded action. Current possible actions
|
||||
are ZOOM, MOVE_FORWARD, MOVE_BACKWARD.
|
||||
ZOOM speed depends on wheelSensitivity() while
|
||||
MOVE_FORWARD and MOVE_BACKWARD depend on flySpeed(). See
|
||||
QGLViewer::setWheelBinding() to customize the binding. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedCameraFrame::wheelEvent(QWheelEvent *const event,
|
||||
Camera *const camera) {
|
||||
//#CONNECTION# QGLViewer::setWheelBinding, ManipulatedFrame::wheelEvent.
|
||||
switch (action_) {
|
||||
case ZOOM: {
|
||||
zoom(wheelDelta(event), camera);
|
||||
Q_EMIT manipulated();
|
||||
break;
|
||||
}
|
||||
case MOVE_FORWARD:
|
||||
case MOVE_BACKWARD:
|
||||
//#CONNECTION# mouseMoveEvent() MOVE_FORWARD case
|
||||
translate(
|
||||
inverseTransformOf(Vec(0.0, 0.0, 0.2 * flySpeed() * event->delta())));
|
||||
Q_EMIT manipulated();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// #CONNECTION# startAction should always be called before
|
||||
if (previousConstraint_)
|
||||
setConstraint(previousConstraint_);
|
||||
|
||||
// The wheel triggers a fastDraw. A final update() is needed after the last
|
||||
// wheel event to polish the rendering using draw(). Since the last wheel
|
||||
// event does not say its name, we use the flyTimer_ to trigger flyUpdate(),
|
||||
// which emits manipulated. Two wheel events separated by more than this delay
|
||||
// milliseconds will trigger a draw().
|
||||
const int finalDrawAfterWheelEventDelay = 400;
|
||||
|
||||
// Starts (or prolungates) the timer.
|
||||
flyTimer_.setSingleShot(true);
|
||||
flyTimer_.start(finalDrawAfterWheelEventDelay);
|
||||
|
||||
// This could also be done *before* manipulated is emitted, so that
|
||||
// isManipulated() returns false. But then fastDraw would not be used with
|
||||
// wheel. Detecting the last wheel event and forcing a final draw() is done
|
||||
// using the timer_.
|
||||
action_ = NO_MOUSE_ACTION;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Returns a Quaternion that is a rotation around current camera Y,
|
||||
* proportionnal to the horizontal mouse position. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion ManipulatedCameraFrame::turnQuaternion(int x,
|
||||
const Camera *const camera) {
|
||||
return Quaternion(Vec(0.0, 1.0, 0.0), rotationSensitivity() *
|
||||
(prevPos_.x() - x) /
|
||||
camera->screenWidth());
|
||||
}
|
||||
|
||||
/*! Returns a Quaternion that is the composition of two rotations, inferred from
|
||||
the mouse pitch (X axis) and yaw (sceneUpVector() axis). */
|
||||
Quaternion
|
||||
CGAL_INLINE_FUNCTION
|
||||
ManipulatedCameraFrame::pitchYawQuaternion(int x, int y,
|
||||
const Camera *const camera) {
|
||||
const Quaternion rotX(Vec(1.0, 0.0, 0.0), rotationSensitivity() *
|
||||
(prevPos_.y() - y) /
|
||||
camera->screenHeight());
|
||||
const Quaternion rotY(transformOf(sceneUpVector()),
|
||||
rotationSensitivity() * (prevPos_.x() - x) /
|
||||
camera->screenWidth());
|
||||
return rotY * rotX;
|
||||
}
|
||||
|
|
@ -22,16 +22,18 @@
|
|||
|
||||
#ifndef QGLVIEWER_MANIPULATED_FRAME_H
|
||||
#define QGLVIEWER_MANIPULATED_FRAME_H
|
||||
|
||||
#include <CGAL/Qt/config.h>
|
||||
#include <CGAL/Qt/frame.h>
|
||||
#include <CGAL/Qt/camera.h>
|
||||
#include <CGAL/Qt/mouseGrabber.h>
|
||||
#include <CGAL/Qt/qglviewer.h>
|
||||
#include <CGAL/Qt/viewer_actions.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
namespace qglviewer {
|
||||
|
||||
/*! \brief A ManipulatedFrame is a Frame that can be rotated and translated
|
||||
using the mouse. \class ManipulatedFrame manipulatedFrame.h
|
||||
QGLViewer/manipulatedFrame.h
|
||||
|
|
@ -299,7 +301,7 @@ public:
|
|||
modifiers, Qt::MouseButton buttons, MouseHandler handler, MouseAction action,
|
||||
bool withConstraint).
|
||||
*/
|
||||
QGLViewer::MouseAction currentMouseAction() const { return action_; }
|
||||
MouseAction currentMouseAction() const { return action_; }
|
||||
//@}
|
||||
|
||||
/*! @name MouseGrabber implementation */
|
||||
|
|
@ -322,12 +324,12 @@ protected:
|
|||
Quaternion deformedBallQuaternion(int x, int y, qreal cx, qreal cy,
|
||||
const Camera *const camera);
|
||||
|
||||
QGLViewer::MouseAction action_;
|
||||
MouseAction action_;
|
||||
Constraint *previousConstraint_; // When manipulation is without Contraint.
|
||||
|
||||
virtual void startAction(
|
||||
int ma,
|
||||
bool withConstraint = true); // int is really a QGLViewer::MouseAction
|
||||
bool withConstraint = true); // int is really a MouseAction
|
||||
void computeMouseSpeed(const QMouseEvent *const e);
|
||||
int mouseOriginalDirection(const QMouseEvent *const e);
|
||||
|
||||
|
|
@ -372,4 +374,7 @@ private:
|
|||
|
||||
} // namespace qglviewer
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
//#include <CGAL/Qt/qglviewer_impl_list.h>
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
#endif // QGLVIEWER_MANIPULATED_FRAME_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,592 @@
|
|||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.7.0.
|
||||
|
||||
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||
|
||||
This file may be used under the terms of the GNU General Public License
|
||||
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||
appearing in the LICENSE file included in the packaging of this file.
|
||||
In addition, as a special exception, Gilles Debunne gives you certain
|
||||
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||
|
||||
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||
purchase a libQGLViewer Commercial License.
|
||||
|
||||
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
#define CGAL_INLINE_FUNCTION inline
|
||||
|
||||
#include <CGAL/license/GraphicsView.h>
|
||||
|
||||
#else
|
||||
#define CGAL_INLINE_FUNCTION
|
||||
#endif
|
||||
|
||||
#include <CGAL/Qt/manipulatedFrame.h>
|
||||
#include <CGAL/Qt/camera.h>
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
#include <CGAL/Qt/qglviewer.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()). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
ManipulatedFrame::ManipulatedFrame()
|
||||
: action_(NO_MOUSE_ACTION), keepsGrabbingMouse_(false) {
|
||||
// #CONNECTION# initFromDOMElement and accessor docs
|
||||
setRotationSensitivity(1.0);
|
||||
setTranslationSensitivity(1.0);
|
||||
setSpinningSensitivity(0.3);
|
||||
setWheelSensitivity(1.0);
|
||||
setZoomSensitivity(1.0);
|
||||
|
||||
isSpinning_ = false;
|
||||
previousConstraint_ = NULL;
|
||||
|
||||
connect(&spinningTimer_, SIGNAL(timeout()), SLOT(spinUpdate()));
|
||||
}
|
||||
|
||||
/*! Equal operator. Calls Frame::operator=() and then copy attributes. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
ManipulatedFrame &ManipulatedFrame::operator=(const ManipulatedFrame &mf) {
|
||||
Frame::operator=(mf);
|
||||
|
||||
setRotationSensitivity(mf.rotationSensitivity());
|
||||
setTranslationSensitivity(mf.translationSensitivity());
|
||||
setSpinningSensitivity(mf.spinningSensitivity());
|
||||
setWheelSensitivity(mf.wheelSensitivity());
|
||||
setZoomSensitivity(mf.zoomSensitivity());
|
||||
|
||||
mouseSpeed_ = 0.0;
|
||||
dirIsFixed_ = false;
|
||||
keepsGrabbingMouse_ = false;
|
||||
action_ = NO_MOUSE_ACTION;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! Copy constructor. Performs a deep copy of all attributes using operator=().
|
||||
*/
|
||||
CGAL_INLINE_FUNCTION
|
||||
ManipulatedFrame::ManipulatedFrame(const ManipulatedFrame &mf)
|
||||
: Frame(mf), MouseGrabber() {
|
||||
(*this) = mf;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Implementation of the MouseGrabber main method.
|
||||
|
||||
The ManipulatedFrame grabsMouse() when the mouse is within a 10 pixels region
|
||||
around its Camera::projectedCoordinatesOf() position().
|
||||
|
||||
See the <a href="../examples/mouseGrabber.html">mouseGrabber example</a> for an
|
||||
illustration. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::checkIfGrabsMouse(int x, int y,
|
||||
const Camera *const camera) {
|
||||
const int thresold = 10;
|
||||
const Vec proj = camera->projectedCoordinatesOf(position());
|
||||
setGrabsMouse(keepsGrabbingMouse_ || ((fabs(x - proj.x) < thresold) &&
|
||||
(fabs(y - proj.y) < thresold)));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// S t a t e s a v i n g a n d r e s t o r i n g //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the ManipulatedFrame.
|
||||
|
||||
Adds to the Frame::domElement() the ManipulatedFrame specific informations in a
|
||||
\c ManipulatedParameters child QDomElement.
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument
|
||||
factory used to create QDomElement.
|
||||
|
||||
Use initFromDOMElement() to restore the ManipulatedFrame state from the
|
||||
resulting \c QDomElement.
|
||||
|
||||
See Vec::domElement() for a complete example. See also
|
||||
Quaternion::domElement(), Camera::domElement()... */
|
||||
CGAL_INLINE_FUNCTION
|
||||
QDomElement ManipulatedFrame::domElement(const QString &name,
|
||||
QDomDocument &document) const {
|
||||
QDomElement e = Frame::domElement(name, document);
|
||||
QDomElement mp = document.createElement("ManipulatedParameters");
|
||||
mp.setAttribute("rotSens", QString::number(rotationSensitivity()));
|
||||
mp.setAttribute("transSens", QString::number(translationSensitivity()));
|
||||
mp.setAttribute("spinSens", QString::number(spinningSensitivity()));
|
||||
mp.setAttribute("wheelSens", QString::number(wheelSensitivity()));
|
||||
mp.setAttribute("zoomSens", QString::number(zoomSensitivity()));
|
||||
e.appendChild(mp);
|
||||
return e;
|
||||
}
|
||||
|
||||
/*! Restores the ManipulatedFrame state from a \c QDomElement created by
|
||||
domElement().
|
||||
|
||||
Fields that are not described in \p element are set to their default values (see
|
||||
ManipulatedFrame()).
|
||||
|
||||
First calls Frame::initFromDOMElement() and then initializes ManipulatedFrame
|
||||
specific parameters. Note that constraint() and referenceFrame() are not
|
||||
restored and are left unchanged.
|
||||
|
||||
|
||||
See Vec::initFromDOMElement() for a complete code example. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::initFromDOMElement(const QDomElement &element) {
|
||||
// Not called since it would set constraint() and referenceFrame() to NULL.
|
||||
// *this = ManipulatedFrame();
|
||||
Frame::initFromDOMElement(element);
|
||||
|
||||
stopSpinning();
|
||||
|
||||
QDomElement child = element.firstChild().toElement();
|
||||
while (!child.isNull()) {
|
||||
if (child.tagName() == "ManipulatedParameters") {
|
||||
// #CONNECTION# constructor default values and accessor docs
|
||||
setRotationSensitivity(DomUtils::qrealFromDom(child, "rotSens", 1.0));
|
||||
setTranslationSensitivity(
|
||||
DomUtils::qrealFromDom(child, "transSens", 1.0));
|
||||
setSpinningSensitivity(DomUtils::qrealFromDom(child, "spinSens", 0.3));
|
||||
setWheelSensitivity(DomUtils::qrealFromDom(child, "wheelSens", 1.0));
|
||||
setZoomSensitivity(DomUtils::qrealFromDom(child, "zoomSens", 1.0));
|
||||
}
|
||||
child = child.nextSibling().toElement();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// M o u s e h a n d l i n g //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Returns \c true when the ManipulatedFrame is being manipulated with the
|
||||
mouse.
|
||||
|
||||
Can be used to change the display of the manipulated object during
|
||||
manipulation.
|
||||
|
||||
When Camera::frame() of the QGLViewer::camera() isManipulated(),
|
||||
QGLViewer::fastDraw() is used in place of QGLViewer::draw() for scene
|
||||
rendering. A simplified drawing will then allow for interactive camera
|
||||
displacements. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
bool ManipulatedFrame::isManipulated() const {
|
||||
return action_ != NO_MOUSE_ACTION;
|
||||
}
|
||||
|
||||
/*! Starts the spinning of the ManipulatedFrame.
|
||||
|
||||
This method starts a timer that will call spin() every \p updateInterval
|
||||
milliseconds. The ManipulatedFrame isSpinning() until you call stopSpinning().
|
||||
*/
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::startSpinning(int updateInterval) {
|
||||
isSpinning_ = true;
|
||||
spinningTimer_.start(updateInterval);
|
||||
}
|
||||
|
||||
/*! Rotates the ManipulatedFrame by its spinningQuaternion(). Called by a timer
|
||||
when the ManipulatedFrame isSpinning(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::spin() { rotate(spinningQuaternion()); }
|
||||
|
||||
/* spin() and spinUpdate() differ since spin can be used by itself (for instance
|
||||
by QGLViewer::SCREEN_ROTATE) without a spun emission. Much nicer to use the
|
||||
spinningQuaternion() and hence spin() for these incremental updates. Nothing
|
||||
special to be done for continuous spinning with this design. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::spinUpdate() {
|
||||
spin();
|
||||
Q_EMIT spun();
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/*! Protected internal method used to handle mouse events. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::startAction(int ma, bool withConstraint) {
|
||||
action_ = (MouseAction)(ma);
|
||||
|
||||
// #CONNECTION# manipulatedFrame::wheelEvent,
|
||||
// manipulatedCameraFrame::wheelEvent and mouseReleaseEvent() restore previous
|
||||
// constraint
|
||||
if (withConstraint)
|
||||
previousConstraint_ = NULL;
|
||||
else {
|
||||
previousConstraint_ = constraint();
|
||||
setConstraint(NULL);
|
||||
}
|
||||
|
||||
switch (action_) {
|
||||
case ROTATE:
|
||||
case SCREEN_ROTATE:
|
||||
mouseSpeed_ = 0.0;
|
||||
stopSpinning();
|
||||
break;
|
||||
|
||||
case SCREEN_TRANSLATE:
|
||||
dirIsFixed_ = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Updates mouse speed, measured in pixels/milliseconds. Should be called by
|
||||
any method which wants to use mouse speed. Currently used to trigger spinning in
|
||||
mouseReleaseEvent(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::computeMouseSpeed(const QMouseEvent *const e) {
|
||||
const QPoint delta = (e->pos() - prevPos_);
|
||||
const qreal dist = sqrt(qreal(delta.x() * delta.x() + delta.y() * delta.y()));
|
||||
delay_ = last_move_time.restart();
|
||||
if (delay_ == 0)
|
||||
// Less than a millisecond: assume delay = 1ms
|
||||
mouseSpeed_ = dist;
|
||||
else
|
||||
mouseSpeed_ = dist / delay_;
|
||||
}
|
||||
|
||||
/*! Return 1 if mouse motion was started horizontally and -1 if it was more
|
||||
vertical. Returns 0 if this could not be determined yet (perfect diagonal
|
||||
motion, rare). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
int ManipulatedFrame::mouseOriginalDirection(const QMouseEvent *const e) {
|
||||
static bool horiz =
|
||||
true; // Two simultaneous manipulatedFrame require two mice !
|
||||
|
||||
if (!dirIsFixed_) {
|
||||
const QPoint delta = e->pos() - pressPos_;
|
||||
dirIsFixed_ = abs(delta.x()) != abs(delta.y());
|
||||
horiz = abs(delta.x()) > abs(delta.y());
|
||||
}
|
||||
|
||||
if (dirIsFixed_)
|
||||
if (horiz)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
qreal ManipulatedFrame::deltaWithPrevPos(QMouseEvent *const event,
|
||||
Camera *const camera) const {
|
||||
qreal dx = qreal(event->x() - prevPos_.x()) / camera->screenWidth();
|
||||
qreal dy = qreal(event->y() - prevPos_.y()) / camera->screenHeight();
|
||||
|
||||
qreal value = fabs(dx) > fabs(dy) ? dx : dy;
|
||||
return value * zoomSensitivity();
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
qreal ManipulatedFrame::wheelDelta(const QWheelEvent *event) const {
|
||||
static const qreal WHEEL_SENSITIVITY_COEF = 8E-4;
|
||||
return event->delta() * wheelSensitivity() * WHEEL_SENSITIVITY_COEF;
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::zoom(qreal delta, const Camera *const camera) {
|
||||
Vec trans(0.0, 0.0, (camera->position() - position()).norm() * delta);
|
||||
|
||||
trans = camera->frame()->orientation().rotate(trans);
|
||||
if (referenceFrame())
|
||||
trans = referenceFrame()->transformOf(trans);
|
||||
translate(trans);
|
||||
}
|
||||
|
||||
#endif // DOXYGEN
|
||||
|
||||
/*! Initiates the ManipulatedFrame mouse manipulation.
|
||||
|
||||
Overloading of MouseGrabber::mousePressEvent(). See also mouseMoveEvent() and
|
||||
mouseReleaseEvent().
|
||||
|
||||
The mouse behavior depends on which button is pressed. See the <a
|
||||
href="../mouse.html">QGLViewer mouse page</a> for details. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::mousePressEvent(QMouseEvent *const event,
|
||||
Camera *const camera) {
|
||||
Q_UNUSED(camera);
|
||||
|
||||
if (grabsMouse())
|
||||
keepsGrabbingMouse_ = true;
|
||||
|
||||
// #CONNECTION setMouseBinding
|
||||
// action_ should no longer possibly be NO_MOUSE_ACTION since this value is
|
||||
// not inserted in mouseBinding_
|
||||
// if (action_ == NO_MOUSE_ACTION)
|
||||
// event->ignore();
|
||||
|
||||
prevPos_ = pressPos_ = event->pos();
|
||||
}
|
||||
|
||||
/*! Modifies the ManipulatedFrame according to the mouse motion.
|
||||
|
||||
Actual behavior depends on mouse bindings. See the MouseAction enum
|
||||
and the <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. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::mouseMoveEvent(QMouseEvent *const event,
|
||||
Camera *const camera) {
|
||||
switch (action_) {
|
||||
case TRANSLATE: {
|
||||
const QPoint delta = event->pos() - prevPos_;
|
||||
Vec trans(delta.x(), -delta.y(), 0.0);
|
||||
// Scale to fit the screen mouse displacement
|
||||
switch (camera->type()) {
|
||||
case Camera::PERSPECTIVE:
|
||||
trans *= 2.0 * tan(camera->fieldOfView() / 2.0) *
|
||||
fabs((camera->frame()->coordinatesOf(position())).z) /
|
||||
camera->screenHeight();
|
||||
break;
|
||||
case Camera::ORTHOGRAPHIC: {
|
||||
GLdouble w, h;
|
||||
camera->getOrthoWidthHeight(w, h);
|
||||
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Transform to world coordinate system.
|
||||
trans =
|
||||
camera->frame()->orientation().rotate(translationSensitivity() * trans);
|
||||
// And then down to frame
|
||||
if (referenceFrame())
|
||||
trans = referenceFrame()->transformOf(trans);
|
||||
translate(trans);
|
||||
break;
|
||||
}
|
||||
|
||||
case ZOOM: {
|
||||
zoom(deltaWithPrevPos(event, camera), camera);
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_ROTATE: {
|
||||
Vec trans = camera->projectedCoordinatesOf(position());
|
||||
|
||||
const qreal prev_angle =
|
||||
atan2(prevPos_.y() - trans[1], prevPos_.x() - trans[0]);
|
||||
const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]);
|
||||
|
||||
const Vec axis =
|
||||
transformOf(camera->frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0)));
|
||||
Quaternion rot(axis, angle - prev_angle);
|
||||
//#CONNECTION# These two methods should go together (spinning detection and
|
||||
// activation)
|
||||
computeMouseSpeed(event);
|
||||
setSpinningQuaternion(rot);
|
||||
spin();
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_TRANSLATE: {
|
||||
Vec trans;
|
||||
int dir = mouseOriginalDirection(event);
|
||||
if (dir == 1)
|
||||
trans.setValue(event->x() - prevPos_.x(), 0.0, 0.0);
|
||||
else if (dir == -1)
|
||||
trans.setValue(0.0, prevPos_.y() - event->y(), 0.0);
|
||||
|
||||
switch (camera->type()) {
|
||||
case Camera::PERSPECTIVE:
|
||||
trans *= 2.0 * tan(camera->fieldOfView() / 2.0) *
|
||||
fabs((camera->frame()->coordinatesOf(position())).z) /
|
||||
camera->screenHeight();
|
||||
break;
|
||||
case Camera::ORTHOGRAPHIC: {
|
||||
GLdouble w, h;
|
||||
camera->getOrthoWidthHeight(w, h);
|
||||
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Transform to world coordinate system.
|
||||
trans =
|
||||
camera->frame()->orientation().rotate(translationSensitivity() * trans);
|
||||
// And then down to frame
|
||||
if (referenceFrame())
|
||||
trans = referenceFrame()->transformOf(trans);
|
||||
|
||||
translate(trans);
|
||||
break;
|
||||
}
|
||||
|
||||
case ROTATE: {
|
||||
Vec trans = camera->projectedCoordinatesOf(position());
|
||||
Quaternion rot = deformedBallQuaternion(event->x(), event->y(), trans[0],
|
||||
trans[1], camera);
|
||||
trans = Vec(-rot[0], -rot[1], -rot[2]);
|
||||
trans = camera->frame()->orientation().rotate(trans);
|
||||
trans = transformOf(trans);
|
||||
rot[0] = trans[0];
|
||||
rot[1] = trans[1];
|
||||
rot[2] = trans[2];
|
||||
//#CONNECTION# These two methods should go together (spinning detection and
|
||||
// activation)
|
||||
computeMouseSpeed(event);
|
||||
setSpinningQuaternion(rot);
|
||||
spin();
|
||||
break;
|
||||
}
|
||||
|
||||
case MOVE_FORWARD:
|
||||
case MOVE_BACKWARD:
|
||||
case LOOK_AROUND:
|
||||
case ROLL:
|
||||
case DRIVE:
|
||||
case ZOOM_ON_REGION:
|
||||
// These MouseAction values make no sense for a manipulatedFrame
|
||||
break;
|
||||
|
||||
case NO_MOUSE_ACTION:
|
||||
// Possible when the ManipulatedFrame is a MouseGrabber. This method is then
|
||||
// called without startAction because of mouseTracking.
|
||||
break;
|
||||
}
|
||||
|
||||
if (action_ != NO_MOUSE_ACTION) {
|
||||
prevPos_ = event->pos();
|
||||
Q_EMIT manipulated();
|
||||
}
|
||||
}
|
||||
|
||||
/*! Stops the ManipulatedFrame mouse manipulation.
|
||||
|
||||
Overloading of MouseGrabber::mouseReleaseEvent().
|
||||
|
||||
If the action was a ROTATE MouseAction, a continuous
|
||||
spinning is possible if the speed of the mouse cursor is larger than
|
||||
spinningSensitivity() when the button is released. Press the rotate button again
|
||||
to stop spinning. See startSpinning() and isSpinning(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::mouseReleaseEvent(QMouseEvent *const event,
|
||||
Camera *const camera) {
|
||||
Q_UNUSED(event);
|
||||
Q_UNUSED(camera);
|
||||
|
||||
keepsGrabbingMouse_ = false;
|
||||
|
||||
if (previousConstraint_)
|
||||
setConstraint(previousConstraint_);
|
||||
|
||||
if (((action_ == ROTATE) ||
|
||||
(action_ == SCREEN_ROTATE)) &&
|
||||
(mouseSpeed_ >= spinningSensitivity()))
|
||||
startSpinning(delay_);
|
||||
|
||||
action_ = NO_MOUSE_ACTION;
|
||||
}
|
||||
|
||||
/*! Overloading of MouseGrabber::mouseDoubleClickEvent().
|
||||
|
||||
Left button double click aligns the ManipulatedFrame with the \p camera axis
|
||||
(see alignWithFrame() and ALIGN_FRAME). Right button projects the
|
||||
ManipulatedFrame on the \p camera view direction. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::mouseDoubleClickEvent(QMouseEvent *const event,
|
||||
Camera *const camera) {
|
||||
if (event->modifiers() == Qt::NoModifier)
|
||||
switch (event->button()) {
|
||||
case Qt::LeftButton:
|
||||
alignWithFrame(camera->frame());
|
||||
break;
|
||||
case Qt::RightButton:
|
||||
projectOnLine(camera->position(), camera->viewDirection());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Overloading of MouseGrabber::wheelEvent().
|
||||
|
||||
Using the wheel is equivalent to a ZOOM MouseAction. See
|
||||
QGLViewer::setWheelBinding(), setWheelSensitivity(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void ManipulatedFrame::wheelEvent(QWheelEvent *const event,
|
||||
Camera *const camera) {
|
||||
//#CONNECTION# QGLViewer::setWheelBinding
|
||||
if (action_ == ZOOM) {
|
||||
zoom(wheelDelta(event), camera);
|
||||
Q_EMIT manipulated();
|
||||
}
|
||||
|
||||
// #CONNECTION# startAction should always be called before
|
||||
if (previousConstraint_)
|
||||
setConstraint(previousConstraint_);
|
||||
|
||||
action_ = NO_MOUSE_ACTION;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*! Returns "pseudo-distance" from (x,y) to ball of radius size.
|
||||
\arg for a point inside the ball, it is proportional to the euclidean distance
|
||||
to the ball \arg for a point outside the ball, it is proportional to the inverse
|
||||
of this distance (tends to zero) on the ball, the function is continuous. */
|
||||
static qreal projectOnBall(qreal x, qreal y) {
|
||||
// If you change the size value, change angle computation in
|
||||
// deformedBallQuaternion().
|
||||
const qreal size = 1.0;
|
||||
const qreal size2 = size * size;
|
||||
const qreal size_limit = size2 * 0.5;
|
||||
|
||||
const qreal d = x * x + y * y;
|
||||
return d < size_limit ? sqrt(size2 - d) : size_limit / sqrt(d);
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/*! Returns a quaternion computed according to the mouse motion. Mouse positions
|
||||
are projected on a deformed ball, centered on (\p cx,\p cy). */
|
||||
Quaternion
|
||||
CGAL_INLINE_FUNCTION
|
||||
ManipulatedFrame::deformedBallQuaternion(int x, int y, qreal cx, qreal cy,
|
||||
const Camera *const camera) {
|
||||
// Points on the deformed ball
|
||||
qreal px =
|
||||
rotationSensitivity() * (prevPos_.x() - cx) / camera->screenWidth();
|
||||
qreal py =
|
||||
rotationSensitivity() * (cy - prevPos_.y()) / camera->screenHeight();
|
||||
qreal dx = rotationSensitivity() * (x - cx) / camera->screenWidth();
|
||||
qreal dy = rotationSensitivity() * (cy - y) / camera->screenHeight();
|
||||
|
||||
const Vec p1(px, py, projectOnBall(px, py));
|
||||
const Vec p2(dx, dy, projectOnBall(dx, dy));
|
||||
// Approximation of rotation angle
|
||||
// Should be divided by the projectOnBall size, but it is 1.0
|
||||
const Vec axis = cross(p2, p1);
|
||||
const qreal angle =
|
||||
5.0 *
|
||||
asin(sqrt(axis.squaredNorm() / p1.squaredNorm() / p2.squaredNorm()));
|
||||
return Quaternion(axis, angle);
|
||||
}
|
||||
#endif // DOXYGEN
|
||||
|
|
@ -140,7 +140,7 @@ public:
|
|||
MouseGrabber();
|
||||
/*! Virtual destructor. Removes the MouseGrabber from the MouseGrabberPool().
|
||||
*/
|
||||
virtual ~MouseGrabber() { MouseGrabber::MouseGrabberPool_.removeAll(this); }
|
||||
virtual ~MouseGrabber() { MouseGrabber::MouseGrabberPool().removeAll(this); }
|
||||
|
||||
/*! @name Mouse grabbing detection */
|
||||
//@{
|
||||
|
|
@ -198,12 +198,8 @@ public:
|
|||
|
||||
You should not have to directly use this list. Use
|
||||
removeFromMouseGrabberPool() and addInMouseGrabberPool() to modify this list.
|
||||
|
||||
\attention This method returns a \c QPtrList<MouseGrabber> with Qt 3 and a \c
|
||||
QList<MouseGrabber> with Qt 2. */
|
||||
static const QList<MouseGrabber *> &MouseGrabberPool() {
|
||||
return MouseGrabber::MouseGrabberPool_;
|
||||
}
|
||||
*/
|
||||
static QList<MouseGrabber *> &MouseGrabberPool();
|
||||
|
||||
/*! Returns \c true if the MouseGrabber is currently in the MouseGrabberPool()
|
||||
list.
|
||||
|
|
@ -212,7 +208,7 @@ public:
|
|||
removeFromMouseGrabberPool(), the QGLViewers no longer checkIfGrabsMouse() on
|
||||
this MouseGrabber. Use addInMouseGrabberPool() to insert it back. */
|
||||
bool isInMouseGrabberPool() const {
|
||||
return MouseGrabber::MouseGrabberPool_.contains(
|
||||
return MouseGrabber::MouseGrabberPool().contains(
|
||||
const_cast<MouseGrabber *>(this));
|
||||
}
|
||||
void addInMouseGrabberPool();
|
||||
|
|
@ -289,10 +285,11 @@ private:
|
|||
|
||||
bool grabsMouse_;
|
||||
|
||||
// Q G L V i e w e r p o o l
|
||||
static QList<MouseGrabber *> MouseGrabberPool_;
|
||||
};
|
||||
|
||||
} // namespace qglviewer
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
//#include <CGAL/Qt/qglviewer_impl_list.h>
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
#endif // QGLVIEWER_MOUSE_GRABBER_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.7.0.
|
||||
|
||||
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||
|
||||
This file may be used under the terms of the GNU General Public License
|
||||
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||
appearing in the LICENSE file included in the packaging of this file.
|
||||
In addition, as a special exception, Gilles Debunne gives you certain
|
||||
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||
|
||||
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||
purchase a libQGLViewer Commercial License.
|
||||
|
||||
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
#define CGAL_INLINE_FUNCTION inline
|
||||
|
||||
#include <CGAL/license/GraphicsView.h>
|
||||
|
||||
#else
|
||||
#define CGAL_INLINE_FUNCTION
|
||||
#endif
|
||||
|
||||
#include <CGAL/Qt/mouseGrabber.h>
|
||||
|
||||
using namespace qglviewer;
|
||||
|
||||
// Static private variable
|
||||
CGAL_INLINE_FUNCTION
|
||||
QList<MouseGrabber *> &MouseGrabber::MouseGrabberPool() {
|
||||
static QList<MouseGrabber*> MouseGrabberPool_;
|
||||
return MouseGrabberPool_;
|
||||
}
|
||||
|
||||
/*! Default constructor.
|
||||
|
||||
Adds the created MouseGrabber in the MouseGrabberPool(). grabsMouse() is set to
|
||||
\c false. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
MouseGrabber::MouseGrabber() : grabsMouse_(false) { addInMouseGrabberPool(); }
|
||||
|
||||
/*! Adds the MouseGrabber in the MouseGrabberPool().
|
||||
|
||||
All created MouseGrabber are automatically added in the MouseGrabberPool() by
|
||||
the constructor. Trying to add a MouseGrabber that already
|
||||
isInMouseGrabberPool() has no effect.
|
||||
|
||||
Use removeFromMouseGrabberPool() to remove the MouseGrabber from the list, so
|
||||
that it is no longer tested with checkIfGrabsMouse() by the QGLViewer, and hence
|
||||
can no longer grab mouse focus. Use isInMouseGrabberPool() to know the current
|
||||
state of the MouseGrabber. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void MouseGrabber::addInMouseGrabberPool() {
|
||||
if (!isInMouseGrabberPool())
|
||||
MouseGrabber::MouseGrabberPool().append(this);
|
||||
}
|
||||
|
||||
/*! Removes the MouseGrabber from the MouseGrabberPool().
|
||||
|
||||
See addInMouseGrabberPool() for details. Removing a MouseGrabber that is not in
|
||||
MouseGrabberPool() has no effect. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void MouseGrabber::removeFromMouseGrabberPool() {
|
||||
if (isInMouseGrabberPool())
|
||||
MouseGrabber::MouseGrabberPool().removeAll(const_cast<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). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void MouseGrabber::clearMouseGrabberPool(bool autoDelete) {
|
||||
if (autoDelete)
|
||||
qDeleteAll(MouseGrabber::MouseGrabberPool());
|
||||
MouseGrabber::MouseGrabberPool().clear();
|
||||
}
|
||||
|
|
@ -22,8 +22,13 @@
|
|||
|
||||
#ifndef QGLVIEWER_QGLVIEWER_H
|
||||
#define QGLVIEWER_QGLVIEWER_H
|
||||
|
||||
#include <CGAL/Qt/config.h>
|
||||
#include <CGAL/Qt/viewer_actions.h>
|
||||
#include <CGAL/Qt/vec.h>
|
||||
#include <CGAL/Qt/camera.h>
|
||||
#include <CGAL/Qt/keyFrameInterpolator.h>
|
||||
#include <CGAL/Qt/manipulatedFrame.h>
|
||||
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QOpenGLFunctions_2_1>
|
||||
|
|
@ -33,16 +38,13 @@
|
|||
#include <QMap>
|
||||
#include <QVector>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
#include <QGLContext>
|
||||
|
||||
|
||||
class QTabWidget;
|
||||
|
||||
namespace qglviewer {
|
||||
class MouseGrabber;
|
||||
class ManipulatedFrame;
|
||||
class ManipulatedCameraFrame;
|
||||
} // namespace qglviewer
|
||||
|
||||
/*! \brief A versatile 3D OpenGL viewer based on QOpenGLWidget.
|
||||
\class QGLViewer qglviewer.h QGLViewer/qglviewer.h
|
||||
|
||||
|
|
@ -248,7 +250,7 @@ public:
|
|||
|
||||
Default value is 1.0. This method is equivalent to camera()->sceneRadius().
|
||||
See setSceneRadius(). */
|
||||
qreal sceneRadius() const { return camera()->sceneRadius(); }
|
||||
qreal sceneRadius() const;
|
||||
/*! Returns the scene center, defined in world coordinates.
|
||||
|
||||
See sceneRadius() for details.
|
||||
|
|
@ -258,24 +260,20 @@ public:
|
|||
|
||||
Do not mismatch this value (that only depends on the scene) with the
|
||||
qglviewer::Camera::pivotPoint(). */
|
||||
qglviewer::Vec sceneCenter() const { return camera()->sceneCenter(); }
|
||||
qglviewer::Vec sceneCenter() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/*! Sets the sceneRadius().
|
||||
|
||||
The camera() qglviewer::Camera::flySpeed() is set to 1% of this value by
|
||||
this method. Simple wrapper around camera()->setSceneRadius(). */
|
||||
virtual void setSceneRadius(qreal radius) {
|
||||
camera()->setSceneRadius(radius);
|
||||
}
|
||||
virtual void setSceneRadius(qreal radius);
|
||||
|
||||
/*! Sets the sceneCenter(), defined in world coordinates.
|
||||
|
||||
\attention The qglviewer::Camera::pivotPoint() is set to the sceneCenter()
|
||||
value by this method. */
|
||||
virtual void setSceneCenter(const qglviewer::Vec ¢er) {
|
||||
camera()->setSceneCenter(center);
|
||||
}
|
||||
virtual void setSceneCenter(const qglviewer::Vec ¢er);
|
||||
|
||||
/*! Convenient way to call setSceneCenter() and setSceneRadius() from a (world
|
||||
axis aligned) bounding box of the scene. Takes the offset into account.
|
||||
|
|
@ -286,17 +284,12 @@ public Q_SLOTS:
|
|||
setSceneRadius((max-min).norm() / 2.0);
|
||||
\endcode */
|
||||
void setSceneBoundingBox(const qglviewer::Vec &min,
|
||||
const qglviewer::Vec &max) {
|
||||
camera()->setSceneBoundingBox(min + offset(), max + offset());
|
||||
}
|
||||
const qglviewer::Vec &max);
|
||||
|
||||
/*! Moves the camera so that the entire scene is visible.
|
||||
|
||||
Simple wrapper around qglviewer::Camera::showEntireScene(). */
|
||||
void showEntireScene() {
|
||||
camera()->showEntireScene();
|
||||
update();
|
||||
}
|
||||
void showEntireScene() ;
|
||||
//@}
|
||||
|
||||
/*! @name Associated objects */
|
||||
|
|
@ -861,34 +854,10 @@ compatible with raster mode): use \c glRasterPos3fv() instead. */
|
|||
/*! @name Keyboard customization */
|
||||
//@{
|
||||
public:
|
||||
/*! Defines the different actions that can be associated with a keyboard
|
||||
shortcut using setShortcut().
|
||||
|
||||
See the <a href="../keyboard.html">keyboard page</a> for details. */
|
||||
enum KeyboardAction {
|
||||
DRAW_AXIS,
|
||||
DRAW_GRID,
|
||||
DISPLAY_FPS,
|
||||
ENABLE_TEXT,
|
||||
EXIT_VIEWER,
|
||||
CAMERA_MODE,
|
||||
FULL_SCREEN,
|
||||
STEREO,
|
||||
ANIMATION,
|
||||
HELP,
|
||||
EDIT_CAMERA,
|
||||
MOVE_CAMERA_LEFT,
|
||||
MOVE_CAMERA_RIGHT,
|
||||
MOVE_CAMERA_UP,
|
||||
MOVE_CAMERA_DOWN,
|
||||
INCREASE_FLYSPEED,
|
||||
DECREASE_FLYSPEED
|
||||
};
|
||||
|
||||
unsigned int shortcut(KeyboardAction action) const;
|
||||
unsigned int shortcut(qglviewer::KeyboardAction action) const;
|
||||
#ifndef DOXYGEN
|
||||
// QGLViewer 1.x
|
||||
unsigned int keyboardAccelerator(KeyboardAction action) const;
|
||||
unsigned int keyboardAccelerator(qglviewer::KeyboardAction action) const;
|
||||
Qt::Key keyFrameKey(unsigned int index) const;
|
||||
Qt::KeyboardModifiers playKeyFramePathStateKey() const;
|
||||
// QGLViewer 2.0 without Qt4 support
|
||||
|
|
@ -900,9 +869,9 @@ public:
|
|||
Qt::KeyboardModifiers playPathKeyboardModifiers() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void setShortcut(KeyboardAction action, unsigned int key);
|
||||
void setShortcut(qglviewer::KeyboardAction action, unsigned int key);
|
||||
#ifndef DOXYGEN
|
||||
void setKeyboardAccelerator(KeyboardAction action, unsigned int key);
|
||||
void setKeyboardAccelerator(qglviewer::KeyboardAction action, unsigned int key);
|
||||
#endif
|
||||
void setKeyDescription(unsigned int key, QString description);
|
||||
void clearShortcuts();
|
||||
|
|
@ -924,97 +893,53 @@ public Q_SLOTS:
|
|||
public:
|
||||
/*! @name Mouse customization */
|
||||
//@{
|
||||
/*! Defines the different mouse handlers: camera() or manipulatedFrame().
|
||||
|
||||
Used by setMouseBinding(), setMouseBinding(Qt::KeyboardModifiers modifiers,
|
||||
Qt::MouseButtons, ClickAction, bool, int) and setWheelBinding() to define
|
||||
which handler receives the mouse events. */
|
||||
enum MouseHandler { CAMERA, FRAME };
|
||||
|
||||
/*! Defines the possible actions that can be binded to a mouse click using
|
||||
setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, ClickAction, bool,
|
||||
int).
|
||||
|
||||
See the <a href="../mouse.html">mouse page</a> for details. */
|
||||
enum ClickAction {
|
||||
NO_CLICK_ACTION,
|
||||
ZOOM_ON_PIXEL,
|
||||
ZOOM_TO_FIT,
|
||||
SELECT,
|
||||
RAP_FROM_PIXEL,
|
||||
RAP_IS_CENTER,
|
||||
CENTER_FRAME,
|
||||
CENTER_SCENE,
|
||||
SHOW_ENTIRE_SCENE,
|
||||
ALIGN_FRAME,
|
||||
ALIGN_CAMERA
|
||||
};
|
||||
|
||||
/*! Defines the possible actions that can be binded to a mouse action (a
|
||||
click, followed by a mouse displacement).
|
||||
|
||||
These actions may be binded to the camera() or to the manipulatedFrame() (see
|
||||
QGLViewer::MouseHandler) using setMouseBinding(). */
|
||||
enum MouseAction {
|
||||
NO_MOUSE_ACTION,
|
||||
ROTATE,
|
||||
ZOOM,
|
||||
TRANSLATE,
|
||||
MOVE_FORWARD,
|
||||
LOOK_AROUND,
|
||||
MOVE_BACKWARD,
|
||||
SCREEN_ROTATE,
|
||||
ROLL,
|
||||
DRIVE,
|
||||
SCREEN_TRANSLATE,
|
||||
ZOOM_ON_REGION
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
MouseAction mouseAction(unsigned int state) const;
|
||||
qglviewer::MouseAction mouseAction(unsigned int state) const;
|
||||
int mouseHandler(unsigned int state) const;
|
||||
int mouseButtonState(MouseHandler handler, MouseAction action,
|
||||
int mouseButtonState(qglviewer::MouseHandler handler, qglviewer::MouseAction action,
|
||||
bool withConstraint = true) const;
|
||||
ClickAction clickAction(unsigned int state, bool doubleClick,
|
||||
qglviewer::ClickAction clickAction(unsigned int state, bool doubleClick,
|
||||
Qt::MouseButtons buttonsBefore) const;
|
||||
void getClickButtonState(ClickAction action, unsigned int &state,
|
||||
void getClickButtonState(qglviewer::ClickAction action, unsigned int &state,
|
||||
bool &doubleClick,
|
||||
Qt::MouseButtons &buttonsBefore) const;
|
||||
unsigned int wheelButtonState(MouseHandler handler, MouseAction action,
|
||||
unsigned int wheelButtonState(qglviewer::MouseHandler handler, qglviewer::MouseAction action,
|
||||
bool withConstraint = true) const;
|
||||
#endif
|
||||
|
||||
MouseAction mouseAction(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
qglviewer::MouseAction mouseAction(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
Qt::MouseButton button) const;
|
||||
int mouseHandler(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
Qt::MouseButton button) const;
|
||||
|
||||
void getMouseActionBinding(MouseHandler handler, MouseAction action,
|
||||
void getMouseActionBinding(qglviewer::MouseHandler handler, qglviewer::MouseAction action,
|
||||
bool withConstraint, Qt::Key &key,
|
||||
Qt::KeyboardModifiers &modifiers,
|
||||
Qt::MouseButton &button) const;
|
||||
|
||||
ClickAction clickAction(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
qglviewer::ClickAction clickAction(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
Qt::MouseButton button, bool doubleClick = false,
|
||||
Qt::MouseButtons buttonsBefore = Qt::NoButton) const;
|
||||
|
||||
void getClickActionBinding(ClickAction action, Qt::Key &key,
|
||||
void getClickActionBinding(qglviewer::ClickAction action, Qt::Key &key,
|
||||
Qt::KeyboardModifiers &modifiers,
|
||||
Qt::MouseButton &button, bool &doubleClick,
|
||||
Qt::MouseButtons &buttonsBefore) const;
|
||||
|
||||
MouseAction wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) const;
|
||||
qglviewer::MouseAction wheelAction(Qt::Key key, Qt::KeyboardModifiers modifiers) const;
|
||||
int wheelHandler(Qt::Key key, Qt::KeyboardModifiers modifiers) const;
|
||||
|
||||
void getWheelActionBinding(MouseHandler handler, MouseAction action,
|
||||
void getWheelActionBinding(qglviewer::MouseHandler handler, qglviewer::MouseAction action,
|
||||
bool withConstraint, Qt::Key &key,
|
||||
Qt::KeyboardModifiers &modifiers) const;
|
||||
|
||||
public Q_SLOTS:
|
||||
#ifndef DOXYGEN
|
||||
void setMouseBinding(unsigned int state, MouseHandler handler,
|
||||
MouseAction action, bool withConstraint = true);
|
||||
void setMouseBinding(unsigned int state, ClickAction action,
|
||||
void setMouseBinding(unsigned int state, qglviewer::MouseHandler handler,
|
||||
qglviewer::MouseAction action, bool withConstraint = true);
|
||||
void setMouseBinding(unsigned int state, qglviewer::ClickAction action,
|
||||
bool doubleClick = false,
|
||||
Qt::MouseButtons buttonsBefore = Qt::NoButton);
|
||||
void
|
||||
|
|
@ -1024,13 +949,13 @@ public Q_SLOTS:
|
|||
#endif
|
||||
|
||||
void setMouseBinding(Qt::KeyboardModifiers modifiers, Qt::MouseButton buttons,
|
||||
MouseHandler handler, MouseAction action,
|
||||
qglviewer::MouseHandler handler, qglviewer::MouseAction action,
|
||||
bool withConstraint = true);
|
||||
void setMouseBinding(Qt::KeyboardModifiers modifiers, Qt::MouseButton button,
|
||||
ClickAction action, bool doubleClick = false,
|
||||
qglviewer::ClickAction action, bool doubleClick = false,
|
||||
Qt::MouseButtons buttonsBefore = Qt::NoButton);
|
||||
void setWheelBinding(Qt::KeyboardModifiers modifiers, MouseHandler handler,
|
||||
MouseAction action, bool withConstraint = true);
|
||||
void setWheelBinding(Qt::KeyboardModifiers modifiers, qglviewer::MouseHandler handler,
|
||||
qglviewer::MouseAction action, bool withConstraint = true);
|
||||
void
|
||||
setMouseBindingDescription(Qt::KeyboardModifiers modifiers,
|
||||
Qt::MouseButton button, QString description,
|
||||
|
|
@ -1038,14 +963,14 @@ public Q_SLOTS:
|
|||
Qt::MouseButtons buttonsBefore = Qt::NoButton);
|
||||
|
||||
void setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
Qt::MouseButton buttons, MouseHandler handler,
|
||||
MouseAction action, bool withConstraint = true);
|
||||
Qt::MouseButton buttons, qglviewer::MouseHandler handler,
|
||||
qglviewer::MouseAction action, bool withConstraint = true);
|
||||
void setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
Qt::MouseButton button, ClickAction action,
|
||||
Qt::MouseButton button, qglviewer::ClickAction action,
|
||||
bool doubleClick = false,
|
||||
Qt::MouseButtons buttonsBefore = Qt::NoButton);
|
||||
void setWheelBinding(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
MouseHandler handler, MouseAction action,
|
||||
qglviewer::MouseHandler handler, qglviewer::MouseAction action,
|
||||
bool withConstraint = true);
|
||||
void
|
||||
setMouseBindingDescription(Qt::Key key, Qt::KeyboardModifiers modifiers,
|
||||
|
|
@ -1056,18 +981,18 @@ public Q_SLOTS:
|
|||
void clearMouseBindings();
|
||||
|
||||
#ifndef DOXYGEN
|
||||
MouseAction wheelAction(Qt::KeyboardModifiers modifiers) const;
|
||||
qglviewer::MouseAction wheelAction(Qt::KeyboardModifiers modifiers) const;
|
||||
int wheelHandler(Qt::KeyboardModifiers modifiers) const;
|
||||
|
||||
void setHandlerKeyboardModifiers(MouseHandler handler,
|
||||
void setHandlerKeyboardModifiers(qglviewer::MouseHandler handler,
|
||||
Qt::KeyboardModifiers modifiers);
|
||||
void setHandlerStateKey(MouseHandler handler, unsigned int buttonState);
|
||||
void setMouseStateKey(MouseHandler handler, unsigned int buttonState);
|
||||
void setHandlerStateKey(qglviewer::MouseHandler handler, unsigned int buttonState);
|
||||
void setMouseStateKey(qglviewer::MouseHandler handler, unsigned int buttonState);
|
||||
#endif
|
||||
|
||||
private:
|
||||
static QString mouseActionString(QGLViewer::MouseAction ma);
|
||||
static QString clickActionString(QGLViewer::ClickAction ca);
|
||||
static QString mouseActionString(qglviewer::MouseAction ma);
|
||||
static QString clickActionString(qglviewer::ClickAction ca);
|
||||
//@}
|
||||
|
||||
/*! @name State persistence */
|
||||
|
|
@ -1119,12 +1044,8 @@ public:
|
|||
foreach (QGLViewer* viewer, QGLViewer::QGLViewerPool())
|
||||
connect(myObject, SIGNAL(IHaveChangedSignal()), viewer, SLOT(update()));
|
||||
\endcode
|
||||
|
||||
\attention With Qt version 3, this method returns a \c QPtrList instead. Use a
|
||||
\c QPtrListIterator to iterate on the list instead.*/
|
||||
static const QList<QGLViewer *> &QGLViewerPool() {
|
||||
return QGLViewer::QGLViewerPool_;
|
||||
}
|
||||
*/
|
||||
static QList<QGLViewer *> &QGLViewerPool();
|
||||
|
||||
/*! Returns the index of the QGLViewer \p viewer in the QGLViewerPool(). This
|
||||
index in unique and can be used to identify the different created QGLViewers
|
||||
|
|
@ -1135,7 +1056,7 @@ public:
|
|||
available position in that list. Returns -1 if the QGLViewer could not be
|
||||
found (which should not be possible). */
|
||||
static int QGLViewerIndex(const QGLViewer *const viewer) {
|
||||
return QGLViewer::QGLViewerPool_.indexOf(const_cast<QGLViewer *>(viewer));
|
||||
return QGLViewer::QGLViewerPool().indexOf(const_cast<QGLViewer *>(viewer));
|
||||
}
|
||||
//@}
|
||||
|
||||
|
|
@ -1168,7 +1089,7 @@ private:
|
|||
// Set parameters to their default values. Called by the constructors.
|
||||
void defaultConstructor();
|
||||
|
||||
void handleKeyboardAction(KeyboardAction id);
|
||||
void handleKeyboardAction(qglviewer::KeyboardAction id);
|
||||
|
||||
// C a m e r a
|
||||
qglviewer::Camera *camera_;
|
||||
|
|
@ -1227,8 +1148,8 @@ private:
|
|||
// S h o r t c u t k e y s
|
||||
void setDefaultShortcuts();
|
||||
QString cameraPathKeysString() const;
|
||||
QMap<KeyboardAction, QString> keyboardActionDescription_;
|
||||
QMap<KeyboardAction, unsigned int> keyboardBinding_;
|
||||
QMap<qglviewer::KeyboardAction, QString> keyboardActionDescription_;
|
||||
QMap<qglviewer::KeyboardAction, unsigned int> keyboardBinding_;
|
||||
QMap<unsigned int, QString> keyDescription_;
|
||||
|
||||
// K e y F r a m e s s h o r t c u t s
|
||||
|
|
@ -1246,8 +1167,8 @@ private:
|
|||
#ifndef DOXYGEN
|
||||
// M o u s e a c t i o n s
|
||||
struct MouseActionPrivate {
|
||||
MouseHandler handler;
|
||||
MouseAction action;
|
||||
qglviewer::MouseHandler handler;
|
||||
qglviewer::MouseAction action;
|
||||
bool withConstraint;
|
||||
};
|
||||
|
||||
|
|
@ -1319,15 +1240,12 @@ private:
|
|||
QMap<ClickBindingPrivate, QString> mouseDescription_;
|
||||
|
||||
void setDefaultMouseBindings();
|
||||
void performClickAction(ClickAction ca, const QMouseEvent *const e);
|
||||
void performClickAction(qglviewer::ClickAction ca, const QMouseEvent *const e);
|
||||
QMap<MouseBindingPrivate, MouseActionPrivate> mouseBinding_;
|
||||
QMap<WheelBindingPrivate, MouseActionPrivate> wheelBinding_;
|
||||
QMap<ClickBindingPrivate, ClickAction> clickBinding_;
|
||||
QMap<ClickBindingPrivate, qglviewer::ClickAction> clickBinding_;
|
||||
Qt::Key currentlyPressedKey_;
|
||||
|
||||
// Q G L V i e w e r p o o l
|
||||
static QList<QGLViewer *> QGLViewerPool_;
|
||||
|
||||
// S t a t e F i l e
|
||||
QString stateFileName_;
|
||||
|
||||
|
|
@ -1371,5 +1289,8 @@ public:
|
|||
bool isOpenGL_4_3()const {return is_ogl_4_3; }
|
||||
|
||||
};
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
#include <CGAL/Qt/qglviewer_impl_list.h>
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
||||
#endif // QGLVIEWER_QGLVIEWER_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef QGLVIEWER_IMPL_LIST_H
|
||||
#define QGLVIEWER_IMPL_LIST_H
|
||||
#include <CGAL/Qt/vec_impl.h>
|
||||
#include <CGAL/Qt/quaternion_impl.h>
|
||||
#include <CGAL/Qt/frame_impl.h>
|
||||
#include <CGAL/Qt/constraint_impl.h>
|
||||
#include <CGAL/Qt/manipulatedFrame_impl.h>
|
||||
#include <CGAL/Qt/manipulatedCameraFrame_impl.h>
|
||||
#include <CGAL/Qt/camera_impl.h>
|
||||
#include <CGAL/Qt/keyFrameInterpolator_impl.h>
|
||||
#include <CGAL/Qt/qglviewer_impl.h>
|
||||
#include <CGAL/Qt/mouseGrabber_impl.h>
|
||||
#endif
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#ifndef QGLVIEWER_QUATERNION_H
|
||||
#define QGLVIEWER_QUATERNION_H
|
||||
|
||||
#include <CGAL/Qt/config.h>
|
||||
#include <CGAL/Qt/vec.h>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,566 @@
|
|||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.7.0.
|
||||
|
||||
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||
|
||||
This file may be used under the terms of the GNU General Public License
|
||||
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||
appearing in the LICENSE file included in the packaging of this file.
|
||||
In addition, as a special exception, Gilles Debunne gives you certain
|
||||
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||
|
||||
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||
purchase a libQGLViewer Commercial License.
|
||||
|
||||
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
#define CGAL_INLINE_FUNCTION inline
|
||||
|
||||
#include <CGAL/license/GraphicsView.h>
|
||||
|
||||
#else
|
||||
#define CGAL_INLINE_FUNCTION
|
||||
#endif
|
||||
|
||||
#include <CGAL/Qt/quaternion.h>
|
||||
#include <CGAL/Qt/domUtils.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. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion::Quaternion(const Vec &from, const Vec &to) {
|
||||
const qreal epsilon = 1E-10;
|
||||
|
||||
const qreal fromSqNorm = from.squaredNorm();
|
||||
const qreal toSqNorm = to.squaredNorm();
|
||||
// Identity Quaternion when one vector is null
|
||||
if ((fromSqNorm < epsilon) || (toSqNorm < epsilon)) {
|
||||
q[0] = q[1] = q[2] = 0.0;
|
||||
q[3] = 1.0;
|
||||
} else {
|
||||
Vec axis = cross(from, to);
|
||||
const qreal axisSqNorm = axis.squaredNorm();
|
||||
|
||||
// Aligned vectors, pick any axis, not aligned with from or to
|
||||
if (axisSqNorm < epsilon)
|
||||
axis = from.orthogonalVec();
|
||||
|
||||
qreal angle = asin(sqrt(axisSqNorm / (fromSqNorm * toSqNorm)));
|
||||
|
||||
if (from * to < 0.0)
|
||||
angle = M_PI - angle;
|
||||
|
||||
setAxisAngle(axis, angle);
|
||||
}
|
||||
}
|
||||
|
||||
/*! Returns the image of \p v by the Quaternion inverse() rotation.
|
||||
|
||||
rotate() performs an inverse transformation. Same as inverse().rotate(v). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Vec Quaternion::inverseRotate(const Vec &v) const {
|
||||
return inverse().rotate(v);
|
||||
}
|
||||
|
||||
/*! Returns the image of \p v by the Quaternion rotation.
|
||||
|
||||
See also inverseRotate() and operator*(const Quaternion&, const Vec&). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Vec Quaternion::rotate(const Vec &v) const {
|
||||
const qreal q00 = 2.0 * q[0] * q[0];
|
||||
const qreal q11 = 2.0 * q[1] * q[1];
|
||||
const qreal q22 = 2.0 * q[2] * q[2];
|
||||
|
||||
const qreal q01 = 2.0 * q[0] * q[1];
|
||||
const qreal q02 = 2.0 * q[0] * q[2];
|
||||
const qreal q03 = 2.0 * q[0] * q[3];
|
||||
|
||||
const qreal q12 = 2.0 * q[1] * q[2];
|
||||
const qreal q13 = 2.0 * q[1] * q[3];
|
||||
|
||||
const qreal q23 = 2.0 * q[2] * q[3];
|
||||
|
||||
return Vec((1.0 - q11 - q22) * v[0] + (q01 - q23) * v[1] + (q02 + q13) * v[2],
|
||||
(q01 + q23) * v[0] + (1.0 - q22 - q00) * v[1] + (q12 - q03) * v[2],
|
||||
(q02 - q13) * v[0] + (q12 + q03) * v[1] +
|
||||
(1.0 - q11 - q00) * v[2]);
|
||||
}
|
||||
|
||||
/*! Set the Quaternion from a (supposedly correct) 3x3 rotation matrix.
|
||||
|
||||
The matrix is expressed in European format: its three \e columns are the
|
||||
images by the rotation of the three vectors of an orthogonal basis. Note that
|
||||
OpenGL uses a symmetric representation for its matrices.
|
||||
|
||||
setFromRotatedBasis() sets a Quaternion from the three axis of a rotated
|
||||
frame. It actually fills the three columns of a matrix with these rotated
|
||||
basis vectors and calls this method. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::setFromRotationMatrix(const qreal m[3][3]) {
|
||||
// Compute one plus the trace of the matrix
|
||||
const qreal onePlusTrace = 1.0 + m[0][0] + m[1][1] + m[2][2];
|
||||
|
||||
if (onePlusTrace > 1E-5) {
|
||||
// Direct computation
|
||||
const qreal s = sqrt(onePlusTrace) * 2.0;
|
||||
q[0] = (m[2][1] - m[1][2]) / s;
|
||||
q[1] = (m[0][2] - m[2][0]) / s;
|
||||
q[2] = (m[1][0] - m[0][1]) / s;
|
||||
q[3] = 0.25 * s;
|
||||
} else {
|
||||
// Computation depends on major diagonal term
|
||||
if ((m[0][0] > m[1][1]) & (m[0][0] > m[2][2])) {
|
||||
const qreal s = sqrt(1.0 + m[0][0] - m[1][1] - m[2][2]) * 2.0;
|
||||
q[0] = 0.25 * s;
|
||||
q[1] = (m[0][1] + m[1][0]) / s;
|
||||
q[2] = (m[0][2] + m[2][0]) / s;
|
||||
q[3] = (m[1][2] - m[2][1]) / s;
|
||||
} else if (m[1][1] > m[2][2]) {
|
||||
const qreal s = sqrt(1.0 + m[1][1] - m[0][0] - m[2][2]) * 2.0;
|
||||
q[0] = (m[0][1] + m[1][0]) / s;
|
||||
q[1] = 0.25 * s;
|
||||
q[2] = (m[1][2] + m[2][1]) / s;
|
||||
q[3] = (m[0][2] - m[2][0]) / s;
|
||||
} else {
|
||||
const qreal s = sqrt(1.0 + m[2][2] - m[0][0] - m[1][1]) * 2.0;
|
||||
q[0] = (m[0][2] + m[2][0]) / s;
|
||||
q[1] = (m[1][2] + m[2][1]) / s;
|
||||
q[2] = 0.25 * s;
|
||||
q[3] = (m[0][1] - m[1][0]) / s;
|
||||
}
|
||||
}
|
||||
normalize();
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::setFromRotatedBase(const Vec &X, const Vec &Y, const Vec &Z) {
|
||||
qWarning("setFromRotatedBase is deprecated, use setFromRotatedBasis instead");
|
||||
setFromRotatedBasis(X, Y, Z);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! Sets the Quaternion from the three rotated vectors of an orthogonal basis.
|
||||
|
||||
The three vectors do not have to be normalized but must be orthogonal and
|
||||
direct (X^Y=k*Z, with k>0).
|
||||
|
||||
\code
|
||||
Quaternion q;
|
||||
q.setFromRotatedBasis(X, Y, Z);
|
||||
// Now q.rotate(Vec(1,0,0)) == X and q.inverseRotate(X) == Vec(1,0,0)
|
||||
// Same goes for Y and Z with Vec(0,1,0) and Vec(0,0,1).
|
||||
\endcode
|
||||
|
||||
See also setFromRotationMatrix() and Quaternion(const Vec&, const Vec&). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::setFromRotatedBasis(const Vec &X, const Vec &Y, const Vec &Z) {
|
||||
qreal m[3][3];
|
||||
qreal normX = X.norm();
|
||||
qreal normY = Y.norm();
|
||||
qreal normZ = Z.norm();
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
m[i][0] = X[i] / normX;
|
||||
m[i][1] = Y[i] / normY;
|
||||
m[i][2] = Z[i] / normZ;
|
||||
}
|
||||
|
||||
setFromRotationMatrix(m);
|
||||
}
|
||||
|
||||
/*! Returns the axis vector and the angle (in radians) of the rotation
|
||||
represented by the Quaternion. See the axis() and angle() documentations. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::getAxisAngle(Vec &axis, qreal &angle) const {
|
||||
angle = 2.0 * acos(q[3]);
|
||||
axis = Vec(q[0], q[1], q[2]);
|
||||
const qreal sinus = axis.norm();
|
||||
if (sinus > 1E-8)
|
||||
axis /= sinus;
|
||||
|
||||
if (angle > M_PI) {
|
||||
angle = 2.0 * qreal(M_PI) - angle;
|
||||
axis = -axis;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Returns the normalized axis direction of the rotation represented by the
|
||||
Quaternion.
|
||||
|
||||
It is null for an identity Quaternion. See also angle() and getAxisAngle(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Vec Quaternion::axis() const {
|
||||
Vec res = Vec(q[0], q[1], q[2]);
|
||||
const qreal sinus = res.norm();
|
||||
if (sinus > 1E-8)
|
||||
res /= sinus;
|
||||
return (acos(q[3]) <= M_PI / 2.0) ? res : -res;
|
||||
}
|
||||
|
||||
/*! Returns the angle (in radians) of the rotation represented by the
|
||||
Quaternion.
|
||||
|
||||
This value is always in the range [0-pi]. Larger rotational angles are obtained
|
||||
by inverting the axis() direction.
|
||||
|
||||
See also axis() and getAxisAngle(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
qreal Quaternion::angle() const {
|
||||
const qreal angle = 2.0 * acos(q[3]);
|
||||
return (angle <= M_PI) ? angle : 2.0 * M_PI - angle;
|
||||
}
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the Quaternion.
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument
|
||||
factory used to create QDomElement.
|
||||
|
||||
When output to a file, the resulting QDomElement will look like:
|
||||
\code
|
||||
<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.
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
See the Vec::domElement() documentation for a complete QDomDocument creation
|
||||
and saving example.
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
See also Frame::domElement(), Camera::domElement(),
|
||||
CGAL_INLINE_FUNCTION
|
||||
KeyFrameInterpolator::domElement()... */
|
||||
CGAL_INLINE_FUNCTION
|
||||
QDomElement Quaternion::domElement(const QString &name,
|
||||
QDomDocument &document) const {
|
||||
QDomElement de = document.createElement(name);
|
||||
de.setAttribute("q0", QString::number(q[0]));
|
||||
de.setAttribute("q1", QString::number(q[1]));
|
||||
de.setAttribute("q2", QString::number(q[2]));
|
||||
de.setAttribute("q3", QString::number(q[3]));
|
||||
return de;
|
||||
}
|
||||
|
||||
/*! Restores the Quaternion state from a \c QDomElement created by domElement().
|
||||
|
||||
The \c QDomElement should contain the \c q0, \c q1 , \c q2 and \c q3
|
||||
attributes. If one of these attributes is missing or is not a number, a warning
|
||||
is displayed and these fields are respectively set to 0.0, 0.0, 0.0 and 1.0
|
||||
(identity Quaternion).
|
||||
|
||||
See also the Quaternion(const QDomElement&) constructor. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::initFromDOMElement(const QDomElement &element) {
|
||||
Quaternion q(element);
|
||||
*this = q;
|
||||
}
|
||||
|
||||
/*! Constructs a Quaternion from a \c QDomElement representing an XML code of
|
||||
the form \code< anyTagName q0=".." q1=".." q2=".." q3=".." />\endcode
|
||||
|
||||
If one of these attributes is missing or is not a number, a warning is
|
||||
displayed and the associated value is respectively set to 0, 0, 0 and 1
|
||||
(identity Quaternion).
|
||||
|
||||
See also domElement() and initFromDOMElement(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion::Quaternion(const QDomElement &element) {
|
||||
QStringList attribute;
|
||||
attribute << "q0"
|
||||
<< "q1"
|
||||
<< "q2"
|
||||
<< "q3";
|
||||
for (int i = 0; i < attribute.size(); ++i)
|
||||
q[i] = DomUtils::qrealFromDom(element, attribute[i], ((i < 3) ? 0.0 : 1.0));
|
||||
}
|
||||
|
||||
/*! Returns the Quaternion associated 4x4 OpenGL rotation matrix.
|
||||
|
||||
Use \c glMultMatrixd(q.matrix()) to apply the rotation represented by
|
||||
Quaternion \c q to the current OpenGL matrix.
|
||||
|
||||
See also getMatrix(), getRotationMatrix() and inverseMatrix().
|
||||
|
||||
\attention The result is only valid until the next call to matrix(). Use it
|
||||
immediately (as shown above) or consider using getMatrix() instead.
|
||||
|
||||
\attention The matrix is given in OpenGL format (row-major order) and is the
|
||||
transpose of the actual mathematical European representation. Consider using
|
||||
getRotationMatrix() instead. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
const GLdouble *Quaternion::matrix() const {
|
||||
static GLdouble m[4][4];
|
||||
getMatrix(m);
|
||||
return (const GLdouble *)(m);
|
||||
}
|
||||
|
||||
/*! Fills \p m with the OpenGL representation of the Quaternion rotation.
|
||||
|
||||
Use matrix() if you do not need to store this matrix and simply want to alter
|
||||
the current OpenGL matrix. See also getInverseMatrix() and Frame::getMatrix().
|
||||
*/
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::getMatrix(GLdouble m[4][4]) const {
|
||||
const qreal q00 = 2.0 * q[0] * q[0];
|
||||
const qreal q11 = 2.0 * q[1] * q[1];
|
||||
const qreal q22 = 2.0 * q[2] * q[2];
|
||||
|
||||
const qreal q01 = 2.0 * q[0] * q[1];
|
||||
const qreal q02 = 2.0 * q[0] * q[2];
|
||||
const qreal q03 = 2.0 * q[0] * q[3];
|
||||
|
||||
const qreal q12 = 2.0 * q[1] * q[2];
|
||||
const qreal q13 = 2.0 * q[1] * q[3];
|
||||
|
||||
const qreal q23 = 2.0 * q[2] * q[3];
|
||||
|
||||
m[0][0] = 1.0 - q11 - q22;
|
||||
m[1][0] = q01 - q23;
|
||||
m[2][0] = q02 + q13;
|
||||
|
||||
m[0][1] = q01 + q23;
|
||||
m[1][1] = 1.0 - q22 - q00;
|
||||
m[2][1] = q12 - q03;
|
||||
|
||||
m[0][2] = q02 - q13;
|
||||
m[1][2] = q12 + q03;
|
||||
m[2][2] = 1.0 - q11 - q00;
|
||||
|
||||
m[0][3] = 0.0;
|
||||
m[1][3] = 0.0;
|
||||
m[2][3] = 0.0;
|
||||
|
||||
m[3][0] = 0.0;
|
||||
m[3][1] = 0.0;
|
||||
m[3][2] = 0.0;
|
||||
m[3][3] = 1.0;
|
||||
}
|
||||
|
||||
/*! Same as getMatrix(), but with a \c GLdouble[16] parameter. See also
|
||||
* getInverseMatrix() and Frame::getMatrix(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::getMatrix(GLdouble m[16]) const {
|
||||
static GLdouble mat[4][4];
|
||||
getMatrix(mat);
|
||||
int count = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int j = 0; j < 4; ++j)
|
||||
m[count++] = mat[i][j];
|
||||
}
|
||||
|
||||
/*! Fills \p m with the 3x3 rotation matrix associated with the Quaternion.
|
||||
|
||||
See also getInverseRotationMatrix().
|
||||
|
||||
\attention \p m uses the European mathematical representation of the rotation
|
||||
matrix. Use matrix() and getMatrix() to retrieve the OpenGL transposed
|
||||
version. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::getRotationMatrix(qreal m[3][3]) const {
|
||||
static GLdouble mat[4][4];
|
||||
getMatrix(mat);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (int j = 0; j < 3; ++j)
|
||||
// Beware of transposition
|
||||
m[i][j] = qreal(mat[j][i]);
|
||||
}
|
||||
|
||||
/*! Returns the associated 4x4 OpenGL \e inverse rotation matrix. This is simply
|
||||
the matrix() of the inverse().
|
||||
|
||||
\attention The result is only valid until the next call to inverseMatrix().
|
||||
Use it immediately (as in \c glMultMatrixd(q.inverseMatrix())) or use
|
||||
getInverseMatrix() instead.
|
||||
|
||||
\attention The matrix is given in OpenGL format (row-major order) and is the
|
||||
transpose of the actual mathematical European representation. Consider using
|
||||
getInverseRotationMatrix() instead. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
const GLdouble *Quaternion::inverseMatrix() const {
|
||||
static GLdouble m[4][4];
|
||||
getInverseMatrix(m);
|
||||
return (const GLdouble *)(m);
|
||||
}
|
||||
|
||||
/*! Fills \p m with the OpenGL matrix corresponding to the inverse() rotation.
|
||||
|
||||
Use inverseMatrix() if you do not need to store this matrix and simply want to
|
||||
alter the current OpenGL matrix. See also getMatrix(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::getInverseMatrix(GLdouble m[4][4]) const {
|
||||
inverse().getMatrix(m);
|
||||
}
|
||||
|
||||
/*! Same as getInverseMatrix(), but with a \c GLdouble[16] parameter. See also
|
||||
* getMatrix(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::getInverseMatrix(GLdouble m[16]) const {
|
||||
inverse().getMatrix(m);
|
||||
}
|
||||
|
||||
/*! \p m is set to the 3x3 \e inverse rotation matrix associated with the
|
||||
Quaternion.
|
||||
|
||||
\attention This is the classical mathematical rotation matrix. The OpenGL
|
||||
format uses its transposed version. See inverseMatrix() and getInverseMatrix().
|
||||
*/
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Quaternion::getInverseRotationMatrix(qreal m[3][3]) const {
|
||||
static GLdouble mat[4][4];
|
||||
getInverseMatrix(mat);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (int j = 0; j < 3; ++j)
|
||||
// Beware of transposition
|
||||
m[i][j] = qreal(mat[j][i]);
|
||||
}
|
||||
|
||||
/*! Returns the slerp interpolation of Quaternions \p a and \p b, at time \p t.
|
||||
|
||||
\p t should range in [0,1]. Result is \p a when \p t=0 and \p b when \p t=1.
|
||||
|
||||
When \p allowFlip is \c true (default) the slerp interpolation will always use
|
||||
the "shortest path" between the Quaternions' orientations, by "flipping" the
|
||||
source Quaternion if needed (see negate()). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion Quaternion::slerp(const Quaternion &a, const Quaternion &b, qreal t,
|
||||
bool allowFlip) {
|
||||
qreal cosAngle = Quaternion::dot(a, b);
|
||||
|
||||
qreal c1, c2;
|
||||
// Linear interpolation for close orientations
|
||||
if ((1.0 - fabs(cosAngle)) < 0.01) {
|
||||
c1 = 1.0 - t;
|
||||
c2 = t;
|
||||
} else {
|
||||
// Spherical interpolation
|
||||
qreal angle = acos(fabs(cosAngle));
|
||||
qreal sinAngle = sin(angle);
|
||||
c1 = sin(angle * (1.0 - t)) / sinAngle;
|
||||
c2 = sin(angle * t) / sinAngle;
|
||||
}
|
||||
|
||||
// Use the shortest path
|
||||
if (allowFlip && (cosAngle < 0.0))
|
||||
c1 = -c1;
|
||||
|
||||
return Quaternion(c1 * a[0] + c2 * b[0], c1 * a[1] + c2 * b[1],
|
||||
c1 * a[2] + c2 * b[2], c1 * a[3] + c2 * b[3]);
|
||||
}
|
||||
|
||||
/*! Returns the slerp interpolation of the two Quaternions \p a and \p b, at
|
||||
time \p t, using tangents \p tgA and \p tgB.
|
||||
|
||||
The resulting Quaternion is "between" \p a and \p b (result is \p a when \p
|
||||
t=0 and \p b for \p t=1).
|
||||
|
||||
Use squadTangent() to define the Quaternion tangents \p tgA and \p tgB. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion Quaternion::squad(const Quaternion &a, const Quaternion &tgA,
|
||||
const Quaternion &tgB, const Quaternion &b,
|
||||
qreal t) {
|
||||
Quaternion ab = Quaternion::slerp(a, b, t);
|
||||
Quaternion tg = Quaternion::slerp(tgA, tgB, t, false);
|
||||
return Quaternion::slerp(ab, tg, 2.0 * t * (1.0 - t), false);
|
||||
}
|
||||
|
||||
/*! Returns the logarithm of the Quaternion. See also exp(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion Quaternion::log() {
|
||||
qreal len = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]);
|
||||
|
||||
if (len < 1E-6)
|
||||
return Quaternion(q[0], q[1], q[2], 0.0);
|
||||
else {
|
||||
qreal coef = acos(q[3]) / len;
|
||||
return Quaternion(q[0] * coef, q[1] * coef, q[2] * coef, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
/*! Returns the exponential of the Quaternion. See also log(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion Quaternion::exp() {
|
||||
qreal theta = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]);
|
||||
|
||||
if (theta < 1E-6)
|
||||
return Quaternion(q[0], q[1], q[2], cos(theta));
|
||||
else {
|
||||
qreal coef = sin(theta) / theta;
|
||||
return Quaternion(q[0] * coef, q[1] * coef, q[2] * coef, cos(theta));
|
||||
}
|
||||
}
|
||||
|
||||
/*! Returns log(a. inverse() * b). Useful for squadTangent(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion Quaternion::lnDif(const Quaternion &a, const Quaternion &b) {
|
||||
Quaternion dif = a.inverse() * b;
|
||||
dif.normalize();
|
||||
return dif.log();
|
||||
}
|
||||
|
||||
/*! Returns a tangent Quaternion for \p center, defined by \p before and \p
|
||||
after Quaternions.
|
||||
|
||||
Useful for smooth spline interpolation of Quaternion with squad() and slerp().
|
||||
*/
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion Quaternion::squadTangent(const Quaternion &before,
|
||||
const Quaternion ¢er,
|
||||
const Quaternion &after) {
|
||||
Quaternion l1 = Quaternion::lnDif(center, before);
|
||||
Quaternion l2 = Quaternion::lnDif(center, after);
|
||||
Quaternion e;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
e.q[i] = -0.25 * (l1.q[i] + l2.q[i]);
|
||||
e = center * (e.exp());
|
||||
|
||||
// if (Quaternion::dot(e,b) < 0.0)
|
||||
// e.negate();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
ostream &operator<<(ostream &o, const Quaternion &Q) {
|
||||
return o << Q[0] << '\t' << Q[1] << '\t' << Q[2] << '\t' << Q[3];
|
||||
}
|
||||
|
||||
/*! Returns a random unit Quaternion.
|
||||
|
||||
You can create a randomly directed unit vector using:
|
||||
\code
|
||||
CGAL_INLINE_FUNCTION
|
||||
Vec randomDir = Quaternion::randomQuaternion() * Vec(1.0, 0.0, 0.0); // or any
|
||||
other Vec \endcode
|
||||
|
||||
\note This function uses rand() to create pseudo-random numbers and the random
|
||||
number generator can be initialized using srand().*/
|
||||
CGAL_INLINE_FUNCTION
|
||||
Quaternion Quaternion::randomQuaternion() {
|
||||
// The rand() function is not very portable and may not be available on your
|
||||
// system. Add the appropriate include or replace by an other random function
|
||||
// in case of problem.
|
||||
qreal seed = rand() / (qreal)RAND_MAX;
|
||||
qreal r1 = sqrt(1.0 - seed);
|
||||
qreal r2 = sqrt(seed);
|
||||
qreal t1 = 2.0 * M_PI * (rand() / (qreal)RAND_MAX);
|
||||
qreal t2 = 2.0 * M_PI * (rand() / (qreal)RAND_MAX);
|
||||
return Quaternion(sin(t1) * r1, cos(t1) * r1, sin(t2) * r2, cos(t2) * r2);
|
||||
}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
/****************************************************************************
|
||||
|
||||
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||
|
||||
This file is part of the QGLViewer library version 2.7.0.
|
||||
|
||||
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||
|
||||
This file may be used under the terms of the GNU General Public License
|
||||
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||
appearing in the LICENSE file included in the packaging of this file.
|
||||
In addition, as a special exception, Gilles Debunne gives you certain
|
||||
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||
|
||||
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||
purchase a libQGLViewer Commercial License.
|
||||
|
||||
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CGAL_HEADER_ONLY
|
||||
#define CGAL_INLINE_FUNCTION inline
|
||||
|
||||
#include <CGAL/license/GraphicsView.h>
|
||||
|
||||
#else
|
||||
#define CGAL_INLINE_FUNCTION
|
||||
#endif
|
||||
|
||||
#include <CGAL/Qt/vec.h>
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
|
||||
// Most of the methods are declared inline in vec.h
|
||||
|
||||
using namespace qglviewer;
|
||||
using namespace std;
|
||||
|
||||
/*! Projects the Vec on the axis of direction \p direction that passes through
|
||||
the origin.
|
||||
|
||||
\p direction does not need to be normalized (but must be non null). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Vec::projectOnAxis(const Vec &direction) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
if (direction.squaredNorm() < 1.0E-10)
|
||||
qWarning("Vec::projectOnAxis: axis direction is not normalized (norm=%f).",
|
||||
direction.norm());
|
||||
#endif
|
||||
|
||||
*this = (((*this) * direction) / direction.squaredNorm()) * direction;
|
||||
}
|
||||
|
||||
/*! Projects the Vec on the plane whose normal is \p normal that passes through
|
||||
the origin.
|
||||
|
||||
\p normal does not need to be normalized (but must be non null). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Vec::projectOnPlane(const Vec &normal) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
if (normal.squaredNorm() < 1.0E-10)
|
||||
qWarning("Vec::projectOnPlane: plane normal is not normalized (norm=%f).",
|
||||
normal.norm());
|
||||
#endif
|
||||
|
||||
*this -= (((*this) * normal) / normal.squaredNorm()) * normal;
|
||||
}
|
||||
|
||||
/*! Returns a Vec orthogonal to the Vec. Its norm() depends on the Vec, but is
|
||||
zero only for a null Vec. Note that the function that associates an
|
||||
orthogonalVec() to a Vec is not continous. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Vec Vec::orthogonalVec() const {
|
||||
// Find smallest component. Keep equal case for null values.
|
||||
if ((fabs(y) >= 0.9 * fabs(x)) && (fabs(z) >= 0.9 * fabs(x)))
|
||||
return Vec(0.0, -z, y);
|
||||
else if ((fabs(x) >= 0.9 * fabs(y)) && (fabs(z) >= 0.9 * fabs(y)))
|
||||
return Vec(-z, 0.0, x);
|
||||
else
|
||||
return Vec(-y, x, 0.0);
|
||||
}
|
||||
|
||||
/*! Constructs a Vec from a \c QDomElement representing an XML code of the form
|
||||
\code< anyTagName x=".." y=".." z=".." />\endcode
|
||||
|
||||
If one of these attributes is missing or is not a number, a warning is displayed
|
||||
and the associated value is set to 0.0.
|
||||
|
||||
See also domElement() and initFromDOMElement(). */
|
||||
CGAL_INLINE_FUNCTION
|
||||
Vec::Vec(const QDomElement &element) {
|
||||
QStringList attribute;
|
||||
attribute << "x"
|
||||
<< "y"
|
||||
<< "z";
|
||||
for (int i = 0; i < attribute.size(); ++i)
|
||||
#ifdef QGLVIEWER_UNION_NOT_SUPPORTED
|
||||
this->operator[](i) = DomUtils::qrealFromDom(element, attribute[i], 0.0);
|
||||
#else
|
||||
v_[i] = DomUtils::qrealFromDom(element, attribute[i], 0.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the Vec.
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument
|
||||
factory used to create QDomElement.
|
||||
|
||||
When output to a file, the resulting QDomElement will look like:
|
||||
\code
|
||||
<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
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
See also Quaternion::domElement(), Frame::domElement(), Camera::domElement()...
|
||||
*/
|
||||
CGAL_INLINE_FUNCTION
|
||||
QDomElement Vec::domElement(const QString &name, QDomDocument &document) const {
|
||||
QDomElement de = document.createElement(name);
|
||||
de.setAttribute("x", QString::number(x));
|
||||
de.setAttribute("y", QString::number(y));
|
||||
de.setAttribute("z", QString::number(z));
|
||||
return de;
|
||||
}
|
||||
|
||||
/*! Restores the Vec state from a \c QDomElement created by domElement().
|
||||
|
||||
The \c QDomElement should contain \c x, \c y and \c z attributes. If one of
|
||||
these attributes is missing or is not a number, a warning is displayed and the
|
||||
associated value is set to 0.0.
|
||||
|
||||
To restore the Vec state from an xml file, use:
|
||||
\code
|
||||
// Load DOM from file
|
||||
QDomDocument doc;
|
||||
QFile f("myFile.xml");
|
||||
if (f.open(IO_ReadOnly))
|
||||
{
|
||||
doc.setContent(&f);
|
||||
f.close();
|
||||
}
|
||||
// Parse the DOM tree and initialize
|
||||
QDomElement main=doc.documentElement();
|
||||
myVec.initFromDOMElement(main);
|
||||
\endcode
|
||||
|
||||
See also the Vec(const QDomElement&) constructor. */
|
||||
CGAL_INLINE_FUNCTION
|
||||
void Vec::initFromDOMElement(const QDomElement &element) {
|
||||
const Vec v(element);
|
||||
*this = v;
|
||||
}
|
||||
|
||||
CGAL_INLINE_FUNCTION
|
||||
ostream &operator<<(ostream &o, const Vec &v) {
|
||||
return o << v.x << '\t' << v.y << '\t' << v.z;
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef VIEWER_ACTIONS_H
|
||||
#define VIEWER_ACTIONS_H
|
||||
namespace qglviewer {
|
||||
/*! Defines the different actions that can be associated with a keyboard
|
||||
shortcut using setShortcut().
|
||||
|
||||
See the <a href="../keyboard.html">keyboard page</a> for details. */
|
||||
enum KeyboardAction {
|
||||
DRAW_AXIS,
|
||||
DRAW_GRID,
|
||||
DISPLAY_FPS,
|
||||
ENABLE_TEXT,
|
||||
EXIT_VIEWER,
|
||||
CAMERA_MODE,
|
||||
FULL_SCREEN,
|
||||
STEREO,
|
||||
ANIMATION,
|
||||
HELP,
|
||||
EDIT_CAMERA,
|
||||
MOVE_CAMERA_LEFT,
|
||||
MOVE_CAMERA_RIGHT,
|
||||
MOVE_CAMERA_UP,
|
||||
MOVE_CAMERA_DOWN,
|
||||
INCREASE_FLYSPEED,
|
||||
DECREASE_FLYSPEED
|
||||
};
|
||||
|
||||
/*! Defines the different mouse handlers: camera() or manipulatedFrame().
|
||||
|
||||
Used by setMouseBinding(), setMouseBinding(Qt::KeyboardModifiers modifiers,
|
||||
Qt::MouseButtons, ClickAction, bool, int) and setWheelBinding() to define
|
||||
which handler receives the mouse events. */
|
||||
enum MouseHandler { CAMERA, FRAME };
|
||||
|
||||
/*! Defines the possible actions that can be binded to a mouse click using
|
||||
setMouseBinding(Qt::KeyboardModifiers, Qt::MouseButtons, ClickAction, bool,
|
||||
int).
|
||||
|
||||
See the <a href="../mouse.html">mouse page</a> for details. */
|
||||
enum ClickAction {
|
||||
NO_CLICK_ACTION,
|
||||
ZOOM_ON_PIXEL,
|
||||
ZOOM_TO_FIT,
|
||||
SELECT,
|
||||
RAP_FROM_PIXEL,
|
||||
RAP_IS_CENTER,
|
||||
CENTER_FRAME,
|
||||
CENTER_SCENE,
|
||||
SHOW_ENTIRE_SCENE,
|
||||
ALIGN_FRAME,
|
||||
ALIGN_CAMERA
|
||||
};
|
||||
|
||||
/*! Defines the possible actions that can be binded to a mouse action (a
|
||||
click, followed by a mouse displacement).
|
||||
|
||||
These actions may be binded to the camera() or to the manipulatedFrame() (see
|
||||
QGLViewer::MouseHandler) using setMouseBinding(). */
|
||||
enum MouseAction {
|
||||
NO_MOUSE_ACTION,
|
||||
ROTATE,
|
||||
ZOOM,
|
||||
TRANSLATE,
|
||||
MOVE_FORWARD,
|
||||
LOOK_AROUND,
|
||||
MOVE_BACKWARD,
|
||||
SCREEN_ROTATE,
|
||||
ROLL,
|
||||
DRIVE,
|
||||
SCREEN_TRANSLATE,
|
||||
ZOOM_ON_REGION
|
||||
};
|
||||
|
||||
}
|
||||
#endif // VIEWER_ACTIONS_H
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -19,262 +19,9 @@
|
|||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
#ifndef CGAL_HEADER_ONLY
|
||||
|
||||
#include <CGAL/Qt/constraint.h>
|
||||
#include <CGAL/Qt/camera.h>
|
||||
#include <CGAL/Qt/frame.h>
|
||||
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
||||
#include <CGAL/Qt/constraint_impl.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;
|
||||
}
|
||||
}
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -19,693 +19,9 @@
|
|||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
#ifndef CGAL_HEADER_ONLY
|
||||
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
#include <CGAL/Qt/qglviewer.h> // for QGLViewer::drawAxis and Camera::drawCamera
|
||||
#include <CGAL/Qt/keyFrameInterpolator.h>
|
||||
#include <CGAL/Qt/keyFrameInterpolator_impl.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void drawCamera(qreal scale) {
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
const qreal halfHeight = scale * 0.07;
|
||||
const qreal halfWidth = halfHeight * 1.3;
|
||||
const qreal dist = halfHeight / tan(qreal(M_PI) / 8.0);
|
||||
|
||||
const qreal arrowHeight = 1.5 * halfHeight;
|
||||
const qreal baseHeight = 1.2 * halfHeight;
|
||||
const qreal arrowHalfWidth = 0.5 * halfWidth;
|
||||
const qreal baseHalfWidth = 0.3 * halfWidth;
|
||||
|
||||
// Frustum outline
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
glVertex3d(-halfWidth, halfHeight, -dist);
|
||||
glVertex3d(-halfWidth, -halfHeight, -dist);
|
||||
glVertex3d(0.0, 0.0, 0.0);
|
||||
glVertex3d(halfWidth, -halfHeight, -dist);
|
||||
glVertex3d(-halfWidth, -halfHeight, -dist);
|
||||
glEnd();
|
||||
glBegin(GL_LINE_STRIP);
|
||||
glVertex3d(halfWidth, -halfHeight, -dist);
|
||||
glVertex3d(halfWidth, halfHeight, -dist);
|
||||
glVertex3d(0.0, 0.0, 0.0);
|
||||
glVertex3d(-halfWidth, halfHeight, -dist);
|
||||
glVertex3d(halfWidth, halfHeight, -dist);
|
||||
glEnd();
|
||||
|
||||
// Up arrow
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
// Base
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3d(-baseHalfWidth, halfHeight, -dist);
|
||||
glVertex3d(baseHalfWidth, halfHeight, -dist);
|
||||
glVertex3d(baseHalfWidth, baseHeight, -dist);
|
||||
glVertex3d(-baseHalfWidth, baseHeight, -dist);
|
||||
glEnd();
|
||||
|
||||
// Arrow
|
||||
glBegin(GL_TRIANGLES);
|
||||
glVertex3d(0.0, arrowHeight, -dist);
|
||||
glVertex3d(-arrowHalfWidth, baseHeight, -dist);
|
||||
glVertex3d(arrowHalfWidth, baseHeight, -dist);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
/*! Draws the path used to interpolate the frame().
|
||||
|
||||
\p mask controls what is drawn: if (mask & 1) (default), the position path is
|
||||
drawn. If (mask & 2), a camera representation is regularly drawn and if (mask
|
||||
& 4), an oriented axis is regularly drawn. Examples:
|
||||
|
||||
\code
|
||||
drawPath(); // Simply draws the interpolation path
|
||||
drawPath(3); // Draws path and cameras
|
||||
drawPath(5); // Draws path and axis
|
||||
\endcode
|
||||
|
||||
In the case where camera or axis is drawn, \p nbFrames controls the number of
|
||||
objects (axis or camera) drawn between two successive keyFrames. When \p
|
||||
nbFrames=1, only the path KeyFrames are drawn. \p nbFrames=2 also draws the
|
||||
intermediate orientation, etc. The maximum value is 30. \p nbFrames should
|
||||
divide 30 so that an object is drawn for each KeyFrame. Default value is 6.
|
||||
|
||||
\p scale (default=1.0) controls the scaling of the camera and axis drawing. A
|
||||
value of QGLViewer::sceneRadius() should give good results.
|
||||
|
||||
See the <a href="../examples/keyFrames.html">keyFrames example</a> for an
|
||||
illustration.
|
||||
|
||||
The color of the path is the current \c glColor().
|
||||
|
||||
\attention The OpenGL state is modified by this method: GL_LIGHTING is
|
||||
disabled and line width set to 2. Use this code to preserve your current
|
||||
OpenGL state: \code glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
drawPathModifyGLState(mask, nbFrames, scale);
|
||||
glPopAttrib();
|
||||
\endcode */
|
||||
void KeyFrameInterpolator::drawPath(int mask, int nbFrames, qreal scale) {
|
||||
const int nbSteps = 30;
|
||||
if (!pathIsValid_) {
|
||||
path_.clear();
|
||||
|
||||
if (keyFrame_.isEmpty())
|
||||
return;
|
||||
|
||||
if (!valuesAreValid_)
|
||||
updateModifiedFrameValues();
|
||||
|
||||
if (keyFrame_.first() == keyFrame_.last())
|
||||
path_.push_back(Frame(keyFrame_.first()->position(),
|
||||
keyFrame_.first()->orientation()));
|
||||
else {
|
||||
static Frame fr;
|
||||
KeyFrame *kf_[4];
|
||||
kf_[0] = keyFrame_.first();
|
||||
kf_[1] = kf_[0];
|
||||
int index = 1;
|
||||
kf_[2] = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL;
|
||||
index++;
|
||||
kf_[3] = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL;
|
||||
|
||||
while (kf_[2]) {
|
||||
Vec diff = kf_[2]->position() - kf_[1]->position();
|
||||
Vec v1 = 3.0 * diff - 2.0 * kf_[1]->tgP() - kf_[2]->tgP();
|
||||
Vec v2 = -2.0 * diff + kf_[1]->tgP() + kf_[2]->tgP();
|
||||
|
||||
// cout << kf_[0]->time() << " , " << kf_[1]->time() << " , " <<
|
||||
// kf_[2]->time() << " , " << kf_[3]->time() << endl;
|
||||
for (int step = 0; step < nbSteps; ++step) {
|
||||
qreal alpha = step / static_cast<qreal>(nbSteps);
|
||||
fr.setPosition(kf_[1]->position() +
|
||||
alpha * (kf_[1]->tgP() + alpha * (v1 + alpha * v2)));
|
||||
fr.setOrientation(Quaternion::squad(kf_[1]->orientation(),
|
||||
kf_[1]->tgQ(), kf_[2]->tgQ(),
|
||||
kf_[2]->orientation(), alpha));
|
||||
path_.push_back(fr);
|
||||
}
|
||||
|
||||
// Shift
|
||||
kf_[0] = kf_[1];
|
||||
kf_[1] = kf_[2];
|
||||
kf_[2] = kf_[3];
|
||||
index++;
|
||||
kf_[3] = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL;
|
||||
}
|
||||
// Add last KeyFrame
|
||||
path_.push_back(Frame(kf_[1]->position(), kf_[1]->orientation()));
|
||||
}
|
||||
pathIsValid_ = true;
|
||||
}
|
||||
|
||||
if (mask) {
|
||||
glDisable(GL_LIGHTING);
|
||||
glLineWidth(2);
|
||||
|
||||
if (mask & 1) {
|
||||
glBegin(GL_LINE_STRIP);
|
||||
Q_FOREACH (Frame fr, path_)
|
||||
glVertex3fv(fr.position());
|
||||
glEnd();
|
||||
}
|
||||
if (mask & 6) {
|
||||
int count = 0;
|
||||
if (nbFrames > nbSteps)
|
||||
nbFrames = nbSteps;
|
||||
qreal goal = 0.0;
|
||||
Q_FOREACH (Frame fr, path_)
|
||||
if ((count++) >= goal) {
|
||||
goal += nbSteps / static_cast<qreal>(nbFrames);
|
||||
glPushMatrix();
|
||||
glMultMatrixd(fr.matrix());
|
||||
if (mask & 2)
|
||||
drawCamera(scale);
|
||||
//if (mask & 4)
|
||||
// QGLViewer::drawAxis(scale / 10.0);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::updateModifiedFrameValues() {
|
||||
Quaternion prevQ = keyFrame_.first()->orientation();
|
||||
KeyFrame *kf;
|
||||
for (int i = 0; i < keyFrame_.size(); ++i) {
|
||||
kf = keyFrame_.at(i);
|
||||
if (kf->frame())
|
||||
kf->updateValuesFromPointer();
|
||||
kf->flipOrientationIfNeeded(prevQ);
|
||||
prevQ = kf->orientation();
|
||||
}
|
||||
|
||||
KeyFrame *prev = keyFrame_.first();
|
||||
kf = keyFrame_.first();
|
||||
int index = 1;
|
||||
while (kf) {
|
||||
KeyFrame *next = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL;
|
||||
index++;
|
||||
if (next)
|
||||
kf->computeTangent(prev, next);
|
||||
else
|
||||
kf->computeTangent(prev, kf);
|
||||
prev = kf;
|
||||
kf = next;
|
||||
}
|
||||
valuesAreValid_ = true;
|
||||
}
|
||||
|
||||
/*! Returns the Frame associated with the keyFrame at index \p index.
|
||||
|
||||
See also keyFrameTime(). \p index has to be in the range
|
||||
0..numberOfKeyFrames()-1.
|
||||
|
||||
\note If this keyFrame was defined using a pointer to a Frame (see
|
||||
addKeyFrame(const Frame* const)), the \e current pointed Frame state is
|
||||
returned. */
|
||||
Frame KeyFrameInterpolator::keyFrame(int index) const {
|
||||
const KeyFrame *const kf = keyFrame_.at(index);
|
||||
return Frame(kf->position(), kf->orientation());
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the \p index keyFrame.
|
||||
|
||||
See also keyFrame(). \p index has to be in the range 0..numberOfKeyFrames()-1.
|
||||
*/
|
||||
qreal KeyFrameInterpolator::keyFrameTime(int index) const {
|
||||
return keyFrame_.at(index)->time();
|
||||
}
|
||||
|
||||
/*! Returns the duration of the KeyFrameInterpolator path, expressed in seconds.
|
||||
|
||||
Simply corresponds to lastTime() - firstTime(). Returns 0.0 if the path has
|
||||
less than 2 keyFrames. See also keyFrameTime(). */
|
||||
qreal KeyFrameInterpolator::duration() const {
|
||||
return lastTime() - firstTime();
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the first keyFrame, expressed in seconds.
|
||||
|
||||
Returns 0.0 if the path is empty. See also lastTime(), duration() and
|
||||
keyFrameTime(). */
|
||||
qreal KeyFrameInterpolator::firstTime() const {
|
||||
if (keyFrame_.isEmpty())
|
||||
return 0.0;
|
||||
else
|
||||
return keyFrame_.first()->time();
|
||||
}
|
||||
|
||||
/*! Returns the time corresponding to the last keyFrame, expressed in seconds.
|
||||
|
||||
Returns 0.0 if the path is empty. See also firstTime(), duration() and
|
||||
keyFrameTime(). */
|
||||
qreal KeyFrameInterpolator::lastTime() const {
|
||||
if (keyFrame_.isEmpty())
|
||||
return 0.0;
|
||||
else
|
||||
return keyFrame_.last()->time();
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::updateCurrentKeyFrameForTime(qreal time) {
|
||||
// Assertion: times are sorted in monotone order.
|
||||
// Assertion: keyFrame_ is not empty
|
||||
|
||||
// TODO: Special case for loops when closed path is implemented !!
|
||||
if (!currentFrameValid_)
|
||||
// Recompute everything from scrach
|
||||
currentFrame_[1]->toFront();
|
||||
|
||||
while (currentFrame_[1]->peekNext()->time() > time) {
|
||||
currentFrameValid_ = false;
|
||||
if (!currentFrame_[1]->hasPrevious())
|
||||
break;
|
||||
currentFrame_[1]->previous();
|
||||
}
|
||||
|
||||
if (!currentFrameValid_)
|
||||
*currentFrame_[2] = *currentFrame_[1];
|
||||
|
||||
while (currentFrame_[2]->peekNext()->time() < time) {
|
||||
currentFrameValid_ = false;
|
||||
if (!currentFrame_[2]->hasNext())
|
||||
break;
|
||||
currentFrame_[2]->next();
|
||||
}
|
||||
|
||||
if (!currentFrameValid_) {
|
||||
*currentFrame_[1] = *currentFrame_[2];
|
||||
if ((currentFrame_[1]->hasPrevious()) &&
|
||||
(time < currentFrame_[2]->peekNext()->time()))
|
||||
currentFrame_[1]->previous();
|
||||
|
||||
*currentFrame_[0] = *currentFrame_[1];
|
||||
if (currentFrame_[0]->hasPrevious())
|
||||
currentFrame_[0]->previous();
|
||||
|
||||
*currentFrame_[3] = *currentFrame_[2];
|
||||
if (currentFrame_[3]->hasNext())
|
||||
currentFrame_[3]->next();
|
||||
|
||||
currentFrameValid_ = true;
|
||||
splineCacheIsValid_ = false;
|
||||
}
|
||||
|
||||
// cout << "Time = " << time << " : " << currentFrame_[0]->peekNext()->time()
|
||||
// << " , " << currentFrame_[1]->peekNext()->time() << " , " <<
|
||||
// currentFrame_[2]->peekNext()->time() << " , " <<
|
||||
// currentFrame_[3]->peekNext()->time() << endl;
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::updateSplineCache() {
|
||||
Vec delta = currentFrame_[2]->peekNext()->position() -
|
||||
currentFrame_[1]->peekNext()->position();
|
||||
v1 = 3.0 * delta - 2.0 * currentFrame_[1]->peekNext()->tgP() -
|
||||
currentFrame_[2]->peekNext()->tgP();
|
||||
v2 = -2.0 * delta + currentFrame_[1]->peekNext()->tgP() +
|
||||
currentFrame_[2]->peekNext()->tgP();
|
||||
splineCacheIsValid_ = true;
|
||||
}
|
||||
|
||||
/*! Interpolate frame() at time \p time (expressed in seconds).
|
||||
interpolationTime() is set to \p time and frame() is set accordingly.
|
||||
|
||||
If you simply want to change interpolationTime() but not the frame() state,
|
||||
use setInterpolationTime() instead.
|
||||
|
||||
Emits the interpolated() signal and makes the frame() emit the
|
||||
Frame::interpolated() signal. */
|
||||
void KeyFrameInterpolator::interpolateAtTime(qreal time) {
|
||||
setInterpolationTime(time);
|
||||
|
||||
if ((keyFrame_.isEmpty()) || (!frame()))
|
||||
return;
|
||||
|
||||
if (!valuesAreValid_)
|
||||
updateModifiedFrameValues();
|
||||
|
||||
updateCurrentKeyFrameForTime(time);
|
||||
|
||||
if (!splineCacheIsValid_)
|
||||
updateSplineCache();
|
||||
|
||||
qreal alpha;
|
||||
qreal dt = currentFrame_[2]->peekNext()->time() -
|
||||
currentFrame_[1]->peekNext()->time();
|
||||
if (dt == 0.0)
|
||||
alpha = 0.0;
|
||||
else
|
||||
alpha = (time - currentFrame_[1]->peekNext()->time()) / dt;
|
||||
|
||||
// Linear interpolation - debug
|
||||
// Vec pos = alpha*(currentFrame_[2]->peekNext()->position()) +
|
||||
// (1.0-alpha)*(currentFrame_[1]->peekNext()->position());
|
||||
Vec pos =
|
||||
currentFrame_[1]->peekNext()->position() +
|
||||
alpha * (currentFrame_[1]->peekNext()->tgP() + alpha * (v1 + alpha * v2));
|
||||
Quaternion q = Quaternion::squad(
|
||||
currentFrame_[1]->peekNext()->orientation(),
|
||||
currentFrame_[1]->peekNext()->tgQ(), currentFrame_[2]->peekNext()->tgQ(),
|
||||
currentFrame_[2]->peekNext()->orientation(), alpha);
|
||||
frame()->setPositionAndOrientationWithConstraint(pos, q);
|
||||
|
||||
Q_EMIT interpolated();
|
||||
}
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the KeyFrameInterpolator.
|
||||
|
||||
The resulting QDomElement holds the KeyFrameInterpolator parameters as well as
|
||||
the path keyFrames (if the keyFrame is defined by a pointer to a Frame, use its
|
||||
current value).
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument
|
||||
factory used to create QDomElement.
|
||||
|
||||
Use initFromDOMElement() to restore the ManipulatedFrame state from the
|
||||
resulting QDomElement.
|
||||
|
||||
See Vec::domElement() for a complete example. See also
|
||||
Quaternion::domElement(), Camera::domElement()...
|
||||
|
||||
Note that the Camera::keyFrameInterpolator() are automatically saved by
|
||||
QGLViewer::saveStateToFile() when a QGLViewer is closed. */
|
||||
QDomElement KeyFrameInterpolator::domElement(const QString &name,
|
||||
QDomDocument &document) const {
|
||||
QDomElement de = document.createElement(name);
|
||||
int count = 0;
|
||||
Q_FOREACH (KeyFrame *kf, keyFrame_) {
|
||||
Frame fr(kf->position(), kf->orientation());
|
||||
QDomElement kfNode = fr.domElement("KeyFrame", document);
|
||||
kfNode.setAttribute("index", QString::number(count));
|
||||
kfNode.setAttribute("time", QString::number(kf->time()));
|
||||
de.appendChild(kfNode);
|
||||
++count;
|
||||
}
|
||||
de.setAttribute("nbKF", QString::number(keyFrame_.count()));
|
||||
de.setAttribute("time", QString::number(interpolationTime()));
|
||||
de.setAttribute("speed", QString::number(interpolationSpeed()));
|
||||
de.setAttribute("period", QString::number(interpolationPeriod()));
|
||||
DomUtils::setBoolAttribute(de, "closedPath", closedPath());
|
||||
DomUtils::setBoolAttribute(de, "loop", loopInterpolation());
|
||||
return de;
|
||||
}
|
||||
|
||||
/*! Restores the KeyFrameInterpolator state from a \c QDomElement created by
|
||||
domElement().
|
||||
|
||||
Note that the frame() pointer is not included in the domElement(): you need to
|
||||
setFrame() after this method to attach a Frame to the KeyFrameInterpolator.
|
||||
|
||||
See Vec::initFromDOMElement() for a complete code example.
|
||||
|
||||
See also Camera::initFromDOMElement() and Frame::initFromDOMElement(). */
|
||||
void KeyFrameInterpolator::initFromDOMElement(const QDomElement &element) {
|
||||
qDeleteAll(keyFrame_);
|
||||
keyFrame_.clear();
|
||||
QDomElement child = element.firstChild().toElement();
|
||||
while (!child.isNull()) {
|
||||
if (child.tagName() == "KeyFrame") {
|
||||
Frame fr;
|
||||
fr.initFromDOMElement(child);
|
||||
qreal time = DomUtils::qrealFromDom(child, "time", 0.0);
|
||||
addKeyFrame(fr, time);
|
||||
}
|
||||
|
||||
child = child.nextSibling().toElement();
|
||||
}
|
||||
|
||||
// #CONNECTION# Values cut pasted from constructor
|
||||
setInterpolationTime(DomUtils::qrealFromDom(element, "time", 0.0));
|
||||
setInterpolationSpeed(DomUtils::qrealFromDom(element, "speed", 1.0));
|
||||
setInterpolationPeriod(DomUtils::intFromDom(element, "period", 40));
|
||||
setClosedPath(DomUtils::boolFromDom(element, "closedPath", false));
|
||||
setLoopInterpolation(DomUtils::boolFromDom(element, "loop", false));
|
||||
|
||||
// setFrame(NULL);
|
||||
pathIsValid_ = false;
|
||||
valuesAreValid_ = false;
|
||||
currentFrameValid_ = false;
|
||||
|
||||
stopInterpolation();
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
//////////// KeyFrame private class implementation /////////
|
||||
KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame &fr, qreal t)
|
||||
: time_(t), frame_(NULL) {
|
||||
p_ = fr.position();
|
||||
q_ = fr.orientation();
|
||||
}
|
||||
|
||||
KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame *fr, qreal t)
|
||||
: time_(t), frame_(fr) {
|
||||
updateValuesFromPointer();
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::KeyFrame::updateValuesFromPointer() {
|
||||
p_ = frame()->position();
|
||||
q_ = frame()->orientation();
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::KeyFrame::computeTangent(
|
||||
const KeyFrame *const prev, const KeyFrame *const next) {
|
||||
tgP_ = 0.5 * (next->position() - prev->position());
|
||||
tgQ_ = Quaternion::squadTangent(prev->orientation(), q_, next->orientation());
|
||||
}
|
||||
|
||||
void KeyFrameInterpolator::KeyFrame::flipOrientationIfNeeded(
|
||||
const Quaternion &prev) {
|
||||
if (Quaternion::dot(prev, q_) < 0.0)
|
||||
q_.negate();
|
||||
}
|
||||
|
||||
#endif // DOXYGEN
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
|
|
|||
|
|
@ -19,456 +19,9 @@
|
|||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
#ifndef CGAL_HEADER_ONLY
|
||||
|
||||
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
#include <CGAL/Qt/qglviewer.h>
|
||||
#include <CGAL/Qt/manipulatedCameraFrame_impl.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),
|
||||
qreal(0.2) * sceneRadius);
|
||||
Vec trans(0.0, 0.0, -coef * delta);
|
||||
translate(inverseTransformOf(trans));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*! Overloading of ManipulatedFrame::mouseMoveEvent().
|
||||
|
||||
Motion depends on mouse binding (see <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;
|
||||
}
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
|
|
|||
|
|
@ -19,543 +19,9 @@
|
|||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
#ifndef CGAL_HEADER_ONLY
|
||||
|
||||
#include <CGAL/Qt/manipulatedFrame.h>
|
||||
#include <CGAL/Qt/camera.h>
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
||||
#include <CGAL/Qt/qglviewer.h>
|
||||
#include <CGAL/Qt/manipulatedFrame_impl.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
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
|
|
|||
|
|
@ -19,55 +19,9 @@
|
|||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
#ifndef CGAL_HEADER_ONLY
|
||||
|
||||
#include <CGAL/Qt/mouseGrabber.h>
|
||||
#include <CGAL/Qt/mouseGrabber_impl.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();
|
||||
}
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
|
|
|||
|
|
@ -1,359 +0,0 @@
|
|||
/* XPM */
|
||||
static const char * qglviewer_icon[] = {
|
||||
"100 100 256 2",
|
||||
" c None",
|
||||
". c #0A0B27",
|
||||
"+ c #090B2C",
|
||||
"@ c #150C12",
|
||||
"# c #080F34",
|
||||
"$ c #1A0E1A",
|
||||
"% c #220D0B",
|
||||
"& c #260B0B",
|
||||
"* c #230E1C",
|
||||
"= c #12113E",
|
||||
"- c #2C0D10",
|
||||
"; c #2F0D09",
|
||||
"> c #310D14",
|
||||
", c #1A123A",
|
||||
"' c #261025",
|
||||
") c #17134B",
|
||||
"! c #151453",
|
||||
"~ c #1B1733",
|
||||
"{ c #14135E",
|
||||
"] c #281430",
|
||||
"^ c #0E1867",
|
||||
"/ c #3B1215",
|
||||
"( c #32132A",
|
||||
"_ c #1D1849",
|
||||
": c #121C58",
|
||||
"< c #431014",
|
||||
"[ c #141974",
|
||||
"} c #3A152E",
|
||||
"| c #201A5E",
|
||||
"1 c #1D204C",
|
||||
"2 c #401431",
|
||||
"3 c #261960",
|
||||
"4 c #48171C",
|
||||
"5 c #411B21",
|
||||
"6 c #371B45",
|
||||
"7 c #2A1C6B",
|
||||
"8 c #0E229B",
|
||||
"9 c #52171D",
|
||||
"0 c #441A35",
|
||||
"a c #1B2582",
|
||||
"b c #65150F",
|
||||
"c c #2D2076",
|
||||
"d c #4D1B3A",
|
||||
"e c #57182F",
|
||||
"f c #3E1F54",
|
||||
"g c #5E1924",
|
||||
"h c #2E2567",
|
||||
"i c #002BD1",
|
||||
"j c #002AD9",
|
||||
"k c #6D1A13",
|
||||
"l c #36237A",
|
||||
"m c #002FCD",
|
||||
"n c #701A0D",
|
||||
"o c #462060",
|
||||
"p c #651C23",
|
||||
"q c #322581",
|
||||
"r c #552429",
|
||||
"s c #581E41",
|
||||
"t c #671E1B",
|
||||
"u c #771911",
|
||||
"v c #6C1B2B",
|
||||
"w c #562341",
|
||||
"x c #342A79",
|
||||
"y c #3C2484",
|
||||
"z c #062FE6",
|
||||
"A c #68212B",
|
||||
"B c #4D236B",
|
||||
"C c #612045",
|
||||
"D c #42267B",
|
||||
"E c #811B10",
|
||||
"F c #7C1E0E",
|
||||
"G c #0033F1",
|
||||
"H c #0032F9",
|
||||
"I c #3A298E",
|
||||
"J c #472872",
|
||||
"K c #72202A",
|
||||
"L c #4F2774",
|
||||
"M c #652449",
|
||||
"N c #442890",
|
||||
"O c #1F37A8",
|
||||
"P c #87200E",
|
||||
"Q c #852014",
|
||||
"R c #402B98",
|
||||
"S c #6C244D",
|
||||
"T c #7D2230",
|
||||
"U c #8F1F12",
|
||||
"V c #70254A",
|
||||
"W c #542A7E",
|
||||
"X c #212FF2",
|
||||
"Y c #6E2E28",
|
||||
"Z c #442E9B",
|
||||
"` c #772834",
|
||||
" . c #971E15",
|
||||
".. c #552B86",
|
||||
"+. c #693132",
|
||||
"@. c #702A46",
|
||||
"#. c #7B2645",
|
||||
"$. c #862435",
|
||||
"%. c #79264E",
|
||||
"&. c #5A2B88",
|
||||
"*. c #1B3AE9",
|
||||
"=. c #9A2211",
|
||||
"-. c #4E2EA0",
|
||||
";. c #1E3BDE",
|
||||
">. c #722F3D",
|
||||
",. c #5E2C91",
|
||||
"'. c #592F91",
|
||||
"). c #7A322A",
|
||||
"!. c #1D42D7",
|
||||
"~. c #902539",
|
||||
"{. c #7D2A52",
|
||||
"]. c #862E26",
|
||||
"^. c #822853",
|
||||
"/. c #922B20",
|
||||
"(. c #4E34AA",
|
||||
"_. c #A62411",
|
||||
":. c #5D309A",
|
||||
"<. c #59387B",
|
||||
"[. c #5631AB",
|
||||
"}. c #5533A6",
|
||||
"|. c #912840",
|
||||
"1. c #8C2B3F",
|
||||
"2. c #AE2215",
|
||||
"3. c #862C57",
|
||||
"4. c #5D32A8",
|
||||
"5. c #882C53",
|
||||
"6. c #5C34A2",
|
||||
"7. c #6231A2",
|
||||
"8. c #A32A18",
|
||||
"9. c #5C398B",
|
||||
"0. c #4D4480",
|
||||
"a. c #982A3F",
|
||||
"b. c #8D2B5A",
|
||||
"c. c #962C44",
|
||||
"d. c #902C56",
|
||||
"e. c #7A3B43",
|
||||
"f. c #3249C3",
|
||||
"g. c #B42812",
|
||||
"h. c #583E9B",
|
||||
"i. c #BA2516",
|
||||
"j. c #524297",
|
||||
"k. c #55448B",
|
||||
"l. c #5E3BA3",
|
||||
"m. c #9E2C47",
|
||||
"n. c #5A4676",
|
||||
"o. c #873847",
|
||||
"p. c #404E94",
|
||||
"q. c #932F59",
|
||||
"r. c #BE2810",
|
||||
"s. c #992D5B",
|
||||
"t. c #5941A4",
|
||||
"u. c #8A3F36",
|
||||
"v. c #9D2F58",
|
||||
"w. c #78415D",
|
||||
"x. c #A72D4D",
|
||||
"y. c #A2304B",
|
||||
"z. c #C72815",
|
||||
"A. c #C12C13",
|
||||
"B. c #654298",
|
||||
"C. c #654780",
|
||||
"D. c #8F385F",
|
||||
"E. c #98345D",
|
||||
"F. c #2F51E3",
|
||||
"G. c #A93724",
|
||||
"H. c #A0325B",
|
||||
"I. c #CA2B10",
|
||||
"J. c #A93054",
|
||||
"K. c #A6305D",
|
||||
"L. c #D42A00",
|
||||
"M. c #D22914",
|
||||
"N. c #89415D",
|
||||
"O. c #6546A5",
|
||||
"P. c #AB3255",
|
||||
"Q. c #DC2806",
|
||||
"R. c #A3355D",
|
||||
"S. c #AA325A",
|
||||
"T. c #A13F2F",
|
||||
"U. c #6A4A8E",
|
||||
"V. c #A53754",
|
||||
"W. c #DE2A00",
|
||||
"X. c #CF300A",
|
||||
"Y. c #D62C0E",
|
||||
"Z. c #A8385B",
|
||||
"`. c #4B54C3",
|
||||
" + c #964356",
|
||||
".+ c #E82903",
|
||||
"++ c #8F4757",
|
||||
"@+ c #DF2C14",
|
||||
"#+ c #D93011",
|
||||
"$+ c #A23D62",
|
||||
"%+ c #8C4E44",
|
||||
"&+ c #E32F00",
|
||||
"*+ c #6E4CA5",
|
||||
"=+ c #6E4F9B",
|
||||
"-+ c #A04259",
|
||||
";+ c #9D4266",
|
||||
">+ c #D03716",
|
||||
",+ c #E3300D",
|
||||
"'+ c #6A51A7",
|
||||
")+ c #BA3F2F",
|
||||
"!+ c #EC2E07",
|
||||
"~+ c #A9405F",
|
||||
"{+ c #EB2E13",
|
||||
"]+ c #EF3100",
|
||||
"^+ c #4A5CDC",
|
||||
"/+ c #984A68",
|
||||
"(+ c #B54536",
|
||||
"_+ c #4563C9",
|
||||
":+ c #F62E03",
|
||||
"<+ c #EF310B",
|
||||
"[+ c #E73411",
|
||||
"}+ c #AA4B3E",
|
||||
"|+ c #F62E10",
|
||||
"1+ c #CE401F",
|
||||
"2+ c #D33D23",
|
||||
"3+ c #7455A7",
|
||||
"4+ c #C54426",
|
||||
"5+ c #A5496A",
|
||||
"6+ c #F93106",
|
||||
"7+ c #A45048",
|
||||
"8+ c #CD412C",
|
||||
"9+ c #F2350E",
|
||||
"0+ c #AA4A64",
|
||||
"a+ c #5266C7",
|
||||
"b+ c #DE411E",
|
||||
"c+ c #EF3C18",
|
||||
"d+ c #755FAD",
|
||||
"e+ c #C94C34",
|
||||
"f+ c #C14F3F",
|
||||
"g+ c #DB452E",
|
||||
"h+ c #AB526E",
|
||||
"i+ c #EB421F",
|
||||
"j+ c #AA566A",
|
||||
"k+ c #A55B6B",
|
||||
"l+ c #AA5873",
|
||||
"m+ c #E94824",
|
||||
"n+ c #D94D35",
|
||||
"o+ c #DB4F31",
|
||||
"p+ c #5B71DE",
|
||||
"q+ c #A85F76",
|
||||
"r+ c #EC4C31",
|
||||
"s+ c #E2522E",
|
||||
"t+ c #E84F30",
|
||||
"u+ c #CD5D42",
|
||||
"v+ c #6976CD",
|
||||
"w+ c #D9583F",
|
||||
"x+ c #E55537",
|
||||
"y+ c #E7573E",
|
||||
"z+ c #D65F4C",
|
||||
"A+ c #CA6457",
|
||||
"B+ c #E35C3E",
|
||||
"C+ c #D1634E",
|
||||
"D+ c #E26447",
|
||||
"E+ c #E1674E",
|
||||
"F+ c #DE6F57",
|
||||
"G+ c #DE705E",
|
||||
" ",
|
||||
" ",
|
||||
" C+ ",
|
||||
" ` >.o. 1+X.u+ ",
|
||||
" e.A K K T $.T ++ 1+L.L.X. ",
|
||||
" A A v ` T T T 1.~.++ X.L.L.L.W.1+ ",
|
||||
" g A K K T T $.$.1.$.1. >+L.L.L.L.L.L. ",
|
||||
" r g A v v T T $.1.$.$.|.|. + u+X.L.L.L.L.W.W.W.2+ ",
|
||||
" f r g g K K v T T T |.|.1.1.c.-+ u+X.L.L.L.Q.L.Q.Q.W.W. ",
|
||||
" 6 o B C. 4 9 p p v T T T 1.~.$.1.~.a.~.1. u+X.L.L.L.Q.L.W.L.W.W.W.o+ ",
|
||||
" f o B C. 4 9 9 g v v T T $.1.1.~.|.c.c.c.m. 4+L.L.L.Q.L.Q.Q.W.&+W.W.,+b+ ",
|
||||
" f o J L U. 9 9 A v K T $.1.1.$.|.|.c.c.c.a.a.q+ 4+L.L.L.L.L.Q.W.W.&+L.&+&+&+W. ",
|
||||
" f o B W L <. < 9 g A e.` ` $.$.|.$.1.~.a.a.c.c.a.k+ 1+L.L.Q.Q.W.L.W.L.&+&+&+,+&+&+w+ ",
|
||||
" 6 f B L L .. 4 4 9 o.$.|.$.c.a.c.c.m.m.y.c.q+ u+L.W.Q.L.W.&+&+W.&+,+&+&+&+.+s+ ",
|
||||
" p. f o L W W .. / < r 1.|.|.c.c.c.c.y.m.y.a.k+ X.L.W.L.,+,+&+,+&+&+&+[+&+&+b+ ",
|
||||
" 3 6 B L W W &.9. / o.|.|.c.a.m.y.a.a.y.y.q+ e+W.W.&+W.W.,+&+&+&+&+.+&+!+b+ ",
|
||||
" a : f o L L ....&.B. / ++a.a.m.c.a.y.y.a.y.y.q+ u+W.W.W.&+.+&+{+&+[+&+!+&+]+&+ ",
|
||||
" O ^ 1 f J W ....&.'.U. / 5 k+c.m.c.m.y.m.y.y.y.y. L.&+&+&+&+&+&+!+]+!+[+!+!+!+ ",
|
||||
" f.[ ) $ <.L W ..&.'.'. / k+c.m.a.m.y.a.y.y.y.y. b+&+,+&+.+&+&+&+[+]+&+!+&+!+E+ ",
|
||||
" `.8 ! = $ <.W ..&.'.'.B. - k+y.a.y.y.a.y.x.y.x.V. b+.+&+!+&+[+!+!+]+!+!+!+9+!+B+ ",
|
||||
" a+m { = # $ ....'.'.'.,.=+ - k+m.y.y.y.V.y.x.y.x.V. w+!+&+!+]+&+[+&+!+[+<+!+]+&+B+ ",
|
||||
" v+m 8 ) # $ U...'.'.'.,.:. - -+m.m.y.V.x.y.y.x.m.0+ s+&+!+[+&+!+]+<+!+]+]+[+<+9+B+ ",
|
||||
" ;.m ) = + $ 9.&.'.l.l.,.*+ & -+y.y.x.x.J.x.y.x.y.0+ B+&+]+!+<+!+!+[+]+!+]+<+]+<+B+ ",
|
||||
" ;.i [ = + . $ &.'.,.,.,.:.*+ & 0+y.y.x.y.y.y.J.V.x.l+ s+!+!+!+[+<+]+]+!+]+]+]+!+]+B+ ",
|
||||
" ;.z ^+1 # + $ =+l.'.l.7.:.'. % y.x.y.x.x.P.y.x.P.y. B+.+[+]+!+[+]+]+<+]+]+]+]+<+B+ ",
|
||||
" ;.z ;. + + . $ '.:.:.:.:.:.3+ & V.y.x.y.x.J.P.P.V.Z. x+<+]+]+]+:+]+]+]+9+<+9+9+]+ ",
|
||||
" p+*.G G p+ + . @ U.:.:.7.:.6.7. % h+P.V.P.P.y.J.P.x.y.0+ s+]+9+]+]+9+9+]+]+]+]+]+9+<+ ",
|
||||
" f. p+F.H H G F. . . . $ l.:.:.:.6.:.O. & 0+x.J.J.V.P.x.x.P.P.h+ x+<+]+<+]+]+]+9+]+]+]+9+]+<+ ",
|
||||
" i G X X X G H ;. . . $ =+:.:.6.l.6.:.3+ % V.y.y.x.P.V.V.P.P.J. m+!+]+]+]+9+]+|+9+|+]+9+]+c+ ",
|
||||
" m z G H G H G p+ . . * O.6.6.:.7.6.:. & V.P.P.P.x.P.P.V.V.V. i+]+]+<+|+:+]+]+:+]+:+:+]+m+ ",
|
||||
" i z G G G G F. . . * 3+7.7.6.6.6.6.3+ % 0+x.P.x.V.x.x.x.x.P.0+ [+9+]+]+]+|+]+]+]+9+9+9+]+m+ ",
|
||||
" i z G G H *. . . * :.6.6.7.6.7.:. & 0+P.P.P.P.P.P.P.P.V. :+:+]+]+9+]+9+9+|+:+:+:+]+B+ ",
|
||||
" i z G G G p+ . * *+7.}.6.4.6.6.*+ % V.y.P.x.P.x.P.P.P.V. F+]+|+9+:+]+:+:+]+]+:+|+|+:+B+ ",
|
||||
" i z G G ^+ . ' n.4.6.7.4.7.4.6. & 0+x.P.P.P.P.P.P.P.P.~+ s+9+]+:+]+|+6+]+]+|+9+]+]+9+ ",
|
||||
" i z G F. . * O.4.6.6.6.6.4.*+ & ~+P.P.P.P.P.P.P.P.Z.l+ s+6+9+|+]+9+|+|+]+6+:+6+6+9+ ",
|
||||
" j z !. . ] J 4.6.4.[.4.[.l. & P.P.P.P.P.P.P.P.P.V. c+9+6+]+|+6+9+]+6+9+|+9+|+i+ ",
|
||||
" a+i ;. . ' l.[.(.6.6.6.4.*+ & h+P.P.S.P.S.P.P.P.P.0+ :+|+]+]+|+6+|+9+6+]+6+]+9+t+ ",
|
||||
" a+!. . ] t.(.6.7.4.4.[.6. & S.P.P.P.P.P.S.S.S.S.q+ D+9+9+|+6+]+9+]+6+|+6+9+6+:+E+ ",
|
||||
" . 3+7.[.6.(.6.6.6.*+ & h+S.S.P.S.~+P.P.P.P.~+ r+6+]+9+6+6+6+9+]+9+6+9+6+9+ ",
|
||||
" . 6.(.6.4.4.4.(.6. & ~+P.P.P.P.P.P.P.P.P.h+ c+6+|+6+9+6+9+6+|+6+|+6+9+9+ ",
|
||||
" . l.[.[.(.6.(.4.(.3+ % l+S.V.~+P.P.P.~+P.P.S. 9+]+|+6+9+6+|+9+9+]+|+6+9+t+ ",
|
||||
" ~ *+(.6.4.4.4.6.4.l. % ~+P.S.P.S.P.S.P.S.S.0+ B+9+6+9+9+6+|+9+6+6+|+]+]+|+D+ ",
|
||||
" ~ '+(.[.(.(.(.4.4.6.d+ & S.P.S.P.S.P.S.P.P.~+ i+9+6+6+9+6+6+:+9+:+6+|+6+c+ ",
|
||||
" = '+(.[.(.(.[.4.[.(.O. % ~+~+S.Z.S.Z.S.P.Z.S.~+ 9+6+6+9+6+9+:+9+6+6+9+6+]+m+ ",
|
||||
" _ '+Z }.}.4.6.(.(.[.4. & q+S.S.S.~+S.~+S.S.S.S.l+ x+]+9+6+9+6+9+6+9+9+6+9+|+6+B+ ",
|
||||
" ) h h.Z -.-.}.(.(.[.[.(.'+ & R.Z.Z.S.S.S.S.~+Z.Z.R. r+|+6+|+6+9+6+|+6+6+9+6+9+]+F+ ",
|
||||
" _ 3 x k.j.I Z -.Z Z }.4.(.(.4.t. % l+S.S.Z.Z.Z.S.S.S.S.Z.h+ F+]+9+6+9+6+9+6+9+9+6+9+6+6+c+ ",
|
||||
" ) 3 7 q I I Z N }.}.}.(.}.}.(. & Z.Z.S.Z.Z.S.Z.Z.Z.Z.~+ B+6+6+9+6+|+6+9+6+6+|+6+6+9+y+ ",
|
||||
" = ! 7 c q I N Z Z Z Z -.}.}.}.'+ % l+S.Z.S.S.Z.S.S.S.S.Z.h+ c+9+6+9+6+9+6+|+9+6+9+9+6+6+ ",
|
||||
" _ ) 3 l q N N Z N -.}.Z -.(.O. & R.R.R.S.K.S.Z.Z.Z.V.$+ D+6+6+9+6+9+6+9+6+6+9+6+6+9+m+ ",
|
||||
" _ 3 3 l q I N Z Z -.-.}.}.Z & $+S.S.Z.Z.Z.K.K.K.K.S.h+ i+9+6+9+6+9+6+9+9+6+9+9+6+9+B+ ",
|
||||
" _ | 7 l y I I N N Z -.(.-.d+ % l+R.R.R.R.S.K.Z.S.S.S.~+ F+9+6+9+|+6+6+9+6+6+9+|+6+6+9+ ",
|
||||
" _ 3 7 c q I R Z :.Z Z -.'+ & R.K.K.K.R.Z.S.Z.Z.Z.K.l+ r+9+6+6+9+6+6+6+9+6+6+9+9+6+t+ ",
|
||||
" ) | 7 l y y I N R Z -.D & ;+R.Z.Z.S.K.K.R.Z.S.S.5+ G+6+6+9+9+6+9+9+9+6+6+9+6+6+]+E+ ",
|
||||
" 3 x c I y I Z Z Z h.' & 5+K.K.R.R.S.K.S.K.R.R.S. t+9+6+|+9+6+6+6+6+9+6+9+|+|+i+ ",
|
||||
" | c l q I R I Z h. $ & $+R.s.K.R.R.Z.R.K.K.K.5+ 9+6+9+6+6+6+c+6+9+6+9+|+6+6+x+ ",
|
||||
" h 7 l q y N N j. * & H.H.R.s.K.H.H.K.Z.K.S.Z. r+9+6+9+6+6+6+6+6+6+6+9+9+9+c+ ",
|
||||
" 0.l q y I y '+ ' ; ;+v.v.K.R.H.K.K.v.H.Z.R.5+ G+9+|+6+c+6+c+6+c+6+6+9+6+|+6+B+ ",
|
||||
" 0.x D j. ' & ;+s.R.s.K.H.s.s.R.K.K.K.$+ m+9+9+|+9+6+9+6+6+c+6+6+9+9+6+ ",
|
||||
" ( ( ; ;+v.$+s.R.v.H.R.K.K.R.s.R. E+9+|+9+6+6+c+6+c+6+6+9+9+6+6+t+ ",
|
||||
" ( } & ; /+5.v.v.v.s.H.R.H.H.s.K.R.5+ i+9+|+9+9+6+6+6+c+6+6+6+9+6+9+ ",
|
||||
" ( } - ; /+q.s.s.s.E.H.v.K.v.R.K.R.$+ y+|+|+9+9+|+9+c+6+6+c+9+9+|+9+x+ ",
|
||||
" ( 2 0 - ; D.d.q.q.E.v.v.s.s.H.s.R.v.$+ 9+9+|+|+9+6+6+9+c+6+6+6+c+6+9+ ",
|
||||
" } 0 0 ; ; 3.q.b.q.q.s.E.s.H.H.v.K.v.H.5+ r+9+|+9+9+6+c+9+6+6+c+9+9+|+|+x+ ",
|
||||
" } 0 d d ; < N.5.3.q.q.q.q.s.E.E.E.s.v.s.H.5+ D+9+9+|+9+9+6+c+9+6+|+6+9+9+9+9+ ",
|
||||
" } 2 s s s M w.w.e @.{.^.3.3.b.d.q.q.q.s.v.v.s.H.s.$+ i+9+|+9+|+c+6+|+6+9+9+9+9+6+|+y+ ",
|
||||
" 0 0 d s C M V V V ^.^.5.5.5.3.d.q.q.q.q.s.v.E.R.E. r+9+c+|+9+|+9+c+c+|+|+9+6+9+6+i+ ",
|
||||
" 2 2 s w C C V V %.{.{.5.b.5.q.3.d.q.q.s.E.E.v.s.q+ w+9+9+9+9+9+9+|+|+|+9+9+|+9+|+9+E+ ",
|
||||
" 0 0 d w C C S V V {.%.#.3.5.d.d.d.q.q.s.s.E.E.q+ E+c+9+9+c+9+c+9+|+9+|+9+9+9+9+|+i+ ",
|
||||
" } d s s C M V S %.%.^.3.3.3.3.3.d.d.q.d.q.s./+ i+!+c+9+9+9+|+9+9+|+9+|+9+|+|+9+G+ ",
|
||||
" 0 d d e M M V S {.{.^.3.3.d.d.D.d.d.q.q.v./+ r+[+|+{+9+c+9+9+9+9+9+9+|+9+9+9+B+ ",
|
||||
" d s s s V S V S %.{.{.5.3.3.d.d.q.q.q./+ o+[+!+c+9+|+9+c+9+c+9+c+9+9+|+9+m+ ",
|
||||
" 0 s C s V V %.V {.^.{.5.5.3.3.D.d.q./+ n+{+[+{+[+c+c+|+9+9+9+|+9+c+9+|+|+G+ ",
|
||||
" d w s M M M V %.%.{.3.{.3.d.d.d.q./+ w+[+[+{+&+{+{+{+9+c+9+c+9+c+|+9+c+B+ ",
|
||||
" w s @.S S S %.%.{.5.5.^.3.b.D. G+@+[+[+[+c+[+<+[+9+c+|+c+9+9+c+|+m+ ",
|
||||
" w s C M V V S %.^.{.{.3.5.N. C+@+[+{+&+{+&+<+[+{+{+{+9+9+9+c+9+c+G+ ",
|
||||
" w M M S V {.V ^.5.5.{. w+,+,+@+[+[+{+[+{+c+9+<+[+9+9+9+9+{+D+ ",
|
||||
" M @.V V {.{.#.++ w+@+@+@+[+{+[+[+{+[+&+[+[+{+c+{+{+{+x+ ",
|
||||
" w.>.p t b ). 8+@+,+[+,+@+{+[+[+&+{+{+{+[+!+[+9+9+b+ ",
|
||||
" +.b b n u %+ 2+#+,+@+Q.@+[+@+,+[+[+[+[+[+<+[+<+{+i+ ",
|
||||
" b b k u u u+M.M.#+#+@+,+@+[+@+[+{+[+[+{+[+[+[+[+{+z+ ",
|
||||
" b b n u u /. A+2+M.#+#+#+,+@+@+,+,+[+@+,+[+{+[+{+{+[+<+w+ ",
|
||||
" t k k F u P /.7+ A+e+z.#+#+@+M.#+#+,+#+@+Q.[+@+@+,+{+&+[+{+[+B+ ",
|
||||
" Y n u u E Q U U T.7+ f+4+I.M.>+M.M.Y.@+M.@+#+@+,+@+@+[+[+[+[+[+[+[+n+ ",
|
||||
" k n F Q Q U .=._.8.G.(+(+)+g.i.z.r.I.M.I.>+I.#+#+#+#+#+#+,+,+Q.,+@+,+@+{+{+o+ ",
|
||||
" k u u E U U U =.=._.2.g.i.i.i.A.r.z.z.I.M.Y.#+>+#+#+#+Q.@+@+@+@+,+[+@+[+,+s+ ",
|
||||
" ).n E E P U =.=.=.8._._.g.g.i.i.A.A.z.I.z.M.Y.M.M.M.#+#+#+,+@+@+@+[+,+@+g+ ",
|
||||
" u u Q U U U =._._.2.g.g.g.A.A.A.z.A.z.M.I.I.Y.#+#+#+#+@+#+,+,+,+,+@+o+ ",
|
||||
" ).F Q U U =.=.=.8._.2.2.g.r.r.r.I.I.I.z.M.I.M.#+M.#+#+#+#+@+@+@+,+w+ ",
|
||||
" Q Q P U U =._._.2._.g.g.g.i.A.A.A.A.I.I.M.X.M.#+@+#+@+@+@+,+#+z+ ",
|
||||
" u.U /.U U 8._.8._.2.2.g.g.i.A.A.I.z.I.I.M.I.Y.>+@+#+#+#+#+#+A+ ",
|
||||
" ].U U =.=.=.2.2._.g.i.i.A.i.A.A.I.z.I.>+Y.>+Y.Y.#+#+#+n+ ",
|
||||
" /.U .=.=._._.2.2.g.i.A.A.z.A.I.I.M.I.M.M.M.M.Y.M.z+ ",
|
||||
" /.=.8._._.2._.g.g.g.g.i.A.z.A.z.I.I.Y.I.X.#+8+ ",
|
||||
" /.8.8._._.2.2.i.i.A.i.A.z.z.I.I.I.M.M.8+A+ ",
|
||||
" }+8._.2.2.g.2.A.i.A.A.A.>+z.z.z.4+A+ ",
|
||||
" T.)+_.g.2.A.r.A.A.r.A.4+z+ ",
|
||||
" }+}+(+(+f+f+A+ ",
|
||||
" ",
|
||||
" "};
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -19,507 +19,9 @@
|
|||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
#ifndef CGAL_HEADER_ONLY
|
||||
|
||||
#include <CGAL/Qt/quaternion.h>
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
#include <stdlib.h> // RAND_MAX
|
||||
#include <CGAL/Qt/quaternion_impl.h>
|
||||
|
||||
// All the methods are declared inline in Quaternion.h
|
||||
using namespace qglviewer;
|
||||
using namespace std;
|
||||
|
||||
/*! Constructs a Quaternion that will rotate from the \p from direction to the
|
||||
\p to direction.
|
||||
|
||||
Note that this rotation is not uniquely defined. The selected axis is usually
|
||||
orthogonal to \p from and \p to, minimizing the rotation angle. This method is
|
||||
robust and can handle small or almost identical vectors. */
|
||||
Quaternion::Quaternion(const Vec &from, const Vec &to) {
|
||||
const qreal epsilon = 1E-10;
|
||||
|
||||
const qreal fromSqNorm = from.squaredNorm();
|
||||
const qreal toSqNorm = to.squaredNorm();
|
||||
// Identity Quaternion when one vector is null
|
||||
if ((fromSqNorm < epsilon) || (toSqNorm < epsilon)) {
|
||||
q[0] = q[1] = q[2] = 0.0;
|
||||
q[3] = 1.0;
|
||||
} else {
|
||||
Vec axis = cross(from, to);
|
||||
const qreal axisSqNorm = axis.squaredNorm();
|
||||
|
||||
// Aligned vectors, pick any axis, not aligned with from or to
|
||||
if (axisSqNorm < epsilon)
|
||||
axis = from.orthogonalVec();
|
||||
|
||||
qreal angle = asin(sqrt(axisSqNorm / (fromSqNorm * toSqNorm)));
|
||||
|
||||
if (from * to < 0.0)
|
||||
angle = M_PI - angle;
|
||||
|
||||
setAxisAngle(axis, angle);
|
||||
}
|
||||
}
|
||||
|
||||
/*! Returns the image of \p v by the Quaternion inverse() rotation.
|
||||
|
||||
rotate() performs an inverse transformation. Same as inverse().rotate(v). */
|
||||
Vec Quaternion::inverseRotate(const Vec &v) const {
|
||||
return inverse().rotate(v);
|
||||
}
|
||||
|
||||
/*! Returns the image of \p v by the Quaternion rotation.
|
||||
|
||||
See also inverseRotate() and operator*(const Quaternion&, const Vec&). */
|
||||
Vec Quaternion::rotate(const Vec &v) const {
|
||||
const qreal q00 = 2.0 * q[0] * q[0];
|
||||
const qreal q11 = 2.0 * q[1] * q[1];
|
||||
const qreal q22 = 2.0 * q[2] * q[2];
|
||||
|
||||
const qreal q01 = 2.0 * q[0] * q[1];
|
||||
const qreal q02 = 2.0 * q[0] * q[2];
|
||||
const qreal q03 = 2.0 * q[0] * q[3];
|
||||
|
||||
const qreal q12 = 2.0 * q[1] * q[2];
|
||||
const qreal q13 = 2.0 * q[1] * q[3];
|
||||
|
||||
const qreal q23 = 2.0 * q[2] * q[3];
|
||||
|
||||
return Vec((1.0 - q11 - q22) * v[0] + (q01 - q23) * v[1] + (q02 + q13) * v[2],
|
||||
(q01 + q23) * v[0] + (1.0 - q22 - q00) * v[1] + (q12 - q03) * v[2],
|
||||
(q02 - q13) * v[0] + (q12 + q03) * v[1] +
|
||||
(1.0 - q11 - q00) * v[2]);
|
||||
}
|
||||
|
||||
/*! Set the Quaternion from a (supposedly correct) 3x3 rotation matrix.
|
||||
|
||||
The matrix is expressed in European format: its three \e columns are the
|
||||
images by the rotation of the three vectors of an orthogonal basis. Note that
|
||||
OpenGL uses a symmetric representation for its matrices.
|
||||
|
||||
setFromRotatedBasis() sets a Quaternion from the three axis of a rotated
|
||||
frame. It actually fills the three columns of a matrix with these rotated
|
||||
basis vectors and calls this method. */
|
||||
void Quaternion::setFromRotationMatrix(const qreal m[3][3]) {
|
||||
// Compute one plus the trace of the matrix
|
||||
const qreal onePlusTrace = 1.0 + m[0][0] + m[1][1] + m[2][2];
|
||||
|
||||
if (onePlusTrace > 1E-5) {
|
||||
// Direct computation
|
||||
const qreal s = sqrt(onePlusTrace) * 2.0;
|
||||
q[0] = (m[2][1] - m[1][2]) / s;
|
||||
q[1] = (m[0][2] - m[2][0]) / s;
|
||||
q[2] = (m[1][0] - m[0][1]) / s;
|
||||
q[3] = 0.25 * s;
|
||||
} else {
|
||||
// Computation depends on major diagonal term
|
||||
if ((m[0][0] > m[1][1]) & (m[0][0] > m[2][2])) {
|
||||
const qreal s = sqrt(1.0 + m[0][0] - m[1][1] - m[2][2]) * 2.0;
|
||||
q[0] = 0.25 * s;
|
||||
q[1] = (m[0][1] + m[1][0]) / s;
|
||||
q[2] = (m[0][2] + m[2][0]) / s;
|
||||
q[3] = (m[1][2] - m[2][1]) / s;
|
||||
} else if (m[1][1] > m[2][2]) {
|
||||
const qreal s = sqrt(1.0 + m[1][1] - m[0][0] - m[2][2]) * 2.0;
|
||||
q[0] = (m[0][1] + m[1][0]) / s;
|
||||
q[1] = 0.25 * s;
|
||||
q[2] = (m[1][2] + m[2][1]) / s;
|
||||
q[3] = (m[0][2] - m[2][0]) / s;
|
||||
} else {
|
||||
const qreal s = sqrt(1.0 + m[2][2] - m[0][0] - m[1][1]) * 2.0;
|
||||
q[0] = (m[0][2] + m[2][0]) / s;
|
||||
q[1] = (m[1][2] + m[2][1]) / s;
|
||||
q[2] = 0.25 * s;
|
||||
q[3] = (m[0][1] - m[1][0]) / s;
|
||||
}
|
||||
}
|
||||
normalize();
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
void Quaternion::setFromRotatedBase(const Vec &X, const Vec &Y, const Vec &Z) {
|
||||
qWarning("setFromRotatedBase is deprecated, use setFromRotatedBasis instead");
|
||||
setFromRotatedBasis(X, Y, Z);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! Sets the Quaternion from the three rotated vectors of an orthogonal basis.
|
||||
|
||||
The three vectors do not have to be normalized but must be orthogonal and
|
||||
direct (X^Y=k*Z, with k>0).
|
||||
|
||||
\code
|
||||
Quaternion q;
|
||||
q.setFromRotatedBasis(X, Y, Z);
|
||||
// Now q.rotate(Vec(1,0,0)) == X and q.inverseRotate(X) == Vec(1,0,0)
|
||||
// Same goes for Y and Z with Vec(0,1,0) and Vec(0,0,1).
|
||||
\endcode
|
||||
|
||||
See also setFromRotationMatrix() and Quaternion(const Vec&, const Vec&). */
|
||||
void Quaternion::setFromRotatedBasis(const Vec &X, const Vec &Y, const Vec &Z) {
|
||||
qreal m[3][3];
|
||||
qreal normX = X.norm();
|
||||
qreal normY = Y.norm();
|
||||
qreal normZ = Z.norm();
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
m[i][0] = X[i] / normX;
|
||||
m[i][1] = Y[i] / normY;
|
||||
m[i][2] = Z[i] / normZ;
|
||||
}
|
||||
|
||||
setFromRotationMatrix(m);
|
||||
}
|
||||
|
||||
/*! Returns the axis vector and the angle (in radians) of the rotation
|
||||
represented by the Quaternion. See the axis() and angle() documentations. */
|
||||
void Quaternion::getAxisAngle(Vec &axis, qreal &angle) const {
|
||||
angle = 2.0 * acos(q[3]);
|
||||
axis = Vec(q[0], q[1], q[2]);
|
||||
const qreal sinus = axis.norm();
|
||||
if (sinus > 1E-8)
|
||||
axis /= sinus;
|
||||
|
||||
if (angle > M_PI) {
|
||||
angle = 2.0 * qreal(M_PI) - angle;
|
||||
axis = -axis;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Returns the normalized axis direction of the rotation represented by the
|
||||
Quaternion.
|
||||
|
||||
It is null for an identity Quaternion. See also angle() and getAxisAngle(). */
|
||||
Vec Quaternion::axis() const {
|
||||
Vec res = Vec(q[0], q[1], q[2]);
|
||||
const qreal sinus = res.norm();
|
||||
if (sinus > 1E-8)
|
||||
res /= sinus;
|
||||
return (acos(q[3]) <= M_PI / 2.0) ? res : -res;
|
||||
}
|
||||
|
||||
/*! Returns the angle (in radians) of the rotation represented by the
|
||||
Quaternion.
|
||||
|
||||
This value is always in the range [0-pi]. Larger rotational angles are obtained
|
||||
by inverting the axis() direction.
|
||||
|
||||
See also axis() and getAxisAngle(). */
|
||||
qreal Quaternion::angle() const {
|
||||
const qreal angle = 2.0 * acos(q[3]);
|
||||
return (angle <= M_PI) ? angle : 2.0 * M_PI - angle;
|
||||
}
|
||||
|
||||
/*! Returns an XML \c QDomElement that represents the Quaternion.
|
||||
|
||||
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument
|
||||
factory used to create QDomElement.
|
||||
|
||||
When output to a file, the resulting QDomElement will look like:
|
||||
\code
|
||||
<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 ¢er,
|
||||
const Quaternion &after) {
|
||||
Quaternion l1 = Quaternion::lnDif(center, before);
|
||||
Quaternion l2 = Quaternion::lnDif(center, after);
|
||||
Quaternion e;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
e.q[i] = -0.25 * (l1.q[i] + l2.q[i]);
|
||||
e = center * (e.exp());
|
||||
|
||||
// if (Quaternion::dot(e,b) < 0.0)
|
||||
// e.negate();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &o, const Quaternion &Q) {
|
||||
return o << Q[0] << '\t' << Q[1] << '\t' << Q[2] << '\t' << Q[3];
|
||||
}
|
||||
|
||||
/*! Returns a random unit Quaternion.
|
||||
|
||||
You can create a randomly directed unit vector using:
|
||||
\code
|
||||
Vec randomDir = Quaternion::randomQuaternion() * Vec(1.0, 0.0, 0.0); // or any
|
||||
other Vec \endcode
|
||||
|
||||
\note This function uses rand() to create pseudo-random numbers and the random
|
||||
number generator can be initialized using srand().*/
|
||||
Quaternion Quaternion::randomQuaternion() {
|
||||
// The rand() function is not very portable and may not be available on your
|
||||
// system. Add the appropriate include or replace by an other random function
|
||||
// in case of problem.
|
||||
qreal seed = rand() / (qreal)RAND_MAX;
|
||||
qreal r1 = sqrt(1.0 - seed);
|
||||
qreal r2 = sqrt(seed);
|
||||
qreal t1 = 2.0 * M_PI * (rand() / (qreal)RAND_MAX);
|
||||
qreal t2 = 2.0 * M_PI * (rand() / (qreal)RAND_MAX);
|
||||
return Quaternion(sin(t1) * r1, cos(t1) * r1, sin(t2) * r2, cos(t2) * r2);
|
||||
}
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
|
|
|||
|
|
@ -19,146 +19,9 @@
|
|||
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
*****************************************************************************/
|
||||
#ifndef CGAL_HEADER_ONLY
|
||||
|
||||
#include <CGAL/Qt/vec.h>
|
||||
#include <CGAL/Qt/domUtils.h>
|
||||
#include <CGAL/Qt/vec_impl.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;
|
||||
}
|
||||
#endif // CGAL_HEADER_ONLY
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ if(CGAL_Qt5_moc_and_resource_files_included)
|
|||
endif()
|
||||
set(CGAL_Qt5_moc_and_resource_files_included TRUE)
|
||||
|
||||
|
||||
qt5_wrap_cpp(_CGAL_Qt5_MOC_FILES_private
|
||||
if(NOT CGAL_HEADER_ONLY)
|
||||
qt5_wrap_cpp(_CGAL_Qt5_MOC_FILES_private
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewNavigation.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/DemosMainWindow.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsItem.h
|
||||
|
|
@ -16,6 +16,7 @@ qt5_wrap_cpp(_CGAL_Qt5_MOC_FILES_private
|
|||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedFrame.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/qglviewer.h
|
||||
)
|
||||
endif()#CGAL_HEADER_ONLY
|
||||
|
||||
# qrc files (resources files, that contain icons, at least)
|
||||
if(EXISTS ${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/demo/resources/CGAL.qrc)
|
||||
|
|
|
|||
|
|
@ -53,10 +53,24 @@ if(NOT CGAL_Qt5_MISSING_DEPS)
|
|||
include(${CMAKE_CURRENT_LIST_DIR}/CGAL_Qt5_moc_and_resource_files.cmake)
|
||||
|
||||
if(CGAL_HEADER_ONLY AND (WITH_demos OR WITH_examples OR NOT CGAL_BUILDING_LIBS) AND NOT TARGET CGAL_Qt5_moc_and_resources)
|
||||
add_library(CGAL_Qt5_moc_and_resources STATIC ${_CGAL_Qt5_MOC_FILES_private} ${_CGAL_Qt5_RESOURCE_FILES_private})
|
||||
add_library(CGAL_Qt5_moc_and_resources STATIC
|
||||
${_CGAL_Qt5_MOC_FILES_private}
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewNavigation.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/DemosMainWindow.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsItem.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/GraphicsViewInput.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/camera.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/frame.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/keyFrameInterpolator.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedCameraFrame.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/manipulatedFrame.h
|
||||
${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/qglviewer.h
|
||||
|
||||
${_CGAL_Qt5_RESOURCE_FILES_private})
|
||||
set_target_properties(CGAL_Qt5_moc_and_resources PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE TRUE
|
||||
EXCLUDE_FROM_ALL TRUE)
|
||||
EXCLUDE_FROM_ALL TRUE
|
||||
AUTOMOC TRUE)
|
||||
target_link_libraries(CGAL_Qt5_moc_and_resources CGAL::CGAL Qt5::Widgets Qt5::OpenGL Qt5::Svg Qt5::Xml)
|
||||
|
||||
add_library(CGAL::CGAL_Qt5_moc_and_resources ALIAS CGAL_Qt5_moc_and_resources)
|
||||
|
|
|
|||
|
|
@ -660,8 +660,8 @@ bool Volume_plane<T>::eventFilter(QObject *, QEvent *event)
|
|||
viewer->setMouseBinding(
|
||||
Qt::NoModifier,
|
||||
Qt::LeftButton,
|
||||
QGLViewer::FRAME,
|
||||
QGLViewer::TRANSLATE);
|
||||
qglviewer::FRAME,
|
||||
qglviewer::TRANSLATE);
|
||||
}
|
||||
}
|
||||
else if(event->type() == QEvent::MouseButtonRelease && is_grabbing)
|
||||
|
|
@ -670,8 +670,8 @@ bool Volume_plane<T>::eventFilter(QObject *, QEvent *event)
|
|||
viewer->setMouseBinding(
|
||||
Qt::NoModifier,
|
||||
Qt::LeftButton,
|
||||
QGLViewer::CAMERA,
|
||||
QGLViewer::ROTATE);
|
||||
qglviewer::CAMERA,
|
||||
qglviewer::ROTATE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -988,7 +988,7 @@ void Scene_edit_box_item_priv::reset_selection()
|
|||
selection_on = false;
|
||||
QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin();
|
||||
viewer->setManipulatedFrame(frame);
|
||||
viewer->setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, QGLViewer::SELECT);
|
||||
viewer->setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, qglviewer::SELECT);
|
||||
constraint.setTranslationConstraintType(qglviewer::AxisPlaneConstraint::FREE);
|
||||
selected_vertices.clear();
|
||||
}
|
||||
|
|
@ -1060,8 +1060,8 @@ bool Scene_edit_box_item::eventFilter(QObject *obj, QEvent *event)
|
|||
viewer->setMouseBinding(
|
||||
Qt::NoModifier,
|
||||
Qt::LeftButton,
|
||||
QGLViewer::FRAME,
|
||||
QGLViewer::TRANSLATE);
|
||||
qglviewer::FRAME,
|
||||
qglviewer::TRANSLATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1108,8 +1108,8 @@ bool Scene_edit_box_item::eventFilter(QObject *obj, QEvent *event)
|
|||
viewer->setMouseBinding(
|
||||
Qt::NoModifier,
|
||||
Qt::LeftButton,
|
||||
QGLViewer::CAMERA,
|
||||
QGLViewer::ROTATE);
|
||||
qglviewer::CAMERA,
|
||||
qglviewer::ROTATE);
|
||||
}
|
||||
else if(event->type() == QEvent::KeyRelease)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include "Polyhedron_demo.h"
|
||||
#include <clocale>
|
||||
|
||||
#include <CGAL/Qt/resources.h>
|
||||
/*!
|
||||
* \brief Defines the entry point of the demo.
|
||||
* Creates the application and sets a main window.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#include <CGAL/Three/Scene_draw_interface.h>
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <CGAL/Qt/manipulatedCameraFrame.h>
|
||||
#include <QDebug>
|
||||
#include <QOpenGLShader>
|
||||
#include <QFileDialog>
|
||||
|
|
@ -92,7 +91,7 @@ Viewer::Viewer(QWidget* parent, bool antialiasing)
|
|||
connect( d->textRenderer, SIGNAL(sendMessage(QString,int)),
|
||||
this, SLOT(printMessage(QString,int)) );
|
||||
connect(&d->messageTimer, SIGNAL(timeout()), SLOT(hideMessage()));
|
||||
setShortcut(EXIT_VIEWER, 0);
|
||||
setShortcut(qglviewer::EXIT_VIEWER, 0);
|
||||
setKeyDescription(Qt::Key_T,
|
||||
tr("Turn the camera by 180 degrees"));
|
||||
setKeyDescription(Qt::Key_M,
|
||||
|
|
@ -107,12 +106,12 @@ Viewer::Viewer(QWidget* parent, bool antialiasing)
|
|||
|
||||
#if QGLVIEWER_VERSION >= 0x020501
|
||||
//modify mouse bindings that have been updated
|
||||
setMouseBinding(Qt::Key(0), Qt::NoModifier, Qt::LeftButton, RAP_FROM_PIXEL, true, Qt::RightButton);
|
||||
setMouseBinding(Qt::Key(0), Qt::NoModifier, Qt::LeftButton, qglviewer::RAP_FROM_PIXEL, true, Qt::RightButton);
|
||||
setMouseBindingDescription(Qt::ShiftModifier, Qt::RightButton,
|
||||
tr("Select and pop context menu"));
|
||||
setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, RAP_FROM_PIXEL);
|
||||
setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, qglviewer::RAP_FROM_PIXEL);
|
||||
//use the new API for these
|
||||
setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, SELECT);
|
||||
setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, qglviewer::SELECT);
|
||||
|
||||
setMouseBindingDescription(Qt::Key(0), Qt::ShiftModifier, Qt::LeftButton,
|
||||
tr("Selects and display context "
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@
|
|||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <CGAL/Three/Viewer_interface.h>
|
||||
|
||||
#include <CGAL/Qt/qglviewer.h>
|
||||
#include <QPoint>
|
||||
#include <QFont>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
|
@ -112,7 +110,7 @@ public Q_SLOTS:
|
|||
void setBindingSelect() Q_DECL_OVERRIDE
|
||||
{
|
||||
#if QGLVIEWER_VERSION >= 0x020501
|
||||
setMouseBinding(::Qt::ShiftModifier, ::Qt::LeftButton, SELECT);
|
||||
setMouseBinding(::Qt::ShiftModifier, ::Qt::LeftButton, qglviewer::SELECT);
|
||||
#else
|
||||
setMouseBinding(::Qt::SHIFT + ::Qt::LeftButton, SELECT);
|
||||
#endif
|
||||
|
|
@ -121,7 +119,7 @@ public Q_SLOTS:
|
|||
virtual void setNoBinding() Q_DECL_OVERRIDE
|
||||
{
|
||||
#if QGLVIEWER_VERSION >= 0x020501
|
||||
setMouseBinding(::Qt::ShiftModifier, ::Qt::LeftButton, NO_CLICK_ACTION);
|
||||
setMouseBinding(::Qt::ShiftModifier, ::Qt::LeftButton, qglviewer::NO_CLICK_ACTION);
|
||||
#else
|
||||
setMouseBinding(::Qt::SHIFT + ::Qt::LeftButton, NO_CLICK_ACTION);
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue