1use geng::prelude::*;
2
3#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
5pub struct Bounded<T> {
6 value: T,
7 min: T,
8 max: T,
9}
10
11impl<T: PartialOrd + Copy> Bounded<T> {
12 pub fn new(value: T, range: std::ops::RangeInclusive<T>) -> Self {
14 Self {
15 value,
16 min: *range.start(),
17 max: *range.end(),
18 }
19 .normalized()
20 }
21
22 pub fn value(&self) -> T {
23 self.value
24 }
25
26 pub fn min(&self) -> T {
27 self.min
28 }
29
30 pub fn max(&self) -> T {
31 self.max
32 }
33
34 pub fn map<U>(&mut self, f: impl Fn(T) -> U) -> Bounded<U> {
35 Bounded {
36 value: f(self.value),
37 min: f(self.min),
38 max: f(self.max),
39 }
40 }
41
42 fn normalized(self) -> Self {
43 let value = self.value.clamp_range(self.min..=self.max);
44 Self { value, ..self }
45 }
46
47 pub fn is_max(&self) -> bool {
49 self.value >= self.max
50 }
51
52 pub fn is_min(&self) -> bool {
54 self.value <= self.min
55 }
56
57 pub fn is_above_min(&self) -> bool {
59 self.value > self.min
60 }
61}
62
63impl<T: UNum> Bounded<T> {
64 pub fn new_max(value: T) -> Self {
66 Self::new(value, T::ZERO..=value)
67 }
68
69 pub fn new_zero(max: T) -> Self {
71 Self::new(T::ZERO, T::ZERO..=max)
72 }
73
74 pub fn change(&mut self, delta: T) {
76 self.value += delta;
77 *self = self.normalized();
78 }
79
80 pub fn set(&mut self, value: T) {
82 self.value = value;
83 *self = self.normalized();
84 }
85}
86
87impl<T: Float> Bounded<T> {
88 pub fn get_ratio(&self) -> T {
91 let len = self.max - self.min;
92 if len.approx_eq(&T::ZERO) {
93 T::ZERO
94 } else {
95 (self.value - self.min) / len
96 }
97 }
98
99 pub fn set_ratio(&mut self, ratio: T) {
100 self.value = ratio.clamp_range(T::ZERO..=T::ONE) * (self.max - self.min) + self.min;
101 }
102}
103
104#[test]
105fn test_bounded() {
106 let mut bounded = Bounded::new(0.0, 0.0..=10.0);
107 assert_eq!(bounded.value(), 0.0);
108
109 bounded.change(5.0);
110 assert_eq!(bounded.value(), 5.0);
111
112 bounded.change(6.0);
113 assert_eq!(bounded.value(), 10.0);
114
115 bounded.change(-11.0);
116 assert_eq!(bounded.value(), 0.0);
117}