plotters_unstable/style/
size.rs

1use crate::coord::CoordTranslate;
2use crate::drawing::DrawingArea;
3use plotters_backend::DrawingBackend;
4
5/// The trait indicates that the type has a dimensional data.
6/// This is the abstraction for the relative sizing model.
7/// A relative sizing value is able to be converted into a concrete size
8/// when coupling with a type with `HasDimension` type.
9pub trait HasDimension {
10    /// Get the dimensional data for this object
11    fn dim(&self) -> (u32, u32);
12}
13
14impl<D: DrawingBackend, C: CoordTranslate> HasDimension for DrawingArea<D, C> {
15    fn dim(&self) -> (u32, u32) {
16        self.dim_in_pixel()
17    }
18}
19
20impl HasDimension for (u32, u32) {
21    fn dim(&self) -> (u32, u32) {
22        *self
23    }
24}
25
26/// The trait that describes a size, it may be a relative size which the
27/// size is determined by the parent size, e.g., 10% of the parent width
28pub trait SizeDesc {
29    /// Convert the size into the number of pixels
30    ///
31    /// - `parent`: The reference to the parent container of this size
32    /// - **returns**: The number of pixels
33    fn in_pixels<T: HasDimension>(&self, parent: &T) -> i32;
34}
35
36impl SizeDesc for i32 {
37    fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
38        *self
39    }
40}
41
42impl SizeDesc for u32 {
43    fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
44        *self as i32
45    }
46}
47
48/// Describes a relative size, might be
49///     1. portion of height
50///     2. portion of width
51///     3. portion of the minimal of height and weight
52pub enum RelativeSize {
53    /// Percentage height
54    Height(f64),
55    /// Percentage width
56    Width(f64),
57    /// Percentage of either height or width, which is smaller
58    Smaller(f64),
59}
60
61impl RelativeSize {
62    /// Set the lower bound of the relative size.
63    ///
64    /// - `min_sz`: The minimal size the relative size can be in pixels
65    /// - **returns**: The relative size with the bound
66    pub fn min(self, min_sz: i32) -> RelativeSizeWithBound {
67        RelativeSizeWithBound {
68            size: self,
69            min: Some(min_sz),
70            max: None,
71        }
72    }
73
74    /// Set the upper bound of the relative size
75    ///
76    /// - `max_size`: The maximum size in pixels for this relative size
77    /// - **returns** The relative size with the upper bound
78    pub fn max(self, max_sz: i32) -> RelativeSizeWithBound {
79        RelativeSizeWithBound {
80            size: self,
81            max: Some(max_sz),
82            min: None,
83        }
84    }
85}
86
87impl SizeDesc for RelativeSize {
88    fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 {
89        let (w, h) = parent.dim();
90        match self {
91            RelativeSize::Width(p) => *p * f64::from(w),
92            RelativeSize::Height(p) => *p * f64::from(h),
93            RelativeSize::Smaller(p) => *p * f64::from(w.min(h)),
94        }
95        .round() as i32
96    }
97}
98
99/// Allows a value turns into a relative size
100pub trait AsRelative: Into<f64> {
101    /// Make the value a relative size of percentage of width
102    fn percent_width(self) -> RelativeSize {
103        RelativeSize::Width(self.into() / 100.0)
104    }
105    /// Make the value a relative size of percentage of height
106    fn percent_height(self) -> RelativeSize {
107        RelativeSize::Height(self.into() / 100.0)
108    }
109    /// Make the value a relative size of percentage of minimal of height and width
110    fn percent(self) -> RelativeSize {
111        RelativeSize::Smaller(self.into() / 100.0)
112    }
113}
114
115impl<T: Into<f64>> AsRelative for T {}
116
117/// The struct describes a relative size with upper bound and lower bound
118pub struct RelativeSizeWithBound {
119    size: RelativeSize,
120    min: Option<i32>,
121    max: Option<i32>,
122}
123
124impl RelativeSizeWithBound {
125    /// Set the lower bound of the bounded relative size
126    ///
127    /// - `min_sz`: The lower bound of this size description
128    /// - **returns**: The newly created size description with the bound
129    pub fn min(mut self, min_sz: i32) -> RelativeSizeWithBound {
130        self.min = Some(min_sz);
131        self
132    }
133
134    /// Set the upper bound of the bounded relative size
135    ///
136    /// - `min_sz`: The upper bound of this size description
137    /// - **returns**: The newly created size description with the bound
138    pub fn max(mut self, max_sz: i32) -> RelativeSizeWithBound {
139        self.max = Some(max_sz);
140        self
141    }
142}
143
144impl SizeDesc for RelativeSizeWithBound {
145    fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 {
146        let size = self.size.in_pixels(parent);
147        let size_lower_capped = self.min.map_or(size, |x| x.max(size));
148        self.max.map_or(size_lower_capped, |x| x.min(size))
149    }
150}
151
152#[cfg(test)]
153mod test {
154    use super::*;
155    #[test]
156    fn test_relative_size() {
157        let size = (10).percent_height();
158        assert_eq!(size.in_pixels(&(100, 200)), 20);
159
160        let size = (10).percent_width();
161        assert_eq!(size.in_pixels(&(100, 200)), 10);
162
163        let size = (-10).percent_width();
164        assert_eq!(size.in_pixels(&(100, 200)), -10);
165
166        let size = (10).percent_width().min(30);
167        assert_eq!(size.in_pixels(&(100, 200)), 30);
168        assert_eq!(size.in_pixels(&(400, 200)), 40);
169
170        let size = (10).percent();
171        assert_eq!(size.in_pixels(&(100, 200)), 10);
172        assert_eq!(size.in_pixels(&(400, 200)), 20);
173    }
174}