From 5b9329fd5d9976c780779003a5193724572e5212 Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Mon, 24 May 2021 15:37:27 +0200 Subject: [PATCH] fix(chart) various scatter chart related fixes --- docs/widgets/extra/chart.md | 42 +++-- examples/widgets/chart/lv_example_chart_7.c | 25 +-- src/extra/widgets/chart/lv_chart.c | 164 ++++++++++---------- src/extra/widgets/chart/lv_chart.h | 17 +- 4 files changed, 143 insertions(+), 105 deletions(-) diff --git a/docs/widgets/extra/chart.md b/docs/widgets/extra/chart.md index 2504b3612..94223c949 100644 --- a/docs/widgets/extra/chart.md +++ b/docs/widgets/extra/chart.md @@ -27,26 +27,37 @@ Charts also support: ## Usage -### Data series -You can add any number of series to the charts by `lv_chart_add_series(chart, color, LV_CHART_AXIS_PRIMARY/SECONDARY)`. -It allocates data for a `lv_chart_series_t` structure which contains the chosen `color` and an array for the data points. -`LV_CHART_AXIS_PRIMARY/SECONDARY` tells series should use the range of the `LV_CHART_AXIS_PRIMARY` (left) or `LV_CHART_AXIS_SECONDARY` (right) Y axis. - -With `lv_chart_set_ext_array(chart, ser, value_array)` makes the chart use an external array for the given series. -`value_array` should look like this: `lv_coord_t * value_array[num_points]`. The array size needs to be large enough to hold all the points of that series. -The array's pointer will be saved in the chart so it needs to be global, static or dynamically allocated. -Note: you should call `lv_chart_refresh(chart)` after the external data source has been updated, to update the chart. - -The value array of a series can be get by `lv_chart_get_array(chart, ser)`. It can be use with `ext_array` or *normal array*s. - -### Series' type +### Chart type The following data display types exist: - `LV_CHART_TYPE_NONE` Do not display any data. It can be used to hide the series. - `LV_CHART_TYPE_LINE` Draw lines between the data points and/or points (rectangles or circles) on the data points. - `LV_CHART_TYPE_BAR` - Draw bars. +- `LV_CHART_TYPE_SCATTER` - X/Y chart drawing point's and lines between the points. . -You can specify the display type with `lv_chart_set_type(chart, LV_CHART_TYPE_...)`. The types can be 'OR'ed (like `LV_CHART_TYPE_LINE`). +You can specify the display type with `lv_chart_set_type(chart, LV_CHART_TYPE_...)`. + + +### Data series +You can add any number of series to the charts by `lv_chart_add_series(chart, color, axis)`. +It allocates data for a `lv_chart_series_t` structure which contains the chosen `color` and an array for the data points. +`axis` can ha the following values: +- `LV_CHART_AXIS_PRIMARY_Y` Left axis +- `LV_CHART_AXIS_SECONDARY_Y` Right axis +- `LV_CHART_AXIS_PRIMARY_X` Bottom axis +- `LV_CHART_AXIS_SECONDARY_X` Top axis + + +`axis` tells which axis's range should be used te scale the values. + +With `lv_chart_set_ext_y_array(chart, ser, value_array)` makes the chart use an external array for the given series. +`value_array` should look like this: `lv_coord_t * value_array[num_points]`. The array size needs to be large enough to hold all the points of that series. +The array's pointer will be saved in the chart so it needs to be global, static or dynamically allocated. +Note: you should call `lv_chart_refresh(chart)` after the external data source has been updated, to update the chart. + +The value array of a series can be get by `lv_chart_get_y_array(chart, ser)`. It can be use with `ext_array` or *normal array*s. + +For `LV_CHART_TYPE_SCATTER` type `lv_chart_set_ext_x_array(chart, ser, value_array)` and `lv_chart_get_x_array(chart, ser)` can be used as well. ### Modify the data You have several options to set the data of series: @@ -57,6 +68,9 @@ You have several options to set the data of series: Use `LV_CHART_POINT_DEF` as value to make the library skip drawing that point, column, or line segment. +For `LV_CHART_TYPE_SCATTER` type `lv_chart_set_value_by_id2(chart, ser, id, value)` and `lv_chart_set_next_value2(chart, ser, x_valuem y_value)` can be used as well. + + ### Update modes `lv_chart_set_next_value` can behave in two ways depending on *update mode*: - `LV_CHART_UPDATE_MODE_SHIFT` Shift old data to the left and add the new one to the right. diff --git a/examples/widgets/chart/lv_example_chart_7.c b/examples/widgets/chart/lv_example_chart_7.c index 8f4a9a660..b65fc196e 100644 --- a/examples/widgets/chart/lv_example_chart_7.c +++ b/examples/widgets/chart/lv_example_chart_7.c @@ -6,23 +6,31 @@ static void draw_event_cb(lv_event_t * e) lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e); if(dsc->part == LV_PART_ITEMS) { lv_obj_t * obj = lv_event_get_target(e); + lv_chart_series_t * ser = lv_chart_get_series_next(obj, NULL); uint32_t cnt = lv_chart_get_point_count(obj); /*Make older value more transparent*/ dsc->rect_dsc->bg_opa = (LV_OPA_COVER * dsc->id) / (cnt - 1); /*Make smaller values blue, higher values red*/ + lv_coord_t * x_array = lv_chart_get_x_array(obj, ser); + lv_coord_t * y_array = lv_chart_get_y_array(obj, ser); + /*dsc->id is the tells drawing order, but we need the ID of the point being drawn.*/ + uint32_t start_point = lv_chart_get_x_start_point(obj, ser); + uint32_t p_act = (start_point + dsc->id) % cnt; /*Consider start point to get the index of the array*/ + lv_opa_t x_opa = (x_array[p_act] * LV_OPA_50) / 200; + lv_opa_t y_opa = (y_array[p_act] * LV_OPA_50) / 1000; + dsc->rect_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_RED), lv_palette_main(LV_PALETTE_BLUE), - (dsc->value * LV_OPA_COVER) / 1000); + x_opa + y_opa); } } static void add_data(lv_timer_t * timer) { LV_UNUSED(timer); - static uint32_t cnt = 0; lv_obj_t * chart = timer->user_data; - lv_chart_set_next_value2(chart, lv_chart_get_series_next(chart, NULL), lv_rand(-100,100), lv_rand(0,1000)); + lv_chart_set_next_value2(chart, lv_chart_get_series_next(chart, NULL), lv_rand(0,200), lv_rand(0,1000)); } /** @@ -32,25 +40,24 @@ void lv_example_chart_7(void) { lv_obj_t * chart = lv_chart_create(lv_scr_act()); lv_obj_set_size(chart, 200, 150); - lv_obj_align(chart, LV_ALIGN_CENTER, 0, -0); + lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0); lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL); - lv_obj_set_style_line_width(chart, 0, LV_PART_ITEMS); + lv_obj_set_style_line_width(chart, 0, LV_PART_ITEMS); /*Remove the lines*/ lv_chart_set_type(chart, LV_CHART_TYPE_SCATTER); - lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 10, 5, 10, 1, true, 30); + lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 5, 5, 5, 1, true, 30); lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 5, true, 50); - lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, -100, 100); + lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 0, 200); lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 1000); - lv_chart_set_point_count(chart, 50); lv_chart_series_t * ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y); uint32_t i; for(i = 0; i < 50; i++) { - lv_chart_set_next_value2(chart, ser, lv_rand(-100,100), lv_rand(0,1000)); + lv_chart_set_next_value2(chart, ser, lv_rand(0, 200), lv_rand(0, 1000)); } lv_timer_create(add_data, 100, chart); diff --git a/src/extra/widgets/chart/lv_chart.c b/src/extra/widgets/chart/lv_chart.c index 7677da199..fe38c04b5 100644 --- a/src/extra/widgets/chart/lv_chart.c +++ b/src/extra/widgets/chart/lv_chart.c @@ -111,7 +111,7 @@ void lv_chart_set_point_count(lv_obj_t * obj, uint16_t cnt) _LV_LL_READ_BACK(&chart->series_ll, ser) { if(!ser->x_ext_buf_assigned) new_points_alloc(obj, ser, cnt, &ser->x_points); if(!ser->y_ext_buf_assigned) new_points_alloc(obj, ser, cnt, &ser->y_points); - ser->last_point = 0; + ser->start_point = 0; } chart->point_cnt = cnt; @@ -253,7 +253,7 @@ uint16_t lv_chart_get_x_start_point(const lv_obj_t * obj, lv_chart_series_t * se LV_UNUSED(obj); LV_ASSERT_NULL(ser); - return ser->last_point; + return ser->start_point; } void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_point_t * p_out) @@ -270,11 +270,15 @@ void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint1 } lv_coord_t w = (lv_obj_get_content_width(obj) * chart->zoom_x) >> 8; + lv_coord_t h = (lv_obj_get_content_height(obj) * chart->zoom_y) >> 8; - if(chart->type & LV_CHART_TYPE_LINE) { + if(chart->type == LV_CHART_TYPE_LINE) { p_out->x = (w * id) / (chart->point_cnt - 1); } - else if(chart->type & LV_CHART_TYPE_BAR) { + 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); + } + else if(chart->type == LV_CHART_TYPE_BAR) { uint32_t ser_cnt = _lv_ll_get_len(&chart->series_ll); int32_t ser_gap = (lv_obj_get_style_pad_column(obj, LV_PART_ITEMS) * chart->zoom_x) >> 8; /*Gap between the column on the ~same X*/ int32_t block_gap = (lv_obj_get_style_pad_column(obj, LV_PART_MAIN) * chart->zoom_x) >> 8; /*Gap between the column on ~adjacent X*/ @@ -292,15 +296,14 @@ void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint1 p_out->x += (col_w - ser_gap) / 2; } - p_out->x += lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN); + p_out->x += lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width; p_out->x -= lv_obj_get_scroll_left(obj); - lv_coord_t h = (lv_obj_get_content_height(obj) * chart->zoom_y) >> 8; - p_out->y = (int32_t)((int32_t)ser->y_points[id] - chart->ymin[ser->y_axis_sec]) * h; p_out->y = p_out->y / (chart->ymax[ser->y_axis_sec] - chart->ymin[ser->y_axis_sec]); p_out->y = h - p_out->y; - p_out->y += lv_obj_get_style_pad_top(obj, LV_PART_MAIN); + p_out->y += lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width; } @@ -342,7 +345,7 @@ lv_chart_series_t * lv_chart_add_series(lv_obj_t * obj, lv_color_t color, lv_cha return NULL; } - ser->last_point = 0; + ser->start_point = 0; ser->y_ext_buf_assigned = false; ser->hidden = 0; ser->x_axis_sec = axis & LV_CHART_AXIS_SECONDARY_X ? 1 : 0; @@ -398,7 +401,7 @@ void lv_chart_set_x_start_point(lv_obj_t * obj, lv_chart_series_t * ser, uint16_ lv_chart_t * chart = (lv_chart_t *)obj; if(id >= chart->point_cnt) return; - ser->last_point = id; + ser->start_point = id; } lv_chart_series_t * lv_chart_get_series_next(const lv_obj_t * obj, const lv_chart_series_t * ser) @@ -507,7 +510,7 @@ void lv_chart_set_all_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t for(i = 0; i < chart->point_cnt; i++) { ser->y_points[i] = value; } - ser->last_point = 0; + ser->start_point = 0; lv_chart_refresh(obj); } @@ -517,24 +520,10 @@ void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t LV_ASSERT_NULL(ser); lv_chart_t * chart = (lv_chart_t *)obj; - if(chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT) { - ser->y_points[ser->last_point] = - value; /*This was the place of the former left most value, after shifting it is the rightmost*/ - ser->last_point = (ser->last_point + 1) % chart->point_cnt; - lv_chart_refresh(obj); - } - else if(chart->update_mode == LV_CHART_UPDATE_MODE_CIRCULAR) { - ser->y_points[ser->last_point] = value; - - invalidate_point(obj, ser->last_point); - - uint32_t next_point = (ser->last_point + 1) % chart->point_cnt; /*update the x for next incoming y*/ - ser->last_point = next_point; - invalidate_point(obj, next_point); - - lv_obj_invalidate(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, lv_coord_t x_value, lv_coord_t y_value) @@ -549,29 +538,16 @@ void lv_chart_set_next_value2(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_ return; } - if(chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT) { - /*This was the place of the former left most value, after shifting it is the rightmost*/ - ser->x_points[ser->last_point] = x_value; - ser->y_points[ser->last_point] = y_value; - ser->last_point = (ser->last_point + 1) % chart->point_cnt; - lv_chart_refresh(obj); - } - else if(chart->update_mode == LV_CHART_UPDATE_MODE_CIRCULAR) { - ser->x_points[ser->last_point] = x_value; - ser->y_points[ser->last_point] = y_value; + ser->x_points[ser->start_point] = x_value; + ser->y_points[ser->start_point] = y_value; + invalidate_point(obj, ser->start_point); + ser->start_point = (ser->start_point + 1) % chart->point_cnt; + invalidate_point(obj, ser->start_point); + lv_chart_refresh(obj); - invalidate_point(obj, ser->last_point); - - uint32_t next_point = (ser->last_point + 1) % chart->point_cnt; /*update the x for next incoming y*/ - ser->last_point = next_point; - invalidate_point(obj, next_point); - - lv_obj_invalidate(obj); - - } } -void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value, uint16_t id) +void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t value) { LV_ASSERT_OBJ(obj, MY_CLASS); LV_ASSERT_NULL(ser); @@ -883,7 +859,7 @@ static void draw_series_line(lv_obj_t * obj, const lv_area_t * clip_area) line_dsc_default.color = ser->color; point_dsc_default.bg_color = ser->color; - lv_coord_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->last_point : 0; + lv_coord_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0; p1.x = x_ofs; p2.x = x_ofs; @@ -1006,12 +982,13 @@ static void draw_series_scatter(lv_obj_t * obj, const lv_area_t * clip_area) uint16_t i; lv_point_t p1; lv_point_t p2; + lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN); lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); lv_coord_t w = (lv_obj_get_content_width(obj) * chart->zoom_x) >> 8; lv_coord_t h = (lv_obj_get_content_height(obj) * chart->zoom_y) >> 8; - lv_coord_t x_ofs = obj->coords.x1 + pad_left - lv_obj_get_scroll_left(obj); - lv_coord_t y_ofs = obj->coords.y1 + pad_top - lv_obj_get_scroll_top(obj); + lv_coord_t x_ofs = obj->coords.x1 + pad_left + border_width - lv_obj_get_scroll_left(obj); + lv_coord_t y_ofs = obj->coords.y1 + pad_top + border_width - lv_obj_get_scroll_top(obj); lv_chart_series_t * ser; lv_area_t series_mask; @@ -1039,7 +1016,7 @@ static void draw_series_scatter(lv_obj_t * obj, const lv_area_t * clip_area) line_dsc_default.color = ser->color; point_dsc_default.bg_color = ser->color; - lv_coord_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->last_point : 0; + lv_coord_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0; p1.x = x_ofs; p2.x = x_ofs; @@ -1182,7 +1159,7 @@ static void draw_series_bar(lv_obj_t * obj, const lv_area_t * clip_area) /*Draw the current point of all data line*/ _LV_LL_READ_BACK(&chart->series_ll, ser) { if (ser->hidden) continue; - lv_coord_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->last_point : 0; + lv_coord_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0; col_a.x1 = x_act; col_a.x2 = col_a.x1 + col_w - ser_gap - 1; @@ -1264,7 +1241,7 @@ static void draw_cursors(lv_obj_t * obj, const lv_area_t * clip_area) } else { if(cursor->point_id == LV_CHART_POINT_NONE) continue; lv_point_t p; - lv_chart_get_point_pos_by_id(obj, lv_chart_get_series_next(obj, NULL), cursor->point_id, &p); + lv_chart_get_point_pos_by_id(obj, cursor->ser, cursor->point_id, &p); cx = p.x; cy = p.y; } @@ -1321,14 +1298,17 @@ static void draw_y_ticks(lv_obj_t * obj, const lv_area_t * clip_area, lv_chart_a if(t->major_cnt <= 1) return; if(!t->label_en && !t->major_len && !t->minor_len) return; + uint8_t sec_axis = axis == LV_CHART_AXIS_PRIMARY_Y ? 0 : 1; + uint32_t i; lv_point_t p1; lv_point_t p2; + lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN); lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); lv_coord_t h = (lv_obj_get_content_height(obj) * chart->zoom_y) >> 8; - lv_coord_t y_ofs = obj->coords.y1 + pad_top - lv_obj_get_scroll_top(obj); + lv_coord_t y_ofs = obj->coords.y1 + pad_top + border_width - lv_obj_get_scroll_top(obj); lv_coord_t label_gap; lv_coord_t x_ofs; @@ -1348,7 +1328,6 @@ static void draw_y_ticks(lv_obj_t * obj, const lv_area_t * clip_area, lv_chart_a minor_len *= -1; } - lv_draw_line_dsc_t line_dsc; lv_draw_line_dsc_init(&line_dsc); lv_obj_init_draw_line_dsc(obj, LV_PART_TICKS, &line_dsc); @@ -1391,7 +1370,7 @@ static void draw_y_ticks(lv_obj_t * obj, const lv_area_t * clip_area, lv_chart_a /*add text only to major tick*/ if(major && t->label_en) { - int32_t tick_value = lv_map(total_tick_num - i, 0, total_tick_num, chart->ymin[axis], chart->ymax[axis]); + int32_t tick_value = lv_map(total_tick_num - i, 0, total_tick_num, chart->ymin[sec_axis], chart->ymax[sec_axis]); lv_snprintf(dsc.text, sizeof(dsc.text), "%d", tick_value); dsc.value = tick_value; lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &dsc); @@ -1405,7 +1384,7 @@ static void draw_y_ticks(lv_obj_t * obj, const lv_area_t * clip_area, lv_chart_a a.y1 = p2.y - size.y / 2; a.y2 = p2.y + size.y / 2; - if(axis == LV_CHART_AXIS_PRIMARY_Y) { + if(!sec_axis) { a.x1 = p2.x - size.x - label_gap; a.x2 = p2.x - label_gap; } @@ -1424,12 +1403,12 @@ static void draw_y_ticks(lv_obj_t * obj, const lv_area_t * clip_area, lv_chart_a } } -static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area) +static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area, lv_chart_axis_t axis) { lv_chart_t * chart = (lv_chart_t *)obj; - lv_chart_tick_dsc_t * t = &chart->tick[LV_CHART_AXIS_PRIMARY_X]; + lv_chart_tick_dsc_t * t = &chart->tick[axis]; if(t->major_cnt <= 1) return; if(!t->label_en && !t->major_len && !t->minor_len) return; @@ -1437,20 +1416,29 @@ static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area) lv_point_t p1; lv_point_t p2; - lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + lv_obj_get_style_border_width(obj, LV_PART_MAIN); lv_coord_t w = (lv_obj_get_content_width(obj) * chart->zoom_x) >> 8; - lv_coord_t x_ofs = obj->coords.x1 + pad_left - lv_obj_get_scroll_left(obj); - lv_coord_t y_ofs = obj->coords.y2; lv_draw_label_dsc_t label_dsc; lv_draw_label_dsc_init(&label_dsc); lv_obj_init_draw_label_dsc(obj, LV_PART_TICKS, &label_dsc); - lv_coord_t label_gap = t->label_en ? lv_obj_get_style_pad_bottom(obj, LV_PART_TICKS) : 0; + lv_coord_t x_ofs = obj->coords.x1 + pad_left - lv_obj_get_scroll_left(obj); + lv_coord_t y_ofs; + lv_coord_t label_gap; + if(axis == LV_CHART_AXIS_PRIMARY_X) { + label_gap = t->label_en ? lv_obj_get_style_pad_bottom(obj, LV_PART_TICKS) : 0; + y_ofs = obj->coords.y2; + } else { + label_gap = t->label_en ? lv_obj_get_style_pad_top(obj, LV_PART_TICKS) : 0; + y_ofs = obj->coords.y1; + } - if(y_ofs > clip_area->y2) return; - if(y_ofs + label_gap + label_dsc.font->line_height + t->major_len < clip_area->y1) return; + if(axis == LV_CHART_AXIS_PRIMARY_X) { + if(y_ofs > clip_area->y2) return; + if(y_ofs + label_gap + label_dsc.font->line_height + t->major_len < clip_area->y1) return; + } lv_draw_line_dsc_t line_dsc; lv_draw_line_dsc_init(&line_dsc); @@ -1465,6 +1453,8 @@ static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area) dsc.label_dsc = &label_dsc; dsc.line_dsc = &line_dsc; + uint8_t sec_axis = axis == LV_CHART_AXIS_PRIMARY_X ? 0 : 1; + /*The columns ticks should be aligned to the center of blocks*/ if(chart->type == LV_CHART_TYPE_BAR) { int32_t block_gap = (lv_obj_get_style_pad_column(obj, LV_PART_MAIN) * chart->zoom_x) >> 8; /*Gap between the columns on ~adjacent X*/ @@ -1482,7 +1472,8 @@ static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area) /*draw a line at moving x position*/ p2.x = p1.x = x_ofs + (int32_t)((int32_t)(w - line_dsc.width) * i) / total_tick_num; - p2.y = p1.y + (major ? t->major_len : t->minor_len); + if(sec_axis) p2.y = p1.y - (major ? t->major_len : t->minor_len); + else p2.y = p1.y + (major ? t->major_len : t->minor_len); if(p1.x + line_dsc.width / 2 >= obj->coords.x1 && p2.x - line_dsc.width / 2 <= obj->coords.x2) @@ -1493,8 +1484,14 @@ static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area) /*add text only to major tick*/ if(!major || !t->label_en) continue; - int32_t tick_value = i / t->minor_cnt; - lv_snprintf(dsc.text, sizeof(dsc.text), "%d", i / t->minor_cnt); + int32_t tick_value; + if(chart->type == LV_CHART_TYPE_SCATTER) { + tick_value = lv_map(i, 0, total_tick_num, chart->xmin[sec_axis], chart->xmax[sec_axis]); + } else { + tick_value = i / t->minor_cnt; + } + + lv_snprintf(dsc.text, sizeof(dsc.text), "%d", tick_value); dsc.value = tick_value; lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &dsc); @@ -1505,9 +1502,15 @@ static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area) /*set the area at some distance of the major tick len under of the tick*/ lv_area_t a; a.x1 = (p2.x - size.x / 2); - a.x2 = (p2.x + size.x / 2), - a.y1 = p2.y + label_gap; - a.y2 = (a.y1 + size.y); + a.x2 = (p2.x + size.x / 2); + if(sec_axis) { + a.y2 = p2.y - label_gap; + a.y1 = a.y2 - size.y; + } + else { + a.y1 = p2.y + label_gap; + a.y2 = a.y1 + size.y; + } if(a.x2 >= obj->coords.x1 && a.x1 <= obj->coords.x2) @@ -1522,7 +1525,8 @@ static void draw_axes(lv_obj_t * obj, const lv_area_t * mask) { draw_y_ticks(obj, mask, LV_CHART_AXIS_PRIMARY_Y); draw_y_ticks(obj, mask, LV_CHART_AXIS_SECONDARY_Y); - draw_x_ticks(obj, mask); + draw_x_ticks(obj, mask, LV_CHART_AXIS_PRIMARY_X); + draw_x_ticks(obj, mask, LV_CHART_AXIS_SECONDARY_X); } /** @@ -1590,7 +1594,11 @@ static void invalidate_point(lv_obj_t * obj, uint16_t i) col_a.x1 -= block_gap; lv_obj_invalidate_area(obj, &col_a); - } else { + } + else if(chart->type == LV_CHART_TYPE_SCATTER) { + lv_obj_invalidate(obj); + } + else { lv_obj_invalidate(obj); } } @@ -1603,7 +1611,7 @@ static void new_points_alloc(lv_obj_t * obj, lv_chart_series_t * ser, uint32_t c uint32_t point_cnt_old = chart->point_cnt; uint32_t i; - if(ser->last_point != 0) { + if(ser->start_point != 0) { lv_coord_t * new_points = lv_mem_alloc(sizeof(lv_coord_t) * cnt); LV_ASSERT_MALLOC(new_points); if(new_points == NULL) return; @@ -1611,7 +1619,7 @@ static void new_points_alloc(lv_obj_t * obj, lv_chart_series_t * ser, uint32_t c if(cnt >= point_cnt_old) { for(i = 0; i < point_cnt_old; i++) { new_points[i] = - (*a)[(i + ser->last_point) % point_cnt_old]; /*Copy old contents to new array*/ + (*a)[(i + ser->start_point) % point_cnt_old]; /*Copy old contents to new array*/ } for(i = point_cnt_old; i < cnt; i++) { new_points[i] = LV_CHART_POINT_NONE; /*Fill up the rest with default value*/ @@ -1620,7 +1628,7 @@ static void new_points_alloc(lv_obj_t * obj, lv_chart_series_t * ser, uint32_t c else { for(i = 0; i < cnt; i++) { new_points[i] = - (*a)[(i + ser->last_point) % point_cnt_old]; /*Copy old contents to new array*/ + (*a)[(i + ser->start_point) % point_cnt_old]; /*Copy old contents to new array*/ } } diff --git a/src/extra/widgets/chart/lv_chart.h b/src/extra/widgets/chart/lv_chart.h index 57af2b8b6..767ecc908 100644 --- a/src/extra/widgets/chart/lv_chart.h +++ b/src/extra/widgets/chart/lv_chart.h @@ -68,7 +68,7 @@ typedef struct { lv_coord_t * x_points; lv_coord_t * y_points; lv_color_t color; - uint16_t last_point; + uint16_t start_point; uint8_t hidden : 1; uint8_t x_ext_buf_assigned : 1; uint8_t y_ext_buf_assigned : 1; @@ -360,21 +360,30 @@ lv_point_t lv_chart_get_cursor_point(lv_obj_t * chart, lv_chart_cursor_t * curso void lv_chart_set_all_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value); /** - * Set the next point according to the update mode policy. + * Set the next point's Y value according to the update mode policy. * @param obj pointer to chart object * @param ser pointer to a data series on 'chart' * @param value the new value of the next data */ void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value); +/** + * Set the next point's X and Y value according to the update mode policy. + * @param obj pointer to chart object + * @param ser pointer to a data series on 'chart' + * @param x_value the new X value of the next data + * @param y_value the new Y value of the next data + */ +void lv_chart_set_next_value2(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t x_value, lv_coord_t y_value); + /** * Set an individual point's y value of a chart's series directly based on its index * @param obj pointer to a chart object * @param ser pointer to a data series on 'chart' - * @param value value to assign to array point * @param id the index of the x point in the array + * @param value value to assign to array point */ -void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value, uint16_t id); +void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t value); /** * Set an individual point's x and y value of a chart's series directly based on its index