use geometry_strategy::{CartesianLineInterpolate, LineInterpolateStrategy};
use geometry_trait::Linestring;
#[inline]
#[must_use]
pub fn line_interpolate<L>(ls: &L, t: f64) -> L::Point
where
L: Linestring,
CartesianLineInterpolate: LineInterpolateStrategy<L>,
{
CartesianLineInterpolate.interpolate(ls, t)
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
reason = "Interpolated coordinates are exact literals."
)]
mod tests {
use super::line_interpolate;
use geometry_cs::Cartesian;
use geometry_model::{Linestring, Point2D, linestring};
use geometry_trait::Point as _;
type Pt = Point2D<f64, Cartesian>;
fn close(got: Pt, x: f64, y: f64) -> bool {
(got.get::<0>() - x).abs() < 1e-9 && (got.get::<1>() - y).abs() < 1e-9
}
#[test]
fn t_zero_returns_first_point() {
let ls: Linestring<Pt> = linestring![(0., 0.), (10., 0.)];
assert!(close(line_interpolate(&ls, 0.0), 0., 0.));
}
#[test]
fn t_one_returns_last_point() {
let ls: Linestring<Pt> = linestring![(0., 0.), (10., 0.)];
assert!(close(line_interpolate(&ls, 1.0), 10., 0.));
}
#[test]
fn t_half_returns_midpoint() {
let ls: Linestring<Pt> = linestring![(0., 0.), (10., 0.)];
assert!(close(line_interpolate(&ls, 0.5), 5., 0.));
}
#[test]
fn t_at_segment_boundary_returns_vertex() {
let ls: Linestring<Pt> = linestring![(0., 0.), (2., 0.), (2., 3.)];
assert!(close(line_interpolate(&ls, 0.4), 2., 0.));
}
#[test]
fn t_inside_second_segment() {
let ls: Linestring<Pt> = linestring![(0., 0.), (2., 0.), (2., 3.)];
assert!(close(line_interpolate(&ls, 0.6), 2., 1.));
}
#[test]
fn degenerate_inputs() {
let empty: Linestring<Pt> = linestring![];
assert!(close(line_interpolate(&empty, 0.5), 0., 0.));
let single: Linestring<Pt> = linestring![(3., 4.)];
assert!(close(line_interpolate(&single, 0.5), 3., 4.));
}
#[test]
#[allow(clippy::float_cmp, reason = "midpoint ordinates are exact literals")]
fn three_d_midpoint_blends_z() {
use geometry_model::Point3D;
type P3 = Point3D<f64, Cartesian>;
let ls: Linestring<P3> =
Linestring::from_vec(alloc::vec![P3::new(0., 0., 0.), P3::new(10., 0., 4.)]);
let p = line_interpolate(&ls, 0.5);
assert_eq!(p.get::<0>(), 5.0);
assert_eq!(p.get::<2>(), 2.0);
}
#[test]
#[allow(clippy::float_cmp, reason = "midpoint ordinates are exact literals")]
fn four_d_midpoint_blends_all_ordinates() {
use geometry_model::Point;
use geometry_trait::PointMut as _;
type P4 = Point<f64, 4, Cartesian>;
let mut a = P4::default();
a.set::<3>(8.0);
let mut b = P4::default();
b.set::<0>(10.0);
let ls: Linestring<P4> = Linestring::from_vec(alloc::vec![a, b]);
let p = line_interpolate(&ls, 0.5);
assert_eq!(p.get::<0>(), 5.0);
assert_eq!(p.get::<3>(), 4.0);
}
}