1use bevy::prelude::*;
4
5#[reflect_trait]
9pub trait Evaluator: std::fmt::Debug + Sync + Send {
10 fn evaluate(&self, value: f32) -> f32;
11}
12
13#[derive(Debug, Clone, Reflect)]
17pub struct LinearEvaluator {
18 xa: f32,
19 ya: f32,
20 yb: f32,
21 dy_over_dx: f32,
22}
23
24impl LinearEvaluator {
25 pub fn new() -> Self {
26 Self::new_full(0.0, 0.0, 1.0, 1.0)
27 }
28 pub fn new_inversed() -> Self {
29 Self::new_ranged(1.0, 0.0)
30 }
31 pub fn new_ranged(min: f32, max: f32) -> Self {
32 Self::new_full(min, 0.0, max, 1.0)
33 }
34 fn new_full(xa: f32, ya: f32, xb: f32, yb: f32) -> Self {
35 Self {
36 xa,
37 ya,
38 yb,
39 dy_over_dx: (yb - ya) / (xb - xa),
40 }
41 }
42}
43
44impl Default for LinearEvaluator {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl Evaluator for LinearEvaluator {
51 fn evaluate(&self, value: f32) -> f32 {
52 clamp(
53 self.ya + self.dy_over_dx * (value - self.xa),
54 self.ya,
55 self.yb,
56 )
57 }
58}
59
60#[derive(Debug, Clone, Reflect)]
64pub struct PowerEvaluator {
65 xa: f32,
66 ya: f32,
67 xb: f32,
68 power: f32,
69 dy: f32,
70}
71
72impl PowerEvaluator {
73 pub fn new(power: f32) -> Self {
74 Self::new_full(power, 0.0, 0.0, 1.0, 1.0)
75 }
76 pub fn new_ranged(power: f32, min: f32, max: f32) -> Self {
77 Self::new_full(power, min, 0.0, max, 1.0)
78 }
79 fn new_full(power: f32, xa: f32, ya: f32, xb: f32, yb: f32) -> Self {
80 Self {
81 power: clamp(power, 0.0, 10000.0),
82 dy: yb - ya,
83 xa,
84 ya,
85 xb,
86 }
87 }
88}
89
90impl Default for PowerEvaluator {
91 fn default() -> Self {
92 Self::new(2.0)
93 }
94}
95
96impl Evaluator for PowerEvaluator {
97 fn evaluate(&self, value: f32) -> f32 {
98 let cx = clamp(value, self.xa, self.xb);
99 self.dy * ((cx - self.xa) / (self.xb - self.xa)).powf(self.power) + self.ya
100 }
101}
102
103#[derive(Debug, Clone, Reflect)]
107pub struct SigmoidEvaluator {
108 xa: f32,
109 xb: f32,
110 ya: f32,
111 yb: f32,
112 k: f32,
113 two_over_dx: f32,
114 x_mean: f32,
115 y_mean: f32,
116 dy_over_two: f32,
117 one_minus_k: f32,
118}
119
120impl SigmoidEvaluator {
121 pub fn new(k: f32) -> Self {
122 Self::new_full(k, 0.0, 0.0, 1.0, 1.0)
123 }
124
125 pub fn new_ranged(k: f32, min: f32, max: f32) -> Self {
126 Self::new_full(k, min, 0.0, max, 1.0)
127 }
128
129 fn new_full(k: f32, xa: f32, ya: f32, xb: f32, yb: f32) -> Self {
130 let k = clamp(k, -0.99999, 0.99999);
131 Self {
132 xa,
133 xb,
134 ya,
135 yb,
136 two_over_dx: (2.0 / (xb - ya)).abs(),
137 x_mean: (xa + xb) / 2.0,
138 y_mean: (ya + yb) / 2.0,
139 dy_over_two: (yb - ya) / 2.0,
140 one_minus_k: 1.0 - k,
141 k,
142 }
143 }
144}
145
146impl Evaluator for SigmoidEvaluator {
147 fn evaluate(&self, x: f32) -> f32 {
148 let cx_minus_x_mean = clamp(x, self.xa, self.xb) - self.x_mean;
149 let numerator = self.two_over_dx * cx_minus_x_mean * self.one_minus_k;
150 let denominator = self.k * (1.0 - 2.0 * (self.two_over_dx * cx_minus_x_mean)).abs() + 1.0;
151 clamp(
152 self.dy_over_two * (numerator / denominator) + self.y_mean,
153 self.ya,
154 self.yb,
155 )
156 }
157}
158
159impl Default for SigmoidEvaluator {
160 fn default() -> Self {
161 Self::new(-0.5)
162 }
163}
164
165pub(crate) fn clamp<T: PartialOrd>(val: T, min: T, max: T) -> T {
166 let val = if val > max { max } else { val };
167 if val < min {
168 min
169 } else {
170 val
171 }
172}