rustpie-chart

Rust pie chart graph divider issue


I'm trying to create a pie chart using the crate plotter. The issue I'm having is adding the black dividers to match. The sample output does not match.

enter image description here

The code in question is in the file: pie_chart.rs I'm assuming the math is wrong.

// Draw black line dividers
if with_dividers {
    let mut start_angle = 66.0;
    for size in sizes.iter() {
        let end_angle = start_angle + size * 360.0;
        let (x, y) = (
            center.0 + (radius * start_angle.to_radians().cos()) as i32,
            center.1 + (radius * start_angle.to_radians().sin()) as i32,
        );
        root_area
            .draw(&PathElement::new(vec![(center.0, center.1), (x, y)], &BLACK))
            .map_err(|e| e.to_string())?;
        start_angle = end_angle;
    }        

}

I put the project in GitHub under the URL https://github.com/adviner/pie_chart_divider

use plotters::prelude::*;
use std::collections::HashMap;

#[warn(dead_code)]
fn hex_to_rgb(hex: &str) -> RGBColor {
    let hex = hex.trim_start_matches('#');
    let r = u8::from_str_radix(&hex[0..2], 16).unwrap();
    let g = u8::from_str_radix(&hex[2..4], 16).unwrap();
    let b = u8::from_str_radix(&hex[4..6], 16).unwrap();
    RGBColor(r, g, b)
}

#[warn(dead_code)]
pub fn generate_pie_chart(tax_data: &Vec<(&str, f64)>, output_file: &str, with_dividers: bool) -> Result<String, String> {
    
    let color_lookup: HashMap<&str, &str> = [
        ("MC", "#F58282"),
        ("SE", "#F5B082"),
        ("ED", "#FDFA7E"),
        ("LI", "#DEF582"),
        ("LH", "#B0F582"),
        ("CC", "#82F582"),
        ("LD", "#82F5B0"),
        ("FA", "#82F5DE"),
        ("SD", "#82DEF5"),
        ("FD", "#82B0F5"),
        ("FL", "#8282F5"),
        ("RI", "#B082F5"),
        ("TV", "#DE82F5"),
        ("MR", "#F582DE"),
        ("WV", "#F582B0"),
    ].iter().cloned().collect();


    let root_area = BitMapBackend::new(output_file, (300, 250)).into_drawing_area();

    // RGB with alpha channel
    let transparent_white = RGBAColor(255, 255, 255, 1.0);
    root_area.fill(&transparent_white).map_err(|e| e.to_string())?;


    let title_style = TextStyle::from(("sans-serif", 30).into_font()).color(&(BLACK));
    root_area.titled("", title_style).unwrap();

    let dims = root_area.dim_in_pixel();
    let center = (dims.0 as i32 / 2, dims.1 as i32 / 2);
    let radius = 100.0;  // Adjusted radius to make the pie chart smaller



    let mut sorted_tax_map = tax_data.clone();
    sorted_tax_map.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());

    let mid = sorted_tax_map.len() / 2;
    let (large, small) = sorted_tax_map.split_at(mid);
    
    let mut interleaved_tax_map = Vec::new();
    for i in 0..mid {
        interleaved_tax_map.push(large[i].clone());
        if i < small.len() {
            interleaved_tax_map.push(small[i].clone());
        }
    }

    let sizes: Vec<f64> = interleaved_tax_map.iter().map(|&(_, v)| v).collect();
    let labels: Vec<&str> = interleaved_tax_map.iter().map(|&(k, _)| k).collect();
    let colors: Vec<RGBColor> = interleaved_tax_map.iter().map(|&(k, _)| hex_to_rgb(color_lookup.get(k).unwrap())).collect();
    
    let mut pie = Pie::new(&center, &radius, &sizes, &colors, &labels);
    pie.start_angle(66.0);
    pie.label_style((("sans-serif", 15).into_font()).color(&(BLACK)));
    pie.label_offset(15.0);
    root_area.draw(&pie).map_err(|e| e.to_string())?;

    // Draw black line dividers
    if with_dividers {
        let mut start_angle = 66.0;
        for size in sizes.iter() {
            let end_angle = start_angle + size * 360.0;
            let (x, y) = (
                center.0 + (radius * start_angle.to_radians().cos()) as i32,
                center.1 + (radius * start_angle.to_radians().sin()) as i32,
            );
            root_area
                .draw(&PathElement::new(vec![(center.0, center.1), (x, y)], &BLACK))
                .map_err(|e| e.to_string())?;
            start_angle = end_angle;
        }        
    
    }

    Ok(format!("Pie chart generated successfully and saved to {}", output_file))
}

From the main.rs file is the data that is passed:

let tax_map = vec![
    ("ED", 36.31),
    ("CC", 9.77),
    ("TV", 0.48),
    ("FA", 0.85),
    ("SD", 5.95),
    ("MC", 14.85),
    ("FD", 29.63),
    ("LI", 2.16),
];

Any help would be appreciated


Solution

  • From cafce25 suggestion. I edited my code to divide the size with 100:

    let end_angle = start_angle + (size/100.0) * 360.0;
    

    Now the chart has the dividers showing properly

    enter image description here