1 #include "../../lv_examples.h"
2 #if LV_USE_CHART && LV_DRAW_COMPLEX && LV_BUILD_EXAMPLES
3 
4 /*  A struct 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. */
5 typedef struct
6 {
7     lv_obj_t *obj;
8     lv_chart_series_t *series_list[3];
9 } stacked_area_chart_t;
10 
11 static stacked_area_chart_t stacked_area_chart;
12 
13 /**
14  * Callback which draws the blocks of colour under the lines
15  **/
draw_event_cb(lv_event_t * e)16 static void draw_event_cb(lv_event_t *e)
17 {
18     lv_obj_t *obj = lv_event_get_target(e);
19 
20     /*Add the faded area before the lines are drawn*/
21     lv_obj_draw_part_dsc_t *dsc = lv_event_get_draw_part_dsc(e);
22     if (dsc->part == LV_PART_ITEMS)
23     {
24         if (!dsc->p1 || !dsc->p2)
25             return;
26 
27         /*Add a line mask that keeps the area below the line*/
28         lv_draw_mask_line_param_t line_mask_param;
29         lv_draw_mask_line_points_init(&line_mask_param, dsc->p1->x, dsc->p1->y, dsc->p2->x, dsc->p2->y, LV_DRAW_MASK_LINE_SIDE_BOTTOM);
30         int16_t line_mask_id = lv_draw_mask_add(&line_mask_param, NULL);
31 
32         /*Draw a rectangle that will be affected by the mask*/
33         lv_draw_rect_dsc_t draw_rect_dsc;
34         lv_draw_rect_dsc_init(&draw_rect_dsc);
35         draw_rect_dsc.bg_opa = LV_OPA_COVER;
36         draw_rect_dsc.bg_color = dsc->line_dsc->color;
37 
38         lv_area_t a;
39         a.x1 = dsc->p1->x;
40         a.x2 = dsc->p2->x;
41         a.y1 = LV_MIN(dsc->p1->y, dsc->p2->y);
42         a.y2 = obj->coords.y2 - 13; /* -13 cuts off where the rectangle draws over the chart margin. Without this an area of 0 doesn't look like 0 */
43         lv_draw_rect(dsc->draw_ctx, &draw_rect_dsc, &a);
44 
45         /*Remove the mask*/
46         lv_draw_mask_free_param(&line_mask_param);
47         lv_draw_mask_remove_id(line_mask_id);
48     }
49 }
50 
51 /**
52  * Helper function to round a fixed point number
53  **/
round_fixed_point(int32_t n,int8_t shift)54 static int32_t round_fixed_point(int32_t n, int8_t shift)
55 {
56     /* Create a bitmask to isolates the decimal part of the fixed point number */
57     int32_t mask = 1;
58     for (int32_t bit_pos = 0; bit_pos < shift; bit_pos++)
59     {
60         mask = (mask << 1) + 1;
61     }
62 
63     int32_t decimal_part = n & mask;
64 
65     /* Get 0.5 as fixed point */
66     int32_t rounding_boundary = 1 << (shift - 1);
67 
68     /* Return either the integer part of n or the integer part + 1 */
69     return (decimal_part < rounding_boundary) ? (n & ~mask) : ((n >> shift) + 1) << shift;
70 }
71 
72 /**
73  * Stacked area chart
74  */
lv_example_chart_8(void)75 void lv_example_chart_8(void)
76 {
77     /*Create a stacked_area_chart.obj*/
78     stacked_area_chart.obj = lv_chart_create(lv_scr_act());
79     lv_obj_set_size(stacked_area_chart.obj, 200, 150);
80     lv_obj_center(stacked_area_chart.obj);
81     lv_chart_set_type(stacked_area_chart.obj, LV_CHART_TYPE_LINE);
82     lv_chart_set_div_line_count(stacked_area_chart.obj, 5, 7);
83     lv_obj_add_event_cb(stacked_area_chart.obj, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
84 
85     /* Set range to 0 to 100 for percentages. Draw ticks */
86     lv_chart_set_range(stacked_area_chart.obj,LV_CHART_AXIS_PRIMARY_Y,0,100);
87     lv_chart_set_axis_tick(stacked_area_chart.obj, LV_CHART_AXIS_PRIMARY_Y, 3, 0, 5, 1, true, 30);
88 
89     /*Set point size to 0 so the lines are smooth */
90     lv_obj_set_style_size(stacked_area_chart.obj, 0, LV_PART_INDICATOR);
91 
92     /*Add some data series*/
93     stacked_area_chart.series_list[0] = lv_chart_add_series(stacked_area_chart.obj, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
94     stacked_area_chart.series_list[1] = lv_chart_add_series(stacked_area_chart.obj, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_PRIMARY_Y);
95     stacked_area_chart.series_list[2] = lv_chart_add_series(stacked_area_chart.obj, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y);
96 
97     for (int point = 0; point < 10; point++)
98     {
99         /* Make some random data */
100         uint32_t vals[3] = {lv_rand(10, 20), lv_rand(20, 30), lv_rand(20, 30)};
101 
102         int8_t fixed_point_shift = 5;
103         uint32_t total = vals[0] + vals[1] + vals[2];
104         uint32_t draw_heights[3];
105         uint32_t int_sum = 0;
106         uint32_t decimal_sum = 0;
107 
108         /* Fixed point cascade rounding ensures percentages add to 100 */
109         for (int32_t series_index = 0; series_index < 3; series_index++)
110         {
111             decimal_sum += (((vals[series_index] * 100) << fixed_point_shift) / total);
112             int_sum += (vals[series_index] * 100) / total;
113 
114             int32_t modifier = (round_fixed_point(decimal_sum, fixed_point_shift) >> fixed_point_shift) - int_sum;
115 
116             /*  The draw heights are equal to the percentage of the total each value is + the cumulative sum of the previous percentages.
117                 The accumulation is how the values get "stacked" */
118             draw_heights[series_index] = int_sum + modifier;
119 
120             /*  Draw to the series in the reverse order to which they were initialised.
121                 Without this the higher values will draw on top of the lower ones.
122                 This is because the Z-height of a series matches the order it was initialised */
123             lv_chart_set_next_value(stacked_area_chart.obj, stacked_area_chart.series_list[3 - series_index - 1], draw_heights[series_index]);
124         }
125     }
126 
127     lv_chart_refresh(stacked_area_chart.obj);
128 }
129 
130 #endif
131