Learn graphics for theoretical gui with rust

Hello guys, I would like to show you my repository:

Its purpose is to help learn how the complex graphics we find in modern graphical user interfaces are drawn, and how the UI works.

I also hope that it is consistent with the goals mentioned at https://areweguiyet.com/

1 Like

Pie chart algorithm

To draw a pie chart - which is used to represent data in circular sections- there are several ways Algorithms in order to do this.
In this example we will use straight lines and mask to do this with Tiny-Skia.

We have user defined inputs:
x, y are the position of the pie chart, r is radius, pie_slices is represented data: (red, green, blue, alpha, value), full_pie is the total/full value, mask is mask layer, paint is shader/color in pixmap.

First, we draw a complete circle that serves as the background or the complementary part of the pie chart if the total data is less than the full value full_pie

    let mut bgcircle = PathBuilder::new();
    bgcircle.push_circle(x, y , r);
    let mut bgpaint = Paint::default();
    bgpaint.set_color_rgba8(bgcolor.0 as u8, bgcolor.1 as u8, bgcolor.2 as u8, bgcolor.3 as u8);
    bgpaint.anti_alias = true;
    let bgpath = bgcircle.finish().unwrap();
    pixmap.fill_path(
        &bgpath,
        &bgpaint,
        FillRule::EvenOdd,
        Transform::identity(),
        None
    );

piechart_bg

Now we draw the pie, by drawing a square and dividing it into 8 equal parts, each part has an angle at the point (x, y) measuring 45 degrees.

pie_chart_drawing_alogrithm_2

A line will be drawn whose starting point is (x,y) and whose ending point is (last_point_x , last_point_y ).

        pb.move_to(x , y );
        pb.line_to(last_point_x , last_point_y );

The ending point (last_point_x , last_point_y) is predetermined given the degree of the previous segment along with the degree of the current segment, and it will be on one of the sides of the square.

if degree > 0.0 && degree <= 45.0{
            new_point_x = x + (r * (degree/45.0));
            new_point_y = y-r
        }
        else if degree > 45.0 && degree <= 90.0 {
           new_point_x = x+r;
           new_point_y = y - (r * (90.0-degree )/45.0);
        }
        else if degree > 90.0 && degree <= 135.0{
            new_point_x = x+r;
            new_point_y = y + (r * (degree-90.0)/45.0 );
        } 
        else if degree > 135.0 && degree <= 180.0{
            new_point_x = x+r - (r * (degree-135.0)/45.0 );
            new_point_y = y+r;
        } 
        else if degree > 180.0 && degree <= 225.0 {
            new_point_x = x - (r * (degree-180.0)/45.0 );
            new_point_y = y+r;
        }
        else if  degree > 225.0 && degree <= 270.0{
            new_point_x = x - r;
            new_point_y = y+r - (r * (degree-225.0)/45.0 )
        }
        else if  degree > 270.0 && degree <= 315.0{
            new_point_x = x- r;
            new_point_y = y - (r * (degree-270.0)/45.0 );
        }
        else if  degree > 315.0 && degree <= 360.0{
            new_point_x = x-r + (r * (degree-315.0)/45.0 );
            new_point_y = y-r
        }

Then we draw other lines to close the section of the square

if used_degree>=0.0 && used_degree<45.0 {
            if degree > 45.0 && degree <= 135.0{
                pb.line_to(x+r , last_point_y );
            }
            else if degree > 135.0 && degree <= 225.0{
                pb.line_to(x+r , y-r );
                pb.line_to(x+r , y+r );
                pb.line_to(last_point_x , y+r );
            }
            else if degree > 225.0 && degree <= 315.0{
                pb.line_to(x+r , y-r );
                pb.line_to(x+r , y+r );
                pb.line_to(x-r , y+r );
                pb.line_to(x-r , last_point_y );
            }
            else if degree > 315.0 && degree <= 360.0{
                pb.line_to(x+r , y-r );
                pb.line_to(x+r , y+r );
                pb.line_to(x-r , y+r );
                pb.line_to(x-r , y-r );
                pb.line_to(last_point_x , y-r );
            }
        }
        else if  used_degree>=45.0 && used_degree<135.0 {
            if degree > 135.0 && degree <= 225.0{
                pb.line_to(x+r , y+r );
                pb.line_to(last_point_x , y+r );
            }
            else if degree > 225.0 && degree <= 315.0{
                pb.line_to(x+r , y+r );
                pb.line_to(x-r , y+r );
                pb.line_to(x-r , last_point_y );
            }
            else if degree > 315.0 && degree <= 360.0{
                pb.line_to(x+r , y+r );
                pb.line_to(x-r , y+r );
                pb.line_to(x-r , y-r );
                pb.line_to(last_point_x , y-r );
            }
        }
        else if used_degree>=135.0 && used_degree<225.0 {
            if degree > 225.0 && degree <= 315.0{
                pb.line_to(x-r , y+r );
                pb.line_to(x-r , last_point_y );
            }
            else if degree > 315.0 && degree <= 360.0{
                pb.line_to(x-r , y+r );
                pb.line_to(x-r , y-r );
            }
        }
        else if  used_degree>=225.0 && used_degree<315.0{
            if degree > 315.0 && degree <= 360.0{
                pb.line_to(x-r , y+r );
                pb.line_to(x-r , y-r );
                pb.line_to(last_point_x , y-r );
            }

        }

