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