1use crate::data::Point2D;
13use crate::error::{ChartError, ChartResult};
14use heapless::Vec;
15
16pub const MAX_INTERPOLATED_POINTS: usize = 512;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum InterpolationType {
22 Linear,
24 CubicSpline,
26 CatmullRom,
28 Bezier,
30}
31
32#[derive(Debug, Clone)]
34pub struct InterpolationConfig {
35 pub interpolation_type: InterpolationType,
37 pub subdivisions: u32,
39 pub tension: f32,
41 pub closed: bool,
43}
44
45impl Default for InterpolationConfig {
46 fn default() -> Self {
47 Self {
48 interpolation_type: InterpolationType::CubicSpline,
49 subdivisions: 8,
50 tension: 0.5,
51 closed: false,
52 }
53 }
54}
55
56pub struct CurveInterpolator;
58
59impl CurveInterpolator {
60 pub fn interpolate(
69 points: &[Point2D],
70 config: &InterpolationConfig,
71 ) -> ChartResult<Vec<Point2D, MAX_INTERPOLATED_POINTS>> {
72 if points.len() < 2 {
73 return Err(ChartError::InsufficientData);
74 }
75
76 match config.interpolation_type {
77 InterpolationType::Linear => Self::linear_interpolation(points, config),
78 InterpolationType::CubicSpline => Self::cubic_spline_interpolation(points, config),
79 InterpolationType::CatmullRom => Self::catmull_rom_interpolation(points, config),
80 InterpolationType::Bezier => Self::bezier_interpolation(points, config),
81 }
82 }
83
84 fn linear_interpolation(
86 points: &[Point2D],
87 config: &InterpolationConfig,
88 ) -> ChartResult<Vec<Point2D, MAX_INTERPOLATED_POINTS>> {
89 let mut result = Vec::new();
90
91 for i in 0..points.len() - 1 {
92 let p0 = points[i];
93 let p1 = points[i + 1];
94
95 result.push(p0).map_err(|_| ChartError::MemoryFull)?;
97
98 for j in 1..config.subdivisions {
100 let t = j as f32 / config.subdivisions as f32;
101 let x = p0.x + t * (p1.x - p0.x);
102 let y = p0.y + t * (p1.y - p0.y);
103 result
104 .push(Point2D::new(x, y))
105 .map_err(|_| ChartError::MemoryFull)?;
106 }
107 }
108
109 if let Some(last) = points.last() {
111 result.push(*last).map_err(|_| ChartError::MemoryFull)?;
112 }
113
114 Ok(result)
115 }
116
117 fn cubic_spline_interpolation(
119 points: &[Point2D],
120 config: &InterpolationConfig,
121 ) -> ChartResult<Vec<Point2D, MAX_INTERPOLATED_POINTS>> {
122 let mut result = Vec::new();
123 let n = points.len();
124
125 if n < 3 {
126 return Self::linear_interpolation(points, config);
127 }
128
129 let mut derivatives = Vec::<f32, 256>::new();
131 for _i in 0..n {
132 derivatives.push(0.0).map_err(|_| ChartError::MemoryFull)?;
133 }
134
135 for i in 1..n - 1 {
137 let h1 = points[i].x - points[i - 1].x;
138 let h2 = points[i + 1].x - points[i].x;
139 let delta1 = (points[i].y - points[i - 1].y) / h1;
140 let delta2 = (points[i + 1].y - points[i].y) / h2;
141 derivatives[i] = 2.0 * (delta2 - delta1) / (h1 + h2);
142 }
143
144 for i in 0..n - 1 {
146 let p0 = points[i];
147 let p1 = points[i + 1];
148 let d0 = derivatives[i];
149 let d1 = derivatives[i + 1];
150
151 result.push(p0).map_err(|_| ChartError::MemoryFull)?;
152
153 let h = p1.x - p0.x;
154 for j in 1..config.subdivisions {
155 let t = j as f32 / config.subdivisions as f32;
156 let t2 = t * t;
157 let t3 = t2 * t;
158
159 let h00 = 2.0 * t3 - 3.0 * t2 + 1.0;
161 let h10 = t3 - 2.0 * t2 + t;
162 let h01 = -2.0 * t3 + 3.0 * t2;
163 let h11 = t3 - t2;
164
165 let x = p0.x + t * h;
166 let y = h00 * p0.y + h10 * h * d0 + h01 * p1.y + h11 * h * d1;
167
168 result
169 .push(Point2D::new(x, y))
170 .map_err(|_| ChartError::MemoryFull)?;
171 }
172 }
173
174 if let Some(last) = points.last() {
175 result.push(*last).map_err(|_| ChartError::MemoryFull)?;
176 }
177
178 Ok(result)
179 }
180
181 fn catmull_rom_interpolation(
183 points: &[Point2D],
184 config: &InterpolationConfig,
185 ) -> ChartResult<Vec<Point2D, MAX_INTERPOLATED_POINTS>> {
186 let mut result = Vec::new();
187 let n = points.len();
188
189 if n < 3 {
190 return Self::linear_interpolation(points, config);
191 }
192
193 for i in 0..n - 1 {
195 let p0 = if i == 0 { points[0] } else { points[i - 1] };
197 let p1 = points[i];
198 let p2 = points[i + 1];
199 let p3 = if i + 2 < n {
200 points[i + 2]
201 } else {
202 points[n - 1]
203 };
204
205 result.push(p1).map_err(|_| ChartError::MemoryFull)?;
206
207 for j in 1..config.subdivisions {
209 let t = j as f32 / config.subdivisions as f32;
210 let t2 = t * t;
211 let t3 = t2 * t;
212
213 let x = 0.5
215 * ((2.0 * p1.x)
216 + (-p0.x + p2.x) * t
217 + (2.0 * p0.x - 5.0 * p1.x + 4.0 * p2.x - p3.x) * t2
218 + (-p0.x + 3.0 * p1.x - 3.0 * p2.x + p3.x) * t3);
219
220 let y = 0.5
221 * ((2.0 * p1.y)
222 + (-p0.y + p2.y) * t
223 + (2.0 * p0.y - 5.0 * p1.y + 4.0 * p2.y - p3.y) * t2
224 + (-p0.y + 3.0 * p1.y - 3.0 * p2.y + p3.y) * t3);
225
226 result
227 .push(Point2D::new(x, y))
228 .map_err(|_| ChartError::MemoryFull)?;
229 }
230 }
231
232 if let Some(last) = points.last() {
233 result.push(*last).map_err(|_| ChartError::MemoryFull)?;
234 }
235
236 Ok(result)
237 }
238
239 fn bezier_interpolation(
241 points: &[Point2D],
242 config: &InterpolationConfig,
243 ) -> ChartResult<Vec<Point2D, MAX_INTERPOLATED_POINTS>> {
244 let mut result = Vec::new();
245 let n = points.len();
246
247 if n < 3 {
248 return Self::linear_interpolation(points, config);
249 }
250
251 for i in 0..n - 1 {
253 let p0 = points[i];
254 let p2 = points[i + 1];
255
256 let mid_x = (p0.x + p2.x) * 0.5;
258 let mid_y = (p0.y + p2.y) * 0.5;
259
260 let offset_x = (p2.y - p0.y) * config.tension * 0.2;
262 let offset_y = (p0.x - p2.x) * config.tension * 0.2;
263
264 let p1 = Point2D::new(mid_x + offset_x, mid_y + offset_y);
265
266 result.push(p0).map_err(|_| ChartError::MemoryFull)?;
267
268 for j in 1..config.subdivisions {
270 let t = j as f32 / config.subdivisions as f32;
271 let one_minus_t = 1.0 - t;
272 let t2 = t * t;
273 let one_minus_t2 = one_minus_t * one_minus_t;
274
275 let x = one_minus_t2 * p0.x + 2.0 * one_minus_t * t * p1.x + t2 * p2.x;
276 let y = one_minus_t2 * p0.y + 2.0 * one_minus_t * t * p1.y + t2 * p2.y;
277
278 result
279 .push(Point2D::new(x, y))
280 .map_err(|_| ChartError::MemoryFull)?;
281 }
282 }
283
284 if let Some(last) = points.last() {
285 result.push(*last).map_err(|_| ChartError::MemoryFull)?;
286 }
287
288 Ok(result)
289 }
290
291 pub fn smooth_point(
293 points: &[Point2D],
294 index: usize,
295 smoothing_factor: f32,
296 ) -> ChartResult<Point2D> {
297 if index >= points.len() {
298 return Err(ChartError::InvalidRange);
299 }
300
301 let n = points.len();
302 if n < 3 || index == 0 || index == n - 1 {
303 return Ok(points[index]);
304 }
305
306 let prev = points[index - 1];
307 let curr = points[index];
308 let next = points[index + 1];
309
310 let factor = smoothing_factor.clamp(0.0, 1.0);
312 let smoothed_x = curr.x * (1.0 - factor) + (prev.x + next.x) * 0.5 * factor;
313 let smoothed_y = curr.y * (1.0 - factor) + (prev.y + next.y) * 0.5 * factor;
314
315 Ok(Point2D::new(smoothed_x, smoothed_y))
316 }
317
318 pub fn smooth_series(
320 points: &[Point2D],
321 smoothing_factor: f32,
322 iterations: u32,
323 ) -> ChartResult<Vec<Point2D, 256>> {
324 let mut working_points = Vec::new();
325 for point in points {
326 working_points
327 .push(*point)
328 .map_err(|_| ChartError::MemoryFull)?;
329 }
330
331 for _ in 0..iterations {
332 let mut smoothed = Vec::new();
333 for i in 0..working_points.len() {
334 let smoothed_point = Self::smooth_point(&working_points, i, smoothing_factor)?;
335 smoothed
336 .push(smoothed_point)
337 .map_err(|_| ChartError::MemoryFull)?;
338 }
339 working_points = smoothed;
340 }
341
342 Ok(working_points)
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_linear_interpolation() {
352 let mut points = heapless::Vec::<Point2D, 16>::new();
353 points.push(Point2D::new(0.0, 0.0)).unwrap();
354 points.push(Point2D::new(1.0, 1.0)).unwrap();
355 let config = InterpolationConfig {
356 interpolation_type: InterpolationType::Linear,
357 subdivisions: 4,
358 ..Default::default()
359 };
360
361 let result = CurveInterpolator::interpolate(&points, &config).unwrap();
362 assert!(result.len() > 2);
363 assert_eq!(result[0], points[0]);
364 assert_eq!(result[result.len() - 1], points[1]);
365 }
366
367 #[test]
368 fn test_catmull_rom_interpolation() {
369 let mut points = heapless::Vec::<Point2D, 16>::new();
370 points.push(Point2D::new(0.0, 0.0)).unwrap();
371 points.push(Point2D::new(1.0, 1.0)).unwrap();
372 points.push(Point2D::new(2.0, 0.0)).unwrap();
373 let config = InterpolationConfig {
374 interpolation_type: InterpolationType::CatmullRom,
375 subdivisions: 8,
376 ..Default::default()
377 };
378
379 let result = CurveInterpolator::interpolate(&points, &config).unwrap();
380 assert!(result.len() > points.len());
381 }
382
383 #[test]
384 fn test_point_smoothing() {
385 let mut points = heapless::Vec::<Point2D, 16>::new();
386 points.push(Point2D::new(0.0, 0.0)).unwrap();
387 points.push(Point2D::new(1.0, 10.0)).unwrap(); points.push(Point2D::new(2.0, 0.0)).unwrap();
389
390 let smoothed = CurveInterpolator::smooth_point(&points, 1, 0.5).unwrap();
391 assert!(smoothed.y < 10.0); assert!(smoothed.y > 0.0); }
394
395 #[test]
396 fn test_series_smoothing() {
397 let mut points = heapless::Vec::<Point2D, 16>::new();
398 points.push(Point2D::new(0.0, 0.0)).unwrap();
399 points.push(Point2D::new(1.0, 10.0)).unwrap();
400 points.push(Point2D::new(2.0, 0.0)).unwrap();
401 points.push(Point2D::new(3.0, 10.0)).unwrap();
402 points.push(Point2D::new(4.0, 0.0)).unwrap();
403
404 let smoothed = CurveInterpolator::smooth_series(&points, 0.3, 2).unwrap();
405 assert_eq!(smoothed.len(), points.len());
406
407 assert!(smoothed[1].y < points[1].y);
409 assert!(smoothed[3].y < points[3].y);
410 }
411}