#![allow(dead_code)]
pub fn catmull_rom_f32(p0: f32, p1: f32, p2: f32, p3: f32, t: f32) -> f32 {
let t2 = t * t;
let t3 = t2 * t;
0.5 * ((2.0 * p1)
+ (-p0 + p2) * t
+ (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2
+ (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3)
}
pub fn catmull_rom_2d(p0: [f32; 2], p1: [f32; 2], p2: [f32; 2], p3: [f32; 2], t: f32) -> [f32; 2] {
[
catmull_rom_f32(p0[0], p1[0], p2[0], p3[0], t),
catmull_rom_f32(p0[1], p1[1], p2[1], p3[1], t),
]
}
pub fn catmull_rom_3d(p0: [f32; 3], p1: [f32; 3], p2: [f32; 3], p3: [f32; 3], t: f32) -> [f32; 3] {
[
catmull_rom_f32(p0[0], p1[0], p2[0], p3[0], t),
catmull_rom_f32(p0[1], p1[1], p2[1], p3[1], t),
catmull_rom_f32(p0[2], p1[2], p2[2], p3[2], t),
]
}
pub fn catmull_rom_chain_2d(points: &[[f32; 2]], steps_per_segment: usize) -> Vec<[f32; 2]> {
let n = points.len();
if n < 4 {
return vec![];
}
let mut result = Vec::new();
for seg in 0..(n - 3) {
let p0 = points[seg];
let p1 = points[seg + 1];
let p2 = points[seg + 2];
let p3 = points[seg + 3];
for step in 0..steps_per_segment {
let t = step as f32 / steps_per_segment as f32;
result.push(catmull_rom_2d(p0, p1, p2, p3, t));
}
}
result
}
pub fn catmull_rom_tangent_f32(p0: f32, p1: f32, p2: f32, p3: f32, t: f32) -> f32 {
0.5 * ((-p0 + p2)
+ 2.0 * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t
+ 3.0 * (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t * t)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_catmull_rom_at_t0() {
let v = catmull_rom_f32(0.0, 1.0, 2.0, 3.0, 0.0);
assert!((v - 1.0).abs() < 1e-6);
}
#[test]
fn test_catmull_rom_at_t1() {
let v = catmull_rom_f32(0.0, 1.0, 2.0, 3.0, 1.0);
assert!((v - 2.0).abs() < 1e-6);
}
#[test]
fn test_catmull_rom_2d_endpoints() {
let p = catmull_rom_2d([0.0, 0.0], [1.0, 1.0], [2.0, 0.0], [3.0, 1.0], 0.0);
assert!((p[0] - 1.0).abs() < 1e-5);
assert!((p[1] - 1.0).abs() < 1e-5);
let p = catmull_rom_2d([0.0, 0.0], [1.0, 1.0], [2.0, 0.0], [3.0, 1.0], 1.0);
assert!((p[0] - 2.0).abs() < 1e-5);
}
#[test]
fn test_catmull_rom_3d_endpoints() {
let p = catmull_rom_3d(
[0.0, 0.0, 0.0],
[1.0, 1.0, 1.0],
[2.0, 2.0, 2.0],
[3.0, 3.0, 3.0],
0.0,
);
assert!((p[0] - 1.0).abs() < 1e-5);
}
#[test]
fn test_chain_empty_too_short() {
let pts = vec![[0.0f32, 0.0], [1.0, 1.0]];
assert!(catmull_rom_chain_2d(&pts, 5).is_empty());
}
#[test]
fn test_chain_point_count() {
let pts = vec![[0.0f32, 0.0], [1.0, 0.0], [2.0, 0.0], [3.0, 0.0]];
let r = catmull_rom_chain_2d(&pts, 10);
assert_eq!(r.len(), 10);
}
#[test]
fn test_tangent_at_zero() {
let tan = catmull_rom_tangent_f32(0.0, 1.0, 2.0, 3.0, 0.0);
assert!(tan > 0.0);
}
}