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 
106 /**
107  * Get the common parts of two areas
108  * @param res_p pointer to an area, the result will be stored here
109  * @param a1_p pointer to the first area
110  * @param a2_p pointer to the second area
111  * @return false: the two area has NO common parts, res_p is invalid
112  */
_lv_area_intersect(lv_area_t * res_p,const lv_area_t * a1_p,const lv_area_t * a2_p)113 bool _lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
114 {
115     /* Get the smaller area from 'a1_p' and 'a2_p' */
116     res_p->x1 = LV_MATH_MAX(a1_p->x1, a2_p->x1);
117     res_p->y1 = LV_MATH_MAX(a1_p->y1, a2_p->y1);
118     res_p->x2 = LV_MATH_MIN(a1_p->x2, a2_p->x2);
119     res_p->y2 = LV_MATH_MIN(a1_p->y2, a2_p->y2);
120 
121     /*If x1 or y1 greater then x2 or y2 then the areas union is empty*/
122     bool union_ok = true;
123     if((res_p->x1 > res_p->x2) || (res_p->y1 > res_p->y2)) {
124         union_ok = false;
125     }
126 
127     return union_ok;
128 }
129 /**
130  * Join two areas into a third which involves the other two
131  * @param res_p pointer to an area, the result will be stored here
132  * @param a1_p pointer to the first area
133  * @param a2_p pointer to the second area
134  */
_lv_area_join(lv_area_t * a_res_p,const lv_area_t * a1_p,const lv_area_t * a2_p)135 void _lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
136 {
137     a_res_p->x1 = LV_MATH_MIN(a1_p->x1, a2_p->x1);
138     a_res_p->y1 = LV_MATH_MIN(a1_p->y1, a2_p->y1);
139     a_res_p->x2 = LV_MATH_MAX(a1_p->x2, a2_p->x2);
140     a_res_p->y2 = LV_MATH_MAX(a1_p->y2, a2_p->y2);
141 }
142 
143 /**
144  * Check if a point is on an area
145  * @param a_p pointer to an area
146  * @param p_p pointer to a point
147  * @param radius radius of area (e.g. for rounded rectangle)
148  * @return false:the point is out of the area
149  */
_lv_area_is_point_on(const lv_area_t * a_p,const lv_point_t * p_p,lv_coord_t radius)150 bool _lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius)
151 {
152     /*First check the basic area*/
153     bool is_on_rect = false;
154     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))) {
155         is_on_rect = true;
156     }
157     if(!is_on_rect)
158         return false;
159     /*Now handle potential rounded rectangles*/
160     if(radius <= 0) {
161         /*No radius, it is within the rectangle*/
162         return true;
163     }
164     lv_coord_t w = lv_area_get_width(a_p) / 2;
165     lv_coord_t h = lv_area_get_height(a_p) / 2;
166     lv_coord_t max_radius = LV_MATH_MIN(w, h);
167     if(radius > max_radius)
168         radius = max_radius;
169 
170     /*Check if it's in one of the corners*/
171     lv_area_t corner_area;
172     /*Top left*/
173     corner_area.x1 = a_p->x1;
174     corner_area.x2 = a_p->x1 + radius;
175     corner_area.y1 = a_p->y1;
176     corner_area.y2 = a_p->y1 + radius;
177     if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
178         corner_area.x2 += radius;
179         corner_area.y2 += radius;
180         return lv_point_within_circle(&corner_area, p_p);
181     }
182     /*Bottom left*/
183     corner_area.y1 = a_p->y2 - radius;
184     corner_area.y2 = a_p->y2;
185     if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
186         corner_area.x2 += radius;
187         corner_area.y1 -= radius;
188         return lv_point_within_circle(&corner_area, p_p);
189     }
190     /*Bottom right*/
191     corner_area.x1 = a_p->x2 - radius;
192     corner_area.x2 = a_p->x2;
193     if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
194         corner_area.x1 -= radius;
195         corner_area.y1 -= radius;
196         return lv_point_within_circle(&corner_area, p_p);
197     }
198     /*Top right*/
199     corner_area.y1 = a_p->y1;
200     corner_area.y2 = a_p->y1 + radius;
201     if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
202         corner_area.x1 -= radius;
203         corner_area.y2 += radius;
204         return lv_point_within_circle(&corner_area, p_p);
205     }
206     /*Not within corners*/
207     return true;
208 }
209 
210 /**
211  * Check if two area has common parts
212  * @param a1_p pointer to an area.
213  * @param a2_p pointer to an other area
214  * @return false: a1_p and a2_p has no common parts
215  */
_lv_area_is_on(const lv_area_t * a1_p,const lv_area_t * a2_p)216 bool _lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p)
217 {
218     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)) {
219         return true;
220     }
221     else {
222         return false;
223     }
224 }
225 
226 /**
227  * Check if an area is fully on an other
228  * @param ain_p pointer to an area which could be in 'aholder_p'
229  * @param aholder_p pointer to an area which could involve 'ain_p'
230  * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
231  * @return true: `ain_p` is fully inside `aholder_p`
232  */
_lv_area_is_in(const lv_area_t * ain_p,const lv_area_t * aholder_p,lv_coord_t radius)233 bool _lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p, lv_coord_t radius)
234 {
235     bool is_in = false;
236 
237     if(ain_p->x1 >= aholder_p->x1 && ain_p->y1 >= aholder_p->y1 && ain_p->x2 <= aholder_p->x2 &&
238        ain_p->y2 <= aholder_p->y2) {
239         is_in = true;
240     }
241 
242     if(radius == 0) return is_in;
243 
244     /*Check if the corner points are inside the radius or not*/
245     lv_point_t p;
246 
247     p.x = ain_p->x1;
248     p.y = ain_p->y1;
249     if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
250 
251     p.x = ain_p->x2;
252     p.y = ain_p->y1;
253     if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
254 
255     p.x = ain_p->x1;
256     p.y = ain_p->y2;
257     if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
258 
259     p.x = ain_p->x2;
260     p.y = ain_p->y2;
261     if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
262 
263     return true;
264 }
265 
266 /**
267  * Align an area to an other
268  * @param base an are where the other will be aligned
269  * @param to_align the area to align
270  * @param align `LV_ALIGN_...`
271  * @param res x/y coordinates where `to_align` align area should be placed
272  */
_lv_area_align(const lv_area_t * base,const lv_area_t * to_align,lv_align_t align,lv_point_t * res)273 void _lv_area_align(const lv_area_t * base, const lv_area_t * to_align, lv_align_t align, lv_point_t * res)
274 {
275 
276     switch(align) {
277         case LV_ALIGN_CENTER:
278             res->x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
279             res->y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
280             break;
281 
282         case LV_ALIGN_IN_TOP_LEFT:
283             res->x = 0;
284             res->y = 0;
285             break;
286         case LV_ALIGN_IN_TOP_MID:
287             res->x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
288             res->y = 0;
289             break;
290 
291         case LV_ALIGN_IN_TOP_RIGHT:
292             res->x = lv_area_get_width(base) - lv_area_get_width(to_align);
293             res->y = 0;
294             break;
295 
296         case LV_ALIGN_IN_BOTTOM_LEFT:
297             res->x = 0;
298             res->y = lv_area_get_height(base) - lv_area_get_height(to_align);
299             break;
300         case LV_ALIGN_IN_BOTTOM_MID:
301             res->x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
302             res->y = lv_area_get_height(base) - lv_area_get_height(to_align);
303             break;
304 
305         case LV_ALIGN_IN_BOTTOM_RIGHT:
306             res->x = lv_area_get_width(base) - lv_area_get_width(to_align);
307             res->y = lv_area_get_height(base) - lv_area_get_height(to_align);
308             break;
309 
310         case LV_ALIGN_IN_LEFT_MID:
311             res->x = 0;
312             res->y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
313             break;
314 
315         case LV_ALIGN_IN_RIGHT_MID:
316             res->x = lv_area_get_width(base) - lv_area_get_width(to_align);
317             res->y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
318             break;
319 
320         case LV_ALIGN_OUT_TOP_LEFT:
321             res->x = 0;
322             res->y = -lv_area_get_height(to_align);
323             break;
324 
325         case LV_ALIGN_OUT_TOP_MID:
326             res->x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
327             res->y = -lv_area_get_height(to_align);
328             break;
329 
330         case LV_ALIGN_OUT_TOP_RIGHT:
331             res->x = lv_area_get_width(base) - lv_area_get_width(to_align);
332             res->y = -lv_area_get_height(to_align);
333             break;
334 
335         case LV_ALIGN_OUT_BOTTOM_LEFT:
336             res->x = 0;
337             res->y = lv_area_get_height(base);
338             break;
339 
340         case LV_ALIGN_OUT_BOTTOM_MID:
341             res->x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
342             res->y = lv_area_get_height(base);
343             break;
344 
345         case LV_ALIGN_OUT_BOTTOM_RIGHT:
346             res->x = lv_area_get_width(base) - lv_area_get_width(to_align);
347             res->y = lv_area_get_height(base);
348             break;
349 
350         case LV_ALIGN_OUT_LEFT_TOP:
351             res->x = -lv_area_get_width(to_align);
352             res->y = 0;
353             break;
354 
355         case LV_ALIGN_OUT_LEFT_MID:
356             res->x = -lv_area_get_width(to_align);
357             res->y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
358             break;
359 
360         case LV_ALIGN_OUT_LEFT_BOTTOM:
361             res->x = -lv_area_get_width(to_align);
362             res->y = lv_area_get_height(base) - lv_area_get_height(to_align);
363             break;
364 
365         case LV_ALIGN_OUT_RIGHT_TOP:
366             res->x = lv_area_get_width(base);
367             res->y = 0;
368             break;
369 
370         case LV_ALIGN_OUT_RIGHT_MID:
371             res->x = lv_area_get_width(base);
372             res->y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
373             break;
374 
375         case LV_ALIGN_OUT_RIGHT_BOTTOM:
376             res->x = lv_area_get_width(base);
377             res->y = lv_area_get_height(base) - lv_area_get_height(to_align);
378             break;
379     }
380 
381     res->x += base->x1;
382     res->y += base->y1;
383 }
384 
385 /**********************
386  *   STATIC FUNCTIONS
387  **********************/
388 
lv_point_within_circle(const lv_area_t * area,const lv_point_t * p)389 static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p)
390 {
391     lv_coord_t r = (area->x2 - area->x1) / 2;
392 
393     /* Circle center */
394     lv_coord_t cx = area->x1 + r;
395     lv_coord_t cy = area->y1 + r;
396 
397     /*Simplify the code by moving everything to (0, 0) */
398     lv_coord_t px = p->x - cx;
399     lv_coord_t py = p->y - cy;
400 
401     int32_t r_sqrd = r * r;
402     int32_t dist = (px * px) + (py * py);
403 
404     if(dist <= r_sqrd)
405         return true;
406     else
407         return false;
408 }
409