mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-29 22:56:58 +08:00
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:
committed by
GitHub
parent
c7a959757c
commit
ade26a471b
@@ -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:
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user