Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qeasingcurve.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5/*
6
7| *property* | *Used for type* |
8| period | QEasingCurve::{In,Out,InOut,OutIn}Elastic |
9| amplitude | QEasingCurve::{In,Out,InOut,OutIn}Bounce, QEasingCurve::{In,Out,InOut,OutIn}Elastic |
10| overshoot | QEasingCurve::{In,Out,InOut,OutIn}Back |
11
12*/
13
14
15
16
17/*!
18 \class QEasingCurve
19 \inmodule QtCore
20 \since 4.6
21 \ingroup animation
22 \brief The QEasingCurve class provides easing curves for controlling animation.
23
24 Easing curves describe a function that controls how the speed of the interpolation
25 between 0 and 1 should be. Easing curves allow transitions from
26 one value to another to appear more natural than a simple constant speed would allow.
27 The QEasingCurve class is usually used in conjunction with the QVariantAnimation and
28 QPropertyAnimation classes but can be used on its own. It is usually used to accelerate
29 the interpolation from zero velocity (ease in) or decelerate to zero velocity (ease out).
30 Ease in and ease out can also be combined in the same easing curve.
31
32 To calculate the speed of the interpolation, the easing curve provides the function
33 valueForProgress(), where the \a progress argument specifies the progress of the
34 interpolation: 0 is the start value of the interpolation, 1 is the end value of the
35 interpolation. The returned value is the effective progress of the interpolation.
36 If the returned value is the same as the input value for all input values the easing
37 curve is a linear curve. This is the default behaviour.
38
39 For example,
40
41 \snippet code/src_corelib_tools_qeasingcurve.cpp 0
42
43 will print the effective progress of the interpolation between 0 and 1.
44
45 When using a QPropertyAnimation, the associated easing curve will be used to control the
46 progress of the interpolation between startValue and endValue:
47
48 \snippet code/src_corelib_tools_qeasingcurve.cpp 1
49
50 The ability to set an amplitude, overshoot, or period depends on
51 the QEasingCurve type. Amplitude access is available to curves
52 that behave as springs such as elastic and bounce curves. Changing
53 the amplitude changes the height of the curve. Period access is
54 only available to elastic curves and setting a higher period slows
55 the rate of bounce. Only curves that have "boomerang" behaviors
56 such as the InBack, OutBack, InOutBack, and OutInBack have
57 overshoot settings. These curves will interpolate beyond the end
58 points and return to the end point, acting similar to a boomerang.
59
60 The \l{Easing Curves Example} contains samples of QEasingCurve
61 types and lets you change the curve settings.
62
63 */
64
65/*!
66 \enum QEasingCurve::Type
67
68 The type of easing curve.
69
70 \value Linear \image qeasingcurve-linear.png {Graph of a
71 linear function.}
72 \caption Easing curve for a linear (t) function:
73 velocity is constant.
74 \value InQuad \image qeasingcurve-inquad.png {Graph of a
75 InQuadratic function.}
76 \caption Easing curve for a quadratic (t^2) function:
77 accelerating from zero velocity.
78 \value OutQuad \image qeasingcurve-outquad.png {Graph of a
79 OutQuadratic function}
80 \caption Easing curve for a quadratic (t^2) function:
81 decelerating to zero velocity.
82 \value InOutQuad \image qeasingcurve-inoutquad.png {Graph of a
83 InOutquad quadratic function}
84 \caption Easing curve for a quadratic (t^2) function:
85 acceleration until halfway, then deceleration.
86 \value OutInQuad \image qeasingcurve-outinquad.png {Graph of a
87 OutInquad quadratic function}
88 \caption Easing curve for a quadratic (t^2) function:
89 deceleration until halfway, then acceleration.
90 \value InCubic \image qeasingcurve-incubic.png {Graph of a
91 InCubic function}
92 \caption Easing curve for a cubic (t^3) function:
93 accelerating from zero velocity.
94 \value OutCubic \image qeasingcurve-outcubic.png {Graph of a
95 OutCubic function}
96 \caption Easing curve for a cubic (t^3) function:
97 decelerating to zero velocity.
98 \value InOutCubic \image qeasingcurve-inoutcubic.png {Graph of a
99 InOutCubic function.}
100 \caption Easing curve for a cubic (t^3) function:
101 acceleration until halfway, then deceleration.
102 \value OutInCubic \image qeasingcurve-outincubic.png {Graph of a
103 OutInCubic function.}
104 \caption Easing curve for a cubic (t^3) function:
105 deceleration until halfway, then acceleration.
106 \value InQuart \image qeasingcurve-inquart.png {Graph of a
107 InQuartic function.}
108 \caption Easing curve for a quartic (t^4) function:
109 accelerating from zero velocity.
110 \value OutQuart \image qeasingcurve-outquart.png {Graph of a
111 OutQuartic function.}
112 \caption
113 Easing curve for a quartic (t^4) function:
114 decelerating to zero velocity.
115 \value InOutQuart \image qeasingcurve-inoutquart.png {Graph of a
116 InOutQuartic function.}
117 \caption
118 Easing curve for a quartic (t^4) function:
119 acceleration until halfway, then deceleration.
120 \value OutInQuart \image qeasingcurve-outinquart.png {Graph of a
121 OutInQuartic function.}
122 \caption
123 Easing curve for a quartic (t^4) function:
124 deceleration until halfway, then acceleration.
125 \value InQuint \image qeasingcurve-inquint.png {Graph of a
126 InQuintic function}
127 \caption
128 Easing curve for a quintic (t^5) easing
129 in: accelerating from zero velocity.
130 \value OutQuint \image qeasingcurve-outquint.png {Graph of a
131 OutQuintic function}
132 \caption
133 Easing curve for a quintic (t^5) function:
134 decelerating to zero velocity.
135 \value InOutQuint \image qeasingcurve-inoutquint.png {Graph of a
136 InOutQuintic function}
137 \caption
138 Easing curve for a quintic (t^5) function:
139 acceleration until halfway, then deceleration.
140 \value OutInQuint \image qeasingcurve-outinquint.png {Graph of a
141 OutInQuintic function}
142 \caption
143 Easing curve for a quintic (t^5) function:
144 deceleration until halfway, then acceleration.
145 \value InSine \image qeasingcurve-insine.png {Graph of a
146 InSinusoidal function}
147 \caption
148 Easing curve for a sinusoidal (sin(t)) function:
149 accelerating from zero velocity.
150 \value OutSine \image qeasingcurve-outsine.png {Graph of a
151 OutSinusoidal function}
152 \caption
153 Easing curve for a sinusoidal (sin(t)) function:
154 decelerating to zero velocity.
155 \value InOutSine \image qeasingcurve-inoutsine.png {Graph of a
156 InOutSinusoidal function}
157 \caption
158 Easing curve for a sinusoidal (sin(t)) function:
159 acceleration until halfway, then deceleration.
160 \value OutInSine \image qeasingcurve-outinsine.png {Graph of a
161 OutInSinusoidal function}
162 \caption
163 Easing curve for a sinusoidal (sin(t)) function:
164 deceleration until halfway, then acceleration.
165 \value InExpo \image qeasingcurve-inexpo.png {Graph of a
166 InExponential function}
167 \caption
168 Easing curve for an exponential (2^t) function:
169 accelerating from zero velocity.
170 \value OutExpo \image qeasingcurve-outexpo.png {Graph of a
171 OutExponential function}
172 \caption
173 Easing curve for an exponential (2^t) function:
174 decelerating to zero velocity.
175 \value InOutExpo \image qeasingcurve-inoutexpo.png {Graph of a
176 InOutExponential function}
177 \caption
178 Easing curve for an exponential (2^t) function:
179 acceleration until halfway, then deceleration.
180 \value OutInExpo \image qeasingcurve-outinexpo.png {Graph of a
181 OutInExponential function}
182 \caption
183 Easing curve for an exponential (2^t) function:
184 deceleration until halfway, then acceleration.
185 \value InCirc \image qeasingcurve-incirc.png {Graph of a
186 InCircular function}
187 \caption
188 Easing curve for a circular (sqrt(1-t^2)) function:
189 accelerating from zero velocity.
190 \value OutCirc \image qeasingcurve-outcirc.png {Graph of a
191 OutCircular function}
192 \caption
193 Easing curve for a circular (sqrt(1-t^2)) function:
194 decelerating to zero velocity.
195 \value InOutCirc \image qeasingcurve-inoutcirc.png {Graph of a
196 InOutCircular function}
197 \caption
198 Easing curve for a circular (sqrt(1-t^2)) function:
199 acceleration until halfway, then deceleration.
200 \value OutInCirc \image qeasingcurve-outincirc.png {Graph of a
201 OutInCircular function}
202 \caption
203 Easing curve for a circular (sqrt(1-t^2)) function:
204 deceleration until halfway, then acceleration.
205 \value InElastic \image qeasingcurve-inelastic.png {Graph of a
206 InElastic function}
207 \caption
208 Easing curve for an elastic
209 (exponentially decaying sine wave) function:
210 accelerating from zero velocity. The peak amplitude
211 can be set with the \e amplitude parameter, and the
212 period of decay by the \e period parameter.
213 \value OutElastic \image qeasingcurve-outelastic.png {Graph of a
214 OutElastic function}
215 \caption
216 Easing curve for an elastic
217 (exponentially decaying sine wave) function:
218 decelerating to zero velocity. The peak amplitude
219 can be set with the \e amplitude parameter, and the
220 period of decay by the \e period parameter.
221 \value InOutElastic \image qeasingcurve-inoutelastic.png {Graph of a
222 InOutElastic function}
223 \caption
224 Easing curve for an elastic
225 (exponentially decaying sine wave) function:
226 acceleration until halfway, then deceleration.
227 \value OutInElastic \image qeasingcurve-outinelastic.png {Graph of a
228 OutInElastic function}
229 \caption
230 Easing curve for an elastic
231 (exponentially decaying sine wave) function:
232 deceleration until halfway, then acceleration.
233 \value InBack \image qeasingcurve-inback.png {Graph of a
234 InBack function}
235 \caption
236 Easing curve for a back (overshooting
237 cubic function: (s+1)*t^3 - s*t^2) easing in:
238 accelerating from zero velocity.
239 \value OutBack \image qeasingcurve-outback.png {Graph of a
240 OutBack function}
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 Graph of a
246 InOutBack function}
247 \caption
248 Easing curve for a back (overshooting
249 cubic function: (s+1)*t^3 - s*t^2) easing in/out:
250 acceleration until halfway, then deceleration.
251 \value OutInBack \image qeasingcurve-outinback.png {Graph of a
252 OutInBack function}
253 \caption
254 Easing curve for a back (overshooting
255 cubic easing: (s+1)*t^3 - s*t^2) easing out/in:
256 deceleration until halfway, then acceleration.
257 \value InBounce \image qeasingcurve-inbounce.png {Graph of a
258 InBounce function}
259 \caption
260 Easing curve for a bounce (exponentially
261 decaying parabolic bounce) function: accelerating
262 from zero velocity.
263 \value OutBounce \image qeasingcurve-outbounce.png {Graph of a
264 OutBounce function}
265 \caption
266 Easing curve for a bounce (exponentially
267 decaying parabolic bounce) function: decelerating
268 from zero velocity.
269 \value InOutBounce \image qeasingcurve-inoutbounce.png {Graph of a
270 InOutBounce function}
271 \caption
272 Easing curve for a bounce (exponentially
273 decaying parabolic bounce) function easing in/out:
274 acceleration until halfway, then deceleration.
275 \value OutInBounce \image qeasingcurve-outinbounce.png {Graph of a
276 OutInBounce function}
277 \caption
278 Easing curve for a bounce (exponentially
279 decaying parabolic bounce) function easing out/in:
280 deceleration until halfway, then acceleration.
281 \omitvalue InCurve
282 \omitvalue OutCurve
283 \omitvalue SineCurve
284 \omitvalue CosineCurve
285 \value BezierSpline Allows defining a custom easing curve using a cubic bezier spline
286 \sa addCubicBezierSegment()
287 \value TCBSpline Allows defining a custom easing curve using a TCB spline
288 \sa addTCBSegment()
289 \value Custom This is returned if the user specified a custom curve type with
290 setCustomType(). Note that you cannot call setType() with this value,
291 but type() can return it.
292 \omitvalue NCurveTypes
293*/
294
295/*!
296 \typedef QEasingCurve::EasingFunction
297
298 This is a typedef for a pointer to a function with the following
299 signature:
300
301 \snippet code/src_corelib_tools_qeasingcurve.cpp typedef
302*/
303
304#include "qeasingcurve.h"
305#include <cmath>
306
307#ifndef QT_NO_DEBUG_STREAM
308#include <QtCore/qdebug.h>
309#include <QtCore/qstring.h>
310#endif
311
312#ifndef QT_NO_DATASTREAM
313#include <QtCore/qdatastream.h>
314#endif
315
316#include <QtCore/qpoint.h>
317#include <QtCore/qlist.h>
318
320
321static constexpr bool isConfigFunction(QEasingCurve::Type type)
322{
323 return (type >= QEasingCurve::InElastic
324 && type <= QEasingCurve::OutInBounce) ||
325 type == QEasingCurve::BezierSpline ||
326 type == QEasingCurve::TCBSpline;
327}
328
330{
335
336
337 friend bool operator==(const TCBPoint &lhs, const TCBPoint &rhs) noexcept
338 {
339 return qFuzzyCompare(lhs._point, rhs._point)
340 && QtPrivate::fuzzyCompare(lhs._t, rhs._t)
341 && QtPrivate::fuzzyCompare(lhs._c, rhs._c)
342 && QtPrivate::fuzzyCompare(lhs._b, rhs._b);
343 }
344};
346
347QDataStream &operator<<(QDataStream &stream, const TCBPoint &point)
348{
349 stream << point._point
350 << point._t
351 << point._c
352 << point._b;
353 return stream;
354}
355
356QDataStream &operator>>(QDataStream &stream, TCBPoint &point)
357{
358 stream >> point._point
359 >> point._t
360 >> point._c
361 >> point._b;
362 return stream;
363}
364
366
368{
369 QEasingCurveFunction &operator=(const QEasingCurveFunction &) = delete;
370 QEasingCurveFunction &operator=(QEasingCurveFunction &&) = delete;
371 QEasingCurveFunction(QEasingCurveFunction &&) = delete;
372protected:
373 QEasingCurveFunction(const QEasingCurveFunction &) = default; // used by clone()
374public:
380 virtual qreal value(qreal t);
381 virtual QEasingCurveFunction *clone() const { return new QEasingCurveFunction{*this}; }
382 // ### virtual? (cf. QTBUG-142709)
383 bool fuzzyCompare(const QEasingCurveFunction &other) const noexcept;
384
391
392};
393
394QDataStream &operator<<(QDataStream &stream, QEasingCurveFunction *func)
395{
396 if (func) {
397 stream << func->_p;
398 stream << func->_a;
399 stream << func->_o;
400 if (stream.version() > QDataStream::Qt_5_12) {
401 stream << func->_bezierCurves;
402 stream << func->_tcbPoints;
403 }
404 }
405 return stream;
406}
407
408QDataStream &operator>>(QDataStream &stream, QEasingCurveFunction *func)
409{
410 if (func) {
411 stream >> func->_p;
412 stream >> func->_a;
413 stream >> func->_o;
414 if (stream.version() > QDataStream::Qt_5_12) {
415 stream >> func->_bezierCurves;
416 stream >> func->_tcbPoints;
417 }
418 }
419 return stream;
420}
421
422static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve);
423
425{
426 QEasingCurve::EasingFunction func = curveToFunc(_t);
427 return func(t);
428}
429
431{
432 return _t == other._t
433 && QtPrivate::fuzzyCompare(_p, other._p)
434 && QtPrivate::fuzzyCompare(_a, other._a)
435 && QtPrivate::fuzzyCompare(_o, other._o)
436 && _bezierCurves == other._bezierCurves
437 && _tcbPoints == other._tcbPoints;
438}
439
440QT_BEGIN_INCLUDE_NAMESPACE
441#include "../../3rdparty/easing/easing.cpp"
442QT_END_INCLUDE_NAMESPACE
443
444class QEasingCurvePrivate
445{
446public:
447 QEasingCurvePrivate()
448 : type(QEasingCurve::Linear),
449 config(nullptr),
450 func(&easeNone)
451 { }
452 QEasingCurvePrivate(const QEasingCurvePrivate &other)
453 : type(other.type),
454 config(other.config ? other.config->clone() : nullptr),
455 func(other.func)
456 { }
457 ~QEasingCurvePrivate() { delete config; }
458 void setType_helper(QEasingCurve::Type);
459
460 QEasingCurve::Type type;
461 QEasingCurveFunction *config;
462 QEasingCurve::EasingFunction func;
463};
464
466{
473
477 bool _init;
478 bool _valid;
479
483
484 void init()
485 {
486 if (_bezierCurves.constLast() == QPointF(1.0, 1.0)) {
487 _init = true;
488 _curveCount = _bezierCurves.size() / 3;
489
490 for (int i=0; i < _curveCount; i++) {
491 _intervals[i] = _bezierCurves.at(i * 3 + 2).x();
492
493 if (i == 0) {
494 _curves[0].p0x = 0.0;
495 _curves[0].p0y = 0.0;
496
497 _curves[0].p1x = _bezierCurves.at(0).x();
498 _curves[0].p1y = _bezierCurves.at(0).y();
499
500 _curves[0].p2x = _bezierCurves.at(1).x();
501 _curves[0].p2y = _bezierCurves.at(1).y();
502
503 _curves[0].p3x = _bezierCurves.at(2).x();
504 _curves[0].p3y = _bezierCurves.at(2).y();
505
506 } else if (i == (_curveCount - 1)) {
507 _curves[i].p0x = _bezierCurves.at(_bezierCurves.size() - 4).x();
508 _curves[i].p0y = _bezierCurves.at(_bezierCurves.size() - 4).y();
509
510 _curves[i].p1x = _bezierCurves.at(_bezierCurves.size() - 3).x();
511 _curves[i].p1y = _bezierCurves.at(_bezierCurves.size() - 3).y();
512
513 _curves[i].p2x = _bezierCurves.at(_bezierCurves.size() - 2).x();
514 _curves[i].p2y = _bezierCurves.at(_bezierCurves.size() - 2).y();
515
516 _curves[i].p3x = _bezierCurves.at(_bezierCurves.size() - 1).x();
517 _curves[i].p3y = _bezierCurves.at(_bezierCurves.size() - 1).y();
518 } else {
519 _curves[i].p0x = _bezierCurves.at(i * 3 - 1).x();
520 _curves[i].p0y = _bezierCurves.at(i * 3 - 1).y();
521
522 _curves[i].p1x = _bezierCurves.at(i * 3).x();
523 _curves[i].p1y = _bezierCurves.at(i * 3).y();
524
525 _curves[i].p2x = _bezierCurves.at(i * 3 + 1).x();
526 _curves[i].p2y = _bezierCurves.at(i * 3 + 1).y();
527
528 _curves[i].p3x = _bezierCurves.at(i * 3 + 2).x();
529 _curves[i].p3y = _bezierCurves.at(i * 3 + 2).y();
530 }
531 }
532 _valid = true;
533 } else {
534 _valid = false;
535 }
536 }
537
538 BezierEase *clone() const override { return new BezierEase{*this}; }
539
540 void getBezierSegment(SingleCubicBezier * &singleCubicBezier, qreal x)
541 {
542
543 int currentSegment = 0;
544
545 while (currentSegment < _curveCount) {
546 if (x <= _intervals.data()[currentSegment])
547 break;
548 currentSegment++;
549 }
550
551 singleCubicBezier = &_curves.data()[currentSegment];
552 }
553
554
555 qreal static inline newtonIteration(const SingleCubicBezier &singleCubicBezier, qreal t, qreal x)
556 {
557 qreal currentXValue = evaluateForX(singleCubicBezier, t);
558
559 const qreal newT = t - (currentXValue - x) / evaluateDerivateForX(singleCubicBezier, t);
560
561 return newT;
562 }
563
564 qreal value(qreal x) override
565 {
566 Q_ASSERT(_bezierCurves.size() % 3 == 0);
567
568 if (_bezierCurves.isEmpty()) {
569 return x;
570 }
571
572 if (!_init)
573 init();
574
575 if (!_valid) {
576 qWarning("QEasingCurve: Invalid bezier curve");
577 return x;
578 }
579
580 // The bezier computation is not always precise on the endpoints, so handle explicitly
581 if (!(x > 0))
582 return 0;
583 if (!(x < 1))
584 return 1;
585
586 SingleCubicBezier *singleCubicBezier = nullptr;
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{
897
898 qreal value(qreal x) override
899 {
900 Q_ASSERT(_bezierCurves.size() % 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 TCBEase *clone() const override
911 {
912 return new TCBEase{*this};
913 }
914};
915
917{
919 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
920 { }
921
922 ElasticEase *clone() const override { return new ElasticEase{*this}; }
923
924 qreal value(qreal t) override
925 {
926 qreal p = (_p < 0) ? qreal(0.3) : _p;
927 qreal a = (_a < 0) ? qreal(1.0) : _a;
928 switch (_t) {
929 case QEasingCurve::InElastic:
930 return easeInElastic(t, a, p);
931 case QEasingCurve::OutElastic:
932 return easeOutElastic(t, a, p);
933 case QEasingCurve::InOutElastic:
934 return easeInOutElastic(t, a, p);
935 case QEasingCurve::OutInElastic:
936 return easeOutInElastic(t, a, p);
937 default:
938 return t;
939 }
940 }
941};
942
944{
946 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0))
947 { }
948
949 BounceEase *clone() const override { return new BounceEase{*this}; }
950
951 qreal value(qreal t) override
952 {
953 qreal a = (_a < 0) ? qreal(1.0) : _a;
954 switch (_t) {
955 case QEasingCurve::InBounce:
956 return easeInBounce(t, a);
957 case QEasingCurve::OutBounce:
958 return easeOutBounce(t, a);
959 case QEasingCurve::InOutBounce:
960 return easeInOutBounce(t, a);
961 case QEasingCurve::OutInBounce:
962 return easeOutInBounce(t, a);
963 default:
964 return t;
965 }
966 }
967};
968
970{
972 : QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158))
973 { }
974
975 BackEase *clone() const override { return new BackEase{*this}; }
976
977 qreal value(qreal t) override
978 {
979 // The *Back() functions are not always precise on the endpoints, so handle explicitly
980 if (!(t > 0))
981 return 0;
982 if (!(t < 1))
983 return 1;
984 qreal o = (_o < 0) ? qreal(1.70158) : _o;
985 switch (_t) {
986 case QEasingCurve::InBack:
987 return easeInBack(t, o);
988 case QEasingCurve::OutBack:
989 return easeOutBack(t, o);
990 case QEasingCurve::InOutBack:
991 return easeInOutBack(t, o);
992 case QEasingCurve::OutInBack:
993 return easeOutInBack(t, o);
994 default:
995 return t;
996 }
997 }
998};
999
1000static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve)
1001{
1002 switch (curve) {
1003 case QEasingCurve::Linear:
1004 return &easeNone;
1005 case QEasingCurve::InQuad:
1006 return &easeInQuad;
1007 case QEasingCurve::OutQuad:
1008 return &easeOutQuad;
1009 case QEasingCurve::InOutQuad:
1010 return &easeInOutQuad;
1011 case QEasingCurve::OutInQuad:
1012 return &easeOutInQuad;
1013 case QEasingCurve::InCubic:
1014 return &easeInCubic;
1015 case QEasingCurve::OutCubic:
1016 return &easeOutCubic;
1017 case QEasingCurve::InOutCubic:
1018 return &easeInOutCubic;
1019 case QEasingCurve::OutInCubic:
1020 return &easeOutInCubic;
1021 case QEasingCurve::InQuart:
1022 return &easeInQuart;
1023 case QEasingCurve::OutQuart:
1024 return &easeOutQuart;
1025 case QEasingCurve::InOutQuart:
1026 return &easeInOutQuart;
1027 case QEasingCurve::OutInQuart:
1028 return &easeOutInQuart;
1029 case QEasingCurve::InQuint:
1030 return &easeInQuint;
1031 case QEasingCurve::OutQuint:
1032 return &easeOutQuint;
1033 case QEasingCurve::InOutQuint:
1034 return &easeInOutQuint;
1035 case QEasingCurve::OutInQuint:
1036 return &easeOutInQuint;
1037 case QEasingCurve::InSine:
1038 return &easeInSine;
1039 case QEasingCurve::OutSine:
1040 return &easeOutSine;
1041 case QEasingCurve::InOutSine:
1042 return &easeInOutSine;
1043 case QEasingCurve::OutInSine:
1044 return &easeOutInSine;
1045 case QEasingCurve::InExpo:
1046 return &easeInExpo;
1047 case QEasingCurve::OutExpo:
1048 return &easeOutExpo;
1049 case QEasingCurve::InOutExpo:
1050 return &easeInOutExpo;
1051 case QEasingCurve::OutInExpo:
1052 return &easeOutInExpo;
1053 case QEasingCurve::InCirc:
1054 return &easeInCirc;
1055 case QEasingCurve::OutCirc:
1056 return &easeOutCirc;
1057 case QEasingCurve::InOutCirc:
1058 return &easeInOutCirc;
1059 case QEasingCurve::OutInCirc:
1060 return &easeOutInCirc;
1061 // Internal - needed for QTimeLine backward-compatibility:
1062 case QEasingCurve::InCurve:
1063 return &easeInCurve;
1064 case QEasingCurve::OutCurve:
1065 return &easeOutCurve;
1066 case QEasingCurve::SineCurve:
1067 return &easeSineCurve;
1068 case QEasingCurve::CosineCurve:
1069 return &easeCosineCurve;
1070 default:
1071 return nullptr;
1072 };
1073}
1074
1075static QEasingCurveFunction *curveToFunctionObject(QEasingCurve::Type type)
1076{
1077 switch (type) {
1078 case QEasingCurve::InElastic:
1079 case QEasingCurve::OutElastic:
1080 case QEasingCurve::InOutElastic:
1081 case QEasingCurve::OutInElastic:
1082 return new ElasticEase(type);
1083 case QEasingCurve::OutBounce:
1084 case QEasingCurve::InBounce:
1085 case QEasingCurve::OutInBounce:
1086 case QEasingCurve::InOutBounce:
1087 return new BounceEase(type);
1088 case QEasingCurve::InBack:
1089 case QEasingCurve::OutBack:
1090 case QEasingCurve::InOutBack:
1091 case QEasingCurve::OutInBack:
1092 return new BackEase(type);
1093 case QEasingCurve::BezierSpline:
1094 return new BezierEase;
1095 case QEasingCurve::TCBSpline:
1096 return new TCBEase;
1097 default:
1098 return new QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158));
1099 }
1100
1101 return nullptr;
1102}
1103
1104/*!
1105 \fn QEasingCurve::QEasingCurve(QEasingCurve &&other)
1106
1107 Move-constructs a QEasingCurve instance, making it point at the same
1108 object that \a other was pointing to.
1109
1110 \since 5.2
1111*/
1112
1113/*!
1114 Constructs an easing curve of the given \a type.
1115 */
1116QEasingCurve::QEasingCurve(Type type)
1117 : d_ptr(new QEasingCurvePrivate)
1118{
1119 setType(type);
1120}
1121
1122/*!
1123 Construct a copy of \a other.
1124 */
1125QEasingCurve::QEasingCurve(const QEasingCurve &other)
1126 : d_ptr(new QEasingCurvePrivate(*other.d_ptr))
1127{
1128 // ### non-atomic, requires malloc on shallow copy
1129}
1130
1131/*!
1132 Destructor.
1133 */
1134
1135QEasingCurve::~QEasingCurve()
1136{
1137 delete d_ptr;
1138}
1139
1140/*!
1141 \fn QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other)
1142 Copy \a other.
1143 */
1144
1145/*!
1146 \fn QEasingCurve &QEasingCurve::operator=(QEasingCurve &&other)
1147
1148 Move-assigns \a other to this QEasingCurve instance.
1149
1150 \since 5.2
1151*/
1152
1153/*!
1154 \fn void QEasingCurve::swap(QEasingCurve &other)
1155 \since 5.0
1156 \memberswap{curve}
1157*/
1158
1159/*!
1160 \fn bool QEasingCurve::operator==(const QEasingCurve &lhs, const QEasingCurve &rhs)
1161
1162 Compares easing curve \a lhs with \a rhs and returns \c true if they are
1163 equal; otherwise returns \c false.
1164 It will also compare the properties of the curves.
1165 */
1166bool comparesEqual(const QEasingCurve &lhs, const QEasingCurve &rhs)
1167{
1168 bool res = lhs.d_ptr->func == rhs.d_ptr->func
1169 && lhs.d_ptr->type == rhs.d_ptr->type;
1170 if (res) {
1171 if (lhs.d_ptr->config && rhs.d_ptr->config) {
1172 // catch the config content
1173 res = lhs.d_ptr->config->fuzzyCompare(*rhs.d_ptr->config);
1174 } else if (lhs.d_ptr->config || rhs.d_ptr->config) {
1175 // one one has a config object, which could contain default values
1176 res = QtPrivate::fuzzyCompare(lhs.amplitude(), rhs.amplitude())
1177 && QtPrivate::fuzzyCompare(lhs.period(), rhs.period())
1178 && QtPrivate::fuzzyCompare(lhs.overshoot(), rhs.overshoot());
1179 }
1180 }
1181 return res;
1182}
1183
1184/*!
1185 \fn bool QEasingCurve::operator!=(const QEasingCurve &lhs, const QEasingCurve &rhs)
1186
1187 Compares easing curve \a lhs with \a rhs and returns \c true if they are
1188 not equal; otherwise returns \c false.
1189 It will also compare the properties of the curves.
1190
1191 \sa operator==()
1192*/
1193
1194/*!
1195 Returns the amplitude. This is not applicable for all curve types.
1196 It is only applicable for bounce and elastic curves (curves of type()
1197 QEasingCurve::InBounce, QEasingCurve::OutBounce, QEasingCurve::InOutBounce,
1198 QEasingCurve::OutInBounce, QEasingCurve::InElastic, QEasingCurve::OutElastic,
1199 QEasingCurve::InOutElastic or QEasingCurve::OutInElastic).
1200 */
1201qreal QEasingCurve::amplitude() const
1202{
1203 return d_ptr->config ? d_ptr->config->_a : qreal(1.0);
1204}
1205
1206/*!
1207 Sets the amplitude to \a amplitude.
1208
1209 This will set the amplitude of the bounce or the amplitude of the
1210 elastic "spring" effect. The higher the number, the higher the amplitude.
1211 \sa amplitude()
1212*/
1213void QEasingCurve::setAmplitude(qreal amplitude)
1214{
1215 if (!d_ptr->config)
1216 d_ptr->config = curveToFunctionObject(d_ptr->type);
1217 d_ptr->config->_a = amplitude;
1218}
1219
1220/*!
1221 Returns the period. This is not applicable for all curve types.
1222 It is only applicable if type() is QEasingCurve::InElastic, QEasingCurve::OutElastic,
1223 QEasingCurve::InOutElastic or QEasingCurve::OutInElastic.
1224 */
1225qreal QEasingCurve::period() const
1226{
1227 return d_ptr->config ? d_ptr->config->_p : qreal(0.3);
1228}
1229
1230/*!
1231 Sets the period to \a period.
1232 Setting a small period value will give a high frequency of the curve. A
1233 large period will give it a small frequency.
1234
1235 \sa period()
1236*/
1237void QEasingCurve::setPeriod(qreal period)
1238{
1239 if (!d_ptr->config)
1240 d_ptr->config = curveToFunctionObject(d_ptr->type);
1241 d_ptr->config->_p = period;
1242}
1243
1244/*!
1245 Returns the overshoot. This is not applicable for all curve types.
1246 It is only applicable if type() is QEasingCurve::InBack, QEasingCurve::OutBack,
1247 QEasingCurve::InOutBack or QEasingCurve::OutInBack.
1248 */
1249qreal QEasingCurve::overshoot() const
1250{
1251 return d_ptr->config ? d_ptr->config->_o : qreal(1.70158);
1252}
1253
1254/*!
1255 Sets the overshoot to \a overshoot.
1256
1257 0 produces no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent.
1258
1259 \sa overshoot()
1260*/
1261void QEasingCurve::setOvershoot(qreal overshoot)
1262{
1263 if (!d_ptr->config)
1264 d_ptr->config = curveToFunctionObject(d_ptr->type);
1265 d_ptr->config->_o = overshoot;
1266}
1267
1268/*!
1269 Adds a segment of a cubic bezier spline to define a custom easing curve.
1270 It is only applicable if type() is QEasingCurve::BezierSpline.
1271 Note that the spline implicitly starts at (0.0, 0.0) and has to end at (1.0, 1.0) to
1272 be a valid easing curve.
1273 \a c1 and \a c2 are the control points used for drawing the curve.
1274 \a endPoint is the endpoint of the curve.
1275 */
1276void QEasingCurve::addCubicBezierSegment(const QPointF & c1, const QPointF & c2, const QPointF & endPoint)
1277{
1278 if (!d_ptr->config)
1279 d_ptr->config = curveToFunctionObject(d_ptr->type);
1280 d_ptr->config->_bezierCurves << c1 << c2 << endPoint;
1281}
1282
1283QList<QPointF> static inline tcbToBezier(const TCBPoints &tcbPoints)
1284{
1285 const int count = tcbPoints.size();
1286 QList<QPointF> bezierPoints;
1287 bezierPoints.reserve(3 * (count - 1));
1288
1289 for (int i = 1; i < count; i++) {
1290 const qreal t_0 = tcbPoints.at(i - 1)._t;
1291 const qreal c_0 = tcbPoints.at(i - 1)._c;
1292 qreal b_0 = -1;
1293
1294 qreal const t_1 = tcbPoints.at(i)._t;
1295 qreal const c_1 = tcbPoints.at(i)._c;
1296 qreal b_1 = 1;
1297
1298 QPointF c_minusOne; //P1 last segment - not available for the first point
1299 const QPointF c0(tcbPoints.at(i - 1)._point); //P0 Hermite/TBC
1300 const QPointF c3(tcbPoints.at(i)._point); //P1 Hermite/TBC
1301 QPointF c4; //P0 next segment - not available for the last point
1302
1303 if (i > 1) { //first point no left tangent
1304 c_minusOne = tcbPoints.at(i - 2)._point;
1305 b_0 = tcbPoints.at(i - 1)._b;
1306 }
1307
1308 if (i < (count - 1)) { //last point no right tangent
1309 c4 = tcbPoints.at(i + 1)._point;
1310 b_1 = tcbPoints.at(i)._b;
1311 }
1312
1313 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()));
1314 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()));
1315
1316 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()));
1317 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()));
1318
1319 const QPointF d_0 = QPointF(dx_0, dy_0);
1320 const QPointF d_1 = QPointF(dx_1, dy_1);
1321
1322 QPointF c1 = (3 * c0 + d_0) / 3;
1323 QPointF c2 = (3 * c3 - d_1) / 3;
1324 bezierPoints << c1 << c2 << c3;
1325 }
1326 return bezierPoints;
1327}
1328
1329/*!
1330 Adds a segment of a TCB bezier spline to define a custom easing curve.
1331 It is only applicable if type() is QEasingCurve::TCBSpline.
1332 The spline has to start explicitly at (0.0, 0.0) and has to end at (1.0, 1.0) to
1333 be a valid easing curve.
1334 The tension \a t changes the length of the tangent vector.
1335 The continuity \a c changes the sharpness in change between the tangents.
1336 The bias \a b changes the direction of the tangent vector.
1337 \a nextPoint is the sample position.
1338 All three parameters are valid between -1 and 1 and define the
1339 tangent of the control point.
1340 If all three parameters are 0 the resulting spline is a Catmull-Rom spline.
1341 The begin and endpoint always have a bias of -1 and 1, since the outer tangent is not defined.
1342 */
1343void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qreal b)
1344{
1345 if (!d_ptr->config)
1346 d_ptr->config = curveToFunctionObject(d_ptr->type);
1347
1348 d_ptr->config->_tcbPoints.append(TCBPoint{nextPoint, t, c, b});
1349
1350 if (nextPoint == QPointF(1.0, 1.0)) {
1351 d_ptr->config->_bezierCurves = tcbToBezier(d_ptr->config->_tcbPoints);
1352 d_ptr->config->_tcbPoints.clear();
1353 }
1354
1355}
1356
1357/*!
1358 \since 5.0
1359
1360 Returns the cubicBezierSpline that defines a custom easing curve.
1361 If the easing curve does not have a custom bezier easing curve the list
1362 is empty.
1363*/
1364QList<QPointF> QEasingCurve::toCubicSpline() const
1365{
1366 return d_ptr->config ? d_ptr->config->_bezierCurves : QList<QPointF>();
1367}
1368
1369/*!
1370 Returns the type of the easing curve.
1371*/
1372QEasingCurve::Type QEasingCurve::type() const
1373{
1374 return d_ptr->type;
1375}
1376
1377void QEasingCurvePrivate::setType_helper(QEasingCurve::Type newType)
1378{
1379 qreal amp = -1.0;
1380 qreal period = -1.0;
1381 qreal overshoot = -1.0;
1382 QList<QPointF> bezierCurves;
1383 QList<TCBPoint> tcbPoints;
1384
1385 if (config) {
1386 amp = config->_a;
1387 period = config->_p;
1388 overshoot = config->_o;
1389 bezierCurves = std::move(config->_bezierCurves);
1390 tcbPoints = std::move(config->_tcbPoints);
1391
1392 delete config;
1393 config = nullptr;
1394 }
1395
1396 if (isConfigFunction(newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0) ||
1397 !bezierCurves.isEmpty()) {
1398 config = curveToFunctionObject(newType);
1399 if (amp != -1.0)
1400 config->_a = amp;
1401 if (period != -1.0)
1402 config->_p = period;
1403 if (overshoot != -1.0)
1404 config->_o = overshoot;
1405 config->_bezierCurves = std::move(bezierCurves);
1406 config->_tcbPoints = std::move(tcbPoints);
1407 func = nullptr;
1408 } else if (newType != QEasingCurve::Custom) {
1409 func = curveToFunc(newType);
1410 }
1411 Q_ASSERT((func == nullptr) == (config != nullptr));
1412 type = newType;
1413}
1414
1415/*!
1416 Sets the type of the easing curve to \a type.
1417*/
1418void QEasingCurve::setType(Type type)
1419{
1420 if (d_ptr->type == type)
1421 return;
1422 if (type < Linear || type >= NCurveTypes - 1) {
1423 qWarning("QEasingCurve: Invalid curve type %d", type);
1424 return;
1425 }
1426
1427 d_ptr->setType_helper(type);
1428}
1429
1430/*!
1431 Sets a custom easing curve that is defined by the user in the function \a func.
1432 The signature of the function is qreal myEasingFunction(qreal progress),
1433 where \e progress and the return value are considered to be normalized between 0 and 1.
1434 (In some cases the return value can be outside that range)
1435 After calling this function type() will return QEasingCurve::Custom.
1436 \a func cannot be \nullptr.
1437
1438 \sa customType()
1439 \sa valueForProgress()
1440*/
1441void QEasingCurve::setCustomType(EasingFunction func)
1442{
1443 if (!func) {
1444 qWarning("Function pointer must not be null");
1445 return;
1446 }
1447 d_ptr->func = func;
1448 d_ptr->setType_helper(Custom);
1449}
1450
1451/*!
1452 Returns the function pointer to the custom easing curve.
1453 If type() does not return QEasingCurve::Custom, this function
1454 will return 0.
1455*/
1456QEasingCurve::EasingFunction QEasingCurve::customType() const
1457{
1458 return d_ptr->type == Custom ? d_ptr->func : nullptr;
1459}
1460
1461/*!
1462 Return the effective progress for the easing curve at \a progress.
1463 Whereas \a progress must be between 0 and 1, the returned effective progress
1464 can be outside those bounds. For example, QEasingCurve::InBack will
1465 return negative values in the beginning of the function.
1466 */
1467qreal QEasingCurve::valueForProgress(qreal progress) const
1468{
1469 progress = qBound<qreal>(0, progress, 1);
1470 if (d_ptr->func)
1471 return d_ptr->func(progress);
1472 else if (d_ptr->config)
1473 return d_ptr->config->value(progress);
1474 else
1475 return progress;
1476}
1477
1478#ifndef QT_NO_DEBUG_STREAM
1479QDebug operator<<(QDebug debug, const QEasingCurve &item)
1480{
1481 QDebugStateSaver saver(debug);
1482 debug << "type:" << item.d_ptr->type
1483 << "func:" << reinterpret_cast<const void *>(item.d_ptr->func);
1484 if (item.d_ptr->config) {
1485 debug << QString::fromLatin1("period:%1").arg(item.d_ptr->config->_p, 0, 'f', 20)
1486 << QString::fromLatin1("amp:%1").arg(item.d_ptr->config->_a, 0, 'f', 20)
1487 << QString::fromLatin1("overshoot:%1").arg(item.d_ptr->config->_o, 0, 'f', 20);
1488 }
1489 return debug;
1490}
1491#endif // QT_NO_DEBUG_STREAM
1492
1493#ifndef QT_NO_DATASTREAM
1494/*!
1495 \fn QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
1496 \relates QEasingCurve
1497
1498 Writes the given \a easing curve to the given \a stream and returns a
1499 reference to the stream.
1500
1501 \warning Writing easing curves of QEasingCurve::Custom type
1502 (that is, curves with a custom easing function) is not supported.
1503
1504 \sa {Serializing Qt Data Types}
1505*/
1506
1507QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
1508{
1509 if (easing.d_ptr->type == QEasingCurve::Custom) {
1510 qWarning("QEasingCurve: Cannot serialize an easing curve with a custom easing function");
1511
1512 // Backwards compatibility: stream _something_ out.
1513 // Deliberately choose a curve that uses a config and not a
1514 // easing function. If this curve is deserialized from old
1515 // code, it will ignore the function pointer (cf.
1516 // QTBUG-132575).
1517 static_assert(isConfigFunction(QEasingCurve::InElastic));
1518 stream << QEasingCurve(QEasingCurve::InElastic);
1519 return stream;
1520 }
1521
1522 stream << quint8(easing.d_ptr->type);
1523 // Unused; for backwards compatibility
1524 stream << quint64(0);
1525
1526 bool hasConfig = easing.d_ptr->config;
1527 stream << hasConfig;
1528 if (hasConfig) {
1529 stream << easing.d_ptr->config;
1530 }
1531 return stream;
1532}
1533
1534/*!
1535 \fn QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
1536 \relates QEasingCurve
1537
1538 Reads an easing curve from the given \a stream into the given \a
1539 easing curve and returns a reference to the stream.
1540
1541 \sa {Serializing Qt Data Types}
1542*/
1543
1544QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
1545{
1546 QEasingCurve::Type type;
1547 quint8 int_type;
1548 stream >> int_type;
1549 type = static_cast<QEasingCurve::Type>(int_type);
1550 if (type == QEasingCurve::Custom) {
1551 qWarning("QEasingCurve: Cannot deserialize an easing curve with a custom easing function");
1552 stream.setStatus(QDataStream::ReadCorruptData);
1553 type = QEasingCurve::Linear;
1554 }
1555 easing.setType(type);
1556
1557 // Unused; for backwards compatibility
1558 [[maybe_unused]] quint64 ptr_func;
1559 stream >> ptr_func;
1560
1561 bool hasConfig;
1562 stream >> hasConfig;
1563 delete easing.d_ptr->config;
1564 easing.d_ptr->config = nullptr;
1565 if (hasConfig) {
1567 stream >> config;
1568 easing.d_ptr->config = config;
1569 }
1570 return stream;
1571}
1572#endif // QT_NO_DATASTREAM
1573
1574QT_END_NAMESPACE
1575
1576#include "moc_qeasingcurve.cpp"
virtual ~QEasingCurveFunction()
QEasingCurveFunction(QEasingCurve::Type type, qreal period=0.3, qreal amplitude=1.0, qreal overshoot=1.70158)
virtual qreal value(qreal t)
QList< QPointF > _bezierCurves
bool fuzzyCompare(const QEasingCurveFunction &other) const noexcept
QEasingCurveFunction(const QEasingCurveFunction &)=default
virtual QEasingCurveFunction * clone() const
\inmodule QtCore\reentrant
Definition qpoint.h:232
static QList< QPointF > tcbToBezier(const TCBPoints &tcbPoints)
static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve)
Q_DECLARE_TYPEINFO(TCBPoint, Q_PRIMITIVE_TYPE)
static QT_BEGIN_NAMESPACE constexpr bool isConfigFunction(QEasingCurve::Type type)
QDataStream & operator<<(QDataStream &stream, QEasingCurveFunction *func)
QList< TCBPoint > TCBPoints
QDataStream & operator>>(QDataStream &stream, QEasingCurveFunction *func)
QDataStream & operator>>(QDataStream &stream, TCBPoint &point)
static QEasingCurveFunction * curveToFunctionObject(QEasingCurve::Type type)
QDataStream & operator<<(QDataStream &stream, const TCBPoint &point)
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
bool comparesEqual(const QFileInfo &lhs, const QFileInfo &rhs)
#define M_PI
Definition qmath.h:200
QDataStream & operator<<(QDataStream &stream, const QImage &image)
[0]
Definition qimage.cpp:4009
QDataStream & operator>>(QDataStream &stream, QImage &image)
Definition qimage.cpp:4035
qreal value(qreal t) override
BackEase * clone() const override
BackEase(QEasingCurve::Type type)
static float _fast_cbrt(float x)
static qreal evaluateDerivateForX(const SingleCubicBezier &singleCubicBezier, qreal t)
static qreal evaluateForX(const SingleCubicBezier &singleCubicBezier, qreal t)
static qreal _acos(qreal x)
static void cosacos(qreal x, qreal &s1, qreal &s2, qreal &s3)
static qreal _cos(qreal x)
static qreal _cbrt(qreal d)
static qreal newtonIteration(const SingleCubicBezier &singleCubicBezier, qreal t, qreal x)
static qreal evaluateSegmentForY(const SingleCubicBezier &singleCubicBezier, qreal t)
QList< SingleCubicBezier > _curves
static qreal singleRealSolutionForCubic(qreal a, qreal b, qreal c)
qreal value(qreal x) override
void getBezierSegment(SingleCubicBezier *&singleCubicBezier, qreal x)
static qreal findTForX(const SingleCubicBezier &singleCubicBezier, qreal x)
BezierEase(QEasingCurve::Type type=QEasingCurve::BezierSpline)
BezierEase * clone() const override
QList< qreal > _intervals
static double _fast_cbrt(double d)
static bool inRange(qreal f)
static bool almostZero(qreal value)
BounceEase(QEasingCurve::Type type)
BounceEase * clone() const override
qreal value(qreal t) override
ElasticEase * clone() const override
qreal value(qreal t) override
ElasticEase(QEasingCurve::Type type)
TCBEase * clone() const override
qreal value(qreal x) override
friend bool operator==(const TCBPoint &lhs, const TCBPoint &rhs) noexcept
QPointF _point