MCU does not support custom easing curves

XMLWordPrintable

Details

• Resolution: Invalid
• P1: Critical
• QDS 1.6

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.

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;
}

People

Yoann Lopes
Thomas Hartmann