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