1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-21 06:53:01 +08:00
lvgl/src/lv_draw/lv_draw_arc.c

272 lines
9.2 KiB
C
Raw Normal View History

/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_arc.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static uint16_t fast_atan2(int x, int y);
2019-06-06 06:05:40 +02:00
static void ver_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color,
lv_opa_t opa);
static void hor_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color,
lv_opa_t opa);
2018-06-09 08:45:38 +02:00
static bool deg_test_norm(uint16_t deg, uint16_t start, uint16_t end);
static bool deg_test_inv(uint16_t deg, uint16_t start, uint16_t end);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Draw an arc. (Can draw pie too with great thickness.)
* @param center_x the x coordinate of the center of the arc
* @param center_y the y coordinate of the center of the arc
* @param radius the radius of the arc
2018-06-08 10:26:10 +02:00
* @param mask the arc will be drawn only in this mask
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
2018-06-08 10:26:10 +02:00
* @param style style of the arc (`body.thickness`, `body.main_color`, `body.opa` is used)
2018-06-14 13:08:19 +02:00
* @param opa_scale scale down all opacities by the factor
*/
2018-06-08 10:26:10 +02:00
void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, const lv_area_t * mask,
2019-06-06 06:05:40 +02:00
uint16_t start_angle, uint16_t end_angle, const lv_style_t * style, lv_opa_t opa_scale)
{
lv_coord_t thickness = style->line.width;
2018-06-19 09:49:58 +02:00
if(thickness > radius) thickness = radius;
2018-06-08 10:26:10 +02:00
lv_coord_t r_out = radius;
2019-04-04 07:15:40 +02:00
lv_coord_t r_in = r_out - thickness;
int16_t deg_base;
int16_t deg;
lv_coord_t x_start[4];
lv_coord_t x_end[4];
2018-09-20 22:16:30 +02:00
lv_color_t color = style->line.color;
2019-06-06 06:05:40 +02:00
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.opa : (uint16_t)((uint16_t)style->body.opa * opa_scale) >> 8;
2018-06-09 08:45:38 +02:00
bool (*deg_test)(uint16_t, uint16_t, uint16_t);
2019-04-04 07:15:40 +02:00
if(start_angle <= end_angle)
deg_test = deg_test_norm;
else
deg_test = deg_test_inv;
if(deg_test(270, start_angle, end_angle))
2019-07-23 08:23:20 +02:00
hor_line(center_x - r_out + 1, center_y, mask, thickness - 1, color, opa); /*Left Middle*/
2019-04-04 07:15:40 +02:00
if(deg_test(90, start_angle, end_angle))
2019-07-23 08:23:20 +02:00
hor_line(center_x + r_in, center_y, mask, thickness - 1, color, opa); /*Right Middle*/
2019-04-04 07:15:40 +02:00
if(deg_test(180, start_angle, end_angle))
2019-07-23 08:23:20 +02:00
ver_line(center_x, center_y - r_out + 1, mask, thickness - 1, color, opa); /*Top Middle*/
2019-04-04 07:15:40 +02:00
if(deg_test(0, start_angle, end_angle))
2019-07-23 08:23:20 +02:00
ver_line(center_x, center_y + r_in, mask, thickness - 1, color, opa); /*Bottom middle*/
uint32_t r_out_sqr = r_out * r_out;
2019-04-04 07:15:40 +02:00
uint32_t r_in_sqr = r_in * r_in;
int16_t xi;
int16_t yi;
for(yi = -r_out; yi < 0; yi++) {
x_start[0] = LV_COORD_MIN;
x_start[1] = LV_COORD_MIN;
x_start[2] = LV_COORD_MIN;
x_start[3] = LV_COORD_MIN;
2019-04-04 07:15:40 +02:00
x_end[0] = LV_COORD_MIN;
x_end[1] = LV_COORD_MIN;
x_end[2] = LV_COORD_MIN;
x_end[3] = LV_COORD_MIN;
2018-06-19 09:49:58 +02:00
for(xi = -r_out; xi < 0; xi++) {
uint32_t r_act_sqr = xi * xi + yi * yi;
if(r_act_sqr > r_out_sqr) continue;
2019-04-04 07:15:40 +02:00
deg_base = fast_atan2(xi, yi) - 180;
2018-06-19 09:49:58 +02:00
deg = 180 + deg_base;
if(deg_test(deg, start_angle, end_angle)) {
if(x_start[0] == LV_COORD_MIN) x_start[0] = xi;
} else if(x_start[0] != LV_COORD_MIN && x_end[0] == LV_COORD_MIN) {
x_end[0] = xi - 1;
}
deg = 360 - deg_base;
if(deg_test(deg, start_angle, end_angle)) {
if(x_start[1] == LV_COORD_MIN) x_start[1] = xi;
} else if(x_start[1] != LV_COORD_MIN && x_end[1] == LV_COORD_MIN) {
x_end[1] = xi - 1;
}
deg = 180 - deg_base;
if(deg_test(deg, start_angle, end_angle)) {
if(x_start[2] == LV_COORD_MIN) x_start[2] = xi;
} else if(x_start[2] != LV_COORD_MIN && x_end[2] == LV_COORD_MIN) {
x_end[2] = xi - 1;
}
deg = deg_base;
if(deg_test(deg, start_angle, end_angle)) {
if(x_start[3] == LV_COORD_MIN) x_start[3] = xi;
} else if(x_start[3] != LV_COORD_MIN && x_end[3] == LV_COORD_MIN) {
x_end[3] = xi - 1;
}
2019-04-04 07:15:40 +02:00
if(r_act_sqr < r_in_sqr)
break; /*No need to continue the iteration in x once we found the inner edge of the
arc*/
}
2018-06-19 09:49:58 +02:00
if(x_start[0] != LV_COORD_MIN) {
if(x_end[0] == LV_COORD_MIN) x_end[0] = xi - 1;
hor_line(center_x + x_start[0], center_y + yi, mask, x_end[0] - x_start[0], color, opa);
}
2018-06-19 09:49:58 +02:00
if(x_start[1] != LV_COORD_MIN) {
if(x_end[1] == LV_COORD_MIN) x_end[1] = xi - 1;
hor_line(center_x + x_start[1], center_y - yi, mask, x_end[1] - x_start[1], color, opa);
}
2018-06-19 09:49:58 +02:00
if(x_start[2] != LV_COORD_MIN) {
if(x_end[2] == LV_COORD_MIN) x_end[2] = xi - 1;
2019-06-06 06:05:40 +02:00
hor_line(center_x - x_end[2], center_y + yi, mask, LV_MATH_ABS(x_end[2] - x_start[2]), color, opa);
2018-06-19 09:49:58 +02:00
}
2018-06-19 09:49:58 +02:00
if(x_start[3] != LV_COORD_MIN) {
if(x_end[3] == LV_COORD_MIN) x_end[3] = xi - 1;
2019-06-06 06:05:40 +02:00
hor_line(center_x - x_end[3], center_y - yi, mask, LV_MATH_ABS(x_end[3] - x_start[3]), color, opa);
2018-06-19 09:49:58 +02:00
}
#if LV_ANTIALIAS
/*TODO*/
#endif
}
}
static uint16_t fast_atan2(int x, int y)
{
// Fast XY vector to integer degree algorithm - Jan 2011 www.RomanBlack.com
// Converts any XY values including 0 to a degree value that should be
// within +/- 1 degree of the accurate value without needing
// large slow trig functions like ArcTan() or ArcCos().
// NOTE! at least one of the X or Y values must be non-zero!
// This is the full version, for all 4 quadrants and will generate
// the angle in integer degrees from 0-360.
// Any values of X and Y are usable including negative values provided
// they are between -1456 and 1456 so the 16bit multiply does not overflow.
2018-06-19 09:49:58 +02:00
unsigned char negflag;
unsigned char tempdegree;
unsigned char comp;
2019-07-23 08:23:20 +02:00
unsigned int degree; /*this will hold the result*/
2018-06-19 09:49:58 +02:00
unsigned int ux;
unsigned int uy;
2019-07-23 08:23:20 +02:00
/*Save the sign flags then remove signs and get XY as unsigned ints*/
2018-06-19 09:49:58 +02:00
negflag = 0;
if(x < 0) {
2019-07-23 08:23:20 +02:00
negflag += 0x01; /*x flag bit*/
x = (0 - x); /*is now +*/
2018-06-19 09:49:58 +02:00
}
2019-07-23 08:23:20 +02:00
ux = x; /*copy to unsigned var before multiply*/
2018-06-19 09:49:58 +02:00
if(y < 0) {
2019-07-23 08:23:20 +02:00
negflag += 0x02; /*y flag bit*/
y = (0 - y); /*is now +*/
2018-06-19 09:49:58 +02:00
}
2019-07-23 08:23:20 +02:00
uy = y; /*copy to unsigned var before multiply*/
2018-06-19 09:49:58 +02:00
2019-07-23 08:23:20 +02:00
/*1. Calc the scaled "degrees"*/
2018-06-19 09:49:58 +02:00
if(ux > uy) {
2019-07-23 08:23:20 +02:00
degree = (uy * 45) / ux; /*degree result will be 0-45 range*/
negflag += 0x10; /*octant flag bit*/
2018-06-19 09:49:58 +02:00
} else {
2019-07-23 08:23:20 +02:00
degree = (ux * 45) / uy; /*degree result will be 0-45 range*/
2018-06-19 09:49:58 +02:00
}
2019-07-23 08:23:20 +02:00
/*2. Compensate for the 4 degree error curve*/
2019-04-04 07:15:40 +02:00
comp = 0;
2019-07-23 08:23:20 +02:00
tempdegree = degree; /*use an unsigned char for speed!*/
if(tempdegree > 22) { /*if top half of range*/
2018-06-19 09:49:58 +02:00
if(tempdegree <= 44) comp++;
if(tempdegree <= 41) comp++;
if(tempdegree <= 37) comp++;
2019-07-23 08:23:20 +02:00
if(tempdegree <= 32) comp++; /*max is 4 degrees compensated*/
} else { /*else is lower half of range*/
2018-06-19 09:49:58 +02:00
if(tempdegree >= 2) comp++;
if(tempdegree >= 6) comp++;
if(tempdegree >= 10) comp++;
2019-07-23 08:23:20 +02:00
if(tempdegree >= 15) comp++; /*max is 4 degrees compensated*/
2018-06-19 09:49:58 +02:00
}
2019-07-23 10:52:53 -03:00
degree += comp; /*degree is now accurate to +/- 1 degree!*/
2018-06-19 09:49:58 +02:00
2019-07-23 08:23:20 +02:00
/*Invert degree if it was X>Y octant, makes 0-45 into 90-45*/
if(negflag & 0x10) degree = (90 - degree);
2018-06-19 09:49:58 +02:00
2019-07-23 08:23:20 +02:00
/*3. Degree is now 0-90 range for this quadrant,*/
/*need to invert it for whichever quadrant it was in*/
if(negflag & 0x02) { /*if -Y*/
if(negflag & 0x01) /*if -Y -X*/
2018-06-19 09:49:58 +02:00
degree = (180 + degree);
2019-07-23 08:23:20 +02:00
else /*else is -Y +X*/
2018-06-19 09:49:58 +02:00
degree = (180 - degree);
2019-07-23 08:23:20 +02:00
} else { /*else is +Y*/
if(negflag & 0x01) /*if +Y -X*/
2018-06-19 09:49:58 +02:00
degree = (360 - degree);
}
return degree;
}
/**********************
* STATIC FUNCTIONS
**********************/
2019-06-06 06:05:40 +02:00
static void ver_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa)
{
2018-06-19 09:49:58 +02:00
lv_area_t area;
lv_area_set(&area, x, y, x, y + len);
2019-02-13 01:40:22 +01:00
lv_draw_fill(&area, mask, color, opa);
}
2019-06-06 06:05:40 +02:00
static void hor_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa)
{
2018-06-19 09:49:58 +02:00
lv_area_t area;
lv_area_set(&area, x, y, x + len, y);
2019-02-13 01:40:22 +01:00
lv_draw_fill(&area, mask, color, opa);
}
2018-06-09 08:45:38 +02:00
static bool deg_test_norm(uint16_t deg, uint16_t start, uint16_t end)
{
2019-04-04 07:15:40 +02:00
if(deg >= start && deg <= end)
return true;
else
return false;
2018-06-09 08:45:38 +02:00
}
static bool deg_test_inv(uint16_t deg, uint16_t start, uint16_t end)
{
2018-06-19 09:49:58 +02:00
if(deg >= start || deg <= end) {
return true;
2019-04-04 07:15:40 +02:00
} else
return false;
2018-06-09 08:45:38 +02:00
}