1 use crate::coord::CoordTranslate;
2 use crate::drawing::DrawingArea;
3 use 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.
9 pub trait HasDimension {
10     /// Get the dimensional data for this object
dim(&self) -> (u32, u32)11     fn dim(&self) -> (u32, u32);
12 }
13 
14 impl<D: DrawingBackend, C: CoordTranslate> HasDimension for DrawingArea<D, C> {
dim(&self) -> (u32, u32)15     fn dim(&self) -> (u32, u32) {
16         self.dim_in_pixel()
17     }
18 }
19 
20 impl HasDimension for (u32, u32) {
dim(&self) -> (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
28 pub 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
in_pixels<T: HasDimension>(&self, parent: &T) -> i3233     fn in_pixels<T: HasDimension>(&self, parent: &T) -> i32;
34 }
35 
36 impl SizeDesc for i32 {
in_pixels<D: HasDimension>(&self, _parent: &D) -> i3237     fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
38         *self
39     }
40 }
41 
42 impl SizeDesc for u32 {
in_pixels<D: HasDimension>(&self, _parent: &D) -> i3243     fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
44         *self as i32
45     }
46 }
47 
48 impl SizeDesc for f32 {
in_pixels<D: HasDimension>(&self, _parent: &D) -> i3249     fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
50         *self as i32
51     }
52 }
53 
54 impl SizeDesc for f64 {
in_pixels<D: HasDimension>(&self, _parent: &D) -> i3255     fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
56         *self as i32
57     }
58 }
59 
60 /// Describes a relative size, might be
61 ///     1. portion of height
62 ///     2. portion of width
63 ///     3. portion of the minimal of height and weight
64 pub enum RelativeSize {
65     /// Percentage height
66     Height(f64),
67     /// Percentage width
68     Width(f64),
69     /// Percentage of either height or width, which is smaller
70     Smaller(f64),
71 }
72 
73 impl RelativeSize {
74     /// Set the lower bound of the relative size.
75     ///
76     /// - `min_sz`: The minimal size the relative size can be in pixels
77     /// - **returns**: The relative size with the bound
min(self, min_sz: i32) -> RelativeSizeWithBound78     pub fn min(self, min_sz: i32) -> RelativeSizeWithBound {
79         RelativeSizeWithBound {
80             size: self,
81             min: Some(min_sz),
82             max: None,
83         }
84     }
85 
86     /// Set the upper bound of the relative size
87     ///
88     /// - `max_size`: The maximum size in pixels for this relative size
89     /// - **returns** The relative size with the upper bound
max(self, max_sz: i32) -> RelativeSizeWithBound90     pub fn max(self, max_sz: i32) -> RelativeSizeWithBound {
91         RelativeSizeWithBound {
92             size: self,
93             max: Some(max_sz),
94             min: None,
95         }
96     }
97 }
98 
99 impl SizeDesc for RelativeSize {
in_pixels<D: HasDimension>(&self, parent: &D) -> i32100     fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 {
101         let (w, h) = parent.dim();
102         match self {
103             RelativeSize::Width(p) => *p * f64::from(w),
104             RelativeSize::Height(p) => *p * f64::from(h),
105             RelativeSize::Smaller(p) => *p * f64::from(w.min(h)),
106         }
107         .round() as i32
108     }
109 }
110 
111 /// Allows a value turns into a relative size
112 pub trait AsRelative: Into<f64> {
113     /// Make the value a relative size of percentage of width
percent_width(self) -> RelativeSize114     fn percent_width(self) -> RelativeSize {
115         RelativeSize::Width(self.into() / 100.0)
116     }
117     /// Make the value a relative size of percentage of height
percent_height(self) -> RelativeSize118     fn percent_height(self) -> RelativeSize {
119         RelativeSize::Height(self.into() / 100.0)
120     }
121     /// Make the value a relative size of percentage of minimal of height and width
percent(self) -> RelativeSize122     fn percent(self) -> RelativeSize {
123         RelativeSize::Smaller(self.into() / 100.0)
124     }
125 }
126 
127 impl<T: Into<f64>> AsRelative for T {}
128 
129 /// The struct describes a relative size with upper bound and lower bound
130 pub struct RelativeSizeWithBound {
131     size: RelativeSize,
132     min: Option<i32>,
133     max: Option<i32>,
134 }
135 
136 impl RelativeSizeWithBound {
137     /// Set the lower bound of the bounded relative size
138     ///
139     /// - `min_sz`: The lower bound of this size description
140     /// - **returns**: The newly created size description with the bound
min(mut self, min_sz: i32) -> RelativeSizeWithBound141     pub fn min(mut self, min_sz: i32) -> RelativeSizeWithBound {
142         self.min = Some(min_sz);
143         self
144     }
145 
146     /// Set the upper bound of the bounded relative size
147     ///
148     /// - `min_sz`: The upper bound of this size description
149     /// - **returns**: The newly created size description with the bound
max(mut self, max_sz: i32) -> RelativeSizeWithBound150     pub fn max(mut self, max_sz: i32) -> RelativeSizeWithBound {
151         self.max = Some(max_sz);
152         self
153     }
154 }
155 
156 impl SizeDesc for RelativeSizeWithBound {
in_pixels<D: HasDimension>(&self, parent: &D) -> i32157     fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 {
158         let size = self.size.in_pixels(parent);
159         let size_lower_capped = self.min.map_or(size, |x| x.max(size));
160         self.max.map_or(size_lower_capped, |x| x.min(size))
161     }
162 }
163 
164 #[cfg(test)]
165 mod test {
166     use super::*;
167     #[test]
test_relative_size()168     fn test_relative_size() {
169         let size = (10).percent_height();
170         assert_eq!(size.in_pixels(&(100, 200)), 20);
171 
172         let size = (10).percent_width();
173         assert_eq!(size.in_pixels(&(100, 200)), 10);
174 
175         let size = (-10).percent_width();
176         assert_eq!(size.in_pixels(&(100, 200)), -10);
177 
178         let size = (10).percent_width().min(30);
179         assert_eq!(size.in_pixels(&(100, 200)), 30);
180         assert_eq!(size.in_pixels(&(400, 200)), 40);
181 
182         let size = (10).percent();
183         assert_eq!(size.in_pixels(&(100, 200)), 10);
184         assert_eq!(size.in_pixels(&(400, 200)), 20);
185     }
186 }
187