Dynamic line drawing problems

Hello,

i am using the latest version of lvgl on an ESP32 with Arduino IDE.

I am trying to produce a chart. As far as i understood, there is no way to give individual x-points while drawing data with the built in functionality. This is why i try to draw line-objects.
The number of “points” and with that the number of lines to draw can change according to a list, which is created somewhere else dynamically.
So far i managed to draw objects dynamically with allocating a vector of objects - this works fine. Trying to apply this to the lines object fails at the point, where

lv_line_set_points(section_lines[i], **points**, 2);

has to be used. If i set a 2D array of lv_point_t, the code compiles, but in the moment of drawing, to be more specific, in the moment of scrolling into the chart, the ESP freezes with the last trace msg:

Trace: lv_draw_get_buf: allocate (C:\Users\Eichler\Documents\Arduino\libraries\LittlevGL\src\src\lv_draw\lv_draw.c #57)

with drawing 2 lines, each one has 2 points. The code looks as follows, excluding some part which is in between to calculate x and y positions (which is working correctly):

lv_point_t time_sections[programs[prog_i].seasons[season_i].profiles[0].size()][2];
std::vector<lv_obj_t *> section_lines(programs[prog_i].seasons[season_i].profiles[0].size());

//const lv_point_t points[] = {{70, 150}, {190, 40}};

  for (int i = 0; i<programs[prog_i].seasons[season_i].profiles[0].size(); i++)
  {
    [......]

    time_sections[i][0] = {(int)xpoint(x1), (int)ppoint(y1)};
    time_sections[i][1] = {(int)xpoint(x2), (int)ppoint(y2)};

    section_lines[i] = lv_line_create(page, NULL);
    lv_line_set_style(section_lines[i], LV_LINE_STYLE_MAIN, &style_line);
  
    lv_line_set_points(section_lines[i], time_sections[i], 2);

    lv_obj_set_pos(section_lines[i], 10, 220);
  }

if i set a pair of constant dummy points (commented in the example code), it works as expected.
I am not able to create a vector / an array, which is written dynamically and can serve the set_points function to draw multiple lines automatically. Can you help me with that? Thank you very much!

Next time, please fill out the template instead of removing it.


  • Have you tried this in the PC simulator to see if it works? If it does, it means that it’s very likely that the problem is with your board.
  • If it doesn’t, can you send a simplified code sample that will compile by itself so we can reproduce the issue? C++ is fine.

I am sorry, i thought removing the template is correct.

I am not getting the PC simulator to work. I installed eclipse on an ubuntu VM, made everything as written in the how-to, imported the project - it compiles without errors. However, i only see a window pop up for half a second and the console gives me (exit value -1). I am new to eclipse and dont have any grip on that so far.

The problem with line points comes up when using an allocated array (or vector) of lv_point_t. When using const or static (predefined) arrays, everything works fine.
Also in the examples given on your page for creating line objects, those arrays are always const or static - i think not without reason.
For me, the simple code snipped shown below creates something on the screen, which looks like the “starting points” of the 2 lines, however, the remaining part of the line is missing. While scrolling over that part of the page, sometimes, the full lines show up for a fraction of a second and disappear again. After scrolling for 20-40 seconds, the controller freezes.
I am sorry for not being able to test this on the simulator, but after producing a menu structure with dynamically created pages and complex behavior of all sorts of objects, i would be very surprised, if this would now be a problem of the ESP. It feels to me, that the problem arises with passing non const points and the scrolling of the page which it is drawn on.

scr = lv_obj_create(NULL, NULL);
lv_scr_load(scr);

lv_obj_t * page = lv_page_create(scr, NULL);
lv_obj_set_size(page, LV_HOR_RES, LV_VER_RES);

lv_point_t time_sections[2][2];
std::vector<lv_obj_t *> section_lines(2);

for (int i = 0; i<2; i++)
{
time_sections[i][0] = {i10, i20};
time_sections[i][1] = {(i+1)*30, (i+1)*50};

section_lines[i] = lv_line_create(page, NULL);
lv_line_set_points(section_lines[i], time_sections[i], 2);
lv_obj_set_pos(section_lines[i], 10, 220);

}

After trying out several things, creating a simple line without const points fails as well:

lv_point_t points[] = {{10, 10}, {100, 100}};

lv_obj_t * line = lv_line_create(page, NULL);
lv_line_set_points(line, points, 2);
lv_obj_set_pos(line, 20, 220);

I get a curved line, which is at least ignoring the second point of (100, 100), flickering while scrolling over it.

On a VM, you need to open lv_drv_conf.h and set one of the options relating to virtual machines to 1 (I’m sorry, I don’t recall the exact name).

lv_line saves only a pointer to the point array. So the point array should live while you are using the line. That’s why the point array is static in the examples.

If ext->point_array in lv_line_set_points() is allocated/reallocated memory
and copy the data from the point array const lv_point_t point_a[],
( Instead of a maker must creating the static point array )

When a maker call lv_line_set_points(),
I think it will help the maker escape from dynamic line drawing problems, or not?

Yes, but it would have the side effect of consuming extra memory (since there would be two copies of the array at some point). In most cases it shouldn’t be a problem to keep track of the array and make sure it stays in scope.

First of all thank you for your suggestions and help. I figured out the problem: I was creating the screen and the page in a function, which is called. Of course, the point array, created in that function, is destroyed in the end and the pointer has nothing to point on.

Making the point array global fixed the problem to draw one line, without making the points const.

The final solution for my problem of drawing multiple lines is so far not very elegant: As there is no vector of arrays in C++, and it seems, the std:array makes some problems for me, i needed to use a global 2D-array, which i need to give a fixed size - large enough so it will not be a constraint.

The solution to modify the lv_line_set_points() function to copy the array instead of handling a pointer, as far as i understand it, leads to the allocation of additional memory, everytime the function is used, which leads to the consuption of all the memory at some point. Is that correct?

It won’t consume all the memory (as the next invocation of the function would free the memory allocated by the previous invocation) but it would consume more than the current setup, which is more flexible, as you can manage your memory in the best way possible for your target platform.

Just in case, can you tell me how exactly i need to modify the lv_line_set_points function to have the point_a[] array copied, instead of “pointered” ?

Thank you very much!

It would be better to do it in your own project instead of modifying LittlevGL, because when you modify LittlevGL it can make it harder to update easily. Plus, it differentiates your codebase from what the rest of the community is using.

You basically need to create a wrapper that allocates a buffer for the points using lv_mem_alloc. Here’s an untested example that would only work for a single chart. If you wanted this to work with multiple charts, it would be better to extend the ext_attr structure to hold the last_point variable.

void lv_line_set_points_alloc(lv_obj_t * line, const lv_point_t *point_a, uint16_t point_num)
{
    static lv_point_t *last_points = NULL;

    lv_point_t *new_points = lv_mem_alloc(sizeof(lv_point_t)*point_num);

    if(new_points == NULL)
        return;

    /* Copy the array to the allocated buffer */
    memcpy(new_points, point_a, sizeof(lv_point_t)*point_num);
    lv_line_set_points(line, new_points, point_num);

    /* Free the buffer from the last call */
    if(last_points != NULL)
        lv_mem_free(last_points);

    last_points = new_points;
}
1 Like