1582 lines
52 KiB
C++
1582 lines
52 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the QtCore module of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** 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 https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
/*
|
|
|
|
| *property* | *Used for type* |
|
|
| period | QEasingCurve::{In,Out,InOut,OutIn}Elastic |
|
|
| amplitude | QEasingCurve::{In,Out,InOut,OutIn}Bounce, QEasingCurve::{In,Out,InOut,OutIn}Elastic |
|
|
| overshoot | QEasingCurve::{In,Out,InOut,OutIn}Back |
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
|
\class QEasingCurve
|
|
\inmodule QtCore
|
|
\since 4.6
|
|
\ingroup animation
|
|
\brief The QEasingCurve class provides easing curves for controlling animation.
|
|
|
|
Easing curves describe a function that controls how the speed of the interpolation
|
|
between 0 and 1 should be. Easing curves allow transitions from
|
|
one value to another to appear more natural than a simple constant speed would allow.
|
|
The QEasingCurve class is usually used in conjunction with the QVariantAnimation and
|
|
QPropertyAnimation classes but can be used on its own. It is usually used to accelerate
|
|
the interpolation from zero velocity (ease in) or decelerate to zero velocity (ease out).
|
|
Ease in and ease out can also be combined in the same easing curve.
|
|
|
|
To calculate the speed of the interpolation, the easing curve provides the function
|
|
valueForProgress(), where the \a progress argument specifies the progress of the
|
|
interpolation: 0 is the start value of the interpolation, 1 is the end value of the
|
|
interpolation. The returned value is the effective progress of the interpolation.
|
|
If the returned value is the same as the input value for all input values the easing
|
|
curve is a linear curve. This is the default behaviour.
|
|
|
|
For example,
|
|
|
|
\snippet code/src_corelib_tools_qeasingcurve.cpp 0
|
|
|
|
will print the effective progress of the interpolation between 0 and 1.
|
|
|
|
When using a QPropertyAnimation, the associated easing curve will be used to control the
|
|
progress of the interpolation between startValue and endValue:
|
|
|
|
\snippet code/src_corelib_tools_qeasingcurve.cpp 1
|
|
|
|
The ability to set an amplitude, overshoot, or period depends on
|
|
the QEasingCurve type. Amplitude access is available to curves
|
|
that behave as springs such as elastic and bounce curves. Changing
|
|
the amplitude changes the height of the curve. Period access is
|
|
only available to elastic curves and setting a higher period slows
|
|
the rate of bounce. Only curves that have "boomerang" behaviors
|
|
such as the InBack, OutBack, InOutBack, and OutInBack have
|
|
overshoot settings. These curves will interpolate beyond the end
|
|
points and return to the end point, acting similar to a boomerang.
|
|
|
|
The \l{Easing Curves Example} contains samples of QEasingCurve
|
|
types and lets you change the curve settings.
|
|
|
|
*/
|
|
|
|
/*!
|
|
\enum QEasingCurve::Type
|
|
|
|
The type of easing curve.
|
|
|
|
\value Linear \image qeasingcurve-linear.png
|
|
\caption Easing curve for a linear (t) function:
|
|
velocity is constant.
|
|
\value InQuad \image qeasingcurve-inquad.png
|
|
\caption Easing curve for a quadratic (t^2) function:
|
|
accelerating from zero velocity.
|
|
\value OutQuad \image qeasingcurve-outquad.png
|
|
\caption Easing curve for a quadratic (t^2) function:
|
|
decelerating to zero velocity.
|
|
\value InOutQuad \image qeasingcurve-inoutquad.png
|
|
\caption Easing curve for a quadratic (t^2) function:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInQuad \image qeasingcurve-outinquad.png
|
|
\caption Easing curve for a quadratic (t^2) function:
|
|
deceleration until halfway, then acceleration.
|
|
\value InCubic \image qeasingcurve-incubic.png
|
|
\caption Easing curve for a cubic (t^3) function:
|
|
accelerating from zero velocity.
|
|
\value OutCubic \image qeasingcurve-outcubic.png
|
|
\caption Easing curve for a cubic (t^3) function:
|
|
decelerating to zero velocity.
|
|
\value InOutCubic \image qeasingcurve-inoutcubic.png
|
|
\caption Easing curve for a cubic (t^3) function:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInCubic \image qeasingcurve-outincubic.png
|
|
\caption Easing curve for a cubic (t^3) function:
|
|
deceleration until halfway, then acceleration.
|
|
\value InQuart \image qeasingcurve-inquart.png
|
|
\caption Easing curve for a quartic (t^4) function:
|
|
accelerating from zero velocity.
|
|
\value OutQuart \image qeasingcurve-outquart.png
|
|
\caption
|
|
Easing curve for a quartic (t^4) function:
|
|
decelerating to zero velocity.
|
|
\value InOutQuart \image qeasingcurve-inoutquart.png
|
|
\caption
|
|
Easing curve for a quartic (t^4) function:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInQuart \image qeasingcurve-outinquart.png
|
|
\caption
|
|
Easing curve for a quartic (t^4) function:
|
|
deceleration until halfway, then acceleration.
|
|
\value InQuint \image qeasingcurve-inquint.png
|
|
\caption
|
|
Easing curve for a quintic (t^5) easing
|
|
in: accelerating from zero velocity.
|
|
\value OutQuint \image qeasingcurve-outquint.png
|
|
\caption
|
|
Easing curve for a quintic (t^5) function:
|
|
decelerating to zero velocity.
|
|
\value InOutQuint \image qeasingcurve-inoutquint.png
|
|
\caption
|
|
Easing curve for a quintic (t^5) function:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInQuint \image qeasingcurve-outinquint.png
|
|
\caption
|
|
Easing curve for a quintic (t^5) function:
|
|
deceleration until halfway, then acceleration.
|
|
\value InSine \image qeasingcurve-insine.png
|
|
\caption
|
|
Easing curve for a sinusoidal (sin(t)) function:
|
|
accelerating from zero velocity.
|
|
\value OutSine \image qeasingcurve-outsine.png
|
|
\caption
|
|
Easing curve for a sinusoidal (sin(t)) function:
|
|
decelerating to zero velocity.
|
|
\value InOutSine \image qeasingcurve-inoutsine.png
|
|
\caption
|
|
Easing curve for a sinusoidal (sin(t)) function:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInSine \image qeasingcurve-outinsine.png
|
|
\caption
|
|
Easing curve for a sinusoidal (sin(t)) function:
|
|
deceleration until halfway, then acceleration.
|
|
\value InExpo \image qeasingcurve-inexpo.png
|
|
\caption
|
|
Easing curve for an exponential (2^t) function:
|
|
accelerating from zero velocity.
|
|
\value OutExpo \image qeasingcurve-outexpo.png
|
|
\caption
|
|
Easing curve for an exponential (2^t) function:
|
|
decelerating to zero velocity.
|
|
\value InOutExpo \image qeasingcurve-inoutexpo.png
|
|
\caption
|
|
Easing curve for an exponential (2^t) function:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInExpo \image qeasingcurve-outinexpo.png
|
|
\caption
|
|
Easing curve for an exponential (2^t) function:
|
|
deceleration until halfway, then acceleration.
|
|
\value InCirc \image qeasingcurve-incirc.png
|
|
\caption
|
|
Easing curve for a circular (sqrt(1-t^2)) function:
|
|
accelerating from zero velocity.
|
|
\value OutCirc \image qeasingcurve-outcirc.png
|
|
\caption
|
|
Easing curve for a circular (sqrt(1-t^2)) function:
|
|
decelerating to zero velocity.
|
|
\value InOutCirc \image qeasingcurve-inoutcirc.png
|
|
\caption
|
|
Easing curve for a circular (sqrt(1-t^2)) function:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInCirc \image qeasingcurve-outincirc.png
|
|
\caption
|
|
Easing curve for a circular (sqrt(1-t^2)) function:
|
|
deceleration until halfway, then acceleration.
|
|
\value InElastic \image qeasingcurve-inelastic.png
|
|
\caption
|
|
Easing curve for an elastic
|
|
(exponentially decaying sine wave) function:
|
|
accelerating from zero velocity. The peak amplitude
|
|
can be set with the \e amplitude parameter, and the
|
|
period of decay by the \e period parameter.
|
|
\value OutElastic \image qeasingcurve-outelastic.png
|
|
\caption
|
|
Easing curve for an elastic
|
|
(exponentially decaying sine wave) function:
|
|
decelerating to zero velocity. The peak amplitude
|
|
can be set with the \e amplitude parameter, and the
|
|
period of decay by the \e period parameter.
|
|
\value InOutElastic \image qeasingcurve-inoutelastic.png
|
|
\caption
|
|
Easing curve for an elastic
|
|
(exponentially decaying sine wave) function:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInElastic \image qeasingcurve-outinelastic.png
|
|
\caption
|
|
Easing curve for an elastic
|
|
(exponentially decaying sine wave) function:
|
|
deceleration until halfway, then acceleration.
|
|
\value InBack \image qeasingcurve-inback.png
|
|
\caption
|
|
Easing curve for a back (overshooting
|
|
cubic function: (s+1)*t^3 - s*t^2) easing in:
|
|
accelerating from zero velocity.
|
|
\value OutBack \image qeasingcurve-outback.png
|
|
\caption
|
|
Easing curve for a back (overshooting
|
|
cubic function: (s+1)*t^3 - s*t^2) easing out:
|
|
decelerating to zero velocity.
|
|
\value InOutBack \image qeasingcurve-inoutback.png
|
|
\caption
|
|
Easing curve for a back (overshooting
|
|
cubic function: (s+1)*t^3 - s*t^2) easing in/out:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInBack \image qeasingcurve-outinback.png
|
|
\caption
|
|
Easing curve for a back (overshooting
|
|
cubic easing: (s+1)*t^3 - s*t^2) easing out/in:
|
|
deceleration until halfway, then acceleration.
|
|
\value InBounce \image qeasingcurve-inbounce.png
|
|
\caption
|
|
Easing curve for a bounce (exponentially
|
|
decaying parabolic bounce) function: accelerating
|
|
from zero velocity.
|
|
\value OutBounce \image qeasingcurve-outbounce.png
|
|
\caption
|
|
Easing curve for a bounce (exponentially
|
|
decaying parabolic bounce) function: decelerating
|
|
from zero velocity.
|
|
\value InOutBounce \image qeasingcurve-inoutbounce.png
|
|
\caption
|
|
Easing curve for a bounce (exponentially
|
|
decaying parabolic bounce) function easing in/out:
|
|
acceleration until halfway, then deceleration.
|
|
\value OutInBounce \image qeasingcurve-outinbounce.png
|
|
\caption
|
|
Easing curve for a bounce (exponentially
|
|
decaying parabolic bounce) function easing out/in:
|
|
deceleration until halfway, then acceleration.
|
|
\omitvalue InCurve
|
|
\omitvalue OutCurve
|
|
\omitvalue SineCurve
|
|
\omitvalue CosineCurve
|
|
\value BezierSpline Allows defining a custom easing curve using a cubic bezier spline
|
|
\sa addCubicBezierSegment()
|
|
\value TCBSpline Allows defining a custom easing curve using a TCB spline
|
|
\sa addTCBSegment()
|
|
\value Custom This is returned if the user specified a custom curve type with
|
|
setCustomType(). Note that you cannot call setType() with this value,
|
|
but type() can return it.
|
|
\omitvalue NCurveTypes
|
|
*/
|
|
|
|
/*!
|
|
\typedef QEasingCurve::EasingFunction
|
|
|
|
This is a typedef for a pointer to a function with the following
|
|
signature:
|
|
|
|
\snippet code/src_corelib_tools_qeasingcurve.cpp typedef
|
|
*/
|
|
|
|
#include "qeasingcurve.h"
|
|
#include <cmath>
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
#include <QtCore/qdebug.h>
|
|
#include <QtCore/qstring.h>
|
|
#endif
|
|
|
|
#ifndef QT_NO_DATASTREAM
|
|
#include <QtCore/qdatastream.h>
|
|
#endif
|
|
|
|
#include <QtCore/qpoint.h>
|
|
#include <QtCore/qlist.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
static bool isConfigFunction(QEasingCurve::Type type)
|
|
{
|
|
return (type >= QEasingCurve::InElastic
|
|
&& type <= QEasingCurve::OutInBounce) ||
|
|
type == QEasingCurve::BezierSpline ||
|
|
type == QEasingCurve::TCBSpline;
|
|
}
|
|
|
|
struct TCBPoint {
|
|
QPointF _point;
|
|
qreal _t;
|
|
qreal _c;
|
|
qreal _b;
|
|
|
|
TCBPoint() {}
|
|
TCBPoint(QPointF point, qreal t, qreal c, qreal b) : _point(point), _t(t), _c(c), _b(b) {}
|
|
|
|
bool operator==(const TCBPoint &other) const
|
|
{
|
|
return _point == other._point &&
|
|
qFuzzyCompare(_t, other._t) &&
|
|
qFuzzyCompare(_c, other._c) &&
|
|
qFuzzyCompare(_b, other._b);
|
|
}
|
|
};
|
|
Q_DECLARE_TYPEINFO(TCBPoint, Q_PRIMITIVE_TYPE);
|
|
|
|
QDataStream &operator<<(QDataStream &stream, const TCBPoint &point)
|
|
{
|
|
stream << point._point
|
|
<< point._t
|
|
<< point._c
|
|
<< point._b;
|
|
return stream;
|
|
}
|
|
|
|
QDataStream &operator>>(QDataStream &stream, TCBPoint &point)
|
|
{
|
|
stream >> point._point
|
|
>> point._t
|
|
>> point._c
|
|
>> point._b;
|
|
return stream;
|
|
}
|
|
|
|
typedef QList<TCBPoint> TCBPoints;
|
|
|
|
class QEasingCurveFunction
|
|
{
|
|
public:
|
|
QEasingCurveFunction(QEasingCurve::Type type, qreal period = 0.3, qreal amplitude = 1.0,
|
|
qreal overshoot = 1.70158)
|
|
: _t(type), _p(period), _a(amplitude), _o(overshoot)
|
|
{ }
|
|
virtual ~QEasingCurveFunction() {}
|
|
virtual qreal value(qreal t);
|
|
virtual QEasingCurveFunction *copy() const;
|
|
bool operator==(const QEasingCurveFunction &other) const;
|
|
|
|
QEasingCurve::Type _t;
|
|
qreal _p;
|
|
qreal _a;
|
|
qreal _o;
|
|
QList<QPointF> _bezierCurves;
|
|
TCBPoints _tcbPoints;
|
|
|
|
};
|
|
|
|
QDataStream &operator<<(QDataStream &stream, QEasingCurveFunction *func)
|
|
{
|
|
if (func) {
|
|
stream << func->_p;
|
|
stream << func->_a;
|
|
stream << func->_o;
|
|
if (stream.version() > QDataStream::Qt_5_12) {
|
|
stream << func->_bezierCurves;
|
|
stream << func->_tcbPoints;
|
|
}
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
QDataStream &operator>>(QDataStream &stream, QEasingCurveFunction *func)
|
|
{
|
|
if (func) {
|
|
stream >> func->_p;
|
|
stream >> func->_a;
|
|
stream >> func->_o;
|
|
if (stream.version() > QDataStream::Qt_5_12) {
|
|
stream >> func->_bezierCurves;
|
|
stream >> func->_tcbPoints;
|
|
}
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve);
|
|
|
|
qreal QEasingCurveFunction::value(qreal t)
|
|
{
|
|
QEasingCurve::EasingFunction func = curveToFunc(_t);
|
|
return func(t);
|
|
}
|
|
|
|
QEasingCurveFunction *QEasingCurveFunction::copy() const
|
|
{
|
|
QEasingCurveFunction *rv = new QEasingCurveFunction(_t, _p, _a, _o);
|
|
rv->_bezierCurves = _bezierCurves;
|
|
rv->_tcbPoints = _tcbPoints;
|
|
return rv;
|
|
}
|
|
|
|
bool QEasingCurveFunction::operator==(const QEasingCurveFunction &other) const
|
|
{
|
|
return _t == other._t &&
|
|
qFuzzyCompare(_p, other._p) &&
|
|
qFuzzyCompare(_a, other._a) &&
|
|
qFuzzyCompare(_o, other._o) &&
|
|
_bezierCurves == other._bezierCurves &&
|
|
_tcbPoints == other._tcbPoints;
|
|
}
|
|
|
|
QT_BEGIN_INCLUDE_NAMESPACE
|
|
#include "../../3rdparty/easing/easing.cpp"
|
|
QT_END_INCLUDE_NAMESPACE
|
|
|
|
class QEasingCurvePrivate
|
|
{
|
|
public:
|
|
QEasingCurvePrivate()
|
|
: type(QEasingCurve::Linear),
|
|
config(nullptr),
|
|
func(&easeNone)
|
|
{ }
|
|
QEasingCurvePrivate(const QEasingCurvePrivate &other)
|
|
: type(other.type),
|
|
config(other.config ? other.config->copy() : nullptr),
|
|
func(other.func)
|
|
{ }
|
|
~QEasingCurvePrivate() { delete config; }
|
|
void setType_helper(QEasingCurve::Type);
|
|
|
|
QEasingCurve::Type type;
|
|
QEasingCurveFunction *config;
|
|
QEasingCurve::EasingFunction func;
|
|
};
|
|
|
|
struct BezierEase : public QEasingCurveFunction
|
|
{
|
|
struct SingleCubicBezier {
|
|
qreal p0x, p0y;
|
|
qreal p1x, p1y;
|
|
qreal p2x, p2y;
|
|
qreal p3x, p3y;
|
|
};
|
|
|
|
QList<SingleCubicBezier> _curves;
|
|
QList<qreal> _intervals;
|
|
int _curveCount;
|
|
bool _init;
|
|
bool _valid;
|
|
|
|
BezierEase(QEasingCurve::Type type = QEasingCurve::BezierSpline)
|
|
: QEasingCurveFunction(type), _curves(10), _intervals(10), _init(false), _valid(false)
|
|
{ }
|
|
|
|
void init()
|
|
{
|
|
if (_bezierCurves.constLast() == QPointF(1.0, 1.0)) {
|
|
_init = true;
|
|
_curveCount = _bezierCurves.count() / 3;
|
|
|
|
for (int i=0; i < _curveCount; i++) {
|
|
_intervals[i] = _bezierCurves.at(i * 3 + 2).x();
|
|
|
|
if (i == 0) {
|
|
_curves[0].p0x = 0.0;
|
|
_curves[0].p0y = 0.0;
|
|
|
|
_curves[0].p1x = _bezierCurves.at(0).x();
|
|
_curves[0].p1y = _bezierCurves.at(0).y();
|
|
|
|
_curves[0].p2x = _bezierCurves.at(1).x();
|
|
_curves[0].p2y = _bezierCurves.at(1).y();
|
|
|
|
_curves[0].p3x = _bezierCurves.at(2).x();
|
|
_curves[0].p3y = _bezierCurves.at(2).y();
|
|
|
|
} else if (i == (_curveCount - 1)) {
|
|
_curves[i].p0x = _bezierCurves.at(_bezierCurves.count() - 4).x();
|
|
_curves[i].p0y = _bezierCurves.at(_bezierCurves.count() - 4).y();
|
|
|
|
_curves[i].p1x = _bezierCurves.at(_bezierCurves.count() - 3).x();
|
|
_curves[i].p1y = _bezierCurves.at(_bezierCurves.count() - 3).y();
|
|
|
|
_curves[i].p2x = _bezierCurves.at(_bezierCurves.count() - 2).x();
|
|
_curves[i].p2y = _bezierCurves.at(_bezierCurves.count() - 2).y();
|
|
|
|
_curves[i].p3x = _bezierCurves.at(_bezierCurves.count() - 1).x();
|
|
_curves[i].p3y = _bezierCurves.at(_bezierCurves.count() - 1).y();
|
|
} else {
|
|
_curves[i].p0x = _bezierCurves.at(i * 3 - 1).x();
|
|
_curves[i].p0y = _bezierCurves.at(i * 3 - 1).y();
|
|
|
|
_curves[i].p1x = _bezierCurves.at(i * 3).x();
|
|
_curves[i].p1y = _bezierCurves.at(i * 3).y();
|
|
|
|
_curves[i].p2x = _bezierCurves.at(i * 3 + 1).x();
|
|
_curves[i].p2y = _bezierCurves.at(i * 3 + 1).y();
|
|
|
|
_curves[i].p3x = _bezierCurves.at(i * 3 + 2).x();
|
|
_curves[i].p3y = _bezierCurves.at(i * 3 + 2).y();
|
|
}
|
|
}
|
|
_valid = true;
|
|
} else {
|
|
_valid = false;
|
|
}
|
|
}
|
|
|
|
QEasingCurveFunction *copy() const override
|
|
{
|
|
BezierEase *rv = new BezierEase();
|
|
rv->_t = _t;
|
|
rv->_p = _p;
|
|
rv->_a = _a;
|
|
rv->_o = _o;
|
|
rv->_bezierCurves = _bezierCurves;
|
|
rv->_tcbPoints = _tcbPoints;
|
|
return rv;
|
|
}
|
|
|
|
void getBezierSegment(SingleCubicBezier * &singleCubicBezier, qreal x)
|
|
{
|
|
|
|
int currentSegment = 0;
|
|
|
|
while (currentSegment < _curveCount) {
|
|
if (x <= _intervals.data()[currentSegment])
|
|
break;
|
|
currentSegment++;
|
|
}
|
|
|
|
singleCubicBezier = &_curves.data()[currentSegment];
|
|
}
|
|
|
|
|
|
qreal static inline newtonIteration(const SingleCubicBezier &singleCubicBezier, qreal t, qreal x)
|
|
{
|
|
qreal currentXValue = evaluateForX(singleCubicBezier, t);
|
|
|
|
const qreal newT = t - (currentXValue - x) / evaluateDerivateForX(singleCubicBezier, t);
|
|
|
|
return newT;
|
|
}
|
|
|
|
qreal value(qreal x) override
|
|
{
|
|
Q_ASSERT(_bezierCurves.count() % 3 == 0);
|
|
|
|
if (_bezierCurves.isEmpty()) {
|
|
return x;
|
|
}
|
|
|
|
if (!_init)
|
|
init();
|
|
|
|
if (!_valid) {
|
|
qWarning("QEasingCurve: Invalid bezier curve");
|
|
return x;
|
|
}
|
|
|
|
// The bezier computation is not always precise on the endpoints, so handle explicitly
|
|
if (!(x > 0))
|
|
return 0;
|
|
if (!(x < 1))
|
|
return 1;
|
|
|
|
SingleCubicBezier *singleCubicBezier = nullptr;
|
|
getBezierSegment(singleCubicBezier, x);
|
|
|
|
return evaluateSegmentForY(*singleCubicBezier, findTForX(*singleCubicBezier, x));
|
|
}
|
|
|
|
qreal static inline evaluateSegmentForY(const SingleCubicBezier &singleCubicBezier, qreal t)
|
|
{
|
|
const qreal p0 = singleCubicBezier.p0y;
|
|
const qreal p1 = singleCubicBezier.p1y;
|
|
const qreal p2 = singleCubicBezier.p2y;
|
|
const qreal p3 = singleCubicBezier.p3y;
|
|
|
|
const qreal s = 1 - t;
|
|
|
|
const qreal s_squared = s*s;
|
|
const qreal t_squared = t*t;
|
|
|
|
const qreal s_cubic = s_squared * s;
|
|
const qreal t_cubic = t_squared * t;
|
|
|
|
return s_cubic * p0 + 3 * s_squared * t * p1 + 3 * s * t_squared * p2 + t_cubic * p3;
|
|
}
|
|
|
|
qreal static inline evaluateForX(const SingleCubicBezier &singleCubicBezier, qreal t)
|
|
{
|
|
const qreal p0 = singleCubicBezier.p0x;
|
|
const qreal p1 = singleCubicBezier.p1x;
|
|
const qreal p2 = singleCubicBezier.p2x;
|
|
const qreal p3 = singleCubicBezier.p3x;
|
|
|
|
const qreal s = 1 - t;
|
|
|
|
const qreal s_squared = s*s;
|
|
const qreal t_squared = t*t;
|
|
|
|
const qreal s_cubic = s_squared * s;
|
|
const qreal t_cubic = t_squared * t;
|
|
|
|
return s_cubic * p0 + 3 * s_squared * t * p1 + 3 * s * t_squared * p2 + t_cubic * p3;
|
|
}
|
|
|
|
qreal static inline evaluateDerivateForX(const SingleCubicBezier &singleCubicBezier, qreal t)
|
|
{
|
|
const qreal p0 = singleCubicBezier.p0x;
|
|
const qreal p1 = singleCubicBezier.p1x;
|
|
const qreal p2 = singleCubicBezier.p2x;
|
|
const qreal p3 = singleCubicBezier.p3x;
|
|
|
|
const qreal t_squared = t*t;
|
|
|
|
return -3*p0 + 3*p1 + 6*p0*t - 12*p1*t + 6*p2*t + 3*p3*t_squared - 3*p0*t_squared + 9*p1*t_squared - 9*p2*t_squared;
|
|
}
|
|
|
|
qreal static inline _cbrt(qreal d)
|
|
{
|
|
qreal sign = 1;
|
|
if (d < 0)
|
|
sign = -1;
|
|
d = d * sign;
|
|
|
|
qreal t = _fast_cbrt(d);
|
|
|
|
//one step of Halley's Method to get a better approximation
|
|
const qreal t_cubic = t * t * t;
|
|
const qreal f = t_cubic + t_cubic + d;
|
|
if (f != qreal(0.0))
|
|
t = t * (t_cubic + d + d) / f;
|
|
|
|
//another step
|
|
/*qreal t_i = t;
|
|
t_i_cubic = pow(t_i, 3);
|
|
t = t_i * (t_i_cubic + d + d) / (t_i_cubic + t_i_cubic + d);*/
|
|
|
|
return t * sign;
|
|
}
|
|
|
|
float static inline _fast_cbrt(float x)
|
|
{
|
|
union {
|
|
float f;
|
|
quint32 i;
|
|
} ux;
|
|
|
|
const unsigned int B1 = 709921077;
|
|
|
|
ux.f = x;
|
|
ux.i = (ux.i / 3 + B1);
|
|
|
|
return ux.f;
|
|
}
|
|
|
|
double static inline _fast_cbrt(double d)
|
|
{
|
|
union {
|
|
double d;
|
|
quint32 pt[2];
|
|
} ut, ux;
|
|
|
|
const unsigned int B1 = 715094163;
|
|
|
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
|
const int h0 = 1;
|
|
#else
|
|
const int h0 = 0;
|
|
#endif
|
|
ut.d = 0.0;
|
|
ux.d = d;
|
|
|
|
quint32 hx = ux.pt[h0]; //high word of d
|
|
ut.pt[h0] = hx / 3 + B1;
|
|
|
|
return ut.d;
|
|
}
|
|
|
|
qreal static inline _acos(qreal x)
|
|
{
|
|
return std::sqrt(1-x)*(1.5707963267948966192313216916398f + x*(-0.213300989f + x*(0.077980478f + x*-0.02164095f)));
|
|
}
|
|
|
|
qreal static inline _cos(qreal x) //super fast _cos
|
|
{
|
|
const qreal pi_times2 = 2 * M_PI;
|
|
const qreal pi_neg = -1 * M_PI;
|
|
const qreal pi_by2 = M_PI / 2.0;
|
|
|
|
x += pi_by2; //the polynom is for sin
|
|
|
|
if (x < pi_neg)
|
|
x += pi_times2;
|
|
else if (x > M_PI)
|
|
x -= pi_times2;
|
|
|
|
const qreal a = 0.405284735;
|
|
const qreal b = 1.27323954;
|
|
|
|
const qreal x_squared = x * x;
|
|
|
|
if (x < 0) {
|
|
qreal cos = b * x + a * x_squared;
|
|
|
|
if (cos < 0)
|
|
return 0.225 * (cos * -1 * cos - cos) + cos;
|
|
return 0.225 * (cos * cos - cos) + cos;
|
|
} //else
|
|
|
|
qreal cos = b * x - a * x_squared;
|
|
|
|
if (cos < 0)
|
|
return 0.225 * (cos * 1 *-cos - cos) + cos;
|
|
return 0.225 * (cos * cos - cos) + cos;
|
|
}
|
|
|
|
bool static inline inRange(qreal f)
|
|
{
|
|
return (f >= -0.01 && f <= 1.01);
|
|
}
|
|
|
|
void static inline cosacos(qreal x, qreal &s1, qreal &s2, qreal &s3 )
|
|
{
|
|
//This function has no proper algebraic representation in real numbers.
|
|
//We use approximations instead
|
|
|
|
const qreal x_squared = x * x;
|
|
const qreal x_plus_one_sqrt = qSqrt(1.0 + x);
|
|
const qreal one_minus_x_sqrt = qSqrt(1.0 - x);
|
|
|
|
//cos(acos(x) / 3)
|
|
//s1 = _cos(_acos(x) / 3);
|
|
s1 = 0.463614 - 0.0347815 * x + 0.00218245 * x_squared + 0.402421 * x_plus_one_sqrt;
|
|
|
|
//cos(acos((x) - M_PI) / 3)
|
|
//s3 = _cos((_acos(x) - M_PI) / 3);
|
|
s3 = 0.463614 + 0.402421 * one_minus_x_sqrt + 0.0347815 * x + 0.00218245 * x_squared;
|
|
|
|
//cos((acos(x) + M_PI) / 3)
|
|
//s2 = _cos((_acos(x) + M_PI) / 3);
|
|
s2 = -0.401644 * one_minus_x_sqrt - 0.0686804 * x + 0.401644 * x_plus_one_sqrt;
|
|
}
|
|
|
|
qreal static inline singleRealSolutionForCubic(qreal a, qreal b, qreal c)
|
|
{
|
|
//returns the real solutiuon in [0..1]
|
|
//We use the Cardano formula
|
|
|
|
//substituiton: x = z - a/3
|
|
// z^3+pz+q=0
|
|
|
|
if (c < 0.000001 && c > -0.000001)
|
|
return 0;
|
|
|
|
const qreal a_by3 = a / 3.0;
|
|
|
|
const qreal a_cubic = a * a * a;
|
|
|
|
const qreal p = b - a * a_by3;
|
|
const qreal q = 2.0 * a_cubic / 27.0 - a * b / 3.0 + c;
|
|
|
|
const qreal q_squared = q * q;
|
|
const qreal p_cubic = p * p * p;
|
|
const qreal D = 0.25 * q_squared + p_cubic / 27.0;
|
|
|
|
if (D >= 0) {
|
|
const qreal D_sqrt = qSqrt(D);
|
|
qreal u = _cbrt( -q * 0.5 + D_sqrt);
|
|
qreal v = _cbrt( -q * 0.5 - D_sqrt);
|
|
qreal z1 = u + v;
|
|
|
|
qreal t1 = z1 - a_by3;
|
|
|
|
if (inRange(t1))
|
|
return t1;
|
|
qreal z2 = -1 *u;
|
|
qreal t2 = z2 - a_by3;
|
|
return t2;
|
|
}
|
|
|
|
//casus irreducibilis
|
|
const qreal p_minus_sqrt = qSqrt(-p);
|
|
|
|
//const qreal f = sqrt(4.0 / 3.0 * -p);
|
|
const qreal f = qSqrt(4.0 / 3.0) * p_minus_sqrt;
|
|
|
|
//const qreal sqrtP = sqrt(27.0 / -p_cubic);
|
|
const qreal sqrtP = -3.0*qSqrt(3.0) / (p_minus_sqrt * p);
|
|
|
|
|
|
const qreal g = -q * 0.5 * sqrtP;
|
|
|
|
qreal s1;
|
|
qreal s2;
|
|
qreal s3;
|
|
|
|
cosacos(g, s1, s2, s3);
|
|
|
|
qreal z1 = -1* f * s2;
|
|
qreal t1 = z1 - a_by3;
|
|
if (inRange(t1))
|
|
return t1;
|
|
|
|
qreal z2 = f * s1;
|
|
qreal t2 = z2 - a_by3;
|
|
if (inRange(t2))
|
|
return t2;
|
|
|
|
qreal z3 = -1 * f * s3;
|
|
qreal t3 = z3 - a_by3;
|
|
return t3;
|
|
}
|
|
|
|
bool static inline almostZero(qreal value)
|
|
{
|
|
// 1e-3 might seem excessively fuzzy, but any smaller value will make the
|
|
// factors a, b, and c large enough to knock out the cubic solver.
|
|
return value > -1e-3 && value < 1e-3;
|
|
}
|
|
|
|
qreal static inline findTForX(const SingleCubicBezier &singleCubicBezier, qreal x)
|
|
{
|
|
const qreal p0 = singleCubicBezier.p0x;
|
|
const qreal p1 = singleCubicBezier.p1x;
|
|
const qreal p2 = singleCubicBezier.p2x;
|
|
const qreal p3 = singleCubicBezier.p3x;
|
|
|
|
const qreal factorT3 = p3 - p0 + 3 * p1 - 3 * p2;
|
|
const qreal factorT2 = 3 * p0 - 6 * p1 + 3 * p2;
|
|
const qreal factorT1 = -3 * p0 + 3 * p1;
|
|
const qreal factorT0 = p0 - x;
|
|
|
|
// Cases for quadratic, linear and invalid equations
|
|
if (almostZero(factorT3)) {
|
|
if (almostZero(factorT2)) {
|
|
if (almostZero(factorT1))
|
|
return 0.0;
|
|
|
|
return -factorT0 / factorT1;
|
|
}
|
|
const qreal discriminant = factorT1 * factorT1 - 4.0 * factorT2 * factorT0;
|
|
if (discriminant < 0.0)
|
|
return 0.0;
|
|
|
|
if (discriminant == 0.0)
|
|
return -factorT1 / (2.0 * factorT2);
|
|
|
|
const qreal solution1 = (-factorT1 + std::sqrt(discriminant)) / (2.0 * factorT2);
|
|
if (solution1 >= 0.0 && solution1 <= 1.0)
|
|
return solution1;
|
|
|
|
const qreal solution2 = (-factorT1 - std::sqrt(discriminant)) / (2.0 * factorT2);
|
|
if (solution2 >= 0.0 && solution2 <= 1.0)
|
|
return solution2;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
const qreal a = factorT2 / factorT3;
|
|
const qreal b = factorT1 / factorT3;
|
|
const qreal c = factorT0 / factorT3;
|
|
|
|
return singleRealSolutionForCubic(a, b, c);
|
|
|
|
//one new iteration to increase numeric stability
|
|
//return newtonIteration(singleCubicBezier, t, x);
|
|
}
|
|
};
|
|
|
|
struct TCBEase : public BezierEase
|
|
{
|
|
TCBEase()
|
|
: BezierEase(QEasingCurve::TCBSpline)
|
|
{ }
|
|
|
|
qreal value(qreal x) override
|
|
{
|
|
Q_ASSERT(_bezierCurves.count() % 3 == 0);
|
|
|
|
if (_bezierCurves.isEmpty()) {
|
|
qWarning("QEasingCurve: Invalid tcb curve");
|
|
return x;
|
|
}
|
|
|
|
return BezierEase::value(x);
|
|
}
|
|
|
|
QEasingCurveFunction *copy() const override
|
|
{
|
|
return new TCBEase{*this};
|
|
}
|
|
};
|
|
|
|
struct ElasticEase : public QEasingCurveFunction
|
|
{
|
|
ElasticEase(QEasingCurve::Type type)
|
|
: QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
|
|
{ }
|
|
|
|
QEasingCurveFunction *copy() const override
|
|
{
|
|
ElasticEase *rv = new ElasticEase(_t);
|
|
rv->_p = _p;
|
|
rv->_a = _a;
|
|
rv->_bezierCurves = _bezierCurves;
|
|
rv->_tcbPoints = _tcbPoints;
|
|
return rv;
|
|
}
|
|
|
|
qreal value(qreal t) override
|
|
{
|
|
qreal p = (_p < 0) ? qreal(0.3) : _p;
|
|
qreal a = (_a < 0) ? qreal(1.0) : _a;
|
|
switch(_t) {
|
|
case QEasingCurve::InElastic:
|
|
return easeInElastic(t, a, p);
|
|
case QEasingCurve::OutElastic:
|
|
return easeOutElastic(t, a, p);
|
|
case QEasingCurve::InOutElastic:
|
|
return easeInOutElastic(t, a, p);
|
|
case QEasingCurve::OutInElastic:
|
|
return easeOutInElastic(t, a, p);
|
|
default:
|
|
return t;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct BounceEase : public QEasingCurveFunction
|
|
{
|
|
BounceEase(QEasingCurve::Type type)
|
|
: QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
|
|
{ }
|
|
|
|
QEasingCurveFunction *copy() const override
|
|
{
|
|
BounceEase *rv = new BounceEase(_t);
|
|
rv->_a = _a;
|
|
rv->_bezierCurves = _bezierCurves;
|
|
rv->_tcbPoints = _tcbPoints;
|
|
return rv;
|
|
}
|
|
|
|
qreal value(qreal t) override
|
|
{
|
|
qreal a = (_a < 0) ? qreal(1.0) : _a;
|
|
switch(_t) {
|
|
case QEasingCurve::InBounce:
|
|
return easeInBounce(t, a);
|
|
case QEasingCurve::OutBounce:
|
|
return easeOutBounce(t, a);
|
|
case QEasingCurve::InOutBounce:
|
|
return easeInOutBounce(t, a);
|
|
case QEasingCurve::OutInBounce:
|
|
return easeOutInBounce(t, a);
|
|
default:
|
|
return t;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct BackEase : public QEasingCurveFunction
|
|
{
|
|
BackEase(QEasingCurve::Type type)
|
|
: QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158))
|
|
{ }
|
|
|
|
QEasingCurveFunction *copy() const override
|
|
{
|
|
BackEase *rv = new BackEase(_t);
|
|
rv->_o = _o;
|
|
rv->_bezierCurves = _bezierCurves;
|
|
rv->_tcbPoints = _tcbPoints;
|
|
return rv;
|
|
}
|
|
|
|
qreal value(qreal t) override
|
|
{
|
|
// The *Back() functions are not always precise on the endpoints, so handle explicitly
|
|
if (!(t > 0))
|
|
return 0;
|
|
if (!(t < 1))
|
|
return 1;
|
|
qreal o = (_o < 0) ? qreal(1.70158) : _o;
|
|
switch(_t) {
|
|
case QEasingCurve::InBack:
|
|
return easeInBack(t, o);
|
|
case QEasingCurve::OutBack:
|
|
return easeOutBack(t, o);
|
|
case QEasingCurve::InOutBack:
|
|
return easeInOutBack(t, o);
|
|
case QEasingCurve::OutInBack:
|
|
return easeOutInBack(t, o);
|
|
default:
|
|
return t;
|
|
}
|
|
}
|
|
};
|
|
|
|
static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve)
|
|
{
|
|
switch(curve) {
|
|
case QEasingCurve::Linear:
|
|
return &easeNone;
|
|
case QEasingCurve::InQuad:
|
|
return &easeInQuad;
|
|
case QEasingCurve::OutQuad:
|
|
return &easeOutQuad;
|
|
case QEasingCurve::InOutQuad:
|
|
return &easeInOutQuad;
|
|
case QEasingCurve::OutInQuad:
|
|
return &easeOutInQuad;
|
|
case QEasingCurve::InCubic:
|
|
return &easeInCubic;
|
|
case QEasingCurve::OutCubic:
|
|
return &easeOutCubic;
|
|
case QEasingCurve::InOutCubic:
|
|
return &easeInOutCubic;
|
|
case QEasingCurve::OutInCubic:
|
|
return &easeOutInCubic;
|
|
case QEasingCurve::InQuart:
|
|
return &easeInQuart;
|
|
case QEasingCurve::OutQuart:
|
|
return &easeOutQuart;
|
|
case QEasingCurve::InOutQuart:
|
|
return &easeInOutQuart;
|
|
case QEasingCurve::OutInQuart:
|
|
return &easeOutInQuart;
|
|
case QEasingCurve::InQuint:
|
|
return &easeInQuint;
|
|
case QEasingCurve::OutQuint:
|
|
return &easeOutQuint;
|
|
case QEasingCurve::InOutQuint:
|
|
return &easeInOutQuint;
|
|
case QEasingCurve::OutInQuint:
|
|
return &easeOutInQuint;
|
|
case QEasingCurve::InSine:
|
|
return &easeInSine;
|
|
case QEasingCurve::OutSine:
|
|
return &easeOutSine;
|
|
case QEasingCurve::InOutSine:
|
|
return &easeInOutSine;
|
|
case QEasingCurve::OutInSine:
|
|
return &easeOutInSine;
|
|
case QEasingCurve::InExpo:
|
|
return &easeInExpo;
|
|
case QEasingCurve::OutExpo:
|
|
return &easeOutExpo;
|
|
case QEasingCurve::InOutExpo:
|
|
return &easeInOutExpo;
|
|
case QEasingCurve::OutInExpo:
|
|
return &easeOutInExpo;
|
|
case QEasingCurve::InCirc:
|
|
return &easeInCirc;
|
|
case QEasingCurve::OutCirc:
|
|
return &easeOutCirc;
|
|
case QEasingCurve::InOutCirc:
|
|
return &easeInOutCirc;
|
|
case QEasingCurve::OutInCirc:
|
|
return &easeOutInCirc;
|
|
// Internal - needed for QTimeLine backward-compatibility:
|
|
case QEasingCurve::InCurve:
|
|
return &easeInCurve;
|
|
case QEasingCurve::OutCurve:
|
|
return &easeOutCurve;
|
|
case QEasingCurve::SineCurve:
|
|
return &easeSineCurve;
|
|
case QEasingCurve::CosineCurve:
|
|
return &easeCosineCurve;
|
|
default:
|
|
return nullptr;
|
|
};
|
|
}
|
|
|
|
static QEasingCurveFunction *curveToFunctionObject(QEasingCurve::Type type)
|
|
{
|
|
switch(type) {
|
|
case QEasingCurve::InElastic:
|
|
case QEasingCurve::OutElastic:
|
|
case QEasingCurve::InOutElastic:
|
|
case QEasingCurve::OutInElastic:
|
|
return new ElasticEase(type);
|
|
case QEasingCurve::OutBounce:
|
|
case QEasingCurve::InBounce:
|
|
case QEasingCurve::OutInBounce:
|
|
case QEasingCurve::InOutBounce:
|
|
return new BounceEase(type);
|
|
case QEasingCurve::InBack:
|
|
case QEasingCurve::OutBack:
|
|
case QEasingCurve::InOutBack:
|
|
case QEasingCurve::OutInBack:
|
|
return new BackEase(type);
|
|
case QEasingCurve::BezierSpline:
|
|
return new BezierEase;
|
|
case QEasingCurve::TCBSpline:
|
|
return new TCBEase;
|
|
default:
|
|
return new QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158));
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/*!
|
|
\fn QEasingCurve::QEasingCurve(QEasingCurve &&other)
|
|
|
|
Move-constructs a QEasingCurve instance, making it point at the same
|
|
object that \a other was pointing to.
|
|
|
|
\since 5.2
|
|
*/
|
|
|
|
/*!
|
|
Constructs an easing curve of the given \a type.
|
|
*/
|
|
QEasingCurve::QEasingCurve(Type type)
|
|
: d_ptr(new QEasingCurvePrivate)
|
|
{
|
|
setType(type);
|
|
}
|
|
|
|
/*!
|
|
Construct a copy of \a other.
|
|
*/
|
|
QEasingCurve::QEasingCurve(const QEasingCurve &other)
|
|
: d_ptr(new QEasingCurvePrivate(*other.d_ptr))
|
|
{
|
|
// ### non-atomic, requires malloc on shallow copy
|
|
}
|
|
|
|
/*!
|
|
Destructor.
|
|
*/
|
|
|
|
QEasingCurve::~QEasingCurve()
|
|
{
|
|
delete d_ptr;
|
|
}
|
|
|
|
/*!
|
|
\fn QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other)
|
|
Copy \a other.
|
|
*/
|
|
|
|
/*!
|
|
\fn QEasingCurve &QEasingCurve::operator=(QEasingCurve &&other)
|
|
|
|
Move-assigns \a other to this QEasingCurve instance.
|
|
|
|
\since 5.2
|
|
*/
|
|
|
|
/*!
|
|
\fn void QEasingCurve::swap(QEasingCurve &other)
|
|
\since 5.0
|
|
|
|
Swaps curve \a other with this curve. This operation is very
|
|
fast and never fails.
|
|
*/
|
|
|
|
/*!
|
|
Compare this easing curve with \a other and returns \c true if they are
|
|
equal. It will also compare the properties of a curve.
|
|
*/
|
|
bool QEasingCurve::operator==(const QEasingCurve &other) const
|
|
{
|
|
bool res = d_ptr->func == other.d_ptr->func
|
|
&& d_ptr->type == other.d_ptr->type;
|
|
if (res) {
|
|
if (d_ptr->config && other.d_ptr->config) {
|
|
// catch the config content
|
|
res = d_ptr->config->operator==(*(other.d_ptr->config));
|
|
|
|
} else if (d_ptr->config || other.d_ptr->config) {
|
|
// one one has a config object, which could contain default values
|
|
res = qFuzzyCompare(amplitude(), other.amplitude()) &&
|
|
qFuzzyCompare(period(), other.period()) &&
|
|
qFuzzyCompare(overshoot(), other.overshoot());
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*!
|
|
\fn bool QEasingCurve::operator!=(const QEasingCurve &other) const
|
|
Compare this easing curve with \a other and returns \c true if they are not equal.
|
|
It will also compare the properties of a curve.
|
|
|
|
\sa operator==()
|
|
*/
|
|
|
|
/*!
|
|
Returns the amplitude. This is not applicable for all curve types.
|
|
It is only applicable for bounce and elastic curves (curves of type()
|
|
QEasingCurve::InBounce, QEasingCurve::OutBounce, QEasingCurve::InOutBounce,
|
|
QEasingCurve::OutInBounce, QEasingCurve::InElastic, QEasingCurve::OutElastic,
|
|
QEasingCurve::InOutElastic or QEasingCurve::OutInElastic).
|
|
*/
|
|
qreal QEasingCurve::amplitude() const
|
|
{
|
|
return d_ptr->config ? d_ptr->config->_a : qreal(1.0);
|
|
}
|
|
|
|
/*!
|
|
Sets the amplitude to \a amplitude.
|
|
|
|
This will set the amplitude of the bounce or the amplitude of the
|
|
elastic "spring" effect. The higher the number, the higher the amplitude.
|
|
\sa amplitude()
|
|
*/
|
|
void QEasingCurve::setAmplitude(qreal amplitude)
|
|
{
|
|
if (!d_ptr->config)
|
|
d_ptr->config = curveToFunctionObject(d_ptr->type);
|
|
d_ptr->config->_a = amplitude;
|
|
}
|
|
|
|
/*!
|
|
Returns the period. This is not applicable for all curve types.
|
|
It is only applicable if type() is QEasingCurve::InElastic, QEasingCurve::OutElastic,
|
|
QEasingCurve::InOutElastic or QEasingCurve::OutInElastic.
|
|
*/
|
|
qreal QEasingCurve::period() const
|
|
{
|
|
return d_ptr->config ? d_ptr->config->_p : qreal(0.3);
|
|
}
|
|
|
|
/*!
|
|
Sets the period to \a period.
|
|
Setting a small period value will give a high frequency of the curve. A
|
|
large period will give it a small frequency.
|
|
|
|
\sa period()
|
|
*/
|
|
void QEasingCurve::setPeriod(qreal period)
|
|
{
|
|
if (!d_ptr->config)
|
|
d_ptr->config = curveToFunctionObject(d_ptr->type);
|
|
d_ptr->config->_p = period;
|
|
}
|
|
|
|
/*!
|
|
Returns the overshoot. This is not applicable for all curve types.
|
|
It is only applicable if type() is QEasingCurve::InBack, QEasingCurve::OutBack,
|
|
QEasingCurve::InOutBack or QEasingCurve::OutInBack.
|
|
*/
|
|
qreal QEasingCurve::overshoot() const
|
|
{
|
|
return d_ptr->config ? d_ptr->config->_o : qreal(1.70158) ;
|
|
}
|
|
|
|
/*!
|
|
Sets the overshoot to \a overshoot.
|
|
|
|
0 produces no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent.
|
|
|
|
\sa overshoot()
|
|
*/
|
|
void QEasingCurve::setOvershoot(qreal overshoot)
|
|
{
|
|
if (!d_ptr->config)
|
|
d_ptr->config = curveToFunctionObject(d_ptr->type);
|
|
d_ptr->config->_o = overshoot;
|
|
}
|
|
|
|
/*!
|
|
Adds a segment of a cubic bezier spline to define a custom easing curve.
|
|
It is only applicable if type() is QEasingCurve::BezierSpline.
|
|
Note that the spline implicitly starts at (0.0, 0.0) and has to end at (1.0, 1.0) to
|
|
be a valid easing curve.
|
|
\a c1 and \a c2 are the control points used for drawing the curve.
|
|
\a endPoint is the endpoint of the curve.
|
|
*/
|
|
void QEasingCurve::addCubicBezierSegment(const QPointF & c1, const QPointF & c2, const QPointF & endPoint)
|
|
{
|
|
if (!d_ptr->config)
|
|
d_ptr->config = curveToFunctionObject(d_ptr->type);
|
|
d_ptr->config->_bezierCurves << c1 << c2 << endPoint;
|
|
}
|
|
|
|
QList<QPointF> static inline tcbToBezier(const TCBPoints &tcbPoints)
|
|
{
|
|
const int count = tcbPoints.count();
|
|
QList<QPointF> bezierPoints;
|
|
bezierPoints.reserve(3 * (count - 1));
|
|
|
|
for (int i = 1; i < count; i++) {
|
|
const qreal t_0 = tcbPoints.at(i - 1)._t;
|
|
const qreal c_0 = tcbPoints.at(i - 1)._c;
|
|
qreal b_0 = -1;
|
|
|
|
qreal const t_1 = tcbPoints.at(i)._t;
|
|
qreal const c_1 = tcbPoints.at(i)._c;
|
|
qreal b_1 = 1;
|
|
|
|
QPointF c_minusOne; //P1 last segment - not available for the first point
|
|
const QPointF c0(tcbPoints.at(i - 1)._point); //P0 Hermite/TBC
|
|
const QPointF c3(tcbPoints.at(i)._point); //P1 Hermite/TBC
|
|
QPointF c4; //P0 next segment - not available for the last point
|
|
|
|
if (i > 1) { //first point no left tangent
|
|
c_minusOne = tcbPoints.at(i - 2)._point;
|
|
b_0 = tcbPoints.at(i - 1)._b;
|
|
}
|
|
|
|
if (i < (count - 1)) { //last point no right tangent
|
|
c4 = tcbPoints.at(i + 1)._point;
|
|
b_1 = tcbPoints.at(i)._b;
|
|
}
|
|
|
|
const qreal dx_0 = 0.5 * (1-t_0) * ((1 + b_0) * (1 + c_0) * (c0.x() - c_minusOne.x()) + (1- b_0) * (1 - c_0) * (c3.x() - c0.x()));
|
|
const qreal dy_0 = 0.5 * (1-t_0) * ((1 + b_0) * (1 + c_0) * (c0.y() - c_minusOne.y()) + (1- b_0) * (1 - c_0) * (c3.y() - c0.y()));
|
|
|
|
const qreal dx_1 = 0.5 * (1-t_1) * ((1 + b_1) * (1 - c_1) * (c3.x() - c0.x()) + (1 - b_1) * (1 + c_1) * (c4.x() - c3.x()));
|
|
const qreal dy_1 = 0.5 * (1-t_1) * ((1 + b_1) * (1 - c_1) * (c3.y() - c0.y()) + (1 - b_1) * (1 + c_1) * (c4.y() - c3.y()));
|
|
|
|
const QPointF d_0 = QPointF(dx_0, dy_0);
|
|
const QPointF d_1 = QPointF(dx_1, dy_1);
|
|
|
|
QPointF c1 = (3 * c0 + d_0) / 3;
|
|
QPointF c2 = (3 * c3 - d_1) / 3;
|
|
bezierPoints << c1 << c2 << c3;
|
|
}
|
|
return bezierPoints;
|
|
}
|
|
|
|
/*!
|
|
Adds a segment of a TCB bezier spline to define a custom easing curve.
|
|
It is only applicable if type() is QEasingCurve::TCBSpline.
|
|
The spline has to start explitly at (0.0, 0.0) and has to end at (1.0, 1.0) to
|
|
be a valid easing curve.
|
|
The tension \a t changes the length of the tangent vector.
|
|
The continuity \a c changes the sharpness in change between the tangents.
|
|
The bias \a b changes the direction of the tangent vector.
|
|
\a nextPoint is the sample position.
|
|
All three parameters are valid between -1 and 1 and define the
|
|
tangent of the control point.
|
|
If all three parameters are 0 the resulting spline is a Catmull-Rom spline.
|
|
The begin and endpoint always have a bias of -1 and 1, since the outer tangent is not defined.
|
|
*/
|
|
void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qreal b)
|
|
{
|
|
if (!d_ptr->config)
|
|
d_ptr->config = curveToFunctionObject(d_ptr->type);
|
|
|
|
d_ptr->config->_tcbPoints.append(TCBPoint(nextPoint, t, c ,b));
|
|
|
|
if (nextPoint == QPointF(1.0, 1.0)) {
|
|
d_ptr->config->_bezierCurves = tcbToBezier(d_ptr->config->_tcbPoints);
|
|
d_ptr->config->_tcbPoints.clear();
|
|
}
|
|
|
|
}
|
|
|
|
/*!
|
|
\since 5.0
|
|
|
|
Returns the cubicBezierSpline that defines a custom easing curve.
|
|
If the easing curve does not have a custom bezier easing curve the list
|
|
is empty.
|
|
*/
|
|
QList<QPointF> QEasingCurve::toCubicSpline() const
|
|
{
|
|
return d_ptr->config ? d_ptr->config->_bezierCurves : QList<QPointF>();
|
|
}
|
|
|
|
/*!
|
|
Returns the type of the easing curve.
|
|
*/
|
|
QEasingCurve::Type QEasingCurve::type() const
|
|
{
|
|
return d_ptr->type;
|
|
}
|
|
|
|
void QEasingCurvePrivate::setType_helper(QEasingCurve::Type newType)
|
|
{
|
|
qreal amp = -1.0;
|
|
qreal period = -1.0;
|
|
qreal overshoot = -1.0;
|
|
QList<QPointF> bezierCurves;
|
|
QList<TCBPoint> tcbPoints;
|
|
|
|
if (config) {
|
|
amp = config->_a;
|
|
period = config->_p;
|
|
overshoot = config->_o;
|
|
bezierCurves = std::move(config->_bezierCurves);
|
|
tcbPoints = std::move(config->_tcbPoints);
|
|
|
|
delete config;
|
|
config = nullptr;
|
|
}
|
|
|
|
if (isConfigFunction(newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0) ||
|
|
!bezierCurves.isEmpty()) {
|
|
config = curveToFunctionObject(newType);
|
|
if (amp != -1.0)
|
|
config->_a = amp;
|
|
if (period != -1.0)
|
|
config->_p = period;
|
|
if (overshoot != -1.0)
|
|
config->_o = overshoot;
|
|
config->_bezierCurves = std::move(bezierCurves);
|
|
config->_tcbPoints = std::move(tcbPoints);
|
|
func = nullptr;
|
|
} else if (newType != QEasingCurve::Custom) {
|
|
func = curveToFunc(newType);
|
|
}
|
|
Q_ASSERT((func == nullptr) == (config != nullptr));
|
|
type = newType;
|
|
}
|
|
|
|
/*!
|
|
Sets the type of the easing curve to \a type.
|
|
*/
|
|
void QEasingCurve::setType(Type type)
|
|
{
|
|
if (d_ptr->type == type)
|
|
return;
|
|
if (type < Linear || type >= NCurveTypes - 1) {
|
|
qWarning("QEasingCurve: Invalid curve type %d", type);
|
|
return;
|
|
}
|
|
|
|
d_ptr->setType_helper(type);
|
|
}
|
|
|
|
/*!
|
|
Sets a custom easing curve that is defined by the user in the function \a func.
|
|
The signature of the function is qreal myEasingFunction(qreal progress),
|
|
where \e progress and the return value are considered to be normalized between 0 and 1.
|
|
(In some cases the return value can be outside that range)
|
|
After calling this function type() will return QEasingCurve::Custom.
|
|
\a func cannot be zero.
|
|
|
|
\sa customType()
|
|
\sa valueForProgress()
|
|
*/
|
|
void QEasingCurve::setCustomType(EasingFunction func)
|
|
{
|
|
if (!func) {
|
|
qWarning("Function pointer must not be null");
|
|
return;
|
|
}
|
|
d_ptr->func = func;
|
|
d_ptr->setType_helper(Custom);
|
|
}
|
|
|
|
/*!
|
|
Returns the function pointer to the custom easing curve.
|
|
If type() does not return QEasingCurve::Custom, this function
|
|
will return 0.
|
|
*/
|
|
QEasingCurve::EasingFunction QEasingCurve::customType() const
|
|
{
|
|
return d_ptr->type == Custom ? d_ptr->func : nullptr;
|
|
}
|
|
|
|
/*!
|
|
Return the effective progress for the easing curve at \a progress.
|
|
Whereas \a progress must be between 0 and 1, the returned effective progress
|
|
can be outside those bounds. For example, QEasingCurve::InBack will
|
|
return negative values in the beginning of the function.
|
|
*/
|
|
qreal QEasingCurve::valueForProgress(qreal progress) const
|
|
{
|
|
progress = qBound<qreal>(0, progress, 1);
|
|
if (d_ptr->func)
|
|
return d_ptr->func(progress);
|
|
else if (d_ptr->config)
|
|
return d_ptr->config->value(progress);
|
|
else
|
|
return progress;
|
|
}
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
QDebug operator<<(QDebug debug, const QEasingCurve &item)
|
|
{
|
|
QDebugStateSaver saver(debug);
|
|
debug << "type:" << item.d_ptr->type
|
|
<< "func:" << reinterpret_cast<const void *>(item.d_ptr->func);
|
|
if (item.d_ptr->config) {
|
|
debug << QString::fromLatin1("period:%1").arg(item.d_ptr->config->_p, 0, 'f', 20)
|
|
<< QString::fromLatin1("amp:%1").arg(item.d_ptr->config->_a, 0, 'f', 20)
|
|
<< QString::fromLatin1("overshoot:%1").arg(item.d_ptr->config->_o, 0, 'f', 20);
|
|
}
|
|
return debug;
|
|
}
|
|
#endif // QT_NO_DEBUG_STREAM
|
|
|
|
#ifndef QT_NO_DATASTREAM
|
|
/*!
|
|
\fn QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
|
|
\relates QEasingCurve
|
|
|
|
Writes the given \a easing curve to the given \a stream and returns a
|
|
reference to the stream.
|
|
|
|
\sa {Serializing Qt Data Types}
|
|
*/
|
|
|
|
QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
|
|
{
|
|
stream << quint8(easing.d_ptr->type);
|
|
stream << quint64(quintptr(easing.d_ptr->func));
|
|
|
|
bool hasConfig = easing.d_ptr->config;
|
|
stream << hasConfig;
|
|
if (hasConfig) {
|
|
stream << easing.d_ptr->config;
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
/*!
|
|
\fn QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
|
|
\relates QEasingCurve
|
|
|
|
Reads an easing curve from the given \a stream into the given \a
|
|
easing curve and returns a reference to the stream.
|
|
|
|
\sa {Serializing Qt Data Types}
|
|
*/
|
|
|
|
QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
|
|
{
|
|
QEasingCurve::Type type;
|
|
quint8 int_type;
|
|
stream >> int_type;
|
|
type = static_cast<QEasingCurve::Type>(int_type);
|
|
easing.setType(type);
|
|
|
|
quint64 ptr_func;
|
|
stream >> ptr_func;
|
|
easing.d_ptr->func = QEasingCurve::EasingFunction(quintptr(ptr_func));
|
|
|
|
bool hasConfig;
|
|
stream >> hasConfig;
|
|
delete easing.d_ptr->config;
|
|
easing.d_ptr->config = nullptr;
|
|
if (hasConfig) {
|
|
QEasingCurveFunction *config = curveToFunctionObject(type);
|
|
stream >> config;
|
|
easing.d_ptr->config = config;
|
|
}
|
|
return stream;
|
|
}
|
|
#endif // QT_NO_DATASTREAM
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#include "moc_qeasingcurve.cpp"
|