use geng::prelude::*;
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Bounded<T> {
value: T,
min: T,
max: T,
}
impl<T: PartialOrd + Copy> Bounded<T> {
pub fn new(value: T, range: std::ops::RangeInclusive<T>) -> Self {
Self {
value,
min: *range.start(),
max: *range.end(),
}
.normalized()
}
pub fn value(&self) -> T {
self.value
}
pub fn min(&self) -> T {
self.min
}
pub fn max(&self) -> T {
self.max
}
pub fn map<U>(&mut self, f: impl Fn(T) -> U) -> Bounded<U> {
Bounded {
value: f(self.value),
min: f(self.min),
max: f(self.max),
}
}
fn normalized(self) -> Self {
let value = self.value.clamp_range(self.min..=self.max);
Self { value, ..self }
}
pub fn is_max(&self) -> bool {
self.value >= self.max
}
pub fn is_min(&self) -> bool {
self.value <= self.min
}
pub fn is_above_min(&self) -> bool {
self.value > self.min
}
}
impl<T: UNum> Bounded<T> {
pub fn new_max(value: T) -> Self {
Self::new(value, T::ZERO..=value)
}
pub fn new_zero(max: T) -> Self {
Self::new(T::ZERO, T::ZERO..=max)
}
pub fn change(&mut self, delta: T) {
self.value += delta;
*self = self.normalized();
}
pub fn set(&mut self, value: T) {
self.value = value;
*self = self.normalized();
}
}
impl<T: Float> Bounded<T> {
pub fn get_ratio(&self) -> T {
let len = self.max - self.min;
if len.approx_eq(&T::ZERO) {
T::ZERO
} else {
(self.value - self.min) / len
}
}
pub fn set_ratio(&mut self, ratio: T) {
self.value = ratio.clamp_range(T::ZERO..=T::ONE) * (self.max - self.min) + self.min;
}
}
#[test]
fn test_bounded() {
let mut bounded = Bounded::new(0.0, 0.0..=10.0);
assert_eq!(bounded.value(), 0.0);
bounded.change(5.0);
assert_eq!(bounded.value(), 5.0);
bounded.change(6.0);
assert_eq!(bounded.value(), 10.0);
bounded.change(-11.0);
assert_eq!(bounded.value(), 0.0);
}