use crate::{points_iter::PointsIter, Error, Point, Points, Result, SplineOpts};
pub fn calc_spline(points: &Points, opts: &SplineOpts) -> Result<Points> {
let points_len = points.get_ref().len();
if points_len < 2 {
return Err(Error::TooFewPoints);
}
let tension_from_opt = opts.get_tension();
let num_of_segments = opts.get_num_of_segments();
let num_of_segments_f64 = f64::from(num_of_segments);
let generated_count = (points_len - 1) * (num_of_segments as usize) + 1;
let mut result: Vec<Point> = Vec::with_capacity(generated_count);
let iter = PointsIter::new(points, opts);
for (prev, curr, next, next2) in iter {
let tension = curr.tension.unwrap_or(tension_from_opt);
let t1x = (next.x - prev.x) * tension;
let t2x = (next2.x - curr.x) * tension;
let t1y = (next.y - prev.y) * tension;
let t2y = (next2.y - curr.y) * tension;
for t in 0..num_of_segments {
let st = f64::from(t) / num_of_segments_f64;
let st_pow2 = st.powi(2);
let st_pow3 = st.powi(3);
let st_pow2x3 = 3.0 * st_pow2;
let st_pow3x2 = 2.0 * st_pow3;
let c1 = st_pow3x2 - st_pow2x3 + 1.0;
let c2 = -st_pow3x2 + st_pow2x3;
let c3 = st_pow3 - 2.0 * st_pow2 + st;
let c4 = st_pow3 - st_pow2;
let x = c1 * curr.x + c2 * next.x + c3 * t1x + c4 * t2x;
let y = c1 * curr.y + c2 * next.y + c3 * t1y + c4 * t2y;
result.push(Point::new(x, y));
}
}
if let Some(last) = points.get_ref().last() {
result.push(Point::new(last.x, last.y));
}
Ok(Points::from(result))
}