| 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 |
| 11 | fn dim(&self) -> (u32, u32); |
| 12 | } |
| 13 | |
| 14 | impl<D: DrawingBackend, C: CoordTranslate> HasDimension for DrawingArea<D, C> { |
| 15 | fn dim(&self) -> (u32, u32) { |
| 16 | self.dim_in_pixel() |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | impl 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 |
| 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 |
| 33 | fn in_pixels<T: HasDimension>(&self, parent: &T) -> i32; |
| 34 | } |
| 35 | |
| 36 | impl SizeDesc for i32 { |
| 37 | fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 { |
| 38 | *self |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | impl SizeDesc for u32 { |
| 43 | fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 { |
| 44 | *self as i32 |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | impl SizeDesc for f32 { |
| 49 | fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 { |
| 50 | *self as i32 |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | impl SizeDesc for f64 { |
| 55 | 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 |
| 78 | 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 |
| 90 | 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 { |
| 100 | fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 { |
| 101 | let (w: u32, h: u32) = parent.dim(); |
| 102 | matchf64 self { |
| 103 | RelativeSize::Width(p: &f64) => *p * f64::from(w), |
| 104 | RelativeSize::Height(p: &f64) => *p * f64::from(h), |
| 105 | RelativeSize::Smaller(p: &f64) => *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 |
| 114 | fn percent_width(self) -> RelativeSize { |
| 115 | RelativeSize::Width(self.into() / 100.0) |
| 116 | } |
| 117 | /// Make the value a relative size of percentage of height |
| 118 | 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 |
| 122 | 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 |
| 141 | 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 |
| 150 | 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 { |
| 157 | fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 { |
| 158 | let size: i32 = self.size.in_pixels(parent); |
| 159 | let size_lower_capped: i32 = self.min.map_or(default:size, |x: i32| x.max(size)); |
| 160 | self.max.map_or(default:size_lower_capped, |x: i32| x.min(size)) |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | #[cfg (test)] |
| 165 | mod test { |
| 166 | use super::*; |
| 167 | #[test ] |
| 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 | |