trajectory/
cubic_spline.rs
1use super::traits::*;
2use super::utils::*;
3use num_traits::Float;
4
5fn convert<T>(val: f64) -> T
6where
7 T: Float,
8{
9 T::from(val).unwrap()
10}
11
12#[derive(Debug)]
13pub struct CubicSpline<T>
14where
15 T: Float,
16{
17 times: Vec<T>,
18 points: Vec<Vec<T>>,
19 a: Vec<Vec<T>>,
20 b: Vec<Vec<T>>,
21 c: Vec<Vec<T>>,
22 d: Vec<Vec<T>>,
23}
24
25impl<T> CubicSpline<T>
27where
28 T: Float,
29{
30 #[allow(clippy::many_single_char_names, clippy::just_underscores_and_digits)]
31 pub fn new(times: Vec<T>, points: Vec<Vec<T>>) -> Option<Self> {
32 if !is_inputs_valid(×, &points) {
33 return None;
34 }
35 let dim = points[0].len();
36 let _0: T = T::zero();
37 let _1: T = T::one();
38 let _2: T = convert(2.0);
39 let _3: T = convert(3.0);
40
41 let n = points.len() - 1;
44
45 let a = points.clone();
47 let mut h = vec![_0; n];
49 for i in 0..n {
50 h[i] = times[i + 1] - times[i];
51 }
52 let mut alpha = vec![vec![_0; dim]; n];
53 for i in 1..n {
54 for j in 0..dim {
55 alpha[i][j] = (_3 / h[i] * (a[i + 1][j] - a[i][j]))
56 - (_3 / h[i - 1] * (a[i][j] - a[i - 1][j]));
57 }
58 }
59 let mut b = vec![vec![_0; dim]; n];
60 let mut c = vec![vec![_0; dim]; n + 1];
61 let mut d = vec![vec![_0; dim]; n];
62 let mut l = vec![_1; n + 1];
63 let mut m = vec![_0; n + 1];
64 let mut z = vec![vec![_0; dim]; n + 1];
65 for i in 1..n {
66 l[i] = _2 * (times[i + 1] - times[i - 1]) - h[i - 1] * m[i - 1];
67 m[i] = h[i] / l[i];
68 for j in 0..dim {
69 z[i][j] = (alpha[i][j] - h[i - 1] * z[i - 1][j]) / l[i];
70 }
71 }
72 l[n] = _1;
73 for j_ in 1..n + 1 {
74 let j = n - j_;
75 for k in 0..dim {
76 c[j][k] = z[j][k] - m[j] * c[j + 1][k];
77 }
78 for k in 0..dim {
79 b[j][k] =
80 (a[j + 1][k] - a[j][k]) / h[j] - (h[j] * (c[j + 1][k] + _2 * c[j][k]) / _3);
81 }
82 for k in 0..dim {
83 d[j][k] = (c[j + 1][k] - c[j][k]) / (_3 * h[j]);
84 }
85 }
86 Some(Self {
87 times,
88 points,
89 a,
90 b,
91 c,
92 d,
93 })
94 }
95}
96
97impl<T> Trajectory for CubicSpline<T>
98where
99 T: Float,
100{
101 type Point = Vec<T>;
102 type Time = T;
103
104 fn position(&self, t: T) -> Option<Vec<T>> {
105 if t < self.times[0] {
106 return None;
107 }
108 let dim = self.points[0].len();
109 for i in 0..(self.points.len() - 1) {
110 if t >= self.times[i] && t <= self.times[i + 1] {
111 let mut pt = vec![T::zero(); dim];
112 let dx = t - self.times[i];
113 for (j, point_iter) in pt.iter_mut().enumerate() {
114 *point_iter = self.a[i][j]
115 + self.b[i][j] * dx
116 + self.c[i][j] * dx * dx
117 + self.d[i][j] * dx * dx * dx;
118 }
119 return Some(pt);
120 }
121 }
122 None
123 }
124
125 fn velocity(&self, t: T) -> Option<Vec<T>> {
126 if t < self.times[0] {
127 return None;
128 }
129 let dim = self.points[0].len();
130 for i in 0..(self.points.len() - 1) {
131 if t >= self.times[i] && t <= self.times[i + 1] {
132 let mut pt = vec![T::zero(); dim];
133 let dx = t - self.times[i];
134 for (j, point_iter) in pt.iter_mut().enumerate() {
135 *point_iter = self.b[i][j]
136 + convert::<T>(2.0) * self.c[i][j] * dx
137 + convert::<T>(3.0) * self.d[i][j] * dx * dx;
138 }
139 return Some(pt);
140 }
141 }
142 None
143 }
144
145 fn acceleration(&self, t: T) -> Option<Vec<T>> {
146 if t < self.times[0] {
147 return None;
148 }
149 let dim = self.points[0].len();
150 for i in 0..(self.points.len() - 1) {
151 if t >= self.times[i] && t <= self.times[i + 1] {
152 let mut pt = vec![T::zero(); dim];
153 let dx = t - self.times[i];
154 for (j, point_iter) in pt.iter_mut().enumerate() {
155 *point_iter =
156 convert::<T>(2.0) * self.c[i][j] + convert::<T>(6.0) * self.d[i][j] * dx;
157 }
158 return Some(pt);
159 }
160 }
161 None
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 #[test]
169 fn test_spline() {
170 let times = vec![0.0, 1.0, 3.0, 4.0];
171 let points = vec![
172 vec![0.0, -1.0],
173 vec![2.0, -3.0],
174 vec![3.0, 3.0],
175 vec![1.0, 5.0],
176 ];
177 let ip = CubicSpline::new(times, points).unwrap();
178 for i in 0..400 {
179 let t = i as f64 * 0.01f64;
180 let p = ip.position(t).unwrap();
181 println!("{t} {} {}", p[0], p[1]);
182 }
183 }
184
185 #[test]
186 fn test_velocity() {
187 let times = vec![0.0, 1.0, 3.0, 4.0];
188 let points = vec![
189 vec![0.0, -1.0],
190 vec![2.0, -3.0],
191 vec![3.0, 3.0],
192 vec![1.0, 5.0],
193 ];
194 let ip = CubicSpline::new(times, points).unwrap();
195 for i in 0..400 {
196 let t = i as f64 * 0.01f64;
197 let p = ip.velocity(t).unwrap();
198 println!("{t} {} {}", p[0], p[1]);
199 }
200 }
201
202 #[test]
203 fn test_acceleration() {
204 let times = vec![0.0, 1.0, 3.0, 4.0];
205 let points = vec![
206 vec![0.0, -1.0],
207 vec![2.0, -3.0],
208 vec![3.0, 3.0],
209 vec![1.0, 5.0],
210 ];
211 let ip = CubicSpline::new(times, points).unwrap();
212 for i in 0..400 {
213 let t = i as f64 * 0.01f64;
214 let p = ip.acceleration(t).unwrap();
215 println!("{t} {} {}", p[0], p[1]);
216 }
217 }
218}