1 /**
2 * @file lv_gauge.c
3 *
4 */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_gauge.h"
10 #if LV_USE_GAUGE != 0
11 
12 #include "../lv_misc/lv_debug.h"
13 #include "../lv_draw/lv_draw.h"
14 #include "../lv_themes/lv_theme.h"
15 #include "../lv_misc/lv_txt.h"
16 #include "../lv_misc/lv_math.h"
17 #include "../lv_misc/lv_utils.h"
18 #include "lv_img.h"
19 #include <stdio.h>
20 #include <string.h>
21 
22 /*********************
23  *      DEFINES
24  *********************/
25 #define LV_OBJX_NAME "lv_gauge"
26 
27 #define LV_GAUGE_DEF_NEEDLE_COLOR LV_COLOR_RED
28 #define LV_GAUGE_DEF_LABEL_COUNT 6
29 #define LV_GAUGE_DEF_LINE_COUNT 21 /*Should be: ((label_cnt - 1) * internal_lines) + 1*/
30 #define LV_GAUGE_DEF_ANGLE 270
31 
32 /**********************
33  *      TYPEDEFS
34  **********************/
35 
36 /**********************
37  *  STATIC PROTOTYPES
38  **********************/
39 static lv_design_res_t lv_gauge_design(lv_obj_t * gauge, const lv_area_t * clip_area, lv_design_mode_t mode);
40 static lv_res_t lv_gauge_signal(lv_obj_t * gauge, lv_signal_t sign, void * param);
41 static lv_style_list_t * lv_gauge_get_style(lv_obj_t * gauge, uint8_t part);
42 static void lv_gauge_draw_labels(lv_obj_t * gauge, const lv_area_t * mask);
43 static void lv_gauge_draw_needle(lv_obj_t * gauge, const lv_area_t * clip_area);
44 
45 /**********************
46  *  STATIC VARIABLES
47  **********************/
48 static lv_design_cb_t ancestor_design;
49 static lv_signal_cb_t ancestor_signal;
50 
51 /**********************
52  *      MACROS
53  **********************/
54 
55 /**********************
56  *   GLOBAL FUNCTIONS
57  **********************/
58 
59 /**
60  * Create a gauge objects
61  * @param par pointer to an object, it will be the parent of the new gauge
62  * @param copy pointer to a gauge object, if not NULL then the new object will be copied from it
63  * @return pointer to the created gauge
64  */
lv_gauge_create(lv_obj_t * par,const lv_obj_t * copy)65 lv_obj_t * lv_gauge_create(lv_obj_t * par, const lv_obj_t * copy)
66 {
67     LV_LOG_TRACE("gauge create started");
68 
69     /*Create the ancestor gauge*/
70     lv_obj_t * gauge = lv_linemeter_create(par, copy);
71     LV_ASSERT_MEM(gauge);
72     if(gauge == NULL) return NULL;
73 
74     /*Allocate the gauge type specific extended data*/
75     lv_gauge_ext_t * ext = lv_obj_allocate_ext_attr(gauge, sizeof(lv_gauge_ext_t));
76     LV_ASSERT_MEM(ext);
77     if(ext == NULL) {
78         lv_obj_del(gauge);
79         return NULL;
80     }
81 
82     /*Initialize the allocated 'ext' */
83     ext->needle_count  = 0;
84     ext->values        = NULL;
85     ext->needle_colors = NULL;
86     ext->label_count   = LV_GAUGE_DEF_LABEL_COUNT;
87     ext->format_cb     = NULL;
88 
89     ext->needle_img = 0;
90     ext->needle_img_pivot.x = 0;
91     ext->needle_img_pivot.y = 0;
92     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(gauge);
93     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(gauge);
94 
95     lv_style_list_init(&ext->style_strong);
96     lv_style_list_init(&ext->style_needle);
97 
98     /*The signal and design functions are not copied so set them here*/
99     lv_obj_set_signal_cb(gauge, lv_gauge_signal);
100     lv_obj_set_design_cb(gauge, lv_gauge_design);
101 
102     /*Init the new gauge gauge*/
103     if(copy == NULL) {
104         lv_gauge_set_scale(gauge, LV_GAUGE_DEF_ANGLE, LV_GAUGE_DEF_LINE_COUNT, LV_GAUGE_DEF_LABEL_COUNT);
105         lv_gauge_set_needle_count(gauge, 1, NULL);
106         lv_gauge_set_critical_value(gauge, 80);
107 
108         lv_theme_apply(gauge, LV_THEME_GAUGE);
109 
110     }
111     /*Copy an existing gauge*/
112     else {
113         lv_gauge_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
114         lv_gauge_set_needle_count(gauge, copy_ext->needle_count, copy_ext->needle_colors);
115 
116         lv_style_list_copy(&ext->style_strong, &copy_ext->style_strong);
117         lv_style_list_copy(&ext->style_needle, &copy_ext->style_needle);
118 
119         uint8_t i;
120         for(i = 0; i < ext->needle_count; i++) {
121             ext->values[i] = copy_ext->values[i];
122         }
123         ext->label_count = copy_ext->label_count;
124         ext->format_cb   = copy_ext->format_cb;
125 
126         /*Refresh the style with new signal function*/
127         lv_obj_refresh_style(gauge, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
128     }
129 
130     LV_LOG_INFO("gauge created");
131 
132     return gauge;
133 }
134 
135 /*=====================
136  * Setter functions
137  *====================*/
138 
139 /**
140  * Set the number of needles
141  * @param gauge pointer to gauge object
142  * @param needle_cnt new count of needles
143  * @param colors an array of colors for needles (with 'num' elements)
144  */
lv_gauge_set_needle_count(lv_obj_t * gauge,uint8_t needle_cnt,const lv_color_t colors[])145 void lv_gauge_set_needle_count(lv_obj_t * gauge, uint8_t needle_cnt, const lv_color_t colors[])
146 {
147     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
148 
149     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
150 
151     if(ext->needle_count != needle_cnt) {
152         if(ext->values != NULL) {
153             lv_mem_free(ext->values);
154             ext->values = NULL;
155         }
156 
157         ext->values = lv_mem_realloc(ext->values, needle_cnt * sizeof(ext->values[0]));
158         LV_ASSERT_MEM(ext->values);
159         if(ext->values == NULL) return;
160 
161         int16_t min = lv_gauge_get_min_value(gauge);
162         uint8_t n;
163         for(n = ext->needle_count; n < needle_cnt; n++) {
164             ext->values[n] = min;
165         }
166 
167         ext->needle_count = needle_cnt;
168     }
169 
170     ext->needle_colors = colors;
171     lv_obj_invalidate(gauge);
172 }
173 
174 /**
175  * Set the value of a needle
176  * @param gauge pointer to a gauge
177  * @param needle_id the id of the needle
178  * @param value the new value
179  */
lv_gauge_set_value(lv_obj_t * gauge,uint8_t needle_id,int32_t value)180 void lv_gauge_set_value(lv_obj_t * gauge, uint8_t needle_id, int32_t value)
181 {
182     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
183 
184     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
185 
186     if(needle_id >= ext->needle_count) return;
187     if(ext->values[needle_id] == value) return;
188 
189     int16_t min = lv_gauge_get_min_value(gauge);
190     int16_t max = lv_gauge_get_max_value(gauge);
191 
192     if(value > max)
193         value = max;
194     else if(value < min)
195         value = min;
196 
197     int32_t old_value = ext->values[needle_id];
198     ext->values[needle_id] = value;
199 
200     //    lv_obj_invalidate(gauge);
201 
202     lv_style_int_t pad = lv_obj_get_style_pad_inner(gauge, LV_GAUGE_PART_NEEDLE);
203     lv_style_int_t left = lv_obj_get_style_pad_left(gauge, LV_GAUGE_PART_MAIN);
204     lv_style_int_t right = lv_obj_get_style_pad_right(gauge, LV_GAUGE_PART_MAIN);
205     lv_style_int_t top = lv_obj_get_style_pad_top(gauge, LV_GAUGE_PART_MAIN);
206     lv_coord_t r      = (lv_obj_get_width(gauge) - left - right) / 2 - pad;
207     lv_coord_t x_ofs  = gauge->coords.x1 + r + left + pad;
208     lv_coord_t y_ofs  = gauge->coords.y1 + r + top + pad;
209     uint16_t angle    = lv_linemeter_get_scale_angle(gauge);
210     int16_t angle_ofs = 90 + (360 - angle) / 2 + lv_gauge_get_angle_offset(gauge);
211     lv_point_t p_mid;
212     lv_point_t p_end;
213     lv_coord_t needle_w;
214 
215     if(ext->needle_img) {
216         lv_img_header_t info;
217         lv_img_decoder_get_info(ext->needle_img, &info);
218         needle_w = info.h;
219     }
220     else {
221         needle_w = lv_obj_get_style_line_width(gauge, LV_GAUGE_PART_NEEDLE);
222     }
223 
224     p_mid.x = x_ofs;
225     p_mid.y = y_ofs;
226     /*Calculate the end point of a needle*/
227     int16_t needle_angle = (old_value - min) * angle / (max - min) + angle_ofs;
228 
229     p_end.y = (_lv_trigo_sin(needle_angle) * r) / LV_TRIGO_SIN_MAX + y_ofs;
230     p_end.x = (_lv_trigo_sin(needle_angle + 90) * r) / LV_TRIGO_SIN_MAX + x_ofs;
231 
232     lv_area_t a;
233     a.x1 = LV_MATH_MIN(p_mid.x, p_end.x) - needle_w;
234     a.y1 = LV_MATH_MIN(p_mid.y, p_end.y) - needle_w;
235     a.x2 = LV_MATH_MAX(p_mid.x, p_end.x) + needle_w;
236     a.y2 = LV_MATH_MAX(p_mid.y, p_end.y) + needle_w;
237     lv_obj_invalidate_area(gauge, &a);
238 
239     needle_angle = (value - min) * angle / (max - min) + angle_ofs;
240     p_end.y = (_lv_trigo_sin(needle_angle) * r) / LV_TRIGO_SIN_MAX + y_ofs;
241     p_end.x = (_lv_trigo_sin(needle_angle + 90) * r) / LV_TRIGO_SIN_MAX + x_ofs;
242 
243     a.x1 = LV_MATH_MIN(p_mid.x, p_end.x) - needle_w;
244     a.y1 = LV_MATH_MIN(p_mid.y, p_end.y) - needle_w;
245     a.x2 = LV_MATH_MAX(p_mid.x, p_end.x) + needle_w;
246     a.y2 = LV_MATH_MAX(p_mid.y, p_end.y) + needle_w;
247     lv_obj_invalidate_area(gauge, &a);
248 
249 
250 }
251 
252 /**
253  * Set the scale settings of a gauge
254  * @param gauge pointer to a gauge object
255  * @param angle angle of the scale (0..360)
256  * @param line_cnt count of scale lines.
257  * To get a given "subdivision" lines between labels:
258  * `line_cnt = (sub_div + 1) * (label_cnt - 1) + 1 `
259  * @param label_cnt count of scale labels.
260  */
lv_gauge_set_scale(lv_obj_t * gauge,uint16_t angle,uint8_t line_cnt,uint8_t label_cnt)261 void lv_gauge_set_scale(lv_obj_t * gauge, uint16_t angle, uint8_t line_cnt, uint8_t label_cnt)
262 {
263     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
264 
265     lv_linemeter_set_scale(gauge, angle, line_cnt);
266 
267     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
268     ext->label_count     = label_cnt;
269     lv_obj_invalidate(gauge);
270 }
271 
272 /**
273  * Set an image to display as needle(s).
274  * The needle image should be horizontal and pointing to the right (`--->`).
275  * @param gauge pointer to a gauge object
276  * @param img_src pointer to an `lv_img_dsc_t` variable or a path to an image
277  *        (not an `lv_img` object)
278  * @param pivot_x the X coordinate of rotation center of the image
279  * @param pivot_y the Y coordinate of rotation center of the image
280  */
lv_gauge_set_needle_img(lv_obj_t * gauge,const void * img,lv_coord_t pivot_x,lv_coord_t pivot_y)281 void lv_gauge_set_needle_img(lv_obj_t * gauge, const void * img, lv_coord_t pivot_x, lv_coord_t pivot_y)
282 {
283     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
284 
285     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
286 
287     ext->needle_img = img;
288     ext->needle_img_pivot.x = pivot_x;
289     ext->needle_img_pivot.y = pivot_y;
290 
291     lv_obj_invalidate(gauge);
292 }
293 
294 /**
295  * Assign a function to format gauge values
296  * @param gauge pointer to a gauge object
297  * @param format_cb pointer to function of lv_gauge_format_cb_t
298  */
lv_gauge_set_formatter_cb(lv_obj_t * gauge,lv_gauge_format_cb_t format_cb)299 void lv_gauge_set_formatter_cb(lv_obj_t * gauge, lv_gauge_format_cb_t format_cb)
300 {
301     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
302 
303     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
304 
305     ext->format_cb = format_cb;
306 }
307 
308 /*=====================
309  * Getter functions
310  *====================*/
311 
312 /**
313  * Get the value of a needle
314  * @param gauge pointer to gauge object
315  * @param needle the id of the needle
316  * @return the value of the needle [min,max]
317  */
lv_gauge_get_value(const lv_obj_t * gauge,uint8_t needle)318 int32_t lv_gauge_get_value(const lv_obj_t * gauge, uint8_t needle)
319 {
320     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
321 
322     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
323     int16_t min          = lv_gauge_get_min_value(gauge);
324 
325     if(needle >= ext->needle_count) return min;
326 
327     return ext->values[needle];
328 }
329 
330 /**
331  * Get the count of needles on a gauge
332  * @param gauge pointer to gauge
333  * @return count of needles
334  */
lv_gauge_get_needle_count(const lv_obj_t * gauge)335 uint8_t lv_gauge_get_needle_count(const lv_obj_t * gauge)
336 {
337     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
338 
339     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
340     return ext->needle_count;
341 }
342 
343 /**
344  * Set the number of labels (and the thicker lines too)
345  * @param gauge pointer to a gauge object
346  * @return count of labels
347  */
lv_gauge_get_label_count(const lv_obj_t * gauge)348 uint8_t lv_gauge_get_label_count(const lv_obj_t * gauge)
349 {
350     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
351 
352     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
353     return ext->label_count;
354 }
355 
356 /**
357  * Get an image to display as needle(s).
358  * @param gauge pointer to a gauge object
359  * @return pointer to an `lv_img_dsc_t` variable or a path to an image
360  *        (not an `lv_img` object). `NULL` if not used.
361  */
lv_gauge_get_needle_img(lv_obj_t * gauge)362 const void * lv_gauge_get_needle_img(lv_obj_t * gauge)
363 {
364     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
365 
366     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
367 
368     return ext->needle_img;
369 }
370 
371 /**
372  * Get the X coordinate of the rotation center of the needle image
373  * @param gauge pointer to a gauge object
374  * @return the X coordinate of rotation center of the image
375  */
lv_gauge_get_needle_img_pivot_x(lv_obj_t * gauge)376 lv_coord_t lv_gauge_get_needle_img_pivot_x(lv_obj_t * gauge)
377 {
378     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
379 
380     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
381 
382     return ext->needle_img_pivot.x;
383 }
384 
385 /**
386  * Get the Y coordinate of the rotation center of the needle image
387  * @param gauge pointer to a gauge object
388  * @return the X coordinate of rotation center of the image
389  */
lv_gauge_get_needle_img_pivot_y(lv_obj_t * gauge)390 lv_coord_t lv_gauge_get_needle_img_pivot_y(lv_obj_t * gauge)
391 {
392     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
393 
394     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
395 
396     return ext->needle_img_pivot.y;
397 }
398 
399 
400 /**********************
401  *   STATIC FUNCTIONS
402  **********************/
403 
404 /**
405  * Handle the drawing related tasks of the gauges
406  * @param gauge pointer to an object
407  * @param clip_area the object will be drawn only in this area
408  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
409  *                                  (return 'true' if yes)
410  *             LV_DESIGN_DRAW: draw the object (always return 'true')
411  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
412  * @param return an element of `lv_design_res_t`
413  */
lv_gauge_design(lv_obj_t * gauge,const lv_area_t * clip_area,lv_design_mode_t mode)414 static lv_design_res_t lv_gauge_design(lv_obj_t * gauge, const lv_area_t * clip_area, lv_design_mode_t mode)
415 {
416     /*Return false if the object is not covers the mask_p area*/
417     if(mode == LV_DESIGN_COVER_CHK) {
418         return LV_DESIGN_RES_NOT_COVER;
419     }
420     /*Draw the object*/
421     else if(mode == LV_DESIGN_DRAW_MAIN) {
422         ancestor_design(gauge, clip_area, mode);
423 
424         lv_gauge_ext_t * ext           = lv_obj_get_ext_attr(gauge);
425         lv_gauge_draw_labels(gauge, clip_area);
426 
427         /*Add the strong lines*/
428         uint16_t line_cnt_tmp = ext->lmeter.line_cnt;
429         ext->lmeter.line_cnt         = ext->label_count;                 /*Only to labels*/
430         lv_linemeter_draw_scale(gauge, clip_area, LV_GAUGE_PART_MAJOR);
431         ext->lmeter.line_cnt = line_cnt_tmp; /*Restore the parameters*/
432 
433         lv_gauge_draw_needle(gauge, clip_area);
434     }
435     /*Post draw when the children are drawn*/
436     else if(mode == LV_DESIGN_DRAW_POST) {
437         ancestor_design(gauge, clip_area, mode);
438     }
439 
440     return LV_DESIGN_RES_OK;
441 }
442 
443 /**
444  * Signal function of the gauge
445  * @param gauge pointer to a gauge object
446  * @param sign a signal type from lv_signal_t enum
447  * @param param pointer to a signal specific variable
448  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
449  */
lv_gauge_signal(lv_obj_t * gauge,lv_signal_t sign,void * param)450 static lv_res_t lv_gauge_signal(lv_obj_t * gauge, lv_signal_t sign, void * param)
451 {
452     lv_res_t res;
453     if(sign == LV_SIGNAL_GET_STYLE) {
454         lv_get_style_info_t * info = param;
455         info->result = lv_gauge_get_style(gauge, info->part);
456         if(info->result != NULL) return LV_RES_OK;
457         else return ancestor_signal(gauge, sign, param);
458     }
459 
460     /* Include the ancient signal function */
461     res = ancestor_signal(gauge, sign, param);
462     if(res != LV_RES_OK) return res;
463     if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
464 
465     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
466     if(sign == LV_SIGNAL_CLEANUP) {
467         lv_mem_free(ext->values);
468         ext->values = NULL;
469         lv_obj_clean_style_list(gauge, LV_GAUGE_PART_NEEDLE);
470         lv_obj_clean_style_list(gauge, LV_GAUGE_PART_MAJOR);
471     }
472 
473     return res;
474 }
475 /**
476  * Get the style descriptor of a part of the object
477  * @param page pointer the object
478  * @param part the part from `lv_gauge_part_t`. (LV_GAUGE_PART_...)
479  * @return pointer to the style descriptor of the specified part
480  */
lv_gauge_get_style(lv_obj_t * gauge,uint8_t part)481 static lv_style_list_t * lv_gauge_get_style(lv_obj_t * gauge, uint8_t part)
482 {
483     LV_ASSERT_OBJ(gauge, LV_OBJX_NAME);
484 
485     lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
486     lv_style_list_t * style_dsc_p;
487 
488     switch(part) {
489         case LV_GAUGE_PART_MAIN:
490             style_dsc_p = &gauge->style_list;
491             break;
492         case LV_GAUGE_PART_MAJOR:
493             style_dsc_p = &ext->style_strong;
494             break;
495         case LV_GAUGE_PART_NEEDLE:
496             style_dsc_p = &ext->style_needle;
497             break;
498         default:
499             style_dsc_p = NULL;
500     }
501 
502     return style_dsc_p;
503 }
504 /**
505  * Draw the scale on a gauge
506  * @param gauge pointer to gauge object
507  * @param mask mask of drawing
508  */
lv_gauge_draw_labels(lv_obj_t * gauge,const lv_area_t * mask)509 static void lv_gauge_draw_labels(lv_obj_t * gauge, const lv_area_t * mask)
510 {
511     lv_gauge_ext_t * ext     = lv_obj_get_ext_attr(gauge);
512     lv_style_int_t scale_width = lv_obj_get_style_scale_width(gauge, LV_GAUGE_PART_MAJOR);
513     lv_style_int_t left = lv_obj_get_style_pad_left(gauge, LV_GAUGE_PART_MAIN);
514     lv_style_int_t right = lv_obj_get_style_pad_right(gauge, LV_GAUGE_PART_MAIN);
515     lv_style_int_t top = lv_obj_get_style_pad_top(gauge, LV_GAUGE_PART_MAIN);
516     lv_style_int_t txt_pad = lv_obj_get_style_pad_inner(gauge, LV_GAUGE_PART_MAIN);
517     lv_coord_t r             = (lv_obj_get_width(gauge) - left - right) / 2 - scale_width - txt_pad;
518     lv_coord_t x_ofs         = gauge->coords.x1 + r + left + scale_width + txt_pad;
519     lv_coord_t y_ofs         = gauge->coords.y1 + r + top + scale_width + txt_pad;
520     int16_t scale_angle      = lv_linemeter_get_scale_angle(gauge);
521     uint16_t label_num       = ext->label_count;
522     int16_t angle_ofs        = 90 + (360 - scale_angle) / 2 + lv_gauge_get_angle_offset(gauge);
523     int32_t min              = lv_gauge_get_min_value(gauge);
524     int32_t max              = lv_gauge_get_max_value(gauge);
525 
526     lv_draw_label_dsc_t label_dsc;
527     lv_draw_label_dsc_init(&label_dsc);
528     lv_obj_init_draw_label_dsc(gauge, LV_GAUGE_PART_MAJOR, &label_dsc);
529 
530     uint8_t i;
531     for(i = 0; i < label_num; i++) {
532         /*Calculate the position a scale label*/
533         int16_t angle = (i * scale_angle) / (label_num - 1) + angle_ofs;
534 
535         lv_coord_t y = (int32_t)((int32_t)_lv_trigo_sin(angle) * r) / LV_TRIGO_SIN_MAX;
536         y += y_ofs;
537 
538         lv_coord_t x = (int32_t)((int32_t)_lv_trigo_sin(angle + 90) * r) / LV_TRIGO_SIN_MAX;
539         x += x_ofs;
540 
541         int32_t scale_act = (int32_t)((int32_t)(max - min) * i) / (label_num - 1);
542         scale_act += min;
543         char scale_txt[16];
544         if(ext->format_cb == NULL)
545             _lv_utils_num_to_str(scale_act, scale_txt);
546         else
547             ext->format_cb(gauge, scale_txt, sizeof(scale_txt), scale_act);
548 
549         lv_area_t label_cord;
550         lv_point_t label_size;
551         _lv_txt_get_size(&label_size, scale_txt, label_dsc.font, label_dsc.letter_space, label_dsc.line_space,
552                          LV_COORD_MAX, LV_TXT_FLAG_NONE);
553 
554         /*Draw the label*/
555         label_cord.x1 = x - label_size.x / 2;
556         label_cord.y1 = y - label_size.y / 2;
557         label_cord.x2 = label_cord.x1 + label_size.x;
558         label_cord.y2 = label_cord.y1 + label_size.y;
559 
560         lv_draw_label(&label_cord, mask, &label_dsc, scale_txt, NULL);
561     }
562 }
563 /**
564  * Draw the needles of a gauge
565  * @param gauge pointer to gauge object
566  * @param mask mask of drawing
567  */
lv_gauge_draw_needle(lv_obj_t * gauge,const lv_area_t * clip_area)568 static void lv_gauge_draw_needle(lv_obj_t * gauge, const lv_area_t * clip_area)
569 {
570     lv_gauge_ext_t * ext     = lv_obj_get_ext_attr(gauge);
571 
572     lv_style_int_t pad = lv_obj_get_style_pad_inner(gauge, LV_GAUGE_PART_NEEDLE);
573     lv_style_int_t left = lv_obj_get_style_pad_left(gauge, LV_GAUGE_PART_MAIN);
574     lv_style_int_t right = lv_obj_get_style_pad_right(gauge, LV_GAUGE_PART_MAIN);
575     lv_style_int_t top = lv_obj_get_style_pad_top(gauge, LV_GAUGE_PART_MAIN);
576 
577     lv_coord_t r      = (lv_obj_get_width(gauge) - left - right) / 2 - pad;
578     lv_coord_t x_ofs  = gauge->coords.x1 + r + left + pad;
579     lv_coord_t y_ofs  = gauge->coords.y1 + r + top + pad;
580     uint16_t angle    = lv_linemeter_get_scale_angle(gauge);
581     int16_t angle_ofs = 90 + (360 - angle) / 2 + lv_gauge_get_angle_offset(gauge);
582     int16_t min       = lv_gauge_get_min_value(gauge);
583     int16_t max       = lv_gauge_get_max_value(gauge);
584     lv_point_t p_mid;
585     lv_point_t p_end;
586     uint8_t i;
587 
588     lv_draw_line_dsc_t line_dsc;
589     lv_draw_line_dsc_init(&line_dsc);
590     lv_obj_init_draw_line_dsc(gauge, LV_GAUGE_PART_NEEDLE, &line_dsc);
591 
592     p_mid.x = x_ofs;
593     p_mid.y = y_ofs;
594     for(i = 0; i < ext->needle_count; i++) {
595         /*Calculate the end point of a needle*/
596         int16_t needle_angle =
597             (ext->values[i] - min) * angle / (max - min) + angle_ofs;
598 
599         /*Draw line*/
600         if(ext->needle_img == NULL) {
601             p_end.y = (_lv_trigo_sin(needle_angle) * r) / LV_TRIGO_SIN_MAX + y_ofs;
602             p_end.x = (_lv_trigo_sin(needle_angle + 90) * r) / LV_TRIGO_SIN_MAX + x_ofs;
603 
604             /*Draw the needle with the corresponding color*/
605             if(ext->needle_colors != NULL) line_dsc.color = ext->needle_colors[i];
606 
607             lv_draw_line(&p_mid, &p_end, clip_area, &line_dsc);
608         }
609         /*Draw image*/
610         else {
611             lv_img_header_t info;
612             lv_img_decoder_get_info(ext->needle_img, &info);
613 
614             lv_area_t a;
615             a.x1 = gauge->coords.x1 + lv_area_get_width(&gauge->coords) / 2 - ext->needle_img_pivot.x;
616             a.y1 = gauge->coords.y1 + lv_area_get_height(&gauge->coords) / 2  - ext->needle_img_pivot.y;
617             a.x2 = a.x1 + info.w - 1;
618             a.y2 = a.y1 + info.h - 1;
619             lv_draw_img_dsc_t img_dsc;
620             lv_draw_img_dsc_init(&img_dsc);
621             lv_obj_init_draw_img_dsc(gauge, LV_GAUGE_PART_MAIN, &img_dsc);
622             img_dsc.pivot.x = ext->needle_img_pivot.x;
623             img_dsc.pivot.y = ext->needle_img_pivot.y;
624 
625             if(ext->needle_colors != NULL)
626                 img_dsc.recolor = ext->needle_colors[i];
627 
628             needle_angle = (needle_angle * 10);
629             if(needle_angle > 3600) needle_angle -= 3600;
630             img_dsc.angle = needle_angle;
631             lv_draw_img(&a, clip_area, ext->needle_img, &img_dsc);
632         }
633     }
634 
635     lv_draw_rect_dsc_t mid_dsc;
636     lv_draw_rect_dsc_init(&mid_dsc);
637     lv_obj_init_draw_rect_dsc(gauge, LV_GAUGE_PART_NEEDLE, &mid_dsc);
638     lv_style_int_t size = lv_obj_get_style_size(gauge, LV_GAUGE_PART_NEEDLE) / 2;
639     lv_area_t nm_cord;
640     nm_cord.x1 = x_ofs - size;
641     nm_cord.y1 = y_ofs - size;
642     nm_cord.x2 = x_ofs + size;
643     nm_cord.y2 = y_ofs + size;
644     lv_draw_rect(&nm_cord, clip_area, &mid_dsc);
645 }
646 
647 #endif
648