blob: 254df8d4fef31e3afcc948ef0bda4d9d79681c27 [file] [log] [blame]
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);
}
}