# Objective - Adds foundational math for Bezier curves, useful for UI/2D/3D animation and smooth paths. https://user-images.githubusercontent.com/2632925/218883143-e138f994-1795-40da-8c59-21d779666991.mp4 ## Solution - Adds the generic `Bezier` type, and a `Point` trait. The `Point` trait allows us to use control points of any dimension, as long as they support vector math. I've implemented it for `f32`(1D), `Vec2`(2D), and `Vec3`/`Vec3A`(3D). - Adds `CubicBezierEasing` on top of `Bezier` with the addition of an implementation of cubic Bezier easing, which is a foundational tool for UI animation. - This involves solving for $t$ in the parametric Bezier function $B(t)$ using the Newton-Raphson method to find a value with error $\leq$ 1e-7, capped at 8 iterations. - Added type aliases for common Bezier curves: `CubicBezier2d`, `CubicBezier3d`, `QuadraticBezier2d`, and `QuadraticBezier3d`. These types use `Vec3A` to represent control points, as this was found to have an 80-90% speedup over using `Vec3`. - Benchmarking shows quadratic/cubic Bezier evaluations $B(t)$ take \~1.8/2.4ns respectively. Easing, which requires an iterative solve takes \~50ns for cubic Beziers. --- ## Changelog - Added `CubicBezier2d`, `CubicBezier3d`, `QuadraticBezier2d`, and `QuadraticBezier3d` types with methods for sampling position, velocity, and acceleration. The generic `Bezier` type is also available, and generic over any degree of Bezier curve. - Added `CubicBezierEasing`, with additional methods to allow for smooth easing animations.
130 lines
3.4 KiB
Rust
130 lines
3.4 KiB
Rust
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
|
|
use bevy_math::*;
|
|
|
|
fn easing(c: &mut Criterion) {
|
|
let cubic_bezier = CubicBezierEasing::new(vec2(0.25, 0.1), vec2(0.25, 1.0));
|
|
c.bench_function("easing_1000", |b| {
|
|
b.iter(|| {
|
|
(0..1000).map(|i| i as f32 / 1000.0).for_each(|t| {
|
|
cubic_bezier.ease(black_box(t));
|
|
})
|
|
});
|
|
});
|
|
}
|
|
|
|
fn fifteen_degree(c: &mut Criterion) {
|
|
let bezier = Bezier::<Vec3A, 16>::new([
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[1.0, 1.0, 1.0],
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[1.0, 1.0, 1.0],
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[1.0, 1.0, 1.0],
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[1.0, 1.0, 1.0],
|
|
]);
|
|
c.bench_function("fifteen_degree_position", |b| {
|
|
b.iter(|| bezier.position(black_box(0.5)));
|
|
});
|
|
}
|
|
|
|
fn quadratic_2d(c: &mut Criterion) {
|
|
let bezier = QuadraticBezier2d::new([[0.0, 0.0], [0.0, 1.0], [1.0, 1.0]]);
|
|
c.bench_function("quadratic_position_Vec2", |b| {
|
|
b.iter(|| bezier.position(black_box(0.5)));
|
|
});
|
|
}
|
|
|
|
fn quadratic(c: &mut Criterion) {
|
|
let bezier = QuadraticBezier3d::new([[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 1.0]]);
|
|
c.bench_function("quadratic_position_Vec3A", |b| {
|
|
b.iter(|| bezier.position(black_box(0.5)));
|
|
});
|
|
}
|
|
|
|
fn quadratic_vec3(c: &mut Criterion) {
|
|
let bezier = Bezier::<Vec3, 3>::new([[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 1.0]]);
|
|
c.bench_function("quadratic_position_Vec3", |b| {
|
|
b.iter(|| bezier.position(black_box(0.5)));
|
|
});
|
|
}
|
|
|
|
fn cubic_2d(c: &mut Criterion) {
|
|
let bezier = CubicBezier2d::new([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]);
|
|
c.bench_function("cubic_position_Vec2", |b| {
|
|
b.iter(|| bezier.position(black_box(0.5)));
|
|
});
|
|
}
|
|
|
|
fn cubic(c: &mut Criterion) {
|
|
let bezier = CubicBezier3d::new([
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[1.0, 1.0, 1.0],
|
|
]);
|
|
c.bench_function("cubic_position_Vec3A", |b| {
|
|
b.iter(|| bezier.position(black_box(0.5)));
|
|
});
|
|
}
|
|
|
|
fn cubic_vec3(c: &mut Criterion) {
|
|
let bezier = Bezier::<Vec3, 4>::new([
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[1.0, 1.0, 1.0],
|
|
]);
|
|
c.bench_function("cubic_position_Vec3", |b| {
|
|
b.iter(|| bezier.position(black_box(0.5)));
|
|
});
|
|
}
|
|
|
|
fn build_pos_cubic(c: &mut Criterion) {
|
|
let bezier = CubicBezier3d::new([
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[1.0, 1.0, 1.0],
|
|
]);
|
|
c.bench_function("build_pos_cubic_100_points", |b| {
|
|
b.iter(|| bezier.iter_positions(black_box(100)).collect::<Vec<_>>());
|
|
});
|
|
}
|
|
|
|
fn build_accel_cubic(c: &mut Criterion) {
|
|
let bezier = CubicBezier3d::new([
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[1.0, 1.0, 1.0],
|
|
]);
|
|
c.bench_function("build_accel_cubic_100_points", |b| {
|
|
b.iter(|| bezier.iter_positions(black_box(100)).collect::<Vec<_>>());
|
|
});
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
easing,
|
|
fifteen_degree,
|
|
quadratic_2d,
|
|
quadratic,
|
|
quadratic_vec3,
|
|
cubic_2d,
|
|
cubic,
|
|
cubic_vec3,
|
|
build_pos_cubic,
|
|
build_accel_cubic,
|
|
);
|
|
criterion_main!(benches);
|