pub const METRES_PER_PX: f64 = 0.0254 / 96.0;
pub const CURVE_STEPS: usize = 32;
pub fn quadratic(from: [f32; 2], control: [f32; 2], to: [f32; 2], steps: usize) -> Vec<[f32; 2]> {
(1..=steps)
.map(|step| {
let t = step as f32 / steps as f32;
let u = 1.0 - t;
[0, 1].map(|axis| u * u * from[axis] + 2.0 * u * t * control[axis] + t * t * to[axis])
})
.collect()
}
pub fn cubic(
from: [f32; 2],
first: [f32; 2],
second: [f32; 2],
to: [f32; 2],
steps: usize,
) -> Vec<[f32; 2]> {
(1..=steps)
.map(|step| {
let t = step as f32 / steps as f32;
let u = 1.0 - t;
[0, 1].map(|axis| {
u * u * u * from[axis]
+ 3.0 * u * u * t * first[axis]
+ 3.0 * u * t * t * second[axis]
+ t * t * t * to[axis]
})
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn close(a: [f32; 2], b: [f32; 2]) -> bool {
(a[0] - b[0]).abs() < 1e-5 && (a[1] - b[1]).abs() < 1e-5
}
#[test]
fn a_quadratic_curve_ends_on_its_end_point() {
let points = quadratic([0.0, 0.0], [5.0, 10.0], [10.0, 0.0], CURVE_STEPS);
assert_eq!(points.len(), CURVE_STEPS);
assert!(close(points[CURVE_STEPS - 1], [10.0, 0.0]), "{points:?}");
assert!(close(points[CURVE_STEPS / 2 - 1], [5.0, 5.0]), "{points:?}");
}
#[test]
fn a_cubic_curve_bulges_toward_its_controls() {
let points = cubic([0.0, 0.0], [0.0, 8.0], [10.0, 8.0], [10.0, 0.0], 4);
assert!(close(points[3], [10.0, 0.0]), "{points:?}");
assert!(close(points[1], [5.0, 6.0]), "{points:?}");
}
}