feat(chart): add curved type (#9564)

Co-authored-by: André <andre_miguel_costa@hotmail.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
Gabor Kiss-Vamosi
2026-02-07 08:11:30 +01:00
committed by GitHub
parent c7a959757c
commit ade26a471b
12 changed files with 270 additions and 28 deletions
@@ -582,7 +582,7 @@ these fields:
:round_end: Rounds the line end. :round_end: Rounds the line end.
:raw_end: Set to 1 to skip end calculations if they are unnecessary. :raw_end: Set to 1 to skip end calculations if they are unnecessary.
+If a large amount of points needs to be rendered it's recommended to use ``points`` If a large amount of points needs to be rendered it's recommended to use ``points``
instead of ``p1`` and ``p2`` as it avoids creating many draw tasks. instead of ``p1`` and ``p2`` as it avoids creating many draw tasks.
Functions for line drawing: Functions for line drawing:
+7 -2
View File
@@ -60,11 +60,12 @@ Point count (number of data points in all data series added to the chart)
- default 10 - default 10
- If you provide your own data-value arrays, each array so provided must contain - If you provide your own data-value arrays, each array so provided must contain
at least this number of values. at least this number of values.
- For LINE-, BAR-, STACKED-charts, this is the number of points on the X axis. - For LINE-, CURVE-, 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. - LINE-, CURVE-, 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. - 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. - SCATTER charts have separate data-value arrays for both X-values and Y-values.
Any number of data series Any number of data series
- After a chart is created, it initially contains no data series. You have to add them. - After a chart is created, it initially contains no data series. You have to add them.
@@ -157,6 +158,10 @@ A chart can be one of the following types:
can also be illustrated if their ``width``, ``height``, ``bg_color`` and ``radius`` 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 styles (for :cpp:enumerator:`LV_PART_ITEMS`) are set and both ``width`` and
``height`` have non-zero values. ``height`` have non-zero values.
- :cpp:enumerator:`LV_CHART_TYPE_CURVE`: Similar to the LINE type, but it draws Bezier curves
between data points. ``LV_USE_VECTOR_GRAPHICS`` and a draw unit (e.g. VGLite, or ThorVG for
software rendering) need to be enabled. It also supports the ``line_dash_gap/width`` style
properties.
- :cpp:enumerator:`LV_CHART_TYPE_BAR`: Draw individual vertical bars for each point in each series. - :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 - :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. are displayed as segments within a single bar for each data point. Supports only positive values.
+20 -13
View File
@@ -58,6 +58,7 @@ void LV_ATTRIBUTE_FAST_MEM lv_draw_line(lv_layer_t * layer, const lv_draw_line_d
LV_PROFILER_DRAW_BEGIN; LV_PROFILER_DRAW_BEGIN;
lv_area_t a; lv_area_t a;
lv_point_precise_t * new_points = NULL;
if(dsc->points == NULL) { if(dsc->points == NULL) {
a.x1 = (int32_t)LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width; a.x1 = (int32_t)LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width;
a.x2 = (int32_t)LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width; a.x2 = (int32_t)LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width;
@@ -76,21 +77,33 @@ void LV_ATTRIBUTE_FAST_MEM lv_draw_line(lv_layer_t * layer, const lv_draw_line_d
a.x2 = LV_COORD_MIN; a.x2 = LV_COORD_MIN;
a.y2 = LV_COORD_MIN; a.y2 = LV_COORD_MIN;
const size_t array_size = dsc->point_cnt * sizeof(lv_point_precise_t);
new_points = lv_malloc(array_size);
LV_ASSERT_MALLOC(new_points);
if(!new_points) {
LV_LOG_WARN("Couldn't allocate %" LV_PRId32 "points", dsc->point_cnt);
LV_PROFILER_DRAW_END;
return;
}
int32_t i; int32_t i;
for(i = 0; i < dsc->point_cnt; i++) { for(i = 0; i < dsc->point_cnt; i++) {
new_points[i] = dsc->points[i];
if(dsc->points[i].x == LV_DRAW_LINE_POINT_NONE || if(dsc->points[i].x == LV_DRAW_LINE_POINT_NONE ||
dsc->points[i].y == LV_DRAW_LINE_POINT_NONE) { dsc->points[i].y == LV_DRAW_LINE_POINT_NONE) {
continue; continue;
} }
a.x1 = (int32_t)LV_MIN(a.x1, dsc->points[i].x); a.x1 = (int32_t)LV_MIN(a.x1, dsc->points[i].x) - dsc->width;
a.x2 = (int32_t)LV_MAX(a.x2, dsc->points[i].x); a.x2 = (int32_t)LV_MAX(a.x2, dsc->points[i].x) + dsc->width;
a.y1 = (int32_t)LV_MIN(a.y1, dsc->points[i].y); a.y1 = (int32_t)LV_MIN(a.y1, dsc->points[i].y) - dsc->width;
a.y2 = (int32_t)LV_MAX(a.y2, dsc->points[i].y); a.y2 = (int32_t)LV_MAX(a.y2, dsc->points[i].y) + dsc->width;
} }
if(a.x1 == LV_COORD_MAX) { if(a.x1 == LV_COORD_MAX) {
lv_free(new_points);
LV_LOG_INFO("No valid point was found. Not adding the draw task."); LV_LOG_INFO("No valid point was found. Not adding the draw task.");
LV_PROFILER_DRAW_END;
return; return;
} }
lv_area_increase(&a, dsc->width, dsc->width); lv_area_increase(&a, dsc->width, dsc->width);
@@ -106,15 +119,9 @@ void LV_ATTRIBUTE_FAST_MEM lv_draw_line(lv_layer_t * layer, const lv_draw_line_d
} }
lv_draw_task_t * t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_LINE); lv_draw_task_t * t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_LINE);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc)); lv_draw_line_dsc_t * line_draw_dsc = t->draw_dsc;
lv_memcpy(line_draw_dsc, dsc, sizeof(*dsc));
if(dsc->points) { line_draw_dsc->points = new_points;
lv_draw_line_dsc_t * new_draw_dsc = t->draw_dsc;
size_t array_size = dsc->point_cnt * sizeof(lv_point_precise_t);
lv_point_precise_t * new_points = lv_malloc(array_size);
lv_memcpy(new_points, dsc->points, array_size);
new_draw_dsc->points = new_points;
}
lv_draw_finalize_task_creation(layer, t); lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END; LV_PROFILER_DRAW_END;
+5 -4
View File
@@ -338,16 +338,17 @@ static void LV_ATTRIBUTE_FAST_MEM draw_line_skew(lv_draw_task_t * t, const lv_dr
masks[3] = &mask_bottom_param; masks[3] = &mask_bottom_param;
} }
/*The real draw area is around the line.
*It's easy to calculate with steep lines, but the area can be very wide with very flat lines.
*So deal with it only with steep lines.*/
int32_t draw_area_w = lv_area_get_width(&blend_area);
/*Draw the background line by line*/ /*Draw the background line by line*/
int32_t h; int32_t h;
size_t mask_buf_size = LV_MIN((int32_t)lv_area_get_size(&blend_area), lv_area_get_width(&blend_area)); size_t mask_buf_size = LV_MIN((int32_t)lv_area_get_size(&blend_area), lv_area_get_width(&blend_area));
lv_opa_t * mask_buf = lv_malloc(mask_buf_size); lv_opa_t * mask_buf = lv_malloc(mask_buf_size);
/*The real draw area is around the line.
*It's easy to calculate with steep lines, but the area can be very wide with very flat lines.
*So deal with it only with steep lines.*/
size_t draw_area_w = LV_MIN((size_t)lv_area_get_width(&blend_area), mask_buf_size);
int32_t y2 = blend_area.y2; int32_t y2 = blend_area.y2;
blend_area.y2 = blend_area.y1; blend_area.y2 = blend_area.y1;
+180 -8
View File
@@ -7,13 +7,14 @@
* INCLUDES * INCLUDES
*********************/ *********************/
#include "lv_chart_private.h" #include "lv_chart_private.h"
#if LV_USE_CHART != 0
#include "../../misc/lv_area_private.h" #include "../../misc/lv_area_private.h"
#include "../../draw/lv_draw_private.h" #include "../../draw/lv_draw_private.h"
#include "../../draw/lv_draw_vector_private.h"
#include "../../core/lv_obj_private.h" #include "../../core/lv_obj_private.h"
#include "../../core/lv_obj_class_private.h" #include "../../core/lv_obj_class_private.h"
#include "../../core/lv_obj_draw_private.h" #include "../../core/lv_obj_draw_private.h"
#if LV_USE_CHART != 0
#include "../../misc/lv_assert.h" #include "../../misc/lv_assert.h"
/********************* /*********************
@@ -39,6 +40,7 @@ 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_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_line(lv_obj_t * obj, lv_layer_t * layer);
static void draw_series_curve(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_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_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_series_scatter(lv_obj_t * obj, lv_layer_t * layer);
@@ -341,7 +343,7 @@ void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint3
int32_t w = lv_obj_get_content_width(obj); int32_t w = lv_obj_get_content_width(obj);
int32_t h = lv_obj_get_content_height(obj); int32_t h = lv_obj_get_content_height(obj);
if(chart->type == LV_CHART_TYPE_LINE) { if(chart->type == LV_CHART_TYPE_LINE || chart->type == LV_CHART_TYPE_CURVE) {
if(chart->point_cnt > 1) { if(chart->point_cnt > 1) {
p_out->x = (w * id) / (chart->point_cnt - 1); p_out->x = (w * id) / (chart->point_cnt - 1);
} }
@@ -933,6 +935,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(lv_ll_is_empty(&chart->series_ll) == false) {
if(chart->type == LV_CHART_TYPE_LINE) draw_series_line(obj, layer); if(chart->type == LV_CHART_TYPE_LINE) draw_series_line(obj, layer);
else if(chart->type == LV_CHART_TYPE_CURVE) draw_series_curve(obj, layer);
else if(chart->type == LV_CHART_TYPE_BAR) draw_series_bar(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_STACKED) draw_series_stacked(obj, layer);
else if(chart->type == LV_CHART_TYPE_SCATTER) draw_series_scatter(obj, layer); else if(chart->type == LV_CHART_TYPE_SCATTER) draw_series_scatter(obj, layer);
@@ -1041,7 +1044,9 @@ static void draw_series_line(lv_obj_t * obj, lv_layer_t * layer)
int32_t bullet_w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2; int32_t bullet_w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
int32_t bullet_h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2; int32_t bullet_h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
int32_t extra_space_x = w / (chart->point_cnt - 1) + bullet_w + line_dsc.width; int32_t extra_space_x;
if(chart->point_cnt <= 1) extra_space_x = 0;
else extra_space_x = w / (chart->point_cnt - 1) + bullet_w + line_dsc.width;
lv_draw_rect_dsc_t point_draw_dsc; lv_draw_rect_dsc_t point_draw_dsc;
if(crowded_mode == false) { if(crowded_mode == false) {
@@ -1075,8 +1080,6 @@ static void draw_series_line(lv_obj_t * obj, lv_layer_t * layer)
} }
line_dsc.color = ser->color; line_dsc.color = ser->color;
line_dsc.base.drop_shadow_color = ser->color; line_dsc.base.drop_shadow_color = ser->color;
point_draw_dsc.bg_color = ser->color;
line_dsc.base.id2 = 0;
int32_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0; int32_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0;
int32_t p_act = start_point; int32_t p_act = start_point;
@@ -1174,6 +1177,174 @@ static void draw_series_line(lv_obj_t * obj, lv_layer_t * layer)
if(points) lv_free(points); if(points) lv_free(points);
} }
static void draw_series_curve(lv_obj_t * obj, lv_layer_t * layer)
{
#if LV_USE_VECTOR_GRAPHIC
lv_chart_t * chart = (lv_chart_t *)obj;
if(chart->point_cnt < 2) return;
int32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
int32_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
int32_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
int32_t w = lv_obj_get_content_width(obj);
int32_t h = lv_obj_get_content_height(obj);
int32_t x_ofs = obj->coords.x1 + pad_left - lv_obj_get_scroll_left(obj);
int32_t y_ofs = obj->coords.y1 + pad_top - lv_obj_get_scroll_top(obj);
lv_chart_series_t * ser;
lv_draw_rect_dsc_t point_dsc_default;
lv_draw_rect_dsc_init(&point_dsc_default);
point_dsc_default.base.layer = layer;
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &point_dsc_default);
int32_t point_w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
int32_t point_h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
uint32_t ser_cnt = lv_ll_get_len(&chart->series_ll);
if(ser_cnt == 0) {
return;
}
float dashes[2];
dashes[0] = lv_obj_get_style_line_dash_width(obj, LV_PART_ITEMS);
dashes[1] = lv_obj_get_style_line_dash_gap(obj, LV_PART_ITEMS);
lv_draw_vector_dsc_t * dsc = lv_draw_vector_dsc_create(layer);
if(dsc == NULL) {
LV_LOG_WARN("Couldn't allocate vector dsc");
return;
}
lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);
dsc->base.id1 = ser_cnt - 1;
point_dsc_default.base.id1 = dsc->base.id1;
/*Go through all data lines (series)*/
LV_LL_READ_BACK(&chart->series_ll, ser) {
if(ser->hidden) {
if(dsc->base.id1 > 0) {
dsc->base.id1--;
point_dsc_default.base.id1--;
}
continue;
}
lv_vector_path_clear(path);
lv_draw_vector_dsc_set_fill_opa(dsc, 0);
lv_draw_vector_dsc_set_stroke_color(dsc, ser->color);
lv_draw_vector_dsc_set_stroke_opa(dsc, LV_OPA_COVER);
lv_draw_vector_dsc_set_stroke_width(dsc, 2.0f);
if(dashes[0]) lv_draw_vector_dsc_set_stroke_dash(dsc, dashes, 2);
point_dsc_default.bg_color = ser->color;
dsc->base.id2 = 0;
point_dsc_default.base.id2 = 0;
int32_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0;
/*The X distance between points.
*Just a rough calculation to know the extra area of interest around the chart*/
int32_t max_dx = w / (chart->point_cnt - 1) + 1;
lv_fpoint_t scaled_points[3];
int32_t raw_points[3];
int32_t s_prev = 0; /*Previous steepness around N-1 (y_diff of N-2 and N) */
int32_t s_act = 0; /*Steepness around N (y_diff of N-1 and N+1)*/
int32_t min_v = chart->ymin[ser->y_axis_sec];
int32_t max_v = chart->ymax[ser->y_axis_sec];
int32_t i;
int32_t valid_point_cnt = 0;
int32_t point_cnt = chart->point_cnt;
for(i = 0; i <= point_cnt; i++) {
int32_t p_x = ((w * i) / (point_cnt - 1)) + x_ofs;
if(p_x > layer->_clip_area.x2 + 2 * max_dx + point_w + 1) break;
if(p_x < layer->_clip_area.x1 - 2 * max_dx - point_w - 1) {
continue;
}
/*We need 3 points to draw the curves (N-1, N, N+1)*/
scaled_points[0] = scaled_points[1];
scaled_points[1] = scaled_points[2];
raw_points[0] = raw_points[1];
raw_points[1] = raw_points[2];
int32_t p_next = (start_point + i) % point_cnt;
raw_points[2] = ser->y_points[p_next];
if(i > point_cnt - 1) {
s_act = 0;
}
else {
if(raw_points[2] == LV_CHART_POINT_NONE) {
s_act = s_prev;
}
else {
scaled_points[2].x = p_x;
scaled_points[2].y = (int32_t)lv_map(ser->y_points[p_next], min_v, max_v, y_ofs + h, y_ofs);
if(i == 0) {
scaled_points[0] = scaled_points[2];
scaled_points[1] = scaled_points[2];
}
if((scaled_points[2].y >= scaled_points[1].y && scaled_points[1].y >= scaled_points[0].y) ||
(scaled_points[2].y <= scaled_points[1].y && scaled_points[1].y <= scaled_points[0].y)) {
s_act = (int32_t)(scaled_points[2].y - scaled_points[0].y) / 2;
}
else {
s_act = 0;
}
}
}
if(valid_point_cnt >= 2) {
if(raw_points[0] != LV_CHART_POINT_NONE && raw_points[1] != LV_CHART_POINT_NONE) {
lv_vector_path_move_to(path, &scaled_points[0]);
dsc->base.id2 = i;
/*Average slope*/
int32_t dx = (int32_t)(scaled_points[1].x - scaled_points[0].x);
lv_fpoint_t c1 = {scaled_points[0].x + dx / 3, scaled_points[0].y + s_prev / 3};
lv_fpoint_t c2 = {scaled_points[1].x - dx / 3, scaled_points[1].y - s_act / 3};
lv_vector_path_cubic_to(path, &c1, &c2, &scaled_points[1]);
}
else {
s_act = 0;
}
}
s_prev = s_act;
if(point_w && point_h && ser->y_points[p_next] != LV_CHART_POINT_NONE) {
lv_area_t point_area;
point_area.x1 = (int32_t)scaled_points[2].x - point_w;
point_area.x2 = (int32_t)scaled_points[2].x + point_w;
point_area.y1 = (int32_t)scaled_points[2].y - point_h;
point_area.y2 = (int32_t)scaled_points[2].y + point_h;
point_dsc_default.base.id2 = i - 1;
lv_draw_rect(layer, &point_dsc_default, &point_area);
}
valid_point_cnt++;
}
lv_draw_vector_dsc_add_path(dsc, path); // draw a path
if(dsc->base.id1 > 0) {
point_dsc_default.base.id1--;
dsc->base.id1--;
}
}
lv_draw_vector(dsc);
lv_vector_path_delete(path);
lv_draw_vector_dsc_delete(dsc);
#else
LV_LOG_WARN("LV_USE_VECTOR_GRAPHIC is not enabled for LV_CHART_TYPE_CURVE. Falling back to LV_CHART_TYPE_LINE");
draw_series_line(obj, layer);
#endif /*LV_USE_VECTOR_GRAPHIC*/
}
static void draw_series_scatter(lv_obj_t * obj, lv_layer_t * layer) static void draw_series_scatter(lv_obj_t * obj, lv_layer_t * layer)
{ {
@@ -1588,7 +1759,8 @@ static uint32_t get_index_from_x(lv_obj_t * obj, int32_t x)
if(x < 0) return 0; if(x < 0) return 0;
if(x > w) return chart->point_cnt - 1; 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_LINE ||
chart->type == LV_CHART_TYPE_CURVE) return (x * (chart->point_cnt - 1) + w / 2) / 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_BAR || chart->type == LV_CHART_TYPE_STACKED) return (x * chart->point_cnt) / w;
if(chart->type == LV_CHART_TYPE_SCATTER) { 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.*/ /*For scatter charts, the nearest id could be different depending on the series. Just check the first series.*/
@@ -1627,7 +1799,7 @@ static void invalidate_point(lv_obj_t * obj, uint32_t i)
int32_t pleft = lv_obj_get_style_pad_left(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 x_ofs = obj->coords.x1 + pleft + bwidth - scroll_left;
if(chart->type == LV_CHART_TYPE_LINE) { if(chart->type == LV_CHART_TYPE_LINE || chart->type == LV_CHART_TYPE_CURVE) {
int32_t line_width = lv_obj_get_style_line_width(obj, LV_PART_ITEMS); 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); int32_t point_w = lv_obj_get_style_width(obj, LV_PART_INDICATOR);
+1
View File
@@ -36,6 +36,7 @@ LV_EXPORT_CONST_INT(LV_CHART_POINT_NONE);
typedef enum { typedef enum {
LV_CHART_TYPE_NONE, /**< Don't draw the series*/ LV_CHART_TYPE_NONE, /**< Don't draw the series*/
LV_CHART_TYPE_LINE, /**< Connect the points with lines*/ LV_CHART_TYPE_LINE, /**< Connect the points with lines*/
LV_CHART_TYPE_CURVE, /**< Connect the points with curves*/
LV_CHART_TYPE_BAR, /**< Draw bars for each series*/ 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_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_SCATTER, /**< Draw points and lines in 2D (x,y coordinates)*/
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+56
View File
@@ -4,6 +4,12 @@
#include "unity/unity.h" #include "unity/unity.h"
#ifndef NON_AMD64_BUILD
#define EXT_NAME ".lp64.png"
#else
#define EXT_NAME ".lp32.png"
#endif
static lv_obj_t * active_screen = NULL; static lv_obj_t * active_screen = NULL;
static lv_obj_t * chart = NULL; static lv_obj_t * chart = NULL;
@@ -218,6 +224,56 @@ void test_chart_scatter(void)
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/chart_scatter.png"); TEST_ASSERT_EQUAL_SCREENSHOT("widgets/chart_scatter.png");
} }
void test_chart_curve(void)
{
#if LV_USE_VECTOR_GRAPHIC
/*Create a chart*/
lv_obj_set_size(chart, 400, 300);
lv_obj_center(chart);
lv_obj_set_style_bg_opa(chart, LV_OPA_50, LV_PART_INDICATOR);
lv_obj_set_style_line_dash_gap(chart, 5, LV_PART_ITEMS);
lv_obj_set_style_line_dash_width(chart, 10, LV_PART_ITEMS);
lv_chart_set_type(chart, LV_CHART_TYPE_CURVE);
lv_chart_set_point_count(chart, 15);
lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_SECONDARY_Y);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 20);
lv_chart_set_next_value(chart, ser1, 30);
lv_chart_set_next_value(chart, ser1, 80);
lv_chart_set_next_value(chart, ser1, 90);
lv_chart_set_next_value(chart, ser1, 90);
lv_chart_set_next_value(chart, ser1, 90);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 20);
lv_chart_set_next_value(chart, ser1, 10);
lv_chart_set_next_value(chart, ser1, 40);
lv_chart_set_next_value(chart, ser1, 60);
lv_chart_set_next_value(chart, ser1, 60);
lv_chart_set_next_value(chart, ser1, 40);
lv_chart_set_next_value(chart, ser1, 45);
lv_chart_set_next_value(chart, ser2, LV_CHART_POINT_NONE);
lv_chart_set_next_value(chart, ser2, 40);
lv_chart_set_next_value(chart, ser2, 50);
lv_chart_set_next_value(chart, ser2, 60);
lv_chart_set_next_value(chart, ser2, LV_CHART_POINT_NONE);
lv_chart_set_next_value(chart, ser2, LV_CHART_POINT_NONE);
lv_chart_set_next_value(chart, ser2, 80);
lv_chart_set_next_value(chart, ser2, 80);
lv_chart_set_next_value(chart, ser2, LV_CHART_POINT_NONE);
lv_chart_set_next_value(chart, ser2, LV_CHART_POINT_NONE);
lv_chart_set_next_value(chart, ser2, LV_CHART_POINT_NONE);
lv_chart_set_next_value(chart, ser2, 45);
lv_chart_set_next_value(chart, ser2, LV_CHART_POINT_NONE);
lv_chart_set_next_value(chart, ser2, 55);
lv_chart_set_next_value(chart, ser2, LV_CHART_POINT_NONE);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/chart_curve" EXT_NAME);
#endif /*LV_USE_VECTOR_GRAPHIC*/
}
void test_chart_properties(void) void test_chart_properties(void)
{ {