1 /**
2 * @file lv_math.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_math.h"
10 #include "../core/lv_global.h"
11
12 /*********************
13 * DEFINES
14 *********************/
15 #define rand_seed LV_GLOBAL_DEFAULT()->math_rand_seed
16
17 /**********************
18 * TYPEDEFS
19 **********************/
20
21 #define CUBIC_NEWTON_ITERATIONS 8
22 #define CUBIC_PRECISION_BITS 10 /* 10 or 14 bits recommended, int64_t calculation is used for >14bit precision */
23
24 #if CUBIC_PRECISION_BITS < 10 || CUBIC_PRECISION_BITS > 20
25 #error "cubic precision bits should be in range of [10, 20] for 32bit/64bit calculations."
26 #endif
27
28 /**********************
29 * STATIC PROTOTYPES
30 **********************/
31
32 /**********************
33 * STATIC VARIABLES
34 **********************/
35 static const uint16_t sin0_90_table[] = {
36 0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126, 5690, 6252, 6813, 7371, 7927, 8481,
37 9032, 9580, 10126, 10668, 11207, 11743, 12275, 12803, 13328, 13848, 14365, 14876, 15384, 15886, 16384, 16877,
38 17364, 17847, 18324, 18795, 19261, 19720, 20174, 20622, 21063, 21498, 21926, 22348, 22763, 23170, 23571, 23965,
39 24351, 24730, 25102, 25466, 25822, 26170, 26510, 26842, 27166, 27482, 27789, 28088, 28378, 28660, 28932, 29197,
40 29452, 29698, 29935, 30163, 30382, 30592, 30792, 30983, 31164, 31336, 31499, 31651, 31795, 31928, 32052, 32166,
41 32270, 32365, 32449, 32524, 32588, 32643, 32688, 32723, 32748, 32763, 32768
42 };
43
44 /**********************
45 * MACROS
46 **********************/
47
48 /**********************
49 * GLOBAL FUNCTIONS
50 **********************/
51
lv_trigo_sin(int16_t angle)52 int32_t LV_ATTRIBUTE_FAST_MEM lv_trigo_sin(int16_t angle)
53 {
54 int32_t ret = 0;
55 while(angle < 0) angle += 360;
56 while(angle >= 360) angle -= 360;
57
58 if(angle < 90) {
59 ret = sin0_90_table[angle];
60 }
61 else if(angle >= 90 && angle < 180) {
62 angle = 180 - angle;
63 ret = sin0_90_table[angle];
64 }
65 else if(angle >= 180 && angle < 270) {
66 angle = angle - 180;
67 ret = -sin0_90_table[angle];
68 }
69 else { /*angle >=270*/
70 angle = 360 - angle;
71 ret = -sin0_90_table[angle];
72 }
73
74 if(ret == 32767) return 32768;
75 else if(ret == -32767) return -32768;
76 else return ret;
77 }
78
79 /**
80 * cubic-bezier Reference:
81 *
82 * https://github.com/gre/bezier-easing
83 * https://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h
84 *
85 * Copyright (c) 2014 Gaëtan Renaudeau
86 *
87 * Permission is hereby granted, free of charge, to any person
88 * obtaining a copy of this software and associated documentation
89 * files (the "Software"), to deal in the Software without
90 * restriction, including without limitation the rights to use,
91 * copy, modify, merge, publish, distribute, sublicense, and/or sell
92 * copies of the Software, and to permit persons to whom the
93 * Software is furnished to do so, subject to the following
94 * conditions:
95 *
96 * The above copyright notice and this permission notice shall be
97 * included in all copies or substantial portions of the Software.
98 *
99 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
100 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
101 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
102 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
103 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
104 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
105 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
106 * OTHER DEALINGS IN THE SOFTWARE.
107 *
108 */
109
do_cubic_bezier(int32_t t,int32_t a,int32_t b,int32_t c)110 static int32_t do_cubic_bezier(int32_t t, int32_t a, int32_t b, int32_t c)
111 {
112 /*a * t^3 + b * t^2 + c * t*/
113 #if CUBIC_PRECISION_BITS > 14
114 int64_t ret;
115 #else
116 int32_t ret;
117 #endif
118
119 ret = a;
120 ret = (ret * t) >> CUBIC_PRECISION_BITS;
121 ret = ((ret + b) * t) >> CUBIC_PRECISION_BITS;
122 ret = ((ret + c) * t) >> CUBIC_PRECISION_BITS;
123 return ret;
124 }
125
lv_cubic_bezier(int32_t x,int32_t x1,int32_t y1,int32_t x2,int32_t y2)126 int32_t lv_cubic_bezier(int32_t x, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
127 {
128 int32_t ax, bx, cx, ay, by, cy;
129 int32_t tl, tr, t; /*t in cubic-bezier function, used for bisection */
130 int32_t xs; /*x sampled on curve */
131 #if CUBIC_PRECISION_BITS > 14
132 int64_t d; /*slope value at specified t*/
133 #else
134 int32_t d;
135 #endif
136
137 if(x == 0 || x == LV_BEZIER_VAL_MAX) return x;
138
139 /* input is always LV_BEZIER_VAL_SHIFT bit precision */
140
141 #if CUBIC_PRECISION_BITS != LV_BEZIER_VAL_SHIFT
142 x <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
143 x1 <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
144 x2 <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
145 y1 <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
146 y2 <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
147 #endif
148
149 cx = 3 * x1;
150 bx = 3 * (x2 - x1) - cx;
151 ax = (1L << CUBIC_PRECISION_BITS) - cx - bx;
152
153 cy = 3 * y1;
154 by = 3 * (y2 - y1) - cy;
155 ay = (1L << CUBIC_PRECISION_BITS) - cy - by;
156
157 /*Try Newton's method firstly */
158 t = x; /*Make a guess*/
159 for(int i = 0; i < CUBIC_NEWTON_ITERATIONS; i++) {
160 /*Check if x on curve at t matches input x*/
161 xs = do_cubic_bezier(t, ax, bx, cx) - x;
162 if(LV_ABS(xs) <= 1) goto found;
163
164 /* get slop at t, d = 3 * ax * t^2 + 2 * bx + t + cx */
165 d = ax; /* use 64bit operation if needed. */
166 d = (3 * d * t) >> CUBIC_PRECISION_BITS;
167 d = ((d + 2 * bx) * t) >> CUBIC_PRECISION_BITS;
168 d += cx;
169
170 if(LV_ABS(d) <= 1) break;
171
172 d = ((int64_t)xs * (1L << CUBIC_PRECISION_BITS)) / d;
173 if(d == 0) break; /*Reached precision limits*/
174 t -= d;
175 }
176
177 /*Fallback to bisection method for reliability*/
178 tl = 0, tr = 1L << CUBIC_PRECISION_BITS, t = x;
179
180 if(t < tl) {
181 t = tl;
182 goto found;
183 }
184
185 if(t > tr) {
186 t = tr;
187 goto found;
188 }
189
190 while(tl < tr) {
191 xs = do_cubic_bezier(t, ax, bx, cx);
192 if(LV_ABS(xs - x) <= 1) goto found;
193 x > xs ? (tl = t) : (tr = t);
194 t = (tr - tl) / 2 + tl;
195 if(t == tl) break;
196 }
197
198 /*Failed to find suitable t for given x, return a value anyway.*/
199 found:
200 /*Return y at t*/
201 #if CUBIC_PRECISION_BITS != LV_BEZIER_VAL_SHIFT
202 return do_cubic_bezier(t, ay, by, cy) >> (CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT);
203 #else
204 return do_cubic_bezier(t, ay, by, cy);
205 #endif
206 }
207
lv_sqrt(uint32_t x,lv_sqrt_res_t * q,uint32_t mask)208 void LV_ATTRIBUTE_FAST_MEM lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask)
209 {
210 x = x << 8; /*To get 4 bit precision. (sqrt(256) = 16 = 4 bit)*/
211
212 uint32_t root = 0;
213 uint32_t trial;
214 /*http://ww1.microchip.com/...en/AppNotes/91040a.pdf*/
215 do {
216 trial = root + mask;
217 if(trial * trial <= x) root = trial;
218 mask = mask >> 1;
219 } while(mask);
220
221 q->i = root >> 4;
222 q->f = (root & 0xf) << 4;
223 }
224
225 /*
226 // Alternative Integer Square Root function
227 // Contributors include Arne Steinarson for the basic approximation idea,
228 // Dann Corbit and Mathew Hendry for the first cut at the algorithm,
229 // Lawrence Kirby for the rearrangement, improvements and range optimization
230 // and Paul Hsieh for the round-then-adjust idea.
231 */
lv_sqrt32(uint32_t x)232 int32_t LV_ATTRIBUTE_FAST_MEM lv_sqrt32(uint32_t x)
233 {
234 static const unsigned char sqq_table[] = {
235 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57,
236 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83,
237 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102,
238 103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118,
239 119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132,
240 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145,
241 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157,
242 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168,
243 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178,
244 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188,
245 189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197,
246 198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206,
247 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215,
248 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223,
249 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231,
250 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238,
251 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246,
252 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253,
253 253, 254, 254, 255
254 };
255
256 int32_t xn;
257
258 if(x >= 0x10000)
259 if(x >= 0x1000000)
260 if(x >= 0x10000000)
261 if(x >= 0x40000000) {
262 if(x >= 65535UL * 65535UL)
263 return 65535;
264 xn = sqq_table[x >> 24] << 8;
265 }
266 else
267 xn = sqq_table[x >> 22] << 7;
268 else if(x >= 0x4000000)
269 xn = sqq_table[x >> 20] << 6;
270 else
271 xn = sqq_table[x >> 18] << 5;
272 else {
273 if(x >= 0x100000)
274 if(x >= 0x400000)
275 xn = sqq_table[x >> 16] << 4;
276 else
277 xn = sqq_table[x >> 14] << 3;
278 else if(x >= 0x40000)
279 xn = sqq_table[x >> 12] << 2;
280 else
281 xn = sqq_table[x >> 10] << 1;
282
283 goto nr1;
284 }
285 else if(x >= 0x100) {
286 if(x >= 0x1000)
287 if(x >= 0x4000)
288 xn = (sqq_table[x >> 8] >> 0) + 1;
289 else
290 xn = (sqq_table[x >> 6] >> 1) + 1;
291 else if(x >= 0x400)
292 xn = (sqq_table[x >> 4] >> 2) + 1;
293 else
294 xn = (sqq_table[x >> 2] >> 3) + 1;
295
296 goto adj;
297 }
298 else
299 return sqq_table[x] >> 4;
300
301 /* Run two iterations of the standard convergence formula */
302
303 xn = (xn + 1 + x / xn) / 2;
304 nr1:
305 xn = (xn + 1 + x / xn) / 2;
306 adj:
307
308 if(xn * xn > (int32_t)x) /* Correct rounding if necessary */
309 xn--;
310
311 return xn;
312 }
313
lv_atan2(int x,int y)314 uint16_t lv_atan2(int x, int y)
315 {
316 /**
317 * Fast XY vector to integer degree algorithm - Jan 2011 www.RomanBlack.com
318 * Converts any XY values including 0 to a degree value that should be
319 * within +/- 1 degree of the accurate value without needing
320 * large slow trig functions like ArcTan() or ArcCos().
321 * NOTE! at least one of the X or Y values must be non-zero!
322 * This is the full version, for all 4 quadrants and will generate
323 * the angle in integer degrees from 0-360.
324 * Any values of X and Y are usable including negative values provided
325 * they are between -1456 and 1456 so the 16bit multiply does not overflow.
326 */
327 unsigned char negflag;
328 unsigned char tempdegree;
329 unsigned char comp;
330 unsigned int degree; /*this will hold the result*/
331 unsigned int ux;
332 unsigned int uy;
333
334 /*Save the sign flags then remove signs and get XY as unsigned ints*/
335 negflag = 0;
336 if(x < 0) {
337 negflag += 0x01; /*x flag bit*/
338 x = (0 - x); /*is now +*/
339 }
340 ux = x; /*copy to unsigned var before multiply*/
341 if(y < 0) {
342 negflag += 0x02; /*y flag bit*/
343 y = (0 - y); /*is now +*/
344 }
345 uy = y; /*copy to unsigned var before multiply*/
346
347 /*1. Calc the scaled "degrees"*/
348 if(ux > uy) {
349 degree = (uy * 45) / ux; /*degree result will be 0-45 range*/
350 negflag += 0x10; /*octant flag bit*/
351 }
352 else {
353 degree = (ux * 45) / uy; /*degree result will be 0-45 range*/
354 }
355
356 /*2. Compensate for the 4 degree error curve*/
357 comp = 0;
358 tempdegree = degree; /*use an unsigned char for speed!*/
359 if(tempdegree > 22) { /*if top half of range*/
360 if(tempdegree <= 44) comp++;
361 if(tempdegree <= 41) comp++;
362 if(tempdegree <= 37) comp++;
363 if(tempdegree <= 32) comp++; /*max is 4 degrees compensated*/
364 }
365 else { /*else is lower half of range*/
366 if(tempdegree >= 2) comp++;
367 if(tempdegree >= 6) comp++;
368 if(tempdegree >= 10) comp++;
369 if(tempdegree >= 15) comp++; /*max is 4 degrees compensated*/
370 }
371 degree += comp; /*degree is now accurate to +/- 1 degree!*/
372
373 /*Invert degree if it was X>Y octant, makes 0-45 into 90-45*/
374 if(negflag & 0x10) degree = (90 - degree);
375
376 /*3. Degree is now 0-90 range for this quadrant,*/
377 /*need to invert it for whichever quadrant it was in*/
378 if(negflag & 0x02) { /*if -Y*/
379 if(negflag & 0x01) /*if -Y -X*/
380 degree = (180 + degree);
381 else /*else is -Y +X*/
382 degree = (180 - degree);
383 }
384 else { /*else is +Y*/
385 if(negflag & 0x01) /*if +Y -X*/
386 degree = (360 - degree);
387 }
388 return degree;
389 }
390
lv_pow(int64_t base,int8_t exp)391 int64_t lv_pow(int64_t base, int8_t exp)
392 {
393 int64_t result = 1;
394 while(exp) {
395 if(exp & 1)
396 result *= base;
397 exp >>= 1;
398 base *= base;
399 }
400
401 return result;
402 }
403
lv_map(int32_t x,int32_t min_in,int32_t max_in,int32_t min_out,int32_t max_out)404 int32_t lv_map(int32_t x, int32_t min_in, int32_t max_in, int32_t min_out, int32_t max_out)
405 {
406 if(max_in >= min_in && x >= max_in) return max_out;
407 if(max_in >= min_in && x <= min_in) return min_out;
408
409 if(max_in <= min_in && x <= max_in) return max_out;
410 if(max_in <= min_in && x >= min_in) return min_out;
411
412 /**
413 * The equation should be:
414 * ((x - min_in) * delta_out) / delta in) + min_out
415 * To avoid rounding error reorder the operations:
416 * (x - min_in) * (delta_out / delta_min) + min_out
417 */
418
419 int32_t delta_in = max_in - min_in;
420 int32_t delta_out = max_out - min_out;
421
422 return ((x - min_in) * delta_out) / delta_in + min_out;
423 }
424
lv_rand_set_seed(uint32_t seed)425 void lv_rand_set_seed(uint32_t seed)
426 {
427 rand_seed = seed;
428 }
429
lv_rand(uint32_t min,uint32_t max)430 uint32_t lv_rand(uint32_t min, uint32_t max)
431 {
432 /*Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs"*/
433 uint32_t x = rand_seed;
434 x ^= x << 13;
435 x ^= x >> 17;
436 x ^= x << 5;
437 rand_seed = x;
438
439 return (rand_seed % (max - min + 1)) + min;
440 }
441
lv_trigo_cos(int16_t angle)442 int32_t LV_ATTRIBUTE_FAST_MEM lv_trigo_cos(int16_t angle)
443 {
444 return lv_trigo_sin(angle + 90);
445 }
446
lv_bezier3(int32_t t,int32_t u0,uint32_t u1,int32_t u2,int32_t u3)447 int32_t lv_bezier3(int32_t t, int32_t u0, uint32_t u1, int32_t u2, int32_t u3)
448 {
449 LV_UNUSED(u0);
450 LV_UNUSED(u3);
451 return lv_cubic_bezier(t, 341, u1, 683, u2);
452 }
453
454 /**********************
455 * STATIC FUNCTIONS
456 **********************/
457