dataviz\figure\figuretypes/
areachart.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::figure::{
    canvas::pixelcanvas::PixelCanvas, configuration::figureconfig::FigureConfig,
    datasets::areachartdataset::AreaChartDataset,
};

/// Represents an area chart, including its title, axis labels, datasets, and configuration.
pub struct AreaChart {
    /// Title of the area chart.
    pub title: String,
    /// Label for the X-axis.
    pub x_label: String,
    /// Label for the Y-axis.
    pub y_label: String,
    /// A collection of datasets to be visualized in the area chart.
    pub datasets: Vec<AreaChartDataset>,
    /// Configuration settings for rendering the chart (e.g., colors, fonts, grid).
    pub config: FigureConfig,
}

impl AreaChart {
    /// Creates a new `AreaChart` instance with the specified title, axis labels, and configuration.
    ///
    /// # Parameters
    /// - `title`: The title of the area chart.
    /// - `x_label`: The label for the X-axis.
    /// - `y_label`: The label for the Y-axis.
    /// - `config`: The `FigureConfig` containing appearance and behavior settings for the chart.
    ///
    /// # Returns
    /// A new `AreaChart` instance with an empty dataset.
    ///
    /// # Example
    /// ```rust
    /// use crate::figure::configuration::figureconfig::FigureConfig;
    /// use crate::figure::areachart::AreaChart;
    ///
    /// let config = FigureConfig::default();
    /// let area_chart = AreaChart::new("Example Chart", "X Axis", "Y Axis", config);
    /// ```
    pub fn new(title: &str, x_label: &str, y_label: &str, config: FigureConfig) -> Self {
        Self {
            title: title.to_string(),
            x_label: x_label.to_string(),
            y_label: y_label.to_string(),
            datasets: Vec::new(),
            config,
        }
    }

    /// Adds a dataset to the area chart.
    ///
    /// # Parameters
    /// - `dataset`: The `AreaChartDataset` to be added to the chart.
    ///
    /// # Example
    /// ```rust
    /// use crate::figure::datasets::areachartdataset::AreaChartDataset;
    /// let dataset = AreaChartDataset::new([255, 0, 0], "Example Dataset", 0.5);
    /// area_chart.add_dataset(dataset);
    /// ```
    pub fn add_dataset(&mut self, dataset: AreaChartDataset) {
        self.datasets.push(dataset);
    }

    /// Draws the area under a dataset on the canvas.
    ///
    /// This method fills the area under the dataset line, interpolating between points
    /// and blending the pixels into the canvas.
    ///
    /// # Parameters
    /// - `canvas`: The `PixelCanvas` on which to draw the area.
    /// - `dataset`: The `AreaChartDataset` whose area is to be drawn.
    /// - `origin_x`: The x-coordinate of the chart's origin on the canvas.
    /// - `origin_y`: The y-coordinate of the chart's origin on the canvas.
    /// - `scale_x`: The scaling factor for converting X-axis values to canvas coordinates.
    /// - `scale_y`: The scaling factor for converting Y-axis values to canvas coordinates.
    ///
    /// # Details
    /// The method interpolates between adjacent points in the dataset to fill the area
    /// under the line segment and blend it into the canvas using the dataset's color and transparency.
    ///
    /// # Example
    /// ```rust
    /// let scale_x = 10.0;
    /// let scale_y = 10.0;
    /// area_chart.draw_area(&mut canvas, &dataset, 50, 400, scale_x, scale_y);
    /// ```
    pub fn draw_area(
        &self,
        canvas: &mut PixelCanvas,
        dataset: &AreaChartDataset,
        origin_x: i32,
        origin_y: i32,
        scale_x: f64,
        scale_y: f64,
    ) {
        let mut points = dataset.points.clone();
        points.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));

        for window in points.windows(2) {
            if let [p1, p2] = window {
                let x1 = origin_x + ((p1.0) * scale_x) as i32;
                let y1 = origin_y - ((p1.1) * scale_y) as i32;
                let x2 = origin_x + ((p2.0) * scale_x) as i32;
                let y2 = origin_y - ((p2.1) * scale_y) as i32;

                // Fill the area under the line
                for x in x1.min(x2)..=x1.max(x2) {
                    let interpolated_y =
                        y1 + ((x - x1) as f64 * (y2 - y1) as f64 / (x2 - x1).abs() as f64) as i32;
                    for y in interpolated_y..=origin_y {
                        canvas.blend_pixel(x as u32, y as u32, dataset.color, dataset.alpha);
                    }
                }
            }
        }
    }
}