• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /// 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
52 pub 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 
61 impl 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
min(self, min_sz: i32) -> RelativeSizeWithBound66     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
max(self, max_sz: i32) -> RelativeSizeWithBound78     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 
87 impl SizeDesc for RelativeSize {
in_pixels<D: HasDimension>(&self, parent: &D) -> i3288     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
100 pub trait AsRelative: Into<f64> {
101     /// Make the value a relative size of percentage of width
percent_width(self) -> RelativeSize102     fn percent_width(self) -> RelativeSize {
103         RelativeSize::Width(self.into() / 100.0)
104     }
105     /// Make the value a relative size of percentage of height
percent_height(self) -> RelativeSize106     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
percent(self) -> RelativeSize110     fn percent(self) -> RelativeSize {
111         RelativeSize::Smaller(self.into() / 100.0)
112     }
113 }
114 
115 impl<T: Into<f64>> AsRelative for T {}
116 
117 /// The struct describes a relative size with upper bound and lower bound
118 pub struct RelativeSizeWithBound {
119     size: RelativeSize,
120     min: Option<i32>,
121     max: Option<i32>,
122 }
123 
124 impl 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
min(mut self, min_sz: i32) -> RelativeSizeWithBound129     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
max(mut self, max_sz: i32) -> RelativeSizeWithBound138     pub fn max(mut self, max_sz: i32) -> RelativeSizeWithBound {
139         self.max = Some(max_sz);
140         self
141     }
142 }
143 
144 impl SizeDesc for RelativeSizeWithBound {
in_pixels<D: HasDimension>(&self, parent: &D) -> i32145     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)]
153 mod test {
154     use super::*;
155     #[test]
test_relative_size()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 }
175