I want to Filled triangle

Description

I used a line drawing function to spell out a triangle, but I don’t know how to fill it and turn it into solid.

What MCU/Processor/Board and compiler are you using?

arm

What do you want to achieve?

I used a line drawing function to spell out a triangle.

What have you tried so far?

sorry,I don’t know what to do.

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.
/*void my_draw_text (lv_obj_t * par, lv_obj_t * label, lv_coord_t x, lv_coord_t y, char * str, lv_align_t alignment)
{
static lv_style_t style_text;
lv_style_copy(&style_text, &lv_style_plain);
style_text.text.color = LV_COLOR_RED;

/*Create a Label on the currently active screen*/
label =  lv_label_create(par, NULL);

/*Modify the Label's text*/
lv_label_set_text(label, str);
lv_label_set_style(label,LV_LABEL_STYLE_MAIN,&style_text);
/* Align the Label to the center
 * NULL means align on parent (which is the screen now)
 * 0, 0 at the end means an x, y offset after alignment*/
lv_obj_align(label, par, alignment, x, y); 

}*/

/**/
The code block(s) should be formatted like:

/*You code here*/

Screenshot and/or video

If possible, add screenshots and/or videos about the current state.

You might want to replace the triangle with a custom bitmap.

I notice that your code snippet isn’t really relevant to the issue (i.e. it doesn’t contain the line drawing code).

I can’t remember whether the lv_canvas_draw_polygon API supports filling or not. It might be worth looking into that, although you will need an lv_canvas object for each polygon (or one tall canvas that you draw on in multiple places).

Or you could just use an image like @barbiani suggested; that is probably more efficient with regards to RAM usage.

At current version (6.0.2)
as @barbiani and @embeddedt suggest, you can create by 2 methods.

However before the next version maybe filled-triangle is coming.
You can use the following temporary code.

Code

//------------------------------
// lv_triangle api
lv_obj_t * lv_triangle_create(lv_obj_t * par, const lv_obj_t * copy);
void lv_triangle_set_points(lv_obj_t* triangle, const lv_point_t * points);
void lv_triangle_set_auto_size(lv_obj_t * triangle, bool en);
bool lv_triangle_get_auto_size(const lv_obj_t * triangle);
//------------------------------

typedef struct {
  lv_point_t points[3];
  bool       auto_size;
} lv_triangle_ext_t;
static lv_design_cb_t ancestor_design;
static bool lv_triangle_design(lv_obj_t* obj, const lv_area_t* mask, lv_design_mode_t mode);

lv_obj_t * lv_triangle_create(lv_obj_t * par, const lv_obj_t * copy)
{
  lv_obj_t * new_triangle = lv_obj_create(par, copy);
  lv_mem_assert(new_triangle);
  if (new_triangle == NULL) return NULL;
  lv_triangle_ext_t * ext = (lv_triangle_ext_t *)lv_obj_allocate_ext_attr(new_triangle, sizeof(lv_triangle_ext_t));
  lv_mem_assert(ext);
  if (ext == NULL) return NULL;
  ext->auto_size = 1;
  lv_obj_set_design_cb(new_triangle, lv_triangle_design);
  lv_obj_set_click(new_triangle, false);

  if (copy == NULL) {
    lv_obj_set_size(new_triangle, LV_DPI, LV_DPI);  /*Auto size is enables, but set default size until no points are added*/
    lv_obj_set_style(new_triangle, NULL); /*Inherit parent's style*/
  }
  else {
    lv_triangle_ext_t * copy_ext = (lv_triangle_ext_t *)lv_obj_get_ext_attr(copy);
    lv_triangle_set_points(new_triangle, copy_ext->points);
    lv_obj_set_style(new_triangle, lv_obj_get_style(copy));
    lv_obj_refresh_style(new_triangle);
  }
  return new_triangle;
}

