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