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

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