plotlars/components/
legend.rs

1use plotly::{
2    common::Font,
3    layout::Legend as LegendPlotly,
4};
5
6use crate::{Orientation, Rgb, Text};
7
8/// A structure representing a customizable plot legend.
9///
10/// # Example
11///
12/// ```rust
13/// use plotlars::{Histogram, Legend, Orientation, Plot, Rgb};
14///
15/// let dataset = LazyCsvReader::new("data/penguins.csv")
16///     .finish()
17///     .unwrap()
18///     .select([
19///         col("species"),
20///         col("sex").alias("gender"),
21///         col("flipper_length_mm").cast(DataType::Int16),
22///         col("body_mass_g").cast(DataType::Int16),
23///     ])
24///     .collect()
25///     .unwrap();
26///
27/// let legend = Legend::new()
28///     .orientation(Orientation::Horizontal)
29///     .border_width(1)
30///     .x(0.78)
31///     .y(0.825);
32///
33/// Histogram::builder()
34///     .data(&dataset)
35///     .x("body_mass_g")
36///     .group("species")
37///     .colors(vec![
38///         Rgb(255, 0, 0),
39///         Rgb(0, 255, 0),
40///         Rgb(0, 0, 255),
41///     ])
42///     .opacity(0.5)
43///     .x_title("Body Mass (g)")
44///     .y_title("Frequency")
45///     .legend_title("Species")
46///     .legend(&legend)
47///     .build()
48///     .plot();
49/// ```
50///
51/// ![example](https://imgur.com/GpUsgli.png)
52#[derive(Clone, Default)]
53pub struct Legend {
54    pub(crate) background_color: Option<Rgb>,
55    pub(crate) border_color: Option<Rgb>,
56    pub(crate) border_width: Option<usize>,
57    pub(crate) font: Option<String>,
58    pub(crate) orientation: Option<Orientation>,
59    pub(crate) x: Option<f64>,
60    pub(crate) y: Option<f64>,
61}
62
63impl Legend {
64    /// Creates a new `Legend` instance with default values.
65    pub fn new() -> Self {
66        Self::default()
67    }
68
69    /// Sets the background color of the legend.
70    ///
71    /// # Argument
72    ///
73    /// * `color` - An `Rgb` struct representing the background color.
74    pub fn background_color(mut self, color: Rgb) -> Self {
75        self.background_color = Some(color);
76        self
77    }
78
79    /// Sets the border color of the legend.
80    ///
81    /// # Argument
82    ///
83    /// * `color` - An `Rgb` struct representing the border color.
84    pub fn border_color(mut self, color: Rgb) -> Self {
85        self.border_color = Some(color);
86        self
87    }
88
89    /// Sets the border width of the legend.
90    ///
91    /// # Argument
92    ///
93    /// * `width` - A `usize` value representing the width of the border.
94    pub fn border_width(mut self, width: usize) -> Self {
95        self.border_width = Some(width);
96        self
97    }
98
99    /// Sets the font of the legend labels.
100    ///
101    /// # Argument
102    ///
103    /// * `font` - A value that can be converted into a `String`, representing the font name for the labels.
104    pub fn font(mut self, font: impl Into<String>) -> Self {
105        self.font = Some(font.into());
106        self
107    }
108
109    /// Sets the orientation of the legend.
110    ///
111    /// # Argument
112    ///
113    /// * `orientation` - An `Orientation` enum value representing the layout direction of the legend.
114    pub fn orientation(mut self, orientation: Orientation) -> Self {
115        self.orientation = Some(orientation);
116        self
117    }
118
119    /// Sets the horizontal position of the legend.
120    ///
121    /// # Argument
122    ///
123    /// * `x` - A `f64` value representing the horizontal position of the legend.
124    pub fn x(mut self, x: f64) -> Self {
125        self.x = Some(x);
126        self
127    }
128
129    /// Sets the vertical position of the legend.
130    ///
131    /// # Argument
132    ///
133    /// * `y` - A `f64` value representing the vertical position of the legend.
134    pub fn y(mut self, y: f64) -> Self {
135        self.y = Some(y);
136        self
137    }
138
139    pub(crate) fn set_legend(title: Option<Text>, format: Option<&Legend>) -> LegendPlotly {
140        let mut legend = LegendPlotly::new();
141
142        if let Some(title) = title {
143            legend = legend.title(title.to_plotly());
144        }
145
146        if let Some(format) = format {
147            legend = Self::set_format(legend, format);
148        }
149
150        legend
151    }
152
153    fn set_format(mut legend: LegendPlotly, format: &Legend) -> LegendPlotly {
154        if let Some(color) = format.background_color {
155            legend = legend.background_color(color.to_plotly());
156        }
157
158        if let Some(color) = format.border_color {
159            legend = legend.border_color(color.to_plotly());
160        }
161
162        if let Some(width) = format.border_width {
163            legend = legend.border_width(width);
164        }
165
166        if let Some(font) = &format.font {
167            legend = legend.font(Font::new().family(font.as_str()));
168        }
169
170        if let Some(orientation) = &format.orientation {
171            legend = legend.orientation(orientation.to_plotly());
172        }
173
174        if let Some(x) = format.x {
175            legend = legend.x(x);
176        }
177
178        if let Some(y) = format.y {
179            legend = legend.y(y);
180        }
181
182        legend
183    }
184}