1 /**
2  * @file lv_linemeter.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_linemeter.h"
10 #if LV_USE_LINEMETER != 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_core/lv_group.h"
16 #include "../lv_misc/lv_math.h"
17 
18 /*********************
19  *      DEFINES
20  *********************/
21 #define LV_OBJX_NAME "lv_linemeter"
22 
23 /**********************
24  *      TYPEDEFS
25  **********************/
26 
27 /**********************
28  *  STATIC PROTOTYPES
29  **********************/
30 static lv_design_res_t lv_linemeter_design(lv_obj_t * lmeter, const lv_area_t * clip_area, lv_design_mode_t mode);
31 static lv_res_t lv_linemeter_signal(lv_obj_t * lmeter, lv_signal_t sign, void * param);
32 
33 /**********************
34  *  STATIC VARIABLES
35  **********************/
36 static lv_signal_cb_t ancestor_signal;
37 
38 /**********************
39  *      MACROS
40  **********************/
41 
42 /**********************
43  *   GLOBAL FUNCTIONS
44  **********************/
45 
46 /**
47  * Create a line meter objects
48  * @param par pointer to an object, it will be the parent of the new line meter
49  * @param copy pointer to a line meter object, if not NULL then the new object will be copied from
50  * it
51  * @return pointer to the created line meter
52  */
lv_linemeter_create(lv_obj_t * par,const lv_obj_t * copy)53 lv_obj_t * lv_linemeter_create(lv_obj_t * par, const lv_obj_t * copy)
54 {
55     LV_LOG_TRACE("line meter create started");
56 
57     /*Create the ancestor of line meter*/
58     lv_obj_t * linemeter = lv_obj_create(par, copy);
59     LV_ASSERT_MEM(linemeter);
60     if(linemeter == NULL) return NULL;
61 
62     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(linemeter);
63 
64     /*Allocate the line meter type specific extended data*/
65     lv_linemeter_ext_t * ext = lv_obj_allocate_ext_attr(linemeter, sizeof(lv_linemeter_ext_t));
66     LV_ASSERT_MEM(ext);
67     if(ext == NULL) {
68         lv_obj_del(linemeter);
69         return NULL;
70     }
71 
72     /*Initialize the allocated 'ext' */
73     ext->min_value   = 0;
74     ext->max_value   = 100;
75     ext->cur_value   = 0;
76     ext->line_cnt    = 18;
77     ext->scale_angle = 240;
78     ext->angle_ofs = 0;
79     ext->mirrored = 0;
80 
81     /*The signal and design functions are not copied so set them here*/
82     lv_obj_set_signal_cb(linemeter, lv_linemeter_signal);
83     lv_obj_set_design_cb(linemeter, lv_linemeter_design);
84 
85     /*Init the new line meter line meter*/
86     if(copy == NULL) {
87         lv_obj_set_size(linemeter, 3 * LV_DPI / 2, 3 * LV_DPI / 2);
88         lv_theme_apply(linemeter, LV_THEME_LINEMETER);
89     }
90     /*Copy an existing line meter*/
91     else {
92         lv_linemeter_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
93         ext->scale_angle           = copy_ext->scale_angle;
94         ext->line_cnt              = copy_ext->line_cnt;
95         ext->min_value             = copy_ext->min_value;
96         ext->max_value             = copy_ext->max_value;
97         ext->cur_value             = copy_ext->cur_value;
98 
99         /*Refresh the style with new signal function*/
100         lv_obj_refresh_style(linemeter, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
101     }
102 
103     LV_LOG_INFO("line meter created");
104 
105     return linemeter;
106 }
107 
108 /*=====================
109  * Setter functions
110  *====================*/
111 
112 /**
113  * Set a new value on the line meter
114  * @param lmeter pointer to a line meter object
115  * @param value new value
116  */
lv_linemeter_set_value(lv_obj_t * lmeter,int32_t value)117 void lv_linemeter_set_value(lv_obj_t * lmeter, int32_t value)
118 {
119     LV_ASSERT_OBJ(lmeter, LV_OBJX_NAME);
120 
121     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
122     if(ext->cur_value == value) return;
123 
124     int32_t old_value = ext->cur_value;
125 
126     ext->cur_value = value > ext->max_value ? ext->max_value : value;
127     ext->cur_value = ext->cur_value < ext->min_value ? ext->min_value : ext->cur_value;
128 
129     int16_t level_old =
130         (int32_t)((int32_t)(old_value - ext->min_value) * (ext->line_cnt - 1)) / (ext->max_value - ext->min_value);
131     int16_t level_new =
132         (int32_t)((int32_t)(ext->cur_value - ext->min_value) * (ext->line_cnt - 1)) / (ext->max_value - ext->min_value);
133 
134     if(level_new == level_old) {
135         return;
136     }
137 
138     lv_style_int_t left = lv_obj_get_style_pad_left(lmeter, LV_LINEMETER_PART_MAIN);
139     lv_style_int_t right = lv_obj_get_style_pad_right(lmeter, LV_LINEMETER_PART_MAIN);
140     lv_style_int_t top = lv_obj_get_style_pad_top(lmeter, LV_LINEMETER_PART_MAIN);
141     lv_style_int_t bottom = lv_obj_get_style_pad_bottom(lmeter, LV_LINEMETER_PART_MAIN);
142 
143     lv_coord_t r_out = (lv_obj_get_width(lmeter) - left - right) / 2 ;
144     lv_coord_t r_in  = r_out - lv_obj_get_style_scale_width(lmeter, LV_LINEMETER_PART_MAIN);
145     if(r_in < 1) r_in = 1;
146 
147     lv_coord_t x_ofs  = lmeter->coords.x1 + r_out + left;
148     lv_coord_t y_ofs  = lmeter->coords.y1 + r_out + top;
149     int16_t angle_ofs = ext->angle_ofs + 90 + (360 - ext->scale_angle) / 2;
150 
151     lv_style_int_t line_width = lv_obj_get_style_scale_end_line_width(lmeter, LV_LINEMETER_PART_MAIN);
152     lv_style_int_t end_line_width = lv_obj_get_style_scale_end_line_width(lmeter, LV_LINEMETER_PART_MAIN);
153     line_width = LV_MATH_MAX(line_width, end_line_width);
154 
155     int32_t angle_old = (level_old * ext->scale_angle) / (ext->line_cnt - 1);
156 
157     /*Use smaller clip area only around the visible line*/
158     int32_t y_in_old  = (int32_t)((int32_t)_lv_trigo_sin(angle_old + angle_ofs) * r_in) >> LV_TRIGO_SHIFT;
159     int32_t x_in_old  = (int32_t)((int32_t)_lv_trigo_sin(angle_old + 90 + angle_ofs) * r_in) >> LV_TRIGO_SHIFT;
160 
161 
162     int32_t y_out_old  = (int32_t)((int32_t)_lv_trigo_sin(angle_old + angle_ofs) * r_out) >> LV_TRIGO_SHIFT;
163     int32_t x_out_old  = (int32_t)((int32_t)_lv_trigo_sin(angle_old + 90 + angle_ofs) * r_out) >> LV_TRIGO_SHIFT;
164 
165 
166 
167     int32_t angle_new = (level_new * ext->scale_angle) / (ext->line_cnt - 1);
168 
169     /*Use smaller clip area only around the visible line*/
170     int32_t y_in_new  = (int32_t)((int32_t)_lv_trigo_sin(angle_new + angle_ofs) * r_in) >> LV_TRIGO_SHIFT;
171     int32_t x_in_new  = (int32_t)((int32_t)_lv_trigo_sin(angle_new + 90 + angle_ofs) * r_in) >> LV_TRIGO_SHIFT;
172 
173 
174     int32_t y_out_new  = (int32_t)((int32_t)_lv_trigo_sin(angle_new + angle_ofs) * r_out) >> LV_TRIGO_SHIFT;
175     int32_t x_out_new  = (int32_t)((int32_t)_lv_trigo_sin(angle_new + 90 + angle_ofs) * r_out) >> LV_TRIGO_SHIFT;
176 
177     lv_area_t a;
178     if(x_out_old < 0 && x_out_new < 0) {
179         a.x1 = lmeter->coords.x1 + left - line_width;
180         a.y1 = LV_MATH_MIN4(y_out_old, y_out_new, y_in_old, y_in_new) + y_ofs - line_width;
181         a.x2 = LV_MATH_MAX(x_in_old, x_in_new) + x_ofs + line_width;
182         a.y2 = LV_MATH_MAX4(y_out_old, y_out_new, y_in_old, y_in_new) + y_ofs + line_width;
183     }
184     else if(x_out_old > 0 && x_out_new > 0) {
185         a.x1 = LV_MATH_MIN(x_in_old, x_in_new) + x_ofs - line_width;
186         a.y1 = LV_MATH_MIN4(y_out_old, y_out_new, y_in_old, y_in_new) + y_ofs - line_width;
187         a.x2 = lmeter->coords.x2 - right + line_width;
188         a.y2 = LV_MATH_MAX4(y_out_old, y_out_new, y_in_old, y_in_new) + y_ofs + line_width;
189     }
190     else if(y_out_old < 0 && y_out_new < 0) {
191         a.x1 = LV_MATH_MIN4(x_out_old, x_out_new, x_in_old, x_in_new) + x_ofs - line_width;
192         a.y1 = lmeter->coords.y1 + top - line_width;
193         a.x2 = LV_MATH_MAX4(x_out_old, x_out_new, x_in_old, x_in_new) + x_ofs + line_width;
194         a.y2 = LV_MATH_MAX(y_in_old, y_in_new) + y_ofs + line_width;
195     }
196     else if(y_out_old > 0 && y_out_new > 0) {
197         a.x1 = LV_MATH_MIN4(x_out_old, x_out_new, x_in_old, x_in_new) + x_ofs - line_width;
198         a.y1 = LV_MATH_MIN(y_in_old, y_in_new) + y_ofs - line_width;
199         a.x2 = LV_MATH_MAX4(x_out_old, x_out_new, x_in_old, x_in_new) + x_ofs + line_width;
200         a.y2 = lmeter->coords.y2 - bottom + line_width;
201     }
202     else {
203         a.x1 = lmeter->coords.x1 + left - line_width;
204         a.y1 = lmeter->coords.y1 + top - line_width;
205         a.x2 = lmeter->coords.x2 - right + line_width;
206         a.y2 = lmeter->coords.y2 - bottom + line_width;
207     }
208 
209     lv_obj_invalidate_area(lmeter, &a);
210 
211 }
212 
213 /**
214  * Set minimum and the maximum values of a line meter
215  * @param lmeter pointer to he line meter object
216  * @param min minimum value
217  * @param max maximum value
218  */
lv_linemeter_set_range(lv_obj_t * lmeter,int32_t min,int32_t max)219 void lv_linemeter_set_range(lv_obj_t * lmeter, int32_t min, int32_t max)
220 {
221     LV_ASSERT_OBJ(lmeter, LV_OBJX_NAME);
222 
223     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
224     if(ext->min_value == min && ext->max_value == max) return;
225 
226     ext->max_value = max;
227     ext->min_value = min;
228     if(ext->cur_value > max) {
229         ext->cur_value = max;
230         lv_linemeter_set_value(lmeter, ext->cur_value);
231     }
232     if(ext->cur_value < min) {
233         ext->cur_value = min;
234         lv_linemeter_set_value(lmeter, ext->cur_value);
235     }
236     lv_obj_invalidate(lmeter);
237 }
238 
239 /**
240  * Set the scale settings of a line meter
241  * @param lmeter pointer to a line meter object
242  * @param angle angle of the scale (0..360)
243  * @param line_cnt number of lines
244  */
lv_linemeter_set_scale(lv_obj_t * lmeter,uint16_t angle,uint16_t line_cnt)245 void lv_linemeter_set_scale(lv_obj_t * lmeter, uint16_t angle, uint16_t line_cnt)
246 {
247     LV_ASSERT_OBJ(lmeter, LV_OBJX_NAME);
248 
249     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
250     if(ext->scale_angle == angle && ext->line_cnt == line_cnt) return;
251 
252     ext->scale_angle = angle;
253     ext->line_cnt    = line_cnt;
254 
255     lv_obj_invalidate(lmeter);
256 }
257 
258 /**
259  * Set the set an offset for the line meter's angles to rotate it.
260  * @param lmeter pointer to a line meter object
261  * @param angle angle where the meter will be facing (with its center)
262  */
lv_linemeter_set_angle_offset(lv_obj_t * lmeter,uint16_t angle)263 void lv_linemeter_set_angle_offset(lv_obj_t * lmeter, uint16_t angle)
264 {
265     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
266     if(ext->angle_ofs == angle) return;
267 
268     ext->angle_ofs = angle;
269 
270     lv_obj_invalidate(lmeter);
271 }
272 
273 /**
274  * Set the orientation of the meter growth, clockwise or counterclockwise (mirrored)
275  * @param lmeter pointer to a line meter object
276  * @param mirror mirror setting
277  */
lv_linemeter_set_mirror(lv_obj_t * lmeter,bool mirror)278 void lv_linemeter_set_mirror(lv_obj_t * lmeter, bool mirror)
279 {
280     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
281     if(ext->mirrored == mirror) return;
282 
283     ext->mirrored = mirror;
284 
285     lv_obj_invalidate(lmeter);
286 }
287 
288 /*=====================
289  * Getter functions
290  *====================*/
291 
292 /**
293  * Get the value of a line meter
294  * @param lmeter pointer to a line meter object
295  * @return the value of the line meter
296  */
lv_linemeter_get_value(const lv_obj_t * lmeter)297 int32_t lv_linemeter_get_value(const lv_obj_t * lmeter)
298 {
299     LV_ASSERT_OBJ(lmeter, LV_OBJX_NAME);
300 
301     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
302     return ext->cur_value;
303 }
304 
305 /**
306  * Get the minimum value of a line meter
307  * @param lmeter pointer to a line meter object
308  * @return the minimum value of the line meter
309  */
lv_linemeter_get_min_value(const lv_obj_t * lmeter)310 int32_t lv_linemeter_get_min_value(const lv_obj_t * lmeter)
311 {
312     LV_ASSERT_OBJ(lmeter, LV_OBJX_NAME);
313 
314     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
315     return ext->min_value;
316 }
317 
318 /**
319  * Get the maximum value of a line meter
320  * @param lmeter pointer to a line meter object
321  * @return the maximum value of the line meter
322  */
lv_linemeter_get_max_value(const lv_obj_t * lmeter)323 int32_t lv_linemeter_get_max_value(const lv_obj_t * lmeter)
324 {
325     LV_ASSERT_OBJ(lmeter, LV_OBJX_NAME);
326 
327     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
328     return ext->max_value;
329 }
330 
331 /**
332  * Get the scale number of a line meter
333  * @param lmeter pointer to a line meter object
334  * @return number of the scale units
335  */
lv_linemeter_get_line_count(const lv_obj_t * lmeter)336 uint16_t lv_linemeter_get_line_count(const lv_obj_t * lmeter)
337 {
338     LV_ASSERT_OBJ(lmeter, LV_OBJX_NAME);
339 
340     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
341     return ext->line_cnt;
342 }
343 
344 /**
345  * Get the scale angle of a line meter
346  * @param lmeter pointer to a line meter object
347  * @return angle_ofs of the scale
348  */
lv_linemeter_get_scale_angle(const lv_obj_t * lmeter)349 uint16_t lv_linemeter_get_scale_angle(const lv_obj_t * lmeter)
350 {
351     LV_ASSERT_OBJ(lmeter, LV_OBJX_NAME);
352 
353     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
354     return ext->scale_angle;
355 }
356 
357 /**
358  * Get the offset for the line meter.
359  * @param lmeter pointer to a line meter object
360  * @return angle offset (0..360)
361  */
lv_linemeter_get_angle_offset(lv_obj_t * lmeter)362 uint16_t lv_linemeter_get_angle_offset(lv_obj_t * lmeter)
363 {
364     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
365 
366     return ext->angle_ofs;
367 }
368 
369 /**
370  * get the mirror setting for the line meter
371  * @param lmeter pointer to a line meter object
372  * @return mirror (true or false)
373  */
lv_linemeter_get_mirror(lv_obj_t * lmeter)374 bool lv_linemeter_get_mirror(lv_obj_t * lmeter)
375 {
376     lv_linemeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
377 
378     return ext->mirrored;
379 }
380 
lv_linemeter_draw_scale(lv_obj_t * lmeter,const lv_area_t * clip_area,uint8_t part)381 void lv_linemeter_draw_scale(lv_obj_t * lmeter, const lv_area_t * clip_area, uint8_t part)
382 {
383     lv_linemeter_ext_t * ext    = lv_obj_get_ext_attr(lmeter);
384 
385     lv_style_int_t left = lv_obj_get_style_pad_left(lmeter, LV_LINEMETER_PART_MAIN);
386     lv_style_int_t right = lv_obj_get_style_pad_right(lmeter, LV_LINEMETER_PART_MAIN);
387     lv_style_int_t top = lv_obj_get_style_pad_top(lmeter, LV_LINEMETER_PART_MAIN);
388 
389     lv_coord_t r_out = (lv_obj_get_width(lmeter) - left - right) / 2 ;
390     lv_coord_t r_in  = r_out - lv_obj_get_style_scale_width(lmeter, part);
391     if(r_in < 1) r_in = 1;
392 
393     lv_coord_t x_ofs  = lmeter->coords.x1 + r_out + left;
394     lv_coord_t y_ofs  = lmeter->coords.y1 + r_out + top;
395     int16_t angle_ofs = ext->angle_ofs + 90 + (360 - ext->scale_angle) / 2;
396     int16_t level = ext->mirrored ?
397                     (int32_t)((int32_t)(ext->max_value - ext->cur_value) * (ext->line_cnt - 1)) / (ext->max_value - ext->min_value) :
398                     (int32_t)((int32_t)(ext->cur_value - ext->min_value) * (ext->line_cnt - 1)) / (ext->max_value - ext->min_value);
399     uint8_t i;
400 
401     lv_color_t main_color = lv_obj_get_style_line_color(lmeter, part);
402     lv_color_t grad_color = lv_obj_get_style_scale_grad_color(lmeter, part);
403     lv_color_t end_color = lv_obj_get_style_scale_end_color(lmeter, part);
404 
405     lv_draw_line_dsc_t line_dsc;
406     lv_draw_line_dsc_init(&line_dsc);
407     lv_obj_init_draw_line_dsc(lmeter, part, &line_dsc);
408 #if LV_LINEMETER_PRECISE == 2
409     line_dsc.raw_end = 1;
410 #endif
411 
412     lv_style_int_t end_line_width = lv_obj_get_style_scale_end_line_width(lmeter, part);
413 
414 #if LV_LINEMETER_PRECISE > 0
415     lv_area_t mask_area;
416     mask_area.x1 = x_ofs - r_in;
417     mask_area.x2 = x_ofs + r_in - 1;
418     mask_area.y1 = y_ofs - r_in;
419     mask_area.y2 = y_ofs + r_in - 1;
420 
421     lv_draw_mask_radius_param_t mask_in_param;
422     lv_draw_mask_radius_init(&mask_in_param, &mask_area, LV_RADIUS_CIRCLE, true);
423     int16_t mask_in_id = lv_draw_mask_add(&mask_in_param, 0);
424 #endif
425 
426 
427 #if LV_LINEMETER_PRECISE > 1
428     mask_area.x1 = x_ofs - r_out;
429     mask_area.x2 = x_ofs + r_out - 1;
430     mask_area.y1 = y_ofs - r_out;
431     mask_area.y2 = y_ofs + r_out - 1;
432     lv_draw_mask_radius_param_t mask_out_param;
433     lv_draw_mask_radius_init(&mask_out_param, &mask_area, LV_RADIUS_CIRCLE, false);
434     int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, 0);
435     /*In calculation use a larger radius to avoid rounding errors */
436     lv_coord_t r_out_extra = r_out + LV_DPI;
437 #else
438     lv_coord_t r_out_extra = r_out;
439 #endif
440 
441     for(i = 0; i < ext->line_cnt; i++) {
442         /* `* 256` for extra precision*/
443         int32_t angle_upscale = (i * ext->scale_angle * 256) / (ext->line_cnt - 1);
444         int32_t angle_normal = angle_upscale >> 8;
445 
446         int32_t angle_low = (angle_upscale >> 8);
447         int32_t angle_high = angle_low + 1;
448         int32_t angle_rem = angle_upscale & 0xFF;
449 
450         /*Interpolate sine and cos*/
451         int32_t sin_low = _lv_trigo_sin(angle_low + angle_ofs);
452         int32_t sin_high = _lv_trigo_sin(angle_high + angle_ofs);
453         int32_t sin_mid = (sin_low * (256 - angle_rem) + sin_high * angle_rem) >> 8;
454 
455         int32_t cos_low = _lv_trigo_sin(angle_low + 90 + angle_ofs);
456         int32_t cos_high = _lv_trigo_sin(angle_high + 90 + angle_ofs);
457         int32_t cos_mid = (cos_low * (256 - angle_rem) + cos_high * angle_rem) >> 8;
458 
459         /*Use the interpolated values to get x and y coordinates*/
460         int32_t y_out_extra = (int32_t)((int32_t)sin_mid * r_out_extra) >> (LV_TRIGO_SHIFT - 8);
461         int32_t x_out_extra = (int32_t)((int32_t)cos_mid * r_out_extra) >> (LV_TRIGO_SHIFT - 8);
462 
463         /*Rounding*/
464         if(x_out_extra > 0) x_out_extra = (x_out_extra + 127) >> 8;
465         else x_out_extra = (x_out_extra - 127) >> 8;
466 
467         if(y_out_extra > 0) y_out_extra = (y_out_extra + 127) >> 8;
468         else y_out_extra = (y_out_extra - 127) >> 8;
469 
470         x_out_extra += x_ofs;
471         y_out_extra += y_ofs;
472 
473         /*With no extra precision use the coordinates on the inner radius*/
474 #if LV_LINEMETER_PRECISE == 0
475         /*Use the interpolated values to get x and y coordinates*/
476         int32_t y_in_extra = (int32_t)((int32_t)sin_mid * r_in) >> (LV_TRIGO_SHIFT - 8);
477         int32_t x_in_extra = (int32_t)((int32_t)cos_mid * r_in) >> (LV_TRIGO_SHIFT - 8);
478 
479         /*Rounding*/
480         if(x_in_extra > 0) x_in_extra = (x_in_extra + 127) >> 8;
481         else x_in_extra = (x_in_extra - 127) >> 8;
482 
483         if(y_in_extra > 0) y_in_extra = (y_in_extra + 127) >> 8;
484         else y_in_extra = (y_in_extra - 127) >> 8;
485 
486         x_in_extra += x_ofs;
487         y_in_extra += y_ofs;
488 #else
489         int32_t x_in_extra = x_ofs;
490         int32_t y_in_extra = y_ofs;
491 #endif
492 
493         /*Use smaller clip area only around the visible line*/
494         int32_t y_in  = (int32_t)((int32_t)_lv_trigo_sin(angle_normal + angle_ofs) * r_in) >> LV_TRIGO_SHIFT;
495         int32_t x_in  = (int32_t)((int32_t)_lv_trigo_sin(angle_normal + 90 + angle_ofs) * r_in) >> LV_TRIGO_SHIFT;
496 
497         x_in += x_ofs;
498         y_in += y_ofs;
499 
500         int32_t y_out  = (int32_t)((int32_t)_lv_trigo_sin(angle_normal + angle_ofs) * r_out) >> LV_TRIGO_SHIFT;
501         int32_t x_out  = (int32_t)((int32_t)_lv_trigo_sin(angle_normal + 90 + angle_ofs) * r_out) >> LV_TRIGO_SHIFT;
502 
503         x_out += x_ofs;
504         y_out += y_ofs;
505 
506         lv_area_t clip_sub;
507         clip_sub.x1 = LV_MATH_MIN(x_in, x_out) - line_dsc.width;
508         clip_sub.x2 = LV_MATH_MAX(x_in, x_out) + line_dsc.width;
509         clip_sub.y1 = LV_MATH_MIN(y_in, y_out) - line_dsc.width;
510         clip_sub.y2 = LV_MATH_MAX(y_in, y_out) + line_dsc.width;
511 
512         if(_lv_area_intersect(&clip_sub, &clip_sub, clip_area) == false) continue;
513 
514         lv_point_t p1;
515         lv_point_t p2;
516 
517         p2.x = x_in_extra;
518         p2.y = y_in_extra;
519 
520         p1.x = x_out_extra;
521         p1.y = y_out_extra;
522 
523         /* Set the color of the lines */
524         if((!ext->mirrored && i >= level) || (ext->mirrored && i <= level)) {
525             line_dsc.color = end_color;
526             line_dsc.width = end_line_width;
527         }
528         else {
529             line_dsc.color = lv_color_mix(grad_color, main_color, (255 * i) / ext->line_cnt);
530         }
531 
532         lv_draw_line(&p1, &p2, &clip_sub, &line_dsc);
533     }
534 
535 #if LV_LINEMETER_PRECISE > 0
536     lv_draw_mask_remove_id(mask_in_id);
537 #endif
538 
539 #if LV_LINEMETER_PRECISE > 1
540     lv_draw_mask_remove_id(mask_out_id);
541 #endif
542 
543     if(part == LV_LINEMETER_PART_MAIN && level < ext->line_cnt - 1) {
544         lv_style_int_t border_width = lv_obj_get_style_scale_border_width(lmeter, part);
545         lv_style_int_t end_border_width = lv_obj_get_style_scale_end_border_width(lmeter, part);
546 
547         if(border_width || end_border_width) {
548             int16_t end_angle = ((level) * ext->scale_angle) / (ext->line_cnt - 1) + angle_ofs;
549             lv_draw_line_dsc_t arc_dsc;
550             lv_draw_line_dsc_init(&arc_dsc);
551             lv_obj_init_draw_line_dsc(lmeter, part, &arc_dsc);
552 
553             if(border_width) {
554                 arc_dsc.width = border_width;
555                 arc_dsc.color = main_color;
556                 lv_draw_arc(x_ofs, y_ofs, r_out, angle_ofs, end_angle, clip_area, &arc_dsc);
557             }
558 
559             if(end_border_width) {
560                 arc_dsc.width = end_border_width;
561                 arc_dsc.color = end_color;
562                 lv_draw_arc(x_ofs, y_ofs, r_out, end_angle, (angle_ofs + ext->scale_angle) % 360, clip_area, &arc_dsc);
563             }
564         }
565     }
566 
567 
568 }
569 
570 /**********************
571  *   STATIC FUNCTIONS
572  **********************/
573 
574 /**
575  * Handle the drawing related tasks of the line meters
576  * @param lmeter pointer to an object
577  * @param clip_area the object will be drawn only in this area
578  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
579  *                                  (return 'true' if yes)
580  *             LV_DESIGN_DRAW: draw the object (always return 'true')
581  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
582  * @param return an element of `lv_design_res_t`
583  */
lv_linemeter_design(lv_obj_t * lmeter,const lv_area_t * clip_area,lv_design_mode_t mode)584 static lv_design_res_t lv_linemeter_design(lv_obj_t * lmeter, const lv_area_t * clip_area, lv_design_mode_t mode)
585 {
586     /*Return false if the object is not covers the mask_p area*/
587     if(mode == LV_DESIGN_COVER_CHK) {
588         return LV_DESIGN_RES_NOT_COVER;
589     }
590     /*Draw the object*/
591     else if(mode == LV_DESIGN_DRAW_MAIN) {
592         lv_draw_rect_dsc_t bg_dsc;
593         lv_draw_rect_dsc_init(&bg_dsc);
594         lv_obj_init_draw_rect_dsc(lmeter, LV_LINEMETER_PART_MAIN, &bg_dsc);
595         lv_draw_rect(&lmeter->coords, clip_area, &bg_dsc);
596         lv_linemeter_draw_scale(lmeter, clip_area, LV_LINEMETER_PART_MAIN);
597     }
598     /*Post draw when the children are drawn*/
599     else if(mode == LV_DESIGN_DRAW_POST) {
600     }
601 
602     return LV_DESIGN_RES_OK;
603 }
604 
605 /**
606  * Signal function of the line meter
607  * @param lmeter pointer to a line meter object
608  * @param sign a signal type from lv_signal_t enum
609  * @param param pointer to a signal specific variable
610  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
611  */
lv_linemeter_signal(lv_obj_t * lmeter,lv_signal_t sign,void * param)612 static lv_res_t lv_linemeter_signal(lv_obj_t * lmeter, lv_signal_t sign, void * param)
613 {
614     lv_res_t res;
615 
616     /* Include the ancient signal function */
617     res = ancestor_signal(lmeter, sign, param);
618     if(res != LV_RES_OK) return res;
619     if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
620 
621     if(sign == LV_SIGNAL_CLEANUP) {
622         /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
623     }
624     else if(sign == LV_SIGNAL_STYLE_CHG) {
625         lv_obj_refresh_ext_draw_pad(lmeter);
626         lv_obj_invalidate(lmeter);
627     }
628 
629     return res;
630 }
631 #endif
632