1 /**
2  * @file lv_area.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../lv_conf_internal.h"
10 
11 #include "lv_area.h"
12 #include "lv_math.h"
13 
14 /*********************
15  *      DEFINES
16  *********************/
17 
18 /**********************
19  *      TYPEDEFS
20  **********************/
21 
22 /**********************
23  *  STATIC PROTOTYPES
24  **********************/
25 
26 static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p);
27 
28 /**********************
29  *  STATIC VARIABLES
30  **********************/
31 
32 /**********************
33  *      MACROS
34  **********************/
35 
36 /**********************
37  *   GLOBAL FUNCTIONS
38  **********************/
39 
40 /**
41  * Initialize an area
42  * @param area_p pointer to an area
43  * @param x1 left coordinate of the area
44  * @param y1 top coordinate of the area
45  * @param x2 right coordinate of the area
46  * @param y2 bottom coordinate of the area
47  */
lv_area_set(lv_area_t * area_p,lv_coord_t x1,lv_coord_t y1,lv_coord_t x2,lv_coord_t y2)48 void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2)
49 {
50     area_p->x1 = x1;
51     area_p->y1 = y1;
52     area_p->x2 = x2;
53     area_p->y2 = y2;
54 }
55 
56 /**
57  * Set the width of an area
58  * @param area_p pointer to an area
59  * @param w the new width of the area (w == 1 makes x1 == x2)
60  */
lv_area_set_width(lv_area_t * area_p,lv_coord_t w)61 void lv_area_set_width(lv_area_t * area_p, lv_coord_t w)
62 {
63     area_p->x2 = area_p->x1 + w - 1;
64 }
65 
66 /**
67  * Set the height of an area
68  * @param area_p pointer to an area
69  * @param h the new height of the area (h == 1 makes y1 == y2)
70  */
lv_area_set_height(lv_area_t * area_p,lv_coord_t h)71 void lv_area_set_height(lv_area_t * area_p, lv_coord_t h)
72 {
73     area_p->y2 = area_p->y1 + h - 1;
74 }
75 
76 /**
77  * Set the position of an area (width and height will be kept)
78  * @param area_p pointer to an area
79  * @param x the new x coordinate of the area
80  * @param y the new y coordinate of the area
81  */
_lv_area_set_pos(lv_area_t * area_p,lv_coord_t x,lv_coord_t y)82 void _lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y)
83 {
84     lv_coord_t w = lv_area_get_width(area_p);
85     lv_coord_t h = lv_area_get_height(area_p);
86     area_p->x1   = x;
87     area_p->y1   = y;
88     lv_area_set_width(area_p, w);
89     lv_area_set_height(area_p, h);
90 }
91 
92 /**
93  * Return with area of an area (x * y)
94  * @param area_p pointer to an area
95  * @return size of area
96  */
lv_area_get_size(const lv_area_t * area_p)97 uint32_t lv_area_get_size(const lv_area_t * area_p)
98 {
99     uint32_t size;
100 
101     size = (uint32_t)(area_p->x2 - area_p->x1 + 1) * (area_p->y2 - area_p->y1 + 1);
102 
103     return size;
104 }
105 
lv_area_increase(lv_area_t * area,lv_coord_t w_extra,lv_coord_t h_extra)106 void lv_area_increase(lv_area_t * area, lv_coord_t w_extra, lv_coord_t h_extra)
107 {
108     area->x1 -= w_extra;
109     area->x2 += w_extra;
110     area->y1 -= h_extra;
111     area->y2 += h_extra;
112 }
113 
lv_area_move(lv_area_t * area,lv_coord_t x_ofs,lv_coord_t y_ofs)114 void lv_area_move(lv_area_t * area, lv_coord_t x_ofs, lv_coord_t y_ofs)
115 {
116     area->x1 += x_ofs;
117     area->x2 += x_ofs;
118     area->y1 += y_ofs;
119     area->y2 += y_ofs;
120 }
121 
122 /**
123  * Get the common parts of two areas
124  * @param res_p pointer to an area, the result will be stored here
125  * @param a1_p pointer to the first area
126  * @param a2_p pointer to the second area
127  * @return false: the two area has NO common parts, res_p is invalid
128  */
_lv_area_intersect(lv_area_t * res_p,const lv_area_t * a1_p,const lv_area_t * a2_p)129 bool _lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
130 {
131     /*Get the smaller area from 'a1_p' and 'a2_p'*/
132     res_p->x1 = LV_MAX(a1_p->x1, a2_p->x1);
133     res_p->y1 = LV_MAX(a1_p->y1, a2_p->y1);
134     res_p->x2 = LV_MIN(a1_p->x2, a2_p->x2);
135     res_p->y2 = LV_MIN(a1_p->y2, a2_p->y2);
136 
137     /*If x1 or y1 greater than x2 or y2 then the areas union is empty*/
138     bool union_ok = true;
139     if((res_p->x1 > res_p->x2) || (res_p->y1 > res_p->y2)) {
140         union_ok = false;
141     }
142 
143     return union_ok;
144 }
145 
146 /**
147  * Join two areas into a third which involves the other two
148  * @param res_p pointer to an area, the result will be stored here
149  * @param a1_p pointer to the first area
150  * @param a2_p pointer to the second area
151  */
_lv_area_join(lv_area_t * a_res_p,const lv_area_t * a1_p,const lv_area_t * a2_p)152 void _lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
153 {
154     a_res_p->x1 = LV_MIN(a1_p->x1, a2_p->x1);
155     a_res_p->y1 = LV_MIN(a1_p->y1, a2_p->y1);
156     a_res_p->x2 = LV_MAX(a1_p->x2, a2_p->x2);
157     a_res_p->y2 = LV_MAX(a1_p->y2, a2_p->y2);
158 }
159 
160 /**
161  * Check if a point is on an area
162  * @param a_p pointer to an area
163  * @param p_p pointer to a point
164  * @param radius radius of area (e.g. for rounded rectangle)
165  * @return false:the point is out of the area
166  */
_lv_area_is_point_on(const lv_area_t * a_p,const lv_point_t * p_p,lv_coord_t radius)167 bool _lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius)
168 {
169     /*First check the basic area*/
170     bool is_on_rect = false;
171     if((p_p->x >= a_p->x1 && p_p->x <= a_p->x2) && ((p_p->y >= a_p->y1 && p_p->y <= a_p->y2))) {
172         is_on_rect = true;
173     }
174     if(!is_on_rect)
175         return false;
176     /*Now handle potential rounded rectangles*/
177     if(radius <= 0) {
178         /*No radius, it is within the rectangle*/
179         return true;
180     }
181     lv_coord_t w = lv_area_get_width(a_p) / 2;
182     lv_coord_t h = lv_area_get_height(a_p) / 2;
183     lv_coord_t max_radius = LV_MIN(w, h);
184     if(radius > max_radius)
185         radius = max_radius;
186 
187     /*Check if it's in one of the corners*/
188     lv_area_t corner_area;
189     /*Top left*/
190     corner_area.x1 = a_p->x1;
191     corner_area.x2 = a_p->x1 + radius;
192     corner_area.y1 = a_p->y1;
193     corner_area.y2 = a_p->y1 + radius;
194     if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
195         corner_area.x2 += radius;
196         corner_area.y2 += radius;
197         return lv_point_within_circle(&corner_area, p_p);
198     }
199     /*Bottom left*/
200     corner_area.y1 = a_p->y2 - radius;
201     corner_area.y2 = a_p->y2;
202     if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
203         corner_area.x2 += radius;
204         corner_area.y1 -= radius;
205         return lv_point_within_circle(&corner_area, p_p);
206     }
207     /*Bottom right*/
208     corner_area.x1 = a_p->x2 - radius;
209     corner_area.x2 = a_p->x2;
210     if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
211         corner_area.x1 -= radius;
212         corner_area.y1 -= radius;
213         return lv_point_within_circle(&corner_area, p_p);
214     }
215     /*Top right*/
216     corner_area.y1 = a_p->y1;
217     corner_area.y2 = a_p->y1 + radius;
218     if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
219         corner_area.x1 -= radius;
220         corner_area.y2 += radius;
221         return lv_point_within_circle(&corner_area, p_p);
222     }
223     /*Not within corners*/
224     return true;
225 }
226 
227 /**
228  * Check if two area has common parts
229  * @param a1_p pointer to an area.
230  * @param a2_p pointer to an other area
231  * @return false: a1_p and a2_p has no common parts
232  */
_lv_area_is_on(const lv_area_t * a1_p,const lv_area_t * a2_p)233 bool _lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p)
234 {
235     if((a1_p->x1 <= a2_p->x2) && (a1_p->x2 >= a2_p->x1) && (a1_p->y1 <= a2_p->y2) && (a1_p->y2 >= a2_p->y1)) {
236         return true;
237     }
238     else {
239         return false;
240     }
241 }
242 
243 /**
244  * Check if an area is fully on an other
245  * @param ain_p pointer to an area which could be in 'aholder_p'
246  * @param aholder_p pointer to an area which could involve 'ain_p'
247  * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
248  * @return true: `ain_p` is fully inside `aholder_p`
249  */
_lv_area_is_in(const lv_area_t * ain_p,const lv_area_t * aholder_p,lv_coord_t radius)250 bool _lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p, lv_coord_t radius)
251 {
252     bool is_in = false;
253 
254     if(ain_p->x1 >= aholder_p->x1 && ain_p->y1 >= aholder_p->y1 && ain_p->x2 <= aholder_p->x2 &&
255        ain_p->y2 <= aholder_p->y2) {
256         is_in = true;
257     }
258 
259     if(!is_in) return false;
260     if(radius == 0) return true;
261 
262     /*Check if the corner points are inside the radius or not*/
263     lv_point_t p;
264 
265     p.x = ain_p->x1;
266     p.y = ain_p->y1;
267     if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
268 
269     p.x = ain_p->x2;
270     p.y = ain_p->y1;
271     if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
272 
273     p.x = ain_p->x1;
274     p.y = ain_p->y2;
275     if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
276 
277     p.x = ain_p->x2;
278     p.y = ain_p->y2;
279     if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
280 
281     return true;
282 }
283 
284 /**
285  * Check if an area is fully out of an other
286  * @param aout_p pointer to an area which could be in 'aholder_p'
287  * @param aholder_p pointer to an area which could involve 'ain_p'
288  * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
289  * @return true: `aout_p` is fully outside `aholder_p`
290  */
_lv_area_is_out(const lv_area_t * aout_p,const lv_area_t * aholder_p,lv_coord_t radius)291 bool _lv_area_is_out(const lv_area_t * aout_p, const lv_area_t * aholder_p, lv_coord_t radius)
292 {
293     if(aout_p->x2 < aholder_p->x1 || aout_p->y2 < aholder_p->y1 || aout_p->x1 > aholder_p->x2 ||
294        aout_p->y1 > aholder_p->y2) {
295         return true;
296     }
297 
298     if(radius == 0) return false;
299 
300     /*Check if the corner points are outside the radius or not*/
301     lv_point_t p;
302 
303     p.x = aout_p->x1;
304     p.y = aout_p->y1;
305     if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
306 
307     p.x = aout_p->x2;
308     p.y = aout_p->y1;
309     if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
310 
311     p.x = aout_p->x1;
312     p.y = aout_p->y2;
313     if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
314 
315     p.x = aout_p->x2;
316     p.y = aout_p->y2;
317     if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
318 
319     return true;
320 }
321 
_lv_area_is_equal(const lv_area_t * a,const lv_area_t * b)322 bool _lv_area_is_equal(const lv_area_t * a, const lv_area_t * b)
323 {
324     return a->x1 == b->x1 && a->x2 == b->x2 && a->y1 == b->y1 && a->y2 == b->y2;
325 }
326 
327 /**
328  * Align an area to an other
329  * @param base an are where the other will be aligned
330  * @param to_align the area to align
331  * @param align `LV_ALIGN_...`
332  * @param res x/y coordinates where `to_align` align area should be placed
333  */
lv_area_align(const lv_area_t * base,lv_area_t * to_align,lv_align_t align,lv_coord_t ofs_x,lv_coord_t ofs_y)334 void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, lv_coord_t ofs_x, lv_coord_t ofs_y)
335 {
336 
337     lv_coord_t x;
338     lv_coord_t y;
339     switch(align) {
340         case LV_ALIGN_CENTER:
341             x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
342             y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
343             break;
344 
345         case LV_ALIGN_TOP_LEFT:
346             x = 0;
347             y = 0;
348             break;
349         case LV_ALIGN_TOP_MID:
350             x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
351             y = 0;
352             break;
353 
354         case LV_ALIGN_TOP_RIGHT:
355             x = lv_area_get_width(base) - lv_area_get_width(to_align);
356             y = 0;
357             break;
358 
359         case LV_ALIGN_BOTTOM_LEFT:
360             x = 0;
361             y = lv_area_get_height(base) - lv_area_get_height(to_align);
362             break;
363         case LV_ALIGN_BOTTOM_MID:
364             x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
365             y = lv_area_get_height(base) - lv_area_get_height(to_align);
366             break;
367 
368         case LV_ALIGN_BOTTOM_RIGHT:
369             x = lv_area_get_width(base) - lv_area_get_width(to_align);
370             y = lv_area_get_height(base) - lv_area_get_height(to_align);
371             break;
372 
373         case LV_ALIGN_LEFT_MID:
374             x = 0;
375             y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
376             break;
377 
378         case LV_ALIGN_RIGHT_MID:
379             x = lv_area_get_width(base) - lv_area_get_width(to_align);
380             y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
381             break;
382 
383         case LV_ALIGN_OUT_TOP_LEFT:
384             x = 0;
385             y = -lv_area_get_height(to_align);
386             break;
387 
388         case LV_ALIGN_OUT_TOP_MID:
389             x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
390             y = -lv_area_get_height(to_align);
391             break;
392 
393         case LV_ALIGN_OUT_TOP_RIGHT:
394             x = lv_area_get_width(base) - lv_area_get_width(to_align);
395             y = -lv_area_get_height(to_align);
396             break;
397 
398         case LV_ALIGN_OUT_BOTTOM_LEFT:
399             x = 0;
400             y = lv_area_get_height(base);
401             break;
402 
403         case LV_ALIGN_OUT_BOTTOM_MID:
404             x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
405             y = lv_area_get_height(base);
406             break;
407 
408         case LV_ALIGN_OUT_BOTTOM_RIGHT:
409             x = lv_area_get_width(base) - lv_area_get_width(to_align);
410             y = lv_area_get_height(base);
411             break;
412 
413         case LV_ALIGN_OUT_LEFT_TOP:
414             x = -lv_area_get_width(to_align);
415             y = 0;
416             break;
417 
418         case LV_ALIGN_OUT_LEFT_MID:
419             x = -lv_area_get_width(to_align);
420             y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
421             break;
422 
423         case LV_ALIGN_OUT_LEFT_BOTTOM:
424             x = -lv_area_get_width(to_align);
425             y = lv_area_get_height(base) - lv_area_get_height(to_align);
426             break;
427 
428         case LV_ALIGN_OUT_RIGHT_TOP:
429             x = lv_area_get_width(base);
430             y = 0;
431             break;
432 
433         case LV_ALIGN_OUT_RIGHT_MID:
434             x = lv_area_get_width(base);
435             y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
436             break;
437 
438         case LV_ALIGN_OUT_RIGHT_BOTTOM:
439             x = lv_area_get_width(base);
440             y = lv_area_get_height(base) - lv_area_get_height(to_align);
441             break;
442         default:
443             x = 0;
444             y = 0;
445             break;
446     }
447 
448     x += base->x1;
449     y += base->y1;
450 
451     lv_coord_t w = lv_area_get_width(to_align);
452     lv_coord_t h = lv_area_get_height(to_align);
453     to_align->x1 = x + ofs_x;
454     to_align->y1 = y + ofs_y;
455     to_align->x2 = to_align->x1 + w - 1;
456     to_align->y2 = to_align->y1 + h - 1;
457 }
458 
459 #define _LV_TRANSFORM_TRIGO_SHIFT 10
lv_point_transform(lv_point_t * p,int32_t angle,int32_t zoom,const lv_point_t * pivot)460 void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot)
461 {
462     if(angle == 0 && zoom == 256) {
463         return;
464     }
465 
466     p->x -= pivot->x;
467     p->y -= pivot->y;
468 
469     if(angle == 0) {
470         p->x = (((int32_t)(p->x) * zoom) >> 8) + pivot->x;
471         p->y = (((int32_t)(p->y) * zoom) >> 8) + pivot->y;
472         return;
473     }
474 
475     static int32_t angle_prev = INT32_MIN;
476     static int32_t sinma;
477     static int32_t cosma;
478     if(angle_prev != angle) {
479         int32_t angle_limited = angle;
480         if(angle_limited > 3600) angle_limited -= 3600;
481         if(angle_limited < 0) angle_limited += 3600;
482 
483         int32_t angle_low = angle_limited / 10;
484         int32_t angle_high = angle_low + 1;
485         int32_t angle_rem = angle_limited  - (angle_low * 10);
486 
487         int32_t s1 = lv_trigo_sin(angle_low);
488         int32_t s2 = lv_trigo_sin(angle_high);
489 
490         int32_t c1 = lv_trigo_sin(angle_low + 90);
491         int32_t c2 = lv_trigo_sin(angle_high + 90);
492 
493         sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
494         cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
495         sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
496         cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
497         angle_prev = angle;
498     }
499     int32_t x = p->x;
500     int32_t y = p->y;
501     if(zoom == 256) {
502         p->x = ((cosma * x - sinma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
503         p->y = ((sinma * x + cosma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
504     }
505     else {
506         p->x = (((cosma * x - sinma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->x;
507         p->y = (((sinma * x + cosma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->y;
508     }
509 }
510 
511 
512 /**********************
513  *   STATIC FUNCTIONS
514  **********************/
515 
lv_point_within_circle(const lv_area_t * area,const lv_point_t * p)516 static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p)
517 {
518     lv_coord_t r = (area->x2 - area->x1) / 2;
519 
520     /*Circle center*/
521     lv_coord_t cx = area->x1 + r;
522     lv_coord_t cy = area->y1 + r;
523 
524     /*Simplify the code by moving everything to (0, 0)*/
525     lv_coord_t px = p->x - cx;
526     lv_coord_t py = p->y - cy;
527 
528     uint32_t r_sqrd = r * r;
529     uint32_t dist = (px * px) + (py * py);
530 
531     if(dist <= r_sqrd)
532         return true;
533     else
534         return false;
535 }
536