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 "../core/lv_group.h"
13 #include "../core/lv_indev.h"
14 #include "../misc/lv_assert.h"
15 #include "../misc/lv_math.h"
16 #include "../draw/lv_draw_arc.h"
17 
18 /*********************
19  *      DEFINES
20  *********************/
21 #define MY_CLASS &lv_arc_class
22 
23 #define VALUE_UNSET INT16_MIN
24 
25 /**********************
26  *      TYPEDEFS
27  **********************/
28 
29 /**********************
30  *  STATIC PROTOTYPES
31  **********************/
32 
33 static void lv_arc_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
34 static void lv_arc_draw(lv_event_t * e);
35 static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e);
36 static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle, lv_part_t part);
37 static void inv_knob_area(lv_obj_t * obj);
38 static void get_center(lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r);
39 static void get_knob_area(lv_obj_t * arc, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area);
40 static void value_update(lv_obj_t * arc);
41 
42 /**********************
43  *  STATIC VARIABLES
44  **********************/
45 const lv_obj_class_t lv_arc_class  = {
46     .constructor_cb = lv_arc_constructor,
47     .event_cb = lv_arc_event,
48     .instance_size = sizeof(lv_arc_t),
49     .editable = LV_OBJ_CLASS_EDITABLE_TRUE,
50     .base_class = &lv_obj_class
51 };
52 
53 /**********************
54  *      MACROS
55  **********************/
56 
57 /**********************
58  *   GLOBAL FUNCTIONS
59  **********************/
60 
lv_arc_create(lv_obj_t * parent)61 lv_obj_t * lv_arc_create(lv_obj_t * parent)
62 {
63     LV_LOG_INFO("begin");
64     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
65     lv_obj_class_init_obj(obj);
66     return obj;
67 }
68 
69 /*======================
70  * Add/remove functions
71  *=====================*/
72 
73 /*
74  * New object specific "add" or "remove" functions come here
75  */
76 
77 /*=====================
78  * Setter functions
79  *====================*/
80 
lv_arc_set_start_angle(lv_obj_t * obj,uint16_t start)81 void lv_arc_set_start_angle(lv_obj_t * obj, uint16_t start)
82 {
83     LV_ASSERT_OBJ(obj, MY_CLASS);
84     lv_arc_t * arc = (lv_arc_t *)obj;
85 
86     if(start > 360) start -= 360;
87 
88     int16_t old_delta = arc->indic_angle_end - arc->indic_angle_start;
89     int16_t new_delta = arc->indic_angle_end - start;
90 
91     if(old_delta < 0) old_delta = 360 + old_delta;
92     if(new_delta < 0) new_delta = 360 + new_delta;
93 
94     if(LV_ABS(new_delta - old_delta) > 180)  lv_obj_invalidate(obj);
95     else if(new_delta < old_delta) inv_arc_area(obj, arc->indic_angle_start, start, LV_PART_INDICATOR);
96     else if(old_delta < new_delta) inv_arc_area(obj, start, arc->indic_angle_start, LV_PART_INDICATOR);
97 
98     inv_knob_area(obj);
99 
100     arc->indic_angle_start = start;
101 
102     inv_knob_area(obj);
103 }
104 
lv_arc_set_end_angle(lv_obj_t * obj,uint16_t end)105 void lv_arc_set_end_angle(lv_obj_t * obj, uint16_t end)
106 {
107     LV_ASSERT_OBJ(obj, MY_CLASS);
108     lv_arc_t * arc = (lv_arc_t *)obj;
109     if(end > 360) end -= 360;
110 
111     int16_t old_delta = arc->indic_angle_end - arc->indic_angle_start;
112     int16_t new_delta = end - arc->indic_angle_start;
113 
114     if(old_delta < 0) old_delta = 360 + old_delta;
115     if(new_delta < 0) new_delta = 360 + new_delta;
116 
117     if(LV_ABS(new_delta - old_delta) > 180)  lv_obj_invalidate(obj);
118     else if(new_delta < old_delta) inv_arc_area(obj, end, arc->indic_angle_end, LV_PART_INDICATOR);
119     else if(old_delta < new_delta) inv_arc_area(obj, arc->indic_angle_end, end, LV_PART_INDICATOR);
120 
121     inv_knob_area(obj);
122 
123     arc->indic_angle_end = end;
124 
125     inv_knob_area(obj);
126 }
127 
lv_arc_set_angles(lv_obj_t * obj,uint16_t start,uint16_t end)128 void lv_arc_set_angles(lv_obj_t * obj, uint16_t start, uint16_t end)
129 {
130     lv_arc_set_end_angle(obj, end);
131     lv_arc_set_start_angle(obj, start);
132 }
133 
lv_arc_set_bg_start_angle(lv_obj_t * obj,uint16_t start)134 void lv_arc_set_bg_start_angle(lv_obj_t * obj, uint16_t start)
135 {
136     LV_ASSERT_OBJ(obj, MY_CLASS);
137     lv_arc_t * arc = (lv_arc_t *)obj;
138 
139     if(start > 360) start -= 360;
140 
141     int16_t old_delta = arc->bg_angle_end - arc->bg_angle_start;
142     int16_t new_delta = arc->bg_angle_end - start;
143 
144     if(old_delta < 0) old_delta = 360 + old_delta;
145     if(new_delta < 0) new_delta = 360 + new_delta;
146 
147     if(LV_ABS(new_delta - old_delta) > 180)  lv_obj_invalidate(obj);
148     else if(new_delta < old_delta) inv_arc_area(obj, arc->bg_angle_start, start, LV_PART_MAIN);
149     else if(old_delta < new_delta) inv_arc_area(obj, start, arc->bg_angle_start, LV_PART_MAIN);
150 
151     arc->bg_angle_start = start;
152 
153     value_update(obj);
154 }
155 
lv_arc_set_bg_end_angle(lv_obj_t * obj,uint16_t end)156 void lv_arc_set_bg_end_angle(lv_obj_t * obj, uint16_t end)
157 {
158     LV_ASSERT_OBJ(obj, MY_CLASS);
159     lv_arc_t * arc = (lv_arc_t *)obj;
160 
161     if(end > 360) end -= 360;
162 
163     int16_t old_delta = arc->bg_angle_end - arc->bg_angle_start;
164     int16_t new_delta = end - arc->bg_angle_start;
165 
166     if(old_delta < 0) old_delta = 360 + old_delta;
167     if(new_delta < 0) new_delta = 360 + new_delta;
168 
169     if(LV_ABS(new_delta - old_delta) > 180)  lv_obj_invalidate(obj);
170     else if(new_delta < old_delta) inv_arc_area(obj, end, arc->bg_angle_end, LV_PART_MAIN);
171     else if(old_delta < new_delta) inv_arc_area(obj, arc->bg_angle_end, end, LV_PART_MAIN);
172 
173     arc->bg_angle_end = end;
174 
175     value_update(obj);
176 }
177 
lv_arc_set_bg_angles(lv_obj_t * obj,uint16_t start,uint16_t end)178 void lv_arc_set_bg_angles(lv_obj_t * obj, uint16_t start, uint16_t end)
179 {
180     lv_arc_set_bg_end_angle(obj, end);
181     lv_arc_set_bg_start_angle(obj, start);
182 }
183 
lv_arc_set_rotation(lv_obj_t * obj,uint16_t rotation)184 void lv_arc_set_rotation(lv_obj_t * obj, uint16_t rotation)
185 {
186     LV_ASSERT_OBJ(obj, MY_CLASS);
187     lv_arc_t * arc = (lv_arc_t *)obj;
188 
189     arc->rotation = rotation;
190 
191     lv_obj_invalidate(obj);
192 }
193 
lv_arc_set_mode(lv_obj_t * obj,lv_arc_mode_t type)194 void lv_arc_set_mode(lv_obj_t * obj, lv_arc_mode_t type)
195 {
196     LV_ASSERT_OBJ(obj, MY_CLASS);
197     lv_arc_t * arc = (lv_arc_t *)obj;
198 
199     int16_t val = arc->value;
200 
201     arc->type = type;
202     arc->value = -1; /** Force set_value handling*/
203 
204     int16_t bg_midpoint, bg_end = arc->bg_angle_end;
205     if(arc->bg_angle_end < arc->bg_angle_start) bg_end = arc->bg_angle_end + 360;
206 
207     switch(arc->type) {
208         case LV_ARC_MODE_SYMMETRICAL:
209             bg_midpoint = (arc->bg_angle_start + bg_end) / 2;
210             lv_arc_set_start_angle(obj, bg_midpoint);
211             lv_arc_set_end_angle(obj, bg_midpoint);
212             break;
213         case LV_ARC_MODE_REVERSE:
214             lv_arc_set_end_angle(obj, arc->bg_angle_end);
215             break;
216         default: /** LV_ARC_TYPE_NORMAL*/
217             lv_arc_set_start_angle(obj, arc->bg_angle_start);
218     }
219 
220     lv_arc_set_value(obj, val);
221 }
222 
lv_arc_set_value(lv_obj_t * obj,int16_t value)223 void lv_arc_set_value(lv_obj_t * obj, int16_t value)
224 {
225     LV_ASSERT_OBJ(obj, MY_CLASS);
226     lv_arc_t * arc = (lv_arc_t *)obj;
227 
228     if(arc->value == value) return;
229 
230     int16_t new_value;
231     new_value = value > arc->max_value ? arc->max_value : value;
232     new_value = new_value < arc->min_value ? arc->min_value : new_value;
233 
234     if(arc->value == new_value) return;
235     arc->value = new_value;
236 
237     value_update(obj);
238 }
239 
lv_arc_set_range(lv_obj_t * obj,int16_t min,int16_t max)240 void lv_arc_set_range(lv_obj_t * obj, int16_t min, int16_t max)
241 {
242     LV_ASSERT_OBJ(obj, MY_CLASS);
243     lv_arc_t * arc = (lv_arc_t *)obj;
244 
245     if(arc->min_value == min && arc->max_value == max) return;
246 
247     arc->min_value = min;
248     arc->max_value = max;
249 
250     if(arc->value < min) {
251         arc->value = min;
252     }
253     if(arc->value > max) {
254         arc->value = max;
255     }
256 
257     value_update(obj); /*value has changed relative to the new range*/
258 }
259 
lv_arc_set_change_rate(lv_obj_t * obj,uint16_t rate)260 void lv_arc_set_change_rate(lv_obj_t * obj, uint16_t rate)
261 {
262     LV_ASSERT_OBJ(obj, MY_CLASS);
263     lv_arc_t * arc = (lv_arc_t *)obj;
264 
265     arc->chg_rate = rate;
266 }
267 
268 /*=====================
269  * Getter functions
270  *====================*/
271 
lv_arc_get_angle_start(lv_obj_t * obj)272 uint16_t lv_arc_get_angle_start(lv_obj_t * obj)
273 {
274     LV_ASSERT_OBJ(obj, MY_CLASS);
275     return ((lv_arc_t *) obj)->indic_angle_start;
276 }
277 
lv_arc_get_angle_end(lv_obj_t * obj)278 uint16_t lv_arc_get_angle_end(lv_obj_t * obj)
279 {
280     LV_ASSERT_OBJ(obj, MY_CLASS);
281     return ((lv_arc_t *) obj)->indic_angle_end;
282 }
283 
lv_arc_get_bg_angle_start(lv_obj_t * obj)284 uint16_t lv_arc_get_bg_angle_start(lv_obj_t * obj)
285 {
286     LV_ASSERT_OBJ(obj, MY_CLASS);
287     return ((lv_arc_t *) obj)->bg_angle_start;
288 }
289 
lv_arc_get_bg_angle_end(lv_obj_t * obj)290 uint16_t lv_arc_get_bg_angle_end(lv_obj_t * obj)
291 {
292     LV_ASSERT_OBJ(obj, MY_CLASS);
293     return ((lv_arc_t *) obj)->bg_angle_end;
294 }
295 
lv_arc_get_value(const lv_obj_t * obj)296 int16_t lv_arc_get_value(const lv_obj_t * obj)
297 {
298     LV_ASSERT_OBJ(obj, MY_CLASS);
299     return ((lv_arc_t *) obj)->value;
300 }
301 
lv_arc_get_min_value(const lv_obj_t * obj)302 int16_t lv_arc_get_min_value(const lv_obj_t * obj)
303 {
304     LV_ASSERT_OBJ(obj, MY_CLASS);
305     return ((lv_arc_t *) obj)->min_value;
306 }
307 
lv_arc_get_max_value(const lv_obj_t * obj)308 int16_t lv_arc_get_max_value(const lv_obj_t * obj)
309 {
310     LV_ASSERT_OBJ(obj, MY_CLASS);
311     return ((lv_arc_t *) obj)->max_value;
312 }
313 
lv_arc_get_mode(const lv_obj_t * obj)314 lv_arc_mode_t lv_arc_get_mode(const lv_obj_t * obj)
315 {
316     LV_ASSERT_OBJ(obj, MY_CLASS);
317     return ((lv_arc_t *) obj)->type;
318 }
319 
320 /**********************
321  *   STATIC FUNCTIONS
322  **********************/
323 
lv_arc_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)324 static void lv_arc_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
325 {
326     LV_UNUSED(class_p);
327     LV_TRACE_OBJ_CREATE("begin");
328 
329     lv_arc_t * arc = (lv_arc_t *)obj;
330 
331     /*Initialize the allocated 'ext'*/
332     arc->rotation = 0;
333     arc->bg_angle_start = 135;
334     arc->bg_angle_end   = 45;
335     arc->indic_angle_start = 135;
336     arc->indic_angle_end   = 270;
337     arc->type = LV_ARC_MODE_NORMAL;
338     arc->value = VALUE_UNSET;
339     arc->min_close = 1;
340     arc->min_value = 0;
341     arc->max_value = 100;
342     arc->dragging = false;
343     arc->chg_rate = 720;
344     arc->last_tick = lv_tick_get();
345     arc->last_angle = arc->indic_angle_end;
346 
347     lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE);
348     lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN);
349     lv_obj_set_ext_click_area(obj, LV_DPI_DEF / 10);
350 
351 
352     LV_TRACE_OBJ_CREATE("finished");
353 }
354 
lv_arc_event(const lv_obj_class_t * class_p,lv_event_t * e)355 static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e)
356 {
357     LV_UNUSED(class_p);
358 
359     lv_res_t res;
360 
361     /*Call the ancestor's event handler*/
362     res = lv_obj_event_base(MY_CLASS, e);
363     if(res != LV_RES_OK) return;
364 
365     lv_event_code_t code = lv_event_get_code(e);
366     lv_obj_t * obj = lv_event_get_target(e);
367     lv_arc_t * arc = (lv_arc_t *)lv_event_get_target(e);
368     if(code == LV_EVENT_PRESSING) {
369         lv_indev_t * indev = lv_indev_get_act();
370         if(indev == NULL) return;
371 
372         /*Handle only pointers here*/
373         lv_indev_type_t indev_type = lv_indev_get_type(indev);
374         if(indev_type != LV_INDEV_TYPE_POINTER) return;
375 
376         lv_point_t p;
377         lv_indev_get_point(indev, &p);
378 
379         /*Make point relative to the arc's center*/
380         lv_point_t center;
381         lv_coord_t r;
382         get_center(obj, &center, &r);
383 
384         p.x -= center.x;
385         p.y -= center.y;
386 
387         /*Enter dragging mode if pressed out of the knob*/
388         if(arc->dragging == false) {
389             lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR);
390             r -= indic_width;
391             /*Add some more sensitive area if there is no advanced git testing.
392              * (Advanced hit testing is more precise)*/
393             if(lv_obj_has_flag(obj, LV_OBJ_FLAG_ADV_HITTEST)) {
394                 r -= indic_width;
395 
396             }
397             else {
398                 r -= LV_MAX(r / 4, indic_width);
399             }
400             if(r < 1) r = 1;
401 
402             if(p.x * p.x + p.y * p.y > r * r) {
403                 arc->dragging = true;
404                 arc->last_tick = lv_tick_get(); /*Capture timestamp at dragging start*/
405             }
406         }
407 
408         /*It must be in "dragging" mode to turn the arc*/
409         if(arc->dragging == false) return;
410 
411         /*No angle can be determined if exactly the middle of the arc is being pressed*/
412         if(p.x == 0 && p.y == 0) return;
413 
414         /*Calculate the angle of the pressed point*/
415         int16_t angle;
416         int16_t bg_end = arc->bg_angle_end;
417         if(arc->bg_angle_end < arc->bg_angle_start) {
418             bg_end = arc->bg_angle_end + 360;
419         }
420 
421         angle = lv_atan2(p.y, p.x);
422         angle -= arc->rotation;
423         angle -= arc->bg_angle_start;  /*Make the angle relative to the start angle*/
424 
425         if(angle < 0) angle += 360;
426 
427         int16_t deg_range = bg_end - arc->bg_angle_start;
428 
429         int16_t last_angle_rel = arc->last_angle - arc->bg_angle_start;
430         int16_t delta_angle = angle - last_angle_rel;
431 
432         /*Do not allow big jumps.
433          *It's mainly to avoid jumping to the opposite end if the "dead" range between min. and max. is crossed.
434          *Check which end was closer on the last valid press (arc->min_close) and prefer that end*/
435         if(LV_ABS(delta_angle) > 280) {
436             if(arc->min_close) angle = 0;
437             else angle = deg_range;
438         }
439         else {
440             if(angle < deg_range / 2)arc->min_close = 1;
441             else arc->min_close = 0;
442         }
443 
444         /*Calculate the slew rate limited angle based on change rate (degrees/sec)*/
445         delta_angle = angle - last_angle_rel;
446         uint32_t delta_tick = lv_tick_elaps(arc->last_tick);
447         int16_t delta_angle_max = (arc->chg_rate * delta_tick) / 1000;
448 
449         if(delta_angle > delta_angle_max) {
450             delta_angle = delta_angle_max;
451         }
452         else if(delta_angle < -delta_angle_max) {
453             delta_angle = -delta_angle_max;
454         }
455 
456         angle = last_angle_rel + delta_angle; /*Apply the limited angle change*/
457 
458         /*Rounding for symmetry*/
459         int32_t round = ((bg_end - arc->bg_angle_start) * 8) / (arc->max_value - arc->min_value);
460         round = (round + 4) >> 4;
461         angle += round;
462 
463         angle += arc->bg_angle_start;  /*Make the angle absolute again*/
464 
465         /*Set the new value*/
466         int16_t old_value = arc->value;
467         int16_t new_value = lv_map(angle, arc->bg_angle_start, bg_end, arc->min_value, arc->max_value);
468         if(new_value != lv_arc_get_value(obj)) {
469             arc->last_tick = lv_tick_get(); /*Cache timestamp for the next iteration*/
470             lv_arc_set_value(obj, new_value); /*set_value caches the last_angle for the next iteration*/
471             if(new_value != old_value) {
472                 res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
473                 if(res != LV_RES_OK) return;
474             }
475         }
476 
477         /*Don't let the elapsed time become too big while sitting on an end point*/
478         if(new_value == arc->min_value || new_value == arc->max_value) {
479             arc->last_tick = lv_tick_get(); /*Cache timestamp for the next iteration*/
480         }
481     }
482     else if(code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
483         arc->dragging = false;
484 
485         /*Leave edit mode if released. (No need to wait for LONG_PRESS)*/
486         lv_group_t * g             = lv_obj_get_group(obj);
487         bool editing               = lv_group_get_editing(g);
488         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
489         if(indev_type == LV_INDEV_TYPE_ENCODER) {
490             if(editing) lv_group_set_editing(g, false);
491         }
492 
493     }
494     else if(code == LV_EVENT_KEY) {
495         char c = *((char *)lv_event_get_param(e));
496 
497         int16_t old_value = arc->value;
498         if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
499             lv_arc_set_value(obj, lv_arc_get_value(obj) + 1);
500         }
501         else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
502             lv_arc_set_value(obj, lv_arc_get_value(obj) - 1);
503         }
504 
505         if(old_value != arc->value) {
506             res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
507             if(res != LV_RES_OK) return;
508         }
509     }
510     else if(code == LV_EVENT_HIT_TEST) {
511         lv_hit_test_info_t * info = lv_event_get_param(e);;
512 
513         lv_point_t p;
514         lv_coord_t r;
515         get_center(obj, &p, &r);
516 
517         lv_coord_t ext_click_area = 0;
518         if(obj->spec_attr) ext_click_area = obj->spec_attr->ext_click_pad;
519 
520         lv_coord_t w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
521         r -= w + ext_click_area;
522 
523         lv_area_t a;
524         /*Invalid if clicked inside*/
525         lv_area_set(&a, p.x - r, p.y - r, p.x + r, p.y + r);
526         if(_lv_area_is_point_on(&a, info->point, LV_RADIUS_CIRCLE)) {
527             info->res = false;
528             return;
529         }
530 
531         /*Valid if no clicked outside*/
532         lv_area_increase(&a, w + ext_click_area * 2, w + ext_click_area * 2);
533         info->res = _lv_area_is_point_on(&a, info->point, LV_RADIUS_CIRCLE);
534     }
535     else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
536         lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
537         lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
538         lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
539         lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
540         lv_coord_t bg_pad = LV_MAX4(bg_left, bg_right, bg_top, bg_bottom);
541 
542         lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
543         lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
544         lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
545         lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
546         lv_coord_t knob_pad = LV_MAX4(knob_left, knob_right, knob_top, knob_bottom) + 2;
547 
548         lv_coord_t * s = lv_event_get_param(e);
549         *s = LV_MAX(*s, knob_pad - bg_pad);
550     }
551     else if(code == LV_EVENT_DRAW_MAIN) {
552         lv_arc_draw(e);
553     }
554 }
555 
lv_arc_draw(lv_event_t * e)556 static void lv_arc_draw(lv_event_t * e)
557 {
558     lv_obj_t * obj = lv_event_get_target(e);
559     lv_arc_t * arc = (lv_arc_t *)obj;
560 
561     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
562 
563     lv_point_t center;
564     lv_coord_t arc_r;
565     get_center(obj, &center, &arc_r);
566 
567     lv_obj_draw_part_dsc_t part_draw_dsc;
568     lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
569 
570     /*Draw the background arc*/
571     lv_draw_arc_dsc_t arc_dsc;
572     if(arc_r > 0) {
573         lv_draw_arc_dsc_init(&arc_dsc);
574         lv_obj_init_draw_arc_dsc(obj, LV_PART_MAIN, &arc_dsc);
575 
576         part_draw_dsc.part = LV_PART_MAIN;
577         part_draw_dsc.class_p = MY_CLASS;
578         part_draw_dsc.type = LV_ARC_DRAW_PART_BACKGROUND;
579         part_draw_dsc.p1 = &center;
580         part_draw_dsc.radius = arc_r;
581         part_draw_dsc.arc_dsc = &arc_dsc;
582         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
583 
584         lv_draw_arc(draw_ctx, &arc_dsc, &center, part_draw_dsc.radius, arc->bg_angle_start + arc->rotation,
585                     arc->bg_angle_end + arc->rotation);
586 
587         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
588     }
589 
590     /*Make the indicator arc smaller or larger according to its greatest padding value*/
591     lv_coord_t left_indic = lv_obj_get_style_pad_left(obj, LV_PART_INDICATOR);
592     lv_coord_t right_indic = lv_obj_get_style_pad_right(obj, LV_PART_INDICATOR);
593     lv_coord_t top_indic = lv_obj_get_style_pad_top(obj, LV_PART_INDICATOR);
594     lv_coord_t bottom_indic = lv_obj_get_style_pad_bottom(obj, LV_PART_INDICATOR);
595     lv_coord_t indic_r = arc_r - LV_MAX4(left_indic, right_indic, top_indic, bottom_indic);
596 
597     if(indic_r > 0) {
598         lv_draw_arc_dsc_init(&arc_dsc);
599         lv_obj_init_draw_arc_dsc(obj, LV_PART_INDICATOR, &arc_dsc);
600 
601         part_draw_dsc.part = LV_PART_INDICATOR;
602         part_draw_dsc.class_p = MY_CLASS;
603         part_draw_dsc.type = LV_ARC_DRAW_PART_FOREGROUND;
604         part_draw_dsc.p1 = &center;
605         part_draw_dsc.radius = indic_r;
606         part_draw_dsc.arc_dsc = &arc_dsc;
607         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
608 
609         if(arc_dsc.width > part_draw_dsc.radius) arc_dsc.width = part_draw_dsc.radius;
610         lv_draw_arc(draw_ctx, &arc_dsc, &center, part_draw_dsc.radius, arc->indic_angle_start + arc->rotation,
611                     arc->indic_angle_end + arc->rotation);
612 
613         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
614     }
615 
616     lv_area_t knob_area;
617     get_knob_area(obj, &center, arc_r, &knob_area);
618 
619     lv_draw_rect_dsc_t knob_rect_dsc;
620     lv_draw_rect_dsc_init(&knob_rect_dsc);
621     lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
622 
623     part_draw_dsc.part = LV_PART_KNOB;
624     part_draw_dsc.class_p = MY_CLASS;
625     part_draw_dsc.type = LV_ARC_DRAW_PART_KNOB;
626     part_draw_dsc.draw_area = &knob_area;
627     part_draw_dsc.rect_dsc = &knob_rect_dsc;
628     lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
629 
630     lv_draw_rect(draw_ctx, &knob_rect_dsc, &knob_area);
631 
632     lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
633 }
634 
inv_arc_area(lv_obj_t * obj,uint16_t start_angle,uint16_t end_angle,lv_part_t part)635 static void inv_arc_area(lv_obj_t * obj, uint16_t start_angle, uint16_t end_angle, lv_part_t part)
636 {
637     LV_ASSERT_OBJ(obj, MY_CLASS);
638 
639     /*Skip this complicated invalidation if the arc is not visible*/
640     if(lv_obj_is_visible(obj) == false) return;
641 
642     lv_arc_t * arc = (lv_arc_t *)obj;
643 
644     if(start_angle == end_angle) return;
645 
646     if(start_angle > 360) start_angle -= 360;
647     if(end_angle > 360) end_angle -= 360;
648 
649     start_angle += arc->rotation;
650     end_angle += arc->rotation;
651 
652     if(start_angle > 360) start_angle -= 360;
653     if(end_angle > 360) end_angle -= 360;
654 
655     lv_coord_t r;
656     lv_point_t c;
657     get_center(obj, &c, &r);
658 
659     lv_coord_t w = lv_obj_get_style_arc_width(obj, part);
660     lv_coord_t rounded = lv_obj_get_style_arc_rounded(obj, part);
661 
662     lv_area_t inv_area;
663     lv_draw_arc_get_area(c.x, c.y, r, start_angle, end_angle, w, rounded, &inv_area);
664     lv_obj_invalidate_area(obj, &inv_area);
665 }
666 
inv_knob_area(lv_obj_t * obj)667 static void inv_knob_area(lv_obj_t * obj)
668 {
669     lv_point_t c;
670     lv_coord_t r;
671     get_center(obj, &c, &r);
672 
673     lv_area_t a;
674     get_knob_area(obj, &c, r, &a);
675     lv_obj_invalidate_area(obj, &a);
676 }
677 
get_center(lv_obj_t * obj,lv_point_t * center,lv_coord_t * arc_r)678 static void get_center(lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r)
679 {
680     lv_coord_t left_bg = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
681     lv_coord_t right_bg = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
682     lv_coord_t top_bg = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
683     lv_coord_t bottom_bg = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
684 
685     lv_coord_t r = (LV_MIN(lv_obj_get_width(obj) - left_bg - right_bg,
686                            lv_obj_get_height(obj) - top_bg - bottom_bg)) / 2;
687 
688     center->x = obj->coords.x1 + r + left_bg;
689     center->y = obj->coords.y1 + r + top_bg;
690 
691     if(arc_r) *arc_r = r;
692 }
693 
get_knob_area(lv_obj_t * obj,const lv_point_t * center,lv_coord_t r,lv_area_t * knob_area)694 static void get_knob_area(lv_obj_t * obj, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area)
695 {
696     LV_ASSERT_OBJ(obj, MY_CLASS);
697     lv_arc_t * arc = (lv_arc_t *)obj;
698 
699     lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR);
700     lv_coord_t indic_width_half = indic_width / 2;
701     r -= indic_width_half;
702 
703     uint16_t angle = arc->rotation;
704     if(arc->type == LV_ARC_MODE_NORMAL) {
705         angle += arc->indic_angle_end;
706     }
707     else if(arc->type == LV_ARC_MODE_REVERSE) {
708         angle += arc->indic_angle_start;
709     }
710     else if(arc->type == LV_ARC_MODE_SYMMETRICAL) {
711         int32_t range_midpoint = (int32_t)(arc->min_value + arc->max_value) / 2;
712         if(arc->value < range_midpoint) angle += arc->indic_angle_start;
713         else angle += arc->indic_angle_end;
714     }
715     lv_coord_t knob_x = (r * lv_trigo_sin(angle + 90)) >> LV_TRIGO_SHIFT;
716     lv_coord_t knob_y = (r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT;
717 
718     lv_coord_t left_knob = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
719     lv_coord_t right_knob = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
720     lv_coord_t top_knob = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
721     lv_coord_t bottom_knob = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
722 
723     knob_area->x1 = center->x + knob_x - left_knob - indic_width_half;
724     knob_area->x2 = center->x + knob_x + right_knob + indic_width_half;
725     knob_area->y1 = center->y + knob_y - top_knob - indic_width_half;
726     knob_area->y2 = center->y + knob_y + bottom_knob + indic_width_half;
727 }
728 
729 /**
730  * Used internally to update arc angles after a value change
731  * @param arc pointer to an arc object
732  */
value_update(lv_obj_t * obj)733 static void value_update(lv_obj_t * obj)
734 {
735     LV_ASSERT_OBJ(obj, MY_CLASS);
736     lv_arc_t * arc = (lv_arc_t *)obj;
737 
738     /*If the value is still not set to any value do not update*/
739     if(arc->value == VALUE_UNSET) return;
740 
741     int16_t bg_midpoint, range_midpoint, bg_end = arc->bg_angle_end;
742     if(arc->bg_angle_end < arc->bg_angle_start) bg_end = arc->bg_angle_end + 360;
743 
744     int16_t angle;
745     switch(arc->type) {
746         case LV_ARC_MODE_SYMMETRICAL:
747             bg_midpoint = (arc->bg_angle_start + bg_end) / 2;
748             range_midpoint = (int32_t)(arc->min_value + arc->max_value) / 2;
749 
750             if(arc->value < range_midpoint) {
751                 angle = lv_map(arc->value, arc->min_value, range_midpoint, arc->bg_angle_start, bg_midpoint);
752                 lv_arc_set_start_angle(obj, angle);
753                 lv_arc_set_end_angle(obj, bg_midpoint);
754             }
755             else {
756                 angle = lv_map(arc->value, range_midpoint, arc->max_value, bg_midpoint, bg_end);
757                 lv_arc_set_start_angle(obj, bg_midpoint);
758                 lv_arc_set_end_angle(obj, angle);
759             }
760             break;
761         case LV_ARC_MODE_REVERSE:
762             angle = lv_map(arc->value, arc->min_value, arc->max_value, arc->bg_angle_start, bg_end);
763             lv_arc_set_angles(obj, angle, arc->bg_angle_end);
764             break;
765         case LV_ARC_MODE_NORMAL:
766             angle = lv_map(arc->value, arc->min_value, arc->max_value, arc->bg_angle_start, bg_end);
767             lv_arc_set_angles(obj, arc->bg_angle_start, angle);
768             break;
769         default:
770             LV_LOG_WARN("Invalid mode: %d", arc->type);
771             return;
772     }
773     arc->last_angle = angle; /*Cache angle for slew rate limiting*/
774 }
775 
776 #endif
777