feat(chart) add SCATTER type

This commit is contained in:
Gabor Kiss-Vamosi
2021-05-22 14:36:31 +02:00
parent 431156e5e7
commit b1f7fbe3b7
7 changed files with 417 additions and 132 deletions

View File

@@ -27,16 +27,16 @@ void lv_example_chart_1(void)
lv_chart_set_next_value(chart, ser1, 90);
/*Directly set points on 'ser2'*/
ser2->points[0] = 90;
ser2->points[1] = 70;
ser2->points[2] = 65;
ser2->points[3] = 65;
ser2->points[4] = 65;
ser2->points[5] = 65;
ser2->points[6] = 65;
ser2->points[7] = 65;
ser2->points[8] = 65;
ser2->points[9] = 65;
ser2->y_points[0] = 90;
ser2->y_points[1] = 70;
ser2->y_points[2] = 65;
ser2->y_points[3] = 65;
ser2->y_points[4] = 65;
ser2->y_points[5] = 65;
ser2->y_points[6] = 65;
ser2->y_points[7] = 65;
ser2->y_points[8] = 65;
ser2->y_points[9] = 65;
lv_chart_refresh(chart); /*Required after direct set*/
}

View File

@@ -4,7 +4,7 @@
static void draw_event_cb(lv_event_t * e)
{
lv_obj_draw_part_dsc_t * dsc = lv_event_get_param(e);
if(dsc->part == LV_PART_TICKS && dsc->id == LV_CHART_AXIS_X) {
if(dsc->part == LV_PART_TICKS && dsc->id == LV_CHART_AXIS_PRIMARY_X) {
const char * month[] = {"Jan", "Febr", "March", "Apr", "May", "Jun", "July", "Aug", "Sept", "Oct", "Nov", "Dec"};
lv_snprintf(dsc->text, sizeof(dsc->text), "%s", month[dsc->value]);
}
@@ -27,7 +27,7 @@ void lv_example_chart_3(void)
lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
/*Add ticks and label to every axis*/
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_X, 10, 5, 12, 3, true, 40);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 10, 5, 12, 3, true, 40);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 2, true, 50);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_SECONDARY_Y, 10, 5, 3, 4, true, 50);
@@ -52,7 +52,7 @@ void lv_example_chart_3(void)
lv_chart_set_next_value(chart, ser1, 22);
lv_chart_set_next_value(chart, ser1, 58);
lv_coord_t * ser2_array = lv_chart_get_array(chart, ser2);
lv_coord_t * ser2_array = lv_chart_get_y_array(chart, ser2);
/*Directly set points on 'ser2'*/
ser2_array[0] = 92;
ser2_array[1] = 71;

View File

@@ -25,7 +25,7 @@ static void event_cb(lv_event_t * e)
lv_point_t p;
lv_chart_get_point_pos_by_id(chart, ser, id, &p);
lv_coord_t * y_array = lv_chart_get_array(chart, ser);
lv_coord_t * y_array = lv_chart_get_y_array(chart, ser);
lv_coord_t value = y_array[id];
char buf[16];

View File

@@ -80,7 +80,7 @@ void lv_example_chart_5(void)
uint32_t pcnt = sizeof(ecg_sample) / sizeof(ecg_sample[0]);
lv_chart_set_point_count(chart, pcnt);
lv_chart_set_ext_array(chart, ser, (lv_coord_t *)ecg_sample);
lv_chart_set_ext_y_array(chart, ser, (lv_coord_t *)ecg_sample);
lv_obj_t * slider;
slider = lv_slider_create(lv_scr_act());

View File

@@ -20,7 +20,7 @@ static void event_cb(lv_event_t * e)
else if(code == LV_EVENT_DRAW_PART_END) {
lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
if(dsc->part == LV_PART_CURSOR && dsc->p1 && dsc->p2 && dsc->p1->y == dsc->p2->y && last_id >= 0) {
lv_coord_t * data_array = lv_chart_get_array(chart, ser);
lv_coord_t * data_array = lv_chart_get_y_array(chart, ser);
lv_coord_t v = data_array[last_id];
char buf[16];
lv_snprintf(buf, sizeof(buf), "%d", v);
@@ -63,7 +63,7 @@ void lv_example_chart_6(void)
lv_obj_align(chart, LV_ALIGN_CENTER, 0, -10);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 5, true, 40);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_X, 10, 5, 10, 1, true, 30);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 10, 5, 10, 1, true, 30);
lv_obj_add_event_cb(chart, event_cb, LV_EVENT_ALL, NULL);
lv_obj_refresh_ext_draw_size(chart);

