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, 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 |
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 = 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] |
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 | |