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