use crate::builder::Unknown;
use crate::{Curve, DiscreteGenerator, Generator, Space};
use core::marker::PhantomData;
use core::ops::{Mul, Sub};
use num_traits::cast::FromPrimitive;
use num_traits::real::Real;
use topology_traits::Merge;
mod builder;
pub use builder::{BezierBuilder, BezierDirector};
mod error;
pub use error::{BezierError, Empty, TooSmallWorkspace};
fn triangle_folding_inline<P, T>(mut triangle: P, func: impl Fn(T, T) -> T, steps: usize)
where
P: AsMut<[T]>,
T: Copy,
{
let elements = triangle.as_mut();
let len = elements.len();
for k in 1..=steps {
for i in 0..len - k {
elements[i] = func(elements[i], elements[i + 1]);
}
}
}
fn lower_triangle_folding_inline<P, T>(mut triangle: P, func: impl Fn(T, T) -> T, steps: usize)
where
P: AsMut<[T]>,
T: Copy,
{
let elements = triangle.as_mut();
let len = elements.len();
for k in 1..=steps {
for i in k..len {
elements[i] = func(elements[i - 1], elements[i]);
}
}
}
fn bezier<R, P, T>(mut elements: P, scalar: R) -> T
where
P: AsMut<[T]>,
T: Merge<R> + Copy,
R: Real,
{
let len = elements.as_mut().len();
triangle_folding_inline(
elements.as_mut(),
|first, second| first.merge(second, scalar),
len - 1,
);
elements.as_mut()[0]
}
fn bezier_with_tangent<R, P, T>(mut elements: P, scalar: R) -> [T; 2]
where
P: AsMut<[T]>,
T: Merge<R> + Mul<R, Output = T> + Sub<Output = T> + Copy,
R: Real + FromPrimitive,
{
let len = elements.as_mut().len();
if len < 2 {
return [elements.as_mut()[0], elements.as_mut()[0] * R::zero()];
}
triangle_folding_inline(
elements.as_mut(),
|first, second| first.merge(second, scalar),
len - 2,
);
let elements = elements.as_mut();
let tangent = (elements[1] - elements[0]) * R::from_usize(len - 1).unwrap();
let result = elements[0].merge(elements[1], scalar);
[result, tangent]
}
fn bezier_with_deriatives<R, P, T, const K: usize>(mut elements: P, scalar: R) -> [T; K]
where
P: AsMut<[T]>,
T: Merge<R> + Mul<R, Output = T> + Sub<Output = T> + Copy,
R: Real + FromPrimitive,
{
let len = elements.as_mut().len();
let deg = K.min(len - 1);
triangle_folding_inline(
elements.as_mut(),
|first, second| first.merge(second, scalar),
len - deg - 1,
);
let mut grad = [elements.as_mut()[0] * R::zero(); K];
for k in (1..=deg).rev() {
let grad_slice = &mut grad[..=k];
lower_triangle_folding_inline(grad_slice, |first, second| second - first, k);
let prod = R::from_usize((len - k..len).product::<usize>()).unwrap();
grad[k] = grad[k] * prod;
triangle_folding_inline(
elements.as_mut(),
|first, second| first.merge(second, scalar),
1,
);
grad[..k].clone_from_slice(&elements.as_mut()[..k]);
}
grad
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Bezier<R, E, S> {
elements: E,
space: S,
_input: PhantomData<*const R>,
}
impl Bezier<Unknown, Unknown, Unknown> {
pub fn builder() -> BezierBuilder<Unknown, Unknown, Unknown, Unknown> {
BezierBuilder::new()
}
}
impl<R, E, S> Bezier<R, E, S>
where
E: DiscreteGenerator,
S: Space<E::Output>,
{
fn workspace(&self) -> impl AsMut<[E::Output]> {
let mut workspace = self.space.workspace();
let mut_workspace = workspace.as_mut();
for (i, val) in mut_workspace
.iter_mut()
.enumerate()
.take(self.elements.len())
{
*val = self.elements.gen(i);
}
workspace
}
}
impl<R, E, S> Generator<R> for Bezier<R, E, S>
where
E: DiscreteGenerator,
E::Output: Merge<R> + Copy,
S: Space<E::Output>,
R: Real,
{
type Output = E::Output;
fn gen(&self, scalar: R) -> E::Output {
bezier(
&mut self.workspace().as_mut()[..self.elements.len()],
scalar,
)
}
}
impl<R, E, S> Curve<R> for Bezier<R, E, S>
where
E: DiscreteGenerator,
E::Output: Merge<R> + Copy,
S: Space<E::Output>,
R: Real,
{
fn domain(&self) -> [R; 2] {
[R::zero(), R::one()]
}
}
impl<R, E, S> Bezier<R, E, S>
where
E: DiscreteGenerator,
E::Output: Merge<R> + Mul<R, Output = E::Output> + Sub<Output = E::Output> + Copy,
S: Space<E::Output>,
R: Real + FromPrimitive,
{
pub fn gen_with_tangent(&self, scalar: R) -> [E::Output; 2] {
bezier_with_tangent(
&mut self.workspace().as_mut()[..self.elements.len()],
scalar,
)
}
pub fn gen_with_deriatives<const K: usize>(&self, scalar: R) -> [E::Output; K] {
bezier_with_deriatives(
&mut self.workspace().as_mut()[..self.elements.len()],
scalar,
)
}
}
impl<R, E, S> Bezier<R, E, S>
where
E: DiscreteGenerator,
S: Space<E::Output>,
{
pub fn new(elements: E, space: S) -> Result<Self, BezierError> {
if space.len() < elements.len() {
return Err(TooSmallWorkspace::new(space.len(), elements.len()).into());
}
if elements.is_empty() {
return Err(Empty::new().into());
}
Ok(Bezier {
space,
elements,
_input: PhantomData,
})
}
pub fn new_unchecked(elements: E, space: S) -> Self {
Bezier {
space,
elements,
_input: PhantomData,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::ConstSpace;
#[test]
fn extrapolation() {
let bez = Bezier::builder()
.elements([20.0, 0.0, 200.0])
.normalized::<f64>()
.constant()
.build()
.unwrap();
assert_f64_near!(bez.gen(2.0), 820.0);
assert_f64_near!(bez.gen(-1.0), 280.0);
}
#[test]
fn bigger_workspace() {
let bez = Bezier::new([5.0], ConstSpace::<_, 3>::new()).unwrap();
let res = bez.gen_with_tangent(0.5);
assert_f64_near!(res[0], 5.0);
assert_f64_near!(res[1], 0.0);
}
#[test]
fn constant() {
let bez = Bezier::builder()
.elements([5.0])
.normalized::<f64>()
.constant()
.build()
.unwrap();
let res = bez.gen_with_tangent(0.25);
assert_f64_near!(res[0], 5.0);
assert_f64_near!(res[1], 0.0);
let res = bez.gen_with_tangent(0.75);
assert_f64_near!(res[0], 5.0);
assert_f64_near!(res[1], 0.0);
}
#[test]
fn deriatives() {
let bez = Bezier::builder()
.elements([1.0, 2.0, 3.0])
.normalized::<f64>()
.constant()
.build()
.unwrap();
let res = bez.gen_with_tangent(0.5);
assert_f64_near!(res[0], 2.0);
assert_f64_near!(res[1], 2.0);
let res = bez.gen_with_deriatives::<3>(0.5);
assert_f64_near!(res[0], 2.0);
assert_f64_near!(res[1], 2.0);
assert_f64_near!(res[2], 0.0);
let res = bez.gen_with_deriatives::<5>(0.5);
assert_f64_near!(res[0], 2.0);
assert_f64_near!(res[1], 2.0);
assert_f64_near!(res[2], 0.0);
assert_f64_near!(res[3], 0.0);
assert_f64_near!(res[4], 0.0);
}
}