use crate::math::easing::eval::easing_value;
use crate::math::{AdvancedEasing, Easable, Easing};
pub fn solve_integration_reciprocal_evaluation<T, const N: usize>(
ease: Easing,
from: T,
to: T,
val: T,
samples: usize,
) -> Option<T>
where
T: Easable<N>,
{
let from = from.components();
let to = to.components();
let val = val.components();
for i in 0..N {
if from[i] * to[i] <= 0.0 {
return None;
}
if !from[i].is_finite() || !to[i].is_finite() || !val[i].is_finite() {
return None;
}
}
Some(T::compose(std::array::from_fn(|i| {
let from = from[i];
let to = to[i];
let val = val[i];
if from < 0.0 || to < 0.0 {
solve_integration_reciprocal(ease, -from, -to, -val, samples)
} else {
solve_integration_reciprocal(ease, from, to, val, samples)
}
})))
}
fn solve_integration_reciprocal(ease: Easing, from: f64, to: f64, val: f64, samples: usize) -> f64 {
if val <= 0.0 {
return 0.0;
}
match ease {
Easing::Linear => {
let delta = to - from;
if delta.abs() <= 1e-12 {
return (val * from).clamp(0.0, 1.0);
}
let denom_p = from * (val * delta).exp();
let p = (denom_p - from) / delta;
p.clamp(0.0, 1.0)
}
Easing::InConst => {
(val * from).clamp(0.0, 1.0)
}
Easing::OutConst => {
(val * to).clamp(0.0, 1.0)
}
Easing::InOutConst => {
let half_r = 0.5 / from;
if val <= half_r {
(val * from).clamp(0.0, 0.5)
} else {
0.5 + ((val - half_r) * to).clamp(0.0, 0.5)
}
}
_ => walk_solve_reciprocal(ease.as_easing(), from, to, val, samples),
}
}
fn walk_solve_reciprocal(
ease: AdvancedEasing,
from: f64,
to: f64,
target: f64,
samples: usize,
) -> f64 {
let n = if samples < 2 {
2
} else if samples % 2 == 1 {
samples + 1
} else {
samples
};
let h = 1.0 / n as f64;
let integrand = |s: f64| {
let e = easing_value(ease, s);
1.0 / (from * (1.0 - e) + to * e)
};
let npairs = n / 2;
let mut cum = 0.0_f64; let mut f_left = integrand(0.0);
for k in 0..npairs {
let f_mid = integrand(((2 * k + 1) as f64) * h);
let f_right = integrand(((2 * k + 2) as f64) * h);
let pair = (h / 3.0) * (f_left + 4.0 * f_mid + f_right);
if cum + pair >= target {
let frac = if pair > 0.0 && pair.is_finite() {
((target - cum) / pair).clamp(0.0, 1.0)
} else {
0.0
};
let s0 = (2 * k) as f64 * h;
let s2 = (2 * k + 2) as f64 * h;
return (s0 + frac * (s2 - s0)).clamp(0.0, 1.0);
}
cum += pair;
f_left = f_right;
}
1.0
}