View File

@@ -14,13 +14,9 @@
*********************/
#define MY_CLASS &lv_chart_class
#define LV_CHART_YMIN_DEF 0
#define LV_CHART_YMAX_DEF 100
#define LV_CHART_HDIV_DEF 3
#define LV_CHART_VDIV_DEF 5
#define LV_CHART_POINT_CNT_DEF 10
#define LV_CHART_LABEL_ITERATOR_FORWARD 1
#define LV_CHART_LABEL_ITERATOR_REVERSE 0
/**********************
* TYPEDEFS
@@ -36,10 +32,12 @@ 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 , const lv_area_t * mask);
static void draw_series_line(lv_obj_t * obj, const lv_area_t * clip_area);
static void draw_series_bar(lv_obj_t * obj, const lv_area_t * clip_area);
static void draw_series_scatter(lv_obj_t * obj, const lv_area_t * clip_area);
static void draw_cursors(lv_obj_t * obj, const lv_area_t * clip_area);
static void draw_axes(lv_obj_t * obj, const lv_area_t * mask);
static uint32_t get_index_from_x(lv_obj_t * obj, lv_coord_t x);
static void invalidate_point(lv_obj_t * obj, uint16_t i);
static void new_points_alloc(lv_obj_t * obj, lv_chart_series_t * ser, uint32_t cnt, lv_coord_t ** a);
/**********************
* STATIC VARIABLES
@@ -77,6 +75,23 @@ void lv_chart_set_type(lv_obj_t * obj, lv_chart_type_t type)
lv_chart_t * chart = (lv_chart_t *)obj;
if(chart->type == type) return;
if(chart->type == LV_CHART_TYPE_SCATTER) {
lv_chart_series_t * ser;
_LV_LL_READ_BACK(&chart->series_ll, ser) {
lv_mem_free(ser->x_points);
ser->x_points = NULL;
}
}
if(type == LV_CHART_TYPE_SCATTER) {
lv_chart_series_t * ser;
_LV_LL_READ_BACK(&chart->series_ll, ser) {
ser->x_points = lv_mem_alloc(sizeof(lv_point_t) * chart->point_cnt);
LV_ASSERT_MALLOC(ser->x_points);
if(ser->x_points == NULL) return;
}
}
chart->type = type;
lv_chart_refresh(obj);
@@ -90,51 +105,12 @@ void lv_chart_set_point_count(lv_obj_t * obj, uint16_t cnt)
if(chart->point_cnt == cnt) return;
lv_chart_series_t * ser;
uint16_t point_cnt_old = chart->point_cnt;
uint16_t i;
lv_coord_t def = LV_CHART_POINT_NONE;
if(cnt < 1) cnt = 1;
_LV_LL_READ_BACK(&chart->series_ll, ser) {
if(!ser->ext_buf_assigned) {
if(ser->last_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;
if(cnt >= point_cnt_old) {
for(i = 0; i < point_cnt_old; i++) {
new_points[i] =
ser->points[(i + ser->last_point) % point_cnt_old]; /*Copy old contents to new array*/
}
for(i = point_cnt_old; i < cnt; i++) {
new_points[i] = def; /*Fill up the rest with default value*/
}
}
else {
for(i = 0; i < cnt; i++) {
new_points[i] =
ser->points[(i + ser->last_point) % point_cnt_old]; /*Copy old contents to new array*/
}
}
/*Switch over pointer from old to new*/
lv_mem_free(ser->points);
ser->points = new_points;
}
else {
ser->points = lv_mem_realloc(ser->points, sizeof(lv_coord_t) * cnt);
LV_ASSERT_MALLOC(ser->points);
if(ser->points == NULL) return;
/*Initialize the new points*/
if(cnt > point_cnt_old) {
for(i = point_cnt_old - 1; i < cnt; i++) {
ser->points[i] = def;
}
}
}
}
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;
}
@@ -147,14 +123,31 @@ void lv_chart_set_range(lv_obj_t * obj, lv_chart_axis_t axis, lv_coord_t min, lv
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(axis != LV_CHART_AXIS_PRIMARY_Y && axis != LV_CHART_AXIS_SECONDARY_Y) {
max = max == min ? max + 1 : max;
lv_chart_t * chart = (lv_chart_t *)obj;
switch(axis) {
case LV_CHART_AXIS_PRIMARY_Y:
chart->ymin[0] = min;
chart->ymax[0] = max;
break;
case LV_CHART_AXIS_SECONDARY_Y:
chart->ymin[1] = min;
chart->ymax[1] = max;
break;
case LV_CHART_AXIS_PRIMARY_X:
chart->xmin[0] = min;
chart->xmax[0] = max;
break;
case LV_CHART_AXIS_SECONDARY_X:
chart->xmin[1] = min;
chart->xmax[1] = max;
break;
default:
LV_LOG_WARN("Invalid axis: %d", axis);
return;
}
lv_chart_t * chart = (lv_chart_t *)obj;
chart->ymin[axis] = min;
chart->ymax[axis] = (max == min ? max + 1 : max);
lv_chart_refresh(obj);
}
@@ -304,8 +297,8 @@ void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint1
lv_coord_t h = (lv_obj_get_content_height(obj) * chart->zoom_y) >> 8;
p_out->y = (int32_t)((int32_t)ser->points[id] - chart->ymin[ser->y_axis]) * h;
p_out->y = p_out->y / (chart->ymax[ser->y_axis] - chart->ymin[ser->y_axis]);
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);
@@ -328,11 +321,6 @@ lv_chart_series_t * lv_chart_add_series(lv_obj_t * obj, lv_color_t color, lv_cha
LV_ASSERT_OBJ(obj, MY_CLASS);
if(axis != LV_CHART_AXIS_PRIMARY_Y && axis != LV_CHART_AXIS_SECONDARY_Y) {
LV_LOG_WARN("Invalid y axis");
return NULL;
}
lv_chart_t * chart = (lv_chart_t *)obj;
lv_chart_series_t * ser = _lv_ll_ins_head(&chart->series_ll);
LV_ASSERT_MALLOC(ser);
@@ -341,21 +329,27 @@ lv_chart_series_t * lv_chart_add_series(lv_obj_t * obj, lv_color_t color, lv_cha
lv_coord_t def = LV_CHART_POINT_NONE;
ser->color = color;
ser->points = lv_mem_alloc(sizeof(lv_coord_t) * chart->point_cnt);
LV_ASSERT_MALLOC(ser->points);
if(ser->points == NULL) {
ser->y_points = lv_mem_alloc(sizeof(lv_coord_t) * chart->point_cnt);
LV_ASSERT_MALLOC(ser->y_points);
if(chart->type == LV_CHART_TYPE_SCATTER) {
ser->x_points = lv_mem_alloc(sizeof(lv_coord_t) * chart->point_cnt);
LV_ASSERT_MALLOC(ser->x_points);
}
if(ser->y_points == NULL) {
_lv_ll_remove(&chart->series_ll, ser);
lv_mem_free(ser);
return NULL;
}
ser->last_point = 0;
ser->ext_buf_assigned = false;
ser->y_ext_buf_assigned = false;
ser->hidden = 0;
ser->y_axis = axis;
ser->x_axis_sec = axis & LV_CHART_AXIS_SECONDARY_X ? 1 : 0;
ser->y_axis_sec = axis & LV_CHART_AXIS_SECONDARY_Y ? 1 : 0;
uint16_t i;
lv_coord_t * p_tmp = ser->points;
lv_coord_t * p_tmp = ser->y_points;
for(i = 0; i < chart->point_cnt; i++) {
*p_tmp = def;
p_tmp++;
@@ -370,7 +364,7 @@ void lv_chart_remove_series(lv_obj_t * obj, lv_chart_series_t * series)
LV_ASSERT_NULL(series);
lv_chart_t * chart = (lv_chart_t *)obj;
if(!series->ext_buf_assigned && series->points) lv_mem_free(series->points);
if(!series->y_ext_buf_assigned && series->y_points) lv_mem_free(series->y_points);
_lv_ll_remove(&chart->series_ll, series);
lv_mem_free(series);
@@ -443,7 +437,6 @@ lv_chart_cursor_t * lv_chart_add_cursor(lv_obj_t * obj, lv_color_t color, lv_di
cursor->color = color;
cursor->dir = dir;
return cursor;
}
@@ -512,7 +505,7 @@ void lv_chart_set_all_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t
lv_chart_t * chart = (lv_chart_t *)obj;
uint16_t i;
for(i = 0; i < chart->point_cnt; i++) {
ser->points[i] = value;
ser->y_points[i] = value;
}
ser->last_point = 0;
lv_chart_refresh(obj);
@@ -525,13 +518,47 @@ void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t
lv_chart_t * chart = (lv_chart_t *)obj;
if(chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT) {
ser->points[ser->last_point] =
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->points[ser->last_point] = value;
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);
}
}
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)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(ser);
lv_chart_t * chart = (lv_chart_t *)obj;
if(chart->type != LV_CHART_TYPE_SCATTER) {
LV_LOG_WARN("Type must be LV_CHART_TYPE_SCATTER");
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;
invalidate_point(obj, ser->last_point);
@@ -551,26 +578,61 @@ void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_
lv_chart_t * chart = (lv_chart_t *)obj;
if(id >= chart->point_cnt) return;
ser->points[id] = value;
ser->y_points[id] = value;
}
void lv_chart_set_ext_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[])
void lv_chart_set_value_by_id2(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t x_value, lv_coord_t y_value)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(ser);
lv_chart_t * chart = (lv_chart_t *)obj;
if(chart->type != LV_CHART_TYPE_SCATTER) {
LV_LOG_WARN("Type must be LV_CHART_TYPE_SCATTER");
return;
}
if(id >= chart->point_cnt) return;
ser->x_points[id] = x_value;
ser->y_points[id] = y_value;
}
void lv_chart_set_ext_y_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[])
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(ser);
if(!ser->ext_buf_assigned && ser->points) lv_mem_free(ser->points);
ser->ext_buf_assigned = true;
ser->points = array;
if(!ser->y_ext_buf_assigned && ser->y_points) lv_mem_free(ser->y_points);
ser->y_ext_buf_assigned = true;
ser->y_points = array;
lv_obj_invalidate(obj);
}
lv_coord_t * lv_chart_get_array(const lv_obj_t * obj, lv_chart_series_t * ser)
void lv_chart_set_ext_x_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[])
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(ser);
if(!ser->x_ext_buf_assigned && ser->x_points) lv_mem_free(ser->x_points);
ser->x_ext_buf_assigned = true;
ser->x_points = array;
lv_obj_invalidate(obj);
}
lv_coord_t * lv_chart_get_y_array(const lv_obj_t * obj, lv_chart_series_t * ser)
{
LV_UNUSED(obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(ser);
return ser->points;
return ser->y_points;
}
lv_coord_t * lv_chart_get_x_array(const lv_obj_t * obj, lv_chart_series_t * ser)
{
LV_UNUSED(obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(ser);
return ser->x_points;
}
uint32_t lv_chart_get_pressed_point(const lv_obj_t * obj)
@@ -593,10 +655,14 @@ static void lv_chart_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
_lv_ll_init(&chart->series_ll, sizeof(lv_chart_series_t));
_lv_ll_init(&chart->cursor_ll, sizeof(lv_chart_cursor_t));
chart->ymin[LV_CHART_AXIS_PRIMARY_Y] = LV_CHART_YMIN_DEF;
chart->ymax[LV_CHART_AXIS_PRIMARY_Y] = LV_CHART_YMAX_DEF;
chart->ymin[LV_CHART_AXIS_SECONDARY_Y] = LV_CHART_YMIN_DEF;
chart->ymax[LV_CHART_AXIS_SECONDARY_Y] = LV_CHART_YMAX_DEF;
chart->ymin[0] = 0;
chart->xmin[0] = 0;
chart->ymin[1] = 0;
chart->xmin[1] = 0;
chart->ymax[0] = 100;
chart->xmax[0] = 100;
chart->ymax[1] = 100;
chart->xmax[1] = 100;
chart->hdiv_cnt = LV_CHART_HDIV_DEF;
chart->vdiv_cnt = LV_CHART_VDIV_DEF;
@@ -620,7 +686,7 @@ static void lv_chart_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
while(chart->series_ll.head) {
ser = _lv_ll_get_head(&chart->series_ll);
if(!ser->ext_buf_assigned) lv_mem_free(ser->points);
if(!ser->y_ext_buf_assigned) lv_mem_free(ser->y_points);
_lv_ll_remove(&chart->series_ll, ser);
lv_mem_free(ser);
@@ -664,7 +730,7 @@ static void lv_chart_event(const lv_obj_class_t * class_p, lv_event_t * e)
lv_obj_refresh_self_size(obj);
} else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX4(*s, chart->tick[LV_CHART_AXIS_X].draw_size,
*s = LV_MAX4(*s, chart->tick[LV_CHART_AXIS_PRIMARY_X].draw_size,
chart->tick[LV_CHART_AXIS_PRIMARY_Y].draw_size, chart->tick[LV_CHART_AXIS_SECONDARY_Y].draw_size);
} else if(code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_param(e);
@@ -675,8 +741,9 @@ static void lv_chart_event(const lv_obj_class_t * class_p, lv_event_t * e)
draw_div_lines(obj, clip_area);
draw_axes(obj, clip_area);
if(chart->type & LV_CHART_TYPE_LINE) draw_series_line(obj, clip_area);
else if(chart->type & LV_CHART_TYPE_BAR) draw_series_bar(obj, clip_area);
if(chart->type == LV_CHART_TYPE_LINE) draw_series_line(obj, clip_area);
else if(chart->type == LV_CHART_TYPE_BAR) draw_series_bar(obj, clip_area);
else if(chart->type == LV_CHART_TYPE_SCATTER) draw_series_scatter(obj, clip_area);
draw_cursors(obj, clip_area);
}
@@ -823,8 +890,8 @@ static void draw_series_line(lv_obj_t * obj, const lv_area_t * clip_area)
lv_coord_t p_act = start_point;
lv_coord_t p_prev = start_point;
int32_t y_tmp = (int32_t)((int32_t)ser->points[p_prev] - chart->ymin[ser->y_axis]) * h;
y_tmp = y_tmp / (chart->ymax[ser->y_axis] - chart->ymin[ser->y_axis]);
int32_t y_tmp = (int32_t)((int32_t)ser->y_points[p_prev] - chart->ymin[ser->y_axis_sec]) * h;
y_tmp = y_tmp / (chart->ymax[ser->y_axis_sec] - chart->ymin[ser->y_axis_sec]);
p2.y = h - y_tmp + y_ofs;
lv_obj_draw_part_dsc_t dsc;
@@ -846,8 +913,8 @@ static void draw_series_line(lv_obj_t * obj, const lv_area_t * clip_area)
p_act = (start_point + i) % chart->point_cnt;
y_tmp = (int32_t)((int32_t)ser->points[p_act] - chart->ymin[ser->y_axis]) * h;
y_tmp = y_tmp / (chart->ymax[ser->y_axis] - chart->ymin[ser->y_axis]);
y_tmp = (int32_t)((int32_t)ser->y_points[p_act] - chart->ymin[ser->y_axis_sec]) * h;
y_tmp = y_tmp / (chart->ymax[ser->y_axis_sec] - chart->ymin[ser->y_axis_sec]);
p2.y = h - y_tmp + y_ofs;
if(p2.x < clip_area->x1 - point_w - 1) {
@@ -858,7 +925,7 @@ static void draw_series_line(lv_obj_t * obj, const lv_area_t * clip_area)
/*Don't draw the first point. A second point is also required to draw the line*/
if(i != 0) {
if(crowded_mode) {
if(ser->points[p_prev] != LV_CHART_POINT_NONE && ser->points[p_act] != LV_CHART_POINT_NONE) {
if(ser->y_points[p_prev] != LV_CHART_POINT_NONE && ser->y_points[p_act] != LV_CHART_POINT_NONE) {
/*Draw only one vertical line between the min an max y values on the same x value*/
y_max = LV_MAX(y_max, p2.y);
y_min = LV_MIN(y_min, p2.y);
@@ -883,18 +950,18 @@ static void draw_series_line(lv_obj_t * obj, const lv_area_t * clip_area)
point_area.y2 = p1.y + point_h;
dsc.id = i - 1;
dsc.p1 = ser->points[p_prev] != LV_CHART_POINT_NONE ? &p1 : NULL;
dsc.p2 = ser->points[p_act] != LV_CHART_POINT_NONE ? &p2 : NULL;
dsc.p1 = ser->y_points[p_prev] != LV_CHART_POINT_NONE ? &p1 : NULL;
dsc.p2 = ser->y_points[p_act] != LV_CHART_POINT_NONE ? &p2 : NULL;
dsc.draw_area = &point_area;
dsc.value = ser->points[p_prev];
dsc.value = ser->y_points[p_prev];
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &dsc);
if(ser->points[p_prev] != LV_CHART_POINT_NONE && ser->points[p_act] != LV_CHART_POINT_NONE) {
if(ser->y_points[p_prev] != LV_CHART_POINT_NONE && ser->y_points[p_act] != LV_CHART_POINT_NONE) {
lv_draw_line(&p1, &p2, &series_mask, &line_dsc_default);
}
if(point_w && point_h && ser->points[p_act] != LV_CHART_POINT_NONE) {
if(point_w && point_h && ser->y_points[p_act] != LV_CHART_POINT_NONE) {
lv_draw_rect(&point_area, &series_mask, &point_dsc_default);
}
@@ -908,7 +975,7 @@ static void draw_series_line(lv_obj_t * obj, const lv_area_t * clip_area)
/*Draw the last point*/
if(!crowded_mode && i == chart->point_cnt) {
if(ser->points[p_act] != LV_CHART_POINT_NONE) {
if(ser->y_points[p_act] != LV_CHART_POINT_NONE) {
lv_area_t point_area;
point_area.x1 = p2.x - point_w;
point_area.x2 = p2.x + point_w;
@@ -919,7 +986,145 @@ static void draw_series_line(lv_obj_t * obj, const lv_area_t * clip_area)
dsc.p1 = NULL;
dsc.p2 = NULL;
dsc.draw_area = &point_area;
dsc.value = ser->points[p_act];
dsc.value = ser->y_points[p_act];
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &dsc);
lv_draw_rect(&point_area, &series_mask, &point_dsc_default);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &dsc);
}
}
}
}
static void draw_series_scatter(lv_obj_t * obj, const lv_area_t * clip_area)
{
lv_area_t com_area;
if(_lv_area_intersect(&com_area, &obj->coords, clip_area) == false) return;
lv_chart_t * chart = (lv_chart_t *)obj;
uint16_t i;
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_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_chart_series_t * ser;
lv_area_t series_mask;
bool mask_ret = _lv_area_intersect(&series_mask, &obj->coords, clip_area);
if(mask_ret == false) return;
lv_draw_line_dsc_t line_dsc_default;
lv_draw_line_dsc_init(&line_dsc_default);
lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &line_dsc_default);
lv_draw_rect_dsc_t point_dsc_default;
lv_draw_rect_dsc_init(&point_dsc_default);
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &point_dsc_default);
lv_coord_t point_w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
lv_coord_t point_h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
/*Do not bother with line ending is the point will over it*/
if(LV_MIN(point_w, point_h) > line_dsc_default.width / 2) line_dsc_default.raw_end = 1;
if(line_dsc_default.width == 1) line_dsc_default.raw_end = 1;
/*Go through all data lines*/
_LV_LL_READ_BACK(&chart->series_ll, ser) {
if (ser->hidden) continue;
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;
p1.x = x_ofs;
p2.x = x_ofs;
lv_coord_t p_act = start_point;
lv_coord_t p_prev = start_point;
if(ser->y_points[p_act] != LV_CHART_POINT_CNT_DEF) {
p2.x = lv_map(ser->x_points[p_act], chart->xmin[ser->x_axis_sec], chart->xmax[ser->x_axis_sec], 0, w);
p2.x += x_ofs;
p2.y = lv_map(ser->y_points[p_act], chart->ymin[ser->y_axis_sec], chart->ymax[ser->y_axis_sec], 0, h);
p2.y = h - p2.y;
p2.y += y_ofs;
} else {
p2.x = LV_COORD_MIN;
p2.y = LV_COORD_MIN;
}
lv_obj_draw_part_dsc_t dsc;
lv_obj_draw_dsc_init(&dsc, clip_area);
dsc.part = LV_PART_ITEMS;
dsc.line_dsc = &line_dsc_default;
dsc.rect_dsc = &point_dsc_default;
dsc.sub_part_ptr = ser;
for(i = 0; i < chart->point_cnt; i++) {
p1.x = p2.x;
p1.y = p2.y;
p_act = (start_point + i) % chart->point_cnt;
if(ser->y_points[p_act] != LV_CHART_POINT_NONE) {
p2.y = lv_map(ser->y_points[p_act], chart->ymin[ser->y_axis_sec], chart->ymax[ser->y_axis_sec], 0, h);
p2.y = h - p2.y;
p2.y += y_ofs;
p2.x = lv_map(ser->x_points[p_act], chart->xmin[ser->x_axis_sec], chart->xmax[ser->x_axis_sec], 0, w);
p2.x += x_ofs;
} else {
p_prev = p_act;
continue;
}
/*Don't draw the first point. A second point is also required to draw the line*/
if(i != 0) {
lv_area_t point_area;
point_area.x1 = p1.x - point_w;
point_area.x2 = p1.x + point_w;
point_area.y1 = p1.y - point_h;
point_area.y2 = p1.y + point_h;
dsc.id = i - 1;
dsc.p1 = ser->y_points[p_prev] != LV_CHART_POINT_NONE ? &p1 : NULL;
dsc.p2 = ser->y_points[p_act] != LV_CHART_POINT_NONE ? &p2 : NULL;
dsc.draw_area = &point_area;
dsc.value = ser->y_points[p_prev];
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &dsc);
if(ser->y_points[p_prev] != LV_CHART_POINT_NONE && ser->y_points[p_act] != LV_CHART_POINT_NONE) {
lv_draw_line(&p1, &p2, &series_mask, &line_dsc_default);
if(point_w && point_h) {
lv_draw_rect(&point_area, &series_mask, &point_dsc_default);
}
}
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &dsc);
}
p_prev = p_act;
}
/*Draw the last point*/
if(i == chart->point_cnt) {
if(ser->y_points[p_act] != LV_CHART_POINT_NONE) {
lv_area_t point_area;
point_area.x1 = p2.x - point_w;
point_area.x2 = p2.x + point_w;
point_area.y1 = p2.y - point_h;
point_area.y2 = p2.y + point_h;
dsc.id = i - 1;
dsc.p1 = NULL;
dsc.p2 = NULL;
dsc.draw_area = &point_area;
dsc.value = ser->y_points[p_act];
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &dsc);
lv_draw_rect(&point_area, &series_mask, &point_dsc_default);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &dsc);
@@ -989,15 +1194,15 @@ static void draw_series_bar(lv_obj_t * obj, const lv_area_t * clip_area)
col_dsc.bg_color = ser->color;
lv_coord_t p_act = (start_point + i) % chart->point_cnt;
y_tmp = (int32_t)((int32_t)ser->points[p_act] - chart->ymin[ser->y_axis]) * h;
y_tmp = y_tmp / (chart->ymax[ser->y_axis] - chart->ymin[ser->y_axis]);
y_tmp = (int32_t)((int32_t)ser->y_points[p_act] - chart->ymin[ser->y_axis_sec]) * h;
y_tmp = y_tmp / (chart->ymax[ser->y_axis_sec] - chart->ymin[ser->y_axis_sec]);
col_a.y1 = h - y_tmp + obj->coords.y1 + y_ofs;
if(ser->points[p_act] != LV_CHART_POINT_NONE) {
if(ser->y_points[p_act] != LV_CHART_POINT_NONE) {
dsc.draw_area = &col_a;
dsc.rect_dsc = &col_dsc;
dsc.sub_part_ptr = ser;
dsc.value = ser->points[p_act];
dsc.value = ser->y_points[p_act];
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &dsc);
lv_draw_rect(&col_a, &series_mask, &col_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &dsc);
@@ -1105,7 +1310,6 @@ static void draw_cursors(lv_obj_t * obj, const lv_area_t * clip_area)
lv_draw_rect(&point_area, &clip_area2, &point_dsc_tmp);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &dsc);
}
}
}
@@ -1225,7 +1429,7 @@ static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area)
lv_chart_t * chart = (lv_chart_t *)obj;
lv_chart_tick_dsc_t * t = &chart->tick[LV_CHART_AXIS_X];
lv_chart_tick_dsc_t * t = &chart->tick[LV_CHART_AXIS_PRIMARY_X];
if(t->major_cnt <= 1) return;
if(!t->label_en && !t->major_len && !t->minor_len) return;
@@ -1256,7 +1460,7 @@ static void draw_x_ticks(lv_obj_t * obj, const lv_area_t * clip_area)
lv_obj_draw_part_dsc_t dsc;
lv_obj_draw_dsc_init(&dsc, clip_area);
dsc.id = LV_CHART_AXIS_X;
dsc.id = LV_CHART_AXIS_PRIMARY_X;
dsc.part = LV_PART_TICKS;
dsc.label_dsc = &label_dsc;
dsc.line_dsc = &line_dsc;
@@ -1391,4 +1595,50 @@ static void invalidate_point(lv_obj_t * obj, uint16_t i)
}
}
static void new_points_alloc(lv_obj_t * obj, lv_chart_series_t * ser, uint32_t cnt, lv_coord_t ** a)
{
if((*a) == NULL) return;
lv_chart_t * chart = (lv_chart_t*) obj;
uint32_t point_cnt_old = chart->point_cnt;
uint32_t i;
if(ser->last_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;
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*/
}
for(i = point_cnt_old; i < cnt; i++) {
new_points[i] = LV_CHART_POINT_NONE; /*Fill up the rest with default value*/
}
}
else {
for(i = 0; i < cnt; i++) {
new_points[i] =
(*a)[(i + ser->last_point) % point_cnt_old]; /*Copy old contents to new array*/
}
}
/*Switch over pointer from old to new*/
lv_mem_free((*a));
(*a) = new_points;
}
else {
(*a) = lv_mem_realloc((*a), sizeof(lv_coord_t) * cnt);
LV_ASSERT_MALLOC((*a));
if((*a) == NULL) return;
/*Initialize the new points*/
if(cnt > point_cnt_old) {
for(i = point_cnt_old - 1; i < cnt; i++) {
(*a)[i] = LV_CHART_POINT_NONE;
}
}
}
}
#endif

