1use crate::coord::CoordTranslate;
2use crate::drawing::DrawingArea;
3use plotters_backend::DrawingBackend;
45/// 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.
9pub trait HasDimension {
10/// Get the dimensional data for this object
11fn dim(&self) -> (u32, u32);
12}
1314impl<D: DrawingBackend, C: CoordTranslate> HasDimension for DrawingArea<D, C> {
15fn dim(&self) -> (u32, u32) {
16self.dim_in_pixel()
17 }
18}
1920impl HasDimension for (u32, u32) {
21fn dim(&self) -> (u32, u32) {
22*self
23}
24}
2526/// 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
28pub 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
33fn in_pixels<T: HasDimension>(&self, parent: &T) -> i32;
34}
3536impl SizeDesc for i32 {
37fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
38*self
39}
40}
4142impl SizeDesc for u32 {
43fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
44*self as i32
45 }
46}
4748impl SizeDesc for f32 {
49fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
50*self as i32
51 }
52}
5354impl SizeDesc for f64 {
55fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
56*self as i32
57 }
58}
5960/// 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
64pub enum RelativeSize {
65/// Percentage height
66Height(f64),
67/// Percentage width
68Width(f64),
69/// Percentage of either height or width, which is smaller
70Smaller(f64),
71}
7273impl 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
78pub fn min(self, min_sz: i32) -> RelativeSizeWithBound {
79 RelativeSizeWithBound {
80 size: self,
81 min: Some(min_sz),
82 max: None,
83 }
84 }
8586/// 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
90pub fn max(self, max_sz: i32) -> RelativeSizeWithBound {
91 RelativeSizeWithBound {
92 size: self,
93 max: Some(max_sz),
94 min: None,
95 }
96 }
97}
9899impl SizeDesc for RelativeSize {
100fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 {
101let (w, h) = parent.dim();
102match 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}
110111/// Allows a value turns into a relative size
112pub trait AsRelative: Into<f64> {
113/// Make the value a relative size of percentage of width
114fn percent_width(self) -> RelativeSize {
115 RelativeSize::Width(self.into() / 100.0)
116 }
117/// Make the value a relative size of percentage of height
118fn 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
122fn percent(self) -> RelativeSize {
123 RelativeSize::Smaller(self.into() / 100.0)
124 }
125}
126127impl<T: Into<f64>> AsRelative for T {}
128129/// The struct describes a relative size with upper bound and lower bound
130pub struct RelativeSizeWithBound {
131 size: RelativeSize,
132 min: Option<i32>,
133 max: Option<i32>,
134}
135136impl 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
141pub fn min(mut self, min_sz: i32) -> RelativeSizeWithBound {
142self.min = Some(min_sz);
143self
144}
145146/// 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
150pub fn max(mut self, max_sz: i32) -> RelativeSizeWithBound {
151self.max = Some(max_sz);
152self
153}
154}
155156impl SizeDesc for RelativeSizeWithBound {
157fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 {
158let size = self.size.in_pixels(parent);
159let size_lower_capped = self.min.map_or(size, |x| x.max(size));
160self.max.map_or(size_lower_capped, |x| x.min(size))
161 }
162}
163164#[cfg(test)]
165mod test {
166use super::*;
167#[test]
168fn test_relative_size() {
169let size = (10).percent_height();
170assert_eq!(size.in_pixels(&(100, 200)), 20);
171172let size = (10).percent_width();
173assert_eq!(size.in_pixels(&(100, 200)), 10);
174175let size = (-10).percent_width();
176assert_eq!(size.in_pixels(&(100, 200)), -10);
177178let size = (10).percent_width().min(30);
179assert_eq!(size.in_pixels(&(100, 200)), 30);
180assert_eq!(size.in_pixels(&(400, 200)), 40);
181182let size = (10).percent();
183assert_eq!(size.in_pixels(&(100, 200)), 10);
184assert_eq!(size.in_pixels(&(400, 200)), 20);
185 }
186}