use crate::core::geometry::{Bounds3f, Lerp, Point3f, Vector3f, VectorLike}; use crate::core::pbrt::Float; use crate::utils::math::lerp; use core::ops::Sub; 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: [Vector3f; 4] = core::array::from_fn(|i| Vector3f::from(cp[i])); 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 elevate_quadratic_bezier_to_cubic(cp: &[Point3f]) -> [Point3f; 4] { [ cp[0], lerp(2. / 3., cp[0], cp[1]), lerp(1. / 3., cp[1], cp[2]), cp[2], ] } pub fn cubic_bspline_to_bezier(cp: &[Point3f]) -> [Point3f; 4] { // Blossom from p012, p123, p234, and p345 to the Bezier control points // p222, p223, p233, and p333. // https://people.eecs.berkeley.edu/~sequin/CS284/IMGS/cubicbsplinepoints.gif let p012 = cp[0]; let p123 = cp[1]; let p234 = cp[2]; let p345 = cp[3]; let p122 = lerp(2. / 3., p012, p123); let p223 = lerp(1. / 3., p123, p234); let p233 = lerp(2. / 3., p123, p234); let p334 = lerp(1. / 3., p234, p345); let p222 = lerp(0.5, p122, p223); let p333 = lerp(0.5, p233, p334); [p222, p223, p233, p333] } pub fn quadratic_bspline_to_bezier(cp: &[Point3f]) -> [Point3f; 3] { // We can compute equivalent Bezier control points via some blossoming. // We have three control points and a uniform knot vector; we will label // the points p01, p12, and p23. We want the Bezier control points of // the equivalent curve, which are p11, p12, and p22. We already have // p12. let p11 = lerp(0.5, cp[0], cp[1]); let p22 = lerp(0.5, cp[1], cp[2]); [p11, cp[1], p22] } pub fn evaluate_cubic_bezier(cp: &[Point3f], u: Float) -> (Point3f, Vector3f) where { 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) }