From bae2e2d44cef3525c2bd587dc1adfc07fd381b17 Mon Sep 17 00:00:00 2001 From: Karijn Wessing Date: Tue, 28 Sep 2021 08:31:46 +0200 Subject: [PATCH] feat(example) add lv_example_chart_8.py (#2611) translated lv_example_chart_8.c to python variant --- examples/widgets/chart/lv_example_chart_8.py | 124 +++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 examples/widgets/chart/lv_example_chart_8.py diff --git a/examples/widgets/chart/lv_example_chart_8.py b/examples/widgets/chart/lv_example_chart_8.py new file mode 100644 index 000000000..c080df87a --- /dev/null +++ b/examples/widgets/chart/lv_example_chart_8.py @@ -0,0 +1,124 @@ +import display_driver +import lvgl as lv + +# A class is used to keep track of the series list because later we +# need to draw to the series in the reverse order to which they were initialised. +class StackedAreaChart: + def __init__(self): + self.obj = None + self.series_list = [None, None, None] + +stacked_area_chart = StackedAreaChart() + +# +# Callback which draws the blocks of colour under the lines +# +def draw_event_cb(e): + + obj = e.get_target() + cont_a = lv.area_t() + obj.get_coords(cont_a) + + #Add the faded area before the lines are drawn + dsc = e.get_draw_part_dsc() + if dsc.part == lv.PART.ITEMS: + if not dsc.p1 or not dsc.p2: + return + + # Add a line mask that keeps the area below the line + line_mask_param = lv.draw_mask_line_param_t() + line_mask_param.points_init(dsc.p1.x, dsc.p1.y, dsc.p2.x, dsc.p2.y, lv.DRAW_MASK_LINE_SIDE.BOTTOM) + line_mask_id = lv.draw_mask_add(line_mask_param, None) + + #Draw a rectangle that will be affected by the mask + draw_rect_dsc = lv.draw_rect_dsc_t() + draw_rect_dsc.init() + draw_rect_dsc.bg_opa = lv.OPA.COVER + draw_rect_dsc.bg_color = dsc.line_dsc.color + + a = lv.area_t() + a.x1 = dsc.p1.x + a.x2 = dsc.p2.x + a.y1 = min(dsc.p1.y, dsc.p2.y) + a.y2 = cont_a.y2 - 13 # -13 cuts off where the rectangle draws over the chart margin. Without this an area of 0 doesnt look like 0 + lv.draw_rect(a, dsc.clip_area, draw_rect_dsc) + + # Remove the mask + lv.draw_mask_free_param(line_mask_param) + lv.draw_mask_remove_id(line_mask_id) + + +# +# Helper function to round a fixed point number +# +def round_fixed_point(n, shift): + # Create a bitmask to isolates the decimal part of the fixed point number + mask = 1 + for bit_pos in range(shift): + mask = (mask << 1) + 1 + + decimal_part = n & mask + + # Get 0.5 as fixed point + rounding_boundary = 1 << (shift - 1) + + # Return either the integer part of n or the integer part + 1 + if decimal_part < rounding_boundary: + return (n & ~mask) + return ((n >> shift) + 1) << shift + + +# +# Stacked area chart +# +def lv_example_chart_8(): + + #Create a stacked_area_chart.obj + stacked_area_chart.obj = lv.chart(lv.scr_act()) + stacked_area_chart.obj.set_size(200, 150) + stacked_area_chart.obj.center() + stacked_area_chart.obj.set_type( lv.chart.TYPE.LINE) + stacked_area_chart.obj.set_div_line_count(5, 7) + stacked_area_chart.obj.add_event_cb( draw_event_cb, lv.EVENT.DRAW_PART_BEGIN, None) + + # Set range to 0 to 100 for percentages. Draw ticks + stacked_area_chart.obj.set_range(lv.chart.AXIS.PRIMARY_Y,0,100) + stacked_area_chart.obj.set_axis_tick(lv.chart.AXIS.PRIMARY_Y, 3, 0, 5, 1, True, 30) + + #Set point size to 0 so the lines are smooth + stacked_area_chart.obj.set_style_size(0, lv.PART.INDICATOR) + + # Add some data series + stacked_area_chart.series_list[0] = stacked_area_chart.obj.add_series(lv.palette_main(lv.PALETTE.RED), lv.chart.AXIS.PRIMARY_Y) + stacked_area_chart.series_list[1] = stacked_area_chart.obj.add_series(lv.palette_main(lv.PALETTE.BLUE), lv.chart.AXIS.PRIMARY_Y) + stacked_area_chart.series_list[2] = stacked_area_chart.obj.add_series(lv.palette_main(lv.PALETTE.GREEN), lv.chart.AXIS.PRIMARY_Y) + + for point in range(10): + # Make some random data + vals = [lv.rand(10, 20), lv.rand(20, 30), lv.rand(20, 30)] + + fixed_point_shift = 5 + total = vals[0] + vals[1] + vals[2] + draw_heights = [0, 0, 0] + int_sum = 0 + decimal_sum = 0 + + # Fixed point cascade rounding ensures percentages add to 100 + for series_index in range(3): + decimal_sum += int(((vals[series_index] * 100) << fixed_point_shift) // total) + int_sum += int((vals[series_index] * 100) / total) + + modifier = (round_fixed_point(decimal_sum, fixed_point_shift) >> fixed_point_shift) - int_sum + + # The draw heights are equal to the percentage of the total each value is + the cumulative sum of the previous percentages. + # The accumulation is how the values get "stacked" + draw_heights[series_index] = int(int_sum + modifier) + + # Draw to the series in the reverse order to which they were initialised. + # Without this the higher values will draw on top of the lower ones. + # This is because the Z-height of a series matches the order it was initialsied + stacked_area_chart.obj.set_next_value( stacked_area_chart.series_list[3 - series_index - 1], draw_heights[series_index]) + + stacked_area_chart.obj.refresh() + +lv_example_chart_8()