summaryrefslogtreecommitdiffstats
path: root/src/animation/frontend/qkeyframeanimation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/animation/frontend/qkeyframeanimation.cpp')
-rw-r--r--src/animation/frontend/qkeyframeanimation.cpp415
1 files changed, 415 insertions, 0 deletions
diff --git a/src/animation/frontend/qkeyframeanimation.cpp b/src/animation/frontend/qkeyframeanimation.cpp
new file mode 100644
index 000000000..597b34cee
--- /dev/null
+++ b/src/animation/frontend/qkeyframeanimation.cpp
@@ -0,0 +1,415 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkeyframeanimation.h"
+#include "Qt3DAnimation/private/qkeyframeanimation_p.h"
+
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DAnimation {
+
+/*!
+ \class Qt3DAnimation::QKeyframeAnimation
+ \brief A class implementing simple keyframe animation to a QTransform
+ \inmodule Qt3DAnimation
+ \since 5.9
+ \inherits Qt3DAnimation::QAbstractAnimation
+
+ A Qt3DAnimation::QKeyframeAnimation class implements simple keyframe animation
+ that can be used to animate \l QTransform. The keyframes consists of multiple
+ timed QTransforms, which are interpolated and applied to the target \l QTransform.
+ \l QEasingCurve is used between keyframes to control the interpolator. RepeatMode
+ can be set for when the position set to the QKeyframeAnimation is below or above
+ the values defined in the keyframe positions.
+*/
+
+/*!
+ \qmltype KeyframeAnimation
+ \brief A type implementing simple keyframe animation to a Transform
+ \inqmlmodule Qt3D.Animation
+ \since 5.9
+ \inherits AbstractAnimation
+ \instantiates Qt3DAnimation::QKeyframeAnimation
+
+ A KeyframeAnimation type implements simple keyframe animation
+ that can be used to animate \l Transform. The keyframes consists of multiple
+ timed \l {Qt3D.Core::Transform}s, which are interpolated and applied
+ to the target Transform. EasingCurve is used between keyframes to control
+ the interpolator. RepeatMode can be set for when the position set to the
+ KeyframeAnimation is less or or greater than the values defined in the keyframe positions.
+*/
+
+/*!
+ \property Qt3DAnimation::QKeyframeAnimation::framePositions
+ Holds the positions of the keyframes. Each position in the list specifies the position
+ of the corresponding keyframe with the same index. The values must be in an ascending order.
+ Values can be positive or negative and do not have any predefined unit.
+*/
+/*!
+ \property Qt3DAnimation::QKeyframeAnimation::target
+ Holds the target QTransform the animation is applied to.
+*/
+/*!
+ \property Qt3DAnimation::QKeyframeAnimation::easing
+ Holds the easing curve of the interpolator between keyframes.
+*/
+/*!
+ \property Qt3DAnimation::QKeyframeAnimation::targetName
+ Holds the name of the target transform. This is a convenience property making it
+ easier to match the target transform to the keyframe animation. The name
+ is usually same as the name of the parent entity of the target transform, but
+ does not have to be.
+*/
+/*!
+ \property Qt3DAnimation::QKeyframeAnimation::startMode
+ Holds the repeat mode for the position values less than the first frame position.
+*/
+/*!
+ \property Qt3DAnimation::QKeyframeAnimation::endMode
+ Holds the repeat mode for the position values greater than the last frame position.
+*/
+/*!
+ \enum QKeyframeAnimation::RepeatMode
+
+ This enumeration specifies how position values outside keyframe values are handled.
+ \value None The animation is not applied to the target transform.
+ \value Constant The edge keyframe value is used.
+ \value Repeat The animation is repeated.
+*/
+/*!
+ \qmlproperty list<real> KeyframeAnimation::framePositions
+ Holds the positions of the keyframes. Each position in the list specifies the position
+ of the corresponding keyframe. The values must be in an ascending order. Values can
+ be positive or negative and do not have any predefined unit.
+*/
+/*!
+ \qmlproperty Transform KeyframeAnimation::target
+ Holds the target Transform the animation is applied to.
+*/
+/*!
+ \qmlproperty EasingCurve KeyframeAnimation::easing
+ Holds the easing curve of the interpolator between keyframes.
+*/
+/*!
+ \qmlproperty string KeyframeAnimation::targetName
+ Holds the name of the target transform. This is a convenience property making it
+ easier to match the target transform to the keyframe animation. The name
+ is usually same as the name of the parent entity of the target transform, but
+ does not have to be.
+*/
+/*!
+ \qmlproperty enumeration KeyframeAnimation::startMode
+ Holds the repeat mode for the position values less than the first frame position.
+ \list
+ \li None
+ \li Constant
+ \li Repeat
+ \endlist
+*/
+/*!
+ \qmlproperty enumeration KeyframeAnimation::endMode
+ Holds the repeat mode for the position values greater than the last frame position.
+ \list
+ \li None
+ \li Constant
+ \li Repeat
+ \endlist
+*/
+/*!
+ \qmlproperty list<Transform> KeyframeAnimation::keyframes
+ Holds the list of keyframes in the keyframe animation.
+*/
+
+QKeyframeAnimationPrivate::QKeyframeAnimationPrivate()
+ : QAbstractAnimationPrivate(QAbstractAnimation::KeyframeAnimation)
+ , m_target(nullptr)
+ , m_minposition(0.0f)
+ , m_maxposition(0.0f)
+ , m_startMode(QKeyframeAnimation::Constant)
+ , m_endMode(QKeyframeAnimation::Constant)
+{
+
+}
+
+/*!
+ Constructs an QKeyframeAnimation with \a parent.
+*/
+QKeyframeAnimation::QKeyframeAnimation(QObject *parent)
+ : QAbstractAnimation(*new QKeyframeAnimationPrivate(), parent)
+{
+ Q_D(QKeyframeAnimation);
+ d->m_positionConnection = QObject::connect(this, &QAbstractAnimation::positionChanged,
+ this, &QKeyframeAnimation::updateAnimation);
+}
+
+
+void QKeyframeAnimation::setFramePositions(const QVector<float> &positions)
+{
+ Q_D(QKeyframeAnimation);
+ d->m_framePositions = positions;
+ d->m_position = -1.0f;
+ if (d->m_framePositions.size() == 0) {
+ d->m_minposition = d->m_maxposition = 0.0f;
+ return;
+ }
+ d->m_minposition = d->m_framePositions.first();
+ d->m_maxposition = d->m_framePositions.last();
+ float lastPos = d->m_minposition;
+ for (float p : qAsConst(d->m_framePositions)) {
+ if (p < lastPos || p > d->m_maxposition)
+ qWarning() << "positions not ordered correctly";
+ lastPos = p;
+ }
+ setDuration(d->m_maxposition);
+}
+
+/*!
+ Sets the \a keyframes of the animation. Old keyframes are cleared.
+ */
+void QKeyframeAnimation::setKeyframes(const QVector<Qt3DCore::QTransform *> &keyframes)
+{
+ Q_D(QKeyframeAnimation);
+ d->m_keyframes = keyframes;
+}
+
+// slerp which allows long path
+QQuaternion lslerp(QQuaternion q1, QQuaternion q2, float t)
+{
+ QQuaternion ret;
+ // Handle the easy cases first.
+ if (t <= 0.0f)
+ return q1;
+ else if (t >= 1.0f)
+ return q2;
+
+ float cos = qBound(-1.0f, QQuaternion::dotProduct(q1, q2), 1.0f);
+ float angle = std::acos(cos);
+ float sin = std::sin(angle);
+ if (!qFuzzyIsNull(sin)) {
+ float a = std::sin((1.0 - t) * angle) / sin;
+ float b = std::sin(t * angle) / sin;
+ ret = (q1 * a + q2 * b).normalized();
+ } else {
+ ret = q1 * (1.0f-t) + q2 * t;
+ }
+ return ret;
+}
+
+void QKeyframeAnimationPrivate::calculateFrame(float position)
+{
+ if (m_target && m_framePositions.size() > 0
+ && m_keyframes.size() == m_framePositions.size()) {
+ if (position < m_minposition) {
+ if (m_startMode == QKeyframeAnimation::None) {
+ return;
+ } else if (m_startMode == QKeyframeAnimation::Constant) {
+ m_target->setRotation(m_keyframes.first()->rotation());
+ m_target->setScale3D(m_keyframes.first()->scale3D());
+ m_target->setTranslation(m_keyframes.first()->translation());
+ return;
+ } else {
+ // must be repeat
+ position = std::fmod(-(position - m_minposition), m_maxposition - m_minposition)
+ + m_minposition;
+ }
+ } else if (position >= m_maxposition) {
+ if (m_endMode == QKeyframeAnimation::None) {
+ return;
+ } else if (m_endMode == QKeyframeAnimation::Constant) {
+ m_target->setRotation(m_keyframes.last()->rotation());
+ m_target->setScale3D(m_keyframes.last()->scale3D());
+ m_target->setTranslation(m_keyframes.last()->translation());
+ return;
+ } else {
+ // must be repeat
+ position = std::fmod(position - m_minposition, m_maxposition - m_minposition)
+ + m_minposition;
+ }
+ }
+ if (position >= m_minposition && position < m_maxposition) {
+ for (int i = 0; i < m_framePositions.size() - 1; i++) {
+ if (position >= m_framePositions.at(i)
+ && position < m_framePositions.at(i+1)) {
+ float ip = (position - m_framePositions.at(i))
+ / (m_framePositions.at(i+1) - m_framePositions.at(i));
+ float eIp = m_easing.valueForProgress(ip);
+ float eIip = 1.0f - eIp;
+
+ Qt3DCore::QTransform *a = m_keyframes.at(i);
+ Qt3DCore::QTransform *b = m_keyframes.at(i+1);
+
+ QVector3D s = a->scale3D() * eIip + b->scale3D() * eIp;
+ QVector3D t = a->translation() * eIip + b->translation() * eIp;
+ QQuaternion r = QQuaternion::slerp(a->rotation(), b->rotation(), eIp);
+
+ m_target->setRotation(r);
+ m_target->setScale3D(s);
+ m_target->setTranslation(t);
+ return;
+ }
+ }
+ }
+ }
+}
+
+void QKeyframeAnimation::updateAnimation(float position)
+{
+ Q_D(QKeyframeAnimation);
+ d->calculateFrame(position);
+}
+
+QVector<float> QKeyframeAnimation::framePositions() const
+{
+ Q_D(const QKeyframeAnimation);
+ return d->m_framePositions;
+}
+
+/*!
+ Returns the list of keyframes.
+ */
+QVector<Qt3DCore::QTransform *> QKeyframeAnimation::keyframeList() const
+{
+ Q_D(const QKeyframeAnimation);
+ return d->m_keyframes;
+}
+
+void QKeyframeAnimation::setTarget(Qt3DCore::QTransform *target)
+{
+ Q_D(QKeyframeAnimation);
+ if (d->m_target != target) {
+ d->m_target = target;
+ emit targetChanged(d->m_target);
+ d->m_position = -1.0f;
+
+ if (target) {
+ d->m_baseScale = target->scale3D();
+ d->m_baseTranslation = target->translation();
+ d->m_baseRotation = target->rotation();
+ }
+ }
+}
+
+QKeyframeAnimation::RepeatMode QKeyframeAnimation::startMode() const
+{
+ Q_D(const QKeyframeAnimation);
+ return d->m_startMode;
+}
+
+QKeyframeAnimation::RepeatMode QKeyframeAnimation::endMode() const
+{
+ Q_D(const QKeyframeAnimation);
+ return d->m_endMode;
+}
+
+void QKeyframeAnimation::setEasing(const QEasingCurve &easing)
+{
+ Q_D(QKeyframeAnimation);
+ if (d->m_easing != easing) {
+ d->m_easing = easing;
+ emit easingChanged(easing);
+ }
+}
+
+void QKeyframeAnimation::setTargetName(const QString &name)
+{
+ Q_D(QKeyframeAnimation);
+ if (d->m_targetName != name) {
+ d->m_targetName = name;
+ emit targetNameChanged(name);
+ }
+}
+
+void QKeyframeAnimation::setStartMode(QKeyframeAnimation::RepeatMode mode)
+{
+ Q_D(QKeyframeAnimation);
+ if (d->m_startMode != mode) {
+ d->m_startMode = mode;
+ emit startModeChanged(mode);
+ }
+}
+
+void QKeyframeAnimation::setEndMode(QKeyframeAnimation::RepeatMode mode)
+{
+ Q_D(QKeyframeAnimation);
+ if (mode != d->m_endMode) {
+ d->m_endMode = mode;
+ emit endModeChanged(mode);
+ }
+}
+
+/*!
+ Adds new \a keyframe at the end of the animation. The QTransform can
+ be added to the animation multiple times.
+ */
+void QKeyframeAnimation::addKeyframe(Qt3DCore::QTransform *keyframe)
+{
+ Q_D(QKeyframeAnimation);
+ d->m_keyframes.push_back(keyframe);
+}
+
+/*!
+ Removes a \a keyframe from the animation. If the same QTransform
+ is set as keyframe multiple times, all occurrences are removed.
+ */
+void QKeyframeAnimation::removeKeyframe(Qt3DCore::QTransform *keyframe)
+{
+ Q_D(QKeyframeAnimation);
+ d->m_keyframes.removeAll(keyframe);
+}
+
+QString QKeyframeAnimation::targetName() const
+{
+ Q_D(const QKeyframeAnimation);
+ return d->m_targetName;
+}
+
+QEasingCurve QKeyframeAnimation::easing() const
+{
+ Q_D(const QKeyframeAnimation);
+ return d->m_easing;
+}
+
+Qt3DCore::QTransform *QKeyframeAnimation::target() const
+{
+ Q_D(const QKeyframeAnimation);
+ return d->m_target;
+}
+
+} // Qt3DAnimation
+
+QT_END_NAMESPACE