1 /**
2  * @file lv_meter.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_meter.h"
10 #if LV_USE_METER != 0
11 
12 #include "../../../misc/lv_assert.h"
13 
14 /*********************
15  *      DEFINES
16  *********************/
17 #define MY_CLASS &lv_meter_class
18 
19 /**********************
20  *      TYPEDEFS
21  **********************/
22 
23 /**********************
24  *  STATIC PROTOTYPES
25  **********************/
26 static void lv_meter_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
27 static void lv_meter_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
28 static void lv_meter_event(const lv_obj_class_t * class_p, lv_event_t * e);
29 static void draw_arcs(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area);
30 static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area);
31 static void draw_needles(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area);
32 static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value);
33 static void inv_line(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
34 
35 /**********************
36  *  STATIC VARIABLES
37  **********************/
38 const lv_obj_class_t lv_meter_class = {
39     .constructor_cb = lv_meter_constructor,
40     .destructor_cb = lv_meter_destructor,
41     .event_cb = lv_meter_event,
42     .instance_size = sizeof(lv_meter_t),
43     .base_class = &lv_obj_class
44 };
45 
46 /**********************
47  *      MACROS
48  **********************/
49 
50 /**********************
51  *   GLOBAL FUNCTIONS
52  **********************/
53 
lv_meter_create(lv_obj_t * parent)54 lv_obj_t * lv_meter_create(lv_obj_t * parent)
55 {
56     LV_LOG_INFO("begin");
57     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
58     lv_obj_class_init_obj(obj);
59     return obj;
60 }
61 
62 /*=====================
63  * Add scale
64  *====================*/
65 
lv_meter_add_scale(lv_obj_t * obj)66 lv_meter_scale_t * lv_meter_add_scale(lv_obj_t * obj)
67 {
68     LV_ASSERT_OBJ(obj, MY_CLASS);
69     lv_meter_t * meter = (lv_meter_t *)obj;
70 
71     lv_meter_scale_t * scale = _lv_ll_ins_head(&meter->scale_ll);
72     LV_ASSERT_MALLOC(scale);
73     lv_memset_00(scale, sizeof(lv_meter_scale_t));
74 
75     scale->angle_range = 270;
76     scale->rotation = 90 + (360 - scale->angle_range) / 2;
77     scale->min = 0;
78     scale->max = 100;
79     scale->tick_cnt = 6;
80     scale->tick_length = 8;
81     scale->tick_width = 2;
82     scale->label_gap = 2;
83 
84     return scale;
85 }
86 
lv_meter_set_scale_ticks(lv_obj_t * obj,lv_meter_scale_t * scale,uint16_t cnt,uint16_t width,uint16_t len,lv_color_t color)87 void lv_meter_set_scale_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t cnt, uint16_t width, uint16_t len,
88                               lv_color_t color)
89 {
90     scale->tick_cnt = cnt;
91     scale->tick_width = width;
92     scale->tick_length = len;
93     scale->tick_color = color;
94     lv_obj_invalidate(obj);
95 }
96 
lv_meter_set_scale_major_ticks(lv_obj_t * obj,lv_meter_scale_t * scale,uint16_t nth,uint16_t width,uint16_t len,lv_color_t color,int16_t label_gap)97 void lv_meter_set_scale_major_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t nth, uint16_t width,
98                                     uint16_t len, lv_color_t color, int16_t label_gap)
99 {
100     scale->tick_major_nth = nth;
101     scale->tick_major_width = width;
102     scale->tick_major_length = len;
103     scale->tick_major_color = color;
104     scale->label_gap = label_gap;
105     lv_obj_invalidate(obj);
106 }
107 
lv_meter_set_scale_range(lv_obj_t * obj,lv_meter_scale_t * scale,int32_t min,int32_t max,uint32_t angle_range,uint32_t rotation)108 void lv_meter_set_scale_range(lv_obj_t * obj, lv_meter_scale_t * scale, int32_t min, int32_t max, uint32_t angle_range,
109                               uint32_t rotation)
110 {
111     scale->min = min;
112     scale->max = max;
113     scale->angle_range = angle_range;
114     scale->rotation = rotation;
115     lv_obj_invalidate(obj);
116 }
117 
118 /*=====================
119  * Add indicator
120  *====================*/
121 
lv_meter_add_needle_line(lv_obj_t * obj,lv_meter_scale_t * scale,uint16_t width,lv_color_t color,int16_t r_mod)122 lv_meter_indicator_t * lv_meter_add_needle_line(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width,
123                                                 lv_color_t color, int16_t r_mod)
124 {
125     LV_ASSERT_OBJ(obj, MY_CLASS);
126     lv_meter_t * meter = (lv_meter_t *)obj;
127     lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
128     LV_ASSERT_MALLOC(indic);
129     lv_memset_00(indic, sizeof(lv_meter_indicator_t));
130     indic->scale = scale;
131     indic->opa = LV_OPA_COVER;
132 
133     indic->type = LV_METER_INDICATOR_TYPE_NEEDLE_LINE;
134     indic->type_data.needle_line.width = width;
135     indic->type_data.needle_line.color = color;
136     indic->type_data.needle_line.r_mod = r_mod;
137     lv_obj_invalidate(obj);
138 
139     return indic;
140 }
141 
lv_meter_add_needle_img(lv_obj_t * obj,lv_meter_scale_t * scale,const void * src,lv_coord_t pivot_x,lv_coord_t pivot_y)142 lv_meter_indicator_t * lv_meter_add_needle_img(lv_obj_t * obj, lv_meter_scale_t * scale, const void * src,
143                                                lv_coord_t pivot_x, lv_coord_t pivot_y)
144 {
145     LV_ASSERT_OBJ(obj, MY_CLASS);
146     lv_meter_t * meter = (lv_meter_t *)obj;
147     lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
148     LV_ASSERT_MALLOC(indic);
149     lv_memset_00(indic, sizeof(lv_meter_indicator_t));
150     indic->scale = scale;
151     indic->opa = LV_OPA_COVER;
152 
153     indic->type = LV_METER_INDICATOR_TYPE_NEEDLE_IMG;
154     indic->type_data.needle_img.src = src;
155     indic->type_data.needle_img.pivot.x = pivot_x;
156     indic->type_data.needle_img.pivot.y = pivot_y;
157     lv_obj_invalidate(obj);
158 
159     return indic;
160 }
161 
lv_meter_add_arc(lv_obj_t * obj,lv_meter_scale_t * scale,uint16_t width,lv_color_t color,int16_t r_mod)162 lv_meter_indicator_t * lv_meter_add_arc(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color,
163                                         int16_t r_mod)
164 {
165     LV_ASSERT_OBJ(obj, MY_CLASS);
166     lv_meter_t * meter = (lv_meter_t *)obj;
167     lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
168     LV_ASSERT_MALLOC(indic);
169     lv_memset_00(indic, sizeof(lv_meter_indicator_t));
170     indic->scale = scale;
171     indic->opa = LV_OPA_COVER;
172 
173     indic->type = LV_METER_INDICATOR_TYPE_ARC;
174     indic->type_data.arc.width = width;
175     indic->type_data.arc.color = color;
176     indic->type_data.arc.r_mod = r_mod;
177 
178     lv_obj_invalidate(obj);
179     return indic;
180 }
181 
lv_meter_add_scale_lines(lv_obj_t * obj,lv_meter_scale_t * scale,lv_color_t color_start,lv_color_t color_end,bool local,int16_t width_mod)182 lv_meter_indicator_t * lv_meter_add_scale_lines(lv_obj_t * obj, lv_meter_scale_t * scale, lv_color_t color_start,
183                                                 lv_color_t color_end, bool local, int16_t width_mod)
184 {
185     LV_ASSERT_OBJ(obj, MY_CLASS);
186     lv_meter_t * meter = (lv_meter_t *)obj;
187     lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
188     LV_ASSERT_MALLOC(indic);
189     lv_memset_00(indic, sizeof(lv_meter_indicator_t));
190     indic->scale = scale;
191     indic->opa = LV_OPA_COVER;
192 
193     indic->type = LV_METER_INDICATOR_TYPE_SCALE_LINES;
194     indic->type_data.scale_lines.color_start = color_start;
195     indic->type_data.scale_lines.color_end = color_end;
196     indic->type_data.scale_lines.local_grad = local;
197     indic->type_data.scale_lines.width_mod = width_mod;
198 
199     lv_obj_invalidate(obj);
200     return indic;
201 }
202 
203 /*=====================
204  * Set indicator value
205  *====================*/
206 
lv_meter_set_indicator_value(lv_obj_t * obj,lv_meter_indicator_t * indic,int32_t value)207 void lv_meter_set_indicator_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
208 {
209     int32_t old_start = indic->start_value;
210     int32_t old_end = indic->end_value;
211     indic->start_value = value;
212     indic->end_value = value;
213 
214     if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
215         inv_arc(obj, indic, old_start, value);
216         inv_arc(obj, indic, old_end, value);
217     }
218     else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
219         inv_line(obj, indic, old_start);
220         inv_line(obj, indic, old_end);
221         inv_line(obj, indic, value);
222     }
223     else {
224         lv_obj_invalidate(obj);
225     }
226 }
227 
lv_meter_set_indicator_start_value(lv_obj_t * obj,lv_meter_indicator_t * indic,int32_t value)228 void lv_meter_set_indicator_start_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
229 {
230     int32_t old_value = indic->start_value;
231     indic->start_value = value;
232 
233     if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
234         inv_arc(obj, indic, old_value, value);
235     }
236     else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
237         inv_line(obj, indic, old_value);
238         inv_line(obj, indic, value);
239     }
240     else {
241         lv_obj_invalidate(obj);
242     }
243 }
244 
lv_meter_set_indicator_end_value(lv_obj_t * obj,lv_meter_indicator_t * indic,int32_t value)245 void lv_meter_set_indicator_end_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
246 {
247     int32_t old_value = indic->end_value;
248     indic->end_value = value;
249 
250     if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
251         inv_arc(obj, indic, old_value, value);
252     }
253     else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
254         inv_line(obj, indic, old_value);
255         inv_line(obj, indic, value);
256     }
257     else {
258         lv_obj_invalidate(obj);
259     }
260 }
261 
262 /**********************
263  *   STATIC FUNCTIONS
264  **********************/
265 
lv_meter_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)266 static void lv_meter_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
267 {
268     LV_UNUSED(class_p);
269     LV_TRACE_OBJ_CREATE("begin");
270 
271     lv_meter_t * meter = (lv_meter_t *)obj;
272 
273     _lv_ll_init(&meter->scale_ll, sizeof(lv_meter_scale_t));
274     _lv_ll_init(&meter->indicator_ll, sizeof(lv_meter_indicator_t));
275 
276     LV_TRACE_OBJ_CREATE("finished");
277 }
278 
lv_meter_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)279 static void lv_meter_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
280 {
281     LV_UNUSED(class_p);
282     LV_ASSERT_OBJ(obj, MY_CLASS);
283     lv_meter_t * meter = (lv_meter_t *)obj;
284     _lv_ll_clear(&meter->indicator_ll);
285     _lv_ll_clear(&meter->scale_ll);
286 
287 }
288 
lv_meter_event(const lv_obj_class_t * class_p,lv_event_t * e)289 static void lv_meter_event(const lv_obj_class_t * class_p, lv_event_t * e)
290 {
291     LV_UNUSED(class_p);
292 
293     lv_res_t res = lv_obj_event_base(MY_CLASS, e);
294     if(res != LV_RES_OK) return;
295 
296     lv_event_code_t code = lv_event_get_code(e);
297     lv_obj_t * obj = lv_event_get_target(e);
298     if(code == LV_EVENT_DRAW_MAIN) {
299         lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
300         lv_area_t scale_area;
301         lv_obj_get_content_coords(obj, &scale_area);
302 
303         draw_arcs(obj, draw_ctx, &scale_area);
304         draw_ticks_and_labels(obj, draw_ctx, &scale_area);
305         draw_needles(obj, draw_ctx, &scale_area);
306 
307         lv_coord_t r_edge = lv_area_get_width(&scale_area) / 2;
308         lv_point_t scale_center;
309         scale_center.x = scale_area.x1 + r_edge;
310         scale_center.y = scale_area.y1 + r_edge;
311 
312         lv_draw_rect_dsc_t mid_dsc;
313         lv_draw_rect_dsc_init(&mid_dsc);
314         lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &mid_dsc);
315         lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
316         lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
317         lv_area_t nm_cord;
318         nm_cord.x1 = scale_center.x - w;
319         nm_cord.y1 = scale_center.y - h;
320         nm_cord.x2 = scale_center.x + w;
321         nm_cord.y2 = scale_center.y + h;
322         lv_draw_rect(draw_ctx, &mid_dsc, &nm_cord);
323     }
324 }
325 
draw_arcs(lv_obj_t * obj,lv_draw_ctx_t * draw_ctx,const lv_area_t * scale_area)326 static void draw_arcs(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area)
327 {
328     lv_meter_t * meter = (lv_meter_t *)obj;
329 
330     lv_draw_arc_dsc_t arc_dsc;
331     lv_draw_arc_dsc_init(&arc_dsc);
332     arc_dsc.rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);
333 
334     lv_coord_t r_out = lv_area_get_width(scale_area) / 2 ;
335     lv_point_t scale_center;
336     scale_center.x = scale_area->x1 + r_out;
337     scale_center.y = scale_area->y1 + r_out;
338 
339     lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
340     lv_meter_indicator_t * indic;
341 
342     lv_obj_draw_part_dsc_t part_draw_dsc;
343     lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
344     part_draw_dsc.arc_dsc = &arc_dsc;
345     part_draw_dsc.part = LV_PART_INDICATOR;
346     part_draw_dsc.class_p = MY_CLASS;
347     part_draw_dsc.type = LV_METER_DRAW_PART_ARC;
348 
349     _LV_LL_READ_BACK(&meter->indicator_ll, indic) {
350         if(indic->type != LV_METER_INDICATOR_TYPE_ARC) continue;
351 
352         arc_dsc.color = indic->type_data.arc.color;
353         arc_dsc.width = indic->type_data.arc.width;
354         arc_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
355 
356         lv_meter_scale_t * scale = indic->scale;
357 
358         int32_t start_angle = lv_map(indic->start_value, scale->min, scale->max, scale->rotation,
359                                      scale->rotation + scale->angle_range);
360         int32_t end_angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation,
361                                    scale->rotation + scale->angle_range);
362 
363         part_draw_dsc.radius = r_out + indic->type_data.arc.r_mod;
364         part_draw_dsc.sub_part_ptr = indic;
365         part_draw_dsc.p1 = &scale_center;
366 
367         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
368         lv_draw_arc(draw_ctx, &arc_dsc, &scale_center, part_draw_dsc.radius, start_angle, end_angle);
369         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
370     }
371 }
372 
draw_ticks_and_labels(lv_obj_t * obj,lv_draw_ctx_t * draw_ctx,const lv_area_t * scale_area)373 static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area)
374 {
375     lv_meter_t * meter    = (lv_meter_t *)obj;
376 
377     lv_point_t p_center;
378     lv_coord_t r_edge = LV_MIN(lv_area_get_width(scale_area) / 2, lv_area_get_height(scale_area) / 2);
379     p_center.x = scale_area->x1 + r_edge;
380     p_center.y = scale_area->y1 + r_edge;
381 
382     uint8_t i;
383 
384     lv_draw_line_dsc_t line_dsc;
385     lv_draw_line_dsc_init(&line_dsc);
386     lv_obj_init_draw_line_dsc(obj, LV_PART_TICKS, &line_dsc);
387     line_dsc.raw_end = 1;
388 
389     lv_draw_label_dsc_t label_dsc;
390     lv_draw_label_dsc_init(&label_dsc);
391     lv_obj_init_draw_label_dsc(obj, LV_PART_TICKS, &label_dsc);
392 
393     lv_meter_scale_t * scale;
394 
395     lv_draw_mask_radius_param_t inner_minor_mask;
396     lv_draw_mask_radius_param_t inner_major_mask;
397     lv_draw_mask_radius_param_t outer_mask;
398 
399     lv_obj_draw_part_dsc_t part_draw_dsc;
400     lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
401     part_draw_dsc.class_p = MY_CLASS;
402     part_draw_dsc.part = LV_PART_TICKS;
403     part_draw_dsc.type = LV_METER_DRAW_PART_TICK;
404     part_draw_dsc.line_dsc = &line_dsc;
405 
406     _LV_LL_READ_BACK(&meter->scale_ll, scale) {
407         part_draw_dsc.sub_part_ptr = scale;
408 
409         lv_coord_t r_out = r_edge + scale->r_mod;
410         lv_coord_t r_in_minor = r_out - scale->tick_length;
411         lv_coord_t r_in_major = r_out - scale->tick_major_length;
412 
413         lv_area_t area_inner_minor;
414         area_inner_minor.x1 = p_center.x - r_in_minor;
415         area_inner_minor.y1 = p_center.y - r_in_minor;
416         area_inner_minor.x2 = p_center.x + r_in_minor;
417         area_inner_minor.y2 = p_center.y + r_in_minor;
418         lv_draw_mask_radius_init(&inner_minor_mask, &area_inner_minor, LV_RADIUS_CIRCLE, true);
419 
420         lv_area_t area_inner_major;
421         area_inner_major.x1 = p_center.x - r_in_major;
422         area_inner_major.y1 = p_center.y - r_in_major;
423         area_inner_major.x2 = p_center.x + r_in_major - 1;
424         area_inner_major.y2 = p_center.y + r_in_major - 1;
425         lv_draw_mask_radius_init(&inner_major_mask, &area_inner_major, LV_RADIUS_CIRCLE, true);
426 
427         lv_area_t area_outer;
428         area_outer.x1 = p_center.x - r_out;
429         area_outer.y1 = p_center.y - r_out;
430         area_outer.x2 = p_center.x + r_out - 1;
431         area_outer.y2 = p_center.y + r_out - 1;
432         lv_draw_mask_radius_init(&outer_mask, &area_outer, LV_RADIUS_CIRCLE, false);
433         int16_t outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
434 
435         int16_t inner_act_mask_id = LV_MASK_ID_INV; /*Will be added later*/
436 
437         uint32_t minor_cnt = scale->tick_major_nth ? scale->tick_major_nth - 1 : 0xFFFF;
438         for(i = 0; i < scale->tick_cnt; i++) {
439             minor_cnt++;
440             bool major = false;
441             if(minor_cnt == scale->tick_major_nth) {
442                 minor_cnt = 0;
443                 major = true;
444             }
445 
446             int32_t value_of_line = lv_map(i, 0, scale->tick_cnt - 1, scale->min, scale->max);
447             part_draw_dsc.value = value_of_line;
448 
449             lv_color_t line_color = major ? scale->tick_major_color : scale->tick_color;
450             lv_color_t line_color_ori = line_color;
451 
452             lv_coord_t line_width_ori = major ? scale->tick_major_width : scale->tick_width;
453             lv_coord_t line_width = line_width_ori;
454 
455             lv_meter_indicator_t * indic;
456             _LV_LL_READ_BACK(&meter->indicator_ll, indic) {
457                 if(indic->type != LV_METER_INDICATOR_TYPE_SCALE_LINES) continue;
458                 if(value_of_line >= indic->start_value && value_of_line <= indic->end_value) {
459                     line_width += indic->type_data.scale_lines.width_mod;
460 
461                     if(indic->type_data.scale_lines.color_start.full == indic->type_data.scale_lines.color_end.full) {
462                         line_color = indic->type_data.scale_lines.color_start;
463                     }
464                     else {
465                         lv_opa_t ratio;
466                         if(indic->type_data.scale_lines.local_grad) {
467                             ratio = lv_map(value_of_line, indic->start_value, indic->end_value, LV_OPA_TRANSP, LV_OPA_COVER);
468                         }
469                         else {
470                             ratio = lv_map(value_of_line, scale->min, scale->max, LV_OPA_TRANSP, LV_OPA_COVER);
471                         }
472                         line_color = lv_color_mix(indic->type_data.scale_lines.color_end, indic->type_data.scale_lines.color_start, ratio);
473                     }
474                 }
475             }
476 
477             /*`* 256` for extra precision*/
478             int32_t angle_upscale = ((i * scale->angle_range) << 8) / (scale->tick_cnt - 1);
479 
480             int32_t angle_low = (angle_upscale >> 8);
481             int32_t angle_high = angle_low + 1;
482             int32_t angle_rem = angle_upscale & 0xFF;
483 
484             /*Interpolate sine and cos*/
485             int32_t sin_low = lv_trigo_sin(angle_low + scale->rotation);
486             int32_t sin_high = lv_trigo_sin(angle_high + scale->rotation);
487             int32_t sin_mid = (sin_low * (256 - angle_rem) + sin_high * angle_rem) >> 8;
488 
489             int32_t cos_low = lv_trigo_cos(angle_low + scale->rotation);
490             int32_t cos_high = lv_trigo_cos(angle_high + scale->rotation);
491             int32_t cos_mid = (cos_low * (256 - angle_rem) + cos_high * angle_rem) >> 8;
492 
493             line_dsc.color = line_color;
494             line_dsc.width = line_width;
495             /*Use the interpolated angle to get the outer x and y coordinates.
496              *Draw a little bit longer lines to be sure the mask will clip them correctly*/
497             lv_point_t p_outer;
498             p_outer.x = (int32_t)(((int32_t)cos_mid * (r_out + line_width) + 127) >> (LV_TRIGO_SHIFT)) + p_center.x;
499             p_outer.y = (int32_t)(((int32_t)sin_mid * (r_out + line_width) + 127) >> (LV_TRIGO_SHIFT)) + p_center.y;
500 
501             part_draw_dsc.p1 = &p_outer;
502             part_draw_dsc.p1 = &p_center;
503             part_draw_dsc.id = i;
504             part_draw_dsc.label_dsc = &label_dsc;
505 
506             /*Draw the text*/
507             if(major) {
508                 lv_draw_mask_remove_id(outer_mask_id);
509                 uint32_t r_text = r_in_major - scale->label_gap;
510                 lv_point_t p;
511                 p.x = (int32_t)((int32_t)((int32_t)cos_mid * r_text + 127) >> LV_TRIGO_SHIFT) + p_center.x;
512                 p.y = (int32_t)((int32_t)((int32_t)sin_mid * r_text + 127) >> LV_TRIGO_SHIFT) + p_center.y;
513 
514                 lv_draw_label_dsc_t label_dsc_tmp;
515                 lv_memcpy(&label_dsc_tmp, &label_dsc, sizeof(label_dsc_tmp));
516 
517                 part_draw_dsc.label_dsc = &label_dsc_tmp;
518                 char buf[16];
519 
520                 lv_snprintf(buf, sizeof(buf), "%" LV_PRId32, value_of_line);
521                 part_draw_dsc.text = buf;
522 
523                 lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
524 
525                 lv_point_t label_size;
526                 lv_txt_get_size(&label_size, part_draw_dsc.text, label_dsc.font, label_dsc.letter_space, label_dsc.line_space,
527                                 LV_COORD_MAX, LV_TEXT_FLAG_NONE);
528 
529                 lv_area_t label_cord;
530                 label_cord.x1 = p.x - label_size.x / 2;
531                 label_cord.y1 = p.y - label_size.y / 2;
532                 label_cord.x2 = label_cord.x1 + label_size.x;
533                 label_cord.y2 = label_cord.y1 + label_size.y;
534 
535                 lv_draw_label(draw_ctx, part_draw_dsc.label_dsc, &label_cord, part_draw_dsc.text, NULL);
536 
537                 outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
538             }
539             else {
540                 part_draw_dsc.label_dsc = NULL;
541                 part_draw_dsc.text = NULL;
542                 lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
543             }
544 
545             inner_act_mask_id = lv_draw_mask_add(major ? &inner_major_mask : &inner_minor_mask, NULL);
546             lv_draw_line(draw_ctx, &line_dsc, &p_outer, &p_center);
547             lv_draw_mask_remove_id(inner_act_mask_id);
548             lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, &part_draw_dsc);
549 
550             line_dsc.color = line_color_ori;
551             line_dsc.width = line_width_ori;
552 
553         }
554         lv_draw_mask_free_param(&inner_minor_mask);
555         lv_draw_mask_free_param(&inner_major_mask);
556         lv_draw_mask_free_param(&outer_mask);
557         lv_draw_mask_remove_id(outer_mask_id);
558     }
559 }
560 
561 
draw_needles(lv_obj_t * obj,lv_draw_ctx_t * draw_ctx,const lv_area_t * scale_area)562 static void draw_needles(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area)
563 {
564     lv_meter_t * meter = (lv_meter_t *)obj;
565 
566     lv_coord_t r_edge = lv_area_get_width(scale_area) / 2;
567     lv_point_t scale_center;
568     scale_center.x = scale_area->x1 + r_edge;
569     scale_center.y = scale_area->y1 + r_edge;
570 
571     lv_draw_line_dsc_t line_dsc;
572     lv_draw_line_dsc_init(&line_dsc);
573     lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &line_dsc);
574 
575     lv_draw_img_dsc_t img_dsc;
576     lv_draw_img_dsc_init(&img_dsc);
577     lv_obj_init_draw_img_dsc(obj, LV_PART_ITEMS, &img_dsc);
578     lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
579 
580     lv_obj_draw_part_dsc_t part_draw_dsc;
581     lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
582     part_draw_dsc.class_p = MY_CLASS;
583     part_draw_dsc.p1 = &scale_center;
584 
585     lv_meter_indicator_t * indic;
586     _LV_LL_READ_BACK(&meter->indicator_ll, indic) {
587         lv_meter_scale_t * scale = indic->scale;
588         part_draw_dsc.sub_part_ptr = indic;
589 
590         if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
591             int32_t angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
592             lv_coord_t r_out = r_edge + scale->r_mod + indic->type_data.needle_line.r_mod;
593             lv_point_t p_end;
594             p_end.y = (lv_trigo_sin(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.y;
595             p_end.x = (lv_trigo_cos(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.x;
596             line_dsc.color = indic->type_data.needle_line.color;
597             line_dsc.width = indic->type_data.needle_line.width;
598             line_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
599 
600             part_draw_dsc.id = LV_METER_DRAW_PART_NEEDLE_LINE;
601             part_draw_dsc.line_dsc = &line_dsc;
602             part_draw_dsc.p2 = &p_end;
603 
604             lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
605             lv_draw_line(draw_ctx, &line_dsc, &scale_center, &p_end);
606             lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
607         }
608         else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG) {
609             if(indic->type_data.needle_img.src == NULL) continue;
610 
611             int32_t angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
612             lv_img_header_t info;
613             lv_img_decoder_get_info(indic->type_data.needle_img.src, &info);
614             lv_area_t a;
615             a.x1 = scale_center.x - indic->type_data.needle_img.pivot.x;
616             a.y1 = scale_center.y - indic->type_data.needle_img.pivot.y;
617             a.x2 = a.x1 + info.w - 1;
618             a.y2 = a.y1 + info.h - 1;
619 
620             img_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
621             img_dsc.pivot.x = indic->type_data.needle_img.pivot.x;
622             img_dsc.pivot.y = indic->type_data.needle_img.pivot.y;
623             angle = angle * 10;
624             if(angle > 3600) angle -= 3600;
625             img_dsc.angle = angle;
626 
627             part_draw_dsc.img_dsc = &img_dsc;
628 
629             lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
630             lv_draw_img(draw_ctx, &img_dsc, &a, indic->type_data.needle_img.src);
631             lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
632         }
633     }
634 }
635 
inv_arc(lv_obj_t * obj,lv_meter_indicator_t * indic,int32_t old_value,int32_t new_value)636 static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value)
637 {
638     bool rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);
639 
640     lv_area_t scale_area;
641     lv_obj_get_content_coords(obj, &scale_area);
642 
643     lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;
644     lv_point_t scale_center;
645     scale_center.x = scale_area.x1 + r_out;
646     scale_center.y = scale_area.y1 + r_out;
647 
648     r_out += indic->type_data.arc.r_mod;
649 
650     lv_meter_scale_t * scale = indic->scale;
651 
652     int32_t start_angle = lv_map(old_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation);
653     int32_t end_angle = lv_map(new_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation);
654 
655     lv_area_t a;
656     lv_draw_arc_get_area(scale_center.x, scale_center.y, r_out, LV_MIN(start_angle, end_angle), LV_MAX(start_angle,
657                                                                                                        end_angle), indic->type_data.arc.width, rounded, &a);
658     lv_obj_invalidate_area(obj, &a);
659 }
660 
661 
inv_line(lv_obj_t * obj,lv_meter_indicator_t * indic,int32_t value)662 static void inv_line(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
663 {
664     lv_area_t scale_area;
665     lv_obj_get_content_coords(obj, &scale_area);
666 
667     lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;
668     lv_point_t scale_center;
669     scale_center.x = scale_area.x1 + r_out;
670     scale_center.y = scale_area.y1 + r_out;
671 
672     lv_meter_scale_t * scale = indic->scale;
673 
674     if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
675         int32_t angle = lv_map(value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
676         r_out += scale->r_mod + indic->type_data.needle_line.r_mod;
677         lv_point_t p_end;
678         p_end.y = (lv_trigo_sin(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.y;
679         p_end.x = (lv_trigo_cos(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.x;
680 
681         lv_area_t a;
682         a.x1 = LV_MIN(scale_center.x, p_end.x) - indic->type_data.needle_line.width - 2;
683         a.y1 = LV_MIN(scale_center.y, p_end.y) - indic->type_data.needle_line.width - 2;
684         a.x2 = LV_MAX(scale_center.x, p_end.x) + indic->type_data.needle_line.width + 2;
685         a.y2 = LV_MAX(scale_center.y, p_end.y) + indic->type_data.needle_line.width + 2;
686 
687         lv_obj_invalidate_area(obj, &a);
688     }
689     else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG) {
690         int32_t angle = lv_map(value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
691         lv_img_header_t info;
692         lv_img_decoder_get_info(indic->type_data.needle_img.src, &info);
693 
694         angle = angle * 10;
695         if(angle > 3600) angle -= 3600;
696 
697         scale_center.x -= indic->type_data.needle_img.pivot.x;
698         scale_center.y -= indic->type_data.needle_img.pivot.y;
699         lv_area_t a;
700         _lv_img_buf_get_transformed_area(&a, info.w, info.h, angle, LV_IMG_ZOOM_NONE, &indic->type_data.needle_img.pivot);
701         a.x1 += scale_center.x - 2;
702         a.y1 += scale_center.y - 2;
703         a.x2 += scale_center.x + 2;
704         a.y2 += scale_center.y + 2;
705 
706         lv_obj_invalidate_area(obj, &a);
707     }
708 }
709 #endif
710