void lv_triangle_set_points(lv_obj_t* triangle, const lv_point_t * points)
{
  lv_triangle_ext_t* ext = (lv_triangle_ext_t*) lv_obj_get_ext_attr(triangle);
  ext->points[0] = points[0];
  ext->points[1] = points[1];
  ext->points[2] = points[2];

  if(ext->auto_size != 0) {
    uint16_t i;
    lv_coord_t xmax = LV_COORD_MIN;
    lv_coord_t ymax = LV_COORD_MIN;
    for(i = 0; i < 3; i++) {
        xmax = LV_MATH_MAX(points[i].x, xmax);
        ymax = LV_MATH_MAX(points[i].y, ymax);
    }
    const lv_style_t * style = lv_obj_get_style(triangle);
    lv_obj_set_size(triangle, xmax + style->body.border.width, ymax + style->body.border.width);
  }
  lv_obj_invalidate(triangle);
}

void lv_triangle_set_auto_size(lv_obj_t * triangle, bool en)
{
  lv_triangle_ext_t* ext = (lv_triangle_ext_t*) lv_obj_get_ext_attr(triangle);
  if(ext->auto_size == en) return;
  ext->auto_size = en == false ? 0 : 1;
  if(en) lv_triangle_set_points(triangle, ext->points);
}

bool lv_triangle_get_auto_size(const lv_obj_t * triangle){
  lv_triangle_ext_t* ext = (lv_triangle_ext_t*) lv_obj_get_ext_attr(triangle);
  return ext->auto_size;
}

static bool lv_triangle_design(lv_obj_t* triangle, const lv_area_t* mask, lv_design_mode_t mode)
{
 if (mode == LV_DESIGN_COVER_CHK) {
    return false;
  } else if (mode == LV_DESIGN_DRAW_MAIN) {
    lv_triangle_ext_t* ext = (lv_triangle_ext_t*) lv_obj_get_ext_attr(triangle);
    if( (ext->points[0].x == ext->points[1].x && ext->points[0].y == ext->points[1].y) ||
        (ext->points[0].x == ext->points[2].x && ext->points[0].y == ext->points[2].y) ||
        (ext->points[1].x == ext->points[2].x && ext->points[1].y == ext->points[2].y))
      return true;
    const lv_style_t* style = lv_obj_get_style(triangle);
    lv_opa_t opa_scale = lv_obj_get_opa_scale(triangle);
    lv_area_t offset; lv_obj_get_coords(triangle, &offset);
    lv_point_t points[3] = {{ext->points[0].x + offset.x1, ext->points[0].y + offset.y1},
                            {ext->points[1].x + offset.x1, ext->points[1].y + offset.y1},
                            {ext->points[2].x + offset.x1, ext->points[2].y + offset.y1}};

    if(style->body.opa != 0)
      lv_draw_triangle( points, mask, style, opa_scale);

    if(style->line.width) {
      lv_style_t style_line; lv_style_copy(&style_line, style);
      style_line.line.width = style->body.border.width;
      style_line.line.color = style->body.border.color;
      
      lv_draw_line(&points[0], &points[1], mask, &style_line, opa_scale );
      lv_draw_line(&points[1], &points[2], mask, &style_line, opa_scale );
      lv_draw_line(&points[2], &points[0], mask, &style_line, opa_scale );
    }
  } 
  return true;
}

Usage

  static lv_style_t style;
    lv_style_copy(&style, &lv_style_plain);
    style.body.main_color = LV_COLOR_RED;       /* filled triangle color */
//    style.body.opa        = LV_OPA_TRANSP;    /* if don't want filled triangle */
//    style.body.border.width      = 5;                /* triangle with border */
//    style.body.border.color      = LV_COLOR_BLUE;
//    style.line.rounded    = true;
  
  lv_obj_t* triangle = lv_triangle_create(lv_scr_act(),NULL);
    lv_obj_set_style(triangle, &style);
    lv_point_t points[] = {{40,50}, {200,120}, {100,210}};
    lv_triangle_set_points(triangle, points);

Result

Without border
capture_00040

With border
capture_00041

1 Like

In v7.0 I’ll add the lv_polygon object.

1 Like

Thank you for your reply, I tried to use it, but I can’t fully understand it, I am very sorry that I am a beginner for littlevgl, so how it should be used.
thank you very much!

@barbiani @embeddedt Thank you very much for your reply, I will try to learn to use this method, thank you very much.

Oh ,it’s very good!!!

Is it possible to draw and fill a full screen triangle while using partial screen buffers?

Your question is not very clear, but the type/size of buffer you use in the driver does not matter. High-level LittlevGL APIs automatically divide the drawing operation as necessary to compose the final screen result.

Thanks. The answer was very clear.