plotlars/components/
legend.rs

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