use crate::core::pbrt::{Float, lerp}; use crate::geometry::{Bounds3f, Lerp, Point3f, Vector3f, VectorLike}; use num_traits::Num; fn bounds_cubic_bezier(cp: &[Point3f]) -> Bounds3f { Bounds3f::from_points(cp[0], cp[1]).union(Bounds3f::from_points(cp[2], cp[3])) } pub fn bound_cubic_bezier(cp: &[Point3f], u_min: Float, u_max: Float) -> Bounds3f { if u_min == 0. && u_max == 1. { return bounds_cubic_bezier(cp); } let cp_seg = cubic_bezier_control_points(cp, u_min, u_max); bounds_cubic_bezier(&cp_seg) } pub fn cubic_bezier_control_points(cp: &[Point3f], u_min: Float, u_max: Float) -> [Point3f; 4] { [ blossom_cubic_bezier(cp, u_min, u_max, u_min), blossom_cubic_bezier(cp, u_min, u_min, u_max), blossom_cubic_bezier(cp, u_min, u_max, u_max), blossom_cubic_bezier(cp, u_max, u_max, u_max), ] } fn blossom_cubic_bezier(p: &[P], u0: F, u1: F, u2: F) -> P where F: Copy + Num, P: Lerp, { let a: [P; 3] = [ lerp(u0, p[0], p[1]), lerp(u0, p[1], p[2]), lerp(u0, p[2], p[3]), ]; let b: [P; 2] = [lerp(u1, a[0], a[1]), lerp(u1, a[1], a[2])]; lerp(u2, b[0], b[1]) } pub fn subdivide_cubic_bezier(cp: &[Point3f]) -> [Point3f; 7] { let v: Vec = cp.iter().map(|&p| p.into()).collect(); let v01 = (v[0] + v[1]) / 2.0; let v12 = (v[1] + v[2]) / 2.0; let v23 = (v[2] + v[3]) / 2.0; let v012 = (v01 + v12) / 2.0; let v123 = (v12 + v23) / 2.0; let v0123 = (v012 + v123) / 2.0; [ cp[0], v01.into(), v012.into(), v0123.into(), v123.into(), v23.into(), cp[3], ] } pub fn evaluate_cubic_bezier(cp: &[Point3f], u: Float) -> (Point3f, Vector3f) { let cp1 = [ lerp(u, cp[0], cp[1]), lerp(u, cp[1], cp[2]), lerp(u, cp[2], cp[3]), ]; let cp2 = [lerp(u, cp1[0], cp1[1]), lerp(u, cp1[1], cp1[2])]; let deriv = if (cp2[1] - cp2[0]).norm_squared() > 0. { (cp2[1] - cp2[0]) * 3. } else { cp[3] - cp[0] }; (lerp(u, cp2[0], cp2[1]), deriv) }