mirror of
https://github.com/ConjureETS/LOG750-LAB2.git
synced 2026-03-23 19:11:20 +00:00
1145 lines
36 KiB
C++
1145 lines
36 KiB
C++
/****************************************************************************
|
|
|
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
|
|
|
This file is part of the QGLViewer library version 2.6.3.
|
|
|
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
|
|
|
This file may be used under the terms of the GNU General Public License
|
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
|
appearing in the LICENSE file included in the packaging of this file.
|
|
In addition, as a special exception, Gilles Debunne gives you certain
|
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
|
|
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
|
purchase a libQGLViewer Commercial License.
|
|
|
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
*****************************************************************************/
|
|
|
|
#include "domUtils.h"
|
|
#include "frame.h"
|
|
#include <math.h>
|
|
|
|
using namespace qglviewer;
|
|
using namespace std;
|
|
|
|
|
|
/*! Creates a default Frame.
|
|
|
|
Its position() is (0,0,0) and it has an identity orientation() Quaternion. The referenceFrame()
|
|
and the constraint() are \c NULL. */
|
|
Frame::Frame()
|
|
: constraint_(NULL), referenceFrame_(NULL)
|
|
{}
|
|
|
|
/*! Creates a Frame with a position() and an orientation().
|
|
|
|
See the Vec and Quaternion documentations for convenient constructors and methods.
|
|
|
|
The Frame is defined in the world coordinate system (its referenceFrame() is \c NULL). It
|
|
has a \c NULL associated constraint(). */
|
|
Frame::Frame(const Vec& position, const Quaternion& orientation)
|
|
: t_(position), q_(orientation), constraint_(NULL), referenceFrame_(NULL)
|
|
{}
|
|
|
|
/*! Equal operator.
|
|
|
|
The referenceFrame() and constraint() pointers are copied.
|
|
|
|
\attention Signal and slot connections are not copied. */
|
|
Frame& Frame::operator=(const Frame& frame)
|
|
{
|
|
// Automatic compiler generated version would not emit the modified() signals as is done in
|
|
// setTranslationAndRotation.
|
|
setTranslationAndRotation(frame.translation(), frame.rotation());
|
|
setConstraint(frame.constraint());
|
|
setReferenceFrame(frame.referenceFrame());
|
|
return *this;
|
|
}
|
|
|
|
/*! Copy constructor.
|
|
|
|
The translation() and rotation() as well as constraint() and referenceFrame() pointers are
|
|
copied. */
|
|
Frame::Frame(const Frame& frame)
|
|
: QObject()
|
|
{
|
|
(*this) = frame;
|
|
}
|
|
|
|
/////////////////////////////// MATRICES //////////////////////////////////////
|
|
|
|
/*! Returns the 4x4 OpenGL transformation matrix represented by the Frame.
|
|
|
|
This method should be used in conjunction with \c glMultMatrixd() to modify the OpenGL modelview
|
|
matrix from a Frame hierarchy. With this Frame hierarchy:
|
|
\code
|
|
Frame* body = new Frame();
|
|
Frame* leftArm = new Frame();
|
|
Frame* rightArm = new Frame();
|
|
leftArm->setReferenceFrame(body);
|
|
rightArm->setReferenceFrame(body);
|
|
\endcode
|
|
|
|
The associated OpenGL drawing code should look like:
|
|
\code
|
|
void Viewer::draw()
|
|
{
|
|
glPushMatrix();
|
|
glMultMatrixd(body->matrix());
|
|
drawBody();
|
|
|
|
glPushMatrix();
|
|
glMultMatrixd(leftArm->matrix());
|
|
drawArm();
|
|
glPopMatrix();
|
|
|
|
glPushMatrix();
|
|
glMultMatrixd(rightArm->matrix());
|
|
drawArm();
|
|
glPopMatrix();
|
|
|
|
glPopMatrix();
|
|
}
|
|
\endcode
|
|
Note the use of nested \c glPushMatrix() and \c glPopMatrix() blocks to represent the frame hierarchy: \c
|
|
leftArm and \c rightArm are both correctly drawn with respect to the \c body coordinate system.
|
|
|
|
This matrix only represents the local Frame transformation (i.e. with respect to the
|
|
referenceFrame()). Use worldMatrix() to get the full Frame transformation matrix (i.e. from the
|
|
world to the Frame coordinate system). These two match when the referenceFrame() is \c NULL.
|
|
|
|
The result is only valid until the next call to matrix(), getMatrix(), worldMatrix() or
|
|
getWorldMatrix(). Use it immediately (as above) or use getMatrix() instead.
|
|
|
|
\attention The OpenGL format of the result is the transpose of the actual mathematical European
|
|
representation (translation is on the last \e line instead of the last \e column).
|
|
|
|
\note The scaling factor of the 4x4 matrix is 1.0. */
|
|
const GLdouble* Frame::matrix() const
|
|
{
|
|
static GLdouble m[4][4];
|
|
getMatrix(m);
|
|
return (const GLdouble*)(m);
|
|
}
|
|
|
|
/*! \c GLdouble[4][4] version of matrix(). See also getWorldMatrix() and matrix(). */
|
|
void Frame::getMatrix(GLdouble m[4][4]) const
|
|
{
|
|
q_.getMatrix(m);
|
|
|
|
m[3][0] = t_[0];
|
|
m[3][1] = t_[1];
|
|
m[3][2] = t_[2];
|
|
}
|
|
|
|
/*! \c GLdouble[16] version of matrix(). See also getWorldMatrix() and matrix(). */
|
|
void Frame::getMatrix(GLdouble m[16]) const
|
|
{
|
|
q_.getMatrix(m);
|
|
|
|
m[12] = t_[0];
|
|
m[13] = t_[1];
|
|
m[14] = t_[2];
|
|
}
|
|
|
|
/*! Returns a Frame representing the inverse of the Frame space transformation.
|
|
|
|
The rotation() of the new Frame is the Quaternion::inverse() of the original rotation.
|
|
Its translation() is the negated inverse rotated image of the original translation.
|
|
|
|
If a Frame is considered as a space rigid transformation (translation and rotation), the inverse()
|
|
Frame performs the inverse transformation.
|
|
|
|
Only the local Frame transformation (i.e. defined with respect to the referenceFrame()) is inverted.
|
|
Use worldInverse() for a global inverse.
|
|
|
|
The resulting Frame has the same referenceFrame() as the Frame and a \c NULL constraint().
|
|
|
|
\note The scaling factor of the 4x4 matrix is 1.0. */
|
|
Frame Frame::inverse() const
|
|
{
|
|
Frame fr(-(q_.inverseRotate(t_)), q_.inverse());
|
|
fr.setReferenceFrame(referenceFrame());
|
|
return fr;
|
|
}
|
|
|
|
/*! Returns the 4x4 OpenGL transformation matrix represented by the Frame.
|
|
|
|
This method should be used in conjunction with \c glMultMatrixd() to modify
|
|
the OpenGL modelview matrix from a Frame:
|
|
\code
|
|
// The modelview here corresponds to the world coordinate system.
|
|
Frame fr(pos, Quaternion(from, to));
|
|
glPushMatrix();
|
|
glMultMatrixd(fr.worldMatrix());
|
|
// draw object in the fr coordinate system.
|
|
glPopMatrix();
|
|
\endcode
|
|
|
|
This matrix represents the global Frame transformation: the entire referenceFrame() hierarchy is
|
|
taken into account to define the Frame transformation from the world coordinate system. Use
|
|
matrix() to get the local Frame transformation matrix (i.e. defined with respect to the
|
|
referenceFrame()). These two match when the referenceFrame() is \c NULL.
|
|
|
|
The OpenGL format of the result is the transpose of the actual mathematical European
|
|
representation (translation is on the last \e line instead of the last \e column).
|
|
|
|
\attention The result is only valid until the next call to matrix(), getMatrix(), worldMatrix() or
|
|
getWorldMatrix(). Use it immediately (as above) or use getWorldMatrix() instead.
|
|
|
|
\note The scaling factor of the 4x4 matrix is 1.0. */
|
|
const GLdouble* Frame::worldMatrix() const
|
|
{
|
|
// This test is done for efficiency reasons (creates lots of temp objects otherwise).
|
|
if (referenceFrame())
|
|
{
|
|
static Frame fr;
|
|
fr.setTranslation(position());
|
|
fr.setRotation(orientation());
|
|
return fr.matrix();
|
|
}
|
|
else
|
|
return matrix();
|
|
}
|
|
|
|
/*! qreal[4][4] parameter version of worldMatrix(). See also getMatrix() and matrix(). */
|
|
void Frame::getWorldMatrix(GLdouble m[4][4]) const
|
|
{
|
|
const GLdouble* mat = worldMatrix();
|
|
for (int i=0; i<4; ++i)
|
|
for (int j=0; j<4; ++j)
|
|
m[i][j] = mat[i*4+j];
|
|
}
|
|
|
|
/*! qreal[16] parameter version of worldMatrix(). See also getMatrix() and matrix(). */
|
|
void Frame::getWorldMatrix(GLdouble m[16]) const
|
|
{
|
|
const GLdouble* mat = worldMatrix();
|
|
for (int i=0; i<16; ++i)
|
|
m[i] = mat[i];
|
|
}
|
|
|
|
/*! This is an overloaded method provided for convenience. Same as setFromMatrix(). */
|
|
void Frame::setFromMatrix(const GLdouble m[4][4])
|
|
{
|
|
if (fabs(m[3][3]) < 1E-8)
|
|
{
|
|
qWarning("Frame::setFromMatrix: Null homogeneous coefficient");
|
|
return;
|
|
}
|
|
|
|
qreal rot[3][3];
|
|
for (int i=0; i<3; ++i)
|
|
{
|
|
t_[i] = m[3][i] / m[3][3];
|
|
for (int j=0; j<3; ++j)
|
|
// Beware of the transposition (OpenGL to European math)
|
|
rot[i][j] = m[j][i] / m[3][3];
|
|
}
|
|
q_.setFromRotationMatrix(rot);
|
|
Q_EMIT modified();
|
|
}
|
|
|
|
/*! Sets the Frame from an OpenGL matrix representation (rotation in the upper left 3x3 matrix and
|
|
translation on the last line).
|
|
|
|
Hence, if a code fragment looks like:
|
|
\code
|
|
GLdouble m[16]={...};
|
|
glMultMatrixd(m);
|
|
\endcode
|
|
It is equivalent to write:
|
|
\code
|
|
Frame fr;
|
|
fr.setFromMatrix(m);
|
|
glMultMatrixd(fr.matrix());
|
|
\endcode
|
|
|
|
Using this conversion, you can benefit from the powerful Frame transformation methods to translate
|
|
points and vectors to and from the Frame coordinate system to any other Frame coordinate system
|
|
(including the world coordinate system). See coordinatesOf() and transformOf().
|
|
|
|
Emits the modified() signal. See also matrix(), getMatrix() and
|
|
Quaternion::setFromRotationMatrix().
|
|
|
|
\attention A Frame does not contain a scale factor. The possible scaling in \p m will not be
|
|
converted into the Frame by this method. */
|
|
void Frame::setFromMatrix(const GLdouble m[16])
|
|
{
|
|
GLdouble mat[4][4];
|
|
for (int i=0; i<4; ++i)
|
|
for (int j=0; j<4; ++j)
|
|
mat[i][j] = m[i*4+j];
|
|
setFromMatrix(mat);
|
|
}
|
|
|
|
//////////////////// SET AND GET LOCAL TRANSLATION AND ROTATION ///////////////////////////////
|
|
|
|
|
|
/*! Same as setTranslation(), but with \p qreal parameters. */
|
|
void Frame::setTranslation(qreal x, qreal y, qreal z)
|
|
{
|
|
setTranslation(Vec(x, y, z));
|
|
}
|
|
|
|
/*! Fill \c x, \c y and \c z with the translation() of the Frame. */
|
|
void Frame::getTranslation(qreal& x, qreal& y, qreal& z) const
|
|
{
|
|
const Vec t = translation();
|
|
x = t[0];
|
|
y = t[1];
|
|
z = t[2];
|
|
}
|
|
|
|
/*! Same as setRotation() but with \c qreal Quaternion parameters. */
|
|
void Frame::setRotation(qreal q0, qreal q1, qreal q2, qreal q3)
|
|
{
|
|
setRotation(Quaternion(q0, q1, q2, q3));
|
|
}
|
|
|
|
/*! The \p q are set to the rotation() of the Frame.
|
|
|
|
See Quaternion::Quaternion(qreal, qreal, qreal, qreal) for details on \c q. */
|
|
void Frame::getRotation(qreal& q0, qreal& q1, qreal& q2, qreal& q3) const
|
|
{
|
|
const Quaternion q = rotation();
|
|
q0 = q[0];
|
|
q1 = q[1];
|
|
q2 = q[2];
|
|
q3 = q[3];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*! Translates the Frame of \p t (defined in the Frame coordinate system).
|
|
|
|
The translation actually applied to the Frame may differ from \p t since it can be filtered by the
|
|
constraint(). Use translate(Vec&) or setTranslationWithConstraint() to retrieve the filtered
|
|
translation value. Use setTranslation() to directly translate the Frame without taking the
|
|
constraint() into account.
|
|
|
|
See also rotate(const Quaternion&). Emits the modified() signal. */
|
|
void Frame::translate(const Vec& t)
|
|
{
|
|
Vec tbis = t;
|
|
translate(tbis);
|
|
}
|
|
|
|
/*! Same as translate(const Vec&) but \p t may be modified to satisfy the translation constraint().
|
|
Its new value corresponds to the translation that has actually been applied to the Frame. */
|
|
void Frame::translate(Vec& t)
|
|
{
|
|
if (constraint())
|
|
constraint()->constrainTranslation(t, this);
|
|
t_ += t;
|
|
Q_EMIT modified();
|
|
}
|
|
|
|
/*! Same as translate(const Vec&) but with \c qreal parameters. */
|
|
void Frame::translate(qreal x, qreal y, qreal z)
|
|
{
|
|
Vec t(x,y,z);
|
|
translate(t);
|
|
}
|
|
|
|
/*! Same as translate(Vec&) but with \c qreal parameters. */
|
|
void Frame::translate(qreal& x, qreal& y, qreal& z)
|
|
{
|
|
Vec t(x,y,z);
|
|
translate(t);
|
|
x = t[0];
|
|
y = t[1];
|
|
z = t[2];
|
|
}
|
|
|
|
/*! Rotates the Frame by \p q (defined in the Frame coordinate system): R = R*q.
|
|
|
|
The rotation actually applied to the Frame may differ from \p q since it can be filtered by the
|
|
constraint(). Use rotate(Quaternion&) or setRotationWithConstraint() to retrieve the filtered
|
|
rotation value. Use setRotation() to directly rotate the Frame without taking the constraint()
|
|
into account.
|
|
|
|
See also translate(const Vec&). Emits the modified() signal. */
|
|
void Frame::rotate(const Quaternion& q)
|
|
{
|
|
Quaternion qbis = q;
|
|
rotate(qbis);
|
|
}
|
|
|
|
/*! Same as rotate(const Quaternion&) but \p q may be modified to satisfy the rotation constraint().
|
|
Its new value corresponds to the rotation that has actually been applied to the Frame. */
|
|
void Frame::rotate(Quaternion& q)
|
|
{
|
|
if (constraint())
|
|
constraint()->constrainRotation(q, this);
|
|
q_ *= q;
|
|
q_.normalize(); // Prevents numerical drift
|
|
Q_EMIT modified();
|
|
}
|
|
|
|
/*! Same as rotate(Quaternion&) but with \c qreal Quaternion parameters. */
|
|
void Frame::rotate(qreal& q0, qreal& q1, qreal& q2, qreal& q3)
|
|
{
|
|
Quaternion q(q0,q1,q2,q3);
|
|
rotate(q);
|
|
q0 = q[0];
|
|
q1 = q[1];
|
|
q2 = q[2];
|
|
q3 = q[3];
|
|
}
|
|
|
|
/*! Same as rotate(const Quaternion&) but with \c qreal Quaternion parameters. */
|
|
void Frame::rotate(qreal q0, qreal q1, qreal q2, qreal q3)
|
|
{
|
|
Quaternion q(q0,q1,q2,q3);
|
|
rotate(q);
|
|
}
|
|
|
|
/*! Makes the Frame rotate() by \p rotation around \p point.
|
|
|
|
\p point is defined in the world coordinate system, while the \p rotation axis is defined in the
|
|
Frame coordinate system.
|
|
|
|
If the Frame has a constraint(), \p rotation is first constrained using
|
|
Constraint::constrainRotation(). The translation which results from the filtered rotation around
|
|
\p point is then computed and filtered using Constraint::constrainTranslation(). The new \p
|
|
rotation value corresponds to the rotation that has actually been applied to the Frame.
|
|
|
|
Emits the modified() signal. */
|
|
void Frame::rotateAroundPoint(Quaternion& rotation, const Vec& point)
|
|
{
|
|
if (constraint())
|
|
constraint()->constrainRotation(rotation, this);
|
|
q_ *= rotation;
|
|
q_.normalize(); // Prevents numerical drift
|
|
Vec trans = point + Quaternion(inverseTransformOf(rotation.axis()), rotation.angle()).rotate(position()-point) - t_;
|
|
if (constraint())
|
|
constraint()->constrainTranslation(trans, this);
|
|
t_ += trans;
|
|
Q_EMIT modified();
|
|
}
|
|
|
|
/*! Same as rotateAroundPoint(), but with a \c const \p rotation Quaternion. Note that the actual
|
|
rotation may differ since it can be filtered by the constraint(). */
|
|
void Frame::rotateAroundPoint(const Quaternion& rotation, const Vec& point)
|
|
{
|
|
Quaternion rot = rotation;
|
|
rotateAroundPoint(rot, point);
|
|
}
|
|
|
|
//////////////////// SET AND GET WORLD POSITION AND ORIENTATION ///////////////////////////////
|
|
|
|
/*! Sets the position() of the Frame, defined in the world coordinate system. Emits the modified()
|
|
signal.
|
|
|
|
Use setTranslation() to define the \e local frame translation (with respect to the
|
|
referenceFrame()). The potential constraint() of the Frame is not taken into account, use
|
|
setPositionWithConstraint() instead. */
|
|
void Frame::setPosition(const Vec& position)
|
|
{
|
|
if (referenceFrame())
|
|
setTranslation(referenceFrame()->coordinatesOf(position));
|
|
else
|
|
setTranslation(position);
|
|
}
|
|
|
|
/*! Same as setPosition(), but with \c qreal parameters. */
|
|
void Frame::setPosition(qreal x, qreal y, qreal z)
|
|
{
|
|
setPosition(Vec(x, y, z));
|
|
}
|
|
|
|
/*! Same as successive calls to setPosition() and then setOrientation().
|
|
|
|
Only one modified() signal is emitted, which is convenient if this signal is connected to a
|
|
QGLViewer::update() slot. See also setTranslationAndRotation() and
|
|
setPositionAndOrientationWithConstraint(). */
|
|
void Frame::setPositionAndOrientation(const Vec& position, const Quaternion& orientation)
|
|
{
|
|
if (referenceFrame())
|
|
{
|
|
t_ = referenceFrame()->coordinatesOf(position);
|
|
q_ = referenceFrame()->orientation().inverse() * orientation;
|
|
}
|
|
else
|
|
{
|
|
t_ = position;
|
|
q_ = orientation;
|
|
}
|
|
Q_EMIT modified();
|
|
}
|
|
|
|
|
|
/*! Same as successive calls to setTranslation() and then setRotation().
|
|
|
|
Only one modified() signal is emitted, which is convenient if this signal is connected to a
|
|
QGLViewer::update() slot. See also setPositionAndOrientation() and
|
|
setTranslationAndRotationWithConstraint(). */
|
|
void Frame::setTranslationAndRotation(const Vec& translation, const Quaternion& rotation)
|
|
{
|
|
t_ = translation;
|
|
q_ = rotation;
|
|
Q_EMIT modified();
|
|
}
|
|
|
|
|
|
/*! \p x, \p y and \p z are set to the position() of the Frame. */
|
|
void Frame::getPosition(qreal& x, qreal& y, qreal& z) const
|
|
{
|
|
Vec p = position();
|
|
x = p.x;
|
|
y = p.y;
|
|
z = p.z;
|
|
}
|
|
|
|
/*! Sets the orientation() of the Frame, defined in the world coordinate system. Emits the modified() signal.
|
|
|
|
Use setRotation() to define the \e local frame rotation (with respect to the referenceFrame()). The
|
|
potential constraint() of the Frame is not taken into account, use setOrientationWithConstraint()
|
|
instead. */
|
|
void Frame::setOrientation(const Quaternion& orientation)
|
|
{
|
|
if (referenceFrame())
|
|
setRotation(referenceFrame()->orientation().inverse() * orientation);
|
|
else
|
|
setRotation(orientation);
|
|
}
|
|
|
|
/*! Same as setOrientation(), but with \c qreal parameters. */
|
|
void Frame::setOrientation(qreal q0, qreal q1, qreal q2, qreal q3)
|
|
{
|
|
setOrientation(Quaternion(q0, q1, q2, q3));
|
|
}
|
|
|
|
/*! Get the current orientation of the frame (same as orientation()).
|
|
Parameters are the orientation Quaternion values.
|
|
See also setOrientation(). */
|
|
|
|
/*! The \p q are set to the orientation() of the Frame.
|
|
|
|
See Quaternion::Quaternion(qreal, qreal, qreal, qreal) for details on \c q. */
|
|
void Frame::getOrientation(qreal& q0, qreal& q1, qreal& q2, qreal& q3) const
|
|
{
|
|
Quaternion o = orientation();
|
|
q0 = o[0];
|
|
q1 = o[1];
|
|
q2 = o[2];
|
|
q3 = o[3];
|
|
}
|
|
|
|
/*! Returns the position of the Frame, defined in the world coordinate system. See also
|
|
orientation(), setPosition() and translation(). */
|
|
Vec Frame::position() const {
|
|
if (referenceFrame_)
|
|
return inverseCoordinatesOf(Vec(0.0,0.0,0.0));
|
|
else
|
|
return t_;
|
|
}
|
|
|
|
/*! Returns the orientation of the Frame, defined in the world coordinate system. See also
|
|
position(), setOrientation() and rotation(). */
|
|
Quaternion Frame::orientation() const
|
|
{
|
|
Quaternion res = rotation();
|
|
const Frame* fr = referenceFrame();
|
|
while (fr != NULL)
|
|
{
|
|
res = fr->rotation() * res;
|
|
fr = fr->referenceFrame();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
////////////////////// C o n s t r a i n t V e r s i o n s //////////////////////////
|
|
|
|
/*! Same as setTranslation(), but \p translation is modified so that the potential constraint() of the
|
|
Frame is satisfied.
|
|
|
|
Emits the modified() signal. See also setRotationWithConstraint() and setPositionWithConstraint(). */
|
|
void Frame::setTranslationWithConstraint(Vec& translation)
|
|
{
|
|
Vec deltaT = translation - this->translation();
|
|
if (constraint())
|
|
constraint()->constrainTranslation(deltaT, this);
|
|
|
|
setTranslation(this->translation() + deltaT);
|
|
translation = this->translation();
|
|
}
|
|
|
|
/*! Same as setRotation(), but \p rotation is modified so that the potential constraint() of the
|
|
Frame is satisfied.
|
|
|
|
Emits the modified() signal. See also setTranslationWithConstraint() and setOrientationWithConstraint(). */
|
|
void Frame::setRotationWithConstraint(Quaternion& rotation)
|
|
{
|
|
Quaternion deltaQ = this->rotation().inverse() * rotation;
|
|
if (constraint())
|
|
constraint()->constrainRotation(deltaQ, this);
|
|
|
|
// Prevent numerical drift
|
|
deltaQ.normalize();
|
|
|
|
setRotation(this->rotation() * deltaQ);
|
|
q_.normalize();
|
|
rotation = this->rotation();
|
|
}
|
|
|
|
/*! Same as setTranslationAndRotation(), but \p translation and \p orientation are modified to
|
|
satisfy the constraint(). Emits the modified() signal. */
|
|
void Frame::setTranslationAndRotationWithConstraint(Vec& translation, Quaternion& rotation)
|
|
{
|
|
Vec deltaT = translation - this->translation();
|
|
Quaternion deltaQ = this->rotation().inverse() * rotation;
|
|
|
|
if (constraint())
|
|
{
|
|
constraint()->constrainTranslation(deltaT, this);
|
|
constraint()->constrainRotation(deltaQ, this);
|
|
}
|
|
|
|
// Prevent numerical drift
|
|
deltaQ.normalize();
|
|
|
|
t_ += deltaT;
|
|
q_ *= deltaQ;
|
|
q_.normalize();
|
|
|
|
translation = this->translation();
|
|
rotation = this->rotation();
|
|
|
|
Q_EMIT modified();
|
|
}
|
|
|
|
/*! Same as setPosition(), but \p position is modified so that the potential constraint() of the
|
|
Frame is satisfied. See also setOrientationWithConstraint() and setTranslationWithConstraint(). */
|
|
void Frame::setPositionWithConstraint(Vec& position)
|
|
{
|
|
if (referenceFrame())
|
|
position = referenceFrame()->coordinatesOf(position);
|
|
|
|
setTranslationWithConstraint(position);
|
|
}
|
|
|
|
/*! Same as setOrientation(), but \p orientation is modified so that the potential constraint() of the Frame
|
|
is satisfied. See also setPositionWithConstraint() and setRotationWithConstraint(). */
|
|
void Frame::setOrientationWithConstraint(Quaternion& orientation)
|
|
{
|
|
if (referenceFrame())
|
|
orientation = referenceFrame()->orientation().inverse() * orientation;
|
|
|
|
setRotationWithConstraint(orientation);
|
|
}
|
|
|
|
/*! Same as setPositionAndOrientation() but \p position and \p orientation are modified to satisfy
|
|
the constraint. Emits the modified() signal. */
|
|
void Frame::setPositionAndOrientationWithConstraint(Vec& position, Quaternion& orientation)
|
|
{
|
|
if (referenceFrame())
|
|
{
|
|
position = referenceFrame()->coordinatesOf(position);
|
|
orientation = referenceFrame()->orientation().inverse() * orientation;
|
|
}
|
|
setTranslationAndRotationWithConstraint(position, orientation);
|
|
}
|
|
|
|
|
|
///////////////////////////// REFERENCE FRAMES ///////////////////////////////////////
|
|
|
|
/*! Sets the referenceFrame() of the Frame.
|
|
|
|
The Frame translation() and rotation() are then defined in the referenceFrame() coordinate system.
|
|
Use position() and orientation() to express these in the world coordinate system.
|
|
|
|
Emits the modified() signal if \p refFrame differs from the current referenceFrame().
|
|
|
|
Using this method, you can create a hierarchy of Frames. This hierarchy needs to be a tree, which
|
|
root is the world coordinate system (i.e. a \c NULL referenceFrame()). A warning is printed and no
|
|
action is performed if setting \p refFrame as the referenceFrame() would create a loop in the Frame
|
|
hierarchy (see settingAsReferenceFrameWillCreateALoop()). */
|
|
void Frame::setReferenceFrame(const Frame* const refFrame)
|
|
{
|
|
if (settingAsReferenceFrameWillCreateALoop(refFrame))
|
|
qWarning("Frame::setReferenceFrame would create a loop in Frame hierarchy");
|
|
else
|
|
{
|
|
bool identical = (referenceFrame_ == refFrame);
|
|
referenceFrame_ = refFrame;
|
|
if (!identical)
|
|
Q_EMIT modified();
|
|
}
|
|
}
|
|
|
|
/*! Returns \c true if setting \p frame as the Frame's referenceFrame() would create a loop in the
|
|
Frame hierarchy. */
|
|
bool Frame::settingAsReferenceFrameWillCreateALoop(const Frame* const frame)
|
|
{
|
|
const Frame* f = frame;
|
|
while (f != NULL)
|
|
{
|
|
if (f == this)
|
|
return true;
|
|
f = f->referenceFrame();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
///////////////////////// FRAME TRANSFORMATIONS OF 3D POINTS //////////////////////////////
|
|
|
|
/*! Returns the Frame coordinates of a point \p src defined in the world coordinate system (converts
|
|
from world to Frame).
|
|
|
|
inverseCoordinatesOf() performs the inverse convertion. transformOf() converts 3D vectors instead
|
|
of 3D coordinates.
|
|
|
|
See the <a href="../examples/frameTransform.html">frameTransform example</a> for an
|
|
illustration. */
|
|
Vec Frame::coordinatesOf(const Vec& src) const
|
|
{
|
|
if (referenceFrame())
|
|
return localCoordinatesOf(referenceFrame()->coordinatesOf(src));
|
|
else
|
|
return localCoordinatesOf(src);
|
|
}
|
|
|
|
/*! Returns the world coordinates of the point whose position in the Frame coordinate system is \p
|
|
src (converts from Frame to world).
|
|
|
|
coordinatesOf() performs the inverse convertion. Use inverseTransformOf() to transform 3D vectors
|
|
instead of 3D coordinates. */
|
|
Vec Frame::inverseCoordinatesOf(const Vec& src) const
|
|
{
|
|
const Frame* fr = this;
|
|
Vec res = src;
|
|
while (fr != NULL)
|
|
{
|
|
res = fr->localInverseCoordinatesOf(res);
|
|
fr = fr->referenceFrame();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*! Returns the Frame coordinates of a point \p src defined in the referenceFrame() coordinate
|
|
system (converts from referenceFrame() to Frame).
|
|
|
|
localInverseCoordinatesOf() performs the inverse convertion. See also localTransformOf(). */
|
|
Vec Frame::localCoordinatesOf(const Vec& src) const
|
|
{
|
|
return rotation().inverseRotate(src - translation());
|
|
}
|
|
|
|
/*! Returns the referenceFrame() coordinates of a point \p src defined in the Frame coordinate
|
|
system (converts from Frame to referenceFrame()).
|
|
|
|
localCoordinatesOf() performs the inverse convertion. See also localInverseTransformOf(). */
|
|
Vec Frame::localInverseCoordinatesOf(const Vec& src) const
|
|
{
|
|
return rotation().rotate(src) + translation();
|
|
}
|
|
|
|
/*! Returns the Frame coordinates of the point whose position in the \p from coordinate system is \p
|
|
src (converts from \p from to Frame).
|
|
|
|
coordinatesOfIn() performs the inverse transformation. */
|
|
Vec Frame::coordinatesOfFrom(const Vec& src, const Frame* const from) const
|
|
{
|
|
if (this == from)
|
|
return src;
|
|
else
|
|
if (referenceFrame())
|
|
return localCoordinatesOf(referenceFrame()->coordinatesOfFrom(src, from));
|
|
else
|
|
return localCoordinatesOf(from->inverseCoordinatesOf(src));
|
|
}
|
|
|
|
/*! Returns the \p in coordinates of the point whose position in the Frame coordinate system is \p
|
|
src (converts from Frame to \p in).
|
|
|
|
coordinatesOfFrom() performs the inverse transformation. */
|
|
Vec Frame::coordinatesOfIn(const Vec& src, const Frame* const in) const
|
|
{
|
|
const Frame* fr = this;
|
|
Vec res = src;
|
|
while ((fr != NULL) && (fr != in))
|
|
{
|
|
res = fr->localInverseCoordinatesOf(res);
|
|
fr = fr->referenceFrame();
|
|
}
|
|
|
|
if (fr != in)
|
|
// in was not found in the branch of this, res is now expressed in the world
|
|
// coordinate system. Simply convert to in coordinate system.
|
|
res = in->coordinatesOf(res);
|
|
|
|
return res;
|
|
}
|
|
|
|
////// qreal[3] versions
|
|
|
|
/*! Same as coordinatesOf(), but with \c qreal parameters. */
|
|
void Frame::getCoordinatesOf(const qreal src[3], qreal res[3]) const
|
|
{
|
|
const Vec r = coordinatesOf(Vec(src));
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as inverseCoordinatesOf(), but with \c qreal parameters. */
|
|
void Frame::getInverseCoordinatesOf(const qreal src[3], qreal res[3]) const
|
|
{
|
|
const Vec r = inverseCoordinatesOf(Vec(src));
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as localCoordinatesOf(), but with \c qreal parameters. */
|
|
void Frame::getLocalCoordinatesOf(const qreal src[3], qreal res[3]) const
|
|
{
|
|
const Vec r = localCoordinatesOf(Vec(src));
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as localInverseCoordinatesOf(), but with \c qreal parameters. */
|
|
void Frame::getLocalInverseCoordinatesOf(const qreal src[3], qreal res[3]) const
|
|
{
|
|
const Vec r = localInverseCoordinatesOf(Vec(src));
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as coordinatesOfIn(), but with \c qreal parameters. */
|
|
void Frame::getCoordinatesOfIn(const qreal src[3], qreal res[3], const Frame* const in) const
|
|
{
|
|
const Vec r = coordinatesOfIn(Vec(src), in);
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as coordinatesOfFrom(), but with \c qreal parameters. */
|
|
void Frame::getCoordinatesOfFrom(const qreal src[3], qreal res[3], const Frame* const from) const
|
|
{
|
|
const Vec r = coordinatesOfFrom(Vec(src), from);
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
|
|
///////////////////////// FRAME TRANSFORMATIONS OF VECTORS //////////////////////////////
|
|
|
|
/*! Returns the Frame transform of a vector \p src defined in the world coordinate system (converts
|
|
vectors from world to Frame).
|
|
|
|
inverseTransformOf() performs the inverse transformation. coordinatesOf() converts 3D coordinates
|
|
instead of 3D vectors (here only the rotational part of the transformation is taken into account).
|
|
|
|
See the <a href="../examples/frameTransform.html">frameTransform example</a> for an
|
|
illustration. */
|
|
Vec Frame::transformOf(const Vec& src) const
|
|
{
|
|
if (referenceFrame())
|
|
return localTransformOf(referenceFrame()->transformOf(src));
|
|
else
|
|
return localTransformOf(src);
|
|
}
|
|
|
|
/*! Returns the world transform of the vector whose coordinates in the Frame coordinate
|
|
system is \p src (converts vectors from Frame to world).
|
|
|
|
transformOf() performs the inverse transformation. Use inverseCoordinatesOf() to transform 3D
|
|
coordinates instead of 3D vectors. */
|
|
Vec Frame::inverseTransformOf(const Vec& src) const
|
|
{
|
|
const Frame* fr = this;
|
|
Vec res = src;
|
|
while (fr != NULL)
|
|
{
|
|
res = fr->localInverseTransformOf(res);
|
|
fr = fr->referenceFrame();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*! Returns the Frame transform of a vector \p src defined in the referenceFrame() coordinate system
|
|
(converts vectors from referenceFrame() to Frame).
|
|
|
|
localInverseTransformOf() performs the inverse transformation. See also localCoordinatesOf(). */
|
|
Vec Frame::localTransformOf(const Vec& src) const
|
|
{
|
|
return rotation().inverseRotate(src);
|
|
}
|
|
|
|
/*! Returns the referenceFrame() transform of a vector \p src defined in the Frame coordinate
|
|
system (converts vectors from Frame to referenceFrame()).
|
|
|
|
localTransformOf() performs the inverse transformation. See also localInverseCoordinatesOf(). */
|
|
Vec Frame::localInverseTransformOf(const Vec& src) const
|
|
{
|
|
return rotation().rotate(src);
|
|
}
|
|
|
|
/*! Returns the Frame transform of the vector whose coordinates in the \p from coordinate system is \p
|
|
src (converts vectors from \p from to Frame).
|
|
|
|
transformOfIn() performs the inverse transformation. */
|
|
Vec Frame::transformOfFrom(const Vec& src, const Frame* const from) const
|
|
{
|
|
if (this == from)
|
|
return src;
|
|
else
|
|
if (referenceFrame())
|
|
return localTransformOf(referenceFrame()->transformOfFrom(src, from));
|
|
else
|
|
return localTransformOf(from->inverseTransformOf(src));
|
|
}
|
|
|
|
/*! Returns the \p in transform of the vector whose coordinates in the Frame coordinate system is \p
|
|
src (converts vectors from Frame to \p in).
|
|
|
|
transformOfFrom() performs the inverse transformation. */
|
|
Vec Frame::transformOfIn(const Vec& src, const Frame* const in) const
|
|
{
|
|
const Frame* fr = this;
|
|
Vec res = src;
|
|
while ((fr != NULL) && (fr != in))
|
|
{
|
|
res = fr->localInverseTransformOf(res);
|
|
fr = fr->referenceFrame();
|
|
}
|
|
|
|
if (fr != in)
|
|
// in was not found in the branch of this, res is now expressed in the world
|
|
// coordinate system. Simply convert to in coordinate system.
|
|
res = in->transformOf(res);
|
|
|
|
return res;
|
|
}
|
|
|
|
///////////////// qreal[3] versions //////////////////////
|
|
|
|
/*! Same as transformOf(), but with \c qreal parameters. */
|
|
void Frame::getTransformOf(const qreal src[3], qreal res[3]) const
|
|
{
|
|
Vec r = transformOf(Vec(src));
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as inverseTransformOf(), but with \c qreal parameters. */
|
|
void Frame::getInverseTransformOf(const qreal src[3], qreal res[3]) const
|
|
{
|
|
Vec r = inverseTransformOf(Vec(src));
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as localTransformOf(), but with \c qreal parameters. */
|
|
void Frame::getLocalTransformOf(const qreal src[3], qreal res[3]) const
|
|
{
|
|
Vec r = localTransformOf(Vec(src));
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as localInverseTransformOf(), but with \c qreal parameters. */
|
|
void Frame::getLocalInverseTransformOf(const qreal src[3], qreal res[3]) const
|
|
{
|
|
Vec r = localInverseTransformOf(Vec(src));
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as transformOfIn(), but with \c qreal parameters. */
|
|
void Frame::getTransformOfIn(const qreal src[3], qreal res[3], const Frame* const in) const
|
|
{
|
|
Vec r = transformOfIn(Vec(src), in);
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
/*! Same as transformOfFrom(), but with \c qreal parameters. */
|
|
void Frame::getTransformOfFrom(const qreal src[3], qreal res[3], const Frame* const from) const
|
|
{
|
|
Vec r = transformOfFrom(Vec(src), from);
|
|
for (int i=0; i<3 ; ++i)
|
|
res[i] = r[i];
|
|
}
|
|
|
|
//////////////////////////// STATE //////////////////////////////
|
|
|
|
/*! Returns an XML \c QDomElement that represents the Frame.
|
|
|
|
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument factory used to create
|
|
QDomElement.
|
|
|
|
The resulting QDomElement looks like:
|
|
\code
|
|
<name>
|
|
<position x=".." y=".." z=".." />
|
|
<orientation q0=".." q1=".." q2=".." q3=".." />
|
|
</name>
|
|
\endcode
|
|
|
|
Use initFromDOMElement() to restore the Frame state from the resulting \c QDomElement.
|
|
|
|
See Vec::domElement() for a complete example. See also Quaternion::domElement(),
|
|
Camera::domElement()...
|
|
|
|
\attention The constraint() and referenceFrame() are not saved in the QDomElement. */
|
|
QDomElement Frame::domElement(const QString& name, QDomDocument& document) const
|
|
{
|
|
// TODO: use translation and rotation instead when referenceFrame is coded...
|
|
QDomElement e = document.createElement(name);
|
|
e.appendChild(position().domElement("position", document));
|
|
e.appendChild(orientation().domElement("orientation", document));
|
|
return e;
|
|
}
|
|
|
|
/*! Restores the Frame state from a \c QDomElement created by domElement().
|
|
|
|
See domElement() for the \c QDomElement syntax. See the Vec::initFromDOMElement() and
|
|
Quaternion::initFromDOMElement() documentations for details on default values if an argument is
|
|
missing.
|
|
|
|
\attention The constraint() and referenceFrame() are not restored by this method and are left
|
|
unchanged. */
|
|
void Frame::initFromDOMElement(const QDomElement& element)
|
|
{
|
|
// TODO: use translation and rotation instead when referenceFrame is coded...
|
|
|
|
// Reset default values. Attention: destroys constraint.
|
|
// *this = Frame();
|
|
// This instead ? Better : what is not set is not changed.
|
|
// setPositionAndOrientation(Vec(), Quaternion());
|
|
|
|
QDomElement child=element.firstChild().toElement();
|
|
while (!child.isNull())
|
|
{
|
|
if (child.tagName() == "position")
|
|
setPosition(Vec(child));
|
|
if (child.tagName() == "orientation")
|
|
setOrientation(Quaternion(child).normalized());
|
|
|
|
child = child.nextSibling().toElement();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////// ALIGN /////////////////////////////////
|
|
|
|
/*! Aligns the Frame with \p frame, so that two of their axis are parallel.
|
|
|
|
If one of the X, Y and Z axis of the Frame is almost parallel to any of the X, Y, or Z axis of \p
|
|
frame, the Frame is rotated so that these two axis actually become parallel.
|
|
|
|
If, after this first rotation, two other axis are also almost parallel, a second alignment is
|
|
performed. The two frames then have identical orientations, up to 90 degrees rotations.
|
|
|
|
\p threshold measures how close two axis must be to be considered parallel. It is compared with the
|
|
absolute values of the dot product of the normalized axis. As a result, useful range is sqrt(2)/2
|
|
(systematic alignment) to 1 (no alignment).
|
|
|
|
When \p move is set to \c true, the Frame position() is also affected by the alignment. The new
|
|
Frame's position() is such that the \p frame position (computed with coordinatesOf(), in the Frame
|
|
coordinates system) does not change.
|
|
|
|
\p frame may be \c NULL and then represents the world coordinate system (same convention than for
|
|
the referenceFrame()).
|
|
|
|
The rotation (and translation when \p move is \c true) applied to the Frame are filtered by the
|
|
possible constraint(). */
|
|
void Frame::alignWithFrame(const Frame* const frame, bool move, qreal threshold)
|
|
{
|
|
Vec directions[2][3];
|
|
for (unsigned short d=0; d<3; ++d)
|
|
{
|
|
Vec dir((d==0)? 1.0 : 0.0, (d==1)? 1.0 : 0.0, (d==2)? 1.0 : 0.0);
|
|
if (frame)
|
|
directions[0][d] = frame->inverseTransformOf(dir);
|
|
else
|
|
directions[0][d] = dir;
|
|
directions[1][d] = inverseTransformOf(dir);
|
|
}
|
|
|
|
qreal maxProj = 0.0;
|
|
qreal proj;
|
|
unsigned short index[2];
|
|
index[0] = index[1] = 0;
|
|
for (unsigned short i=0; i<3; ++i)
|
|
for (unsigned short j=0; j<3; ++j)
|
|
if ( (proj=fabs(directions[0][i]*directions[1][j])) >= maxProj )
|
|
{
|
|
index[0] = i;
|
|
index[1] = j;
|
|
maxProj = proj;
|
|
}
|
|
|
|
Frame old;
|
|
old=*this;
|
|
|
|
qreal coef = directions[0][index[0]] * directions[1][index[1]];
|
|
if (fabs(coef) >= threshold)
|
|
{
|
|
const Vec axis = cross(directions[0][index[0]], directions[1][index[1]]);
|
|
qreal angle = asin(axis.norm());
|
|
if (coef >= 0.0)
|
|
angle = -angle;
|
|
rotate(rotation().inverse() * Quaternion(axis, angle) * orientation());
|
|
|
|
// Try to align an other axis direction
|
|
unsigned short d = (index[1]+1) % 3;
|
|
Vec dir((d==0)? 1.0 : 0.0, (d==1)? 1.0 : 0.0, (d==2)? 1.0 : 0.0);
|
|
dir = inverseTransformOf(dir);
|
|
|
|
qreal max = 0.0;
|
|
for (unsigned short i=0; i<3; ++i)
|
|
{
|
|
qreal proj = fabs(directions[0][i]*dir);
|
|
if (proj > max)
|
|
{
|
|
index[0] = i;
|
|
max = proj;
|
|
}
|
|
}
|
|
|
|
if (max >= threshold)
|
|
{
|
|
const Vec axis = cross(directions[0][index[0]], dir);
|
|
qreal angle = asin(axis.norm());
|
|
if (directions[0][index[0]] * dir >= 0.0)
|
|
angle = -angle;
|
|
rotate(rotation().inverse() * Quaternion(axis, angle) * orientation());
|
|
}
|
|
}
|
|
|
|
if (move)
|
|
{
|
|
Vec center;
|
|
if (frame)
|
|
center = frame->position();
|
|
|
|
translate(center - orientation().rotate(old.coordinatesOf(center)) - translation());
|
|
}
|
|
}
|
|
|
|
/*! Translates the Frame so that its position() lies on the line defined by \p origin and \p
|
|
direction (defined in the world coordinate system).
|
|
|
|
Simply uses an orthogonal projection. \p direction does not need to be normalized. */
|
|
void Frame::projectOnLine(const Vec& origin, const Vec& direction)
|
|
{
|
|
// If you are trying to find a bug here, because of memory problems, you waste your time.
|
|
// This is a bug in the gcc 3.3 compiler. Compile the library in debug mode and test.
|
|
// Uncommenting this line also seems to solve the problem. Horrible.
|
|
// cout << "position = " << position() << endl;
|
|
// If you found a problem or are using a different compiler, please let me know.
|
|
const Vec shift = origin - position();
|
|
Vec proj = shift;
|
|
proj.projectOnAxis(direction);
|
|
translate(shift-proj);
|
|
}
|