1 #if LV_BUILD_TEST
2 #include "../lvgl.h"
3 #include "../../lvgl_private.h"
4 
5 #include "unity/unity.h"
6 
7 #define ERROR_THRESHOLD         5 /*5 in 1024, 0.5% max error allowed*/
8 #define NEWTON_ITERATIONS       8
9 
do_cubic_bezier_f(float t,float a,float b,float c)10 static float do_cubic_bezier_f(float t, float a, float b, float c)
11 {
12     /*a*t^3 + b*t^2 + c*t*/
13     return ((a * t + b) * t + c) * t;
14 }
15 
16 /**
17  * Calculate the y value of cubic-bezier(x1, y1, x2, y2) function as specified x.
18  * @param x time in range of [0..1]
19  * @param x1 x of control point 1 in range of [0..1]
20  * @param y1 y of control point 1 in range of [0..1]
21  * @param x2 x of control point 2 in range of [0..1]
22  * @param y2 y of control point 2 in range of [0..1]
23  * @return the value calculated
24  */
lv_cubic_bezier_f(float x,float x1,float y1,float x2,float y2)25 static float lv_cubic_bezier_f(float x, float x1, float y1, float x2, float y2)
26 {
27     float ax, bx, cx, ay, by, cy;
28     float tl, tr, t;  /*t in cubic-bezier function, used for bisection */
29     float xs;  /*x sampled on curve */
30     float d; /*slope value at specified t*/
31 
32     if(x == 0 || x == 1) return x;
33 
34     cx = 3.f * x1;
35     bx = 3.f * (x2 - x1) - cx;
36     ax = 1.f - cx - bx;
37 
38     cy = 3.f * y1;
39     by = 3.f * (y2 - y1) - cy;
40     ay = 1.f - cy - by;
41 
42     /*Try Newton's method firstly */
43     t = x; /*Make a guess*/
44     for(int i = 0; i < NEWTON_ITERATIONS; i++) {
45         xs = do_cubic_bezier_f(t, ax, bx, cx);
46         xs -= x;
47         if(LV_ABS(xs) < 1e-6f) goto found;
48 
49         d = (3.f * ax * t + 2.f * bx) * t + cx;
50         if(LV_ABS(d) < 1e-6f) break;
51         t -= xs / d;
52     }
53 
54     /*Fallback to bisection method for reliability*/
55     tl = 0.f, tr = 1.f, t = x;
56 
57     if(t < tl) {
58         t = tl;
59         goto found;
60     }
61 
62     if(t > tr) {
63         t = tr;
64         goto found;
65     }
66 
67     while(tl < tr) {
68         xs = do_cubic_bezier_f(t, ax, bx, cx);
69         if(LV_ABS(xs - x) < 1e-6f) goto found;
70         x > xs ? (tl = t) : (tr = t);
71         t = (tr - tl) * .5f + tl;
72     }
73 
74 found:
75     return do_cubic_bezier_f(t, ay, by, cy);
76 }
77 
test_cubic_bezier_ease_functions(float fx1,float fy1,float fx2,float fy2)78 static int test_cubic_bezier_ease_functions(float fx1, float fy1, float fx2, float fy2)
79 {
80     int x1, y1, x2, y2, y;
81     float t, t_step, fy;
82 
83     t_step = .001f;
84     x1 = LV_BEZIER_VAL_FLOAT(fx1);
85     y1 = LV_BEZIER_VAL_FLOAT(fy1);
86     x2 = LV_BEZIER_VAL_FLOAT(fx2);
87     y2 = LV_BEZIER_VAL_FLOAT(fy2);
88 
89     for(t = 0; t <= 1; t += t_step) {
90         fy = lv_cubic_bezier_f(t, fx1, fy1, fx2, fy2);
91         y = lv_cubic_bezier(LV_BEZIER_VAL_FLOAT(t), x1, y1, x2, y2);
92         if(LV_ABS(LV_BEZIER_VAL_FLOAT(fy) - y) >= ERROR_THRESHOLD) {
93             return 0;
94         }
95     }
96 
97     return 1;
98 }
99 
lv_bezier3_legacy(uint32_t t,uint32_t u0,uint32_t u1,uint32_t u2,uint32_t u3)100 static uint32_t lv_bezier3_legacy(uint32_t t, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3)
101 {
102     uint32_t t_rem  = 1024 - t;
103     uint32_t t_rem2 = (t_rem * t_rem) >> 10;
104     uint32_t t_rem3 = (t_rem2 * t_rem) >> 10;
105     uint32_t t2     = (t * t) >> 10;
106     uint32_t t3     = (t2 * t) >> 10;
107 
108     uint32_t v1 = (t_rem3 * u0) >> 10;
109     uint32_t v2 = (3 * t_rem2 * t * u1) >> 20;
110     uint32_t v3 = (3 * t_rem * t2 * u2) >> 20;
111     uint32_t v4 = (t3 * u3) >> 10;
112 
113     return v1 + v2 + v3 + v4;
114 }
115 
test_math_cubic_bezier_result_should_be_precise(void)116 void test_math_cubic_bezier_result_should_be_precise(void)
117 {
118     /*ease-in-out function*/
119     TEST_ASSERT_TRUE(test_cubic_bezier_ease_functions(.42f, 0, .58f, 1));
120 
121     /*ease-out function*/
122     TEST_ASSERT_TRUE(test_cubic_bezier_ease_functions(0, 0, .58f, 1));
123 
124     /*ease-in function*/
125     TEST_ASSERT_TRUE(test_cubic_bezier_ease_functions(.42f, 0, 1, 1));
126 
127     /*ease function*/
128     TEST_ASSERT_TRUE(test_cubic_bezier_ease_functions(.25f, .1f, .25f, 1));
129 
130     int32_t u0 = 0, u1 = 50, u2 = 952, u3 = LV_BEZIER_VAL_MAX;
131     for(int32_t i = 0; i <= 1024; i++) {
132         int32_t legacy = lv_bezier3_legacy(i, u0, u1, u2, u3);
133         int32_t cubic_bezier = lv_bezier3(i, u0, u1, u2, u3);
134 
135         TEST_ASSERT_TRUE(LV_ABS(legacy - cubic_bezier) <= 5);
136     }
137 }
138 
139 #endif
140