Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ /*
++
++ | *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 {Graph of a
++ linear function.}
++ \caption Easing curve for a linear (t) function:
++ velocity is constant.
++ \value InQuad \image qeasingcurve-inquad.png {Graph of a
++ InQuadratic function.}
++ \caption Easing curve for a quadratic (t^2) function:
++ accelerating from zero velocity.
++ \value OutQuad \image qeasingcurve-outquad.png {Graph of a
++ OutQuadratic function}
++ \caption Easing curve for a quadratic (t^2) function:
++ decelerating to zero velocity.
++ \value InOutQuad \image qeasingcurve-inoutquad.png {Graph of a
++ InOutquad quadratic function}
++ \caption Easing curve for a quadratic (t^2) function:
++ acceleration until halfway, then deceleration.
++ \value OutInQuad \image qeasingcurve-outinquad.png {Graph of a
++ OutInquad quadratic function}
++ \caption Easing curve for a quadratic (t^2) function:
++ deceleration until halfway, then acceleration.
++ \value InCubic \image qeasingcurve-incubic.png {Graph of a
++ InCubic function}
++ \caption Easing curve for a cubic (t^3) function:
++ accelerating from zero velocity.
++ \value OutCubic \image qeasingcurve-outcubic.png {Graph of a
++ OutCubic function}
++ \caption Easing curve for a cubic (t^3) function:
++ decelerating to zero velocity.
++ \value InOutCubic \image qeasingcurve-inoutcubic.png {Graph of a
++ InOutCubic function.}
++ \caption Easing curve for a cubic (t^3) function:
++ acceleration until halfway, then deceleration.
++ \value OutInCubic \image qeasingcurve-outincubic.png {Graph of a
++ OutInCubic function.}
++ \caption Easing curve for a cubic (t^3) function:
++ deceleration until halfway, then acceleration.
++ \value InQuart \image qeasingcurve-inquart.png {Graph of a
++ InQuartic function.}
++ \caption Easing curve for a quartic (t^4) function:
++ accelerating from zero velocity.
++ \value OutQuart \image qeasingcurve-outquart.png {Graph of a
++ OutQuartic function.}
++ \caption
++ Easing curve for a quartic (t^4) function:
++ decelerating to zero velocity.
++ \value InOutQuart \image qeasingcurve-inoutquart.png {Graph of a
++ InOutQuartic function.}
++ \caption
++ Easing curve for a quartic (t^4) function:
++ acceleration until halfway, then deceleration.
++ \value OutInQuart \image qeasingcurve-outinquart.png {Graph of a
++ OutInQuartic function.}
++ \caption
++ Easing curve for a quartic (t^4) function:
++ deceleration until halfway, then acceleration.
++ \value InQuint \image qeasingcurve-inquint.png {Graph of a
++ InQuintic function}
++ \caption
++ Easing curve for a quintic (t^5) easing
++ in: accelerating from zero velocity.
++ \value OutQuint \image qeasingcurve-outquint.png {Graph of a
++ OutQuintic function}
++ \caption
++ Easing curve for a quintic (t^5) function:
++ decelerating to zero velocity.
++ \value InOutQuint \image qeasingcurve-inoutquint.png {Graph of a
++ InOutQuintic function}
++ \caption
++ Easing curve for a quintic (t^5) function:
++ acceleration until halfway, then deceleration.
++ \value OutInQuint \image qeasingcurve-outinquint.png {Graph of a
++ OutInQuintic function}
++ \caption
++ Easing curve for a quintic (t^5) function:
++ deceleration until halfway, then acceleration.
++ \value InSine \image qeasingcurve-insine.png {Graph of a
++ InSinusoidal function}
++ \caption
++ Easing curve for a sinusoidal (sin(t)) function:
++ accelerating from zero velocity.
++ \value OutSine \image qeasingcurve-outsine.png {Graph of a
++ OutSinusoidal function}
++ \caption
++ Easing curve for a sinusoidal (sin(t)) function:
++ decelerating to zero velocity.
++ \value InOutSine \image qeasingcurve-inoutsine.png {Graph of a
++ InOutSinusoidal function}
++ \caption
++ Easing curve for a sinusoidal (sin(t)) function:
++ acceleration until halfway, then deceleration.
++ \value OutInSine \image qeasingcurve-outinsine.png {Graph of a
++ OutInSinusoidal function}
++ \caption
++ Easing curve for a sinusoidal (sin(t)) function:
++ deceleration until halfway, then acceleration.
++ \value InExpo \image qeasingcurve-inexpo.png {Graph of a
++ InExponential function}
++ \caption
++ Easing curve for an exponential (2^t) function:
++ accelerating from zero velocity.
++ \value OutExpo \image qeasingcurve-outexpo.png {Graph of a
++ OutExponential function}
++ \caption
++ Easing curve for an exponential (2^t) function:
++ decelerating to zero velocity.
++ \value InOutExpo \image qeasingcurve-inoutexpo.png {Graph of a
++ InOutExponential function}
++ \caption
++ Easing curve for an exponential (2^t) function:
++ acceleration until halfway, then deceleration.
++ \value OutInExpo \image qeasingcurve-outinexpo.png {Graph of a
++ OutInExponential function}
++ \caption
++ Easing curve for an exponential (2^t) function:
++ deceleration until halfway, then acceleration.
++ \value InCirc \image qeasingcurve-incirc.png {Graph of a
++ InCircular function}
++ \caption
++ Easing curve for a circular (sqrt(1-t^2)) function:
++ accelerating from zero velocity.
++ \value OutCirc \image qeasingcurve-outcirc.png {Graph of a
++ OutCircular function}
++ \caption
++ Easing curve for a circular (sqrt(1-t^2)) function:
++ decelerating to zero velocity.
++ \value InOutCirc \image qeasingcurve-inoutcirc.png {Graph of a
++ InOutCircular function}
++ \caption
++ Easing curve for a circular (sqrt(1-t^2)) function:
++ acceleration until halfway, then deceleration.
++ \value OutInCirc \image qeasingcurve-outincirc.png {Graph of a
++ OutInCircular function}
++ \caption
++ Easing curve for a circular (sqrt(1-t^2)) function:
++ deceleration until halfway, then acceleration.
++ \value InElastic \image qeasingcurve-inelastic.png {Graph of a
++ InElastic function}
++ \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 {Graph of a
++ OutElastic function}
++ \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 {Graph of a
++ InOutElastic function}
++ \caption
++ Easing curve for an elastic
++ (exponentially decaying sine wave) function:
++ acceleration until halfway, then deceleration.
++ \value OutInElastic \image qeasingcurve-outinelastic.png {Graph of a
++ OutInElastic function}
++ \caption
++ Easing curve for an elastic
++ (exponentially decaying sine wave) function:
++ deceleration until halfway, then acceleration.
++ \value InBack \image qeasingcurve-inback.png {Graph of a
++ InBack function}
++ \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 {Graph of a
++ OutBack function}
++ \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 Graph of a
++ InOutBack function}
++ \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 {Graph of a
++ OutInBack function}
++ \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 {Graph of a
++ InBounce function}
++ \caption
++ Easing curve for a bounce (exponentially
++ decaying parabolic bounce) function: accelerating
++ from zero velocity.
++ \value OutBounce \image qeasingcurve-outbounce.png {Graph of a
++ OutBounce function}
++ \caption
++ Easing curve for a bounce (exponentially
++ decaying parabolic bounce) function: decelerating
++ from zero velocity.
++ \value InOutBounce \image qeasingcurve-inoutbounce.png {Graph of a
++ InOutBounce function}
++ \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 {Graph of a
++ OutInBounce function}
++ \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 constexpr 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;
++
++
++ friend bool operator==(const TCBPoint &lhs, const TCBPoint &rhs) noexcept
++ {
++ return qFuzzyCompare(lhs._point, rhs._point)
++ && QtPrivate::fuzzyCompare(lhs._t, rhs._t)
++ && QtPrivate::fuzzyCompare(lhs._c, rhs._c)
++ && QtPrivate::fuzzyCompare(lhs._b, rhs._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
++ {
++ QEasingCurveFunction &operator=(const QEasingCurveFunction &) = delete;
++ QEasingCurveFunction &operator=(QEasingCurveFunction &&) = delete;
++ QEasingCurveFunction(QEasingCurveFunction &&) = delete;
++ protected:
++ QEasingCurveFunction(const QEasingCurveFunction &) = default; // used by clone()
++ 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 *clone() const { return new QEasingCurveFunction{*this}; }
++ // ### virtual? (cf. QTBUG-142709)
++ bool fuzzyCompare(const QEasingCurveFunction &other) const noexcept;
++
++ 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);
++ }
++
++ bool QEasingCurveFunction::fuzzyCompare(const QEasingCurveFunction &other) const noexcept
++ {
++ return _t == other._t
++ && QtPrivate::fuzzyCompare(_p, other._p)
++ && QtPrivate::fuzzyCompare(_a, other._a)
++ && QtPrivate::fuzzyCompare(_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->clone() : 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.size() / 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.size() - 4).x();
++ _curves[i].p0y = _bezierCurves.at(_bezierCurves.size() - 4).y();
++
++ _curves[i].p1x = _bezierCurves.at(_bezierCurves.size() - 3).x();
++ _curves[i].p1y = _bezierCurves.at(_bezierCurves.size() - 3).y();
++
++ _curves[i].p2x = _bezierCurves.at(_bezierCurves.size() - 2).x();
++ _curves[i].p2y = _bezierCurves.at(_bezierCurves.size() - 2).y();
++
++ _curves[i].p3x = _bezierCurves.at(_bezierCurves.size() - 1).x();
++ _curves[i].p3y = _bezierCurves.at(_bezierCurves.size() - 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;
++ }
++ }
++
++ BezierEase *clone() const override { return new BezierEase{*this}; }
++
++ 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.size() % 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.size() % 3 == 0);
++
++ if (_bezierCurves.isEmpty()) {
++ qWarning("QEasingCurve: Invalid tcb curve");
++ return x;
++ }
++
++ return BezierEase::value(x);
++ }
++
++ TCBEase *clone() const override
++ {
++ return new TCBEase{*this};
++ }
++ };
++
++ struct ElasticEase : public QEasingCurveFunction
++ {
++ ElasticEase(QEasingCurve::Type type)
++ : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
++ { }
++
++ ElasticEase *clone() const override { return new ElasticEase{*this}; }
++
++ 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))
++ { }
++
++ BounceEase *clone() const override { return new BounceEase{*this}; }
++
++ 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))
++ { }
++
++ BackEase *clone() const override { return new BackEase{*this}; }
++
++ 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
++ \memberswap{curve}
++ */
++
++ /*!
++ \fn bool QEasingCurve::operator==(const QEasingCurve &lhs, const QEasingCurve &rhs)
++
++ Compares easing curve \a lhs with \a rhs and returns \c true if they are
++ equal; otherwise returns \c false.
++ It will also compare the properties of the curves.
++ */
++ bool comparesEqual(const QEasingCurve &lhs, const QEasingCurve &rhs)
++ {
++ bool res = lhs.d_ptr->func == rhs.d_ptr->func
++ && lhs.d_ptr->type == rhs.d_ptr->type;
++ if (res) {
++ if (lhs.d_ptr->config && rhs.d_ptr->config) {
++ // catch the config content
++ res = lhs.d_ptr->config->fuzzyCompare(*rhs.d_ptr->config);
++ } else if (lhs.d_ptr->config || rhs.d_ptr->config) {
++ // one one has a config object, which could contain default values
++ res = QtPrivate::fuzzyCompare(lhs.amplitude(), rhs.amplitude())
++ && QtPrivate::fuzzyCompare(lhs.period(), rhs.period())
++ && QtPrivate::fuzzyCompare(lhs.overshoot(), rhs.overshoot());
++ }
++ }
++ return res;
++ }
++
++ /*!
++ \fn bool QEasingCurve::operator!=(const QEasingCurve &lhs, const QEasingCurve &rhs)
++
++ Compares easing curve \a lhs with \a rhs and returns \c true if they are
++ not equal; otherwise returns \c false.
++ It will also compare the properties of the curves.
++
++ \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.
++
++ \sa fromCubicBezier()
++ */
++ 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.size();
++ 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 explicitly 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 \nullptr.
++
++ \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;
++ }
++
++ /*!
++ Returns a cubic Bezier curve with the specified \a c1 and \a c2 control
++ points.
++
++ A cubic Bezier curve is defined by four control points: C0, C1, C2, and C3.
++ C0 and C3 correspond to the start and the end position, respectively. This
++ function returns a cubic Bezier curve whose start point C0 is fixed at (0, 0)
++ and the end point C3 is fixed at (1, 1).
++
++ This is a convenience helper, it is equivalent to calling
++ \e {addCubicBezierSegment(c1, c2, QPointF(1, 1))}. If you need to construct
++ a more complex cubic Bezier spline, use the addCubicBezierSegment() function
++ instead.
++
++ \sa addCubicBezierSegment()
++ \since 6.12
++ */
++ QEasingCurve QEasingCurve::fromCubicBezier(QPointF c1, QPointF c2)
++ {
++ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
++ easingCurve.addCubicBezierSegment(c1, c2, QPointF(1, 1));
++ return easingCurve;
++ }
++
++ #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.
++
++ \warning Writing easing curves of QEasingCurve::Custom type
++ (that is, curves with a custom easing function) is not supported.
++
++ \sa {Serializing Qt Data Types}
++ */
++
++ QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
++ {
++ if (easing.d_ptr->type == QEasingCurve::Custom) {
++ qWarning("QEasingCurve: Cannot serialize an easing curve with a custom easing function");
++
++ // Backwards compatibility: stream _something_ out.
++ // Deliberately choose a curve that uses a config and not a
++ // easing function. If this curve is deserialized from old
++ // code, it will ignore the function pointer (cf.
++ // QTBUG-132575).
++ static_assert(isConfigFunction(QEasingCurve::InElastic));
++ stream << QEasingCurve(QEasingCurve::InElastic);
++ return stream;
++ }
++
++ stream << quint8(easing.d_ptr->type);
++ // Unused; for backwards compatibility
++ stream << quint64(0);
++
++ 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);
++ if (type == QEasingCurve::Custom) {
++ qWarning("QEasingCurve: Cannot deserialize an easing curve with a custom easing function");
++ stream.setStatus(QDataStream::ReadCorruptData);
++ type = QEasingCurve::Linear;
++ }
++ easing.setType(type);
++
++ // Unused; for backwards compatibility
++ [[maybe_unused]] quint64 ptr_func;
++ stream >> 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"