From 0aec010a52cb65f18e484d0b76549ca23766e13d Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Tue, 16 Mar 2021 12:32:52 +0100 Subject: [PATCH] feat(arc): add lv_draw_arc_get_area to have a common function to get the area to invalidated --- src/lv_draw/lv_draw_arc.c | 87 ++++++++++++++++++++++++++++++++++----- src/lv_draw/lv_draw_arc.h | 28 +++++++++---- src/lv_widgets/lv_meter.c | 72 ++++++++++++++++++++++++++++++-- 3 files changed, 165 insertions(+), 22 deletions(-) diff --git a/src/lv_draw/lv_draw_arc.c b/src/lv_draw/lv_draw_arc.c index 941168e97..85c29a97e 100644 --- a/src/lv_draw/lv_draw_arc.c +++ b/src/lv_draw/lv_draw_arc.c @@ -67,17 +67,6 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc) dsc->color = lv_color_black(); } -/** - * Draw an arc. (Can draw pie too with great thickness.) - * @param center_x the x coordinate of the center of the arc - * @param center_y the y coordinate of the center of the arc - * @param radius the radius of the arc - * @param mask the arc will be drawn only in this mask - * @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right) - * @param end_angle the end angle of the arc - * @param clip_area the arc will be drawn only in this area - * @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable - */ void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc) { @@ -219,6 +208,82 @@ void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uin #endif /*LV_DRAW_COMPLEX*/ } +void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, lv_coord_t w, bool rounded, lv_area_t * inv_area) +{ + lv_coord_t rout = radius; + lv_coord_t rin = radius - w; + lv_coord_t extra_area = rounded ? w : 0; + uint8_t start_quarter = start_angle / 90; + uint8_t end_quarter = end_angle / 90; + + + if(start_quarter == end_quarter && start_angle <= end_angle) { + if(start_quarter == 0) { + inv_area->y1 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area; + + inv_area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area; + inv_area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area; + } + else if(start_quarter == 1) { + inv_area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area; + inv_area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area; + + inv_area->y1 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area; + } + else if(start_quarter == 2) { + inv_area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->y2 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area; + + inv_area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area; + } + else if(start_quarter == 3) { + inv_area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area; + + inv_area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area; + inv_area->y2 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area; + } + } + else if(start_quarter == 0 && end_quarter == 1) { + inv_area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->y1 = y + ((LV_MIN(lv_trigo_sin(end_angle), + lv_trigo_sin(start_angle)) * rin) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area; + inv_area->y2 = y + rout + extra_area; + } + else if(start_quarter == 1 && end_quarter == 2) { + inv_area->x1 = x - rout - extra_area; + inv_area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->x2 = x + ((LV_MAX(lv_trigo_sin(start_angle + 90), + lv_trigo_sin(end_angle + 90)) * rin) >> LV_TRIGO_SHIFT) + extra_area; + inv_area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area; + } + else if(start_quarter == 2 && end_quarter == 3) { + inv_area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->y1 = y - rout - extra_area; + inv_area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area; + inv_area->y2 = y + (LV_MAX(lv_trigo_sin(end_angle) * rin, + lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area; + } + else if(start_quarter == 3 && end_quarter == 0) { + inv_area->x1 = x + ((LV_MIN(lv_trigo_sin(end_angle + 90), + lv_trigo_sin(start_angle + 90)) * rin) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area; + inv_area->x2 = x + rout + extra_area; + inv_area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area; + + } + else { + inv_area->x1 = x - rout; + inv_area->y1 = y - rout; + inv_area->x2 = x + rout; + inv_area->y2 = y + rout; + } +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/lv_draw/lv_draw_arc.h b/src/lv_draw/lv_draw_arc.h index 3c1dd6344..8f74a5500 100644 --- a/src/lv_draw/lv_draw_arc.h +++ b/src/lv_draw/lv_draw_arc.h @@ -40,18 +40,30 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc); /** * Draw an arc. (Can draw pie too with great thickness.) - * @param center_x the x coordinate of the center of the arc - * @param center_y the y coordinate of the center of the arc - * @param radius the radius of the arc - * @param mask the arc will be drawn only in this mask - * @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right) - * @param end_angle the end angle of the arc - * @param clip_area the arc will be drawn only in this area - * @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable + * @param center_x the x coordinate of the center of the arc + * @param center_y the y coordinate of the center of the arc + * @param radius the radius of the arc + * @param mask the arc will be drawn only in this mask + * @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right) + * @param end_angle the end angle of the arc + * @param clip_area the arc will be drawn only in this area + * @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable */ void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc); +/** + * Get an area the should be invalidated when the arcs angle changed between start_angle and end_ange + * @param x the x coordinate of the center of the arc + * @param y the y coordinate of the center of the arc + * @param radius the radius of the arc + * @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right) + * @param end_angle the end angle of the arc + * @param w width of the arc + * @param rounded true: the arc is rounded + * @param result store the area to invalidate here + */ +void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, lv_coord_t w, bool rounded, lv_area_t * result); /********************** * MACROS **********************/ diff --git a/src/lv_widgets/lv_meter.c b/src/lv_widgets/lv_meter.c index 0d8adc1b4..b4a04ad9a 100644 --- a/src/lv_widgets/lv_meter.c +++ b/src/lv_widgets/lv_meter.c @@ -33,6 +33,8 @@ static lv_draw_res_t lv_meter_draw(lv_obj_t * lmeter, const lv_area_t * clip_are static void draw_arcs(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area); static void draw_ticks_and_labels(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area); static void draw_needles(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area); +static lv_meter_scale_t * get_scale_of_indic(lv_obj_t * obj, lv_meter_indicator_t * indic); +static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value); /********************** * STATIC VARIABLES @@ -187,21 +189,41 @@ lv_meter_indicator_t * lv_meter_add_scale_lines(lv_obj_t * obj, lv_meter_scale_t void lv_meter_set_indicator_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value) { + int32_t old_start = indic->start_value; + int32_t old_end = indic->end_value; indic->start_value = value; indic->end_value = value; - lv_obj_invalidate(obj); + + if(indic->type == LV_METER_INDICATOR_TYPE_ARC) { + inv_arc(obj, indic, old_start, value); + inv_arc(obj, indic, old_end, value); + } else { + lv_obj_invalidate(obj); + } } void lv_meter_set_indicator_start_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value) { + int32_t old_value = indic->start_value; indic->start_value = value; - lv_obj_invalidate(obj); + + if(indic->type == LV_METER_INDICATOR_TYPE_ARC) { + inv_arc(obj, indic, old_value, value); + } else { + lv_obj_invalidate(obj); + } } void lv_meter_set_indicator_end_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value) { + int32_t old_value = indic->end_value; indic->end_value = value; - lv_obj_invalidate(obj); + + if(indic->type == LV_METER_INDICATOR_TYPE_ARC) { + inv_arc(obj, indic, old_value, value); + } else { + lv_obj_invalidate(obj); + } } /********************** @@ -581,4 +603,48 @@ static void draw_needles(lv_obj_t * obj, const lv_area_t * clip_area, const lv_a } } +static lv_meter_scale_t * get_scale_of_indic(lv_obj_t * obj, lv_meter_indicator_t * indic) +{ + lv_meter_t * meter = (lv_meter_t *)obj; + + lv_meter_scale_t * scale; + + _LV_LL_READ_BACK(&meter->scale_ll, scale) { + lv_meter_indicator_t * ind; + _LV_LL_READ_BACK(&scale->indicator_ll, ind) { + if(ind == indic) return scale; + } + } + + return NULL; +} + + +static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value) +{ + bool rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS); + + lv_area_t scale_area; + lv_obj_get_coords(obj, &scale_area); + scale_area.x1 += lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + scale_area.y1 += lv_obj_get_style_pad_top(obj, LV_PART_MAIN); + scale_area.x2 -= lv_obj_get_style_pad_right(obj, LV_PART_MAIN); + scale_area.y2 -= lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); + + lv_coord_t r_out = lv_area_get_width(&scale_area) / 2; + lv_point_t scale_center; + scale_center.x = scale_area.x1 + r_out; + scale_center.y = scale_area.y1 + r_out; + + r_out += indic->type_data.arc.r_mod; + + lv_meter_scale_t * scale = get_scale_of_indic(obj, indic); + + int32_t start_angle = lv_map(old_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation); + int32_t end_angle = lv_map(new_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation); + lv_area_t a; + lv_draw_arc_get_area(scale_center.x, scale_center.y, r_out, LV_MIN(start_angle, end_angle), LV_MAX(start_angle, end_angle), indic->type_data.arc.width, rounded, &a); + lv_obj_invalidate_area(obj, &a); +} + #endif