text: /**
   * @file lv_draw_triangle.c
   *
   */
  
  /*********************
   *      INCLUDES
   *********************/
  #include "lv_draw_triangle.h"
  #include "../misc/lv_math.h"
  #include "../misc/lv_mem.h"
  
  /*********************
   *      DEFINES
   *********************/
  
  /**********************
   *      TYPEDEFS
   **********************/
  
  /**********************
   *  STATIC PROTOTYPES
   **********************/
  
  /**********************
   *  STATIC VARIABLES
   **********************/
  
  /**********************
   *      MACROS
   **********************/
  
  /**********************
   *   GLOBAL FUNCTIONS
   **********************/
  
  /**
   * Draw a triangle
   * @param points pointer to an array with 3 points
   * @param clip_area the triangle will be drawn only in this area
   * @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
   */
  void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip_area, const lv_draw_rect_dsc_t * draw_dsc)
  {
  #if LV_DRAW_COMPLEX
      lv_draw_polygon(points, 3, clip_area, draw_dsc);
  #else
      LV_UNUSED(points);
      LV_UNUSED(clip_area);
      LV_UNUSED(draw_dsc);
      LV_LOG_WARN("Can't draw triangle with LV_DRAW_COMPLEX == 0");
  #endif /*LV_DRAW_COMPLEX*/
  }
  
  /**
   * Draw a polygon. Only convex polygons are supported
   * @param points an array of points
   * @param point_cnt number of points
   * @param clip_area polygon will be drawn only in this area
   * @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
   */
  void lv_draw_polygon(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * clip_area,
                       const lv_draw_rect_dsc_t * draw_dsc)
  {
  #if LV_DRAW_COMPLEX
      if(point_cnt < 3) return;
      if(points == NULL) return;
  
      /*Join adjacent points if they are on the same coordinate*/
      lv_point_t * p = lv_mem_buf_get(point_cnt * sizeof(lv_point_t));
      if(p == NULL) return;
      uint16_t i;
      uint16_t pcnt = 0;
      p[0] = points[0];
      for(i = 0; i < point_cnt - 1; i++) {
          if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) {
              p[pcnt] = points[i];
              pcnt++;
          }
      }
      /*The first and the last points are also adjacent*/
      if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) {
          p[pcnt] = points[point_cnt - 1];
          pcnt++;
      }
  
      point_cnt = pcnt;
      if(point_cnt < 3) {
          lv_mem_buf_release(p);
          return;
      }
  
      lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN};
  
      for(i = 0; i < point_cnt; i++) {
          poly_coords.x1 = LV_MIN(poly_coords.x1, p[i].x);
          poly_coords.y1 = LV_MIN(poly_coords.y1, p[i].y);
          poly_coords.x2 = LV_MAX(poly_coords.x2, p[i].x);
          poly_coords.y2 = LV_MAX(poly_coords.y2, p[i].y);
      }
  
      bool is_common;
      lv_area_t poly_mask;
      is_common = _lv_area_intersect(&poly_mask, &poly_coords, clip_area);
      if(!is_common) {
          lv_mem_buf_release(p);
          return;
      }
      /*Find the lowest point*/
      lv_coord_t y_min = p[0].y;
      int16_t y_min_i = 0;
  
      for(i = 1; i < point_cnt; i++) {
          if(p[i].y < y_min) {
              y_min = p[i].y;
              y_min_i = i;
          }
      }
  
      lv_draw_mask_line_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_line_param_t) * point_cnt);
      lv_draw_mask_line_param_t * mp_next = mp;
  
      int32_t i_prev_left = y_min_i;
      int32_t i_prev_right = y_min_i;
      int32_t i_next_left;
      int32_t i_next_right;
      uint32_t mask_cnt = 0;
  
      /*Get the index of the left and right points*/
      i_next_left = y_min_i - 1;
      if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
  
      i_next_right = y_min_i + 1;
      if(i_next_right > point_cnt - 1) i_next_right = 0;
  
      /**
       * Check if the order of points is inverted or not.
       * The normal case is when the left point is on `y_min_i - 1`
       * Explanation:
       *   if angle(p_left) < angle(p_right) -> inverted
       *   dy_left/dx_left < dy_right/dx_right
       *   dy_left * dx_right < dy_right * dx_left
       */
      lv_coord_t dxl = p[i_next_left].x - p[y_min_i].x;
      lv_coord_t dxr = p[i_next_right].x - p[y_min_i].x;
      lv_coord_t dyl = p[i_next_left].y - p[y_min_i].y;
      lv_coord_t dyr = p[i_next_right].y - p[y_min_i].y;
  
      bool inv = false;
      if(dyl * dxr < dyr * dxl) inv = true;
  
      do {
          if(!inv) {
              i_next_left = i_prev_left - 1;
              if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
  
              i_next_right = i_prev_right + 1;
              if(i_next_right > point_cnt - 1) i_next_right = 0;
          }
          else {
              i_next_left = i_prev_left + 1;
              if(i_next_left > point_cnt - 1) i_next_left = 0;
  
              i_next_right = i_prev_right - 1;
              if(i_next_right < 0) i_next_right = point_cnt + i_next_right;
          }
  
          if(p[i_next_left].y >=  p[i_prev_left].y) {
              if(p[i_next_left].y != p[i_prev_left].y &&
                 p[i_next_left].x !=  p[i_prev_left].x) {
                  lv_draw_mask_line_points_init(mp_next, p[i_prev_left].x, p[i_prev_left].y,
                                                p[i_next_left].x, p[i_next_left].y,
                                                LV_DRAW_MASK_LINE_SIDE_RIGHT);
                  lv_draw_mask_add(mp_next, mp);
                  mp_next++;
              }
              mask_cnt++;
              i_prev_left = i_next_left;
          }
  
          if(mask_cnt == point_cnt) break;
  
          if(p[i_next_right].y >=  p[i_prev_right].y) {
              if(p[i_next_right].y != p[i_prev_right].y &&
                 p[i_next_right].x !=  p[i_prev_right].x) {
  
                  lv_draw_mask_line_points_init(mp_next, p[i_prev_right].x, p[i_prev_right].y,
                                                p[i_next_right].x, p[i_next_right].y,
                                                LV_DRAW_MASK_LINE_SIDE_LEFT);
                  lv_draw_mask_add(mp_next, mp);
                  mp_next++;
              }
              mask_cnt++;
              i_prev_right = i_next_right;
          }
  
      } while(mask_cnt < point_cnt);
  
      lv_draw_rect(&poly_coords, clip_area, draw_dsc);
  
      lv_draw_mask_remove_custom(mp);
  
      lv_mem_buf_release(mp);
      lv_mem_buf_release(p);
  #else
      LV_UNUSED(points);
      LV_UNUSED(point_cnt);
      LV_UNUSED(clip_area);
      LV_UNUSED(draw_dsc);
      LV_LOG_WARN("Can't draw polygon with LV_DRAW_COMPLEX == 0");
  #endif /*LV_DRAW_COMPLEX*/
  }
  
  /**********************
   *   STATIC FUNCTIONS
   **********************/
  