1 /**
2  * @file lv_scale.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_scale_private.h"
10 #include "../../core/lv_obj_private.h"
11 #include "../../core/lv_obj_class_private.h"
12 #if LV_USE_SCALE != 0
13 
14 #include "../../core/lv_group.h"
15 #include "../../misc/lv_assert.h"
16 #include "../../misc/lv_math.h"
17 #include "../../draw/lv_draw_arc.h"
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 #define MY_CLASS (&lv_scale_class)
23 
24 #define LV_SCALE_LABEL_TXT_LEN          (20U)
25 #define LV_SCALE_DEFAULT_ANGLE_RANGE    ((uint32_t) 270U)
26 #define LV_SCALE_DEFAULT_ROTATION       ((int32_t) 135U)
27 #define LV_SCALE_TICK_IDX_DEFAULT_ID    ((uint32_t) 255U)
28 #define LV_SCALE_DEFAULT_LABEL_GAP      ((uint32_t) 15U)
29 
30 /**********************
31  *      TYPEDEFS
32  **********************/
33 
34 /**********************
35  *  STATIC PROTOTYPES
36  **********************/
37 
38 static void lv_scale_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
39 static void lv_scale_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
40 static void lv_scale_event(const lv_obj_class_t * class_p, lv_event_t * event);
41 
42 static void scale_draw_main(lv_obj_t * obj, lv_event_t * event);
43 static void scale_draw_indicator(lv_obj_t * obj, lv_event_t * event);
44 static void scale_draw_label(lv_obj_t * obj, lv_event_t * event, lv_draw_label_dsc_t * label_dsc,
45                              const uint32_t major_tick_idx, const int32_t tick_value, lv_point_t * tick_point_b, const uint32_t tick_idx);
46 static void scale_calculate_main_compensation(lv_obj_t * obj);
47 
48 static void scale_get_center(const lv_obj_t * obj, lv_point_t * center, int32_t * arc_r);
49 static void scale_get_tick_points(lv_obj_t * obj, const uint32_t tick_idx, bool is_major_tick,
50                                   lv_point_t * tick_point_a, lv_point_t * tick_point_b);
51 static void scale_get_label_coords(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc, lv_point_t * tick_point,
52                                    lv_area_t * label_coords);
53 static void scale_set_indicator_label_properties(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc,
54                                                  lv_style_t * indicator_section_style);
55 static void scale_set_line_properties(lv_obj_t * obj, lv_draw_line_dsc_t * line_dsc, lv_style_t * section_style,
56                                       lv_part_t part);
57 static void scale_set_arc_properties(lv_obj_t * obj, lv_draw_arc_dsc_t * arc_dsc, lv_style_t * section_style);
58 /* Helpers */
59 static void scale_find_section_tick_idx(lv_obj_t * obj);
60 static void scale_store_main_line_tick_width_compensation(lv_obj_t * obj, const uint32_t tick_idx,
61                                                           const bool is_major_tick, const int32_t major_tick_width, const int32_t minor_tick_width);
62 static void scale_store_section_line_tick_width_compensation(lv_obj_t * obj, const bool is_major_tick,
63                                                              lv_draw_line_dsc_t * major_tick_dsc, lv_draw_line_dsc_t * minor_tick_dsc,
64                                                              const int32_t tick_value, const uint8_t tick_idx, lv_point_t * tick_point_a);
65 static void scale_build_custom_label_text(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc,
66                                           const uint16_t major_tick_idx);
67 
68 static void scale_free_line_needle_points_cb(lv_event_t * e);
69 
70 /**********************
71  *  STATIC VARIABLES
72  **********************/
73 
74 const lv_obj_class_t lv_scale_class  = {
75     .constructor_cb = lv_scale_constructor,
76     .destructor_cb = lv_scale_destructor,
77     .event_cb = lv_scale_event,
78     .instance_size = sizeof(lv_scale_t),
79     .editable = LV_OBJ_CLASS_EDITABLE_TRUE,
80     .base_class = &lv_obj_class,
81     .name = "scale",
82 };
83 
84 /**********************
85  *      MACROS
86  **********************/
87 
88 /**********************
89  *   GLOBAL FUNCTIONS
90  **********************/
91 
lv_scale_create(lv_obj_t * parent)92 lv_obj_t * lv_scale_create(lv_obj_t * parent)
93 {
94     LV_LOG_INFO("begin");
95     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
96     lv_obj_class_init_obj(obj);
97     return obj;
98 }
99 
100 /*======================
101  * Add/remove functions
102  *=====================*/
103 
104 /*
105  * New object specific "add" or "remove" functions come here
106  */
107 
108 /*=====================
109  * Setter functions
110  *====================*/
111 
lv_scale_set_mode(lv_obj_t * obj,lv_scale_mode_t mode)112 void lv_scale_set_mode(lv_obj_t * obj, lv_scale_mode_t mode)
113 {
114     LV_ASSERT_OBJ(obj, MY_CLASS);
115     lv_scale_t * scale = (lv_scale_t *)obj;
116 
117     scale->mode = mode;
118 
119     lv_obj_invalidate(obj);
120 }
121 
lv_scale_set_total_tick_count(lv_obj_t * obj,uint32_t total_tick_count)122 void lv_scale_set_total_tick_count(lv_obj_t * obj, uint32_t total_tick_count)
123 {
124     LV_ASSERT_OBJ(obj, MY_CLASS);
125     lv_scale_t * scale = (lv_scale_t *)obj;
126 
127     scale->total_tick_count = total_tick_count;
128 
129     lv_obj_invalidate(obj);
130 }
131 
lv_scale_set_major_tick_every(lv_obj_t * obj,uint32_t major_tick_every)132 void lv_scale_set_major_tick_every(lv_obj_t * obj, uint32_t major_tick_every)
133 {
134     LV_ASSERT_OBJ(obj, MY_CLASS);
135     lv_scale_t * scale = (lv_scale_t *)obj;
136 
137     scale->major_tick_every = major_tick_every;
138 
139     lv_obj_invalidate(obj);
140 }
141 
lv_scale_set_label_show(lv_obj_t * obj,bool show_label)142 void lv_scale_set_label_show(lv_obj_t * obj, bool show_label)
143 {
144     LV_ASSERT_OBJ(obj, MY_CLASS);
145     lv_scale_t * scale = (lv_scale_t *)obj;
146 
147     scale->label_enabled = show_label;
148 
149     lv_obj_invalidate(obj);
150 }
151 
lv_scale_set_range(lv_obj_t * obj,int32_t min,int32_t max)152 void lv_scale_set_range(lv_obj_t * obj, int32_t min, int32_t max)
153 {
154     LV_ASSERT_OBJ(obj, MY_CLASS);
155     lv_scale_t * scale = (lv_scale_t *)obj;
156 
157     scale->range_min = min;
158     scale->range_max = max;
159 
160     lv_obj_invalidate(obj);
161 }
162 
lv_scale_set_angle_range(lv_obj_t * obj,uint32_t angle_range)163 void lv_scale_set_angle_range(lv_obj_t * obj, uint32_t angle_range)
164 {
165     LV_ASSERT_OBJ(obj, MY_CLASS);
166     lv_scale_t * scale = (lv_scale_t *)obj;
167 
168     scale->angle_range = angle_range;
169 
170     lv_obj_invalidate(obj);
171 }
172 
lv_scale_set_rotation(lv_obj_t * obj,int32_t rotation)173 void lv_scale_set_rotation(lv_obj_t * obj, int32_t rotation)
174 {
175     LV_ASSERT_OBJ(obj, MY_CLASS);
176     lv_scale_t * scale = (lv_scale_t *)obj;
177     int32_t normalized_angle = rotation;
178 
179     if(normalized_angle < 0 || normalized_angle > 360) {
180         normalized_angle = rotation % 360;
181 
182         if(normalized_angle < 0) {
183             normalized_angle += 360;
184         }
185     }
186 
187     scale->rotation = normalized_angle;
188     lv_obj_invalidate(obj);
189 }
190 
lv_scale_set_line_needle_value(lv_obj_t * obj,lv_obj_t * needle_line,int32_t needle_length,int32_t value)191 void lv_scale_set_line_needle_value(lv_obj_t * obj, lv_obj_t * needle_line, int32_t needle_length,
192                                     int32_t value)
193 {
194     int32_t angle;
195     int32_t scale_width, scale_height;
196     int32_t actual_needle_length;
197     int32_t needle_length_x, needle_length_y;
198     lv_point_precise_t * needle_line_points = NULL;
199 
200     LV_ASSERT_OBJ(obj, MY_CLASS);
201     lv_scale_t * scale = (lv_scale_t *)obj;
202     if((scale->mode != LV_SCALE_MODE_ROUND_INNER) &&
203        (scale->mode != LV_SCALE_MODE_ROUND_OUTER)) {
204         return;
205     }
206 
207     lv_obj_align(needle_line, LV_ALIGN_TOP_LEFT, 0, 0);
208 
209     scale_width = lv_obj_get_style_width(obj, LV_PART_MAIN);
210     scale_height = lv_obj_get_style_height(obj, LV_PART_MAIN);
211 
212     if(scale_width != scale_height) {
213         return;
214     }
215 
216     if(needle_length >= scale_width / 2) {
217         actual_needle_length = scale_width / 2;
218     }
219     else if(needle_length >= 0) {
220         actual_needle_length = needle_length;
221     }
222     else if(needle_length + scale_width / 2 < 0) {
223         actual_needle_length = 0;
224     }
225     else {
226         actual_needle_length = scale_width / 2 + needle_length;
227     }
228 
229     if(value < scale->range_min) {
230         angle = 0;
231     }
232     else if(value > scale->range_max) {
233         angle = scale->angle_range;
234     }
235     else {
236         angle = scale->angle_range * (value - scale->range_min) / (scale->range_max - scale->range_min);
237     }
238 
239     needle_length_x = (actual_needle_length * lv_trigo_cos(scale->rotation + angle)) >> LV_TRIGO_SHIFT;
240     needle_length_y = (actual_needle_length * lv_trigo_sin(scale->rotation + angle)) >> LV_TRIGO_SHIFT;
241 
242     if(lv_line_is_point_array_mutable(needle_line) && lv_line_get_point_count(needle_line) >= 2) {
243         needle_line_points = lv_line_get_points_mutable(needle_line);
244     }
245 
246     if(needle_line_points == NULL) {
247         uint32_t i;
248         uint32_t line_event_cnt = lv_obj_get_event_count(needle_line);
249         for(i = 0; i < line_event_cnt; i--) {
250             lv_event_dsc_t * dsc = lv_obj_get_event_dsc(needle_line, i);
251             if(lv_event_dsc_get_cb(dsc) == scale_free_line_needle_points_cb) {
252                 needle_line_points = lv_event_dsc_get_user_data(dsc);
253                 break;
254             }
255         }
256     }
257 
258     if(needle_line_points == NULL) {
259         needle_line_points = lv_malloc(sizeof(lv_point_precise_t) * 2);
260         LV_ASSERT_MALLOC(needle_line_points);
261         if(needle_line_points == NULL) return;
262         lv_obj_add_event_cb(needle_line, scale_free_line_needle_points_cb, LV_EVENT_DELETE, needle_line_points);
263     }
264 
265     needle_line_points[0].x = scale_width / 2;
266     needle_line_points[0].y = scale_height / 2;
267     needle_line_points[1].x = scale_width / 2 + needle_length_x;
268     needle_line_points[1].y = scale_height / 2 + needle_length_y;
269 
270     lv_line_set_points_mutable(needle_line, needle_line_points, 2);
271 }
272 
lv_scale_set_image_needle_value(lv_obj_t * obj,lv_obj_t * needle_img,int32_t value)273 void lv_scale_set_image_needle_value(lv_obj_t * obj, lv_obj_t * needle_img, int32_t value)
274 {
275     int32_t angle;
276     LV_ASSERT_OBJ(obj, MY_CLASS);
277     lv_scale_t * scale = (lv_scale_t *)obj;
278     if((scale->mode != LV_SCALE_MODE_ROUND_INNER) &&
279        (scale->mode != LV_SCALE_MODE_ROUND_OUTER)) {
280         return;
281     }
282 
283     if(value < scale->range_min) {
284         angle = 0;
285     }
286     else if(value > scale->range_max) {
287         angle = scale->angle_range;
288     }
289     else {
290         angle = scale->angle_range * (value - scale->range_min) / (scale->range_max - scale->range_min);
291     }
292 
293     lv_image_set_rotation(needle_img, (scale->rotation + angle) * 10);
294 }
295 
lv_scale_set_text_src(lv_obj_t * obj,const char * txt_src[])296 void lv_scale_set_text_src(lv_obj_t * obj, const char * txt_src[])
297 {
298     LV_ASSERT_OBJ(obj, MY_CLASS);
299     lv_scale_t * scale = (lv_scale_t *)obj;
300 
301     scale->txt_src = txt_src;
302     scale->custom_label_cnt = 0;
303     if(scale->txt_src) {
304         int32_t idx;
305         for(idx = 0; txt_src[idx]; ++idx) {
306             scale->custom_label_cnt++;
307         }
308     }
309 
310     lv_obj_invalidate(obj);
311 }
312 
lv_scale_set_post_draw(lv_obj_t * obj,bool en)313 void lv_scale_set_post_draw(lv_obj_t * obj, bool en)
314 {
315     LV_ASSERT_OBJ(obj, MY_CLASS);
316     lv_scale_t * scale = (lv_scale_t *)obj;
317 
318     scale->post_draw = en;
319 
320     lv_obj_invalidate(obj);
321 }
322 
lv_scale_set_draw_ticks_on_top(lv_obj_t * obj,bool en)323 void lv_scale_set_draw_ticks_on_top(lv_obj_t * obj, bool en)
324 {
325     LV_ASSERT_OBJ(obj, MY_CLASS);
326     lv_scale_t * scale = (lv_scale_t *)obj;
327 
328     scale->draw_ticks_on_top = en;
329 
330     lv_obj_invalidate(obj);
331 }
332 
lv_scale_add_section(lv_obj_t * obj)333 lv_scale_section_t * lv_scale_add_section(lv_obj_t * obj)
334 {
335     LV_ASSERT_OBJ(obj, MY_CLASS);
336 
337     lv_scale_t * scale = (lv_scale_t *)obj;
338     lv_scale_section_t * section = lv_ll_ins_head(&scale->section_ll);
339     LV_ASSERT_MALLOC(section);
340     if(section == NULL) return NULL;
341 
342     /* Section default values */
343     lv_memzero(section, sizeof(lv_scale_section_t));
344     section->first_tick_idx_in_section = LV_SCALE_TICK_IDX_DEFAULT_ID;
345     section->last_tick_idx_in_section = LV_SCALE_TICK_IDX_DEFAULT_ID;
346     /* Initial range is [0..-1] to make it "neutral" (i.e. will not be drawn until user
347      * sets a different range).  `range_min` is already 0 from `lv_memzero()` above. */
348     section->range_max = -1;
349 
350     return section;
351 }
352 
lv_scale_section_set_range(lv_scale_section_t * section,int32_t min,int32_t max)353 void lv_scale_section_set_range(lv_scale_section_t * section, int32_t min, int32_t max)
354 {
355     if(NULL == section) return;
356 
357     section->range_min = min;
358     section->range_max = max;
359 }
360 
lv_scale_section_set_style(lv_scale_section_t * section,lv_part_t part,lv_style_t * section_part_style)361 void lv_scale_section_set_style(lv_scale_section_t * section, lv_part_t part, lv_style_t * section_part_style)
362 {
363     if(NULL == section) return;
364 
365     switch(part) {
366         case LV_PART_MAIN:
367             section->main_style = section_part_style;
368             break;
369         case LV_PART_INDICATOR:
370             section->indicator_style = section_part_style;
371             break;
372         case LV_PART_ITEMS:
373             section->items_style = section_part_style;
374             break;
375         default:
376             /* Invalid part */
377             break;
378     }
379 }
380 
381 /*=====================
382  * Getter functions
383  *====================*/
384 
lv_scale_get_mode(lv_obj_t * obj)385 lv_scale_mode_t lv_scale_get_mode(lv_obj_t * obj)
386 {
387     lv_scale_t * scale = (lv_scale_t *)obj;
388     return scale->mode;
389 }
390 
lv_scale_get_total_tick_count(lv_obj_t * obj)391 int32_t lv_scale_get_total_tick_count(lv_obj_t * obj)
392 {
393     lv_scale_t * scale = (lv_scale_t *)obj;
394     return scale->total_tick_count;
395 }
396 
lv_scale_get_major_tick_every(lv_obj_t * obj)397 int32_t lv_scale_get_major_tick_every(lv_obj_t * obj)
398 {
399     lv_scale_t * scale = (lv_scale_t *)obj;
400     return scale->major_tick_every;
401 }
402 
lv_scale_get_rotation(lv_obj_t * obj)403 lv_scale_mode_t lv_scale_get_rotation(lv_obj_t * obj)
404 {
405     lv_scale_t * scale = (lv_scale_t *)obj;
406     return scale->rotation;
407 }
408 
lv_scale_get_label_show(lv_obj_t * obj)409 bool lv_scale_get_label_show(lv_obj_t * obj)
410 {
411     lv_scale_t * scale = (lv_scale_t *)obj;
412     return scale->label_enabled;
413 }
414 
lv_scale_get_angle_range(lv_obj_t * obj)415 uint32_t lv_scale_get_angle_range(lv_obj_t * obj)
416 {
417     lv_scale_t * scale = (lv_scale_t *)obj;
418     return scale->angle_range;
419 }
420 
lv_scale_get_range_min_value(lv_obj_t * obj)421 int32_t lv_scale_get_range_min_value(lv_obj_t * obj)
422 {
423     lv_scale_t * scale = (lv_scale_t *)obj;
424     return scale->range_min;
425 }
426 
lv_scale_get_range_max_value(lv_obj_t * obj)427 int32_t lv_scale_get_range_max_value(lv_obj_t * obj)
428 {
429     lv_scale_t * scale = (lv_scale_t *)obj;
430     return scale->range_max;
431 }
432 
433 /*=====================
434  * Other functions
435  *====================*/
436 
437 /**********************
438  *   STATIC FUNCTIONS
439  **********************/
440 
lv_scale_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)441 static void lv_scale_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
442 {
443     LV_UNUSED(class_p);
444     LV_TRACE_OBJ_CREATE("begin");
445 
446     lv_scale_t * scale = (lv_scale_t *)obj;
447 
448     lv_ll_init(&scale->section_ll, sizeof(lv_scale_section_t));
449 
450     scale->total_tick_count = LV_SCALE_TOTAL_TICK_COUNT_DEFAULT;
451     scale->major_tick_every = LV_SCALE_MAJOR_TICK_EVERY_DEFAULT;
452     scale->mode = LV_SCALE_MODE_HORIZONTAL_BOTTOM;
453     scale->label_enabled = LV_SCALE_LABEL_ENABLED_DEFAULT;
454     scale->angle_range = LV_SCALE_DEFAULT_ANGLE_RANGE;
455     scale->rotation = LV_SCALE_DEFAULT_ROTATION;
456     scale->range_min = 0;
457     scale->range_max = 100;
458     scale->last_tick_width = 0;
459     scale->first_tick_width = 0;
460     scale->post_draw = false;
461     scale->draw_ticks_on_top = false;
462     scale->custom_label_cnt = 0;
463     scale->txt_src = NULL;
464 
465     lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
466 
467     LV_TRACE_OBJ_CREATE("finished");
468 }
469 
lv_scale_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)470 static void lv_scale_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
471 {
472     LV_UNUSED(class_p);
473     LV_TRACE_OBJ_CREATE("begin");
474 
475     lv_scale_t * scale = (lv_scale_t *)obj;
476     lv_scale_section_t * section;
477     while(scale->section_ll.head) {
478         section = lv_ll_get_head(&scale->section_ll);
479         lv_ll_remove(&scale->section_ll, section);
480         lv_free(section);
481     }
482     lv_ll_clear(&scale->section_ll);
483 
484     LV_TRACE_OBJ_CREATE("finished");
485 }
486 
lv_scale_event(const lv_obj_class_t * class_p,lv_event_t * event)487 static void lv_scale_event(const lv_obj_class_t * class_p, lv_event_t * event)
488 {
489     LV_UNUSED(class_p);
490 
491     /*Call the ancestor's event handler*/
492     lv_result_t res = lv_obj_event_base(MY_CLASS, event);
493     if(res != LV_RESULT_OK) return;
494 
495     lv_event_code_t event_code = lv_event_get_code(event);
496     lv_obj_t * obj = lv_event_get_current_target(event);
497     lv_scale_t * scale = (lv_scale_t *) obj;
498     LV_UNUSED(scale);
499 
500     if(event_code == LV_EVENT_DRAW_MAIN) {
501         if(scale->post_draw == false) {
502             scale_find_section_tick_idx(obj);
503             scale_calculate_main_compensation(obj);
504 
505             if(scale->draw_ticks_on_top) {
506                 scale_draw_main(obj, event);
507                 scale_draw_indicator(obj, event);
508             }
509             else {
510                 scale_draw_indicator(obj, event);
511                 scale_draw_main(obj, event);
512             }
513         }
514     }
515     if(event_code == LV_EVENT_DRAW_POST) {
516         if(scale->post_draw == true) {
517             scale_find_section_tick_idx(obj);
518             scale_calculate_main_compensation(obj);
519 
520             if(scale->draw_ticks_on_top) {
521                 scale_draw_main(obj, event);
522                 scale_draw_indicator(obj, event);
523             }
524             else {
525                 scale_draw_indicator(obj, event);
526                 scale_draw_main(obj, event);
527             }
528         }
529     }
530     else if(event_code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
531         /* NOTE: Extend scale draw size so the first tick label can be shown */
532         lv_event_set_ext_draw_size(event, 100);
533     }
534     else {
535         /* Nothing to do. Invalid event */
536     }
537 }
538 
scale_draw_indicator(lv_obj_t * obj,lv_event_t * event)539 static void scale_draw_indicator(lv_obj_t * obj, lv_event_t * event)
540 {
541     lv_scale_t * scale = (lv_scale_t *)obj;
542     lv_layer_t * layer = lv_event_get_layer(event);
543 
544     if(scale->total_tick_count <= 1) return;
545 
546     lv_draw_label_dsc_t label_dsc;
547     lv_draw_label_dsc_init(&label_dsc);
548     label_dsc.base.layer = layer;
549     /* Formatting the labels with the configured style for LV_PART_INDICATOR */
550     lv_obj_init_draw_label_dsc(obj, LV_PART_INDICATOR, &label_dsc);
551 
552     /* Major tick style */
553     lv_draw_line_dsc_t major_tick_dsc;
554     lv_draw_line_dsc_init(&major_tick_dsc);
555     major_tick_dsc.base.layer = layer;
556     lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &major_tick_dsc);
557     if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
558         major_tick_dsc.raw_end = 0;
559     }
560 
561     /* Configure line draw descriptor for the minor tick drawing */
562     lv_draw_line_dsc_t minor_tick_dsc;
563     lv_draw_line_dsc_init(&minor_tick_dsc);
564     minor_tick_dsc.base.layer = layer;
565     lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &minor_tick_dsc);
566 
567     /* Main line style */
568     lv_draw_line_dsc_t main_line_dsc;
569     lv_draw_line_dsc_init(&main_line_dsc);
570     main_line_dsc.base.layer = layer;
571     lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &main_line_dsc);
572 
573     /* These 2 values need to be signed since they are being passed
574      * to `lv_map()` which expects signed integers. */
575     const int32_t total_tick_count = scale->total_tick_count;
576     int32_t tick_idx = 0;
577     uint32_t major_tick_idx = 0U;
578     for(tick_idx = 0; tick_idx < total_tick_count; tick_idx++) {
579         /* A major tick is the one which has a label in it */
580         bool is_major_tick = false;
581         if(tick_idx % scale->major_tick_every == 0) is_major_tick = true;
582         if(is_major_tick) major_tick_idx++;
583 
584         const int32_t tick_value = lv_map(tick_idx, 0, total_tick_count - 1, scale->range_min, scale->range_max);
585 
586         label_dsc.base.id1 = tick_idx;
587         label_dsc.base.id2 = tick_value;
588         label_dsc.base.layer = layer;
589 
590         /* Overwrite label and tick properties if tick value is within section range */
591         lv_scale_section_t * section;
592         LV_LL_READ_BACK(&scale->section_ll, section) {
593             if(section->range_min <= tick_value && section->range_max >= tick_value) {
594                 if(is_major_tick) {
595                     scale_set_indicator_label_properties(obj, &label_dsc, section->indicator_style);
596                     scale_set_line_properties(obj, &major_tick_dsc, section->indicator_style, LV_PART_INDICATOR);
597                 }
598                 else {
599                     scale_set_line_properties(obj, &minor_tick_dsc, section->items_style, LV_PART_ITEMS);
600                 }
601                 break;
602             }
603             else {
604                 /* Tick is not in section, get the proper styles */
605                 lv_obj_init_draw_label_dsc(obj, LV_PART_INDICATOR, &label_dsc);
606                 lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &major_tick_dsc);
607                 lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &minor_tick_dsc);
608             }
609         }
610 
611         /* The tick is represented by a line. We need two points to draw it */
612         lv_point_t tick_point_a;
613         lv_point_t tick_point_b;
614         scale_get_tick_points(obj, tick_idx, is_major_tick, &tick_point_a, &tick_point_b);
615 
616         /* Setup a label if they're enabled and we're drawing a major tick */
617         if(scale->label_enabled && is_major_tick) {
618             scale_draw_label(obj, event, &label_dsc, major_tick_idx, tick_value, &tick_point_b, tick_idx);
619         }
620 
621         if(is_major_tick) {
622             major_tick_dsc.p1 = lv_point_to_precise(&tick_point_a);
623             major_tick_dsc.p2 = lv_point_to_precise(&tick_point_b);
624             lv_draw_line(layer, &major_tick_dsc);
625         }
626         else {
627             minor_tick_dsc.p1 = lv_point_to_precise(&tick_point_a);
628             minor_tick_dsc.p2 = lv_point_to_precise(&tick_point_b);
629             lv_draw_line(layer, &minor_tick_dsc);
630         }
631     }
632 }
633 
scale_draw_label(lv_obj_t * obj,lv_event_t * event,lv_draw_label_dsc_t * label_dsc,const uint32_t major_tick_idx,const int32_t tick_value,lv_point_t * tick_point_b,const uint32_t tick_idx)634 static void scale_draw_label(lv_obj_t * obj, lv_event_t * event, lv_draw_label_dsc_t * label_dsc,
635                              const uint32_t major_tick_idx, const int32_t tick_value, lv_point_t * tick_point_b,
636                              const uint32_t tick_idx)
637 {
638     lv_scale_t * scale = (lv_scale_t *)obj;
639     lv_layer_t * layer = lv_event_get_layer(event);
640 
641     /* Label text setup */
642     char text_buffer[LV_SCALE_LABEL_TXT_LEN] = {0};
643     lv_area_t label_coords;
644 
645     /* Check if the custom text array has element for this major tick index */
646     if(scale->txt_src) {
647         scale_build_custom_label_text(obj, label_dsc, major_tick_idx);
648     }
649     else { /* Add label with mapped values */
650         lv_snprintf(text_buffer, sizeof(text_buffer), "%" LV_PRId32, tick_value);
651         label_dsc->text = text_buffer;
652         label_dsc->text_local = 1;
653     }
654 
655     int32_t translate_x = lv_obj_get_style_translate_x(obj, LV_PART_INDICATOR);
656     int32_t translate_y = lv_obj_get_style_translate_y(obj, LV_PART_INDICATOR);
657     int32_t label_rotation = lv_obj_get_style_transform_rotation(obj, LV_PART_INDICATOR);
658     int32_t translate_rotation = 0;
659 
660     if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)
661        || (LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode || LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode)) {
662         lv_point_t label_origin;
663         label_origin.x = tick_point_b->x + translate_x;
664         label_origin.y = tick_point_b->y + translate_y;
665         scale_get_label_coords(obj, label_dsc, &label_origin, &label_coords);
666         label_rotation = (label_rotation & LV_SCALE_ROTATION_ANGLE_MASK);
667     }
668     else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
669         translate_rotation = lv_obj_get_style_translate_radial(obj, LV_PART_INDICATOR);
670         uint32_t label_gap = lv_obj_get_style_pad_radial(obj, LV_PART_INDICATOR) + LV_SCALE_DEFAULT_LABEL_GAP;
671 
672         lv_area_t scale_area;
673         lv_obj_get_content_coords(obj, &scale_area);
674 
675         /* Find the center of the scale */
676         lv_point_t center_point;
677         int32_t radius_edge = LV_MIN(lv_area_get_width(&scale_area) / 2, lv_area_get_height(&scale_area) / 2);
678         center_point.x = scale_area.x1 + radius_edge;
679         center_point.y = scale_area.y1 + radius_edge;
680 
681         const int32_t major_len = lv_obj_get_style_length(obj, LV_PART_INDICATOR);
682 
683         /* Also take into consideration the letter space of the style */
684         int32_t angle_upscale = ((tick_idx * scale->angle_range) * 10U) / (scale->total_tick_count - 1U) +
685                                 (translate_rotation * 10);
686         angle_upscale += scale->rotation * 10;
687 
688         uint32_t radius_text = 0;
689         if(LV_SCALE_MODE_ROUND_INNER == scale->mode) {
690             radius_text = (radius_edge - major_len) - (label_gap + label_dsc->letter_space);
691         }
692         else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode) {
693             radius_text = (radius_edge + major_len) + (label_gap + label_dsc->letter_space);
694         }
695         else { /* Nothing to do */ }
696 
697         lv_point_t point;
698         point.x = center_point.x + radius_text + translate_x;
699         point.y = center_point.y + translate_y;
700         int32_t label_rotation_temp = 0;
701 
702         if(label_rotation & LV_SCALE_LABEL_ROTATE_MATCH_TICKS) {
703             label_rotation_temp = (label_rotation & LV_SCALE_ROTATION_ANGLE_MASK) + angle_upscale;
704 
705             /* keep text upright if the user asked for it, otherwise it will be upside-down on half the dial */
706             if(label_rotation & LV_SCALE_LABEL_ROTATE_KEEP_UPRIGHT) {
707                 while(label_rotation_temp > 3600) {
708                     label_rotation_temp -= 3600;
709                 }
710                 if(label_rotation_temp > 900 && label_rotation_temp < 2400) {
711                     label_rotation_temp += 1800;
712                 }
713             }
714             label_rotation = label_rotation_temp;
715         }
716         else {
717             label_rotation = label_rotation & LV_SCALE_ROTATION_ANGLE_MASK;
718         }
719 
720         lv_point_transform(&point, angle_upscale, LV_SCALE_NONE, LV_SCALE_NONE, &center_point, false);
721         scale_get_label_coords(obj, label_dsc, &point, &label_coords);
722     }
723     /* Invalid mode */
724     else {
725         return;
726     }
727 
728     if(label_rotation > 0) {
729         /*Draw the label to a new layer and draw the layer rotated*/
730         lv_layer_t * layer_label = lv_draw_layer_create(layer, LV_COLOR_FORMAT_ARGB8888, &label_coords);
731         lv_draw_label(layer_label, label_dsc, &label_coords);
732 
733         lv_point_t pivot_point;
734         /* Set pivot point to the center of the label so it matches the scale curve */
735         pivot_point.x = lv_area_get_width(&label_coords) / 2;
736         pivot_point.y = lv_area_get_height(&label_coords) / 2;
737 
738         lv_draw_image_dsc_t layer_draw_dsc;
739         lv_draw_image_dsc_init(&layer_draw_dsc);
740         layer_draw_dsc.src = layer_label;
741         layer_draw_dsc.rotation = label_rotation;
742         layer_draw_dsc.pivot = pivot_point;
743         lv_draw_layer(layer, &layer_draw_dsc, &label_coords);
744     }
745     else {
746         lv_draw_label(layer, label_dsc, &label_coords);
747     }
748 }
749 
scale_calculate_main_compensation(lv_obj_t * obj)750 static void scale_calculate_main_compensation(lv_obj_t * obj)
751 {
752     lv_scale_t * scale = (lv_scale_t *)obj;
753 
754     const uint32_t total_tick_count = scale->total_tick_count;
755 
756     if(total_tick_count <= 1) return;
757     /* Not supported in round modes */
758     if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) return;
759 
760     /* Major tick style */
761     lv_draw_line_dsc_t major_tick_dsc;
762     lv_draw_line_dsc_init(&major_tick_dsc);
763     lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &major_tick_dsc);
764 
765     /* Configure line draw descriptor for the minor tick drawing */
766     lv_draw_line_dsc_t minor_tick_dsc;
767     lv_draw_line_dsc_init(&minor_tick_dsc);
768     lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &minor_tick_dsc);
769 
770     uint32_t tick_idx = 0;
771     for(tick_idx = 0; tick_idx < total_tick_count; tick_idx++) {
772 
773         const bool is_major_tick = tick_idx % scale->major_tick_every == 0;
774 
775         const int32_t tick_value = lv_map(tick_idx, 0, total_tick_count - 1, scale->range_min, scale->range_max);
776 
777         /* Overwrite label and tick properties if tick value is within section range */
778         lv_scale_section_t * section;
779         LV_LL_READ_BACK(&scale->section_ll, section) {
780             if(section->range_min <= tick_value && section->range_max >= tick_value) {
781                 if(is_major_tick) {
782                     scale_set_line_properties(obj, &major_tick_dsc, section->indicator_style, LV_PART_INDICATOR);
783                 }
784                 else {
785                     scale_set_line_properties(obj, &minor_tick_dsc, section->items_style, LV_PART_ITEMS);
786                 }
787                 break;
788             }
789             else {
790                 /* Tick is not in section, get the proper styles */
791                 lv_obj_init_draw_line_dsc(obj, LV_PART_INDICATOR, &major_tick_dsc);
792                 lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &minor_tick_dsc);
793             }
794         }
795 
796         /* The tick is represented by a line. We need two points to draw it */
797         lv_point_t tick_point_a;
798         lv_point_t tick_point_b;
799         scale_get_tick_points(obj, tick_idx, is_major_tick, &tick_point_a, &tick_point_b);
800 
801         /* Store initial and last tick widths to be used in the main line drawing */
802         scale_store_main_line_tick_width_compensation(obj, tick_idx, is_major_tick, major_tick_dsc.width, minor_tick_dsc.width);
803         /* Store the first and last section tick vertical/horizontal position */
804         scale_store_section_line_tick_width_compensation(obj, is_major_tick, &major_tick_dsc, &minor_tick_dsc,
805                                                          tick_value, tick_idx, &tick_point_a);
806     }
807 }
808 
scale_draw_main(lv_obj_t * obj,lv_event_t * event)809 static void scale_draw_main(lv_obj_t * obj, lv_event_t * event)
810 {
811     lv_scale_t * scale = (lv_scale_t *)obj;
812     lv_layer_t * layer = lv_event_get_layer(event);
813 
814     if(scale->total_tick_count <= 1) return;
815 
816     if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)
817        || (LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode || LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode)) {
818 
819         /* Configure both line and label draw descriptors for the tick and label drawings */
820         lv_draw_line_dsc_t line_dsc;
821         lv_draw_line_dsc_init(&line_dsc);
822         line_dsc.base.layer = layer;
823         lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc);
824 
825         /* Get style properties so they can be used in the main line drawing */
826         const int32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
827         const int32_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
828         const int32_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN) + border_width;
829         const int32_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
830         const int32_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width;
831 
832         int32_t x_ofs = 0;
833         int32_t y_ofs = 0;
834 
835         if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) {
836             x_ofs = obj->coords.x2 + (line_dsc.width / 2) - pad_right;
837             y_ofs = obj->coords.y1 + pad_top;
838         }
839         else if(LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
840             x_ofs = obj->coords.x1 + (line_dsc.width / 2) + pad_left;
841             y_ofs = obj->coords.y1 + pad_top;
842         }
843         if(LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode) {
844             x_ofs = obj->coords.x1 + pad_right;
845             y_ofs = obj->coords.y1 + (line_dsc.width / 2) + pad_top;
846         }
847         else if(LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode) {
848             x_ofs = obj->coords.x1 + pad_left;
849             y_ofs = obj->coords.y2 + (line_dsc.width / 2) - pad_bottom;
850         }
851         else { /* Nothing to do */ }
852 
853         lv_point_t main_line_point_a;
854         lv_point_t main_line_point_b;
855 
856         /* Setup the tick points */
857         if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
858             main_line_point_a.x = x_ofs - 1;
859             main_line_point_a.y = y_ofs;
860             main_line_point_b.x = x_ofs - 1;
861             main_line_point_b.y = obj->coords.y2 - pad_bottom;
862 
863             /* Adjust main line with initial and last tick width */
864             main_line_point_a.y -= scale->last_tick_width / 2;
865             main_line_point_b.y += scale->first_tick_width / 2;
866         }
867         else {
868             main_line_point_a.x = x_ofs;
869             main_line_point_a.y = y_ofs;
870             /* X of second point starts at the edge of the object minus the left pad */
871             main_line_point_b.x = obj->coords.x2 - (pad_left);
872             main_line_point_b.y = y_ofs;
873 
874             /* Adjust main line with initial and last tick width */
875             main_line_point_a.x -= scale->last_tick_width / 2;
876             main_line_point_b.x += scale->first_tick_width / 2;
877         }
878 
879         line_dsc.p1 = lv_point_to_precise(&main_line_point_a);
880         line_dsc.p2 = lv_point_to_precise(&main_line_point_b);
881         lv_draw_line(layer, &line_dsc);
882 
883         lv_scale_section_t * section;
884         LV_LL_READ_BACK(&scale->section_ll, section) {
885             lv_draw_line_dsc_t section_line_dsc;
886             lv_draw_line_dsc_init(&section_line_dsc);
887             section_line_dsc.base.layer = layer;
888             lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &section_line_dsc);
889 
890             /* Calculate the points of the section line */
891             lv_point_t section_point_a;
892             lv_point_t section_point_b;
893 
894             const int32_t first_tick_width_halved = (int32_t)(section->first_tick_in_section_width / 2);
895             const int32_t last_tick_width_halved = (int32_t)(section->last_tick_in_section_width / 2);
896 
897             /* Calculate the position of the section based on the ticks (first and last) index */
898             if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
899                 /* Calculate position of the first tick in the section */
900                 section_point_a.x = main_line_point_a.x;
901                 section_point_a.y = section->first_tick_in_section.y + first_tick_width_halved;
902 
903                 /* Calculate position of the last tick in the section */
904                 section_point_b.x = main_line_point_a.x;
905                 section_point_b.y = section->last_tick_in_section.y - last_tick_width_halved;
906             }
907             else {
908                 /* Calculate position of the first tick in the section */
909                 section_point_a.x = section->first_tick_in_section.x - first_tick_width_halved;
910                 section_point_a.y = main_line_point_a.y;
911 
912                 /* Calculate position of the last tick in the section */
913                 section_point_b.x = section->last_tick_in_section.x + last_tick_width_halved;
914                 section_point_b.y = main_line_point_a.y;
915             }
916 
917             scale_set_line_properties(obj, &section_line_dsc, section->main_style, LV_PART_MAIN);
918 
919             section_line_dsc.p1.x = section_point_a.x;
920             section_line_dsc.p1.y = section_point_a.y;
921             section_line_dsc.p2.x = section_point_b.x;
922             section_line_dsc.p2.y = section_point_b.y;
923             lv_draw_line(layer, &section_line_dsc);
924         }
925     }
926     else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
927         /* Configure arc draw descriptors for the main part */
928         lv_draw_arc_dsc_t arc_dsc;
929         lv_draw_arc_dsc_init(&arc_dsc);
930         arc_dsc.base.layer = layer;
931         lv_obj_init_draw_arc_dsc(obj, LV_PART_MAIN, &arc_dsc);
932 
933         lv_point_t arc_center;
934         int32_t arc_radius;
935         scale_get_center(obj, &arc_center, &arc_radius);
936 
937         /* TODO: Add compensation for the width of the first and last tick over the arc */
938         const int32_t start_angle = lv_map(scale->range_min, scale->range_min, scale->range_max, scale->rotation,
939                                            scale->rotation + scale->angle_range);
940         const int32_t end_angle = lv_map(scale->range_max, scale->range_min, scale->range_max, scale->rotation,
941                                          scale->rotation + scale->angle_range);
942 
943         arc_dsc.center = arc_center;
944         arc_dsc.radius = arc_radius;
945         arc_dsc.start_angle = start_angle;
946         arc_dsc.end_angle = end_angle;
947 
948         lv_draw_arc(layer, &arc_dsc);
949 
950         lv_scale_section_t * section;
951         LV_LL_READ_BACK(&scale->section_ll, section) {
952             lv_draw_arc_dsc_t main_arc_section_dsc;
953             lv_draw_arc_dsc_init(&main_arc_section_dsc);
954             main_arc_section_dsc.base.layer = layer;
955             lv_obj_init_draw_arc_dsc(obj, LV_PART_MAIN, &main_arc_section_dsc);
956 
957             lv_point_t section_arc_center;
958             int32_t section_arc_radius;
959             scale_get_center(obj, &section_arc_center, &section_arc_radius);
960 
961             /* TODO: Add compensation for the width of the first and last tick over the arc */
962             const int32_t section_start_angle = lv_map(section->range_min, scale->range_min, scale->range_max, scale->rotation,
963                                                        scale->rotation + scale->angle_range);
964             const int32_t section_end_angle = lv_map(section->range_max, scale->range_min, scale->range_max, scale->rotation,
965                                                      scale->rotation + scale->angle_range);
966 
967             scale_set_arc_properties(obj, &main_arc_section_dsc, section->main_style);
968 
969             main_arc_section_dsc.center = section_arc_center;
970             main_arc_section_dsc.radius = section_arc_radius;
971             main_arc_section_dsc.start_angle = section_start_angle;
972             main_arc_section_dsc.end_angle = section_end_angle;
973 
974             lv_draw_arc(layer, &main_arc_section_dsc);
975         }
976     }
977     else { /* Nothing to do */ }
978 }
979 
980 /**
981  * Get center point and radius of scale arc
982  * @param obj       pointer to a scale object
983  * @param center    pointer to center
984  * @param arc_r     pointer to arc radius
985  */
scale_get_center(const lv_obj_t * obj,lv_point_t * center,int32_t * arc_r)986 static void scale_get_center(const lv_obj_t * obj, lv_point_t * center, int32_t * arc_r)
987 {
988     int32_t left_bg = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
989     int32_t right_bg = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
990     int32_t top_bg = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
991     int32_t bottom_bg = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
992 
993     int32_t r = (LV_MIN(lv_obj_get_width(obj) - left_bg - right_bg, lv_obj_get_height(obj) - top_bg - bottom_bg)) / 2;
994 
995     center->x = obj->coords.x1 + r + left_bg;
996     center->y = obj->coords.y1 + r + top_bg;
997 
998     if(arc_r) *arc_r = r;
999 }
1000 
1001 /**
1002  * Get points for ticks
1003  *
1004  * In order to draw ticks we need two points, this interface returns both points for all scale modes.
1005  *
1006  * @param obj       pointer to a scale object
1007  * @param tick_idx  index of the current tick
1008  * @param is_major_tick true if tick_idx is a major tick
1009  * @param tick_point_a  pointer to point 'a' of the tick
1010  * @param tick_point_b  pointer to point 'b' of the tick
1011  */
scale_get_tick_points(lv_obj_t * obj,const uint32_t tick_idx,bool is_major_tick,lv_point_t * tick_point_a,lv_point_t * tick_point_b)1012 static void scale_get_tick_points(lv_obj_t * obj, const uint32_t tick_idx, bool is_major_tick,
1013                                   lv_point_t * tick_point_a, lv_point_t * tick_point_b)
1014 {
1015     lv_scale_t * scale = (lv_scale_t *)obj;
1016 
1017     /* Main line style */
1018     lv_draw_line_dsc_t main_line_dsc;
1019     lv_draw_line_dsc_init(&main_line_dsc);
1020     lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &main_line_dsc);
1021 
1022     int32_t minor_len = 0;
1023     int32_t major_len = 0;
1024     int32_t radial_offset = 0;
1025 
1026     if(is_major_tick) {
1027         major_len = lv_obj_get_style_length(obj, LV_PART_INDICATOR);
1028         radial_offset = lv_obj_get_style_radial_offset(obj, LV_PART_INDICATOR);
1029     }
1030     else {
1031         minor_len = lv_obj_get_style_length(obj, LV_PART_ITEMS);
1032         radial_offset = lv_obj_get_style_radial_offset(obj, LV_PART_ITEMS);
1033     }
1034 
1035     if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)
1036        || (LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode || LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode)) {
1037 
1038         /* Get style properties so they can be used in the tick and label drawing */
1039         const int32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
1040         const int32_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
1041         const int32_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN) + border_width;
1042         const int32_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width;
1043         const int32_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
1044         const int32_t tick_pad_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS);
1045         const int32_t tick_pad_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS);
1046         const int32_t tick_pad_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS);
1047         const int32_t tick_pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS);
1048 
1049         int32_t x_ofs = 0;
1050         int32_t y_ofs = 0;
1051 
1052         if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) {
1053             x_ofs = obj->coords.x2 + (main_line_dsc.width / 2) - pad_right;
1054             y_ofs = obj->coords.y1 + (pad_top + tick_pad_top);
1055         }
1056         else if(LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
1057             x_ofs = obj->coords.x1 + (main_line_dsc.width / 2) + pad_left;
1058             y_ofs = obj->coords.y1 + (pad_top + tick_pad_top);
1059         }
1060         else if(LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode) {
1061             x_ofs = obj->coords.x1 + (pad_right + tick_pad_right);
1062             y_ofs = obj->coords.y1 + (main_line_dsc.width / 2) + pad_top;
1063         }
1064         /* LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode */
1065         else {
1066             x_ofs = obj->coords.x1 + (pad_left + tick_pad_left);
1067             y_ofs = obj->coords.y2 + (main_line_dsc.width / 2) - pad_bottom;
1068         }
1069 
1070         /* Adjust length when tick will be drawn on horizontal top or vertical right scales */
1071         if((LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode) || (LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)) {
1072             if(is_major_tick) {
1073                 major_len *= -1;
1074             }
1075             else {
1076                 minor_len *= -1;
1077             }
1078         }
1079         else { /* Nothing to do */ }
1080 
1081         const int32_t tick_length = is_major_tick ? major_len : minor_len;
1082         /* NOTE
1083          * Minus 1 because tick count starts at 0
1084          * TODO
1085          * What if total_tick_count is 1? This will lead to an division by 0 further down */
1086         const uint32_t tmp_tick_count = scale->total_tick_count - 1U;
1087 
1088         /* Calculate the position of the tick points based on the mode and tick index */
1089         if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
1090             /* Vertical position starts at y2 of the scale main line, we start at y2 because the ticks are drawn from bottom to top */
1091             int32_t vertical_position = obj->coords.y2 - (pad_bottom + tick_pad_bottom);
1092 
1093             /* Position the last tick */
1094             if(tmp_tick_count == tick_idx) {
1095                 vertical_position = y_ofs;
1096             }
1097             /* Otherwise adjust the tick position depending of its index and number of ticks on the scale */
1098             else if(0 != tick_idx) {
1099                 const int32_t scale_total_height = lv_obj_get_height(obj) - (pad_top + pad_bottom + tick_pad_top + tick_pad_bottom);
1100                 const int32_t offset = ((int32_t) tick_idx * (int32_t) scale_total_height) / (int32_t)(tmp_tick_count);
1101                 vertical_position -= offset;
1102             }
1103             else { /* Nothing to do */ }
1104 
1105             tick_point_a->x = x_ofs - 1; /* Move extra pixel out of scale boundary */
1106             tick_point_a->y = vertical_position;
1107             tick_point_b->x = tick_point_a->x - tick_length;
1108             tick_point_b->y = vertical_position;
1109         }
1110         else {
1111             /* Horizontal position starts at x1 of the scale main line */
1112             int32_t horizontal_position = x_ofs;
1113 
1114             /* Position the last tick */
1115             if(tmp_tick_count == tick_idx) {
1116                 horizontal_position = obj->coords.x2 - (pad_left + tick_pad_left);
1117             }
1118             /* Otherwise adjust the tick position depending of its index and number of ticks on the scale */
1119             else if(0U != tick_idx) {
1120                 const int32_t scale_total_width = lv_obj_get_width(obj) - (pad_right + pad_left + tick_pad_right + tick_pad_left);
1121                 const int32_t offset = ((int32_t) tick_idx * (int32_t) scale_total_width) / (int32_t)(tmp_tick_count);
1122                 horizontal_position += offset;
1123             }
1124             else { /* Nothing to do */ }
1125 
1126             tick_point_a->x = horizontal_position;
1127             tick_point_a->y = y_ofs;
1128             tick_point_b->x = horizontal_position;
1129             tick_point_b->y = tick_point_a->y + tick_length;
1130         }
1131     }
1132     else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
1133         lv_area_t scale_area;
1134         lv_obj_get_content_coords(obj, &scale_area);
1135 
1136         /* Find the center of the scale */
1137         lv_point_t center_point;
1138         const int32_t radius_edge = LV_MIN(lv_area_get_width(&scale_area) / 2, lv_area_get_height(&scale_area) / 2);
1139         center_point.x = scale_area.x1 + radius_edge;
1140         center_point.y = scale_area.y1 + radius_edge;
1141 
1142         int32_t angle_upscale = (int32_t)((tick_idx * scale->angle_range) * 10U) / (scale->total_tick_count - 1U);
1143         angle_upscale += scale->rotation * 10;
1144 
1145         /* Draw a little bit longer lines to be sure the mask will clip them correctly
1146          * and to get a better precision. Adding the main line width to the calculation so we don't have gaps
1147          * between the arc and the ticks */
1148         int32_t point_closer_to_arc = 0;
1149         int32_t adjusted_radio_with_tick_len = 0;
1150         if(LV_SCALE_MODE_ROUND_INNER == scale->mode) {
1151             point_closer_to_arc = radius_edge - main_line_dsc.width;
1152             adjusted_radio_with_tick_len = point_closer_to_arc - (is_major_tick ? major_len : minor_len);
1153         }
1154         /* LV_SCALE_MODE_ROUND_OUTER == scale->mode */
1155         else {
1156             point_closer_to_arc = radius_edge - main_line_dsc.width;
1157             adjusted_radio_with_tick_len = point_closer_to_arc + (is_major_tick ? major_len : minor_len);
1158         }
1159 
1160         tick_point_a->x = center_point.x + point_closer_to_arc + radial_offset;
1161         tick_point_a->y = center_point.y;
1162         lv_point_transform(tick_point_a, angle_upscale, LV_SCALE_NONE, LV_SCALE_NONE, &center_point, false);
1163 
1164         tick_point_b->x = center_point.x + adjusted_radio_with_tick_len + radial_offset;
1165         tick_point_b->y = center_point.y;
1166         lv_point_transform(tick_point_b, angle_upscale, LV_SCALE_NONE, LV_SCALE_NONE, &center_point, false);
1167     }
1168     else { /* Nothing to do */ }
1169 }
1170 
1171 /**
1172  * Get coordinates for label
1173  *
1174  * @param obj       pointer to a scale object
1175  * @param label_dsc pointer to label descriptor
1176  * @param tick_point    pointer to reference tick
1177  * @param label_coords  pointer to label coordinates output
1178  */
scale_get_label_coords(lv_obj_t * obj,lv_draw_label_dsc_t * label_dsc,lv_point_t * tick_point,lv_area_t * label_coords)1179 static void scale_get_label_coords(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc, lv_point_t * tick_point,
1180                                    lv_area_t * label_coords)
1181 {
1182     lv_scale_t * scale = (lv_scale_t *)obj;
1183 
1184     /* Reserve appropriate size for the tick label */
1185     lv_point_t label_size;
1186     lv_text_get_size(&label_size, label_dsc->text,
1187                      label_dsc->font, label_dsc->letter_space, label_dsc->line_space, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
1188 
1189     /* Set the label draw area at some distance of the major tick */
1190     if((LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode) || (LV_SCALE_MODE_HORIZONTAL_TOP == scale->mode)) {
1191         label_coords->x1 = tick_point->x - (label_size.x / 2);
1192         label_coords->x2 = tick_point->x + (label_size.x / 2);
1193 
1194         if(LV_SCALE_MODE_HORIZONTAL_BOTTOM == scale->mode) {
1195             label_coords->y1 = tick_point->y + lv_obj_get_style_pad_bottom(obj, LV_PART_INDICATOR);
1196             label_coords->y2 = label_coords->y1 + label_size.y;
1197         }
1198         else {
1199             label_coords->y2 = tick_point->y - lv_obj_get_style_pad_top(obj, LV_PART_INDICATOR);
1200             label_coords->y1 = label_coords->y2 - label_size.y;
1201         }
1202     }
1203     else if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) || (LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)) {
1204         label_coords->y1 = tick_point->y - (label_size.y / 2);
1205         label_coords->y2 = tick_point->y + (label_size.y / 2);
1206 
1207         if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) {
1208             label_coords->x1 = tick_point->x - label_size.x - lv_obj_get_style_pad_left(obj, LV_PART_INDICATOR);
1209             label_coords->x2 = tick_point->x - lv_obj_get_style_pad_left(obj, LV_PART_INDICATOR);
1210         }
1211         else {
1212             label_coords->x1 = tick_point->x + lv_obj_get_style_pad_right(obj, LV_PART_INDICATOR);
1213             label_coords->x2 = tick_point->x + label_size.x + lv_obj_get_style_pad_right(obj, LV_PART_INDICATOR);
1214         }
1215     }
1216     else if(LV_SCALE_MODE_ROUND_OUTER == scale->mode || LV_SCALE_MODE_ROUND_INNER == scale->mode) {
1217         label_coords->x1 = tick_point->x - (label_size.x / 2);
1218         label_coords->y1 = tick_point->y - (label_size.y / 2);
1219         label_coords->x2 = label_coords->x1 + label_size.x;
1220         label_coords->y2 = label_coords->y1 + label_size.y;
1221     }
1222     else { /* Nothing to do */ }
1223 }
1224 
1225 /**
1226  * Set line properties
1227  *
1228  * Checks if the line has a custom section configuration or not and sets the properties accordingly.
1229  *
1230  * @param obj       pointer to a scale object
1231  * @param line_dsc  pointer to line descriptor
1232  * @param items_section_style  pointer to indicator section style
1233  * @param part      line part, example: LV_PART_INDICATOR, LV_PART_ITEMS, LV_PART_MAIN
1234  */
scale_set_line_properties(lv_obj_t * obj,lv_draw_line_dsc_t * line_dsc,lv_style_t * section_style,lv_part_t part)1235 static void scale_set_line_properties(lv_obj_t * obj, lv_draw_line_dsc_t * line_dsc, lv_style_t * section_style,
1236                                       lv_part_t part)
1237 {
1238     if(section_style) {
1239         lv_style_value_t value;
1240         lv_style_res_t res;
1241 
1242         /* Line width */
1243         res = lv_style_get_prop(section_style, LV_STYLE_LINE_WIDTH, &value);
1244         if(res == LV_STYLE_RES_FOUND) {
1245             line_dsc->width = (int32_t)value.num;
1246         }
1247         else {
1248             line_dsc->width = lv_obj_get_style_line_width(obj, part);
1249         }
1250 
1251         /* Line color */
1252         res = lv_style_get_prop(section_style, LV_STYLE_LINE_COLOR, &value);
1253         if(res == LV_STYLE_RES_FOUND) {
1254             line_dsc->color = value.color;
1255         }
1256         else {
1257             line_dsc->color = lv_obj_get_style_line_color(obj, part);
1258         }
1259 
1260         /* Line opa */
1261         res = lv_style_get_prop(section_style, LV_STYLE_LINE_OPA, &value);
1262         if(res == LV_STYLE_RES_FOUND) {
1263             line_dsc->opa = (lv_opa_t)value.num;
1264         }
1265         else {
1266             line_dsc->opa = lv_obj_get_style_line_opa(obj, part);
1267         }
1268     }
1269     else {
1270         line_dsc->color = lv_obj_get_style_line_color(obj, part);
1271         line_dsc->opa = lv_obj_get_style_line_opa(obj, part);
1272         line_dsc->width = lv_obj_get_style_line_width(obj, part);
1273     }
1274 }
1275 
1276 /**
1277  * Set arc properties
1278  *
1279  * Checks if the arc has a custom section configuration or not and sets the properties accordingly.
1280  *
1281  * @param obj       pointer to a scale object
1282  * @param arc_dsc  pointer to arc descriptor
1283  * @param items_section_style  pointer to indicator section style
1284  */
scale_set_arc_properties(lv_obj_t * obj,lv_draw_arc_dsc_t * arc_dsc,lv_style_t * section_style)1285 static void scale_set_arc_properties(lv_obj_t * obj, lv_draw_arc_dsc_t * arc_dsc, lv_style_t * section_style)
1286 {
1287     if(section_style) {
1288         lv_style_value_t value;
1289         lv_style_res_t res;
1290 
1291         /* arc width */
1292         res = lv_style_get_prop(section_style, LV_STYLE_ARC_WIDTH, &value);
1293         if(res == LV_STYLE_RES_FOUND) {
1294             arc_dsc->width = (int32_t)value.num;
1295         }
1296         else {
1297             arc_dsc->width = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
1298         }
1299 
1300         /* arc color */
1301         res = lv_style_get_prop(section_style, LV_STYLE_ARC_COLOR, &value);
1302         if(res == LV_STYLE_RES_FOUND) {
1303             arc_dsc->color = value.color;
1304         }
1305         else {
1306             arc_dsc->color = lv_obj_get_style_arc_color(obj, LV_PART_MAIN);
1307         }
1308 
1309         /* arc opa */
1310         res = lv_style_get_prop(section_style, LV_STYLE_ARC_OPA, &value);
1311         if(res == LV_STYLE_RES_FOUND) {
1312             arc_dsc->opa = (lv_opa_t)value.num;
1313         }
1314         else {
1315             arc_dsc->opa = lv_obj_get_style_arc_opa(obj, LV_PART_MAIN);
1316         }
1317 
1318         /* arc rounded */
1319         res = lv_style_get_prop(section_style, LV_STYLE_ARC_ROUNDED, &value);
1320         if(res == LV_STYLE_RES_FOUND) {
1321             arc_dsc->rounded = (uint8_t)value.num;
1322         }
1323         else {
1324             arc_dsc->rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_MAIN);
1325         }
1326 
1327         /* arc image src */
1328         res = lv_style_get_prop(section_style, LV_STYLE_ARC_IMAGE_SRC, &value);
1329         if(res == LV_STYLE_RES_FOUND) {
1330             arc_dsc->img_src = (const void *)value.ptr;
1331         }
1332         else {
1333             arc_dsc->img_src = lv_obj_get_style_arc_image_src(obj, LV_PART_MAIN);
1334         }
1335     }
1336     else {
1337         arc_dsc->color = lv_obj_get_style_arc_color(obj, LV_PART_MAIN);
1338         arc_dsc->opa = lv_obj_get_style_arc_opa(obj, LV_PART_MAIN);
1339         arc_dsc->width = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
1340         arc_dsc->rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_MAIN);
1341         arc_dsc->img_src = lv_obj_get_style_arc_image_src(obj, LV_PART_MAIN);
1342     }
1343 }
1344 
1345 /**
1346  * Set indicator label properties
1347  *
1348  * Checks if the indicator has a custom section configuration or not and sets the properties accordingly.
1349  *
1350  * @param obj       pointer to a scale object
1351  * @param label_dsc  pointer to label descriptor
1352  * @param items_section_style  pointer to indicator section style
1353  */
scale_set_indicator_label_properties(lv_obj_t * obj,lv_draw_label_dsc_t * label_dsc,lv_style_t * indicator_section_style)1354 static void scale_set_indicator_label_properties(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc,
1355                                                  lv_style_t * indicator_section_style)
1356 {
1357     if(indicator_section_style) {
1358         lv_style_value_t value;
1359         lv_style_res_t res;
1360 
1361         /* Text color */
1362         res = lv_style_get_prop(indicator_section_style, LV_STYLE_TEXT_COLOR, &value);
1363         if(res == LV_STYLE_RES_FOUND) {
1364             label_dsc->color = value.color;
1365         }
1366         else {
1367             label_dsc->color = lv_obj_get_style_text_color(obj, LV_PART_INDICATOR);
1368         }
1369 
1370         /* Text opa */
1371         res = lv_style_get_prop(indicator_section_style, LV_STYLE_TEXT_OPA, &value);
1372         if(res == LV_STYLE_RES_FOUND) {
1373             label_dsc->opa = (lv_opa_t)value.num;
1374         }
1375         else {
1376             label_dsc->opa = lv_obj_get_style_text_opa(obj, LV_PART_INDICATOR);
1377         }
1378 
1379         /* Text letter space */
1380         res = lv_style_get_prop(indicator_section_style, LV_STYLE_TEXT_LETTER_SPACE, &value);
1381         if(res == LV_STYLE_RES_FOUND) {
1382             label_dsc->letter_space = (int32_t)value.num;
1383         }
1384         else {
1385             label_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_INDICATOR);
1386         }
1387 
1388         /* Text font */
1389         res = lv_style_get_prop(indicator_section_style, LV_STYLE_TEXT_FONT, &value);
1390         if(res == LV_STYLE_RES_FOUND) {
1391             label_dsc->font = (const lv_font_t *)value.ptr;
1392         }
1393         else {
1394             label_dsc->font = lv_obj_get_style_text_font(obj, LV_PART_INDICATOR);
1395         }
1396     }
1397     else {
1398         /* If label is not within a range then get the indicator style */
1399         label_dsc->color = lv_obj_get_style_text_color(obj, LV_PART_INDICATOR);
1400         label_dsc->opa = lv_obj_get_style_text_opa(obj, LV_PART_INDICATOR);
1401         label_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_INDICATOR);
1402         label_dsc->font = lv_obj_get_style_text_font(obj, LV_PART_INDICATOR);
1403     }
1404 }
1405 
scale_find_section_tick_idx(lv_obj_t * obj)1406 static void scale_find_section_tick_idx(lv_obj_t * obj)
1407 {
1408     lv_scale_t * scale = (lv_scale_t *)obj;
1409 
1410     const int32_t min_out = scale->range_min;
1411     const int32_t max_out = scale->range_max;
1412     const uint32_t total_tick_count = scale->total_tick_count;
1413 
1414     /* Section handling */
1415     uint32_t tick_idx = 0;
1416     for(tick_idx = 0; tick_idx < total_tick_count; tick_idx++) {
1417         bool is_major_tick = false;
1418         if(tick_idx % scale->major_tick_every == 0) is_major_tick = true;
1419 
1420         const int32_t tick_value = lv_map(tick_idx, 0, total_tick_count - 1, min_out, max_out);
1421 
1422         lv_scale_section_t * section;
1423         LV_LL_READ_BACK(&scale->section_ll, section) {
1424             if(section->range_min <= tick_value && section->range_max >= tick_value) {
1425                 if(LV_SCALE_TICK_IDX_DEFAULT_ID == section->first_tick_idx_in_section) {
1426                     section->first_tick_idx_in_section = tick_idx;
1427                     section->first_tick_idx_is_major = is_major_tick;
1428                 }
1429                 if(LV_SCALE_TICK_IDX_DEFAULT_ID == section->last_tick_idx_in_section) {
1430                     /* This gets it initialized when the beginning and ending range values are the same. */
1431                     section->last_tick_idx_in_section = tick_idx;
1432                     section->last_tick_idx_is_major = is_major_tick;
1433                 }
1434                 /* Now keep setting the `last_tick_idx_...` values as we
1435                  * proceed through the `for` loop so it is left with the
1436                  * actual last-tick value that is within the Scale's range. */
1437                 else if(section->first_tick_idx_in_section != tick_idx) {
1438                     section->last_tick_idx_in_section = tick_idx;
1439                     section->last_tick_idx_is_major = is_major_tick;
1440                 }
1441             }
1442             else {
1443                 /* `tick_value` is outside Section's range.
1444                  * Nothing to do. */
1445             }
1446         }
1447     }
1448 }
1449 
1450 /**
1451  * Stores the width of the initial and last tick of the main line
1452  *
1453  * This width is used to compensate the main line drawing taking into consideration the widths of both ticks
1454  *
1455  * @param obj       pointer to a scale object
1456  * @param tick_idx  index of the current tick
1457  * @param is_major_tick true if tick_idx is a major tick
1458  * @param major_tick_width width of the major tick
1459  * @param minor_tick_width width of the minor tick
1460  */
scale_store_main_line_tick_width_compensation(lv_obj_t * obj,const uint32_t tick_idx,const bool is_major_tick,const int32_t major_tick_width,const int32_t minor_tick_width)1461 static void scale_store_main_line_tick_width_compensation(lv_obj_t * obj, const uint32_t tick_idx,
1462                                                           const bool is_major_tick, const int32_t major_tick_width, const int32_t minor_tick_width)
1463 {
1464     lv_scale_t * scale = (lv_scale_t *)obj;
1465     const bool is_first_tick = 0U == tick_idx;
1466     const bool is_last_tick = scale->total_tick_count == tick_idx;
1467     const int32_t tick_width = is_major_tick ? major_tick_width : minor_tick_width;
1468 
1469     /* Exit early if tick_idx is neither the first nor last tick on the main line */
1470     if(((!is_last_tick) && (!is_first_tick))
1471        /* Exit early if scale mode is round. It doesn't support main line compensation */
1472        || ((LV_SCALE_MODE_ROUND_INNER == scale->mode) || (LV_SCALE_MODE_ROUND_OUTER == scale->mode))) {
1473         return;
1474     }
1475 
1476     if(is_last_tick) {
1477         /* Mode is vertical */
1478         if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) || (LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)) {
1479             scale->last_tick_width = tick_width;
1480         }
1481         /* Mode is horizontal */
1482         else {
1483             scale->first_tick_width = tick_width;
1484         }
1485     }
1486     /* is_first_tick */
1487     else {
1488         /* Mode is vertical */
1489         if((LV_SCALE_MODE_VERTICAL_LEFT == scale->mode) || (LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode)) {
1490             scale->first_tick_width = tick_width;
1491         }
1492         /* Mode is horizontal */
1493         else {
1494             scale->last_tick_width = tick_width;
1495         }
1496     }
1497 }
1498 
1499 /**
1500  * Sets the text of the tick label descriptor when using custom labels
1501  *
1502  * Sets the text pointer when valid custom label is available, otherwise set it to NULL.
1503  *
1504  * @param obj       pointer to a scale object
1505  * @param label_dsc pointer to the label descriptor
1506  * @param major_tick_idx  index of the current major tick
1507  */
scale_build_custom_label_text(lv_obj_t * obj,lv_draw_label_dsc_t * label_dsc,const uint16_t major_tick_idx)1508 static void scale_build_custom_label_text(lv_obj_t * obj, lv_draw_label_dsc_t * label_dsc,
1509                                           const uint16_t major_tick_idx)
1510 {
1511     lv_scale_t * scale = (lv_scale_t *) obj;
1512 
1513     /* Check if the scale has valid custom labels available,
1514      * this avoids reading past txt_src array when the scale requires more tick labels than available */
1515     if(major_tick_idx <= scale->custom_label_cnt) {
1516         if(scale->txt_src[major_tick_idx - 1U]) {
1517             label_dsc->text = scale->txt_src[major_tick_idx - 1U];
1518             label_dsc->text_local = 0;
1519         }
1520         else {
1521             label_dsc->text = NULL;
1522         }
1523     }
1524     else {
1525         label_dsc->text = NULL;
1526     }
1527 }
1528 
1529 /**
1530  * Stores tick width compensation information for main line sections
1531  *
1532  * @param obj       pointer to a scale object
1533  * @param is_major_tick Indicates if tick is major or not
1534  * @param major_tick_dsc pointer to the major_tick_dsc
1535  * @param minor_tick_dsc pointer to the minor_tick_dsc
1536  * @param tick_value Current tick value, used to know if tick_idx belongs to a section or not
1537  * @param tick_idx Current tick index
1538  * @param tick_point_a Pointer to tick point a
1539  */
scale_store_section_line_tick_width_compensation(lv_obj_t * obj,const bool is_major_tick,lv_draw_line_dsc_t * major_tick_dsc,lv_draw_line_dsc_t * minor_tick_dsc,const int32_t tick_value,const uint8_t tick_idx,lv_point_t * tick_point_a)1540 static void scale_store_section_line_tick_width_compensation(lv_obj_t * obj, const bool is_major_tick,
1541                                                              lv_draw_line_dsc_t * major_tick_dsc, lv_draw_line_dsc_t * minor_tick_dsc,
1542                                                              const int32_t tick_value, const uint8_t tick_idx, lv_point_t * tick_point_a)
1543 {
1544     lv_scale_t * scale = (lv_scale_t *) obj;
1545     lv_scale_section_t * section;
1546 
1547     LV_LL_READ_BACK(&scale->section_ll, section) {
1548         if(section->range_min <= tick_value && section->range_max >= tick_value) {
1549             if(is_major_tick) {
1550                 scale_set_line_properties(obj, major_tick_dsc, section->indicator_style, LV_PART_INDICATOR);
1551             }
1552             else {
1553                 scale_set_line_properties(obj, minor_tick_dsc, section->items_style, LV_PART_ITEMS);
1554             }
1555         }
1556 
1557         int32_t tmp_width = 0;
1558 
1559         if(tick_idx == section->first_tick_idx_in_section) {
1560             if(section->first_tick_idx_is_major) {
1561                 tmp_width = major_tick_dsc->width;
1562             }
1563             else {
1564                 tmp_width = minor_tick_dsc->width;
1565             }
1566 
1567             section->first_tick_in_section = *tick_point_a;
1568             /* Add 1px as adjustment if tmp_width is odd */
1569             if(tmp_width & 0x01U) {
1570                 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
1571                     tmp_width += 1;
1572                 }
1573                 else {
1574                     tmp_width -= 1;
1575                 }
1576             }
1577             section->first_tick_in_section_width = tmp_width;
1578         }
1579 
1580         /* This can also apply when
1581          * (tick_idx == section->first_tick_idx_in_section) when the
1582          * beginning and ending vlues of the range are the same. */
1583         if(tick_idx == section->last_tick_idx_in_section) {
1584             if(section->last_tick_idx_is_major) {
1585                 tmp_width = major_tick_dsc->width;
1586             }
1587             else {
1588                 tmp_width = minor_tick_dsc->width;
1589             }
1590 
1591             section->last_tick_in_section = *tick_point_a;
1592             /* Add 1px as adjustment if tmp_width is odd */
1593             if(tmp_width & 0x01U) {
1594                 if(LV_SCALE_MODE_VERTICAL_LEFT == scale->mode || LV_SCALE_MODE_VERTICAL_RIGHT == scale->mode) {
1595                     tmp_width -= 1;
1596                 }
1597                 else {
1598                     tmp_width += 1;
1599                 }
1600             }
1601             section->last_tick_in_section_width = tmp_width;
1602         }
1603         else { /* Nothing to do */ }
1604     }
1605 }
1606 
scale_free_line_needle_points_cb(lv_event_t * e)1607 static void scale_free_line_needle_points_cb(lv_event_t * e)
1608 {
1609     lv_point_precise_t * needle_line_points = lv_event_get_user_data(e);
1610     lv_free(needle_line_points);
1611 }
1612 
1613 #endif
1614