geometry_algorithm/
line_interpolate.rs1use geometry_strategy::{CartesianLineInterpolate, LineInterpolateStrategy};
13use geometry_trait::Linestring;
14
15#[inline]
23#[must_use]
24pub fn line_interpolate<L>(ls: &L, t: f64) -> L::Point
25where
26 L: Linestring,
27 CartesianLineInterpolate: LineInterpolateStrategy<L>,
28{
29 CartesianLineInterpolate.interpolate(ls, t)
30}
31
32#[cfg(test)]
33#[allow(
34 clippy::float_cmp,
35 reason = "Interpolated coordinates are exact literals."
36)]
37mod tests {
38 use super::line_interpolate;
42 use geometry_cs::Cartesian;
43 use geometry_model::{Linestring, Point2D, linestring};
44 use geometry_trait::Point as _;
45
46 type Pt = Point2D<f64, Cartesian>;
47
48 fn close(got: Pt, x: f64, y: f64) -> bool {
49 (got.get::<0>() - x).abs() < 1e-9 && (got.get::<1>() - y).abs() < 1e-9
50 }
51
52 #[test]
53 fn t_zero_returns_first_point() {
54 let ls: Linestring<Pt> = linestring![(0., 0.), (10., 0.)];
55 assert!(close(line_interpolate(&ls, 0.0), 0., 0.));
56 }
57
58 #[test]
59 fn t_one_returns_last_point() {
60 let ls: Linestring<Pt> = linestring![(0., 0.), (10., 0.)];
61 assert!(close(line_interpolate(&ls, 1.0), 10., 0.));
62 }
63
64 #[test]
65 fn t_half_returns_midpoint() {
66 let ls: Linestring<Pt> = linestring![(0., 0.), (10., 0.)];
67 assert!(close(line_interpolate(&ls, 0.5), 5., 0.));
68 }
69
70 #[test]
71 fn t_at_segment_boundary_returns_vertex() {
72 let ls: Linestring<Pt> = linestring![(0., 0.), (2., 0.), (2., 3.)];
75 assert!(close(line_interpolate(&ls, 0.4), 2., 0.));
76 }
77
78 #[test]
79 fn t_inside_second_segment() {
80 let ls: Linestring<Pt> = linestring![(0., 0.), (2., 0.), (2., 3.)];
82 assert!(close(line_interpolate(&ls, 0.6), 2., 1.));
83 }
84
85 #[test]
88 fn degenerate_inputs() {
89 let empty: Linestring<Pt> = linestring![];
90 assert!(close(line_interpolate(&empty, 0.5), 0., 0.));
91 let single: Linestring<Pt> = linestring![(3., 4.)];
92 assert!(close(line_interpolate(&single, 0.5), 3., 4.));
93 }
94
95 #[test]
98 #[allow(clippy::float_cmp, reason = "midpoint ordinates are exact literals")]
99 fn three_d_midpoint_blends_z() {
100 use geometry_model::Point3D;
101 type P3 = Point3D<f64, Cartesian>;
102 let ls: Linestring<P3> =
103 Linestring::from_vec(alloc::vec![P3::new(0., 0., 0.), P3::new(10., 0., 4.)]);
104 let p = line_interpolate(&ls, 0.5);
105 assert_eq!(p.get::<0>(), 5.0);
106 assert_eq!(p.get::<2>(), 2.0);
107 }
108
109 #[test]
111 #[allow(clippy::float_cmp, reason = "midpoint ordinates are exact literals")]
112 fn four_d_midpoint_blends_all_ordinates() {
113 use geometry_model::Point;
114 use geometry_trait::PointMut as _;
115 type P4 = Point<f64, 4, Cartesian>;
116 let mut a = P4::default();
117 a.set::<3>(8.0);
118 let mut b = P4::default();
119 b.set::<0>(10.0);
120 let ls: Linestring<P4> = Linestring::from_vec(alloc::vec![a, b]);
121 let p = line_interpolate(&ls, 0.5);
122 assert_eq!(p.get::<0>(), 5.0);
123 assert_eq!(p.get::<3>(), 4.0);
124 }
125}