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/// 
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}