Search code examples
cgradientcairotizentizen-wearable-sdk

Dynamiclly growing gradient on a circle


I started writing an application for tizen wearable 2.3.1. I wanted to add dynamically growing radiant on an arc (that grows until it becomes a circle). My circle is created using cairo2d.

I have been following this article about the approximation of cubic bezier curve. I have made some progress. I have a dynamic gradient for the first half of circle - but the gradient doesn't start from my X1,Y1, but it seems to grow from the center.

First step is to move the cairo center and rotate (so the center is in the middle of circle and X,Y are proper axis), then I count the angle based on the percentage and initialize the variables:

double current_angle= (2 * M_PI * percent) / 100;
double R = 74;
double K = 0.5522847498; // - magic number from article

// start and end point
double X1 = 0;
double Y1 = R;
double X4 = R * sin(current_angle);
double Y4 = R * cos(current_angle);
// bezier points
double X2 = X1 + K * R * sin(current_angle);
double Y2 = Y1 - K * R * cos(current_angle);
double X3 = X4 + K * R * sin(current_angle);
double Y3 = Y4 + K * R * cos(current_angle);

Then at the end, I create my pattern and draw my arc:

Evas_Coord_Point P1 = {X1, Y1};
Evas_Coord_Point P2 = {X2, Y2};
Evas_Coord_Point P3 = {X3, Y3};
Evas_Coord_Point P4 = {X4, Y4};

cairo_pattern_t *dynamic_pattern = cairo_pattern_create_mesh ();
cairo_mesh_pattern_begin_patch (dynamic_pattern);
cairo_mesh_pattern_move_to (dynamic_pattern, P1.x, P1.y);
cairo_mesh_pattern_curve_to (dynamic_pattern, P2.x, P2.y,  P3.x, P3.y, P4.x, P4.y);
cairo_mesh_pattern_set_corner_color_rgb (dynamic_pattern, 1, 1, 1, 1);
cairo_mesh_pattern_set_corner_color_rgb (dynamic_pattern, 0, 0, 1, 0);
cairo_mesh_pattern_end_patch (dynamic_pattern);
cairo_set_source(cairo, dynamic_pattern);

cairo_arc(cairo, 0, 0, cairo_circle_radius, 0, current_angle);
cairo_stroke(cairo);

and for the first few percentage values, I don't get anything, but then around 12% it starts to draw and expands in both directions. What am I doing wrong?

This graphics shows what I want to achieve (sorry for my not very professional image, but that's the best I can do).

enter image description here

EDIT: I did some testing and It starts working for angle >= 45.


Solution

  • Ok, I found a way to create Conical gradient, and with little twists I managed to make it working as dynamic gradient. The solution is to create patches (triangle cuts of circle) and set theirs gradients.

    patch sector method:

    static void sector_patch (cairo_pattern_t *pattern,
          double angle_A,
          double A_r, double A_g, double A_b,
          double angle_B,
          double B_r, double B_g, double B_b)
    {
        double r_sin_A, r_cos_A;
        double r_sin_B, r_cos_B;
        double h;
    
        r_sin_A = RADIUS * sin (angle_A);
        r_cos_A = RADIUS * cos (angle_A);
        r_sin_B = RADIUS * sin (angle_B);
        r_cos_B = RADIUS * cos (angle_B);
    
        h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
    
        cairo_mesh_pattern_begin_patch (pattern);
    
        cairo_mesh_pattern_move_to (pattern, CENTER_X, CENTER_Y);
        cairo_mesh_pattern_line_to (pattern,
                CENTER_X + r_cos_A,
                CENTER_Y + r_sin_A);
    
        cairo_mesh_pattern_curve_to (pattern,
                 CENTER_X + r_cos_A - h * r_sin_A,
                 CENTER_Y + r_sin_A + h * r_cos_A,
                 CENTER_X + r_cos_B + h * r_sin_B,
                 CENTER_Y + r_sin_B - h * r_cos_B,
                 CENTER_X + r_cos_B,
                 CENTER_Y + r_sin_B);
    
        cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 1, 1);
        cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, A_r, A_g, A_b);
        cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, B_r, B_g, B_b);
    
        cairo_mesh_pattern_end_patch (pattern);
    }
    

    and here is a method for crating gradient for full circle (if you want to draw the gradient add cairo_paint(cr); at the end of set gradient method)

    static void set_conical_gradient(cairo_t *cr, int width, int height)
    {
        cairo_pattern_t *pattern;
    
        pattern = cairo_pattern_create_mesh ();
        sector_patch (pattern,
          0,         1, 1, 1,
          M_PI/4,    0.9, 0.96, 0.87);
        sector_patch (pattern,
          M_PI/4,   0.9, 0.96, 0.87,
          M_PI/2,    0.81, 0.91, 0.75);
        sector_patch (pattern,
          M_PI/2,    0.81, 0.91, 0.75,
          3*M_PI/4,  0.7, 0.87, 0.63);
        sector_patch (pattern,
          3*M_PI/4,  0.7, 0.87, 0.63,
          M_PI,      0.62, 0.83, 0.5);
        sector_patch (pattern,
          -M_PI,     0.62, 0.83, 0.5,
          -3*M_PI/4, 0.53, 0.78, 0.38);
        sector_patch (pattern,
          -3*M_PI/4, 0.53, 0.78, 0.38,
          -M_PI/2,   0.43, 0.74, 0.25);
        sector_patch (pattern,
          -M_PI/2,   0.43, 0.74, 0.25,
          -M_PI/4,   0.38, 0.72, 0.18);
        sector_patch (pattern,
          -M_PI/4,   0.38, 0.72, 0.18,
          0,        0.34, 0.7, 0.13);
    
        cairo_set_source(cr, pattern);
    }