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