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 lv_obj_t * obj;
7 lv_chart_series_t * series_list[3];
8 } stacked_area_chart_t;
9
10 static stacked_area_chart_t stacked_area_chart;
11
12 /**
13 * Callback which draws the blocks of colour under the lines
14 **/
draw_event_cb(lv_event_t * e)15 static void draw_event_cb(lv_event_t * e)
16 {
17 lv_obj_t * obj = lv_event_get_target(e);
18
19 /*Add the faded area before the lines are drawn*/
20 lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
21 if(dsc->part == LV_PART_ITEMS) {
22 if(!dsc->p1 || !dsc->p2)
23 return;
24
25 /*Add a line mask that keeps the area below the line*/
26 lv_draw_mask_line_param_t line_mask_param;
27 lv_draw_mask_line_points_init(&line_mask_param, dsc->p1->x, dsc->p1->y, dsc->p2->x, dsc->p2->y,
28 LV_DRAW_MASK_LINE_SIDE_BOTTOM);
29 int16_t line_mask_id = lv_draw_mask_add(&line_mask_param, NULL);
30
31 /*Draw a rectangle that will be affected by the mask*/
32 lv_draw_rect_dsc_t draw_rect_dsc;
33 lv_draw_rect_dsc_init(&draw_rect_dsc);
34 draw_rect_dsc.bg_opa = LV_OPA_COVER;
35 draw_rect_dsc.bg_color = dsc->line_dsc->color;
36
37 lv_area_t a;
38 a.x1 = dsc->p1->x;
39 a.x2 = dsc->p2->x;
40 a.y1 = LV_MIN(dsc->p1->y, dsc->p2->y);
41 a.y2 = obj->coords.y2 -
42 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 mask = (mask << 1) + 1;
60 }
61
62 int32_t decimal_part = n & mask;
63
64 /* Get 0.5 as fixed point */
65 int32_t rounding_boundary = 1 << (shift - 1);
66
67 /* Return either the integer part of n or the integer part + 1 */
68 return (decimal_part < rounding_boundary) ? (n & ~mask) : ((n >> shift) + 1) << shift;
69 }
70
71 /**
72 * Stacked area chart
73 */
lv_example_chart_8(void)74 void lv_example_chart_8(void)
75 {
76 /*Create a stacked_area_chart.obj*/
77 stacked_area_chart.obj = lv_chart_create(lv_scr_act());
78 lv_obj_set_size(stacked_area_chart.obj, 200, 150);
79 lv_obj_center(stacked_area_chart.obj);
80 lv_chart_set_type(stacked_area_chart.obj, LV_CHART_TYPE_LINE);
81 lv_chart_set_div_line_count(stacked_area_chart.obj, 5, 7);
82 lv_obj_add_event_cb(stacked_area_chart.obj, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
83
84 /* Set range to 0 to 100 for percentages. Draw ticks */
85 lv_chart_set_range(stacked_area_chart.obj, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
86 lv_chart_set_axis_tick(stacked_area_chart.obj, LV_CHART_AXIS_PRIMARY_Y, 3, 0, 5, 1, true, 30);
87
88 /*Set point size to 0 so the lines are smooth */
89 lv_obj_set_style_size(stacked_area_chart.obj, 0, LV_PART_INDICATOR);
90
91 /*Add some data series*/
92 stacked_area_chart.series_list[0] = lv_chart_add_series(stacked_area_chart.obj, lv_palette_main(LV_PALETTE_RED),
93 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),
95 LV_CHART_AXIS_PRIMARY_Y);
96 stacked_area_chart.series_list[2] = lv_chart_add_series(stacked_area_chart.obj, lv_palette_main(LV_PALETTE_GREEN),
97 LV_CHART_AXIS_PRIMARY_Y);
98
99 for(int point = 0; point < 10; point++) {
100 /* Make some random data */
101 uint32_t vals[3] = {lv_rand(10, 20), lv_rand(20, 30), lv_rand(20, 30)};
102
103 int8_t fixed_point_shift = 5;
104 uint32_t total = vals[0] + vals[1] + vals[2];
105 uint32_t draw_heights[3];
106 uint32_t int_sum = 0;
107 uint32_t decimal_sum = 0;
108
109 /* Fixed point cascade rounding ensures percentages add to 100 */
110 for(int32_t series_index = 0; series_index < 3; series_index++) {
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],
124 draw_heights[series_index]);
125 }
126 }
127
128 lv_chart_refresh(stacked_area_chart.obj);
129 }
130
131 #endif
132