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
//! Extra utilities for [Ord] and [PartialOrd] types
#![warn(missing_docs)]
use batbox_range::*;
pub use std::cmp::{max, min};
/// Extension trait for getting minimum/maximum of values grouped together
pub trait MinMax: Sized {
/// Type of a single value
type T;
/// Find (min, max)
fn min_max(self) -> (Self::T, Self::T);
/// Find min
fn min(self) -> Self::T {
self.min_max().0
}
/// Find max
fn max(self) -> Self::T {
self.min_max().1
}
}
impl<T: Ord> MinMax for (T, T) {
type T = T;
fn min_max(self) -> (T, T) {
let (a, b) = self;
if a.cmp(&b) == std::cmp::Ordering::Less {
(a, b)
} else {
(b, a)
}
}
}
/// Compares arguments and returns (min, max)
pub fn min_max<T: Ord>(a: T, b: T) -> (T, T) {
(a, b).min_max()
}
/// Extension trait for getting minimum/maximum of values grouped together
pub trait PartialMinMax: Sized {
/// Type of a single value
type T;
/// Find (min, max)
fn partial_min_max(self) -> (Self::T, Self::T);
/// Find min
fn partial_min(self) -> Self::T {
self.partial_min_max().0
}
/// Find max
fn partial_max(self) -> Self::T {
self.partial_min_max().1
}
}
impl<T: PartialOrd> PartialMinMax for (T, T) {
type T = T;
fn partial_min_max(self) -> (T, T) {
let (a, b) = self;
if a.partial_cmp(&b) == Some(std::cmp::Ordering::Less) {
(a, b)
} else {
(b, a)
}
}
}
/// Compares and returns the minimum of two values
pub fn partial_min<T: PartialOrd>(a: T, b: T) -> T {
(a, b).partial_min()
}
/// Compares and returns the maximum of two values
pub fn partial_max<T: PartialOrd>(a: T, b: T) -> T {
(a, b).partial_max()
}
/// Compares arguments and returns (min, max)
pub fn partial_min_max<T: PartialOrd>(a: T, b: T) -> (T, T) {
(a, b).partial_min_max()
}
/// Provides methods for clamping values
pub trait Clamp: Sized + PartialOrd {
/// Clamps a value in range.
/// # Examples
/// ```
/// # use batbox_cmp::*;
/// assert_eq!(2.0.clamp_range(0.0..=1.0), 1.0);
/// assert_eq!(2.0.clamp_range(3.0..), 3.0);
/// assert_eq!(2.0.clamp_range(..=0.0), 0.0);
/// ```
fn clamp_range(mut self, range: impl FixedRangeBounds<Self>) -> Self
where
Self: Clone,
{
match range.start_bound() {
FixedBound::Included(start) => self = partial_max(self, start.clone()),
FixedBound::Unbounded => (),
}
match range.end_bound() {
FixedBound::Included(end) => self = partial_min(self, end.clone()),
FixedBound::Unbounded => (),
}
self
}
/// Clamp the absolute value. Same as self.clamp_range(-max..=max)
fn clamp_abs(self, max: Self) -> Self
where
Self: std::ops::Neg<Output = Self> + Copy,
{
self.clamp_range(-max..=max)
}
/// Clamp by maximum value
///
/// If self is greater than max then return max, otherwise return self
fn clamp_max(self, max: Self) -> Self {
partial_min(self, max)
}
/// Clamp by minimum value
///
/// If self is less than min then return min, otherwise return self
fn clamp_min(self, min: Self) -> Self {
partial_max(self, min)
}
}
impl<T: PartialOrd> Clamp for T {}