1 /**
2  * @file lv_arc.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_arc.h"
10 #if LV_USE_ARC != 0
11 
12 #include "../lv_core/lv_group.h"
13 #include "../lv_core/lv_indev.h"
14 #include "../lv_misc/lv_debug.h"
15 #include "../lv_misc/lv_math.h"
16 #include "../lv_draw/lv_draw_arc.h"
17 #include "../lv_themes/lv_theme.h"
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 #define LV_OBJX_NAME "lv_arc"
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static lv_design_res_t lv_arc_design(lv_obj_t * arc, const lv_area_t * clip_area, lv_design_mode_t mode);
32 static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param);
33 static lv_style_list_t * lv_arc_get_style(lv_obj_t * arc, uint8_t part);
34 static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle, lv_arc_part_t part);
35 static void get_center(lv_obj_t * arc, lv_point_t * center, lv_coord_t * arc_r);
36 static void get_knob_area(lv_obj_t * arc, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area);
37 static void value_update(lv_obj_t * arc);
38 
39 /**********************
40  *  STATIC VARIABLES
41  **********************/
42 static lv_signal_cb_t ancestor_signal;
43 static lv_design_cb_t ancestor_design;
44 
45 /**********************
46  *      MACROS
47  **********************/
48 
49 /**********************
50  *   GLOBAL FUNCTIONS
51  **********************/
52 
53 /**
54  * Create a arc object
55  * @param par pointer to an object, it will be the parent of the new arc
56  * @param copy pointer to a arc object, if not NULL then the new object will be copied from it
57  * @return pointer to the created arc
58  */
lv_arc_create(lv_obj_t * par,const lv_obj_t * copy)59 lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy)
60 {
61 
62     LV_LOG_TRACE("arc create started");
63 
64     /*Create the ancestor of arc*/
65     lv_obj_t * arc = lv_obj_create(par, copy);
66     LV_ASSERT_MEM(arc);
67     if(arc == NULL) return NULL;
68 
69     /*Allocate the arc type specific extended data*/
70     lv_arc_ext_t * ext = lv_obj_allocate_ext_attr(arc, sizeof(lv_arc_ext_t));
71     LV_ASSERT_MEM(ext);
72     if(ext == NULL) {
73         lv_obj_del(arc);
74         return NULL;
75     }
76 
77     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(arc);
78     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(arc);
79 
80     /*Initialize the allocated 'ext' */
81     ext->rotation_angle = 0;
82     ext->bg_angle_start = 135;
83     ext->bg_angle_end   = 45;
84     ext->arc_angle_start = 135;
85     ext->arc_angle_end   = 270;
86     ext->type = LV_ARC_TYPE_NORMAL;
87     ext->cur_value = -1;
88     ext->min_value = 0;
89     ext->max_value = 100;
90     ext->dragging = false;
91     ext->adjustable = false;
92     ext->chg_rate = 540;
93     ext->last_tick = lv_tick_get();
94     ext->last_angle = ext->arc_angle_end;
95     lv_style_list_init(&ext->style_arc);
96     lv_style_list_init(&ext->style_knob);
97 
98     lv_obj_set_size(arc, LV_DPI, LV_DPI);
99 
100     /*The signal and design functions are not copied so set them here*/
101     lv_obj_set_signal_cb(arc, lv_arc_signal);
102     lv_obj_set_design_cb(arc, lv_arc_design);
103 
104     /*Init the new arc arc*/
105     if(copy == NULL) {
106         lv_obj_set_click(arc, true);
107         lv_obj_add_protect(arc, LV_PROTECT_PRESS_LOST);
108         lv_obj_set_ext_click_area(arc, LV_DPI / 10, LV_DPI / 10, LV_DPI / 10, LV_DPI / 10);
109         lv_arc_set_value(arc, ext->min_value);
110         lv_theme_apply(arc, LV_THEME_ARC);
111     }
112     /*Copy an existing arc*/
113     else {
114         lv_arc_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
115         ext->arc_angle_start = copy_ext->arc_angle_start;
116         ext->arc_angle_end   = copy_ext->arc_angle_end;
117         ext->bg_angle_start  = copy_ext->bg_angle_start;
118         ext->bg_angle_end    = copy_ext->bg_angle_end;
119         ext->type = copy_ext->type;
120         ext->cur_value = copy_ext->cur_value;
121         ext->min_value = copy_ext->min_value;
122         ext->max_value = copy_ext->max_value;
123         ext->dragging = copy_ext->dragging;
124         ext->adjustable = copy_ext->adjustable;
125         ext->chg_rate = copy_ext->chg_rate;
126         ext->last_tick = copy_ext->last_tick;
127         ext->last_angle = copy_ext->last_angle;
128         lv_style_list_copy(&ext->style_knob, &copy_ext->style_knob);
129         lv_style_list_copy(&ext->style_arc, &copy_ext->style_arc);
130 
131         /*Refresh the style with new signal function*/
132         lv_obj_refresh_style(arc, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
133     }
134 
135     LV_LOG_INFO("arc created");
136 
137     return arc;
138 }
139 
140 /*======================
141  * Add/remove functions
142  *=====================*/
143 
144 /*
145  * New object specific "add" or "remove" functions come here
146  */
147 
148 /*=====================
149  * Setter functions
150  *====================*/
151 
152 /**
153  * Set the start angle of an arc. 0 deg: right, 90 bottom, etc.
154  * @param arc pointer to an arc object
155  * @param start the start angle [0..360]
156  */
lv_arc_set_start_angle(lv_obj_t * arc,uint16_t start)157 void lv_arc_set_start_angle(lv_obj_t * arc, uint16_t start)
158 {
159     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
160 
161     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
162 
163     if(start > 360) start -= 360;
164 
165     /*Too large move, the whole arc need to be invalidated anyway*/
166     if(LV_MATH_ABS(start - ext->arc_angle_start) >= 180) {
167         lv_obj_invalidate(arc);
168     }
169     /*Only a smaller incremental move*/
170     else if(ext->arc_angle_start > ext->arc_angle_end && start > ext->arc_angle_end) {
171         inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_start, start), LV_MATH_MAX(ext->arc_angle_start, start),
172                      LV_ARC_PART_INDIC);
173     }
174     /*Only a smaller incremental move*/
175     else  if(ext->arc_angle_start < ext->arc_angle_end && start < ext->arc_angle_end) {
176         inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_start, start), LV_MATH_MAX(ext->arc_angle_start, start),
177                      LV_ARC_PART_INDIC);
178     }
179     /*Crossing the start angle makes the whole arc change*/
180     else {
181         lv_obj_invalidate(arc);
182     }
183 
184     ext->arc_angle_start = start;
185 }
186 
187 /**
188  * Set the start angle of an arc. 0 deg: right, 90 bottom, etc.
189  * @param arc pointer to an arc object
190  * @param start the start angle [0..360]
191  */
lv_arc_set_end_angle(lv_obj_t * arc,uint16_t end)192 void lv_arc_set_end_angle(lv_obj_t * arc, uint16_t end)
193 {
194     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
195 
196     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
197 
198     if(end > (ext->arc_angle_start + 360)) end = ext->arc_angle_start + 360;
199 
200     /*Too large move, the whole arc need to be invalidated anyway*/
201     if(LV_MATH_ABS(end - ext->arc_angle_end) >= 180) {
202         lv_obj_invalidate(arc);
203     }
204     /*Only a smaller incremental move*/
205     else if(ext->arc_angle_end > ext->arc_angle_start && end > ext->arc_angle_start) {
206         inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_end, end), LV_MATH_MAX(ext->arc_angle_end, end), LV_ARC_PART_INDIC);
207     }
208     /*Only a smaller incremental move*/
209     else  if(ext->arc_angle_end < ext->arc_angle_start && end < ext->arc_angle_start) {
210         inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_end, end), LV_MATH_MAX(ext->arc_angle_end, end), LV_ARC_PART_INDIC);
211     }
212     /*Crossing the end angle makes the whole arc change*/
213     else {
214         lv_obj_invalidate(arc);
215     }
216 
217     ext->arc_angle_end = end;
218 }
219 
220 
221 /**
222  * Set the start and end angles
223  * @param arc pointer to an arc object
224  * @param start the start angle
225  * @param end the end angle
226  */
lv_arc_set_angles(lv_obj_t * arc,uint16_t start,uint16_t end)227 void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end)
228 {
229     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
230 
231     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
232 
233     if(start > 360) start -= 360;
234     if(end > (start + 360)) end = start + 360;
235 
236     inv_arc_area(arc, ext->arc_angle_start, ext->arc_angle_end, LV_ARC_PART_INDIC);
237 
238     ext->arc_angle_start = start;
239     ext->arc_angle_end = end;
240 
241     inv_arc_area(arc, ext->arc_angle_start, ext->arc_angle_end, LV_ARC_PART_INDIC);
242 }
243 
244 /**
245  * Set the start angle of an arc background. 0 deg: right, 90 bottom, etc.
246  * @param arc pointer to an arc object
247  * @param start the start angle
248  */
lv_arc_set_bg_start_angle(lv_obj_t * arc,uint16_t start)249 void lv_arc_set_bg_start_angle(lv_obj_t * arc, uint16_t start)
250 {
251     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
252 
253     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
254 
255     if(start > 360) start -= 360;
256 
257     /*Too large move, the whole arc need to be invalidated anyway*/
258     if(LV_MATH_ABS(start - ext->bg_angle_start) >= 180) {
259         lv_obj_invalidate(arc);
260     }
261     /*Only a smaller incremental move*/
262     else if(ext->bg_angle_start > ext->bg_angle_end && start > ext->bg_angle_end) {
263         inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_start, start), LV_MATH_MAX(ext->bg_angle_start, start), LV_ARC_PART_BG);
264     }
265     /*Only a smaller incremental move*/
266     else  if(ext->bg_angle_start < ext->bg_angle_end && start < ext->bg_angle_end) {
267         inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_start, start), LV_MATH_MAX(ext->bg_angle_start, start), LV_ARC_PART_BG);
268     }
269     /*Crossing the start angle makes the whole arc change*/
270     else {
271         lv_obj_invalidate(arc);
272     }
273 
274     ext->bg_angle_start = start;
275 }
276 
277 /**
278  * Set the start angle of an arc background. 0 deg: right, 90 bottom etc.
279  * @param arc pointer to an arc object
280  * @param end the end angle
281  */
lv_arc_set_bg_end_angle(lv_obj_t * arc,uint16_t end)282 void lv_arc_set_bg_end_angle(lv_obj_t * arc, uint16_t end)
283 {
284     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
285 
286     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
287 
288     if(end > (ext->bg_angle_start + 360)) end = ext->bg_angle_start + 360;
289 
290     /*Too large move, the whole arc need to be invalidated anyway*/
291     if(LV_MATH_ABS(end - ext->bg_angle_end) >= 180) {
292         lv_obj_invalidate(arc);
293     }
294     /*Only a smaller incremental move*/
295     else if(ext->bg_angle_end > ext->bg_angle_start && end > ext->bg_angle_start) {
296         inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_end, end), LV_MATH_MAX(ext->bg_angle_end, end), LV_ARC_PART_BG);
297     }
298     /*Only a smaller incremental move*/
299     else  if(ext->bg_angle_end < ext->bg_angle_start && end < ext->bg_angle_start) {
300         inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_end, end), LV_MATH_MAX(ext->bg_angle_end, end), LV_ARC_PART_BG);
301     }
302     /*Crossing the end angle makes the whole arc change*/
303     else {
304         lv_obj_invalidate(arc);
305     }
306 
307     ext->bg_angle_end = end;
308 }
309 
310 /**
311  * Set the start and end angles of the arc background
312  * @param arc pointer to an arc object
313  * @param start the start angle
314  * @param end the end angle
315  */
lv_arc_set_bg_angles(lv_obj_t * arc,uint16_t start,uint16_t end)316 void lv_arc_set_bg_angles(lv_obj_t * arc, uint16_t start, uint16_t end)
317 {
318     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
319 
320     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
321 
322     if(start > 360) start -= 360;
323     if(end > (start + 360)) end = start + 360;
324 
325     inv_arc_area(arc, ext->bg_angle_start, ext->bg_angle_end, LV_ARC_PART_BG);
326 
327     ext->bg_angle_start = start;
328     ext->bg_angle_end = end;
329 
330     inv_arc_area(arc, ext->bg_angle_start, ext->bg_angle_end, LV_ARC_PART_BG);
331 }
332 
333 /**
334  * Set the rotation for the whole arc
335  * @param arc pointer to an arc object
336  * @param rotation_angle rotation angle
337  */
lv_arc_set_rotation(lv_obj_t * arc,uint16_t rotation_angle)338 void lv_arc_set_rotation(lv_obj_t * arc, uint16_t rotation_angle)
339 {
340     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
341 
342     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
343 
344     ext->rotation_angle = rotation_angle;
345 
346     lv_obj_invalidate(arc);
347 }
348 
349 
350 /**
351  * Set the type of arc.
352  * @param arc pointer to arc object
353  * @param type arc type
354  */
lv_arc_set_type(lv_obj_t * arc,lv_arc_type_t type)355 void lv_arc_set_type(lv_obj_t * arc, lv_arc_type_t type)
356 {
357     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
358 
359     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
360     int16_t val = ext->cur_value;
361 
362     ext->type = type;
363     ext->cur_value = -1; /** Force set_value handling*/
364 
365     int16_t bg_midpoint, bg_end = ext->bg_angle_end;
366     if(ext->bg_angle_end < ext->bg_angle_start) bg_end = ext->bg_angle_end + 360;
367 
368     switch(ext->type) {
369         case LV_ARC_TYPE_SYMMETRIC:
370             bg_midpoint = (ext->bg_angle_start + bg_end) / 2;
371             lv_arc_set_start_angle(arc, bg_midpoint);
372             lv_arc_set_end_angle(arc, bg_midpoint);
373             break;
374         case LV_ARC_TYPE_REVERSE:
375             lv_arc_set_end_angle(arc, ext->bg_angle_end);
376             break;
377         default: /** LV_ARC_TYPE_NORMAL*/
378             lv_arc_set_start_angle(arc, ext->bg_angle_start);
379     }
380 
381     lv_arc_set_value(arc, val);
382 }
383 
384 /**
385  * Set a new value on the arc
386  * @param arc pointer to a arc object
387  * @param value new value
388  */
lv_arc_set_value(lv_obj_t * arc,int16_t value)389 void lv_arc_set_value(lv_obj_t * arc, int16_t value)
390 {
391     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
392 
393     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
394     if(ext->cur_value == value) return;
395 
396     int16_t new_value;
397     new_value = value > ext->max_value ? ext->max_value : value;
398     new_value = new_value < ext->min_value ? ext->min_value : new_value;
399 
400     if(ext->cur_value == new_value) return;
401     ext->cur_value = new_value;
402 
403     value_update(arc);
404 }
405 
406 /**
407  * Set minimum and the maximum values of a arc
408  * @param arc pointer to the arc object
409  * @param min minimum value
410  * @param max maximum value
411  */
lv_arc_set_range(lv_obj_t * arc,int16_t min,int16_t max)412 void lv_arc_set_range(lv_obj_t * arc, int16_t min, int16_t max)
413 {
414     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
415 
416     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
417     if(ext->min_value == min && ext->max_value == max) return;
418 
419     ext->min_value = min;
420     ext->max_value = max;
421 
422     if(ext->cur_value < min) {
423         ext->cur_value = min;
424     }
425     if(ext->cur_value > max) {
426         ext->cur_value = max;
427     }
428 
429     value_update(arc); /* value has changed relative to the new range */
430 }
431 
432 /**
433  * Set the threshold of arc knob increments
434  * position.
435  * @param arc pointer to a arc object
436  * @param threshold increment threshold
437  */
lv_arc_set_chg_rate(lv_obj_t * arc,uint16_t rate)438 void lv_arc_set_chg_rate(lv_obj_t * arc, uint16_t rate)
439 {
440     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
441 
442     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
443     ext->chg_rate = rate;
444 }
445 
446 /**
447  * Set whether the arc is adjustable.
448  * @param arc pointer to a arc object
449  * @param adjustable whether the arc has a knob that can be dragged
450  */
lv_arc_set_adjustable(lv_obj_t * arc,bool adjustable)451 void lv_arc_set_adjustable(lv_obj_t * arc, bool adjustable)
452 {
453     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
454 
455     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
456     if(ext->adjustable == adjustable)
457         return;
458 
459     ext->adjustable = adjustable;
460     if(!adjustable)
461         ext->dragging = false;
462     lv_obj_invalidate(arc);
463 }
464 
465 /*=====================
466  * Getter functions
467  *====================*/
468 
469 /**
470  * Get the start angle of an arc.
471  * @param arc pointer to an arc object
472  * @return the start angle [0..360]
473  */
lv_arc_get_angle_start(lv_obj_t * arc)474 uint16_t lv_arc_get_angle_start(lv_obj_t * arc)
475 {
476     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
477 
478     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
479 
480     return ext->arc_angle_start;
481 }
482 
483 /**
484  * Get the end angle of an arc.
485  * @param arc pointer to an arc object
486  * @return the end angle [0..360]
487  */
lv_arc_get_angle_end(lv_obj_t * arc)488 uint16_t lv_arc_get_angle_end(lv_obj_t * arc)
489 {
490     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
491 
492     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
493 
494     return ext->arc_angle_end;
495 }
496 
497 /**
498  * Get the start angle of an arc background.
499  * @param arc pointer to an arc object
500  * @return the start angle [0..360]
501  */
lv_arc_get_bg_angle_start(lv_obj_t * arc)502 uint16_t lv_arc_get_bg_angle_start(lv_obj_t * arc)
503 {
504     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
505 
506     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
507 
508     return ext->bg_angle_start;
509 }
510 
511 /**
512  * Get the end angle of an arc background.
513  * @param arc pointer to an arc object
514  * @return the end angle [0..360]
515  */
lv_arc_get_bg_angle_end(lv_obj_t * arc)516 uint16_t lv_arc_get_bg_angle_end(lv_obj_t * arc)
517 {
518     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
519 
520     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
521 
522     return ext->bg_angle_end;
523 }
524 
525 
526 /**
527  * Get the value of a arc
528  * @param arc pointer to a arc object
529  * @return the value of the arc
530  */
lv_arc_get_value(const lv_obj_t * arc)531 int16_t lv_arc_get_value(const lv_obj_t * arc)
532 {
533     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
534 
535     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
536     return ext->cur_value;
537 }
538 
539 /**
540  * Get the minimum value of a arc
541  * @param arc pointer to a arc object
542  * @return the minimum value of the arc
543  */
lv_arc_get_min_value(const lv_obj_t * arc)544 int16_t lv_arc_get_min_value(const lv_obj_t * arc)
545 {
546     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
547 
548     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
549     return ext->min_value;
550 }
551 
552 /**
553  * Get the maximum value of a arc
554  * @param arc pointer to a arc object
555  * @return the maximum value of the arc
556  */
lv_arc_get_max_value(const lv_obj_t * arc)557 int16_t lv_arc_get_max_value(const lv_obj_t * arc)
558 {
559     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
560 
561     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
562     return ext->max_value;
563 }
564 
565 /**
566  * Give the arc is being dragged or not
567  * @param arc pointer to a arc object
568  * @return true: drag in progress false: not dragged
569  */
lv_arc_is_dragged(const lv_obj_t * arc)570 bool lv_arc_is_dragged(const lv_obj_t * arc)
571 {
572     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
573 
574     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
575     return ext->dragging;
576 }
577 
578 /**
579  * Get whether the arc is type or not.
580  * @param arc pointer to a arc object
581  * @return arc type
582  */
lv_arc_get_type(const lv_obj_t * arc)583 lv_arc_type_t lv_arc_get_type(const lv_obj_t * arc)
584 {
585     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
586 
587     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
588     return ext->type;
589 }
590 
591 /**
592  * Get whether the arc is adjustable.
593  * @param arc pointer to a arc object
594  * @return whether the arc has a knob that can be dragged
595  */
lv_arc_get_adjustable(lv_obj_t * arc)596 bool lv_arc_get_adjustable(lv_obj_t * arc)
597 {
598     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
599 
600     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
601     return ext->adjustable;
602 }
603 
604 /*=====================
605  * Other functions
606  *====================*/
607 
608 /*
609  * New object specific "other" functions come here
610  */
611 
612 /**********************
613  *   STATIC FUNCTIONS
614  **********************/
615 
616 /**
617  * Handle the drawing related tasks of the arcs
618  * @param arc pointer to an object
619  * @param clip_area the object will be drawn only in this area
620  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
621  *                                  (return 'true' if yes)
622  *             LV_DESIGN_DRAW: draw the object (always return 'true')
623  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
624  * @param return an element of `lv_design_res_t`
625  */
lv_arc_design(lv_obj_t * arc,const lv_area_t * clip_area,lv_design_mode_t mode)626 static lv_design_res_t lv_arc_design(lv_obj_t * arc, const lv_area_t * clip_area, lv_design_mode_t mode)
627 {
628     /*Return false if the object is not covers the mask_p area*/
629     if(mode == LV_DESIGN_COVER_CHK) {
630         return LV_DESIGN_RES_NOT_COVER;
631     }
632     /*Draw the object*/
633     else if(mode == LV_DESIGN_DRAW_MAIN) {
634         lv_arc_ext_t * ext       = lv_obj_get_ext_attr(arc);
635 
636         lv_draw_rect_dsc_t bg_dsc;
637         lv_draw_rect_dsc_init(&bg_dsc);
638         lv_obj_init_draw_rect_dsc(arc, LV_ARC_PART_BG, &bg_dsc);
639 
640         lv_draw_rect(&arc->coords, clip_area, &bg_dsc);
641 
642         lv_point_t center;
643         lv_coord_t arc_r;
644         get_center(arc, &center, &arc_r);
645 
646         /*Draw the background arc*/
647         lv_draw_line_dsc_t arc_dsc;
648         if(arc_r > 0) {
649             lv_draw_line_dsc_init(&arc_dsc);
650             lv_obj_init_draw_line_dsc(arc, LV_ARC_PART_BG, &arc_dsc);
651 
652             lv_draw_arc(center.x, center.y, arc_r, ext->bg_angle_start + ext->rotation_angle,
653                         ext->bg_angle_end + ext->rotation_angle, clip_area,
654                         &arc_dsc);
655         }
656 
657 
658         /*make the indicator arc smaller or larger according to its greatest padding value*/
659         lv_coord_t left_indic = lv_obj_get_style_pad_left(arc, LV_ARC_PART_INDIC);
660         lv_coord_t right_indic = lv_obj_get_style_pad_right(arc, LV_ARC_PART_INDIC);
661         lv_coord_t top_indic = lv_obj_get_style_pad_top(arc, LV_ARC_PART_INDIC);
662         lv_coord_t bottom_indic = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_INDIC);
663         lv_coord_t indic_r = arc_r - LV_MATH_MAX4(left_indic, right_indic, top_indic, bottom_indic);
664 
665         if(indic_r > 0) {
666             lv_draw_line_dsc_init(&arc_dsc);
667             lv_obj_init_draw_line_dsc(arc, LV_ARC_PART_INDIC, &arc_dsc);
668 
669             lv_draw_arc(center.x, center.y, indic_r, ext->arc_angle_start + ext->rotation_angle,
670                         ext->arc_angle_end + ext->rotation_angle, clip_area,
671                         &arc_dsc);
672         }
673 
674         if(ext->adjustable) {
675             lv_area_t knob_area;
676             get_knob_area(arc, &center, arc_r, &knob_area);
677 
678             lv_draw_rect_dsc_t knob_rect_dsc;
679             lv_draw_rect_dsc_init(&knob_rect_dsc);
680             lv_obj_init_draw_rect_dsc(arc, LV_ARC_PART_KNOB, &knob_rect_dsc);
681 
682             lv_draw_rect(&knob_area, clip_area, &knob_rect_dsc);
683         }
684 
685     }
686     /*Post draw when the children are drawn*/
687     else if(mode == LV_DESIGN_DRAW_POST) {
688     }
689 
690     return LV_DESIGN_RES_OK;
691 }
692 
693 /**
694  * Signal function of the arc
695  * @param arc pointer to a arc object
696  * @param sign a signal type from lv_signal_t enum
697  * @param param pointer to a signal specific variable
698  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
699  */
lv_arc_signal(lv_obj_t * arc,lv_signal_t sign,void * param)700 static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param)
701 {
702     lv_res_t res;
703     if(sign == LV_SIGNAL_GET_STYLE) {
704         lv_get_style_info_t * info = param;
705         info->result = lv_arc_get_style(arc, info->part);
706         if(info->result != NULL) return LV_RES_OK;
707         else return ancestor_signal(arc, sign, param);
708     }
709 
710     /* Include the ancient signal function */
711     res = ancestor_signal(arc, sign, param);
712     if(res != LV_RES_OK) return res;
713 
714     if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
715 
716     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
717 
718     if(sign == LV_SIGNAL_PRESSING) {
719         /* Only adjustable arcs can be dragged */
720         if(!ext->adjustable) return res;
721 
722         lv_indev_t * indev = lv_indev_get_act();
723         if(indev == NULL) return res;
724 
725         /*Handle only pointers here*/
726         lv_indev_type_t indev_type = lv_indev_get_type(indev);
727         if(indev_type != LV_INDEV_TYPE_POINTER) return res;
728 
729         lv_point_t p;
730         lv_indev_get_point(indev, &p);
731 
732         /*Make point relative to the arc's center*/
733         lv_point_t center;
734         lv_coord_t r;
735         get_center(arc, &center, &r);
736 
737         p.x -=  center.x;
738         p.y -=  center.y;
739 
740         /*Enter dragging mode if pressed out of the knob*/
741         if(ext->dragging == false) {
742             lv_coord_t indic_width = lv_obj_get_style_line_width(arc, LV_ARC_PART_INDIC);
743             r -=  indic_width;
744             r -= r / 2; /*Add some more sensitive area*/
745             if(p.x * p.x + p.y * p.y > r * r) {
746                 ext->dragging = true;
747                 ext->last_tick = lv_tick_get(); /*Capture timestamp at dragging start*/
748             }
749         }
750 
751         /*It must be in "dragging" mode to turn the arc*/
752         if(ext->dragging == false) return res;
753 
754         /*Calculate the angle of the pressed point*/
755         int16_t angle;
756         int16_t bg_end = ext->bg_angle_end;
757         if(ext->bg_angle_end < ext->bg_angle_start) {
758             bg_end = ext->bg_angle_end + 360;
759         }
760 
761         angle = 360 - _lv_atan2(p.x, p.y) + 90; /*Some transformation is required*/
762         angle -= ext->rotation_angle;
763         if(angle < ext->bg_angle_start) angle = ext->bg_angle_start;
764         if(angle > bg_end) angle = bg_end;
765 
766         /*Calculate the slew rate limited angle based on change rate (degrees/sec)*/
767         int16_t delta_angle = angle - ext->last_angle;
768         uint32_t delta_tick = lv_tick_elaps(ext->last_tick);
769         int16_t delta_angle_max = (ext->chg_rate * delta_tick) / 1000;
770 
771         if(delta_angle > delta_angle_max) {
772             delta_angle = delta_angle_max;
773         }
774         else if(delta_angle < -delta_angle_max) {
775             delta_angle = -delta_angle_max;
776         }
777 
778         angle = ext->last_angle + delta_angle; /*Apply the limited angle change*/
779 
780         /*Rounding for symmetry*/
781         int32_t round = ((bg_end - ext->bg_angle_start) * 8) / (ext->max_value - ext->min_value);
782         round = (round + 4) >> 4;
783         angle += round;
784 
785         /*Set the new value*/
786         int16_t old_value = ext->cur_value;
787         int16_t new_value = _lv_map(angle, ext->bg_angle_start, bg_end, ext->min_value, ext->max_value);
788         if(new_value != lv_arc_get_value(arc)) {
789             ext->last_tick = lv_tick_get(); /*Cache timestamp for the next iteration*/
790             lv_arc_set_value(arc, new_value); /*set_value caches the last_angle for the next iteration*/
791             if(new_value != old_value) {
792                 res = lv_event_send(arc, LV_EVENT_VALUE_CHANGED, NULL);
793                 if(res != LV_RES_OK) return res;
794             }
795         }
796 
797         /*Don1't let the elapsed time to big while sitting on an end point*/
798         if(new_value == ext->min_value || new_value == ext->max_value) {
799             ext->last_tick = lv_tick_get(); /*Cache timestamp for the next iteration*/
800         }
801     }
802     else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
803         ext->dragging = false;
804 
805 #if LV_USE_GROUP
806         /*Leave edit mode if released. (No need to wait for LONG_PRESS) */
807         lv_group_t * g             = lv_obj_get_group(arc);
808         bool editing               = lv_group_get_editing(g);
809         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
810         if(indev_type == LV_INDEV_TYPE_ENCODER) {
811             if(editing) lv_group_set_editing(g, false);
812         }
813 #endif
814 
815     }
816     else if(sign == LV_SIGNAL_CONTROL) {
817         if(!ext->adjustable) return res;
818 
819         char c = *((char *)param);
820 
821         int16_t old_value = ext->cur_value;
822         if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
823             lv_arc_set_value(arc, lv_arc_get_value(arc) + 1);
824         }
825         else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
826             lv_arc_set_value(arc, lv_arc_get_value(arc) - 1);
827         }
828 
829         if(old_value != ext->cur_value) {
830             res = lv_event_send(arc, LV_EVENT_VALUE_CHANGED, NULL);
831             if(res != LV_RES_OK) return res;
832         }
833     }
834     else if(sign == LV_SIGNAL_CLEANUP) {
835         lv_obj_clean_style_list(arc, LV_ARC_PART_KNOB);
836         lv_obj_clean_style_list(arc, LV_ARC_PART_INDIC);
837     }
838 
839     return res;
840 }
841 
842 /**
843  * Get the style descriptor of a part of the object
844  * @param arc pointer the object
845  * @param part the part of the object. (LV_ARC_PART_...)
846  * @return pointer to the style descriptor of the specified part
847  */
lv_arc_get_style(lv_obj_t * arc,uint8_t part)848 static lv_style_list_t * lv_arc_get_style(lv_obj_t * arc, uint8_t part)
849 {
850     LV_ASSERT_OBJ(arc, LV_OBJX_NAME);
851 
852     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
853 
854     lv_style_list_t * style_dsc_p;
855 
856     switch(part) {
857         case LV_ARC_PART_BG:
858             style_dsc_p = &arc->style_list;
859             break;
860         case LV_ARC_PART_INDIC:
861             style_dsc_p = &ext->style_arc;
862             break;
863         case LV_ARC_PART_KNOB:
864             style_dsc_p = &ext->style_knob;
865             break;
866         default:
867             style_dsc_p = NULL;
868     }
869 
870     return style_dsc_p;
871 }
872 
inv_arc_area(lv_obj_t * arc,uint16_t start_angle,uint16_t end_angle,lv_arc_part_t part)873 static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle, lv_arc_part_t part)
874 {
875     /*Skip this complicated invalidation if the arc is not visible*/
876     if(lv_obj_is_visible(arc) == false) return;
877 
878     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
879 
880     start_angle += ext->rotation_angle;
881     end_angle += ext->rotation_angle;
882 
883     if(start_angle >= 360) start_angle -= 360;
884     if(end_angle >= 360) end_angle -= 360;
885 
886     uint8_t start_quarter = start_angle / 90;
887     uint8_t end_quarter = end_angle / 90;
888 
889     lv_coord_t left = lv_obj_get_style_pad_left(arc, LV_ARC_PART_BG);
890     lv_coord_t right = lv_obj_get_style_pad_right(arc, LV_ARC_PART_BG);
891     lv_coord_t top = lv_obj_get_style_pad_top(arc, LV_ARC_PART_BG);
892     lv_coord_t bottom = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_BG);
893     lv_coord_t rout       = (LV_MATH_MIN(lv_obj_get_width(arc) - left - right, lv_obj_get_height(arc) - top - bottom)) / 2;
894     lv_coord_t x       = arc->coords.x1 + rout + left;
895     lv_coord_t y       = arc->coords.y1 + rout + top;
896     lv_style_int_t w = lv_obj_get_style_line_width(arc, part);
897     lv_style_int_t rounded = lv_obj_get_style_line_rounded(arc, part);
898     lv_coord_t rin       = rout - w;
899     lv_coord_t extra_area = 0;
900 
901     extra_area = rounded ? w / 2 + 2 : 0;
902 
903     if(part == LV_ARC_PART_INDIC && lv_style_list_get_style(&ext->style_knob, 0) != NULL) {
904         lv_coord_t knob_extra_size = lv_obj_get_draw_rect_ext_pad_size(arc, LV_ARC_PART_KNOB);
905 
906         lv_coord_t knob_left = lv_obj_get_style_pad_left(arc, LV_ARC_PART_KNOB);
907         lv_coord_t knob_right = lv_obj_get_style_pad_right(arc, LV_ARC_PART_KNOB);
908         lv_coord_t knob_top = lv_obj_get_style_pad_top(arc, LV_ARC_PART_KNOB);
909         lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_KNOB);
910 
911         knob_extra_size += LV_MATH_MAX4(knob_left, knob_right, knob_top, knob_bottom);
912 
913         extra_area = LV_MATH_MAX(extra_area, w / 2 + 2 + knob_extra_size);
914 
915     }
916 
917     lv_area_t inv_area;
918 
919     if(start_quarter == end_quarter && start_angle <= end_angle) {
920         if(start_quarter == 0) {
921             inv_area.y1 = y + ((_lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
922             inv_area.x2 = x + ((_lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
923 
924             inv_area.y2 = y + ((_lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
925             inv_area.x1 = x + ((_lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
926 
927             lv_obj_invalidate_area(arc, &inv_area);
928         }
929         else if(start_quarter == 1) {
930             inv_area.y2 = y + ((_lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
931             inv_area.x2 = x + ((_lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
932 
933             inv_area.y1 = y + ((_lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
934             inv_area.x1 = x + ((_lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
935 
936             lv_obj_invalidate_area(arc, &inv_area);
937         }
938         else if(start_quarter == 2) {
939             inv_area.x1 = x + ((_lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
940             inv_area.y2 = y + ((_lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
941 
942             inv_area.y1 = y + ((_lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
943             inv_area.x2 = x + ((_lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
944 
945             lv_obj_invalidate_area(arc, &inv_area);
946         }
947         else if(start_quarter == 3) {
948             /*Small arc here*/
949             inv_area.x1 = x + ((_lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
950             inv_area.y1 = y + ((_lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
951 
952             inv_area.x2 = x + ((_lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
953             inv_area.y2 = y + ((_lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
954 
955             lv_obj_invalidate_area(arc, &inv_area);
956         }
957 
958     }
959     else if(start_quarter == 0 && end_quarter == 1) {
960         inv_area.x1 = x + ((_lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
961         inv_area.y1 = y + ((LV_MATH_MIN(_lv_trigo_sin(end_angle),
962                                         _lv_trigo_sin(start_angle))  * rin) >> LV_TRIGO_SHIFT) - extra_area;
963         inv_area.x2 = x + ((_lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
964         inv_area.y2 = y + rout + extra_area;
965         lv_obj_invalidate_area(arc, &inv_area);
966     }
967     else if(start_quarter == 1 && end_quarter == 2) {
968         inv_area.x1 = x - rout - extra_area;
969         inv_area.y1 = y + ((_lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
970         inv_area.x2 = x + ((LV_MATH_MAX(_lv_trigo_sin(start_angle + 90),
971                                         _lv_trigo_sin(end_angle + 90)) * rin) >> LV_TRIGO_SHIFT) + extra_area;
972         inv_area.y2 = y + ((_lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
973         lv_obj_invalidate_area(arc, &inv_area);
974     }
975     else if(start_quarter == 2 && end_quarter == 3) {
976         inv_area.x1 = x + ((_lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
977         inv_area.y1 = y - rout - extra_area;
978         inv_area.x2 = x + ((_lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
979         inv_area.y2 = y + (LV_MATH_MAX(_lv_trigo_sin(end_angle) * rin,
980                                        _lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
981         lv_obj_invalidate_area(arc, &inv_area);
982     }
983     else if(start_quarter == 3 && end_quarter == 0) {
984         inv_area.x1 = x + ((LV_MATH_MIN(_lv_trigo_sin(end_angle + 90),
985                                         _lv_trigo_sin(start_angle + 90)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
986         inv_area.y1 = y + ((_lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
987         inv_area.x2 = x + rout + extra_area;
988         inv_area.y2 = y + ((_lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
989 
990         lv_obj_invalidate_area(arc, &inv_area);
991     }
992     else {
993         lv_obj_invalidate(arc);
994     }
995 }
996 
get_center(lv_obj_t * arc,lv_point_t * center,lv_coord_t * arc_r)997 static void get_center(lv_obj_t * arc, lv_point_t * center, lv_coord_t * arc_r)
998 {
999     lv_coord_t left_bg = lv_obj_get_style_pad_left(arc, LV_ARC_PART_BG);
1000     lv_coord_t right_bg = lv_obj_get_style_pad_right(arc, LV_ARC_PART_BG);
1001     lv_coord_t top_bg = lv_obj_get_style_pad_top(arc, LV_ARC_PART_BG);
1002     lv_coord_t bottom_bg = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_BG);
1003 
1004     lv_coord_t r = (LV_MATH_MIN(lv_obj_get_width(arc) - left_bg - right_bg,
1005                                 lv_obj_get_height(arc) - top_bg - bottom_bg)) / 2;
1006 
1007     *arc_r = r;
1008     center->x = arc->coords.x1 + r + left_bg;
1009     center->y = arc->coords.y1 + r + top_bg;
1010 
1011 
1012     lv_coord_t indic_width = lv_obj_get_style_line_width(arc, LV_ARC_PART_INDIC);
1013     r -= indic_width;
1014 }
1015 
get_knob_area(lv_obj_t * arc,const lv_point_t * center,lv_coord_t r,lv_area_t * knob_area)1016 static void get_knob_area(lv_obj_t * arc, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area)
1017 {
1018     lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
1019 
1020     lv_coord_t indic_width = lv_obj_get_style_line_width(arc, LV_ARC_PART_INDIC);
1021     lv_coord_t indic_width_half = indic_width / 2;
1022     r -= indic_width_half;
1023 
1024     uint16_t angle = ext->rotation_angle;
1025     if(ext->type == LV_ARC_TYPE_NORMAL) {
1026         angle += ext->arc_angle_end;
1027     }
1028     else if(ext->type == LV_ARC_TYPE_REVERSE) {
1029         angle += ext->arc_angle_start;
1030     }
1031     else if(ext->type == LV_ARC_TYPE_SYMMETRIC) {
1032         int32_t range_midpoint = (int32_t)(ext->min_value + ext->max_value) / 2;
1033         if(ext->cur_value < range_midpoint) angle += ext->arc_angle_start;
1034         else angle += ext->arc_angle_end;
1035     }
1036     lv_coord_t knob_x = (r * _lv_trigo_sin(angle + 90)) >> LV_TRIGO_SHIFT;
1037     lv_coord_t knob_y = (r * _lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT;
1038 
1039     lv_coord_t left_knob = lv_obj_get_style_pad_left(arc, LV_ARC_PART_KNOB);
1040     lv_coord_t right_knob = lv_obj_get_style_pad_right(arc, LV_ARC_PART_KNOB);
1041     lv_coord_t top_knob = lv_obj_get_style_pad_top(arc, LV_ARC_PART_KNOB);
1042     lv_coord_t bottom_knob = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_KNOB);
1043 
1044     knob_area->x1 = center->x + knob_x - left_knob - indic_width_half;
1045     knob_area->x2 = center->x + knob_x + right_knob + indic_width_half;
1046     knob_area->y1 = center->y + knob_y - top_knob - indic_width_half;
1047     knob_area->y2 = center->y + knob_y + bottom_knob + indic_width_half;
1048 }
1049 
1050 /**
1051  * Used internally to update arc angles after a value change
1052  * @param arc pointer to a arc object
1053  */
value_update(lv_obj_t * arc)1054 static void value_update(lv_obj_t * arc)
1055 {
1056     lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc);
1057 
1058     int16_t bg_midpoint, range_midpoint, bg_end = ext->bg_angle_end;
1059     if(ext->bg_angle_end < ext->bg_angle_start) bg_end = ext->bg_angle_end + 360;
1060 
1061     int16_t angle;
1062     switch(ext->type) {
1063         case LV_ARC_TYPE_SYMMETRIC:
1064             bg_midpoint = (ext->bg_angle_start + bg_end) / 2;
1065             range_midpoint = (int32_t)(ext->min_value + ext->max_value) / 2;
1066 
1067             if(ext->cur_value < range_midpoint) {
1068                 angle = _lv_map(ext->cur_value, ext->min_value, range_midpoint, ext->bg_angle_start, bg_midpoint);
1069                 lv_arc_set_start_angle(arc, angle);
1070                 lv_arc_set_end_angle(arc, bg_midpoint);
1071             }
1072             else {
1073                 angle = _lv_map(ext->cur_value, range_midpoint, ext->max_value, bg_midpoint, bg_end);
1074                 lv_arc_set_start_angle(arc, bg_midpoint);
1075                 lv_arc_set_end_angle(arc, angle);
1076             }
1077             break;
1078         case LV_ARC_TYPE_REVERSE:
1079             angle = _lv_map(ext->cur_value, ext->min_value, ext->max_value, ext->bg_angle_start, bg_end);
1080             lv_arc_set_start_angle(arc, angle);
1081             break;
1082         default: /** LV_ARC_TYPE_NORMAL*/
1083             angle = _lv_map(ext->cur_value, ext->min_value, ext->max_value, ext->bg_angle_start, bg_end);
1084             lv_arc_set_end_angle(arc, angle);
1085             lv_arc_set_start_angle(arc, ext->bg_angle_start);
1086     }
1087     ext->last_angle = angle; /*Cache angle for slew rate limiting*/
1088 }
1089 
1090 #endif
1091