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)
}