feat(chart): add stacked chart support (#8573)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: liamHowatt <liamjmh0@gmail.com>
This commit is contained in:
Gabor Kiss-Vamosi
2025-08-15 23:20:37 +02:00
committed by GitHub
parent 709461813b
commit e6864400a2
6 changed files with 259 additions and 74 deletions
+11 -11
View File
@@ -31,8 +31,6 @@ static void shop_chart_event_cb(lv_event_t * e);
**********************/
static lv_obj_t * chart3;
static lv_chart_series_t * ser4;
/**********************
* GLOBAL VARIABLES
**********************/
@@ -66,19 +64,21 @@ void lv_demo_widgets_shop_create(lv_obj_t * parent)
lv_obj_set_style_text_color(hint, lv_palette_main(LV_PALETTE_GREEN), 0);
chart3 = lv_chart_create(panel1);
lv_chart_set_type(chart3, LV_CHART_TYPE_BAR);
lv_chart_set_type(chart3, LV_CHART_TYPE_STACKED);
lv_chart_set_div_line_count(chart3, 6, 0);
lv_chart_set_point_count(chart3, 7);
lv_obj_add_event_cb(chart3, shop_chart_event_cb, LV_EVENT_ALL, NULL);
ser4 = lv_chart_add_series(chart3, lv_theme_get_color_primary(chart3), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_set_next_value(chart3, ser4, lv_rand(60, 90));
lv_chart_set_next_value(chart3, ser4, lv_rand(60, 90));
lv_chart_set_next_value(chart3, ser4, lv_rand(60, 90));
lv_chart_set_next_value(chart3, ser4, lv_rand(60, 90));
lv_chart_set_next_value(chart3, ser4, lv_rand(60, 90));
lv_chart_set_next_value(chart3, ser4, lv_rand(60, 90));
lv_chart_set_next_value(chart3, ser4, lv_rand(60, 90));
lv_chart_series_t * ser4 = lv_chart_add_series(chart3, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_series_t * ser5 = lv_chart_add_series(chart3, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_series_t * ser6 = lv_chart_add_series(chart3, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
uint32_t i;
for(i = 0; i < 8; i++) {
lv_chart_set_next_value(chart3, ser4, lv_rand(20, 40));
lv_chart_set_next_value(chart3, ser5, lv_rand(15, 30));
lv_chart_set_next_value(chart3, ser6, lv_rand(15, 30));
}
if(disp_size == DISP_LARGE) {
static int32_t grid1_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
+7 -5
View File
@@ -38,7 +38,7 @@ Each chart has the following attributes (over and above attributes found in
Type (governs how a chart's data series are drawn)
- Can be LINE (default), BAR, SCATTER, or none.
- Can be LINE (default), BAR, STACKED, SCATTER, or NONE.
- You can change the chart's type at any point during its life.
Horizontal and Vertical division lines
@@ -60,8 +60,8 @@ Point count (number of data points in all data series added to the chart)
- default 10
- If you provide your own data-value arrays, each array so provided must contain
at least this number of values.
- For LINE- and BAR-charts, this is the number of points on the X axis.
- LINE- and BAR-charts require only one data-value array to supply Y-values for each data point.
- For LINE-, BAR-, STACKED-charts, this is the number of points on the X axis.
- LINE-, BAR-, STACKED-charts require only one data-value array to supply Y-values for each data point.
- For SCATTER charts, this is the number of scatter-points in the data series.
- SCATTER charts have separate data-value arrays for both X-values and Y-values.
@@ -121,7 +121,7 @@ Parts and Styles
- :cpp:enumerator:`LV_PART_MAIN` The background of the chart. Uses the :ref:`typical
background <typical bg props>` and line style properties (for division lines).
*Padding* makes the series area smaller. For BAR charts ``pad_column`` sets the
*Padding* makes the series area smaller. For BAR and STACKED charts ``pad_column`` sets the
space between bars in the same data series.
- :cpp:enumerator:`LV_PART_SCROLLBAR` A scrollbar used if the chart is zoomed. See
:ref:`base_widget`'s documentation for details.
@@ -157,7 +157,9 @@ A chart can be one of the following types:
can also be illustrated if their ``width``, ``height``, ``bg_color`` and ``radius``
styles (for :cpp:enumerator:`LV_PART_ITEMS`) are set and both ``width`` and
``height`` have non-zero values.
- :cpp:enumerator:`LV_CHART_TYPE_BAR`: Draw bars.
- :cpp:enumerator:`LV_CHART_TYPE_BAR`: Draw individual vertical bars for each point in each series.
- :cpp:enumerator:`LV_CHART_TYPE_STACKED`: Draw vertical stacked bars where multiple data series
are displayed as segments within a single bar for each data point. Supports only positive values.
- :cpp:enumerator:`LV_CHART_TYPE_SCATTER`: X/Y chart drawing point's and optionally
lines between the points if line-width style values for
:cpp:enumerator:`LV_PART_ITEMS` is a non-zero value, and the point's Y-value is
+41 -35
View File
@@ -20,44 +20,49 @@ static void event_cb(lv_event_t * e)
LV_LOG_USER("Selected point %d", (int)id);
lv_chart_series_t * ser = lv_chart_get_series_next(chart, NULL);
int32_t value = 0;
while(ser) {
lv_point_t p;
lv_chart_get_point_pos_by_id(chart, ser, id, &p);
int32_t * y_array = lv_chart_get_series_y_array(chart, ser);
int32_t value = y_array[id];
if(y_array[id] != LV_CHART_POINT_NONE && y_array[id] >= 0) {
/*Draw a rectangle above the clicked point*/
lv_layer_t * layer = lv_event_get_layer(e);
lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_color = lv_color_black();
draw_rect_dsc.bg_opa = LV_OPA_50;
draw_rect_dsc.radius = 3;
/*Accumulate the values to show the rectangles at the top of each segment*/
value += y_array[id];
lv_area_t chart_obj_coords;
lv_obj_get_coords(chart, &chart_obj_coords);
lv_area_t rect_area;
rect_area.x1 = chart_obj_coords.x1 + p.x - 20;
rect_area.x2 = chart_obj_coords.x1 + p.x + 20;
rect_area.y1 = chart_obj_coords.y1 + p.y - 30;
rect_area.y2 = chart_obj_coords.y1 + p.y - 10;
lv_draw_rect(layer, &draw_rect_dsc, &rect_area);
/*Draw a rectangle above the clicked point*/
lv_layer_t * layer = lv_event_get_layer(e);
lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_color = lv_color_black();
draw_rect_dsc.bg_opa = LV_OPA_50;
draw_rect_dsc.radius = 3;
/*Draw the value as label to the center of the rectangle*/
char buf[16];
lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"$%d", value);
lv_area_t chart_obj_coords;
lv_obj_get_coords(chart, &chart_obj_coords);
lv_area_t rect_area;
rect_area.x1 = chart_obj_coords.x1 + p.x - 20;
rect_area.x2 = chart_obj_coords.x1 + p.x + 20;
rect_area.y1 = chart_obj_coords.y1 + p.y - 10;
rect_area.y2 = chart_obj_coords.y1 + p.y + 10;
lv_draw_rect(layer, &draw_rect_dsc, &rect_area);
lv_draw_label_dsc_t draw_label_dsc;
lv_draw_label_dsc_init(&draw_label_dsc);
draw_label_dsc.color = lv_color_white();
draw_label_dsc.text = buf;
draw_label_dsc.text_local = 1;
draw_label_dsc.align = LV_TEXT_ALIGN_CENTER;
lv_area_t label_area = rect_area;
lv_area_set_height(&label_area, lv_font_get_line_height(draw_label_dsc.font));
lv_area_align(&rect_area, &label_area, LV_ALIGN_CENTER, 0, 0);
lv_draw_label(layer, &draw_label_dsc, &label_area);
/*Draw the value as label to the center of the rectangle*/
char buf[16];
lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"$%d", value);
lv_draw_label_dsc_t draw_label_dsc;
lv_draw_label_dsc_init(&draw_label_dsc);
draw_label_dsc.color = lv_color_white();
draw_label_dsc.text = buf;
draw_label_dsc.text_local = 1;
draw_label_dsc.align = LV_TEXT_ALIGN_CENTER;
lv_area_t label_area = rect_area;
lv_area_set_height(&label_area, lv_font_get_line_height(draw_label_dsc.font));
lv_area_align(&rect_area, &label_area, LV_ALIGN_CENTER, 0, 0);
lv_draw_label(layer, &draw_label_dsc, &label_area);
}
ser = lv_chart_get_series_next(chart, ser);
}
@@ -75,22 +80,23 @@ void lv_example_chart_3(void)
/*Create a chart*/
lv_obj_t * chart;
chart = lv_chart_create(lv_screen_active());
lv_obj_set_size(chart, 200, 150);
lv_obj_set_size(chart, 280, 180);
lv_obj_set_style_pad_column(chart, 4, 0);
lv_obj_center(chart);
lv_chart_set_type(chart, LV_CHART_TYPE_STACKED);
lv_obj_add_event_cb(chart, event_cb, LV_EVENT_ALL, NULL);
lv_obj_refresh_ext_draw_size(chart);
/*Zoom in a little in X*/
// lv_chart_set_scale_x(chart, 800);
/*Add two data series*/
lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_series_t * ser3 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_PRIMARY_Y);
uint32_t i;
for(i = 0; i < 10; i++) {
lv_chart_set_next_value(chart, ser1, (int32_t)lv_rand(60, 90));
lv_chart_set_next_value(chart, ser2, (int32_t)lv_rand(10, 40));
lv_chart_set_next_value(chart, ser1, lv_rand(15, 30));
lv_chart_set_next_value(chart, ser2, lv_rand(15, 30));
lv_chart_set_next_value(chart, ser3, lv_rand(15, 30));
}
}
+197 -21
View File
@@ -40,11 +40,13 @@ static void lv_chart_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_div_lines(lv_obj_t * obj, lv_layer_t * layer);
static void draw_series_line(lv_obj_t * obj, lv_layer_t * layer);
static void draw_series_bar(lv_obj_t * obj, lv_layer_t * layer);
static void draw_series_stacked(lv_obj_t * obj, lv_layer_t * layer);
static void draw_series_scatter(lv_obj_t * obj, lv_layer_t * layer);
static void draw_cursors(lv_obj_t * obj, lv_layer_t * layer);
static uint32_t get_index_from_x(lv_obj_t * obj, int32_t x);
static void invalidate_point(lv_obj_t * obj, uint32_t i);
static void new_points_alloc(lv_obj_t * obj, lv_chart_series_t * ser, uint32_t cnt, int32_t ** a);
static int32_t value_to_y(lv_obj_t * obj, lv_chart_series_t * ser, int32_t v, int32_t h);
/**********************
* STATIC VARIABLES
@@ -291,9 +293,13 @@ void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint3
else {
p_out->x = 0;
}
int32_t temp_y = value_to_y(obj, ser, ser->y_points[id], h);
p_out->y = h - temp_y;
}
else if(chart->type == LV_CHART_TYPE_SCATTER) {
p_out->x = lv_map(ser->x_points[id], chart->xmin[ser->x_axis_sec], chart->xmax[ser->x_axis_sec], 0, w);
int32_t temp_y = value_to_y(obj, ser, ser->y_points[id], h);
p_out->y = h - temp_y;
}
else if(chart->type == LV_CHART_TYPE_BAR) {
uint32_t ser_cnt = lv_ll_get_len(&chart->series_ll);
@@ -328,9 +334,51 @@ void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint3
else {
LV_LOG_WARN("bar chart series count is zero");
}
int32_t temp_y = value_to_y(obj, ser, ser->y_points[id], h);
p_out->y = h - temp_y;
}
else if(chart->type == LV_CHART_TYPE_STACKED) {
/*Gap between the columns on adjacent X ticks*/
int32_t block_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN);
int32_t block_w = (w - ((chart->point_cnt - 1) * block_gap)) / chart->point_cnt;
if(chart->point_cnt > 1) {
p_out->x = (int32_t)((int32_t)(w - block_w) * id) / (chart->point_cnt - 1);
}
else {
p_out->x = 0;
}
p_out->x += block_w / 2;
int32_t v_sum = 0;
lv_chart_series_t * s;
LV_LL_READ(&chart->series_ll, s) {
if(s->hidden) continue;
int32_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? s->start_point : 0;
int32_t p_act = (start_point + id) % chart->point_cnt;
int32_t v_act = s->y_points[p_act];
if(s->y_points[p_act] == LV_CHART_POINT_NONE) continue;
/* Skip negative values in stacked charts. Negative values are not supported
* in stacked charts as they cannot be visually represented in the stacking logic. */
if(v_act <= 0) {
LV_LOG_WARN("Stacked chart doesn't support negative values.");
continue;
}
v_sum += v_act;
if(s == ser) break;
}
int32_t temp_y = value_to_y(obj, ser, v_sum, h);
p_out->y = h - temp_y;
}
/*LV_CHART_TYPE_NONE*/
else {
p_out->x = 0;
p_out->y = 0;
}
int32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
@@ -339,10 +387,7 @@ void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint3
uint32_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0;
id = ((int32_t)start_point + id) % chart->point_cnt;
int32_t temp_y = 0;
temp_y = (int32_t)((int32_t)ser->y_points[id] - chart->ymin[ser->y_axis_sec]) * h;
temp_y = temp_y / (chart->ymax[ser->y_axis_sec] - chart->ymin[ser->y_axis_sec]);
p_out->y = h - temp_y;
p_out->y += lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
p_out->y -= lv_obj_get_scroll_top(obj);
}
@@ -578,10 +623,10 @@ void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, int32_t va
LV_ASSERT_NULL(ser);
lv_chart_t * chart = (lv_chart_t *)obj;
ser->y_points[ser->start_point] = value;
invalidate_point(obj, ser->start_point);
ser->start_point = (ser->start_point + 1) % chart->point_cnt;
invalidate_point(obj, ser->start_point);
}
void lv_chart_set_next_value2(lv_obj_t * obj, lv_chart_series_t * ser, int32_t x_value, int32_t y_value)
@@ -698,7 +743,7 @@ int32_t lv_chart_get_first_point_center_offset(lv_obj_t * obj)
lv_chart_t * chart = (lv_chart_t *)obj;
int32_t x_ofs = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
if(chart->type == LV_CHART_TYPE_BAR) {
if(chart->type == LV_CHART_TYPE_BAR || chart->type == LV_CHART_TYPE_STACKED) {
lv_obj_update_layout(obj);
/*Gap between the columns on ~adjacent X*/
int32_t block_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN);
@@ -824,6 +869,7 @@ static void lv_chart_event(const lv_obj_class_t * class_p, lv_event_t * e)
if(lv_ll_is_empty(&chart->series_ll) == false) {
if(chart->type == LV_CHART_TYPE_LINE) draw_series_line(obj, layer);
else if(chart->type == LV_CHART_TYPE_BAR) draw_series_bar(obj, layer);
else if(chart->type == LV_CHART_TYPE_STACKED) draw_series_stacked(obj, layer);
else if(chart->type == LV_CHART_TYPE_SCATTER) draw_series_scatter(obj, layer);
}
@@ -1113,7 +1159,7 @@ static void draw_series_scatter(lv_obj_t * obj, lv_layer_t * layer)
p_act = (start_point + i) % chart->point_cnt;
if(ser->y_points[p_act] != LV_CHART_POINT_NONE) {
line_dsc.p2.y = lv_map(ser->y_points[p_act], chart->ymin[ser->y_axis_sec], chart->ymax[ser->y_axis_sec], 0, h);
line_dsc.p2.y = lv_map(ser->y_points[p_act], chart->ymin[ser->y_axis_sec], chart->ymax[ser->y_axis_sec], 0, h);
line_dsc.p2.y = h - line_dsc.p2.y;
line_dsc.p2.y += y_ofs;
@@ -1244,6 +1290,118 @@ static void draw_series_bar(lv_obj_t * obj, lv_layer_t * layer)
}
}
static void draw_series_stacked(lv_obj_t * obj, lv_layer_t * layer)
{
lv_chart_t * chart = (lv_chart_t *)obj;
uint32_t i;
int32_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
int32_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
int32_t w = lv_obj_get_content_width(obj);
int32_t h = lv_obj_get_content_height(obj);
lv_chart_series_t * ser;
uint32_t ser_cnt = lv_ll_get_len(&chart->series_ll);
if(ser_cnt == 0) {
return;
}
int32_t block_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); /*Gap between the columns on adjacent X ticks*/
int32_t block_w = (w - ((chart->point_cnt - 1) * block_gap)) / chart->point_cnt;
int32_t border_w = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
int32_t x_ofs = pad_left - lv_obj_get_scroll_left(obj) + border_w;
int32_t y_ofs = pad_bottom - lv_obj_get_scroll_top(obj) + border_w;
lv_draw_rect_dsc_t col_dsc;
lv_draw_rect_dsc_init(&col_dsc);
col_dsc.base.layer = layer;
lv_obj_init_draw_rect_dsc(obj, LV_PART_ITEMS, &col_dsc);
col_dsc.bg_grad.dir = LV_GRAD_DIR_NONE;
col_dsc.bg_opa = LV_OPA_COVER;
lv_area_t clip_area_ori = layer->_clip_area;
/*Go through all points*/
lv_area_t bar_full_area;
for(i = 0; i < chart->point_cnt; i++) {
col_dsc.base.id2 = i;
col_dsc.base.id1 = 0;
/*Get the total bar height. All segments (series) will be drawn in the height*/
int32_t v_sum_all = 0;
LV_LL_READ(&chart->series_ll, ser) {
if(ser->hidden) continue;
int32_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0;
int32_t p_act = (start_point + i) % chart->point_cnt;
int32_t v_act = ser->y_points[p_act];
if(ser->y_points[p_act] == LV_CHART_POINT_NONE) continue;
/* Skip negative values in stacked charts. Negative values are not supported
* in stacked charts as they cannot be visually represented in the stacking logic. */
if(v_act <= 0) {
LV_LOG_WARN("Stacked chart doesn't support negative values.");
continue;
}
v_sum_all += v_act;
}
int32_t total_bar_height = value_to_y(obj, lv_ll_get_head(&chart->series_ll), v_sum_all, h);
if(total_bar_height <= 0) continue;
if(chart->point_cnt <= 1) {
bar_full_area.x1 = obj->coords.x1 + x_ofs;
}
else {
bar_full_area.x1 = (int32_t)((int32_t)(w - block_w) * i) / (chart->point_cnt - 1) + obj->coords.x1 + x_ofs;
}
bar_full_area.x2 = bar_full_area.x1 + block_w - 1;
/*No in the clip area yet*/
if(bar_full_area.x2 < clip_area_ori.x1) continue;
/*Out of the clip area already*/
if(bar_full_area.x1 > clip_area_ori.x2) break;
/*Draw the full_bar_area and set the clip area to clip the segments*/
bar_full_area.y2 = obj->coords.y2 + col_dsc.radius;
bar_full_area.y1 = obj->coords.y2 - y_ofs - total_bar_height + 1;
lv_area_t bar_clip_area = bar_full_area;
int32_t y_prev = obj->coords.y2 + 1;
/*Draw the current point of all data line*/
int32_t v_sum_act = 0;
LV_LL_READ(&chart->series_ll, ser) {
int32_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0;
col_dsc.bg_color = ser->color;
int32_t p_act = (start_point + i) % chart->point_cnt;
int32_t v_act = ser->y_points[p_act];
/*Can't show negative values on a stacked chart*/
if(ser->y_points[p_act] == LV_CHART_POINT_NONE ||
v_act <= 0 ||
ser->hidden) {
col_dsc.base.id1++;
continue;
}
v_sum_act += v_act; /*Use the summed value to get its `y` to avoid rounding errors*/
int32_t segment_y = value_to_y(obj, ser, v_sum_act, h);
bar_clip_area.y2 = y_prev - 1;
bar_clip_area.y1 = obj->coords.y2 - y_ofs - segment_y + 1;
y_prev = bar_clip_area.y1;
if(lv_area_intersect(&layer->_clip_area, &clip_area_ori, &bar_clip_area)) {
lv_draw_rect(layer, &col_dsc, &bar_full_area);
}
col_dsc.base.id1++;
}
}
layer->_clip_area = clip_area_ori;
}
static void draw_cursors(lv_obj_t * obj, lv_layer_t * layer)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
@@ -1352,7 +1510,7 @@ static uint32_t get_index_from_x(lv_obj_t * obj, int32_t x)
if(x < 0) return 0;
if(x > w) return chart->point_cnt - 1;
if(chart->type == LV_CHART_TYPE_LINE) return (x * (chart->point_cnt - 1) + w / 2) / w;
if(chart->type == LV_CHART_TYPE_BAR) return (x * chart->point_cnt) / w;
if(chart->type == LV_CHART_TYPE_BAR || chart->type == LV_CHART_TYPE_STACKED) return (x * chart->point_cnt) / w;
if(chart->type == LV_CHART_TYPE_SCATTER) {
/*For scatter charts, the nearest id could be different depending on the series. Just check the first series.*/
lv_chart_series_t * ser = lv_chart_get_series_next(obj, NULL);
@@ -1378,19 +1536,19 @@ static void invalidate_point(lv_obj_t * obj, uint32_t i)
lv_chart_t * chart = (lv_chart_t *)obj;
if(i >= chart->point_cnt) return;
int32_t w = lv_obj_get_content_width(obj);
int32_t scroll_left = lv_obj_get_scroll_left(obj);
/*In shift mode the whole chart changes so the whole object*/
if(chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT) {
lv_obj_invalidate(obj);
return;
}
int32_t w = lv_obj_get_content_width(obj);
int32_t scroll_left = lv_obj_get_scroll_left(obj);
int32_t bwidth = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
int32_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
int32_t x_ofs = obj->coords.x1 + pleft + bwidth - scroll_left;
if(chart->type == LV_CHART_TYPE_LINE) {
int32_t bwidth = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
int32_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
int32_t x_ofs = obj->coords.x1 + pleft + bwidth - scroll_left;
int32_t line_width = lv_obj_get_style_line_width(obj, LV_PART_ITEMS);
int32_t point_w = lv_obj_get_style_width(obj, LV_PART_INDICATOR);
@@ -1399,6 +1557,7 @@ static void invalidate_point(lv_obj_t * obj, uint32_t i)
coords.y1 -= line_width + point_w;
coords.y2 += line_width + point_w;
/*Invalidate the area between the previous and the next points*/
if(i < chart->point_cnt - 1) {
coords.x1 = ((w * i) / (chart->point_cnt - 1)) + x_ofs - line_width - point_w;
coords.x2 = ((w * (i + 1)) / (chart->point_cnt - 1)) + x_ofs + line_width + point_w;
@@ -1411,21 +1570,23 @@ static void invalidate_point(lv_obj_t * obj, uint32_t i)
lv_obj_invalidate_area(obj, &coords);
}
}
else if(chart->type == LV_CHART_TYPE_BAR) {
else if(chart->type == LV_CHART_TYPE_BAR || chart->type == LV_CHART_TYPE_STACKED) {
lv_area_t col_a;
/*Gap between the column on ~adjacent X*/
int32_t block_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN);
int32_t block_w = (w - ((chart->point_cnt - 1) * block_gap)) / chart->point_cnt;
int32_t block_w = (w + block_gap) / chart->point_cnt;
int32_t bwidth = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
int32_t x_act;
x_act = (int32_t)((int32_t)(block_w) * i) ;
x_act += obj->coords.x1 + bwidth + lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
if(chart->point_cnt > 1) {
x_act = (int32_t)((int32_t)(w - block_w) * i) / (chart->point_cnt - 1);
}
else {
x_act = 0;
}
lv_obj_get_coords(obj, &col_a);
col_a.x1 = x_act - scroll_left;
col_a.x2 = col_a.x1 + block_w;
col_a.x1 = x_act + x_ofs;
col_a.x2 = col_a.x1 + block_w + block_gap;
col_a.x1 -= block_gap;
lv_obj_invalidate_area(obj, &col_a);
@@ -1481,4 +1642,19 @@ static void new_points_alloc(lv_obj_t * obj, lv_chart_series_t * ser, uint32_t c
}
}
/**
* Map a value to a height
* @param obj pointer to a chart
* @param ser pointer to the series
* @param v the value to map
* @param h the height to which the value needs to be mapped
* @return the mapped y-coordinate value corresponding to the input value
*/
static int32_t value_to_y(lv_obj_t * obj, lv_chart_series_t * ser, int32_t v, int32_t h)
{
lv_chart_t * chart = (lv_chart_t *) obj;
return lv_map(v, chart->ymin[ser->y_axis_sec], chart->ymax[ser->y_axis_sec], 0, h);
}
#endif
+2 -1
View File
@@ -36,7 +36,8 @@ LV_EXPORT_CONST_INT(LV_CHART_POINT_NONE);
typedef enum {
LV_CHART_TYPE_NONE, /**< Don't draw the series*/
LV_CHART_TYPE_LINE, /**< Connect the points with lines*/
LV_CHART_TYPE_BAR, /**< Draw columns*/
LV_CHART_TYPE_BAR, /**< Draw bars for each series*/
LV_CHART_TYPE_STACKED, /**< Draw a single stacked bar for each data point. Supports only positive values*/
LV_CHART_TYPE_SCATTER, /**< Draw points and lines in 2D (x,y coordinates)*/
} lv_chart_type_t;
+1 -1
View File
@@ -63,7 +63,7 @@ struct _lv_chart_t {
uint32_t hdiv_cnt; /**< Number of horizontal division lines */
uint32_t vdiv_cnt; /**< Number of vertical division lines */
uint32_t point_cnt; /**< Number of points in all series */
lv_chart_type_t type : 3; /**< Chart type */
lv_chart_type_t type : 4; /**< Chart type */
lv_chart_update_mode_t update_mode : 2;
};