plotlars_core/components/dimensions.rs
1/// A structure representing plot dimensions and sizing behavior.
2///
3/// The `Dimensions` struct allows customization of plot size including width, height,
4/// and auto-sizing behavior. It is particularly useful when creating subplot grids or
5/// when you need precise control over plot dimensions.
6///
7/// # Example
8///
9/// ```rust
10/// use plotlars::{
11/// Axis, BarPlot, BoxPlot, Dimensions, Legend, Line, Orientation, Plot, Rgb, ScatterPlot, Shape,
12/// SubplotGrid, Text, TickDirection, TimeSeriesPlot,
13/// };
14/// use polars::prelude::*;
15///
16/// let penguins_dataset = LazyCsvReader::new(PlRefPath::new("data/penguins.csv"))
17/// .finish()
18/// .unwrap()
19/// .select([
20/// col("species"),
21/// col("sex").alias("gender"),
22/// col("flipper_length_mm").cast(DataType::Int16),
23/// col("body_mass_g").cast(DataType::Int16),
24/// ])
25/// .collect()
26/// .unwrap();
27///
28/// let temperature_dataset = LazyCsvReader::new(PlRefPath::new("data/debilt_2023_temps.csv"))
29/// .with_has_header(true)
30/// .with_try_parse_dates(true)
31/// .finish()
32/// .unwrap()
33/// .with_columns(vec![
34/// (col("tavg") / lit(10)).alias("tavg"),
35/// (col("tmin") / lit(10)).alias("tmin"),
36/// (col("tmax") / lit(10)).alias("tmax"),
37/// ])
38/// .collect()
39/// .unwrap();
40///
41/// let animals_dataset = LazyCsvReader::new(PlRefPath::new("data/animal_statistics.csv"))
42/// .finish()
43/// .unwrap()
44/// .collect()
45/// .unwrap();
46///
47/// let axis = Axis::new()
48/// .show_line(true)
49/// .tick_direction(TickDirection::OutSide)
50/// .value_thousands(true);
51///
52/// let plot1 = TimeSeriesPlot::builder()
53/// .data(&temperature_dataset)
54/// .x("date")
55/// .y("tavg")
56/// .additional_series(vec!["tmin", "tmax"])
57/// .colors(vec![Rgb(128, 128, 128), Rgb(0, 122, 255), Rgb(255, 128, 0)])
58/// .lines(vec![Line::Solid, Line::Dot, Line::Dot])
59/// .plot_title(
60/// Text::from("De Bilt Temperature 2023")
61/// .font("Arial Black")
62/// .size(16),
63/// )
64/// .y_title(Text::from("temperature (°C)").size(13).x(-0.08))
65/// .legend(&Legend::new().x(0.1).y(0.9))
66/// .build();
67///
68/// let plot2 = ScatterPlot::builder()
69/// .data(&penguins_dataset)
70/// .x("body_mass_g")
71/// .y("flipper_length_mm")
72/// .group("species")
73/// .sort_groups_by(|a, b| {
74/// if a.len() == b.len() {
75/// a.cmp(b)
76/// } else {
77/// a.len().cmp(&b.len())
78/// }
79/// })
80/// .opacity(0.6)
81/// .size(10)
82/// .colors(vec![Rgb(178, 34, 34), Rgb(65, 105, 225), Rgb(255, 140, 0)])
83/// .shapes(vec![Shape::Circle, Shape::Square, Shape::Diamond])
84/// .plot_title(Text::from("Penguin Morphology").font("Arial Black").size(16))
85/// .x_title(Text::from("body mass (g)").size(13))
86/// .y_title(Text::from("flipper length (mm)").size(13).x(-0.11))
87/// .legend_title(Text::from("Species").size(12))
88/// .x_axis(&axis.clone().value_range(2500.0, 6500.0))
89/// .y_axis(&axis.clone().value_range(170.0, 240.0))
90/// .legend(&Legend::new().x(0.85).y(0.4))
91/// .build();
92///
93/// let plot3 = BarPlot::builder()
94/// .data(&animals_dataset)
95/// .labels("animal")
96/// .values("value")
97/// .orientation(Orientation::Vertical)
98/// .group("gender")
99/// .sort_groups_by(|a, b| a.len().cmp(&b.len()))
100/// .error("error")
101/// .colors(vec![Rgb(255, 127, 80), Rgb(64, 224, 208)])
102/// .plot_title(Text::from("Animal Statistics").font("Arial Black").size(16))
103/// .x_title(Text::from("animal").size(13))
104/// .y_title(Text::from("value").size(13))
105/// .legend_title(Text::from("Gender").size(12))
106/// .legend(
107/// &Legend::new()
108/// .orientation(Orientation::Horizontal)
109/// .x(0.35)
110/// .y(0.9),
111/// )
112/// .build();
113///
114/// let plot4 = BoxPlot::builder()
115/// .data(&penguins_dataset)
116/// .labels("species")
117/// .values("body_mass_g")
118/// .orientation(Orientation::Vertical)
119/// .group("gender")
120/// .box_points(true)
121/// .point_offset(-1.5)
122/// .jitter(0.01)
123/// .opacity(0.15)
124/// .colors(vec![Rgb(0, 191, 255), Rgb(57, 255, 20), Rgb(255, 105, 180)])
125/// .plot_title(
126/// Text::from("Body Mass Distribution")
127/// .font("Arial Black")
128/// .size(16),
129/// )
130/// .x_title(Text::from("species").size(13))
131/// .y_title(Text::from("body mass (g)").size(13).x(-0.12))
132/// .legend_title(Text::from("Gender").size(12))
133/// .y_axis(&Axis::new().value_thousands(true))
134/// .legend(&Legend::new().x(0.85).y(0.9))
135/// .build();
136///
137/// let dimensions = Dimensions::new().width(1400).height(850).auto_size(false);
138///
139/// SubplotGrid::regular()
140/// .plots(vec![&plot1, &plot2, &plot3, &plot4])
141/// .rows(2)
142/// .cols(2)
143/// .v_gap(0.3)
144/// .h_gap(0.2)
145/// .dimensions(&dimensions)
146/// .title(
147/// Text::from("Scientific Data Visualization Dashboard")
148/// .size(26)
149/// .font("Arial Black"),
150/// )
151/// .build()
152/// .plot();
153/// ```
154///
155/// 
156#[derive(Clone, Default)]
157pub struct Dimensions {
158 pub width: Option<usize>,
159 pub height: Option<usize>,
160 pub auto_size: Option<bool>,
161}
162
163impl Dimensions {
164 /// Creates a new `Dimensions` instance with default values.
165 pub fn new() -> Self {
166 Self::default()
167 }
168
169 /// Sets the width of the plot in pixels.
170 pub fn width(mut self, width: usize) -> Self {
171 self.width = Some(width);
172 self
173 }
174
175 /// Sets the height of the plot in pixels.
176 pub fn height(mut self, height: usize) -> Self {
177 self.height = Some(height);
178 self
179 }
180
181 /// Sets whether the plot should automatically resize.
182 pub fn auto_size(mut self, auto_size: bool) -> Self {
183 self.auto_size = Some(auto_size);
184 self
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn test_default() {
194 let dims = Dimensions::new();
195 assert!(dims.width.is_none());
196 assert!(dims.height.is_none());
197 assert!(dims.auto_size.is_none());
198 }
199
200 #[test]
201 fn test_width() {
202 let dims = Dimensions::new().width(800);
203 assert_eq!(dims.width, Some(800));
204 }
205
206 #[test]
207 fn test_height() {
208 let dims = Dimensions::new().height(600);
209 assert_eq!(dims.height, Some(600));
210 }
211
212 #[test]
213 fn test_auto_size() {
214 let dims = Dimensions::new().auto_size(true);
215 assert_eq!(dims.auto_size, Some(true));
216 }
217}