plotters_unsable/chart/
builder.rs

1use super::context::ChartContext;
2
3use crate::coord::{AsRangedCoord, RangedCoord, Shift};
4use crate::drawing::backend::DrawingBackend;
5use crate::drawing::{DrawingArea, DrawingAreaErrorKind};
6use crate::style::TextStyle;
7
8/// The helper object to create a chart context, which is used for the high-level figure drawing
9pub struct ChartBuilder<'a, DB: DrawingBackend> {
10    x_label_size: u32,
11    y_label_size: u32,
12    root_area: &'a DrawingArea<DB, Shift>,
13    title: Option<(String, TextStyle<'a>)>,
14    margin: u32,
15}
16
17impl<'a, DB: DrawingBackend> ChartBuilder<'a, DB> {
18    /// Create a chart builder on the given drawing area
19    /// - `root`: The root drawing area
20    /// - Returns: The chart builder object
21    pub fn on(root: &'a DrawingArea<DB, Shift>) -> Self {
22        Self {
23            x_label_size: 0,
24            y_label_size: 0,
25            root_area: root,
26            title: None,
27            margin: 0,
28        }
29    }
30
31    /// Set the margin size of the chart
32    /// - `size`: The size of the chart margin. If the chart builder is titled, we don't apply any
33    /// margin
34    pub fn margin(&mut self, size: u32) -> &mut Self {
35        self.margin = size;
36        self
37    }
38
39    /// Set the size of X label area
40    /// - `size`: The height of the x label area, if x is 0, the chart doesn't have the X label area
41    pub fn x_label_area_size(&mut self, size: u32) -> &mut Self {
42        self.x_label_size = size;
43        self
44    }
45
46    /// Set the size of the Y label area
47    /// - `size`: The width of the Y label area. If size is 0, the chart doesn't have Y label area
48    pub fn y_label_area_size(&mut self, size: u32) -> &mut Self {
49        self.y_label_size = size;
50        self
51    }
52
53    /// Set the caption of the chart
54    /// - `caption`: The caption of the chart
55    /// - `style`: The text style
56    /// - Note: If the caption is set, the margin option will be ignored
57    pub fn caption<S: AsRef<str>, Style: Into<TextStyle<'a>>>(
58        &mut self,
59        caption: S,
60        style: Style,
61    ) -> &mut Self {
62        self.title = Some((caption.as_ref().to_string(), style.into()));
63        self
64    }
65
66    /// Build the chart with a 2D Cartesian coordinate system. The function will returns a chart
67    /// context, where data series can be rendered on.
68    /// - `x_spec`: The specification of X axis
69    /// - `y_spec`: The specification of Y axis
70    /// - Returns: A chart context
71    #[allow(clippy::type_complexity)]
72    pub fn build_ranged<X: AsRangedCoord, Y: AsRangedCoord>(
73        &mut self,
74        x_spec: X,
75        y_spec: Y,
76    ) -> Result<
77        ChartContext<DB, RangedCoord<X::CoordDescType, Y::CoordDescType>>,
78        DrawingAreaErrorKind<DB::ErrorType>,
79    > {
80        let mut x_label_area = None;
81        let mut y_label_area = None;
82
83        let mut drawing_area = DrawingArea::clone(self.root_area);
84
85        if self.margin > 0 {
86            let s = self.margin as i32;
87            drawing_area = drawing_area.margin(s, s, s, s);
88        }
89
90        if let Some((ref title, ref style)) = self.title {
91            drawing_area = drawing_area.titled(title, style.clone())?;
92        }
93
94        if self.x_label_size > 0 {
95            let (_, h) = drawing_area.dim_in_pixel();
96            let (upper, bottom) =
97                drawing_area.split_vertically(h as i32 - self.x_label_size as i32);
98            drawing_area = upper;
99            x_label_area = Some(bottom);
100        }
101
102        if self.y_label_size > 0 {
103            let (left, right) = drawing_area.split_horizentally(self.y_label_size as i32);
104            drawing_area = right;
105            y_label_area = Some(left);
106
107            if let Some(xl) = x_label_area {
108                let (_, right) = xl.split_horizentally(self.y_label_size as i32);
109                x_label_area = Some(right);
110            }
111        }
112
113        let mut pixel_range = drawing_area.get_pixel_range();
114        pixel_range.1 = pixel_range.1.end..pixel_range.1.start;
115
116        Ok(ChartContext {
117            x_label_area,
118            y_label_area,
119            drawing_area: drawing_area.apply_coord_spec(RangedCoord::new(
120                x_spec,
121                y_spec,
122                pixel_range,
123            )),
124            series_anno: vec![],
125        })
126    }
127}