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 * Get resulting sub areas after removing the common parts of two areas from the first area
148 * @param res_p pointer to an array of areas with a count of 4, the resulting areas will be stored here
149 * @param a1_p pointer to the first area
150 * @param a2_p pointer to the second area
151 * @return number of results or -1 if no intersect
152 */
_lv_area_diff(lv_area_t * res_p,const lv_area_t * a1_p,const lv_area_t * a2_p)153 int8_t _lv_area_diff(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
154 {
155 /*Areas have no common parts*/
156 if(!_lv_area_is_on(a1_p, a2_p)) return -1;
157
158 /*No remaining areas after removing common parts*/
159 if(_lv_area_is_in(a1_p, a2_p, 0)) return 0;
160
161 /*Result counter*/
162 int8_t res_c = 0;
163
164 /*Get required information*/
165 lv_area_t n;
166 lv_coord_t a1_w = lv_area_get_width(a1_p) - 1;
167 lv_coord_t a1_h = lv_area_get_height(a1_p) - 1;
168
169 /*Compute top rectangle*/
170 lv_coord_t th = a2_p->y1 - a1_p->y1;
171 if(th > 0) {
172 n.x1 = a1_p->x1;
173 n.y1 = a1_p->y1;
174 n.x2 = a1_p->x2;
175 n.y2 = a1_p->y1 + th;
176 res_p[res_c++] = n;
177 }
178
179 /*Compute the bottom rectangle*/
180 lv_coord_t bh = a1_h - (a2_p->y2 - a1_p->y1);
181 if(bh > 0 && a2_p->y2 < a1_p->y2) {
182 n.x1 = a1_p->x1;
183 n.y1 = a2_p->y2;
184 n.x2 = a1_p->x2;
185 n.y2 = a2_p->y2 + bh;
186 res_p[res_c++] = n;
187 }
188
189 /*Compute side height*/
190 lv_coord_t y1 = a2_p->y1 > a1_p->y1 ? a2_p->y1 : a1_p->y1;
191 lv_coord_t y2 = a2_p->y2 < a1_p->y2 ? a2_p->y2 : a1_p->y2;
192 lv_coord_t sh = y2 - y1;
193
194 /*Compute the left rectangle*/
195 lv_coord_t lw = a2_p->x1 - a1_p->x1;
196 if(lw > 0 && sh > 0) {
197 n.x1 = a1_p->x1;
198 n.y1 = y1;
199 n.x2 = a1_p->x1 + lw;
200 n.y2 = y1 + sh;
201 res_p[res_c++] = n;
202 }
203
204 /*Compute the right rectangle*/
205 lv_coord_t rw = a1_w - (a2_p->x2 - a1_p->x1);
206 if(rw > 0) {
207 n.x1 = a2_p->x2;
208 n.y1 = y1;
209 n.x2 = a2_p->x2 + rw;
210 n.y2 = y1 + sh;
211 res_p[res_c++] = n;
212 }
213
214 //Return number of results
215 return res_c;
216 }
217
218 /**
219 * Join two areas into a third which involves the other two
220 * @param res_p pointer to an area, the result will be stored here
221 * @param a1_p pointer to the first area
222 * @param a2_p pointer to the second area
223 */
_lv_area_join(lv_area_t * a_res_p,const lv_area_t * a1_p,const lv_area_t * a2_p)224 void _lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
225 {
226 a_res_p->x1 = LV_MIN(a1_p->x1, a2_p->x1);
227 a_res_p->y1 = LV_MIN(a1_p->y1, a2_p->y1);
228 a_res_p->x2 = LV_MAX(a1_p->x2, a2_p->x2);
229 a_res_p->y2 = LV_MAX(a1_p->y2, a2_p->y2);
230 }
231
232 /**
233 * Check if a point is on an area
234 * @param a_p pointer to an area
235 * @param p_p pointer to a point
236 * @param radius radius of area (e.g. for rounded rectangle)
237 * @return false:the point is out of the area
238 */
_lv_area_is_point_on(const lv_area_t * a_p,const lv_point_t * p_p,lv_coord_t radius)239 bool _lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius)
240 {
241 /*First check the basic area*/
242 bool is_on_rect = false;
243 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))) {
244 is_on_rect = true;
245 }
246 if(!is_on_rect)
247 return false;
248 /*Now handle potential rounded rectangles*/
249 if(radius <= 0) {
250 /*No radius, it is within the rectangle*/
251 return true;
252 }
253 lv_coord_t w = lv_area_get_width(a_p) / 2;
254 lv_coord_t h = lv_area_get_height(a_p) / 2;
255 lv_coord_t max_radius = LV_MIN(w, h);
256 if(radius > max_radius)
257 radius = max_radius;
258
259 /*Check if it's in one of the corners*/
260 lv_area_t corner_area;
261 /*Top left*/
262 corner_area.x1 = a_p->x1;
263 corner_area.x2 = a_p->x1 + radius;
264 corner_area.y1 = a_p->y1;
265 corner_area.y2 = a_p->y1 + radius;
266 if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
267 corner_area.x2 += radius;
268 corner_area.y2 += radius;
269 return lv_point_within_circle(&corner_area, p_p);
270 }
271 /*Bottom left*/
272 corner_area.y1 = a_p->y2 - radius;
273 corner_area.y2 = a_p->y2;
274 if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
275 corner_area.x2 += radius;
276 corner_area.y1 -= radius;
277 return lv_point_within_circle(&corner_area, p_p);
278 }
279 /*Bottom right*/
280 corner_area.x1 = a_p->x2 - radius;
281 corner_area.x2 = a_p->x2;
282 if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
283 corner_area.x1 -= radius;
284 corner_area.y1 -= radius;
285 return lv_point_within_circle(&corner_area, p_p);
286 }
287 /*Top right*/
288 corner_area.y1 = a_p->y1;
289 corner_area.y2 = a_p->y1 + radius;
290 if(_lv_area_is_point_on(&corner_area, p_p, 0)) {
291 corner_area.x1 -= radius;
292 corner_area.y2 += radius;
293 return lv_point_within_circle(&corner_area, p_p);
294 }
295 /*Not within corners*/
296 return true;
297 }
298
299 /**
300 * Check if two area has common parts
301 * @param a1_p pointer to an area.
302 * @param a2_p pointer to an other area
303 * @return false: a1_p and a2_p has no common parts
304 */
_lv_area_is_on(const lv_area_t * a1_p,const lv_area_t * a2_p)305 bool _lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p)
306 {
307 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)) {
308 return true;
309 }
310 else {
311 return false;
312 }
313 }
314
315 /**
316 * Check if an area is fully on an other
317 * @param ain_p pointer to an area which could be in 'aholder_p'
318 * @param aholder_p pointer to an area which could involve 'ain_p'
319 * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
320 * @return true: `ain_p` is fully inside `aholder_p`
321 */
_lv_area_is_in(const lv_area_t * ain_p,const lv_area_t * aholder_p,lv_coord_t radius)322 bool _lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p, lv_coord_t radius)
323 {
324 bool is_in = false;
325
326 if(ain_p->x1 >= aholder_p->x1 && ain_p->y1 >= aholder_p->y1 && ain_p->x2 <= aholder_p->x2 &&
327 ain_p->y2 <= aholder_p->y2) {
328 is_in = true;
329 }
330
331 if(!is_in) return false;
332 if(radius == 0) return true;
333
334 /*Check if the corner points are inside the radius or not*/
335 lv_point_t p;
336
337 p.x = ain_p->x1;
338 p.y = ain_p->y1;
339 if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
340
341 p.x = ain_p->x2;
342 p.y = ain_p->y1;
343 if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
344
345 p.x = ain_p->x1;
346 p.y = ain_p->y2;
347 if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
348
349 p.x = ain_p->x2;
350 p.y = ain_p->y2;
351 if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
352
353 return true;
354 }
355
356 /**
357 * Check if an area is fully out of an other
358 * @param aout_p pointer to an area which could be in 'aholder_p'
359 * @param aholder_p pointer to an area which could involve 'ain_p'
360 * @param radius radius of `aholder_p` (e.g. for rounded rectangle)
361 * @return true: `aout_p` is fully outside `aholder_p`
362 */
_lv_area_is_out(const lv_area_t * aout_p,const lv_area_t * aholder_p,lv_coord_t radius)363 bool _lv_area_is_out(const lv_area_t * aout_p, const lv_area_t * aholder_p, lv_coord_t radius)
364 {
365 if(aout_p->x2 < aholder_p->x1 || aout_p->y2 < aholder_p->y1 || aout_p->x1 > aholder_p->x2 ||
366 aout_p->y1 > aholder_p->y2) {
367 return true;
368 }
369
370 if(radius == 0) return false;
371
372 /*Check if the corner points are outside the radius or not*/
373 lv_point_t p;
374
375 p.x = aout_p->x1;
376 p.y = aout_p->y1;
377 if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
378
379 p.x = aout_p->x2;
380 p.y = aout_p->y1;
381 if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
382
383 p.x = aout_p->x1;
384 p.y = aout_p->y2;
385 if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
386
387 p.x = aout_p->x2;
388 p.y = aout_p->y2;
389 if(_lv_area_is_point_on(aholder_p, &p, radius)) return false;
390
391 return true;
392 }
393
_lv_area_is_equal(const lv_area_t * a,const lv_area_t * b)394 bool _lv_area_is_equal(const lv_area_t * a, const lv_area_t * b)
395 {
396 return a->x1 == b->x1 && a->x2 == b->x2 && a->y1 == b->y1 && a->y2 == b->y2;
397 }
398
399 /**
400 * Align an area to an other
401 * @param base an are where the other will be aligned
402 * @param to_align the area to align
403 * @param align `LV_ALIGN_...`
404 * @param res x/y coordinates where `to_align` align area should be placed
405 */
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)406 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)
407 {
408
409 lv_coord_t x;
410 lv_coord_t y;
411 switch(align) {
412 case LV_ALIGN_CENTER:
413 x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
414 y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
415 break;
416
417 case LV_ALIGN_TOP_LEFT:
418 x = 0;
419 y = 0;
420 break;
421 case LV_ALIGN_TOP_MID:
422 x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
423 y = 0;
424 break;
425
426 case LV_ALIGN_TOP_RIGHT:
427 x = lv_area_get_width(base) - lv_area_get_width(to_align);
428 y = 0;
429 break;
430
431 case LV_ALIGN_BOTTOM_LEFT:
432 x = 0;
433 y = lv_area_get_height(base) - lv_area_get_height(to_align);
434 break;
435 case LV_ALIGN_BOTTOM_MID:
436 x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
437 y = lv_area_get_height(base) - lv_area_get_height(to_align);
438 break;
439
440 case LV_ALIGN_BOTTOM_RIGHT:
441 x = lv_area_get_width(base) - lv_area_get_width(to_align);
442 y = lv_area_get_height(base) - lv_area_get_height(to_align);
443 break;
444
445 case LV_ALIGN_LEFT_MID:
446 x = 0;
447 y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
448 break;
449
450 case LV_ALIGN_RIGHT_MID:
451 x = lv_area_get_width(base) - lv_area_get_width(to_align);
452 y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
453 break;
454
455 case LV_ALIGN_OUT_TOP_LEFT:
456 x = 0;
457 y = -lv_area_get_height(to_align);
458 break;
459
460 case LV_ALIGN_OUT_TOP_MID:
461 x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
462 y = -lv_area_get_height(to_align);
463 break;
464
465 case LV_ALIGN_OUT_TOP_RIGHT:
466 x = lv_area_get_width(base) - lv_area_get_width(to_align);
467 y = -lv_area_get_height(to_align);
468 break;
469
470 case LV_ALIGN_OUT_BOTTOM_LEFT:
471 x = 0;
472 y = lv_area_get_height(base);
473 break;
474
475 case LV_ALIGN_OUT_BOTTOM_MID:
476 x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2;
477 y = lv_area_get_height(base);
478 break;
479
480 case LV_ALIGN_OUT_BOTTOM_RIGHT:
481 x = lv_area_get_width(base) - lv_area_get_width(to_align);
482 y = lv_area_get_height(base);
483 break;
484
485 case LV_ALIGN_OUT_LEFT_TOP:
486 x = -lv_area_get_width(to_align);
487 y = 0;
488 break;
489
490 case LV_ALIGN_OUT_LEFT_MID:
491 x = -lv_area_get_width(to_align);
492 y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
493 break;
494
495 case LV_ALIGN_OUT_LEFT_BOTTOM:
496 x = -lv_area_get_width(to_align);
497 y = lv_area_get_height(base) - lv_area_get_height(to_align);
498 break;
499
500 case LV_ALIGN_OUT_RIGHT_TOP:
501 x = lv_area_get_width(base);
502 y = 0;
503 break;
504
505 case LV_ALIGN_OUT_RIGHT_MID:
506 x = lv_area_get_width(base);
507 y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2;
508 break;
509
510 case LV_ALIGN_OUT_RIGHT_BOTTOM:
511 x = lv_area_get_width(base);
512 y = lv_area_get_height(base) - lv_area_get_height(to_align);
513 break;
514 default:
515 x = 0;
516 y = 0;
517 break;
518 }
519
520 x += base->x1;
521 y += base->y1;
522
523 lv_coord_t w = lv_area_get_width(to_align);
524 lv_coord_t h = lv_area_get_height(to_align);
525 to_align->x1 = x + ofs_x;
526 to_align->y1 = y + ofs_y;
527 to_align->x2 = to_align->x1 + w - 1;
528 to_align->y2 = to_align->y1 + h - 1;
529 }
530
531 #define _LV_TRANSFORM_TRIGO_SHIFT 10
lv_point_transform(lv_point_t * p,int32_t angle,int32_t zoom,const lv_point_t * pivot)532 void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot)
533 {
534 if(angle == 0 && zoom == 256) {
535 return;
536 }
537
538 p->x -= pivot->x;
539 p->y -= pivot->y;
540
541 if(angle == 0) {
542 p->x = (((int32_t)(p->x) * zoom) >> 8) + pivot->x;
543 p->y = (((int32_t)(p->y) * zoom) >> 8) + pivot->y;
544 return;
545 }
546
547 static int32_t angle_prev = INT32_MIN;
548 static int32_t sinma;
549 static int32_t cosma;
550 if(angle_prev != angle) {
551 int32_t angle_limited = angle;
552 if(angle_limited > 3600) angle_limited -= 3600;
553 if(angle_limited < 0) angle_limited += 3600;
554
555 int32_t angle_low = angle_limited / 10;
556 int32_t angle_high = angle_low + 1;
557 int32_t angle_rem = angle_limited - (angle_low * 10);
558
559 int32_t s1 = lv_trigo_sin(angle_low);
560 int32_t s2 = lv_trigo_sin(angle_high);
561
562 int32_t c1 = lv_trigo_sin(angle_low + 90);
563 int32_t c2 = lv_trigo_sin(angle_high + 90);
564
565 sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
566 cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
567 sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
568 cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
569 angle_prev = angle;
570 }
571 int32_t x = p->x;
572 int32_t y = p->y;
573 if(zoom == 256) {
574 p->x = ((cosma * x - sinma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
575 p->y = ((sinma * x + cosma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
576 }
577 else {
578 p->x = (((cosma * x - sinma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->x;
579 p->y = (((sinma * x + cosma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->y;
580 }
581 }
582
583
584 /**********************
585 * STATIC FUNCTIONS
586 **********************/
587
lv_point_within_circle(const lv_area_t * area,const lv_point_t * p)588 static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p)
589 {
590 lv_coord_t r = (area->x2 - area->x1) / 2;
591
592 /*Circle center*/
593 lv_coord_t cx = area->x1 + r;
594 lv_coord_t cy = area->y1 + r;
595
596 /*Simplify the code by moving everything to (0, 0)*/
597 lv_coord_t px = p->x - cx;
598 lv_coord_t py = p->y - cy;
599
600 uint32_t r_sqrd = r * r;
601 uint32_t dist = (px * px) + (py * py);
602
603 if(dist <= r_sqrd)
604 return true;
605 else
606 return false;
607 }
608