1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 /*
41 
42 | *property* | *Used for type* |
43 | period     | QEasingCurve::{In,Out,InOut,OutIn}Elastic |
44 | amplitude  | QEasingCurve::{In,Out,InOut,OutIn}Bounce, QEasingCurve::{In,Out,InOut,OutIn}Elastic |
45 | overshoot  | QEasingCurve::{In,Out,InOut,OutIn}Back   |
46 
47 */
48 
49 
50 
51 
52 /*!
53     \class QEasingCurve
54     \inmodule QtCore
55     \since 4.6
56     \ingroup animation
57     \brief The QEasingCurve class provides easing curves for controlling animation.
58 
59     Easing curves describe a function that controls how the speed of the interpolation
60     between 0 and 1 should be. Easing curves allow transitions from
61     one value to another to appear more natural than a simple constant speed would allow.
62     The QEasingCurve class is usually used in conjunction with the QVariantAnimation and
63     QPropertyAnimation classes but can be used on its own. It is usually used to accelerate
64     the interpolation from zero velocity (ease in) or decelerate to zero velocity (ease out).
65     Ease in and ease out can also be combined in the same easing curve.
66 
67     To calculate the speed of the interpolation, the easing curve provides the function
68     valueForProgress(), where the \a progress argument specifies the progress of the
69     interpolation: 0 is the start value of the interpolation, 1 is the end value of the
70     interpolation. The returned value is the effective progress of the interpolation.
71     If the returned value is the same as the input value for all input values the easing
72     curve is a linear curve. This is the default behaviour.
73 
74     For example,
75 
76     \snippet code/src_corelib_tools_qeasingcurve.cpp 0
77 
78     will print the effective progress of the interpolation between 0 and 1.
79 
80     When using a QPropertyAnimation, the associated easing curve will be used to control the
81     progress of the interpolation between startValue and endValue:
82 
83     \snippet code/src_corelib_tools_qeasingcurve.cpp 1
84 
85     The ability to set an amplitude, overshoot, or period depends on
86     the QEasingCurve type. Amplitude access is available to curves
87     that behave as springs such as elastic and bounce curves. Changing
88     the amplitude changes the height of the curve. Period access is
89     only available to elastic curves and setting a higher period slows
90     the rate of bounce. Only curves that have "boomerang" behaviors
91     such as the InBack, OutBack, InOutBack, and OutInBack have
92     overshoot settings. These curves will interpolate beyond the end
93     points and return to the end point, acting similar to a boomerang.
94 
95     The \l{Easing Curves Example} contains samples of QEasingCurve
96     types and lets you change the curve settings.
97 
98  */
99 
100 /*!
101     \enum QEasingCurve::Type
102 
103     The type of easing curve.
104 
105     \value Linear       \image qeasingcurve-linear.png
106                         \caption Easing curve for a linear (t) function:
107                         velocity is constant.
108     \value InQuad       \image qeasingcurve-inquad.png
109                         \caption Easing curve for a quadratic (t^2) function:
110                         accelerating from zero velocity.
111     \value OutQuad      \image qeasingcurve-outquad.png
112                         \caption Easing curve for a quadratic (t^2) function:
113                         decelerating to zero velocity.
114     \value InOutQuad    \image qeasingcurve-inoutquad.png
115                         \caption Easing curve for a quadratic (t^2) function:
116                         acceleration until halfway, then deceleration.
117     \value OutInQuad    \image qeasingcurve-outinquad.png
118                         \caption Easing curve for a quadratic (t^2) function:
119                         deceleration until halfway, then acceleration.
120     \value InCubic      \image qeasingcurve-incubic.png
121                         \caption Easing curve for a cubic (t^3) function:
122                         accelerating from zero velocity.
123     \value OutCubic     \image qeasingcurve-outcubic.png
124                         \caption Easing curve for a cubic (t^3) function:
125                         decelerating to zero velocity.
126     \value InOutCubic   \image qeasingcurve-inoutcubic.png
127                         \caption Easing curve for a cubic (t^3) function:
128                         acceleration until halfway, then deceleration.
129     \value OutInCubic   \image qeasingcurve-outincubic.png
130                         \caption Easing curve for a cubic (t^3) function:
131                         deceleration until halfway, then acceleration.
132     \value InQuart      \image qeasingcurve-inquart.png
133                         \caption Easing curve for a quartic (t^4) function:
134                         accelerating from zero velocity.
135     \value OutQuart     \image qeasingcurve-outquart.png
136                         \caption
137                         Easing curve for a quartic (t^4) function:
138                         decelerating to zero velocity.
139     \value InOutQuart   \image qeasingcurve-inoutquart.png
140                         \caption
141                         Easing curve for a quartic (t^4) function:
142                         acceleration until halfway, then deceleration.
143     \value OutInQuart   \image qeasingcurve-outinquart.png
144                         \caption
145                         Easing curve for a quartic (t^4) function:
146                         deceleration until halfway, then acceleration.
147     \value InQuint      \image qeasingcurve-inquint.png
148                         \caption
149                         Easing curve for a quintic (t^5) easing
150                         in: accelerating from zero velocity.
151     \value OutQuint     \image qeasingcurve-outquint.png
152                         \caption
153                         Easing curve for a quintic (t^5) function:
154                         decelerating to zero velocity.
155     \value InOutQuint   \image qeasingcurve-inoutquint.png
156                         \caption
157                         Easing curve for a quintic (t^5) function:
158                         acceleration until halfway, then deceleration.
159     \value OutInQuint   \image qeasingcurve-outinquint.png
160                         \caption
161                         Easing curve for a quintic (t^5) function:
162                         deceleration until halfway, then acceleration.
163     \value InSine       \image qeasingcurve-insine.png
164                         \caption
165                         Easing curve for a sinusoidal (sin(t)) function:
166                         accelerating from zero velocity.
167     \value OutSine      \image qeasingcurve-outsine.png
168                         \caption
169                         Easing curve for a sinusoidal (sin(t)) function:
170                         decelerating to zero velocity.
171     \value InOutSine    \image qeasingcurve-inoutsine.png
172                         \caption
173                         Easing curve for a sinusoidal (sin(t)) function:
174                         acceleration until halfway, then deceleration.
175     \value OutInSine    \image qeasingcurve-outinsine.png
176                         \caption
177                         Easing curve for a sinusoidal (sin(t)) function:
178                         deceleration until halfway, then acceleration.
179     \value InExpo       \image qeasingcurve-inexpo.png
180                         \caption
181                         Easing curve for an exponential (2^t) function:
182                         accelerating from zero velocity.
183     \value OutExpo      \image qeasingcurve-outexpo.png
184                         \caption
185                         Easing curve for an exponential (2^t) function:
186                         decelerating to zero velocity.
187     \value InOutExpo    \image qeasingcurve-inoutexpo.png
188                         \caption
189                         Easing curve for an exponential (2^t) function:
190                         acceleration until halfway, then deceleration.
191     \value OutInExpo    \image qeasingcurve-outinexpo.png
192                         \caption
193                         Easing curve for an exponential (2^t) function:
194                         deceleration until halfway, then acceleration.
195     \value InCirc       \image qeasingcurve-incirc.png
196                         \caption
197                         Easing curve for a circular (sqrt(1-t^2)) function:
198                         accelerating from zero velocity.
199     \value OutCirc      \image qeasingcurve-outcirc.png
200                         \caption
201                         Easing curve for a circular (sqrt(1-t^2)) function:
202                         decelerating to zero velocity.
203     \value InOutCirc    \image qeasingcurve-inoutcirc.png
204                         \caption
205                         Easing curve for a circular (sqrt(1-t^2)) function:
206                         acceleration until halfway, then deceleration.
207     \value OutInCirc    \image qeasingcurve-outincirc.png
208                         \caption
209                         Easing curve for a circular (sqrt(1-t^2)) function:
210                         deceleration until halfway, then acceleration.
211     \value InElastic    \image qeasingcurve-inelastic.png
212                         \caption
213                         Easing curve for an elastic
214                         (exponentially decaying sine wave) function:
215                         accelerating from zero velocity.  The peak amplitude
216                         can be set with the \e amplitude parameter, and the
217                         period of decay by the \e period parameter.
218     \value OutElastic   \image qeasingcurve-outelastic.png
219                         \caption
220                         Easing curve for an elastic
221                         (exponentially decaying sine wave) function:
222                         decelerating to zero velocity.  The peak amplitude
223                         can be set with the \e amplitude parameter, and the
224                         period of decay by the \e period parameter.
225     \value InOutElastic \image qeasingcurve-inoutelastic.png
226                         \caption
227                         Easing curve for an elastic
228                         (exponentially decaying sine wave) function:
229                         acceleration until halfway, then deceleration.
230     \value OutInElastic \image qeasingcurve-outinelastic.png
231                         \caption
232                         Easing curve for an elastic
233                         (exponentially decaying sine wave) function:
234                         deceleration until halfway, then acceleration.
235     \value InBack       \image qeasingcurve-inback.png
236                         \caption
237                         Easing curve for a back (overshooting
238                         cubic function: (s+1)*t^3 - s*t^2) easing in:
239                         accelerating from zero velocity.
240     \value OutBack      \image qeasingcurve-outback.png
241                         \caption
242                         Easing curve for a back (overshooting
243                         cubic function: (s+1)*t^3 - s*t^2) easing out:
244                         decelerating to zero velocity.
245     \value InOutBack    \image qeasingcurve-inoutback.png
246                         \caption
247                         Easing curve for a back (overshooting
248                         cubic function: (s+1)*t^3 - s*t^2) easing in/out:
249                         acceleration until halfway, then deceleration.
250     \value OutInBack    \image qeasingcurve-outinback.png
251                         \caption
252                         Easing curve for a back (overshooting
253                         cubic easing: (s+1)*t^3 - s*t^2) easing out/in:
254                         deceleration until halfway, then acceleration.
255     \value InBounce     \image qeasingcurve-inbounce.png
256                         \caption
257                         Easing curve for a bounce (exponentially
258                         decaying parabolic bounce) function: accelerating
259                         from zero velocity.
260     \value OutBounce    \image qeasingcurve-outbounce.png
261                         \caption
262                         Easing curve for a bounce (exponentially
263                         decaying parabolic bounce) function: decelerating
264                         from zero velocity.
265     \value InOutBounce  \image qeasingcurve-inoutbounce.png
266                         \caption
267                         Easing curve for a bounce (exponentially
268                         decaying parabolic bounce) function easing in/out:
269                         acceleration until halfway, then deceleration.
270     \value OutInBounce  \image qeasingcurve-outinbounce.png
271                         \caption
272                         Easing curve for a bounce (exponentially
273                         decaying parabolic bounce) function easing out/in:
274                         deceleration until halfway, then acceleration.
275     \omitvalue InCurve
276     \omitvalue OutCurve
277     \omitvalue SineCurve
278     \omitvalue CosineCurve
279     \value BezierSpline Allows defining a custom easing curve using a cubic bezier spline
280                         \sa addCubicBezierSegment()
281     \value TCBSpline    Allows defining a custom easing curve using a TCB spline
282                         \sa addTCBSegment()
283     \value Custom       This is returned if the user specified a custom curve type with
284                         setCustomType(). Note that you cannot call setType() with this value,
285                         but type() can return it.
286     \omitvalue NCurveTypes
287 */
288 
289 /*!
290     \typedef QEasingCurve::EasingFunction
291 
292     This is a typedef for a pointer to a function with the following
293     signature:
294 
295     \snippet code/src_corelib_tools_qeasingcurve.cpp typedef
296 */
297 
298 #include "qeasingcurve.h"
299 #include <cmath>
300 
301 #ifndef QT_NO_DEBUG_STREAM
302 #include <QtCore/qdebug.h>
303 #include <QtCore/qstring.h>
304 #endif
305 
306 #ifndef QT_NO_DATASTREAM
307 #include <QtCore/qdatastream.h>
308 #endif
309 
310 #include <QtCore/qpoint.h>
311 #include <QtCore/qvector.h>
312 
313 QT_BEGIN_NAMESPACE
314 
isConfigFunction(QEasingCurve::Type type)315 static bool isConfigFunction(QEasingCurve::Type type)
316 {
317     return (type >= QEasingCurve::InElastic
318             && type <= QEasingCurve::OutInBounce) ||
319             type == QEasingCurve::BezierSpline ||
320             type == QEasingCurve::TCBSpline;
321 }
322 
323 struct TCBPoint {
324     QPointF _point;
325     qreal _t;
326     qreal _c;
327     qreal _b;
328 
TCBPointTCBPoint329     TCBPoint() {}
TCBPointTCBPoint330     TCBPoint(QPointF point, qreal t, qreal c, qreal b) : _point(point), _t(t), _c(c), _b(b) {}
331 
operator ==TCBPoint332     bool operator==(const TCBPoint &other) const
333     {
334         return _point == other._point &&
335                 qFuzzyCompare(_t, other._t) &&
336                 qFuzzyCompare(_c, other._c) &&
337                 qFuzzyCompare(_b, other._b);
338     }
339 };
340 Q_DECLARE_TYPEINFO(TCBPoint, Q_PRIMITIVE_TYPE);
341 
operator <<(QDataStream & stream,const TCBPoint & point)342 QDataStream &operator<<(QDataStream &stream, const TCBPoint &point)
343 {
344     stream << point._point
345            << point._t
346            << point._c
347            << point._b;
348     return stream;
349 }
350 
operator >>(QDataStream & stream,TCBPoint & point)351 QDataStream &operator>>(QDataStream &stream, TCBPoint &point)
352 {
353     stream >> point._point
354            >> point._t
355            >> point._c
356            >> point._b;
357     return stream;
358 }
359 
360 typedef QVector<TCBPoint> TCBPoints;
361 
362 class QEasingCurveFunction
363 {
364 public:
QEasingCurveFunction(QEasingCurve::Type type,qreal period=0.3,qreal amplitude=1.0,qreal overshoot=1.70158)365     QEasingCurveFunction(QEasingCurve::Type type, qreal period = 0.3, qreal amplitude = 1.0,
366         qreal overshoot = 1.70158)
367         : _t(type), _p(period), _a(amplitude), _o(overshoot)
368     { }
~QEasingCurveFunction()369     virtual ~QEasingCurveFunction() {}
370     virtual qreal value(qreal t);
371     virtual QEasingCurveFunction *copy() const;
372     bool operator==(const QEasingCurveFunction &other) const;
373 
374     QEasingCurve::Type _t;
375     qreal _p;
376     qreal _a;
377     qreal _o;
378     QVector<QPointF> _bezierCurves;
379     TCBPoints _tcbPoints;
380 
381 };
382 
operator <<(QDataStream & stream,QEasingCurveFunction * func)383 QDataStream &operator<<(QDataStream &stream, QEasingCurveFunction *func)
384 {
385     if (func) {
386         stream << func->_p;
387         stream << func->_a;
388         stream << func->_o;
389         if (stream.version() > QDataStream::Qt_5_12) {
390             stream << func->_bezierCurves;
391             stream << func->_tcbPoints;
392         }
393     }
394     return stream;
395 }
396 
operator >>(QDataStream & stream,QEasingCurveFunction * func)397 QDataStream &operator>>(QDataStream &stream, QEasingCurveFunction *func)
398 {
399     if (func) {
400         stream >> func->_p;
401         stream >> func->_a;
402         stream >> func->_o;
403         if (stream.version() > QDataStream::Qt_5_12) {
404             stream >> func->_bezierCurves;
405             stream >> func->_tcbPoints;
406         }
407     }
408     return stream;
409 }
410 
411 static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve);
412 
value(qreal t)413 qreal QEasingCurveFunction::value(qreal t)
414 {
415     QEasingCurve::EasingFunction func = curveToFunc(_t);
416     return func(t);
417 }
418 
copy() const419 QEasingCurveFunction *QEasingCurveFunction::copy() const
420 {
421     QEasingCurveFunction *rv = new QEasingCurveFunction(_t, _p, _a, _o);
422     rv->_bezierCurves = _bezierCurves;
423     rv->_tcbPoints = _tcbPoints;
424     return rv;
425 }
426 
operator ==(const QEasingCurveFunction & other) const427 bool QEasingCurveFunction::operator==(const QEasingCurveFunction &other) const
428 {
429     return _t == other._t &&
430            qFuzzyCompare(_p, other._p) &&
431            qFuzzyCompare(_a, other._a) &&
432            qFuzzyCompare(_o, other._o) &&
433             _bezierCurves == other._bezierCurves &&
434             _tcbPoints == other._tcbPoints;
435 }
436 
437 QT_BEGIN_INCLUDE_NAMESPACE
438 #include "../../3rdparty/easing/easing.cpp"
439 QT_END_INCLUDE_NAMESPACE
440 
441 class QEasingCurvePrivate
442 {
443 public:
QEasingCurvePrivate()444     QEasingCurvePrivate()
445         : type(QEasingCurve::Linear),
446           config(nullptr),
447           func(&easeNone)
448     { }
QEasingCurvePrivate(const QEasingCurvePrivate & other)449     QEasingCurvePrivate(const QEasingCurvePrivate &other)
450         : type(other.type),
451           config(other.config ? other.config->copy() : nullptr),
452           func(other.func)
453     { }
~QEasingCurvePrivate()454     ~QEasingCurvePrivate() { delete config; }
455     void setType_helper(QEasingCurve::Type);
456 
457     QEasingCurve::Type type;
458     QEasingCurveFunction *config;
459     QEasingCurve::EasingFunction func;
460 };
461 
462 struct BezierEase : public QEasingCurveFunction
463 {
464     struct SingleCubicBezier {
465         qreal p0x, p0y;
466         qreal p1x, p1y;
467         qreal p2x, p2y;
468         qreal p3x, p3y;
469     };
470 
471     QVector<SingleCubicBezier> _curves;
472     QVector<qreal> _intervals;
473     int _curveCount;
474     bool _init;
475     bool _valid;
476 
BezierEaseBezierEase477     BezierEase(QEasingCurve::Type type = QEasingCurve::BezierSpline)
478         : QEasingCurveFunction(type), _curves(10), _intervals(10), _init(false), _valid(false)
479     { }
480 
initBezierEase481     void init()
482     {
483         if (_bezierCurves.constLast() == QPointF(1.0, 1.0)) {
484             _init = true;
485             _curveCount = _bezierCurves.count() / 3;
486 
487             for (int i=0; i < _curveCount; i++) {
488                 _intervals[i] = _bezierCurves.at(i * 3 + 2).x();
489 
490                 if (i == 0) {
491                     _curves[0].p0x = 0.0;
492                     _curves[0].p0y = 0.0;
493 
494                     _curves[0].p1x = _bezierCurves.at(0).x();
495                     _curves[0].p1y = _bezierCurves.at(0).y();
496 
497                     _curves[0].p2x = _bezierCurves.at(1).x();
498                     _curves[0].p2y = _bezierCurves.at(1).y();
499 
500                     _curves[0].p3x = _bezierCurves.at(2).x();
501                     _curves[0].p3y = _bezierCurves.at(2).y();
502 
503                 } else if (i == (_curveCount - 1)) {
504                     _curves[i].p0x = _bezierCurves.at(_bezierCurves.count() - 4).x();
505                     _curves[i].p0y = _bezierCurves.at(_bezierCurves.count() - 4).y();
506 
507                     _curves[i].p1x = _bezierCurves.at(_bezierCurves.count() - 3).x();
508                     _curves[i].p1y = _bezierCurves.at(_bezierCurves.count() - 3).y();
509 
510                     _curves[i].p2x = _bezierCurves.at(_bezierCurves.count() - 2).x();
511                     _curves[i].p2y = _bezierCurves.at(_bezierCurves.count() - 2).y();
512 
513                     _curves[i].p3x = _bezierCurves.at(_bezierCurves.count() - 1).x();
514                     _curves[i].p3y = _bezierCurves.at(_bezierCurves.count() - 1).y();
515                 } else {
516                     _curves[i].p0x = _bezierCurves.at(i * 3 - 1).x();
517                     _curves[i].p0y = _bezierCurves.at(i * 3 - 1).y();
518 
519                     _curves[i].p1x = _bezierCurves.at(i * 3).x();
520                     _curves[i].p1y = _bezierCurves.at(i * 3).y();
521 
522                     _curves[i].p2x = _bezierCurves.at(i * 3 + 1).x();
523                     _curves[i].p2y = _bezierCurves.at(i * 3 + 1).y();
524 
525                     _curves[i].p3x = _bezierCurves.at(i * 3 + 2).x();
526                     _curves[i].p3y = _bezierCurves.at(i * 3 + 2).y();
527                 }
528             }
529             _valid = true;
530         } else {
531             _valid = false;
532         }
533     }
534 
copyBezierEase535     QEasingCurveFunction *copy() const override
536     {
537         BezierEase *rv = new BezierEase();
538         rv->_t = _t;
539         rv->_p = _p;
540         rv->_a = _a;
541         rv->_o = _o;
542         rv->_bezierCurves = _bezierCurves;
543         rv->_tcbPoints = _tcbPoints;
544         return rv;
545     }
546 
getBezierSegmentBezierEase547     void getBezierSegment(SingleCubicBezier * &singleCubicBezier, qreal x)
548     {
549 
550         int currentSegment = 0;
551 
552         while (currentSegment < _curveCount) {
553             if (x <= _intervals.data()[currentSegment])
554                 break;
555             currentSegment++;
556         }
557 
558         singleCubicBezier = &_curves.data()[currentSegment];
559     }
560 
561 
newtonIterationBezierEase562     qreal static inline newtonIteration(const SingleCubicBezier &singleCubicBezier, qreal t, qreal x)
563     {
564         qreal currentXValue = evaluateForX(singleCubicBezier, t);
565 
566         const qreal newT = t - (currentXValue - x) / evaluateDerivateForX(singleCubicBezier, t);
567 
568         return newT;
569     }
570 
valueBezierEase571     qreal value(qreal x) override
572     {
573         Q_ASSERT(_bezierCurves.count() % 3 == 0);
574 
575         if (_bezierCurves.isEmpty()) {
576             return x;
577         }
578 
579         if (!_init)
580             init();
581 
582         if (!_valid) {
583             qWarning("QEasingCurve: Invalid bezier curve");
584             return x;
585         }
586 
587         // The bezier computation is not always precise on the endpoints, so handle explicitly
588         if (!(x > 0))
589             return 0;
590         if (!(x < 1))
591             return 1;
592 
593         SingleCubicBezier *singleCubicBezier = nullptr;
594         getBezierSegment(singleCubicBezier, x);
595 
596         return evaluateSegmentForY(*singleCubicBezier, findTForX(*singleCubicBezier, x));
597     }
598 
evaluateSegmentForYBezierEase599     qreal static inline evaluateSegmentForY(const SingleCubicBezier &singleCubicBezier, qreal t)
600     {
601         const qreal p0 = singleCubicBezier.p0y;
602         const qreal p1 = singleCubicBezier.p1y;
603         const qreal p2 = singleCubicBezier.p2y;
604         const qreal p3 = singleCubicBezier.p3y;
605 
606         const qreal s = 1 - t;
607 
608         const qreal s_squared = s*s;
609         const qreal t_squared = t*t;
610 
611         const qreal s_cubic = s_squared * s;
612         const qreal t_cubic = t_squared * t;
613 
614         return s_cubic * p0 + 3 * s_squared * t * p1 + 3 * s * t_squared * p2 + t_cubic * p3;
615     }
616 
evaluateForXBezierEase617     qreal static inline evaluateForX(const SingleCubicBezier &singleCubicBezier, qreal t)
618     {
619         const qreal p0 = singleCubicBezier.p0x;
620         const qreal p1 = singleCubicBezier.p1x;
621         const qreal p2 = singleCubicBezier.p2x;
622         const qreal p3 = singleCubicBezier.p3x;
623 
624         const qreal s = 1 - t;
625 
626         const qreal s_squared = s*s;
627         const qreal t_squared = t*t;
628 
629         const qreal s_cubic = s_squared * s;
630         const qreal t_cubic = t_squared * t;
631 
632         return s_cubic * p0 + 3 * s_squared * t * p1 + 3 * s * t_squared * p2 + t_cubic * p3;
633     }
634 
evaluateDerivateForXBezierEase635     qreal static inline evaluateDerivateForX(const SingleCubicBezier &singleCubicBezier, qreal t)
636     {
637         const qreal p0 = singleCubicBezier.p0x;
638         const qreal p1 = singleCubicBezier.p1x;
639         const qreal p2 = singleCubicBezier.p2x;
640         const qreal p3 = singleCubicBezier.p3x;
641 
642         const qreal t_squared = t*t;
643 
644         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;
645     }
646 
_cbrtBezierEase647     qreal static inline _cbrt(qreal d)
648     {
649         qreal sign = 1;
650         if (d < 0)
651             sign = -1;
652         d = d * sign;
653 
654         qreal t = _fast_cbrt(d);
655 
656         //one step of Halley's Method to get a better approximation
657         const qreal t_cubic = t * t * t;
658         const qreal f = t_cubic + t_cubic + d;
659         if (f != qreal(0.0))
660             t = t * (t_cubic + d + d) / f;
661 
662         //another step
663         /*qreal t_i = t;
664          t_i_cubic = pow(t_i, 3);
665          t = t_i * (t_i_cubic + d + d) / (t_i_cubic + t_i_cubic + d);*/
666 
667         return t * sign;
668     }
669 
_fast_cbrtBezierEase670     float static inline _fast_cbrt(float x)
671     {
672         union {
673             float f;
674             quint32 i;
675         } ux;
676 
677         const unsigned int B1 = 709921077;
678 
679         ux.f = x;
680         ux.i = (ux.i / 3 + B1);
681 
682         return ux.f;
683     }
684 
_fast_cbrtBezierEase685     double static inline _fast_cbrt(double d)
686     {
687         union {
688             double d;
689             quint32 pt[2];
690         } ut, ux;
691 
692         const unsigned int B1 = 715094163;
693 
694 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
695         const int h0 = 1;
696 #else
697         const int h0 = 0;
698 #endif
699         ut.d = 0.0;
700         ux.d = d;
701 
702         quint32 hx = ux.pt[h0]; //high word of d
703         ut.pt[h0] = hx / 3 + B1;
704 
705         return ut.d;
706     }
707 
_acosBezierEase708     qreal static inline _acos(qreal x)
709     {
710         return std::sqrt(1-x)*(1.5707963267948966192313216916398f + x*(-0.213300989f + x*(0.077980478f + x*-0.02164095f)));
711     }
712 
_cosBezierEase713     qreal static inline _cos(qreal x) //super fast _cos
714     {
715         const qreal pi_times2 = 2 * M_PI;
716         const qreal pi_neg = -1 * M_PI;
717         const qreal pi_by2 = M_PI / 2.0;
718 
719         x += pi_by2; //the polynom is for sin
720 
721         if (x < pi_neg)
722             x += pi_times2;
723         else if (x > M_PI)
724             x -= pi_times2;
725 
726         const qreal a = 0.405284735;
727         const qreal b = 1.27323954;
728 
729         const qreal x_squared = x * x;
730 
731         if (x < 0) {
732             qreal cos = b * x + a * x_squared;
733 
734             if (cos < 0)
735                 return 0.225 * (cos * -1 * cos - cos) + cos;
736             return 0.225 * (cos * cos - cos) + cos;
737         } //else
738 
739         qreal cos = b * x - a * x_squared;
740 
741         if (cos < 0)
742             return 0.225 * (cos * 1 *-cos - cos) + cos;
743         return 0.225 * (cos * cos - cos) + cos;
744     }
745 
inRangeBezierEase746     bool static inline inRange(qreal f)
747     {
748         return (f >= -0.01 && f <= 1.01);
749     }
750 
cosacosBezierEase751     void static inline cosacos(qreal x, qreal &s1, qreal &s2, qreal &s3 )
752     {
753         //This function has no proper algebraic representation in real numbers.
754         //We use approximations instead
755 
756         const qreal x_squared = x * x;
757         const qreal x_plus_one_sqrt = qSqrt(1.0 + x);
758         const qreal one_minus_x_sqrt = qSqrt(1.0 - x);
759 
760         //cos(acos(x) / 3)
761         //s1 = _cos(_acos(x) / 3);
762         s1 = 0.463614 - 0.0347815 * x + 0.00218245 * x_squared +  0.402421 * x_plus_one_sqrt;
763 
764         //cos(acos((x) -  M_PI) / 3)
765         //s3 = _cos((_acos(x) - M_PI) / 3);
766         s3 = 0.463614 + 0.402421 * one_minus_x_sqrt + 0.0347815 * x + 0.00218245 * x_squared;
767 
768         //cos((acos(x) +  M_PI) / 3)
769         //s2 = _cos((_acos(x) + M_PI) / 3);
770         s2 = -0.401644 * one_minus_x_sqrt - 0.0686804  * x + 0.401644 * x_plus_one_sqrt;
771     }
772 
singleRealSolutionForCubicBezierEase773     qreal static inline singleRealSolutionForCubic(qreal a, qreal b, qreal c)
774     {
775         //returns the real solutiuon in [0..1]
776         //We use the Cardano formula
777 
778         //substituiton: x = z - a/3
779         // z^3+pz+q=0
780 
781         if (c < 0.000001 && c > -0.000001)
782             return 0;
783 
784         const qreal a_by3 = a / 3.0;
785 
786         const qreal a_cubic = a * a * a;
787 
788         const qreal p = b - a * a_by3;
789         const qreal q = 2.0 * a_cubic / 27.0 - a * b / 3.0 + c;
790 
791         const qreal q_squared = q * q;
792         const qreal p_cubic = p * p * p;
793         const qreal D = 0.25 * q_squared + p_cubic / 27.0;
794 
795         if (D >= 0) {
796             const qreal D_sqrt = qSqrt(D);
797             qreal u = _cbrt( -q * 0.5 + D_sqrt);
798             qreal v = _cbrt( -q * 0.5 - D_sqrt);
799             qreal z1 = u + v;
800 
801             qreal t1 = z1 - a_by3;
802 
803             if (inRange(t1))
804                 return t1;
805             qreal z2 = -1 *u;
806             qreal t2 = z2 - a_by3;
807             return t2;
808         }
809 
810         //casus irreducibilis
811         const qreal p_minus_sqrt = qSqrt(-p);
812 
813         //const qreal f = sqrt(4.0 / 3.0 * -p);
814         const qreal f = qSqrt(4.0 / 3.0) * p_minus_sqrt;
815 
816         //const qreal sqrtP = sqrt(27.0 / -p_cubic);
817         const qreal sqrtP = -3.0*qSqrt(3.0) / (p_minus_sqrt * p);
818 
819 
820         const qreal g = -q * 0.5 * sqrtP;
821 
822         qreal s1;
823         qreal s2;
824         qreal s3;
825 
826         cosacos(g, s1, s2, s3);
827 
828         qreal z1 = -1* f * s2;
829         qreal t1 = z1 - a_by3;
830         if (inRange(t1))
831             return t1;
832 
833         qreal z2 = f * s1;
834         qreal t2 = z2 - a_by3;
835         if (inRange(t2))
836             return t2;
837 
838         qreal z3 = -1 * f * s3;
839         qreal t3 = z3 - a_by3;
840         return t3;
841     }
842 
almostZeroBezierEase843     bool static inline almostZero(qreal value)
844     {
845         // 1e-3 might seem excessively fuzzy, but any smaller value will make the
846         // factors a, b, and c large enough to knock out the cubic solver.
847         return value > -1e-3 && value < 1e-3;
848     }
849 
findTForXBezierEase850     qreal static inline findTForX(const SingleCubicBezier &singleCubicBezier, qreal x)
851     {
852         const qreal p0 = singleCubicBezier.p0x;
853         const qreal p1 = singleCubicBezier.p1x;
854         const qreal p2 = singleCubicBezier.p2x;
855         const qreal p3 = singleCubicBezier.p3x;
856 
857         const qreal factorT3 = p3 - p0 + 3 * p1 - 3 * p2;
858         const qreal factorT2 = 3 * p0 - 6 * p1 + 3 * p2;
859         const qreal factorT1 = -3 * p0 + 3 * p1;
860         const qreal factorT0 = p0 - x;
861 
862         // Cases for quadratic, linear and invalid equations
863         if (almostZero(factorT3)) {
864             if (almostZero(factorT2)) {
865                 if (almostZero(factorT1))
866                     return 0.0;
867 
868                 return -factorT0 / factorT1;
869             }
870             const qreal discriminant = factorT1 * factorT1 - 4.0 * factorT2 * factorT0;
871             if (discriminant < 0.0)
872                 return 0.0;
873 
874             if (discriminant == 0.0)
875                 return -factorT1 / (2.0 * factorT2);
876 
877             const qreal solution1 = (-factorT1 + std::sqrt(discriminant)) / (2.0 * factorT2);
878             if (solution1 >= 0.0 && solution1 <= 1.0)
879                 return solution1;
880 
881             const qreal solution2 = (-factorT1 - std::sqrt(discriminant)) / (2.0 * factorT2);
882             if (solution2 >= 0.0 && solution2 <= 1.0)
883                 return solution2;
884 
885             return 0.0;
886         }
887 
888         const qreal a = factorT2 / factorT3;
889         const qreal b = factorT1 / factorT3;
890         const qreal c = factorT0 / factorT3;
891 
892         return singleRealSolutionForCubic(a, b, c);
893 
894         //one new iteration to increase numeric stability
895         //return newtonIteration(singleCubicBezier, t, x);
896     }
897 };
898 
899 struct TCBEase : public BezierEase
900 {
TCBEaseTCBEase901     TCBEase()
902         : BezierEase(QEasingCurve::TCBSpline)
903     { }
904 
valueTCBEase905     qreal value(qreal x) override
906     {
907         Q_ASSERT(_bezierCurves.count() % 3 == 0);
908 
909         if (_bezierCurves.isEmpty()) {
910             qWarning("QEasingCurve: Invalid tcb curve");
911             return x;
912         }
913 
914         return BezierEase::value(x);
915     }
916 
copyTCBEase917     QEasingCurveFunction *copy() const override
918     {
919         return new TCBEase{*this};
920     }
921 };
922 
923 struct ElasticEase : public QEasingCurveFunction
924 {
ElasticEaseElasticEase925     ElasticEase(QEasingCurve::Type type)
926         : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
927     { }
928 
copyElasticEase929     QEasingCurveFunction *copy() const override
930     {
931         ElasticEase *rv = new ElasticEase(_t);
932         rv->_p = _p;
933         rv->_a = _a;
934         rv->_bezierCurves = _bezierCurves;
935         rv->_tcbPoints = _tcbPoints;
936         return rv;
937     }
938 
valueElasticEase939     qreal value(qreal t) override
940     {
941         qreal p = (_p < 0) ? qreal(0.3) : _p;
942         qreal a = (_a < 0) ? qreal(1.0) : _a;
943         switch(_t) {
944         case QEasingCurve::InElastic:
945             return easeInElastic(t, a, p);
946         case QEasingCurve::OutElastic:
947             return easeOutElastic(t, a, p);
948         case QEasingCurve::InOutElastic:
949             return easeInOutElastic(t, a, p);
950         case QEasingCurve::OutInElastic:
951             return easeOutInElastic(t, a, p);
952         default:
953             return t;
954         }
955     }
956 };
957 
958 struct BounceEase : public QEasingCurveFunction
959 {
BounceEaseBounceEase960     BounceEase(QEasingCurve::Type type)
961         : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
962     { }
963 
copyBounceEase964     QEasingCurveFunction *copy() const override
965     {
966         BounceEase *rv = new BounceEase(_t);
967         rv->_a = _a;
968         rv->_bezierCurves = _bezierCurves;
969         rv->_tcbPoints = _tcbPoints;
970         return rv;
971     }
972 
valueBounceEase973     qreal value(qreal t) override
974     {
975         qreal a = (_a < 0) ? qreal(1.0) : _a;
976         switch(_t) {
977         case QEasingCurve::InBounce:
978             return easeInBounce(t, a);
979         case QEasingCurve::OutBounce:
980             return easeOutBounce(t, a);
981         case QEasingCurve::InOutBounce:
982             return easeInOutBounce(t, a);
983         case QEasingCurve::OutInBounce:
984             return easeOutInBounce(t, a);
985         default:
986             return t;
987         }
988     }
989 };
990 
991 struct BackEase : public QEasingCurveFunction
992 {
BackEaseBackEase993     BackEase(QEasingCurve::Type type)
994         : QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158))
995     { }
996 
copyBackEase997     QEasingCurveFunction *copy() const override
998     {
999         BackEase *rv = new BackEase(_t);
1000         rv->_o = _o;
1001         rv->_bezierCurves = _bezierCurves;
1002         rv->_tcbPoints = _tcbPoints;
1003         return rv;
1004     }
1005 
valueBackEase1006     qreal value(qreal t) override
1007     {
1008         // The *Back() functions are not always precise on the endpoints, so handle explicitly
1009         if (!(t > 0))
1010             return 0;
1011         if (!(t < 1))
1012             return 1;
1013         qreal o = (_o < 0) ? qreal(1.70158) : _o;
1014         switch(_t) {
1015         case QEasingCurve::InBack:
1016             return easeInBack(t, o);
1017         case QEasingCurve::OutBack:
1018             return easeOutBack(t, o);
1019         case QEasingCurve::InOutBack:
1020             return easeInOutBack(t, o);
1021         case QEasingCurve::OutInBack:
1022             return easeOutInBack(t, o);
1023         default:
1024             return t;
1025         }
1026     }
1027 };
1028 
curveToFunc(QEasingCurve::Type curve)1029 static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve)
1030 {
1031     switch(curve) {
1032     case QEasingCurve::Linear:
1033         return &easeNone;
1034     case QEasingCurve::InQuad:
1035         return &easeInQuad;
1036     case QEasingCurve::OutQuad:
1037         return &easeOutQuad;
1038     case QEasingCurve::InOutQuad:
1039         return &easeInOutQuad;
1040     case QEasingCurve::OutInQuad:
1041         return &easeOutInQuad;
1042     case QEasingCurve::InCubic:
1043         return &easeInCubic;
1044     case QEasingCurve::OutCubic:
1045         return &easeOutCubic;
1046     case QEasingCurve::InOutCubic:
1047         return &easeInOutCubic;
1048     case QEasingCurve::OutInCubic:
1049         return &easeOutInCubic;
1050     case QEasingCurve::InQuart:
1051         return &easeInQuart;
1052     case QEasingCurve::OutQuart:
1053         return &easeOutQuart;
1054     case QEasingCurve::InOutQuart:
1055         return &easeInOutQuart;
1056     case QEasingCurve::OutInQuart:
1057         return &easeOutInQuart;
1058     case QEasingCurve::InQuint:
1059         return &easeInQuint;
1060     case QEasingCurve::OutQuint:
1061         return &easeOutQuint;
1062     case QEasingCurve::InOutQuint:
1063         return &easeInOutQuint;
1064     case QEasingCurve::OutInQuint:
1065         return &easeOutInQuint;
1066     case QEasingCurve::InSine:
1067         return &easeInSine;
1068     case QEasingCurve::OutSine:
1069         return &easeOutSine;
1070     case QEasingCurve::InOutSine:
1071         return &easeInOutSine;
1072     case QEasingCurve::OutInSine:
1073         return &easeOutInSine;
1074     case QEasingCurve::InExpo:
1075         return &easeInExpo;
1076     case QEasingCurve::OutExpo:
1077         return &easeOutExpo;
1078     case QEasingCurve::InOutExpo:
1079         return &easeInOutExpo;
1080     case QEasingCurve::OutInExpo:
1081         return &easeOutInExpo;
1082     case QEasingCurve::InCirc:
1083         return &easeInCirc;
1084     case QEasingCurve::OutCirc:
1085         return &easeOutCirc;
1086     case QEasingCurve::InOutCirc:
1087         return &easeInOutCirc;
1088     case QEasingCurve::OutInCirc:
1089         return &easeOutInCirc;
1090     // Internal - needed for QTimeLine backward-compatibility:
1091     case QEasingCurve::InCurve:
1092         return &easeInCurve;
1093     case QEasingCurve::OutCurve:
1094         return &easeOutCurve;
1095     case QEasingCurve::SineCurve:
1096         return &easeSineCurve;
1097     case QEasingCurve::CosineCurve:
1098         return &easeCosineCurve;
1099     default:
1100         return nullptr;
1101     };
1102 }
1103 
curveToFunctionObject(QEasingCurve::Type type)1104 static QEasingCurveFunction *curveToFunctionObject(QEasingCurve::Type type)
1105 {
1106     switch(type) {
1107     case QEasingCurve::InElastic:
1108     case QEasingCurve::OutElastic:
1109     case QEasingCurve::InOutElastic:
1110     case QEasingCurve::OutInElastic:
1111         return new ElasticEase(type);
1112     case QEasingCurve::OutBounce:
1113     case QEasingCurve::InBounce:
1114     case QEasingCurve::OutInBounce:
1115     case QEasingCurve::InOutBounce:
1116         return new BounceEase(type);
1117     case QEasingCurve::InBack:
1118     case QEasingCurve::OutBack:
1119     case QEasingCurve::InOutBack:
1120     case QEasingCurve::OutInBack:
1121         return new BackEase(type);
1122     case QEasingCurve::BezierSpline:
1123         return new BezierEase;
1124     case QEasingCurve::TCBSpline:
1125         return new TCBEase;
1126     default:
1127         return new QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158));
1128     }
1129 
1130     return nullptr;
1131 }
1132 
1133 /*!
1134     \fn QEasingCurve::QEasingCurve(QEasingCurve &&other)
1135 
1136     Move-constructs a QEasingCurve instance, making it point at the same
1137     object that \a other was pointing to.
1138 
1139     \since 5.2
1140 */
1141 
1142 /*!
1143     Constructs an easing curve of the given \a type.
1144  */
QEasingCurve(Type type)1145 QEasingCurve::QEasingCurve(Type type)
1146     : d_ptr(new QEasingCurvePrivate)
1147 {
1148     setType(type);
1149 }
1150 
1151 /*!
1152     Construct a copy of \a other.
1153  */
QEasingCurve(const QEasingCurve & other)1154 QEasingCurve::QEasingCurve(const QEasingCurve &other)
1155     : d_ptr(new QEasingCurvePrivate(*other.d_ptr))
1156 {
1157     // ### non-atomic, requires malloc on shallow copy
1158 }
1159 
1160 /*!
1161     Destructor.
1162  */
1163 
~QEasingCurve()1164 QEasingCurve::~QEasingCurve()
1165 {
1166     delete d_ptr;
1167 }
1168 
1169 /*!
1170     \fn QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other)
1171     Copy \a other.
1172  */
1173 
1174 /*!
1175     \fn QEasingCurve &QEasingCurve::operator=(QEasingCurve &&other)
1176 
1177     Move-assigns \a other to this QEasingCurve instance.
1178 
1179     \since 5.2
1180 */
1181 
1182 /*!
1183     \fn void QEasingCurve::swap(QEasingCurve &other)
1184     \since 5.0
1185 
1186     Swaps curve \a other with this curve. This operation is very
1187     fast and never fails.
1188 */
1189 
1190 /*!
1191     Compare this easing curve with \a other and returns \c true if they are
1192     equal. It will also compare the properties of a curve.
1193  */
operator ==(const QEasingCurve & other) const1194 bool QEasingCurve::operator==(const QEasingCurve &other) const
1195 {
1196     bool res = d_ptr->func == other.d_ptr->func
1197             && d_ptr->type == other.d_ptr->type;
1198     if (res) {
1199         if (d_ptr->config && other.d_ptr->config) {
1200         // catch the config content
1201             res = d_ptr->config->operator==(*(other.d_ptr->config));
1202 
1203         } else if (d_ptr->config || other.d_ptr->config) {
1204         // one one has a config object, which could contain default values
1205             res = qFuzzyCompare(amplitude(), other.amplitude()) &&
1206                   qFuzzyCompare(period(), other.period()) &&
1207                   qFuzzyCompare(overshoot(), other.overshoot());
1208         }
1209     }
1210     return res;
1211 }
1212 
1213 /*!
1214     \fn bool QEasingCurve::operator!=(const QEasingCurve &other) const
1215     Compare this easing curve with \a other and returns \c true if they are not equal.
1216     It will also compare the properties of a curve.
1217 
1218     \sa operator==()
1219 */
1220 
1221 /*!
1222     Returns the amplitude. This is not applicable for all curve types.
1223     It is only applicable for bounce and elastic curves (curves of type()
1224     QEasingCurve::InBounce, QEasingCurve::OutBounce, QEasingCurve::InOutBounce,
1225     QEasingCurve::OutInBounce, QEasingCurve::InElastic, QEasingCurve::OutElastic,
1226     QEasingCurve::InOutElastic or QEasingCurve::OutInElastic).
1227  */
amplitude() const1228 qreal QEasingCurve::amplitude() const
1229 {
1230     return d_ptr->config ? d_ptr->config->_a : qreal(1.0);
1231 }
1232 
1233 /*!
1234     Sets the amplitude to \a amplitude.
1235 
1236     This will set the amplitude of the bounce or the amplitude of the
1237     elastic "spring" effect. The higher the number, the higher the amplitude.
1238     \sa amplitude()
1239 */
setAmplitude(qreal amplitude)1240 void QEasingCurve::setAmplitude(qreal amplitude)
1241 {
1242     if (!d_ptr->config)
1243         d_ptr->config = curveToFunctionObject(d_ptr->type);
1244     d_ptr->config->_a = amplitude;
1245 }
1246 
1247 /*!
1248     Returns the period. This is not applicable for all curve types.
1249     It is only applicable if type() is QEasingCurve::InElastic, QEasingCurve::OutElastic,
1250     QEasingCurve::InOutElastic or QEasingCurve::OutInElastic.
1251  */
period() const1252 qreal QEasingCurve::period() const
1253 {
1254     return d_ptr->config ? d_ptr->config->_p : qreal(0.3);
1255 }
1256 
1257 /*!
1258     Sets the period to \a period.
1259     Setting a small period value will give a high frequency of the curve. A
1260     large period will give it a small frequency.
1261 
1262     \sa period()
1263 */
setPeriod(qreal period)1264 void QEasingCurve::setPeriod(qreal period)
1265 {
1266     if (!d_ptr->config)
1267         d_ptr->config = curveToFunctionObject(d_ptr->type);
1268     d_ptr->config->_p = period;
1269 }
1270 
1271 /*!
1272     Returns the overshoot. This is not applicable for all curve types.
1273     It is only applicable if type() is QEasingCurve::InBack, QEasingCurve::OutBack,
1274     QEasingCurve::InOutBack or QEasingCurve::OutInBack.
1275  */
overshoot() const1276 qreal QEasingCurve::overshoot() const
1277 {
1278     return d_ptr->config ? d_ptr->config->_o : qreal(1.70158) ;
1279 }
1280 
1281 /*!
1282     Sets the overshoot to \a overshoot.
1283 
1284     0 produces no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent.
1285 
1286     \sa overshoot()
1287 */
setOvershoot(qreal overshoot)1288 void QEasingCurve::setOvershoot(qreal overshoot)
1289 {
1290     if (!d_ptr->config)
1291         d_ptr->config = curveToFunctionObject(d_ptr->type);
1292     d_ptr->config->_o = overshoot;
1293 }
1294 
1295 /*!
1296     Adds a segment of a cubic bezier spline to define a custom easing curve.
1297     It is only applicable if type() is QEasingCurve::BezierSpline.
1298     Note that the spline implicitly starts at (0.0, 0.0) and has to end at (1.0, 1.0) to
1299     be a valid easing curve.
1300     \a c1 and \a c2 are the control points used for drawing the curve.
1301     \a endPoint is the endpoint of the curve.
1302  */
addCubicBezierSegment(const QPointF & c1,const QPointF & c2,const QPointF & endPoint)1303 void QEasingCurve::addCubicBezierSegment(const QPointF & c1, const QPointF & c2, const QPointF & endPoint)
1304 {
1305     if (!d_ptr->config)
1306         d_ptr->config = curveToFunctionObject(d_ptr->type);
1307     d_ptr->config->_bezierCurves << c1 << c2 << endPoint;
1308 }
1309 
tcbToBezier(const TCBPoints & tcbPoints)1310 QVector<QPointF> static inline tcbToBezier(const TCBPoints &tcbPoints)
1311 {
1312     const int count = tcbPoints.count();
1313     QVector<QPointF> bezierPoints;
1314     bezierPoints.reserve(3 * (count - 1));
1315 
1316     for (int i = 1; i < count; i++) {
1317         const qreal t_0 = tcbPoints.at(i - 1)._t;
1318         const qreal c_0 = tcbPoints.at(i - 1)._c;
1319         qreal b_0 = -1;
1320 
1321         qreal const t_1 = tcbPoints.at(i)._t;
1322         qreal const c_1 = tcbPoints.at(i)._c;
1323         qreal b_1 = 1;
1324 
1325         QPointF c_minusOne;                                   //P1 last segment - not available for the first point
1326         const QPointF c0(tcbPoints.at(i - 1)._point);         //P0 Hermite/TBC
1327         const QPointF c3(tcbPoints.at(i)._point);             //P1 Hermite/TBC
1328         QPointF c4;                                           //P0 next segment - not available for the last point
1329 
1330         if (i > 1) { //first point no left tangent
1331             c_minusOne = tcbPoints.at(i - 2)._point;
1332             b_0 = tcbPoints.at(i - 1)._b;
1333         }
1334 
1335         if (i < (count - 1)) { //last point no right tangent
1336             c4 = tcbPoints.at(i + 1)._point;
1337             b_1 = tcbPoints.at(i)._b;
1338         }
1339 
1340         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()));
1341         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()));
1342 
1343         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()));
1344         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()));
1345 
1346         const QPointF d_0 = QPointF(dx_0, dy_0);
1347         const QPointF d_1 = QPointF(dx_1, dy_1);
1348 
1349         QPointF c1 = (3 * c0 + d_0) / 3;
1350         QPointF c2 = (3 * c3 - d_1) / 3;
1351         bezierPoints << c1 << c2 << c3;
1352     }
1353     return bezierPoints;
1354 }
1355 
1356 /*!
1357     Adds a segment of a TCB bezier spline to define a custom easing curve.
1358     It is only applicable if type() is QEasingCurve::TCBSpline.
1359     The spline has to start explitly at (0.0, 0.0) and has to end at (1.0, 1.0) to
1360     be a valid easing curve.
1361     The tension \a t changes the length of the tangent vector.
1362     The continuity \a c changes the sharpness in change between the tangents.
1363     The bias \a b changes the direction of the tangent vector.
1364     \a nextPoint is the sample position.
1365     All three parameters are valid between -1 and 1 and define the
1366     tangent of the control point.
1367     If all three parameters are 0 the resulting spline is a Catmull-Rom spline.
1368     The begin and endpoint always have a bias of -1 and 1, since the outer tangent is not defined.
1369  */
addTCBSegment(const QPointF & nextPoint,qreal t,qreal c,qreal b)1370 void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qreal b)
1371 {
1372     if (!d_ptr->config)
1373         d_ptr->config = curveToFunctionObject(d_ptr->type);
1374 
1375     d_ptr->config->_tcbPoints.append(TCBPoint(nextPoint, t, c ,b));
1376 
1377     if (nextPoint == QPointF(1.0, 1.0)) {
1378         d_ptr->config->_bezierCurves = tcbToBezier(d_ptr->config->_tcbPoints);
1379         d_ptr->config->_tcbPoints.clear();
1380     }
1381 
1382 }
1383 
1384 /*!
1385     \fn QList<QPointF> QEasingCurve::cubicBezierSpline() const
1386     \obsolete Use toCubicSpline() instead.
1387  */
1388 
1389 /*!
1390     \since 5.0
1391 
1392     Returns the cubicBezierSpline that defines a custom easing curve.
1393     If the easing curve does not have a custom bezier easing curve the list
1394     is empty.
1395 */
toCubicSpline() const1396 QVector<QPointF> QEasingCurve::toCubicSpline() const
1397 {
1398     return d_ptr->config ? d_ptr->config->_bezierCurves : QVector<QPointF>();
1399 }
1400 
1401 /*!
1402     Returns the type of the easing curve.
1403 */
type() const1404 QEasingCurve::Type QEasingCurve::type() const
1405 {
1406     return d_ptr->type;
1407 }
1408 
setType_helper(QEasingCurve::Type newType)1409 void QEasingCurvePrivate::setType_helper(QEasingCurve::Type newType)
1410 {
1411     qreal amp = -1.0;
1412     qreal period = -1.0;
1413     qreal overshoot = -1.0;
1414     QVector<QPointF> bezierCurves;
1415     QVector<TCBPoint> tcbPoints;
1416 
1417     if (config) {
1418         amp = config->_a;
1419         period = config->_p;
1420         overshoot = config->_o;
1421         bezierCurves = std::move(config->_bezierCurves);
1422         tcbPoints = std::move(config->_tcbPoints);
1423 
1424         delete config;
1425         config = nullptr;
1426     }
1427 
1428     if (isConfigFunction(newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0) ||
1429         !bezierCurves.isEmpty()) {
1430         config = curveToFunctionObject(newType);
1431         if (amp != -1.0)
1432             config->_a = amp;
1433         if (period != -1.0)
1434             config->_p = period;
1435         if (overshoot != -1.0)
1436             config->_o = overshoot;
1437         config->_bezierCurves = std::move(bezierCurves);
1438         config->_tcbPoints = std::move(tcbPoints);
1439         func = nullptr;
1440     } else if (newType != QEasingCurve::Custom) {
1441         func = curveToFunc(newType);
1442     }
1443     Q_ASSERT((func == nullptr) == (config != nullptr));
1444     type = newType;
1445 }
1446 
1447 /*!
1448     Sets the type of the easing curve to \a type.
1449 */
setType(Type type)1450 void QEasingCurve::setType(Type type)
1451 {
1452     if (d_ptr->type == type)
1453         return;
1454     if (type < Linear || type >= NCurveTypes - 1) {
1455         qWarning("QEasingCurve: Invalid curve type %d", type);
1456         return;
1457     }
1458 
1459     d_ptr->setType_helper(type);
1460 }
1461 
1462 /*!
1463     Sets a custom easing curve that is defined by the user in the function \a func.
1464     The signature of the function is qreal myEasingFunction(qreal progress),
1465     where \e progress and the return value are considered to be normalized between 0 and 1.
1466     (In some cases the return value can be outside that range)
1467     After calling this function type() will return QEasingCurve::Custom.
1468     \a func cannot be zero.
1469 
1470     \sa customType()
1471     \sa valueForProgress()
1472 */
setCustomType(EasingFunction func)1473 void QEasingCurve::setCustomType(EasingFunction func)
1474 {
1475     if (!func) {
1476         qWarning("Function pointer must not be null");
1477         return;
1478     }
1479     d_ptr->func = func;
1480     d_ptr->setType_helper(Custom);
1481 }
1482 
1483 /*!
1484     Returns the function pointer to the custom easing curve.
1485     If type() does not return QEasingCurve::Custom, this function
1486     will return 0.
1487 */
customType() const1488 QEasingCurve::EasingFunction QEasingCurve::customType() const
1489 {
1490     return d_ptr->type == Custom ? d_ptr->func : nullptr;
1491 }
1492 
1493 /*!
1494     Return the effective progress for the easing curve at \a progress.
1495     Whereas \a progress must be between 0 and 1, the returned effective progress
1496     can be outside those bounds. For example, QEasingCurve::InBack will
1497     return negative values in the beginning of the function.
1498  */
valueForProgress(qreal progress) const1499 qreal QEasingCurve::valueForProgress(qreal progress) const
1500 {
1501     progress = qBound<qreal>(0, progress, 1);
1502     if (d_ptr->func)
1503         return d_ptr->func(progress);
1504     else if (d_ptr->config)
1505         return d_ptr->config->value(progress);
1506     else
1507         return progress;
1508 }
1509 
1510 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug debug,const QEasingCurve & item)1511 QDebug operator<<(QDebug debug, const QEasingCurve &item)
1512 {
1513     QDebugStateSaver saver(debug);
1514     debug << "type:" << item.d_ptr->type
1515           << "func:" << reinterpret_cast<const void *>(item.d_ptr->func);
1516     if (item.d_ptr->config) {
1517         debug << QString::fromLatin1("period:%1").arg(item.d_ptr->config->_p, 0, 'f', 20)
1518               << QString::fromLatin1("amp:%1").arg(item.d_ptr->config->_a, 0, 'f', 20)
1519               << QString::fromLatin1("overshoot:%1").arg(item.d_ptr->config->_o, 0, 'f', 20);
1520     }
1521     return debug;
1522 }
1523 #endif // QT_NO_DEBUG_STREAM
1524 
1525 #ifndef QT_NO_DATASTREAM
1526 /*!
1527     \fn QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
1528     \relates QEasingCurve
1529 
1530     Writes the given \a easing curve to the given \a stream and returns a
1531     reference to the stream.
1532 
1533     \sa {Serializing Qt Data Types}
1534 */
1535 
operator <<(QDataStream & stream,const QEasingCurve & easing)1536 QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
1537 {
1538     stream << quint8(easing.d_ptr->type);
1539     stream << quint64(quintptr(easing.d_ptr->func));
1540 
1541     bool hasConfig = easing.d_ptr->config;
1542     stream << hasConfig;
1543     if (hasConfig) {
1544         stream << easing.d_ptr->config;
1545     }
1546     return stream;
1547 }
1548 
1549 /*!
1550     \fn QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
1551     \relates QEasingCurve
1552 
1553     Reads an easing curve from the given \a stream into the given \a
1554     easing curve and returns a reference to the stream.
1555 
1556     \sa {Serializing Qt Data Types}
1557 */
1558 
operator >>(QDataStream & stream,QEasingCurve & easing)1559 QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
1560 {
1561     QEasingCurve::Type type;
1562     quint8 int_type;
1563     stream >> int_type;
1564     type = static_cast<QEasingCurve::Type>(int_type);
1565     easing.setType(type);
1566 
1567     quint64 ptr_func;
1568     stream >> ptr_func;
1569     easing.d_ptr->func = QEasingCurve::EasingFunction(quintptr(ptr_func));
1570 
1571     bool hasConfig;
1572     stream >> hasConfig;
1573     delete easing.d_ptr->config;
1574     easing.d_ptr->config = nullptr;
1575     if (hasConfig) {
1576         QEasingCurveFunction *config = curveToFunctionObject(type);
1577         stream >> config;
1578         easing.d_ptr->config = config;
1579     }
1580     return stream;
1581 }
1582 #endif // QT_NO_DATASTREAM
1583 
1584 QT_END_NAMESPACE
1585 
1586 #include "moc_qeasingcurve.cpp"
1587