Uploaded image for project: 'Qt Design Studio'
  1. Qt Design Studio
  2. QDS-2739

MCU does not support custom easing curves

    XMLWordPrintable

Details

    Description

      MCU does not support custom easing curves (easing.bezierCurve).

      This is a serious issue because Qt Design Studio provides editors for those and only supports custom easing curves, because we do not use the defaults defined in the enums.
      Also the graphical curve editor does rely on this feature. Custom easing curves are a real powerful tool for animations.

      If there are concerns about performance, then I would suggest to support at least easing curves with a single segment.
      Qt Quick and QDS support multiple bezier segments for the easing curve and those allow defining complex curves for e.g. a bounce.
      If there are serious issues with implementing multiple bezier segments then we could fallback to single segment curves.
      Those are signifcant easier and simpler to compute. This should be possible even without an FPU.

      To support custom easing curves with multiple segments it is required to solve a cubic polynom (see singleRealSolutionForCubic)

      The code for this can be found below. For this code an FPU is required, though.

       qreal static inline _cbrt(qreal d)
          {
              qreal sign = 1;
              if (d < 0)
                  sign = -1;
              d = d * sign;
      
              qreal t = _fast_cbrt(d);
      
              //one step of Halley's Method to get a better approximation
              const qreal t_cubic = t * t * t;
              const qreal f = t_cubic + t_cubic + d;
              if (f != qreal(0.0))
                  t = t * (t_cubic + d + d) / f;
      
              //another step
              /*qreal t_i = t;
               t_i_cubic = pow(t_i, 3);
               t = t_i * (t_i_cubic + d + d) / (t_i_cubic + t_i_cubic + d);*/
      
              return t * sign;
          }
      
          float static inline _fast_cbrt(float x)
          {
              union {
                  float f;
                  quint32 i;
              } ux;
      
              const unsigned int B1 = 709921077;
      
              ux.f = x;
              ux.i = (ux.i / 3 + B1);
      
              return ux.f;
          }
      
          double static inline _fast_cbrt(double d)
          {
              union {
                  double d;
                  quint32 pt[2];
              } ut, ux;
      
              const unsigned int B1 = 715094163;
      
      #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
              const int h0 = 1;
      #else
              const int h0 = 0;
      #endif
              ut.d = 0.0;
              ux.d = d;
      
              quint32 hx = ux.pt[h0]; //high word of d
              ut.pt[h0] = hx / 3 + B1;
      
              return ut.d;
          }
      
          qreal static inline _acos(qreal x)
          {
              return std::sqrt(1-x)*(1.5707963267948966192313216916398f + x*(-0.213300989f + x*(0.077980478f + x*-0.02164095f)));
          }
      
          qreal static inline _cos(qreal x) //super fast _cos
          {
              const qreal pi_times2 = 2 * M_PI;
              const qreal pi_neg = -1 * M_PI;
              const qreal pi_by2 = M_PI / 2.0;
      
              x += pi_by2; //the polynom is for sin
      
              if (x < pi_neg)
                  x += pi_times2;
              else if (x > M_PI)
                  x -= pi_times2;
      
              const qreal a = 0.405284735;
              const qreal b = 1.27323954;
      
              const qreal x_squared = x * x;
      
              if (x < 0) {
                  qreal cos = b * x + a * x_squared;
      
                  if (cos < 0)
                      return 0.225 * (cos * -1 * cos - cos) + cos;
                  return 0.225 * (cos * cos - cos) + cos;
              } //else
      
              qreal cos = b * x - a * x_squared;
      
              if (cos < 0)
                  return 0.225 * (cos * 1 *-cos - cos) + cos;
              return 0.225 * (cos * cos - cos) + cos;
          }
      
          bool static inline inRange(qreal f)
          {
              return (f >= -0.01 && f <= 1.01);
          }
      
          void static inline cosacos(qreal x, qreal &s1, qreal &s2, qreal &s3 )
          {
              //This function has no proper algebraic representation in real numbers.
              //We use approximations instead
      
              const qreal x_squared = x * x;
              const qreal x_plus_one_sqrt = qSqrt(1.0 + x);
              const qreal one_minus_x_sqrt = qSqrt(1.0 - x);
      
              //cos(acos(x) / 3)
              //s1 = _cos(_acos(x) / 3);
              s1 = 0.463614 - 0.0347815 * x + 0.00218245 * x_squared +  0.402421 * x_plus_one_sqrt;
      
              //cos(acos((x) -  M_PI) / 3)
              //s3 = _cos((_acos(x) - M_PI) / 3);
              s3 = 0.463614 + 0.402421 * one_minus_x_sqrt + 0.0347815 * x + 0.00218245 * x_squared;
      
              //cos((acos(x) +  M_PI) / 3)
              //s2 = _cos((_acos(x) + M_PI) / 3);
              s2 = -0.401644 * one_minus_x_sqrt - 0.0686804  * x + 0.401644 * x_plus_one_sqrt;
          }
      
          qreal static inline singleRealSolutionForCubic(qreal a, qreal b, qreal c)
          {
              //returns the real solutiuon in [0..1]
              //We use the Cardano formula
      
              //substituiton: x = z - a/3
              // z^3+pz+q=0
      
              if (c < 0.000001 && c > -0.000001)
                  return 0;
      
              const qreal a_by3 = a / 3.0;
      
              const qreal a_cubic = a * a * a;
      
              const qreal p = b - a * a_by3;
              const qreal q = 2.0 * a_cubic / 27.0 - a * b / 3.0 + c;
      
              const qreal q_squared = q * q;
              const qreal p_cubic = p * p * p;
              const qreal D = 0.25 * q_squared + p_cubic / 27.0;
      
              if (D >= 0) {
                  const qreal D_sqrt = qSqrt(D);
                  qreal u = _cbrt( -q * 0.5 + D_sqrt);
                  qreal v = _cbrt( -q * 0.5 - D_sqrt);
                  qreal z1 = u + v;
      
                  qreal t1 = z1 - a_by3;
      
                  if (inRange(t1))
                      return t1;
                  qreal z2 = -1 *u;
                  qreal t2 = z2 - a_by3;
                  return t2;
              }
      
              //casus irreducibilis
              const qreal p_minus_sqrt = qSqrt(-p);
      
              //const qreal f = sqrt(4.0 / 3.0 * -p);
              const qreal f = qSqrt(4.0 / 3.0) * p_minus_sqrt;
      
              //const qreal sqrtP = sqrt(27.0 / -p_cubic);
              const qreal sqrtP = -3.0*qSqrt(3.0) / (p_minus_sqrt * p);
      
      
              const qreal g = -q * 0.5 * sqrtP;
      
              qreal s1;
              qreal s2;
              qreal s3;
      
              cosacos(g, s1, s2, s3);
      
              qreal z1 = -1* f * s2;
              qreal t1 = z1 - a_by3;
              if (inRange(t1))
                  return t1;
      
              qreal z2 = f * s1;
              qreal t2 = z2 - a_by3;
              if (inRange(t2))
                  return t2;
      
              qreal z3 = -1 * f * s3;
              qreal t3 = z3 - a_by3;
              return t3;
          }
      

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            ylopes Yoann Lopes
            thohartm Thomas Hartmann
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes