use crate::interpolate::lerp::linear_interpolator;
pub type ChannelInterp = Box<dyn Fn(f64) -> f64 + Send + Sync>;
pub type ChannelInterpFactory = Box<dyn Fn(&[f64]) -> ChannelInterp + Send + Sync>;
#[inline]
fn modulo(v: i64, l: i64) -> usize {
(((v % l) + l) % l) as usize
}
#[inline]
fn bspline(v_im2: f64, v_im1: f64, v_i: f64, v_ip1: f64, t: f64) -> f64 {
let t2 = t * t;
let t3 = t2 * t;
((1.0 - 3.0 * t + 3.0 * t2 - t3) * v_im2
+ (4.0 - 6.0 * t2 + 3.0 * t3) * v_im1
+ (1.0 + 3.0 * t + 3.0 * t2 - 3.0 * t3) * v_i
+ t3 * v_ip1)
/ 6.0
}
pub fn interpolator_spline_basis() -> ChannelInterpFactory {
Box::new(|arr: &[f64]| -> ChannelInterp {
let arr: Vec<f64> = arr.to_vec();
Box::new(move |t: f64| {
let n = arr.len();
if n == 0 {
return f64::NAN;
}
if n == 1 {
return arr[0];
}
let classes = (n - 1) as i64;
let i = if t >= 1.0 {
classes - 1
} else {
((t * classes as f64).floor() as i64).max(0)
};
let i_us = i as usize;
let v_im2 = if i > 0 {
arr[i_us - 1]
} else {
2.0 * arr[i_us] - arr[i_us + 1]
};
let v_im1 = arr[i_us];
let v_i = arr[i_us + 1];
let v_ip1 = if i < classes - 1 {
arr[i_us + 2]
} else {
2.0 * arr[i_us + 1] - arr[i_us]
};
let local_t = (t - i as f64 / classes as f64) * classes as f64;
bspline(v_im2, v_im1, v_i, v_ip1, local_t)
})
})
}
pub fn interpolator_spline_basis_closed() -> ChannelInterpFactory {
Box::new(|arr: &[f64]| -> ChannelInterp {
let arr: Vec<f64> = arr.to_vec();
Box::new(move |t: f64| {
let n = arr.len();
if n == 0 {
return f64::NAN;
}
if n == 1 {
return arr[0];
}
let classes = (n - 1) as i64;
let i = (t * classes as f64).floor() as i64;
let len = arr.len() as i64;
let v_im2 = arr[modulo(i - 1, len)];
let v_im1 = arr[modulo(i, len)];
let v_i = arr[modulo(i + 1, len)];
let v_ip1 = arr[modulo(i + 2, len)];
let local_t = (t - i as f64 / classes as f64) * classes as f64;
bspline(v_im2, v_im1, v_i, v_ip1, local_t)
})
})
}
fn natural_solve(v: &[f64]) -> Vec<f64> {
let n = v.len() - 1;
let mut c = vec![0.0_f64; n];
let mut _v = vec![0.0_f64; n];
let mut sol = vec![0.0_f64; n + 1];
if n >= 2 {
c[1] = 0.25;
_v[1] = (6.0 * v[1] - v[0]) / 4.0;
for i in 2..n {
c[i] = 1.0 / (4.0 - c[i - 1]);
let term_n = if i == n - 1 { v[n] } else { 0.0 };
_v[i] = (6.0 * v[i] - term_n - _v[i - 1]) * c[i];
}
}
sol[0] = v[0];
sol[n] = v[n];
if n >= 2 {
sol[n - 1] = _v[n - 1];
for i in (1..=(n - 2)).rev() {
sol[i] = _v[i] - c[i] * sol[i + 1];
}
}
sol
}
pub fn interpolator_spline_natural() -> ChannelInterpFactory {
let basis = interpolator_spline_basis();
Box::new(move |arr: &[f64]| -> ChannelInterp {
if arr.len() < 2 {
return basis(arr);
}
let solved = natural_solve(arr);
basis(&solved)
})
}
pub fn interpolator_spline_natural_closed() -> ChannelInterpFactory {
let basis_closed = interpolator_spline_basis_closed();
Box::new(move |arr: &[f64]| -> ChannelInterp {
if arr.len() < 2 {
return basis_closed(arr);
}
let solved = natural_solve(arr);
basis_closed(&solved)
})
}
#[inline]
fn sgn(v: f64) -> f64 {
if v > 0.0 {
1.0
} else if v < 0.0 {
-1.0
} else {
0.0
}
}
fn mono(arr: &[f64]) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
let n = (arr.len() - 1) as f64;
let len = arr.len() - 1;
let mut s = Vec::with_capacity(len);
let mut p = vec![f64::NAN; len + 1];
let mut yp = vec![f64::NAN; len + 1];
for i in 0..len {
s.push((arr[i + 1] - arr[i]) * n);
if i > 0 {
let p_i = 0.5 * (arr[i + 1] - arr[i - 1]) * n;
p[i] = p_i;
yp[i] =
(sgn(s[i - 1]) + sgn(s[i])) * s[i - 1].abs().min(s[i].abs()).min(0.5 * p_i.abs());
}
}
(s, p, yp)
}
fn evaluate_monotone(arr: Vec<f64>, yp: Vec<f64>, s: Vec<f64>) -> ChannelInterp {
Box::new(move |t: f64| {
let n = (arr.len() - 1) as f64;
let n_us = arr.len() - 1;
let n2 = n * n;
let i = if t >= 1.0 {
n_us - 1
} else {
((t * n).floor() as isize).max(0) as usize
};
let t1 = t - i as f64 / n;
let t2 = t1 * t1;
let t3 = t2 * t1;
(yp[i] + yp[i + 1] - 2.0 * s[i]) * n2 * t3
+ (3.0 * s[i] - 2.0 * yp[i] - yp[i + 1]) * n * t2
+ yp[i] * t1
+ arr[i]
})
}
pub fn interpolator_spline_monotone() -> ChannelInterpFactory {
Box::new(|arr: &[f64]| -> ChannelInterp {
if arr.len() < 3 {
return Box::new(linear_interpolator(arr.to_vec()));
}
let (s, _p, mut yp) = mono(arr);
let n = arr.len() - 1;
yp[0] = s[0];
yp[n] = s[n - 1];
evaluate_monotone(arr.to_vec(), yp, s)
})
}
pub fn interpolator_spline_monotone_2() -> ChannelInterpFactory {
Box::new(|arr: &[f64]| -> ChannelInterp {
if arr.len() < 3 {
return Box::new(linear_interpolator(arr.to_vec()));
}
let (s, mut p, mut yp) = mono(arr);
let n_us = arr.len() - 1;
let n = n_us as f64;
p[0] = (arr[1] * 2.0 - arr[0] * 1.5 - arr[2] * 0.5) * n;
p[n_us] = (arr[n_us] * 1.5 - arr[n_us - 1] * 2.0 + arr[n_us - 2] * 0.5) * n;
yp[0] = if p[0] * s[0] <= 0.0 {
0.0
} else if p[0].abs() > 2.0 * s[0].abs() {
2.0 * s[0]
} else {
p[0]
};
yp[n_us] = if p[n_us] * s[n_us - 1] <= 0.0 {
0.0
} else if p[n_us].abs() > 2.0 * s[n_us - 1].abs() {
2.0 * s[n_us - 1]
} else {
p[n_us]
};
evaluate_monotone(arr.to_vec(), yp, s)
})
}
pub fn interpolator_spline_monotone_closed() -> ChannelInterpFactory {
Box::new(|arr: &[f64]| -> ChannelInterp {
if arr.len() < 3 {
return Box::new(linear_interpolator(arr.to_vec()));
}
let (s, mut p, mut yp) = mono(arr);
let n_us = arr.len() - 1;
let n = n_us as f64;
p[0] = 0.5 * (arr[1] - arr[n_us]) * n;
p[n_us] = 0.5 * (arr[0] - arr[n_us - 1]) * n;
let s_m1 = (arr[0] - arr[n_us]) * n;
let s_n = s_m1;
yp[0] = (sgn(s_m1) + sgn(s[0])) * s_m1.abs().min(s[0].abs()).min(0.5 * p[0].abs());
yp[n_us] = (sgn(s[n_us - 1]) + sgn(s_n))
* s[n_us - 1].abs().min(s_n.abs()).min(0.5 * p[n_us].abs());
evaluate_monotone(arr.to_vec(), yp, s)
})
}