For example: When we draw the first segment whose degree is 60, the following values are as follows:

last_point_x = x, last_point_y = y-r, used_degree = 0.0, degree = 60.0

Note : degree = 360.0 * (the value from pie_slices's item / full_pie ) + used_degree =
360.0 * (60.0 / 360.0) + 0.0 = 60
, full_pie = 360.0
In this case:

        else if degree > 45.0 && degree <= 90.0 {
           new_point_x = x+r;
           new_point_y = y - (r * (90.0-degree )/45.0);
        }
if used_degree>=0.0 && used_degree<45.0 {
            if degree > 45.0 && degree <= 135.0{
                pb.line_to(x+r , last_point_y );
            }

pie_chart_drawing_alogrithm_3

The variables are then assigned values ​​that are used to plot the next section

last_point_x = new_point_x = x+r, last_point_y = new_point_y = y - (r * (90.0-degree )/45.0), used_degree = degree = 60.0

When we draw the second segment whose degree is 95, the following values are as follows:

last_point_x = x+r, last_point_y = y - (r * (90.0-60.0 )/45.0), used_degree = 60.0,
degree = 360.0 * (the value from pie_slices's item / full_pie ) + used_degree = 360.0 * (95.0 / 360) + 60.0 = 155.0

In this case:

        else if degree > 135.0 && degree <= 180.0{
            new_point_x = x+r - (r * (degree-135.0)/45.0 );
            new_point_y = y+r;
        } 
        else if  used_degree>=45.0 && used_degree<135.0 {
            if degree > 135.0 && degree <= 225.0{
                pb.line_to(x+r , y+r );
                pb.line_to(last_point_x , y+r );
            }

pie_chart_drawing_alogrithm_4

The variables are then assigned values ​​that are used to plot the next section

last_point_x = new_point_x = x+r - (r * (degree-135.0)/45.0 ), last_point_y = new_point_y = y+r, used_degree = degree = 155.0

And so on.
We will end up with this form:

piechart_square

We see that there is a missing part because the sum of the data values ​​is less than the full value specified by the user. The back circle will appear as if it is the missing part.

Finally, we draw another circle that will be used as a mask to cut the square and turn it into a circle:

let mut mpb = PathBuilder::new();
        mpb.push_circle(x , y , r );
        let mut mpaint = Paint::default();
        mpaint.set_color_rgba8(0, 0, 0, 255);
        mpaint.anti_alias = true;
        let path = pb.finish().unwrap();
        let mpath = mpb.finish().unwrap();
        mask.fill_path(&path, FillRule::EvenOdd, true, Transform::default());
        mask.intersect_path(&mpath,FillRule::EvenOdd, true, Transform::default());
        paint.set_color_rgba8(red as u8,green as u8, blue as u8, alpha as u8);
        paint.anti_alias = true;
        pixmap.fill_path(
            &path,
            &paint,
            FillRule::EvenOdd,
            Transform::identity(),
            Some(&mask),
        );

We get the final output:

piechart

Usefull resources:

Rounded rectangle algorithm

To draw a rounded rectangle - which is used as a button widget in modern GUI - there are several ways Algorithms in order to do this.
In this example we will use straight lines and cubic curves to do this with Tiny-Skia.
In the file rounded_corners_rectangle_1.rs:

    // x, y are shape position, w is width, h is height and r is cubic curve sides length.
    //  Set limits for r, such that the sides of the curves do not intersect each other.
    //==============================================================================
    if h>w{
        if r > 0.24 * w{
            r = 0.24 * w
        }
    }
    else if h<w {
        if r > 0.24 * h{
            r = 0.24 * h
        }
    }
    else if h==w {
        if r > 0.24 * w{
            r = 0.24 * w
        }
    }
    //==============================================================================
    // Cubic curves used as corners of the rectangle.
    //==============================================================================
    let mut pb = PathBuilder::new();
    pb.move_to(x+r, y);
    pb.line_to(w-r, y);
    pb.cubic_to(w-(r/2 as f32),y,w,y+(r/2 as f32),w,y+r);
    pb.line_to(w, h-r);
    pb.cubic_to(w,h-(r/2 as f32),w-(r/2 as f32),h,w-r, h);
    pb.line_to(x+r, h);
    pb.cubic_to(x+(r/2 as f32),h,x,h-(r/2 as f32), x ,h-r);
    pb.line_to(x, y+r);
    pb.cubic_to(x,y+(r/2 as f32),x+(r/2 as f32),y, x+r, y);
    pb.close();

The following image may explain how the code works:

rounded_corners_rectangle_alogrithm

When we put the values ​​w=800, h=500, and r=60 in the function

rounded_corners_rectangle_draw_1(x:f32,y:f32,w:f32,h:f32,mut r:f32) -> Path

, we get the following form:

​​rounded_corners_rectangle_1_w800_h500_r60

w=800, h=500, and r=120:
rounded_corners_rectangle_1_w800_h500_r120

​​w=h:
rounded_corners_rectangle_1_w_eq_h

Usefull resources:

Graph algorithm

To draw a graph - which is used to represent data in x and y axis- we will use straight lines to do this with Tiny-Skia.

graph_alogrithm

We get the final output:

graph

Usefull resources: