diff --git a/demos/vector_graphic/lv_demo_vector_graphic.c b/demos/vector_graphic/lv_demo_vector_graphic.c index 13ed3c888a..e9ade77c61 100644 --- a/demos/vector_graphic/lv_demo_vector_graphic.c +++ b/demos/vector_graphic/lv_demo_vector_graphic.c @@ -189,6 +189,37 @@ static void draw_blend(lv_vector_dsc_t * ctx, lv_vector_path_t * path) lv_vector_dsc_add_path(ctx, path); // draw a path } +static void draw_arc(lv_vector_dsc_t * ctx, lv_vector_path_t * path) +{ + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_area_t rect = {100, 0, 150, 50}; + lv_vector_dsc_set_fill_color(ctx, lv_color_lighten(lv_color_black(), 50)); + lv_vector_clear_area(ctx, &rect); // clear screen + + lv_fpoint_t p = {100, 50}; /* Center */ + lv_vector_dsc_set_stroke_color(ctx, lv_color_make(0x00, 0xff, 0xff)); + lv_vector_dsc_set_stroke_opa(ctx, LV_OPA_COVER); + lv_vector_dsc_set_stroke_width(ctx, 2.0f); + lv_vector_dsc_set_stroke_dash(ctx, NULL, 0); + lv_vector_dsc_set_fill_color32(ctx, lv_color32_make(0xFF, 0x00, 0x00, 0x80)); + + lv_vector_path_move_to(path, &p); + lv_fpoint_t temp = {p.x + 50, p.y}; + lv_vector_path_line_to(path, &temp); + lv_vector_path_append_arc(path, &p, 50, 0, -90, false); + lv_vector_path_line_to(path, &p); + lv_vector_path_close(path); + + lv_vector_dsc_add_path(ctx, path); + + /* Below code has same effect as above one. */ + lv_vector_path_clear(path); + lv_vector_path_append_arc(path, &p, 50, 45, 45, true); + lv_vector_dsc_add_path(ctx, path); // draw a path +} + static void draw_vector(lv_layer_t * layer) { lv_vector_dsc_t * ctx = lv_vector_dsc_create(layer); @@ -205,7 +236,7 @@ static void draw_vector(lv_layer_t * layer) draw_radial_gradient(ctx, path); draw_gradient(ctx, path); draw_blend(ctx, path); - + draw_arc(ctx, path); lv_draw_vector(ctx); // submit draw lv_vector_path_delete(path); lv_vector_dsc_delete(ctx); diff --git a/scripts/code-format.cfg b/scripts/code-format.cfg index 0bca2ba76a..0f6fe5fd69 100644 --- a/scripts/code-format.cfg +++ b/scripts/code-format.cfg @@ -47,3 +47,5 @@ --exclude=../tests/unity/unity_support.c --exclude=../tests/unity/unity_support.h --exclude=../tests/test_images +--exclude=../tests/build_test_defheap +--exclude=../tests/build_test_sysheap diff --git a/src/draw/lv_draw_vector.c b/src/draw/lv_draw_vector.c index 400e09f4d3..854f1d6fdb 100644 --- a/src/draw/lv_draw_vector.c +++ b/src/draw/lv_draw_vector.c @@ -14,6 +14,16 @@ #include "../stdlib/lv_string.h" #include #include +#include + +#define MATH_PI 3.14159265358979323846f +#define MATH_HALF_PI 1.57079632679489661923f + +#define DEG_TO_RAD 0.017453292519943295769236907684886f +#define RAD_TO_DEG 57.295779513082320876798154814105f + +#define MATH_RADIANS(deg) ((deg) * DEG_TO_RAD) +#define MATH_DEGRESS(rad) ((rad) * RAD_TO_DEG) /********************* * DEFINES @@ -470,6 +480,92 @@ void lv_vector_path_append_circle(lv_vector_path_t * path, const lv_fpoint_t * c lv_vector_path_close(path); } +/** + * Add a arc to the path + * @param path pointer to a path + * @param c pointer to a `lv_fpoint_t` variable for center of the circle + * @param radius the radius for arc + * @param start_angle the start angle for arc + * @param sweep the sweep angle for arc, could be negative + * @param pie true: draw a pie, false: draw a arc + */ +void lv_vector_path_append_arc(lv_vector_path_t * path, const lv_fpoint_t * c, float radius, float start_angle, + float sweep, bool pie) +{ + float cx = c->x; + float cy = c->y; + + /* just circle */ + if(sweep >= 360.0f || sweep <= -360.0f) { + lv_vector_path_append_circle(path, c, radius, radius); + return; + } + + start_angle = MATH_RADIANS(start_angle); + sweep = MATH_RADIANS(sweep); + + int n_curves = (int)ceil(fabsf(sweep / MATH_HALF_PI)); + float sweep_sign = sweep < 0 ? -1.f : 1.f; + float fract = fmodf(sweep, MATH_HALF_PI); + fract = (fabsf(fract) < FLT_EPSILON) ? MATH_HALF_PI * sweep_sign : fract; + + /* Start from here */ + lv_fpoint_t start = { + .x = radius * cosf(start_angle), + .y = radius * sinf(start_angle), + }; + + if(pie) { + lv_vector_path_move_to(path, &(lv_fpoint_t) { + cx, cy + }); + lv_vector_path_line_to(path, &(lv_fpoint_t) { + start.x + cx, start.y + cy + }); + } + else { + lv_vector_path_move_to(path, &(lv_fpoint_t) { + start.x + cx, start.y + cy + }); + } + + for(int i = 0; i < n_curves; ++i) { + float end_angle = start_angle + ((i != n_curves - 1) ? MATH_HALF_PI * sweep_sign : fract); + float end_x = radius * cosf(end_angle); + float end_y = radius * sinf(end_angle); + + /* variables needed to calculate bezier control points */ + + /** get bezier control points using article: + * (http://itc.ktu.lt/index.php/ITC/article/view/11812/6479) + */ + float ax = start.x; + float ay = start.y; + float bx = end_x; + float by = end_y; + float q1 = ax * ax + ay * ay; + float q2 = ax * bx + ay * by + q1; + float k2 = (4.0f / 3.0f) * ((sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx)); + + /* Next start point is the current end point */ + start.x = end_x; + start.y = end_y; + + end_x += cx; + end_y += cy; + + lv_fpoint_t ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy}; + lv_fpoint_t ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy}; + lv_fpoint_t end = {end_x, end_y}; + lv_vector_path_cubic_to(path, &ctrl1, &ctrl2, &end); + start_angle = end_angle; + } + + if(pie) { + lv_vector_path_close(path); + } +} + void lv_vector_path_append_path(lv_vector_path_t * path, const lv_vector_path_t * subpath) { uint32_t ops_size = lv_array_size(&path->ops); diff --git a/src/draw/lv_draw_vector.h b/src/draw/lv_draw_vector.h index 577ef65e49..675359d3d1 100644 --- a/src/draw/lv_draw_vector.h +++ b/src/draw/lv_draw_vector.h @@ -308,6 +308,18 @@ void lv_vector_path_append_rect(lv_vector_path_t * path, const lv_area_t * rect, */ void lv_vector_path_append_circle(lv_vector_path_t * path, const lv_fpoint_t * c, float rx, float ry); +/** + * Add a arc to the path + * @param path pointer to a path + * @param c pointer to a `lv_fpoint_t` variable for center of the circle + * @param radius the radius for arc + * @param start_angle the start angle for arc + * @param sweep the sweep angle for arc, could be negative + * @param pie true: draw a pie, false: draw a arc + */ +void lv_vector_path_append_arc(lv_vector_path_t * path, const lv_fpoint_t * c, float radius, float start_angle, + float sweep, bool pie); + /** * Add an sub path to the path * @param path pointer to a path diff --git a/tests/ref_imgs/draw/vector_draw_shapes.png b/tests/ref_imgs/draw/vector_draw_shapes.png index a42c3c7a3e..82a5be3a62 100644 Binary files a/tests/ref_imgs/draw/vector_draw_shapes.png and b/tests/ref_imgs/draw/vector_draw_shapes.png differ diff --git a/tests/src/test_cases/draw/test_draw_vector.c b/tests/src/test_cases/draw/test_draw_vector.c index 75c5292ea0..dcf747d0fa 100644 --- a/tests/src/test_cases/draw/test_draw_vector.c +++ b/tests/src/test_cases/draw/test_draw_vector.c @@ -94,6 +94,38 @@ static void draw_shapes(lv_layer_t * layer) lv_vector_dsc_set_fill_rule(ctx, LV_VECTOR_FILL_EVENODD); lv_vector_dsc_add_path(ctx, path); + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + lv_vector_dsc_set_blend_mode(ctx, LV_VECTOR_BLEND_SRC_OVER); + + rect = (lv_area_t) { + 500, 50, 550, 100 + }; + lv_vector_dsc_set_fill_color(ctx, lv_color_lighten(lv_color_black(), 50)); + lv_vector_clear_area(ctx, &rect); // clear screen + + lv_fpoint_t p = {500, 100}; /* Center */ + lv_vector_dsc_set_stroke_color(ctx, lv_color_make(0x00, 0xff, 0xff)); + lv_vector_dsc_set_stroke_opa(ctx, LV_OPA_COVER); + lv_vector_dsc_set_stroke_width(ctx, 2.0f); + lv_vector_dsc_set_stroke_dash(ctx, NULL, 0); + lv_vector_dsc_set_fill_opa(ctx, LV_OPA_TRANSP); + lv_vector_path_move_to(path, &p); + lv_fpoint_t temp = {p.x + 50, p.y}; + lv_vector_path_line_to(path, &temp); + lv_vector_path_append_arc(path, &p, 50, 0, -90, false); + lv_vector_path_line_to(path, &p); + lv_vector_path_close(path); + + lv_vector_dsc_add_path(ctx, path); + + /* Below code has same effect as above one but with solid fill */ + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0xFF, 0x00, 0x00)); + lv_vector_dsc_set_fill_opa(ctx, LV_OPA_COVER); + lv_vector_path_clear(path); + lv_vector_path_append_arc(path, &p, 50, 45, 45, true); + lv_vector_dsc_add_path(ctx, path); // draw a path + lv_draw_vector(ctx); lv_vector_path_delete(path); lv_vector_dsc_delete(ctx);