Contents
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
use crate::coord::CoordTranslate;
use crate::drawing::DrawingArea;
use plotters_backend::DrawingBackend;
/// The trait indicates that the type has a dimensional data.
/// This is the abstraction for the relative sizing model.
/// A relative sizing value is able to be converted into a concrete size
/// when coupling with a type with `HasDimension` type.
pub trait HasDimension {
/// Get the dimensional data for this object
fn dim(&self) -> (u32, u32);
}
impl<D: DrawingBackend, C: CoordTranslate> HasDimension for DrawingArea<D, C> {
fn dim(&self) -> (u32, u32) {
self.dim_in_pixel()
}
}
impl HasDimension for (u32, u32) {
fn dim(&self) -> (u32, u32) {
*self
}
}
/// The trait that describes a size, it may be a relative size which the
/// size is determined by the parent size, e.g., 10% of the parent width
pub trait SizeDesc {
/// Convert the size into the number of pixels
///
/// - `parent`: The reference to the parent container of this size
/// - **returns**: The number of pixels
fn in_pixels<T: HasDimension>(&self, parent: &T) -> i32;
}
impl SizeDesc for i32 {
fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
*self
}
}
impl SizeDesc for u32 {
fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
*self as i32
}
}
impl SizeDesc for f32 {
fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
*self as i32
}
}
impl SizeDesc for f64 {
fn in_pixels<D: HasDimension>(&self, _parent: &D) -> i32 {
*self as i32
}
}
/// Describes a relative size, might be
/// 1. portion of height
/// 2. portion of width
/// 3. portion of the minimal of height and weight
pub enum RelativeSize {
/// Percentage height
Height(f64),
/// Percentage width
Width(f64),
/// Percentage of either height or width, which is smaller
Smaller(f64),
}
impl RelativeSize {
/// Set the lower bound of the relative size.
///
/// - `min_sz`: The minimal size the relative size can be in pixels
/// - **returns**: The relative size with the bound
pub fn min(self, min_sz: i32) -> RelativeSizeWithBound {
RelativeSizeWithBound {
size: self,
min: Some(min_sz),
max: None,
}
}
/// Set the upper bound of the relative size
///
/// - `max_size`: The maximum size in pixels for this relative size
/// - **returns** The relative size with the upper bound
pub fn max(self, max_sz: i32) -> RelativeSizeWithBound {
RelativeSizeWithBound {
size: self,
max: Some(max_sz),
min: None,
}
}
}
impl SizeDesc for RelativeSize {
fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 {
let (w, h) = parent.dim();
match self {
RelativeSize::Width(p) => *p * f64::from(w),
RelativeSize::Height(p) => *p * f64::from(h),
RelativeSize::Smaller(p) => *p * f64::from(w.min(h)),
}
.round() as i32
}
}
/// Allows a value turns into a relative size
pub trait AsRelative: Into<f64> {
/// Make the value a relative size of percentage of width
fn percent_width(self) -> RelativeSize {
RelativeSize::Width(self.into() / 100.0)
}
/// Make the value a relative size of percentage of height
fn percent_height(self) -> RelativeSize {
RelativeSize::Height(self.into() / 100.0)
}
/// Make the value a relative size of percentage of minimal of height and width
fn percent(self) -> RelativeSize {
RelativeSize::Smaller(self.into() / 100.0)
}
}
impl<T: Into<f64>> AsRelative for T {}
/// The struct describes a relative size with upper bound and lower bound
pub struct RelativeSizeWithBound {
size: RelativeSize,
min: Option<i32>,
max: Option<i32>,
}
impl RelativeSizeWithBound {
/// Set the lower bound of the bounded relative size
///
/// - `min_sz`: The lower bound of this size description
/// - **returns**: The newly created size description with the bound
pub fn min(mut self, min_sz: i32) -> RelativeSizeWithBound {
self.min = Some(min_sz);
self
}
/// Set the upper bound of the bounded relative size
///
/// - `min_sz`: The upper bound of this size description
/// - **returns**: The newly created size description with the bound
pub fn max(mut self, max_sz: i32) -> RelativeSizeWithBound {
self.max = Some(max_sz);
self
}
}
impl SizeDesc for RelativeSizeWithBound {
fn in_pixels<D: HasDimension>(&self, parent: &D) -> i32 {
let size = self.size.in_pixels(parent);
let size_lower_capped = self.min.map_or(size, |x| x.max(size));
self.max.map_or(size_lower_capped, |x| x.min(size))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_relative_size() {
let size = (10).percent_height();
assert_eq!(size.in_pixels(&(100, 200)), 20);
let size = (10).percent_width();
assert_eq!(size.in_pixels(&(100, 200)), 10);
let size = (-10).percent_width();
assert_eq!(size.in_pixels(&(100, 200)), -10);
let size = (10).percent_width().min(30);
assert_eq!(size.in_pixels(&(100, 200)), 30);
assert_eq!(size.in_pixels(&(400, 200)), 40);
let size = (10).percent();
assert_eq!(size.in_pixels(&(100, 200)), 10);
assert_eq!(size.in_pixels(&(400, 200)), 20);
}
}