View File

@@ -33,9 +33,10 @@ LV_EXPORT_CONST_INT(LV_CHART_POINT_NONE);
* Chart types
*/
enum {
LV_CHART_TYPE_NONE = 0x00, /**< Don't draw the series*/
LV_CHART_TYPE_LINE = 0x01, /**< Connect the points with lines*/
LV_CHART_TYPE_BAR = 0x02, /**< Draw columns*/
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_SCATTER, /**< Draw points and lines in 2D (x,y coordinates)*/
};
typedef uint8_t lv_chart_type_t;
@@ -52,9 +53,10 @@ typedef uint8_t lv_chart_update_mode_t;
* Enumeration of the axis'
*/
enum {
LV_CHART_AXIS_PRIMARY_Y, /*Y axis should be the first to allow indexing arrays with the values*/
LV_CHART_AXIS_SECONDARY_Y,
LV_CHART_AXIS_X,
LV_CHART_AXIS_PRIMARY_Y = 0x00,
LV_CHART_AXIS_SECONDARY_Y = 0x01,
LV_CHART_AXIS_PRIMARY_X = 0x02,
LV_CHART_AXIS_SECONDARY_X = 0x04,
_LV_CHART_AXIS_LAST
};
typedef uint8_t lv_chart_axis_t;
@@ -63,12 +65,15 @@ typedef uint8_t lv_chart_axis_t;
* Descriptor a chart series
*/
typedef struct {
lv_coord_t * points;
lv_coord_t * x_points;
lv_coord_t * y_points;
lv_color_t color;
uint16_t last_point;
uint8_t hidden : 1;
uint8_t ext_buf_assigned : 1;
lv_chart_axis_t y_axis : 2;
uint8_t x_ext_buf_assigned : 1;
uint8_t y_ext_buf_assigned : 1;
uint8_t x_axis_sec : 1;
uint8_t y_axis_sec : 1;
} _lv_chart_series_t;
/*Trick to no expose the fields of the struct in the MicroPython binding*/
@@ -105,13 +110,15 @@ typedef struct {
lv_chart_tick_dsc_t tick[_LV_CHART_AXIS_LAST];
lv_coord_t ymin[2];
lv_coord_t ymax[2];
lv_coord_t xmin[2];
lv_coord_t xmax[2];
uint16_t pressed_point_id;
uint16_t hdiv_cnt; /**< Number of horizontal division lines*/
uint16_t vdiv_cnt; /**< Number of vertical division lines*/
uint16_t point_cnt; /**< Point number in a data line*/
uint16_t zoom_x;
uint16_t zoom_y;
lv_chart_type_t type :2; /**< Line or column chart*/
lv_chart_type_t type :3; /**< Line or column chart*/
lv_chart_update_mode_t update_mode : 1;
}_lv_chart_t;
@@ -370,21 +377,49 @@ void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t
void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value, uint16_t id);
/**
* Set an external array of data points to use for the chart
* Set an individual point's x and y value of a chart's series directly based on its index
* Can be used only with `LV_CHART_TYPE_SCATTER`.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the array
* @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_value_by_id2(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t x_value, lv_coord_t y_value);
/**
* Set an external array for the y data points to use for the chart
* NOTE: It is the users responsibility to make sure the `point_cnt` matches the external array size.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param array external array of points for chart
*/
void lv_chart_set_ext_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
void lv_chart_set_ext_y_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
/**
* Get the array of values of a series
* Set an external array for the x data points to use for the chart
* NOTE: It is the users responsibility to make sure the `point_cnt` matches the external array size.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param array external array of points for chart
*/
void lv_chart_set_ext_x_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
/**
* Get the array of y values of a series
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the array of values with 'point_count' elements
*/
lv_coord_t * lv_chart_get_array(const lv_obj_t * obj, lv_chart_series_t * ser);
lv_coord_t * lv_chart_get_y_array(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the array of x values of a series
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the array of values with 'point_count' elements
*/
lv_coord_t * lv_chart_get_x_array(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the index of the currently pressed point. It's the same for every series.