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
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 QPointF _point;
325 qreal _t;
326 qreal _c;
327 qreal _b;
328
329 TCBPoint() {}
330 TCBPoint(QPointF point, qreal t, qreal c, qreal b) : _point(point), _t(t), _c(c), _b(b) {}
331
332 bool operator==(const TCBPoint &other) const
333 {
334 return _point == other._point &&
335 qFuzzyCompare(p1: _t, p2: other._t) &&
336 qFuzzyCompare(p1: _c, p2: other._c) &&
337 qFuzzyCompare(p1: _b, p2: other._b);
338 }
339};
340Q_DECLARE_TYPEINFO(TCBPoint, Q_PRIMITIVE_TYPE);
341
342QDataStream &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
351QDataStream &operator>>(QDataStream &stream, TCBPoint &point)
352{
353 stream >> point._point
354 >> point._t
355 >> point._c
356 >> point._b;
357 return stream;
358}
359
360typedef QVector<TCBPoint> TCBPoints;
361
362class QEasingCurveFunction
363{
364public:
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 { }
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
383QDataStream &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
397QDataStream &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
411static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve);
412
413qreal QEasingCurveFunction::value(qreal t)
414{
415 QEasingCurve::EasingFunction func = curveToFunc(curve: _t);
416 return func(t);
417}
418
419QEasingCurveFunction *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
427bool QEasingCurveFunction::operator==(const QEasingCurveFunction &other) const
428{
429 return _t == other._t &&
430 qFuzzyCompare(p1: _p, p2: other._p) &&
431 qFuzzyCompare(p1: _a, p2: other._a) &&
432 qFuzzyCompare(p1: _o, p2: other._o) &&
433 _bezierCurves == other._bezierCurves &&
434 _tcbPoints == other._tcbPoints;
435}
436
437QT_BEGIN_INCLUDE_NAMESPACE
438#include "../../3rdparty/easing/easing.cpp"
439QT_END_INCLUDE_NAMESPACE
440
441class QEasingCurvePrivate
442{
443public:
444 QEasingCurvePrivate()
445 : type(QEasingCurve::Linear),
446 config(nullptr),
447 func(&easeNone)
448 { }
449 QEasingCurvePrivate(const QEasingCurvePrivate &other)
450 : type(other.type),
451 config(other.config ? other.config->copy() : nullptr),
452 func(other.func)
453 { }
454 ~QEasingCurvePrivate() { delete config; }
455 void setType_helper(QEasingCurve::Type);
456
457 QEasingCurve::Type type;
458 QEasingCurveFunction *config;
459 QEasingCurve::EasingFunction func;
460};
461
462struct 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
477 BezierEase(QEasingCurve::Type type = QEasingCurve::BezierSpline)
478 : QEasingCurveFunction(type), _curves(10), _intervals(10), _init(false), _valid(false)
479 { }
480
481 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: 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(i: 0).x();
495 _curves[0].p1y = _bezierCurves.at(i: 0).y();
496
497 _curves[0].p2x = _bezierCurves.at(i: 1).x();
498 _curves[0].p2y = _bezierCurves.at(i: 1).y();
499
500 _curves[0].p3x = _bezierCurves.at(i: 2).x();
501 _curves[0].p3y = _bezierCurves.at(i: 2).y();
502
503 } else if (i == (_curveCount - 1)) {
504 _curves[i].p0x = _bezierCurves.at(i: _bezierCurves.count() - 4).x();
505 _curves[i].p0y = _bezierCurves.at(i: _bezierCurves.count() - 4).y();
506
507 _curves[i].p1x = _bezierCurves.at(i: _bezierCurves.count() - 3).x();
508 _curves[i].p1y = _bezierCurves.at(i: _bezierCurves.count() - 3).y();
509
510 _curves[i].p2x = _bezierCurves.at(i: _bezierCurves.count() - 2).x();
511 _curves[i].p2y = _bezierCurves.at(i: _bezierCurves.count() - 2).y();
512
513 _curves[i].p3x = _bezierCurves.at(i: _bezierCurves.count() - 1).x();
514 _curves[i].p3y = _bezierCurves.at(i: _bezierCurves.count() - 1).y();
515 } else {
516 _curves[i].p0x = _bezierCurves.at(i: i * 3 - 1).x();
517 _curves[i].p0y = _bezierCurves.at(i: i * 3 - 1).y();
518
519 _curves[i].p1x = _bezierCurves.at(i: i * 3).x();
520 _curves[i].p1y = _bezierCurves.at(i: i * 3).y();
521
522 _curves[i].p2x = _bezierCurves.at(i: i * 3 + 1).x();
523 _curves[i].p2y = _bezierCurves.at(i: i * 3 + 1).y();
524
525 _curves[i].p3x = _bezierCurves.at(i: i * 3 + 2).x();
526 _curves[i].p3y = _bezierCurves.at(i: i * 3 + 2).y();
527 }
528 }
529 _valid = true;
530 } else {
531 _valid = false;
532 }
533 }
534
535 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
547 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
562 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
571 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(msg: "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: *singleCubicBezier, t: findTForX(singleCubicBezier: *singleCubicBezier, x));
597 }
598
599 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
617 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
635 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
647 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
670 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
685 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
708 qreal static inline _acos(qreal x)
709 {
710 return std::sqrt(x: 1-x)*(1.5707963267948966192313216916398f + x*(-0.213300989f + x*(0.077980478f + x*-0.02164095f)));
711 }
712
713 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
746 bool static inline inRange(qreal f)
747 {
748 return (f >= -0.01 && f <= 1.01);
749 }
750
751 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(v: 1.0 + x);
758 const qreal one_minus_x_sqrt = qSqrt(v: 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
773 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(v: D);
797 qreal u = _cbrt( d: -q * 0.5 + D_sqrt);
798 qreal v = _cbrt( d: -q * 0.5 - D_sqrt);
799 qreal z1 = u + v;
800
801 qreal t1 = z1 - a_by3;
802
803 if (inRange(f: 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(v: -p);
812
813 //const qreal f = sqrt(4.0 / 3.0 * -p);
814 const qreal f = qSqrt(v: 4.0 / 3.0) * p_minus_sqrt;
815
816 //const qreal sqrtP = sqrt(27.0 / -p_cubic);
817 const qreal sqrtP = -3.0*qSqrt(v: 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(x: g, s1, s2, s3);
827
828 qreal z1 = -1* f * s2;
829 qreal t1 = z1 - a_by3;
830 if (inRange(f: t1))
831 return t1;
832
833 qreal z2 = f * s1;
834 qreal t2 = z2 - a_by3;
835 if (inRange(f: t2))
836 return t2;
837
838 qreal z3 = -1 * f * s3;
839 qreal t3 = z3 - a_by3;
840 return t3;
841 }
842
843 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
850 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(value: factorT3)) {
864 if (almostZero(value: factorT2)) {
865 if (almostZero(value: 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(x: discriminant)) / (2.0 * factorT2);
878 if (solution1 >= 0.0 && solution1 <= 1.0)
879 return solution1;
880
881 const qreal solution2 = (-factorT1 - std::sqrt(x: 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
899struct TCBEase : public BezierEase
900{
901 TCBEase()
902 : BezierEase(QEasingCurve::TCBSpline)
903 { }
904
905 qreal value(qreal x) override
906 {
907 Q_ASSERT(_bezierCurves.count() % 3 == 0);
908
909 if (_bezierCurves.isEmpty()) {
910 qWarning(msg: "QEasingCurve: Invalid tcb curve");
911 return x;
912 }
913
914 return BezierEase::value(x);
915 }
916
917 QEasingCurveFunction *copy() const override
918 {
919 return new TCBEase{*this};
920 }
921};
922
923struct ElasticEase : public QEasingCurveFunction
924{
925 ElasticEase(QEasingCurve::Type type)
926 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
927 { }
928
929 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
939 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
958struct BounceEase : public QEasingCurveFunction
959{
960 BounceEase(QEasingCurve::Type type)
961 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
962 { }
963
964 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
973 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
991struct BackEase : public QEasingCurveFunction
992{
993 BackEase(QEasingCurve::Type type)
994 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158))
995 { }
996
997 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
1006 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, s: o);
1017 case QEasingCurve::OutBack:
1018 return easeOutBack(t, s: o);
1019 case QEasingCurve::InOutBack:
1020 return easeInOutBack(t, s: o);
1021 case QEasingCurve::OutInBack:
1022 return easeOutInBack(t, s: o);
1023 default:
1024 return t;
1025 }
1026 }
1027};
1028
1029static 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
1104static 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 */
1145QEasingCurve::QEasingCurve(Type type)
1146 : d_ptr(new QEasingCurvePrivate)
1147{
1148 setType(type);
1149}
1150
1151/*!
1152 Construct a copy of \a other.
1153 */
1154QEasingCurve::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
1164QEasingCurve::~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 */
1194bool 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: *(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(p1: amplitude(), p2: other.amplitude()) &&
1206 qFuzzyCompare(p1: period(), p2: other.period()) &&
1207 qFuzzyCompare(p1: overshoot(), p2: 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 */
1228qreal 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*/
1240void QEasingCurve::setAmplitude(qreal amplitude)
1241{
1242 if (!d_ptr->config)
1243 d_ptr->config = curveToFunctionObject(type: 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 */
1252qreal 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*/
1264void QEasingCurve::setPeriod(qreal period)
1265{
1266 if (!d_ptr->config)
1267 d_ptr->config = curveToFunctionObject(type: 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 */
1276qreal 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*/
1288void QEasingCurve::setOvershoot(qreal overshoot)
1289{
1290 if (!d_ptr->config)
1291 d_ptr->config = curveToFunctionObject(type: 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 */
1303void QEasingCurve::addCubicBezierSegment(const QPointF & c1, const QPointF & c2, const QPointF & endPoint)
1304{
1305 if (!d_ptr->config)
1306 d_ptr->config = curveToFunctionObject(type: d_ptr->type);
1307 d_ptr->config->_bezierCurves << c1 << c2 << endPoint;
1308}
1309
1310QVector<QPointF> static inline tcbToBezier(const TCBPoints &tcbPoints)
1311{
1312 const int count = tcbPoints.count();
1313 QVector<QPointF> bezierPoints;
1314 bezierPoints.reserve(size: 3 * (count - 1));
1315
1316 for (int i = 1; i < count; i++) {
1317 const qreal t_0 = tcbPoints.at(i: i - 1)._t;
1318 const qreal c_0 = tcbPoints.at(i: 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: 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: i - 2)._point;
1332 b_0 = tcbPoints.at(i: i - 1)._b;
1333 }
1334
1335 if (i < (count - 1)) { //last point no right tangent
1336 c4 = tcbPoints.at(i: 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 */
1370void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qreal b)
1371{
1372 if (!d_ptr->config)
1373 d_ptr->config = curveToFunctionObject(type: d_ptr->type);
1374
1375 d_ptr->config->_tcbPoints.append(t: TCBPoint(nextPoint, t, c ,b));
1376
1377 if (nextPoint == QPointF(1.0, 1.0)) {
1378 d_ptr->config->_bezierCurves = tcbToBezier(tcbPoints: 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*/
1396QVector<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*/
1404QEasingCurve::Type QEasingCurve::type() const
1405{
1406 return d_ptr->type;
1407}
1408
1409void 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(type: newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0) ||
1429 !bezierCurves.isEmpty()) {
1430 config = curveToFunctionObject(type: 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(curve: 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*/
1450void QEasingCurve::setType(Type type)
1451{
1452 if (d_ptr->type == type)
1453 return;
1454 if (type < Linear || type >= NCurveTypes - 1) {
1455 qWarning(msg: "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*/
1473void QEasingCurve::setCustomType(EasingFunction func)
1474{
1475 if (!func) {
1476 qWarning(msg: "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*/
1488QEasingCurve::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 */
1499qreal QEasingCurve::valueForProgress(qreal progress) const
1500{
1501 progress = qBound<qreal>(min: 0, val: progress, max: 1);
1502 if (d_ptr->func)
1503 return d_ptr->func(progress);
1504 else if (d_ptr->config)
1505 return d_ptr->config->value(t: progress);
1506 else
1507 return progress;
1508}
1509
1510#ifndef QT_NO_DEBUG_STREAM
1511QDebug 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(str: "period:%1").arg(a: item.d_ptr->config->_p, fieldWidth: 0, fmt: 'f', prec: 20)
1518 << QString::fromLatin1(str: "amp:%1").arg(a: item.d_ptr->config->_a, fieldWidth: 0, fmt: 'f', prec: 20)
1519 << QString::fromLatin1(str: "overshoot:%1").arg(a: item.d_ptr->config->_o, fieldWidth: 0, fmt: 'f', prec: 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
1536QDataStream &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
1559QDataStream &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
1584QT_END_NAMESPACE
1585
1586#include "moc_qeasingcurve.cpp"
1587

source code of qtbase/src/corelib/tools/qeasingcurve.cpp