Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
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
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
278
286
288{
293
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(_t, other._t) &&
301 qFuzzyCompare(_c, other._c) &&
303 }
304};
306
308{
309 stream << point._point
310 << point._t
311 << point._c
312 << point._b;
313 return stream;
314}
315
317{
318 stream >> point._point
319 >> point._t
320 >> point._c
321 >> point._b;
322 return stream;
323}
324
325typedef QList<TCBPoint> TCBPoints;
326
328{
329public:
331 qreal overshoot = 1.70158)
332 : _t(type), _p(period), _a(amplitude), _o(overshoot)
333 { }
335 virtual qreal value(qreal t);
336 virtual QEasingCurveFunction *copy() const;
337 bool operator==(const QEasingCurveFunction &other) const;
338
343 QList<QPointF> _bezierCurves;
345
346};
347
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
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
377
383
385{
387 rv->_bezierCurves = _bezierCurves;
388 rv->_tcbPoints = _tcbPoints;
389 return rv;
390}
391
393{
394 return _t == other._t &&
395 qFuzzyCompare(_p, other._p) &&
396 qFuzzyCompare(_a, other._a) &&
397 qFuzzyCompare(_o, other._o) &&
398 _bezierCurves == other._bezierCurves &&
399 _tcbPoints == other._tcbPoints;
400}
401
403#include "../../3rdparty/easing/easing.cpp"
405
426
428{
435
436 QList<SingleCubicBezier> _curves;
437 QList<qreal> _intervals;
439 bool _init;
440 bool _valid;
441
445
446 void init()
447 {
448 if (_bezierCurves.constLast() == QPointF(1.0, 1.0)) {
449 _init = true;
451
452 for (int i=0; i < _curveCount; i++) {
453 _intervals[i] = _bezierCurves.at(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(0).x();
460 _curves[0].p1y = _bezierCurves.at(0).y();
461
462 _curves[0].p2x = _bezierCurves.at(1).x();
463 _curves[0].p2y = _bezierCurves.at(1).y();
464
465 _curves[0].p3x = _bezierCurves.at(2).x();
466 _curves[0].p3y = _bezierCurves.at(2).y();
467
468 } else if (i == (_curveCount - 1)) {
469 _curves[i].p0x = _bezierCurves.at(_bezierCurves.size() - 4).x();
470 _curves[i].p0y = _bezierCurves.at(_bezierCurves.size() - 4).y();
471
472 _curves[i].p1x = _bezierCurves.at(_bezierCurves.size() - 3).x();
473 _curves[i].p1y = _bezierCurves.at(_bezierCurves.size() - 3).y();
474
475 _curves[i].p2x = _bezierCurves.at(_bezierCurves.size() - 2).x();
476 _curves[i].p2y = _bezierCurves.at(_bezierCurves.size() - 2).y();
477
478 _curves[i].p3x = _bezierCurves.at(_bezierCurves.size() - 1).x();
479 _curves[i].p3y = _bezierCurves.at(_bezierCurves.size() - 1).y();
480 } else {
481 _curves[i].p0x = _bezierCurves.at(i * 3 - 1).x();
482 _curves[i].p0y = _bezierCurves.at(i * 3 - 1).y();
483
484 _curves[i].p1x = _bezierCurves.at(i * 3).x();
485 _curves[i].p1y = _bezierCurves.at(i * 3).y();
486
487 _curves[i].p2x = _bezierCurves.at(i * 3 + 1).x();
488 _curves[i].p2y = _bezierCurves.at(i * 3 + 1).y();
489
490 _curves[i].p3x = _bezierCurves.at(i * 3 + 2).x();
491 _curves[i].p3y = _bezierCurves.at(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("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, findTForX(*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(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(1.0 + x);
723 const qreal one_minus_x_sqrt = qSqrt(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
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(D);
762 qreal u = _cbrt(-q * 0.5 + D_sqrt);
763 qreal v = _cbrt(-q * 0.5 - D_sqrt);
764 qreal z1 = u + v;
765
766 qreal t1 = z1 - a_by3;
767
768 if (inRange(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(-p);
777
778 //const qreal f = sqrt(4.0 / 3.0 * -p);
779 const qreal f = qSqrt(4.0 / 3.0) * p_minus_sqrt;
780
781 //const qreal sqrtP = sqrt(27.0 / -p_cubic);
782 const qreal sqrtP = -3.0*qSqrt(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(g, s1, s2, s3);
792
793 qreal z1 = -1 * f * s2;
794 qreal t1 = z1 - a_by3;
795 if (inRange(t1))
796 return t1;
797
798 qreal z2 = f * s1;
799 qreal t2 = z2 - a_by3;
800 if (inRange(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(factorT3)) {
829 if (almostZero(factorT2)) {
830 if (almostZero(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(discriminant)) / (2.0 * factorT2);
843 if (solution1 >= 0.0 && solution1 <= 1.0)
844 return solution1;
845
846 const qreal solution2 = (-factorT1 - std::sqrt(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
858
859 //one new iteration to increase numeric stability
860 //return newtonIteration(singleCubicBezier, t, x);
861 }
862};
863
864struct TCBEase : public BezierEase
865{
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("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
889{
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) {
910 return easeInElastic(t, a, p);
912 return easeOutElastic(t, a, p);
914 return easeInOutElastic(t, a, p);
916 return easeOutInElastic(t, a, p);
917 default:
918 return t;
919 }
920 }
921};
922
924{
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) {
943 return easeInBounce(t, a);
945 return easeOutBounce(t, a);
947 return easeInOutBounce(t, a);
949 return easeOutInBounce(t, a);
950 default:
951 return t;
952 }
953 }
954};
955
957{
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) {
981 return easeInBack(t, o);
983 return easeOutBack(t, o);
985 return easeInOutBack(t, o);
987 return easeOutInBack(t, o);
988 default:
989 return t;
990 }
991 }
992};
993
995{
996 switch (curve) {
998 return &easeNone;
1000 return &easeInQuad;
1002 return &easeOutQuad;
1004 return &easeInOutQuad;
1006 return &easeOutInQuad;
1008 return &easeInCubic;
1010 return &easeOutCubic;
1012 return &easeInOutCubic;
1014 return &easeOutInCubic;
1016 return &easeInQuart;
1018 return &easeOutQuart;
1020 return &easeInOutQuart;
1022 return &easeOutInQuart;
1024 return &easeInQuint;
1026 return &easeOutQuint;
1028 return &easeInOutQuint;
1030 return &easeOutInQuint;
1032 return &easeInSine;
1034 return &easeOutSine;
1036 return &easeInOutSine;
1038 return &easeOutInSine;
1040 return &easeInExpo;
1042 return &easeOutExpo;
1044 return &easeInOutExpo;
1046 return &easeOutInExpo;
1048 return &easeInCirc;
1050 return &easeOutCirc;
1052 return &easeInOutCirc;
1054 return &easeOutInCirc;
1055 // Internal - needed for QTimeLine backward-compatibility:
1057 return &easeInCurve;
1059 return &easeOutCurve;
1061 return &easeSineCurve;
1063 return &easeCosineCurve;
1064 default:
1065 return nullptr;
1066 };
1067}
1068
1070{
1071 switch (type) {
1076 return new ElasticEase(type);
1081 return new BounceEase(type);
1086 return new BackEase(type);
1088 return new BezierEase;
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
1115
1120 : d_ptr(new QEasingCurvePrivate(*other.d_ptr))
1121{
1122 // ### non-atomic, requires malloc on shallow copy
1123}
1124
1130{
1131 delete d_ptr;
1132}
1133
1162bool comparesEqual(const QEasingCurve &lhs, const QEasingCurve &rhs) noexcept
1163{
1164 bool res = lhs.d_ptr->func == rhs.d_ptr->func
1165 && lhs.d_ptr->type == rhs.d_ptr->type;
1166 if (res) {
1167 if (lhs.d_ptr->config && rhs.d_ptr->config) {
1168 // catch the config content
1169 res = lhs.d_ptr->config->operator==(*(rhs.d_ptr->config));
1170
1171 } else if (lhs.d_ptr->config || rhs.d_ptr->config) {
1172 // one one has a config object, which could contain default values
1173 res = qFuzzyCompare(lhs.amplitude(), rhs.amplitude())
1174 && qFuzzyCompare(lhs.period(), rhs.period())
1175 && qFuzzyCompare(lhs.overshoot(), rhs.overshoot());
1176 }
1177 }
1178 return res;
1179}
1180
1199{
1200 return d_ptr->config ? d_ptr->config->_a : qreal(1.0);
1201}
1202
1211{
1212 if (!d_ptr->config)
1213 d_ptr->config = curveToFunctionObject(d_ptr->type);
1214 d_ptr->config->_a = amplitude;
1215}
1216
1223{
1224 return d_ptr->config ? d_ptr->config->_p : qreal(0.3);
1225}
1226
1235{
1236 if (!d_ptr->config)
1237 d_ptr->config = curveToFunctionObject(d_ptr->type);
1238 d_ptr->config->_p = period;
1239}
1240
1247{
1248 return d_ptr->config ? d_ptr->config->_o : qreal(1.70158);
1249}
1250
1259{
1260 if (!d_ptr->config)
1261 d_ptr->config = curveToFunctionObject(d_ptr->type);
1262 d_ptr->config->_o = overshoot;
1263}
1264
1273void QEasingCurve::addCubicBezierSegment(const QPointF & c1, const QPointF & c2, const QPointF & endPoint)
1274{
1275 if (!d_ptr->config)
1276 d_ptr->config = curveToFunctionObject(d_ptr->type);
1277 d_ptr->config->_bezierCurves << c1 << c2 << endPoint;
1278}
1279
1280QList<QPointF> static inline tcbToBezier(const TCBPoints &tcbPoints)
1281{
1282 const int count = tcbPoints.size();
1283 QList<QPointF> bezierPoints;
1284 bezierPoints.reserve(3 * (count - 1));
1285
1286 for (int i = 1; i < count; i++) {
1287 const qreal t_0 = tcbPoints.at(i - 1)._t;
1288 const qreal c_0 = tcbPoints.at(i - 1)._c;
1289 qreal b_0 = -1;
1290
1291 qreal const t_1 = tcbPoints.at(i)._t;
1292 qreal const c_1 = tcbPoints.at(i)._c;
1293 qreal b_1 = 1;
1294
1295 QPointF c_minusOne; //P1 last segment - not available for the first point
1296 const QPointF c0(tcbPoints.at(i - 1)._point); //P0 Hermite/TBC
1297 const QPointF c3(tcbPoints.at(i)._point); //P1 Hermite/TBC
1298 QPointF c4; //P0 next segment - not available for the last point
1299
1300 if (i > 1) { //first point no left tangent
1301 c_minusOne = tcbPoints.at(i - 2)._point;
1302 b_0 = tcbPoints.at(i - 1)._b;
1303 }
1304
1305 if (i < (count - 1)) { //last point no right tangent
1306 c4 = tcbPoints.at(i + 1)._point;
1307 b_1 = tcbPoints.at(i)._b;
1308 }
1309
1310 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()));
1311 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()));
1312
1313 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()));
1314 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()));
1315
1316 const QPointF d_0 = QPointF(dx_0, dy_0);
1317 const QPointF d_1 = QPointF(dx_1, dy_1);
1318
1319 QPointF c1 = (3 * c0 + d_0) / 3;
1320 QPointF c2 = (3 * c3 - d_1) / 3;
1321 bezierPoints << c1 << c2 << c3;
1322 }
1323 return bezierPoints;
1324}
1325
1341{
1342 if (!d_ptr->config)
1343 d_ptr->config = curveToFunctionObject(d_ptr->type);
1344
1345 d_ptr->config->_tcbPoints.append(TCBPoint(nextPoint, t, c, b));
1346
1347 if (nextPoint == QPointF(1.0, 1.0)) {
1349 d_ptr->config->_tcbPoints.clear();
1350 }
1351
1352}
1353
1361QList<QPointF> QEasingCurve::toCubicSpline() const
1362{
1363 return d_ptr->config ? d_ptr->config->_bezierCurves : QList<QPointF>();
1364}
1365
1370{
1371 return d_ptr->type;
1372}
1373
1375{
1376 qreal amp = -1.0;
1377 qreal period = -1.0;
1378 qreal overshoot = -1.0;
1379 QList<QPointF> bezierCurves;
1380 QList<TCBPoint> tcbPoints;
1381
1382 if (config) {
1383 amp = config->_a;
1384 period = config->_p;
1385 overshoot = config->_o;
1386 bezierCurves = std::move(config->_bezierCurves);
1387 tcbPoints = std::move(config->_tcbPoints);
1388
1389 delete config;
1390 config = nullptr;
1391 }
1392
1393 if (isConfigFunction(newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0) ||
1394 !bezierCurves.isEmpty()) {
1395 config = curveToFunctionObject(newType);
1396 if (amp != -1.0)
1397 config->_a = amp;
1398 if (period != -1.0)
1399 config->_p = period;
1400 if (overshoot != -1.0)
1401 config->_o = overshoot;
1402 config->_bezierCurves = std::move(bezierCurves);
1403 config->_tcbPoints = std::move(tcbPoints);
1404 func = nullptr;
1405 } else if (newType != QEasingCurve::Custom) {
1406 func = curveToFunc(newType);
1407 }
1408 Q_ASSERT((func == nullptr) == (config != nullptr));
1409 type = newType;
1410}
1411
1416{
1417 if (d_ptr->type == type)
1418 return;
1419 if (type < Linear || type >= NCurveTypes - 1) {
1420 qWarning("QEasingCurve: Invalid curve type %d", type);
1421 return;
1422 }
1423
1424 d_ptr->setType_helper(type);
1425}
1426
1439{
1440 if (!func) {
1441 qWarning("Function pointer must not be null");
1442 return;
1443 }
1444 d_ptr->func = func;
1445 d_ptr->setType_helper(Custom);
1446}
1447
1454{
1455 return d_ptr->type == Custom ? d_ptr->func : nullptr;
1456}
1457
1465{
1466 progress = qBound<qreal>(0, progress, 1);
1467 if (d_ptr->func)
1468 return d_ptr->func(progress);
1469 else if (d_ptr->config)
1470 return d_ptr->config->value(progress);
1471 else
1472 return progress;
1473}
1474
1475#ifndef QT_NO_DEBUG_STREAM
1477{
1478 QDebugStateSaver saver(debug);
1479 debug << "type:" << item.d_ptr->type
1480 << "func:" << reinterpret_cast<const void *>(item.d_ptr->func);
1481 if (item.d_ptr->config) {
1482 debug << QString::fromLatin1("period:%1").arg(item.d_ptr->config->_p, 0, 'f', 20)
1483 << QString::fromLatin1("amp:%1").arg(item.d_ptr->config->_a, 0, 'f', 20)
1484 << QString::fromLatin1("overshoot:%1").arg(item.d_ptr->config->_o, 0, 'f', 20);
1485 }
1486 return debug;
1487}
1488#endif // QT_NO_DEBUG_STREAM
1489
1490#ifndef QT_NO_DATASTREAM
1502{
1503 stream << quint8(easing.d_ptr->type);
1504 stream << quint64(quintptr(easing.d_ptr->func));
1505
1506 bool hasConfig = easing.d_ptr->config;
1507 stream << hasConfig;
1508 if (hasConfig) {
1509 stream << easing.d_ptr->config;
1510 }
1511 return stream;
1512}
1513
1525{
1527 quint8 int_type;
1528 stream >> int_type;
1529 type = static_cast<QEasingCurve::Type>(int_type);
1531
1532 quint64 ptr_func;
1533 stream >> ptr_func;
1535
1536 bool hasConfig;
1537 stream >> hasConfig;
1538 delete easing.d_ptr->config;
1539 easing.d_ptr->config = nullptr;
1540 if (hasConfig) {
1542 stream >> config;
1543 easing.d_ptr->config = config;
1544 }
1545 return stream;
1546}
1547#endif // QT_NO_DATASTREAM
1548
1550
1551#include "moc_qeasingcurve.cpp"
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\inmodule QtCore
\inmodule QtCore
QList< QPointF > _bezierCurves
virtual ~QEasingCurveFunction()
bool operator==(const QEasingCurveFunction &other) const
QEasingCurveFunction(QEasingCurve::Type type, qreal period=0.3, qreal amplitude=1.0, qreal overshoot=1.70158)
virtual qreal value(qreal t)
virtual QEasingCurveFunction * copy() const
QEasingCurve::Type _t
QEasingCurveFunction * config
QEasingCurve::EasingFunction func
QEasingCurvePrivate(const QEasingCurvePrivate &other)
QEasingCurve::Type type
void setType_helper(QEasingCurve::Type)
\inmodule QtCore
EasingFunction customType() const
Returns the function pointer to the custom easing curve.
~QEasingCurve()
Destructor.
void setAmplitude(qreal amplitude)
Sets the amplitude to amplitude.
void setPeriod(qreal period)
Sets the period to period.
qreal period() const
Returns the period.
Type type() const
Returns the type of the easing curve.
qreal overshoot() const
Returns the overshoot.
qreal(* EasingFunction)(qreal progress)
This is a typedef for a pointer to a function with the following signature:
void addCubicBezierSegment(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
Adds a segment of a cubic bezier spline to define a custom easing curve.
QEasingCurve(Type type=Linear)
Constructs an easing curve of the given type.
void setOvershoot(qreal overshoot)
Sets the overshoot to overshoot.
void addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qreal b)
Adds a segment of a TCB bezier spline to define a custom easing curve.
void setCustomType(EasingFunction func)
Sets a custom easing curve that is defined by the user in the function func.
Type
The type of easing curve.
qreal valueForProgress(qreal progress) const
Return the effective progress for the easing curve at progress.
qreal amplitude() const
Returns the amplitude.
void setType(Type type)
Sets the type of the easing curve to type.
QList< QPointF > toCubicSpline() const
QScopedPointer< QGraphicsItemPrivate > d_ptr
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
const T & constLast() const noexcept
Definition qlist.h:650
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
pointer data()
Definition qlist.h:431
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QPixmap p2
QPixmap p1
[0]
Combined button and popup list for selecting options.
static jboolean copy(JNIEnv *, jobject)
static QList< QPointF > tcbToBezier(const TCBPoints &tcbPoints)
static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve)
static QT_BEGIN_NAMESPACE bool isConfigFunction(QEasingCurve::Type type)
bool comparesEqual(const QEasingCurve &lhs, const QEasingCurve &rhs) noexcept
QDataStream & operator>>(QDataStream &stream, TCBPoint &point)
QList< TCBPoint > TCBPoints
static QEasingCurveFunction * curveToFunctionObject(QEasingCurve::Type type)
QDataStream & operator<<(QDataStream &stream, const TCBPoint &point)
EGLStreamKHR stream
EGLConfig config
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
#define qWarning
Definition qlogging.h:166
#define M_PI
Definition qmath.h:209
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLenum type
GLboolean GLboolean g
GLdouble s
[6]
Definition qopenglext.h:235
GLenum func
Definition qopenglext.h:663
GLuint res
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define s3
#define s2
#define s1
#define t3
#define t2
#define t1
#define QT_BEGIN_INCLUDE_NAMESPACE
#define QT_END_INCLUDE_NAMESPACE
@ Q_PRIMITIVE_TYPE
Definition qtypeinfo.h:157
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:180
unsigned int quint32
Definition qtypes.h:50
size_t quintptr
Definition qtypes.h:167
unsigned long long quint64
Definition qtypes.h:61
double qreal
Definition qtypes.h:187
unsigned char quint8
Definition qtypes.h:46
static int sign(int x)
QObject::connect nullptr
MyCustomStruct c2
QEasingCurve easing(QEasingCurve::InOutQuad)
[typedef]
QSharedPointer< T > other(t)
[5]
QGraphicsItem * item
QEasingCurveFunction * copy() const override
qreal value(qreal t) 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)
static qreal singleRealSolutionForCubic(qreal a, qreal b, qreal c)
qreal value(qreal x) override
QList< SingleCubicBezier > _curves
QEasingCurveFunction * copy() const override
void getBezierSegment(SingleCubicBezier *&singleCubicBezier, qreal x)
static qreal findTForX(const SingleCubicBezier &singleCubicBezier, qreal x)
QList< qreal > _intervals
BezierEase(QEasingCurve::Type type=QEasingCurve::BezierSpline)
static double _fast_cbrt(double d)
static bool inRange(qreal f)
static bool almostZero(qreal value)
BounceEase(QEasingCurve::Type type)
QEasingCurveFunction * copy() const override
qreal value(qreal t) override
qreal value(qreal t) override
ElasticEase(QEasingCurve::Type type)
QEasingCurveFunction * copy() const override
QEasingCurveFunction * copy() const override
qreal value(qreal x) override
bool operator==(const TCBPoint &other) const
TCBPoint(QPointF point, qreal t, qreal c, qreal b)
QPointF _point
Definition moc.h:23