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/qlist.h>
312
313QT_BEGIN_NAMESPACE
314
315static 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
323struct TCBPoint
324{
325 QPointF _point;
326 qreal _t;
327 qreal _c;
328 qreal _b;
329
330 TCBPoint() {}
331 TCBPoint(QPointF point, qreal t, qreal c, qreal b) : _point(point), _t(t), _c(c), _b(b) {}
332
333 bool operator==(const TCBPoint &other) const
334 {
335 return _point == other._point &&
336 qFuzzyCompare(_t, other._t) &&
337 qFuzzyCompare(_c, other._c) &&
338 qFuzzyCompare(_b, other._b);
339 }
340};
341Q_DECLARE_TYPEINFO(TCBPoint, Q_PRIMITIVE_TYPE);
342
343QDataStream &operator<<(QDataStream &stream, const TCBPoint &point)
344{
345 stream << point._point
346 << point._t
347 << point._c
348 << point._b;
349 return stream;
350}
351
352QDataStream &operator>>(QDataStream &stream, TCBPoint &point)
353{
354 stream >> point._point
355 >> point._t
356 >> point._c
357 >> point._b;
358 return stream;
359}
360
361typedef QList<TCBPoint> TCBPoints;
362
363class QEasingCurveFunction
364{
365public:
366 QEasingCurveFunction(QEasingCurve::Type type, qreal period = 0.3, qreal amplitude = 1.0,
367 qreal overshoot = 1.70158)
368 : _t(type), _p(period), _a(amplitude), _o(overshoot)
369 { }
370 virtual ~QEasingCurveFunction() {}
371 virtual qreal value(qreal t);
372 virtual QEasingCurveFunction *copy() const;
373 bool operator==(const QEasingCurveFunction &other) const;
374
375 QEasingCurve::Type _t;
376 qreal _p;
377 qreal _a;
378 qreal _o;
379 QList<QPointF> _bezierCurves;
380 TCBPoints _tcbPoints;
381
382};
383
384QDataStream &operator<<(QDataStream &stream, QEasingCurveFunction *func)
385{
386 if (func) {
387 stream << func->_p;
388 stream << func->_a;
389 stream << func->_o;
390 if (stream.version() > QDataStream::Qt_5_12) {
391 stream << func->_bezierCurves;
392 stream << func->_tcbPoints;
393 }
394 }
395 return stream;
396}
397
398QDataStream &operator>>(QDataStream &stream, QEasingCurveFunction *func)
399{
400 if (func) {
401 stream >> func->_p;
402 stream >> func->_a;
403 stream >> func->_o;
404 if (stream.version() > QDataStream::Qt_5_12) {
405 stream >> func->_bezierCurves;
406 stream >> func->_tcbPoints;
407 }
408 }
409 return stream;
410}
411
412static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve);
413
414qreal QEasingCurveFunction::value(qreal t)
415{
416 QEasingCurve::EasingFunction func = curveToFunc(_t);
417 return func(t);
418}
419
420QEasingCurveFunction *QEasingCurveFunction::copy() const
421{
422 QEasingCurveFunction *rv = new QEasingCurveFunction(_t, _p, _a, _o);
423 rv->_bezierCurves = _bezierCurves;
424 rv->_tcbPoints = _tcbPoints;
425 return rv;
426}
427
428bool QEasingCurveFunction::operator==(const QEasingCurveFunction &other) const
429{
430 return _t == other._t &&
431 qFuzzyCompare(_p, other._p) &&
432 qFuzzyCompare(_a, other._a) &&
433 qFuzzyCompare(_o, other._o) &&
434 _bezierCurves == other._bezierCurves &&
435 _tcbPoints == other._tcbPoints;
436}
437
438QT_BEGIN_INCLUDE_NAMESPACE
439#include "../../3rdparty/easing/easing.cpp"
440QT_END_INCLUDE_NAMESPACE
441
442class QEasingCurvePrivate
443{
444public:
445 QEasingCurvePrivate()
446 : type(QEasingCurve::Linear),
447 config(nullptr),
448 func(&easeNone)
449 { }
450 QEasingCurvePrivate(const QEasingCurvePrivate &other)
451 : type(other.type),
452 config(other.config ? other.config->copy() : nullptr),
453 func(other.func)
454 { }
455 ~QEasingCurvePrivate() { delete config; }
456 void setType_helper(QEasingCurve::Type);
457
458 QEasingCurve::Type type;
459 QEasingCurveFunction *config;
460 QEasingCurve::EasingFunction func;
461};
462
463struct BezierEase : public QEasingCurveFunction
464{
465 struct SingleCubicBezier {
466 qreal p0x, p0y;
467 qreal p1x, p1y;
468 qreal p2x, p2y;
469 qreal p3x, p3y;
470 };
471
472 QList<SingleCubicBezier> _curves;
473 QList<qreal> _intervals;
474 int _curveCount;
475 bool _init;
476 bool _valid;
477
478 BezierEase(QEasingCurve::Type type = QEasingCurve::BezierSpline)
479 : QEasingCurveFunction(type), _curves(10), _intervals(10), _init(false), _valid(false)
480 { }
481
482 void init()
483 {
484 if (_bezierCurves.constLast() == QPointF(1.0, 1.0)) {
485 _init = true;
486 _curveCount = _bezierCurves.count() / 3;
487
488 for (int i=0; i < _curveCount; i++) {
489 _intervals[i] = _bezierCurves.at(i * 3 + 2).x();
490
491 if (i == 0) {
492 _curves[0].p0x = 0.0;
493 _curves[0].p0y = 0.0;
494
495 _curves[0].p1x = _bezierCurves.at(0).x();
496 _curves[0].p1y = _bezierCurves.at(0).y();
497
498 _curves[0].p2x = _bezierCurves.at(1).x();
499 _curves[0].p2y = _bezierCurves.at(1).y();
500
501 _curves[0].p3x = _bezierCurves.at(2).x();
502 _curves[0].p3y = _bezierCurves.at(2).y();
503
504 } else if (i == (_curveCount - 1)) {
505 _curves[i].p0x = _bezierCurves.at(_bezierCurves.count() - 4).x();
506 _curves[i].p0y = _bezierCurves.at(_bezierCurves.count() - 4).y();
507
508 _curves[i].p1x = _bezierCurves.at(_bezierCurves.count() - 3).x();
509 _curves[i].p1y = _bezierCurves.at(_bezierCurves.count() - 3).y();
510
511 _curves[i].p2x = _bezierCurves.at(_bezierCurves.count() - 2).x();
512 _curves[i].p2y = _bezierCurves.at(_bezierCurves.count() - 2).y();
513
514 _curves[i].p3x = _bezierCurves.at(_bezierCurves.count() - 1).x();
515 _curves[i].p3y = _bezierCurves.at(_bezierCurves.count() - 1).y();
516 } else {
517 _curves[i].p0x = _bezierCurves.at(i * 3 - 1).x();
518 _curves[i].p0y = _bezierCurves.at(i * 3 - 1).y();
519
520 _curves[i].p1x = _bezierCurves.at(i * 3).x();
521 _curves[i].p1y = _bezierCurves.at(i * 3).y();
522
523 _curves[i].p2x = _bezierCurves.at(i * 3 + 1).x();
524 _curves[i].p2y = _bezierCurves.at(i * 3 + 1).y();
525
526 _curves[i].p3x = _bezierCurves.at(i * 3 + 2).x();
527 _curves[i].p3y = _bezierCurves.at(i * 3 + 2).y();
528 }
529 }
530 _valid = true;
531 } else {
532 _valid = false;
533 }
534 }
535
536 QEasingCurveFunction *copy() const override
537 {
538 BezierEase *rv = new BezierEase();
539 rv->_t = _t;
540 rv->_p = _p;
541 rv->_a = _a;
542 rv->_o = _o;
543 rv->_bezierCurves = _bezierCurves;
544 rv->_tcbPoints = _tcbPoints;
545 return rv;
546 }
547
548 void getBezierSegment(SingleCubicBezier * &singleCubicBezier, qreal x)
549 {
550
551 int currentSegment = 0;
552
553 while (currentSegment < _curveCount) {
554 if (x <= _intervals.data()[currentSegment])
555 break;
556 currentSegment++;
557 }
558
559 singleCubicBezier = &_curves.data()[currentSegment];
560 }
561
562
563 qreal static inline newtonIteration(const SingleCubicBezier &singleCubicBezier, qreal t, qreal x)
564 {
565 qreal currentXValue = evaluateForX(singleCubicBezier, t);
566
567 const qreal newT = t - (currentXValue - x) / evaluateDerivateForX(singleCubicBezier, t);
568
569 return newT;
570 }
571
572 qreal value(qreal x) override
573 {
574 Q_ASSERT(_bezierCurves.count() % 3 == 0);
575
576 if (_bezierCurves.isEmpty()) {
577 return x;
578 }
579
580 if (!_init)
581 init();
582
583 if (!_valid) {
584 qWarning("QEasingCurve: Invalid bezier curve");
585 return x;
586 }
587
588 // The bezier computation is not always precise on the endpoints, so handle explicitly
589 if (!(x > 0))
590 return 0;
591 if (!(x < 1))
592 return 1;
593
594 SingleCubicBezier *singleCubicBezier = nullptr;
595 getBezierSegment(singleCubicBezier, x);
596
597 return evaluateSegmentForY(*singleCubicBezier, findTForX(*singleCubicBezier, x));
598 }
599
600 qreal static inline evaluateSegmentForY(const SingleCubicBezier &singleCubicBezier, qreal t)
601 {
602 const qreal p0 = singleCubicBezier.p0y;
603 const qreal p1 = singleCubicBezier.p1y;
604 const qreal p2 = singleCubicBezier.p2y;
605 const qreal p3 = singleCubicBezier.p3y;
606
607 const qreal s = 1 - t;
608
609 const qreal s_squared = s * s;
610 const qreal t_squared = t * t;
611
612 const qreal s_cubic = s_squared * s;
613 const qreal t_cubic = t_squared * t;
614
615 return s_cubic * p0 + 3 * s_squared * t * p1 + 3 * s * t_squared * p2 + t_cubic * p3;
616 }
617
618 qreal static inline evaluateForX(const SingleCubicBezier &singleCubicBezier, qreal t)
619 {
620 const qreal p0 = singleCubicBezier.p0x;
621 const qreal p1 = singleCubicBezier.p1x;
622 const qreal p2 = singleCubicBezier.p2x;
623 const qreal p3 = singleCubicBezier.p3x;
624
625 const qreal s = 1 - t;
626
627 const qreal s_squared = s * s;
628 const qreal t_squared = t * t;
629
630 const qreal s_cubic = s_squared * s;
631 const qreal t_cubic = t_squared * t;
632
633 return s_cubic * p0 + 3 * s_squared * t * p1 + 3 * s * t_squared * p2 + t_cubic * p3;
634 }
635
636 qreal static inline evaluateDerivateForX(const SingleCubicBezier &singleCubicBezier, qreal t)
637 {
638 const qreal p0 = singleCubicBezier.p0x;
639 const qreal p1 = singleCubicBezier.p1x;
640 const qreal p2 = singleCubicBezier.p2x;
641 const qreal p3 = singleCubicBezier.p3x;
642
643 const qreal t_squared = t * t;
644
645 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;
646 }
647
648 qreal static inline _cbrt(qreal d)
649 {
650 qreal sign = 1;
651 if (d < 0)
652 sign = -1;
653 d = d * sign;
654
655 qreal t = _fast_cbrt(d);
656
657 //one step of Halley's Method to get a better approximation
658 const qreal t_cubic = t * t * t;
659 const qreal f = t_cubic + t_cubic + d;
660 if (f != qreal(0.0))
661 t = t * (t_cubic + d + d) / f;
662
663 //another step
664 /*qreal t_i = t;
665 t_i_cubic = pow(t_i, 3);
666 t = t_i * (t_i_cubic + d + d) / (t_i_cubic + t_i_cubic + d);*/
667
668 return t * sign;
669 }
670
671 float static inline _fast_cbrt(float x)
672 {
673 union {
674 float f;
675 quint32 i;
676 } ux;
677
678 const unsigned int B1 = 709921077;
679
680 ux.f = x;
681 ux.i = (ux.i / 3 + B1);
682
683 return ux.f;
684 }
685
686 double static inline _fast_cbrt(double d)
687 {
688 union {
689 double d;
690 quint32 pt[2];
691 } ut, ux;
692
693 const unsigned int B1 = 715094163;
694
695#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
696 const int h0 = 1;
697#else
698 const int h0 = 0;
699#endif
700 ut.d = 0.0;
701 ux.d = d;
702
703 quint32 hx = ux.pt[h0]; //high word of d
704 ut.pt[h0] = hx / 3 + B1;
705
706 return ut.d;
707 }
708
709 qreal static inline _acos(qreal x)
710 {
711 return std::sqrt(1-x)*(1.5707963267948966192313216916398f + x*(-0.213300989f + x*(0.077980478f + x*-0.02164095f)));
712 }
713
714 qreal static inline _cos(qreal x) //super fast _cos
715 {
716 const qreal pi_times2 = 2 * M_PI;
717 const qreal pi_neg = -1 * M_PI;
718 const qreal pi_by2 = M_PI / 2.0;
719
720 x += pi_by2; //the polynom is for sin
721
722 if (x < pi_neg)
723 x += pi_times2;
724 else if (x > M_PI)
725 x -= pi_times2;
726
727 const qreal a = 0.405284735;
728 const qreal b = 1.27323954;
729
730 const qreal x_squared = x * x;
731
732 if (x < 0) {
733 qreal cos = b * x + a * x_squared;
734
735 if (cos < 0)
736 return 0.225 * (cos * -1 * cos - cos) + cos;
737 return 0.225 * (cos * cos - cos) + cos;
738 } //else
739
740 qreal cos = b * x - a * x_squared;
741
742 if (cos < 0)
743 return 0.225 * (cos * 1 * -cos - cos) + cos;
744 return 0.225 * (cos * cos - cos) + cos;
745 }
746
747 bool static inline inRange(qreal f)
748 {
749 return (f >= -0.01 && f <= 1.01);
750 }
751
752 void static inline cosacos(qreal x, qreal &s1, qreal &s2, qreal &s3 )
753 {
754 //This function has no proper algebraic representation in real numbers.
755 //We use approximations instead
756
757 const qreal x_squared = x * x;
758 const qreal x_plus_one_sqrt = qSqrt(1.0 + x);
759 const qreal one_minus_x_sqrt = qSqrt(1.0 - x);
760
761 //cos(acos(x) / 3)
762 //s1 = _cos(_acos(x) / 3);
763 s1 = 0.463614 - 0.0347815 * x + 0.00218245 * x_squared + 0.402421 * x_plus_one_sqrt;
764
765 //cos(acos((x) - M_PI) / 3)
766 //s3 = _cos((_acos(x) - M_PI) / 3);
767 s3 = 0.463614 + 0.402421 * one_minus_x_sqrt + 0.0347815 * x + 0.00218245 * x_squared;
768
769 //cos((acos(x) + M_PI) / 3)
770 //s2 = _cos((_acos(x) + M_PI) / 3);
771 s2 = -0.401644 * one_minus_x_sqrt - 0.0686804 * x + 0.401644 * x_plus_one_sqrt;
772 }
773
774 qreal static inline singleRealSolutionForCubic(qreal a, qreal b, qreal c)
775 {
776 //returns the real solutiuon in [0..1]
777 //We use the Cardano formula
778
779 //substituiton: x = z - a/3
780 // z^3+pz+q=0
781
782 if (c < 0.000001 && c > -0.000001)
783 return 0;
784
785 const qreal a_by3 = a / 3.0;
786
787 const qreal a_cubic = a * a * a;
788
789 const qreal p = b - a * a_by3;
790 const qreal q = 2.0 * a_cubic / 27.0 - a * b / 3.0 + c;
791
792 const qreal q_squared = q * q;
793 const qreal p_cubic = p * p * p;
794 const qreal D = 0.25 * q_squared + p_cubic / 27.0;
795
796 if (D >= 0) {
797 const qreal D_sqrt = qSqrt(D);
798 qreal u = _cbrt(-q * 0.5 + D_sqrt);
799 qreal v = _cbrt(-q * 0.5 - D_sqrt);
800 qreal z1 = u + v;
801
802 qreal t1 = z1 - a_by3;
803
804 if (inRange(t1))
805 return t1;
806 qreal z2 = -1 * u;
807 qreal t2 = z2 - a_by3;
808 return t2;
809 }
810
811 //casus irreducibilis
812 const qreal p_minus_sqrt = qSqrt(-p);
813
814 //const qreal f = sqrt(4.0 / 3.0 * -p);
815 const qreal f = qSqrt(4.0 / 3.0) * p_minus_sqrt;
816
817 //const qreal sqrtP = sqrt(27.0 / -p_cubic);
818 const qreal sqrtP = -3.0*qSqrt(3.0) / (p_minus_sqrt * p);
819
820
821 const qreal g = -q * 0.5 * sqrtP;
822
823 qreal s1;
824 qreal s2;
825 qreal s3;
826
827 cosacos(g, s1, s2, s3);
828
829 qreal z1 = -1 * f * s2;
830 qreal t1 = z1 - a_by3;
831 if (inRange(t1))
832 return t1;
833
834 qreal z2 = f * s1;
835 qreal t2 = z2 - a_by3;
836 if (inRange(t2))
837 return t2;
838
839 qreal z3 = -1 * f * s3;
840 qreal t3 = z3 - a_by3;
841 return t3;
842 }
843
844 bool static inline almostZero(qreal value)
845 {
846 // 1e-3 might seem excessively fuzzy, but any smaller value will make the
847 // factors a, b, and c large enough to knock out the cubic solver.
848 return value > -1e-3 && value < 1e-3;
849 }
850
851 qreal static inline findTForX(const SingleCubicBezier &singleCubicBezier, qreal x)
852 {
853 const qreal p0 = singleCubicBezier.p0x;
854 const qreal p1 = singleCubicBezier.p1x;
855 const qreal p2 = singleCubicBezier.p2x;
856 const qreal p3 = singleCubicBezier.p3x;
857
858 const qreal factorT3 = p3 - p0 + 3 * p1 - 3 * p2;
859 const qreal factorT2 = 3 * p0 - 6 * p1 + 3 * p2;
860 const qreal factorT1 = -3 * p0 + 3 * p1;
861 const qreal factorT0 = p0 - x;
862
863 // Cases for quadratic, linear and invalid equations
864 if (almostZero(factorT3)) {
865 if (almostZero(factorT2)) {
866 if (almostZero(factorT1))
867 return 0.0;
868
869 return -factorT0 / factorT1;
870 }
871 const qreal discriminant = factorT1 * factorT1 - 4.0 * factorT2 * factorT0;
872 if (discriminant < 0.0)
873 return 0.0;
874
875 if (discriminant == 0.0)
876 return -factorT1 / (2.0 * factorT2);
877
878 const qreal solution1 = (-factorT1 + std::sqrt(discriminant)) / (2.0 * factorT2);
879 if (solution1 >= 0.0 && solution1 <= 1.0)
880 return solution1;
881
882 const qreal solution2 = (-factorT1 - std::sqrt(discriminant)) / (2.0 * factorT2);
883 if (solution2 >= 0.0 && solution2 <= 1.0)
884 return solution2;
885
886 return 0.0;
887 }
888
889 const qreal a = factorT2 / factorT3;
890 const qreal b = factorT1 / factorT3;
891 const qreal c = factorT0 / factorT3;
892
893 return singleRealSolutionForCubic(a, b, c);
894
895 //one new iteration to increase numeric stability
896 //return newtonIteration(singleCubicBezier, t, x);
897 }
898};
899
900struct TCBEase : public BezierEase
901{
902 TCBEase()
903 : BezierEase(QEasingCurve::TCBSpline)
904 { }
905
906 qreal value(qreal x) override
907 {
908 Q_ASSERT(_bezierCurves.count() % 3 == 0);
909
910 if (_bezierCurves.isEmpty()) {
911 qWarning("QEasingCurve: Invalid tcb curve");
912 return x;
913 }
914
915 return BezierEase::value(x);
916 }
917
918 QEasingCurveFunction *copy() const override
919 {
920 return new TCBEase{*this};
921 }
922};
923
924struct ElasticEase : public QEasingCurveFunction
925{
926 ElasticEase(QEasingCurve::Type type)
927 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
928 { }
929
930 QEasingCurveFunction *copy() const override
931 {
932 ElasticEase *rv = new ElasticEase(_t);
933 rv->_p = _p;
934 rv->_a = _a;
935 rv->_bezierCurves = _bezierCurves;
936 rv->_tcbPoints = _tcbPoints;
937 return rv;
938 }
939
940 qreal value(qreal t) override
941 {
942 qreal p = (_p < 0) ? qreal(0.3) : _p;
943 qreal a = (_a < 0) ? qreal(1.0) : _a;
944 switch (_t) {
945 case QEasingCurve::InElastic:
946 return easeInElastic(t, a, p);
947 case QEasingCurve::OutElastic:
948 return easeOutElastic(t, a, p);
949 case QEasingCurve::InOutElastic:
950 return easeInOutElastic(t, a, p);
951 case QEasingCurve::OutInElastic:
952 return easeOutInElastic(t, a, p);
953 default:
954 return t;
955 }
956 }
957};
958
959struct BounceEase : public QEasingCurveFunction
960{
961 BounceEase(QEasingCurve::Type type)
962 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
963 { }
964
965 QEasingCurveFunction *copy() const override
966 {
967 BounceEase *rv = new BounceEase(_t);
968 rv->_a = _a;
969 rv->_bezierCurves = _bezierCurves;
970 rv->_tcbPoints = _tcbPoints;
971 return rv;
972 }
973
974 qreal value(qreal t) override
975 {
976 qreal a = (_a < 0) ? qreal(1.0) : _a;
977 switch (_t) {
978 case QEasingCurve::InBounce:
979 return easeInBounce(t, a);
980 case QEasingCurve::OutBounce:
981 return easeOutBounce(t, a);
982 case QEasingCurve::InOutBounce:
983 return easeInOutBounce(t, a);
984 case QEasingCurve::OutInBounce:
985 return easeOutInBounce(t, a);
986 default:
987 return t;
988 }
989 }
990};
991
992struct BackEase : public QEasingCurveFunction
993{
994 BackEase(QEasingCurve::Type type)
995 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158))
996 { }
997
998 QEasingCurveFunction *copy() const override
999 {
1000 BackEase *rv = new BackEase(_t);
1001 rv->_o = _o;
1002 rv->_bezierCurves = _bezierCurves;
1003 rv->_tcbPoints = _tcbPoints;
1004 return rv;
1005 }
1006
1007 qreal value(qreal t) override
1008 {
1009 // The *Back() functions are not always precise on the endpoints, so handle explicitly
1010 if (!(t > 0))
1011 return 0;
1012 if (!(t < 1))
1013 return 1;
1014 qreal o = (_o < 0) ? qreal(1.70158) : _o;
1015 switch (_t) {
1016 case QEasingCurve::InBack:
1017 return easeInBack(t, o);
1018 case QEasingCurve::OutBack:
1019 return easeOutBack(t, o);
1020 case QEasingCurve::InOutBack:
1021 return easeInOutBack(t, o);
1022 case QEasingCurve::OutInBack:
1023 return easeOutInBack(t, o);
1024 default:
1025 return t;
1026 }
1027 }
1028};
1029
1030static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve)
1031{
1032 switch (curve) {
1033 case QEasingCurve::Linear:
1034 return &easeNone;
1035 case QEasingCurve::InQuad:
1036 return &easeInQuad;
1037 case QEasingCurve::OutQuad:
1038 return &easeOutQuad;
1039 case QEasingCurve::InOutQuad:
1040 return &easeInOutQuad;
1041 case QEasingCurve::OutInQuad:
1042 return &easeOutInQuad;
1043 case QEasingCurve::InCubic:
1044 return &easeInCubic;
1045 case QEasingCurve::OutCubic:
1046 return &easeOutCubic;
1047 case QEasingCurve::InOutCubic:
1048 return &easeInOutCubic;
1049 case QEasingCurve::OutInCubic:
1050 return &easeOutInCubic;
1051 case QEasingCurve::InQuart:
1052 return &easeInQuart;
1053 case QEasingCurve::OutQuart:
1054 return &easeOutQuart;
1055 case QEasingCurve::InOutQuart:
1056 return &easeInOutQuart;
1057 case QEasingCurve::OutInQuart:
1058 return &easeOutInQuart;
1059 case QEasingCurve::InQuint:
1060 return &easeInQuint;
1061 case QEasingCurve::OutQuint:
1062 return &easeOutQuint;
1063 case QEasingCurve::InOutQuint:
1064 return &easeInOutQuint;
1065 case QEasingCurve::OutInQuint:
1066 return &easeOutInQuint;
1067 case QEasingCurve::InSine:
1068 return &easeInSine;
1069 case QEasingCurve::OutSine:
1070 return &easeOutSine;
1071 case QEasingCurve::InOutSine:
1072 return &easeInOutSine;
1073 case QEasingCurve::OutInSine:
1074 return &easeOutInSine;
1075 case QEasingCurve::InExpo:
1076 return &easeInExpo;
1077 case QEasingCurve::OutExpo:
1078 return &easeOutExpo;
1079 case QEasingCurve::InOutExpo:
1080 return &easeInOutExpo;
1081 case QEasingCurve::OutInExpo:
1082 return &easeOutInExpo;
1083 case QEasingCurve::InCirc:
1084 return &easeInCirc;
1085 case QEasingCurve::OutCirc:
1086 return &easeOutCirc;
1087 case QEasingCurve::InOutCirc:
1088 return &easeInOutCirc;
1089 case QEasingCurve::OutInCirc:
1090 return &easeOutInCirc;
1091 // Internal - needed for QTimeLine backward-compatibility:
1092 case QEasingCurve::InCurve:
1093 return &easeInCurve;
1094 case QEasingCurve::OutCurve:
1095 return &easeOutCurve;
1096 case QEasingCurve::SineCurve:
1097 return &easeSineCurve;
1098 case QEasingCurve::CosineCurve:
1099 return &easeCosineCurve;
1100 default:
1101 return nullptr;
1102 };
1103}
1104
1105static QEasingCurveFunction *curveToFunctionObject(QEasingCurve::Type type)
1106{
1107 switch (type) {
1108 case QEasingCurve::InElastic:
1109 case QEasingCurve::OutElastic:
1110 case QEasingCurve::InOutElastic:
1111 case QEasingCurve::OutInElastic:
1112 return new ElasticEase(type);
1113 case QEasingCurve::OutBounce:
1114 case QEasingCurve::InBounce:
1115 case QEasingCurve::OutInBounce:
1116 case QEasingCurve::InOutBounce:
1117 return new BounceEase(type);
1118 case QEasingCurve::InBack:
1119 case QEasingCurve::OutBack:
1120 case QEasingCurve::InOutBack:
1121 case QEasingCurve::OutInBack:
1122 return new BackEase(type);
1123 case QEasingCurve::BezierSpline:
1124 return new BezierEase;
1125 case QEasingCurve::TCBSpline:
1126 return new TCBEase;
1127 default:
1128 return new QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158));
1129 }
1130
1131 return nullptr;
1132}
1133
1134/*!
1135 \fn QEasingCurve::QEasingCurve(QEasingCurve &&other)
1136
1137 Move-constructs a QEasingCurve instance, making it point at the same
1138 object that \a other was pointing to.
1139
1140 \since 5.2
1141*/
1142
1143/*!
1144 Constructs an easing curve of the given \a type.
1145 */
1146QEasingCurve::QEasingCurve(Type type)
1147 : d_ptr(new QEasingCurvePrivate)
1148{
1149 setType(type);
1150}
1151
1152/*!
1153 Construct a copy of \a other.
1154 */
1155QEasingCurve::QEasingCurve(const QEasingCurve &other)
1156 : d_ptr(new QEasingCurvePrivate(*other.d_ptr))
1157{
1158 // ### non-atomic, requires malloc on shallow copy
1159}
1160
1161/*!
1162 Destructor.
1163 */
1164
1165QEasingCurve::~QEasingCurve()
1166{
1167 delete d_ptr;
1168}
1169
1170/*!
1171 \fn QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other)
1172 Copy \a other.
1173 */
1174
1175/*!
1176 \fn QEasingCurve &QEasingCurve::operator=(QEasingCurve &&other)
1177
1178 Move-assigns \a other to this QEasingCurve instance.
1179
1180 \since 5.2
1181*/
1182
1183/*!
1184 \fn void QEasingCurve::swap(QEasingCurve &other)
1185 \since 5.0
1186
1187 Swaps curve \a other with this curve. This operation is very
1188 fast and never fails.
1189*/
1190
1191/*!
1192 Compare this easing curve with \a other and returns \c true if they are
1193 equal. It will also compare the properties of a curve.
1194 */
1195bool QEasingCurve::operator==(const QEasingCurve &other) const
1196{
1197 bool res = d_ptr->func == other.d_ptr->func
1198 && d_ptr->type == other.d_ptr->type;
1199 if (res) {
1200 if (d_ptr->config && other.d_ptr->config) {
1201 // catch the config content
1202 res = d_ptr->config->operator==(*(other.d_ptr->config));
1203
1204 } else if (d_ptr->config || other.d_ptr->config) {
1205 // one one has a config object, which could contain default values
1206 res = qFuzzyCompare(amplitude(), other.amplitude())
1207 && qFuzzyCompare(period(), other.period())
1208 && qFuzzyCompare(overshoot(), other.overshoot());
1209 }
1210 }
1211 return res;
1212}
1213
1214/*!
1215 \fn bool QEasingCurve::operator!=(const QEasingCurve &other) const
1216 Compare this easing curve with \a other and returns \c true if they are not equal.
1217 It will also compare the properties of a curve.
1218
1219 \sa operator==()
1220*/
1221
1222/*!
1223 Returns the amplitude. This is not applicable for all curve types.
1224 It is only applicable for bounce and elastic curves (curves of type()
1225 QEasingCurve::InBounce, QEasingCurve::OutBounce, QEasingCurve::InOutBounce,
1226 QEasingCurve::OutInBounce, QEasingCurve::InElastic, QEasingCurve::OutElastic,
1227 QEasingCurve::InOutElastic or QEasingCurve::OutInElastic).
1228 */
1229qreal QEasingCurve::amplitude() const
1230{
1231 return d_ptr->config ? d_ptr->config->_a : qreal(1.0);
1232}
1233
1234/*!
1235 Sets the amplitude to \a amplitude.
1236
1237 This will set the amplitude of the bounce or the amplitude of the
1238 elastic "spring" effect. The higher the number, the higher the amplitude.
1239 \sa amplitude()
1240*/
1241void QEasingCurve::setAmplitude(qreal amplitude)
1242{
1243 if (!d_ptr->config)
1244 d_ptr->config = curveToFunctionObject(d_ptr->type);
1245 d_ptr->config->_a = amplitude;
1246}
1247
1248/*!
1249 Returns the period. This is not applicable for all curve types.
1250 It is only applicable if type() is QEasingCurve::InElastic, QEasingCurve::OutElastic,
1251 QEasingCurve::InOutElastic or QEasingCurve::OutInElastic.
1252 */
1253qreal QEasingCurve::period() const
1254{
1255 return d_ptr->config ? d_ptr->config->_p : qreal(0.3);
1256}
1257
1258/*!
1259 Sets the period to \a period.
1260 Setting a small period value will give a high frequency of the curve. A
1261 large period will give it a small frequency.
1262
1263 \sa period()
1264*/
1265void QEasingCurve::setPeriod(qreal period)
1266{
1267 if (!d_ptr->config)
1268 d_ptr->config = curveToFunctionObject(d_ptr->type);
1269 d_ptr->config->_p = period;
1270}
1271
1272/*!
1273 Returns the overshoot. This is not applicable for all curve types.
1274 It is only applicable if type() is QEasingCurve::InBack, QEasingCurve::OutBack,
1275 QEasingCurve::InOutBack or QEasingCurve::OutInBack.
1276 */
1277qreal QEasingCurve::overshoot() const
1278{
1279 return d_ptr->config ? d_ptr->config->_o : qreal(1.70158);
1280}
1281
1282/*!
1283 Sets the overshoot to \a overshoot.
1284
1285 0 produces no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent.
1286
1287 \sa overshoot()
1288*/
1289void QEasingCurve::setOvershoot(qreal overshoot)
1290{
1291 if (!d_ptr->config)
1292 d_ptr->config = curveToFunctionObject(d_ptr->type);
1293 d_ptr->config->_o = overshoot;
1294}
1295
1296/*!
1297 Adds a segment of a cubic bezier spline to define a custom easing curve.
1298 It is only applicable if type() is QEasingCurve::BezierSpline.
1299 Note that the spline implicitly starts at (0.0, 0.0) and has to end at (1.0, 1.0) to
1300 be a valid easing curve.
1301 \a c1 and \a c2 are the control points used for drawing the curve.
1302 \a endPoint is the endpoint of the curve.
1303 */
1304void QEasingCurve::addCubicBezierSegment(const QPointF & c1, const QPointF & c2, const QPointF & endPoint)
1305{
1306 if (!d_ptr->config)
1307 d_ptr->config = curveToFunctionObject(d_ptr->type);
1308 d_ptr->config->_bezierCurves << c1 << c2 << endPoint;
1309}
1310
1311QList<QPointF> static inline tcbToBezier(const TCBPoints &tcbPoints)
1312{
1313 const int count = tcbPoints.count();
1314 QList<QPointF> bezierPoints;
1315 bezierPoints.reserve(3 * (count - 1));
1316
1317 for (int i = 1; i < count; i++) {
1318 const qreal t_0 = tcbPoints.at(i - 1)._t;
1319 const qreal c_0 = tcbPoints.at(i - 1)._c;
1320 qreal b_0 = -1;
1321
1322 qreal const t_1 = tcbPoints.at(i)._t;
1323 qreal const c_1 = tcbPoints.at(i)._c;
1324 qreal b_1 = 1;
1325
1326 QPointF c_minusOne; //P1 last segment - not available for the first point
1327 const QPointF c0(tcbPoints.at(i - 1)._point); //P0 Hermite/TBC
1328 const QPointF c3(tcbPoints.at(i)._point); //P1 Hermite/TBC
1329 QPointF c4; //P0 next segment - not available for the last point
1330
1331 if (i > 1) { //first point no left tangent
1332 c_minusOne = tcbPoints.at(i - 2)._point;
1333 b_0 = tcbPoints.at(i - 1)._b;
1334 }
1335
1336 if (i < (count - 1)) { //last point no right tangent
1337 c4 = tcbPoints.at(i + 1)._point;
1338 b_1 = tcbPoints.at(i)._b;
1339 }
1340
1341 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()));
1342 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()));
1343
1344 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()));
1345 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()));
1346
1347 const QPointF d_0 = QPointF(dx_0, dy_0);
1348 const QPointF d_1 = QPointF(dx_1, dy_1);
1349
1350 QPointF c1 = (3 * c0 + d_0) / 3;
1351 QPointF c2 = (3 * c3 - d_1) / 3;
1352 bezierPoints << c1 << c2 << c3;
1353 }
1354 return bezierPoints;
1355}
1356
1357/*!
1358 Adds a segment of a TCB bezier spline to define a custom easing curve.
1359 It is only applicable if type() is QEasingCurve::TCBSpline.
1360 The spline has to start explitly at (0.0, 0.0) and has to end at (1.0, 1.0) to
1361 be a valid easing curve.
1362 The tension \a t changes the length of the tangent vector.
1363 The continuity \a c changes the sharpness in change between the tangents.
1364 The bias \a b changes the direction of the tangent vector.
1365 \a nextPoint is the sample position.
1366 All three parameters are valid between -1 and 1 and define the
1367 tangent of the control point.
1368 If all three parameters are 0 the resulting spline is a Catmull-Rom spline.
1369 The begin and endpoint always have a bias of -1 and 1, since the outer tangent is not defined.
1370 */
1371void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qreal b)
1372{
1373 if (!d_ptr->config)
1374 d_ptr->config = curveToFunctionObject(d_ptr->type);
1375
1376 d_ptr->config->_tcbPoints.append(TCBPoint(nextPoint, t, c, b));
1377
1378 if (nextPoint == QPointF(1.0, 1.0)) {
1379 d_ptr->config->_bezierCurves = tcbToBezier(d_ptr->config->_tcbPoints);
1380 d_ptr->config->_tcbPoints.clear();
1381 }
1382
1383}
1384
1385/*!
1386 \since 5.0
1387
1388 Returns the cubicBezierSpline that defines a custom easing curve.
1389 If the easing curve does not have a custom bezier easing curve the list
1390 is empty.
1391*/
1392QList<QPointF> QEasingCurve::toCubicSpline() const
1393{
1394 return d_ptr->config ? d_ptr->config->_bezierCurves : QList<QPointF>();
1395}
1396
1397/*!
1398 Returns the type of the easing curve.
1399*/
1400QEasingCurve::Type QEasingCurve::type() const
1401{
1402 return d_ptr->type;
1403}
1404
1405void QEasingCurvePrivate::setType_helper(QEasingCurve::Type newType)
1406{
1407 qreal amp = -1.0;
1408 qreal period = -1.0;
1409 qreal overshoot = -1.0;
1410 QList<QPointF> bezierCurves;
1411 QList<TCBPoint> tcbPoints;
1412
1413 if (config) {
1414 amp = config->_a;
1415 period = config->_p;
1416 overshoot = config->_o;
1417 bezierCurves = std::move(config->_bezierCurves);
1418 tcbPoints = std::move(config->_tcbPoints);
1419
1420 delete config;
1421 config = nullptr;
1422 }
1423
1424 if (isConfigFunction(newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0) ||
1425 !bezierCurves.isEmpty()) {
1426 config = curveToFunctionObject(newType);
1427 if (amp != -1.0)
1428 config->_a = amp;
1429 if (period != -1.0)
1430 config->_p = period;
1431 if (overshoot != -1.0)
1432 config->_o = overshoot;
1433 config->_bezierCurves = std::move(bezierCurves);
1434 config->_tcbPoints = std::move(tcbPoints);
1435 func = nullptr;
1436 } else if (newType != QEasingCurve::Custom) {
1437 func = curveToFunc(newType);
1438 }
1439 Q_ASSERT((func == nullptr) == (config != nullptr));
1440 type = newType;
1441}
1442
1443/*!
1444 Sets the type of the easing curve to \a type.
1445*/
1446void QEasingCurve::setType(Type type)
1447{
1448 if (d_ptr->type == type)
1449 return;
1450 if (type < Linear || type >= NCurveTypes - 1) {
1451 qWarning("QEasingCurve: Invalid curve type %d", type);
1452 return;
1453 }
1454
1455 d_ptr->setType_helper(type);
1456}
1457
1458/*!
1459 Sets a custom easing curve that is defined by the user in the function \a func.
1460 The signature of the function is qreal myEasingFunction(qreal progress),
1461 where \e progress and the return value are considered to be normalized between 0 and 1.
1462 (In some cases the return value can be outside that range)
1463 After calling this function type() will return QEasingCurve::Custom.
1464 \a func cannot be zero.
1465
1466 \sa customType()
1467 \sa valueForProgress()
1468*/
1469void QEasingCurve::setCustomType(EasingFunction func)
1470{
1471 if (!func) {
1472 qWarning("Function pointer must not be null");
1473 return;
1474 }
1475 d_ptr->func = func;
1476 d_ptr->setType_helper(Custom);
1477}
1478
1479/*!
1480 Returns the function pointer to the custom easing curve.
1481 If type() does not return QEasingCurve::Custom, this function
1482 will return 0.
1483*/
1484QEasingCurve::EasingFunction QEasingCurve::customType() const
1485{
1486 return d_ptr->type == Custom ? d_ptr->func : nullptr;
1487}
1488
1489/*!
1490 Return the effective progress for the easing curve at \a progress.
1491 Whereas \a progress must be between 0 and 1, the returned effective progress
1492 can be outside those bounds. For example, QEasingCurve::InBack will
1493 return negative values in the beginning of the function.
1494 */
1495qreal QEasingCurve::valueForProgress(qreal progress) const
1496{
1497 progress = qBound<qreal>(0, progress, 1);
1498 if (d_ptr->func)
1499 return d_ptr->func(progress);
1500 else if (d_ptr->config)
1501 return d_ptr->config->value(progress);
1502 else
1503 return progress;
1504}
1505
1506#ifndef QT_NO_DEBUG_STREAM
1507QDebug operator<<(QDebug debug, const QEasingCurve &item)
1508{
1509 QDebugStateSaver saver(debug);
1510 debug << "type:" << item.d_ptr->type
1511 << "func:" << reinterpret_cast<const void *>(item.d_ptr->func);
1512 if (item.d_ptr->config) {
1513 debug << QString::fromLatin1("period:%1").arg(item.d_ptr->config->_p, 0, 'f', 20)
1514 << QString::fromLatin1("amp:%1").arg(item.d_ptr->config->_a, 0, 'f', 20)
1515 << QString::fromLatin1("overshoot:%1").arg(item.d_ptr->config->_o, 0, 'f', 20);
1516 }
1517 return debug;
1518}
1519#endif // QT_NO_DEBUG_STREAM
1520
1521#ifndef QT_NO_DATASTREAM
1522/*!
1523 \fn QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
1524 \relates QEasingCurve
1525
1526 Writes the given \a easing curve to the given \a stream and returns a
1527 reference to the stream.
1528
1529 \sa {Serializing Qt Data Types}
1530*/
1531
1532QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
1533{
1534 stream << quint8(easing.d_ptr->type);
1535 stream << quint64(quintptr(easing.d_ptr->func));
1536
1537 bool hasConfig = easing.d_ptr->config;
1538 stream << hasConfig;
1539 if (hasConfig) {
1540 stream << easing.d_ptr->config;
1541 }
1542 return stream;
1543}
1544
1545/*!
1546 \fn QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
1547 \relates QEasingCurve
1548
1549 Reads an easing curve from the given \a stream into the given \a
1550 easing curve and returns a reference to the stream.
1551
1552 \sa {Serializing Qt Data Types}
1553*/
1554
1555QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
1556{
1557 QEasingCurve::Type type;
1558 quint8 int_type;
1559 stream >> int_type;
1560 type = static_cast<QEasingCurve::Type>(int_type);
1561 easing.setType(type);
1562
1563 quint64 ptr_func;
1564 stream >> ptr_func;
1565 easing.d_ptr->func = QEasingCurve::EasingFunction(quintptr(ptr_func));
1566
1567 bool hasConfig;
1568 stream >> hasConfig;
1569 delete easing.d_ptr->config;
1570 easing.d_ptr->config = nullptr;
1571 if (hasConfig) {
1572 QEasingCurveFunction *config = curveToFunctionObject(type);
1573 stream >> config;
1574 easing.d_ptr->config = config;
1575 }
1576 return stream;
1577}
1578#endif // QT_NO_DATASTREAM
1579
1580QT_END_NAMESPACE
1581
1582#include "moc_qeasingcurve.cpp"
1583