pbrt/src/utils/splines.rs

75 lines
2.1 KiB
Rust

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, F>(p: &[P], u0: F, u1: F, u2: F) -> P
where
F: Copy + Num,
P: Lerp<F>,
{
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<Vector3f> = 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)
}