diff options
Diffstat (limited to 'src/animation/frontend/qkeyframeanimation.cpp')
-rw-r--r-- | src/animation/frontend/qkeyframeanimation.cpp | 415 |
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 |