use super::error::{BezierError, Empty};
use super::{Bezier, TooSmallWorkspace};
use crate::builder::{InputDomain, NormalizedInput, Unknown, WithWeight, WithoutWeight};
use crate::weights::{Homogeneous, IntoWeight, Weighted, Weights};
#[cfg(feature = "std")]
use crate::DynSpace;
use crate::{
ConstDiscreteGenerator, ConstSpace, DiscreteGenerator, Generator, Space, TransformInput,
};
use core::marker::PhantomData;
use core::ops::{Div, Mul};
use num_traits::identities::Zero;
use num_traits::real::Real;
use topology_traits::Merge;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct BezierDirector<I, E, S, W> {
input: I,
elements: E,
space: S,
_phantom: PhantomData<*const W>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct BezierBuilder<I, E, S, W> {
inner: Result<BezierDirector<I, E, S, W>, BezierError>,
}
impl Default for BezierDirector<Unknown, Unknown, Unknown, Unknown> {
fn default() -> Self {
BezierDirector::new()
}
}
impl Default for BezierBuilder<Unknown, Unknown, Unknown, Unknown> {
fn default() -> Self {
BezierBuilder::new()
}
}
impl BezierDirector<Unknown, Unknown, Unknown, Unknown> {
pub const fn new() -> Self {
BezierDirector {
input: Unknown,
elements: Unknown,
space: Unknown,
_phantom: PhantomData,
}
}
}
impl BezierBuilder<Unknown, Unknown, Unknown, Unknown> {
pub const fn new() -> Self {
BezierBuilder {
inner: Ok(BezierDirector::new()),
}
}
}
impl BezierDirector<Unknown, Unknown, Unknown, Unknown> {
pub fn elements<E>(
self,
elements: E,
) -> Result<BezierDirector<Unknown, E, Unknown, WithoutWeight>, Empty>
where
E: DiscreteGenerator,
{
if elements.len() < 1 {
return Err(Empty::new());
}
Ok(BezierDirector {
input: self.input,
space: self.space,
elements,
_phantom: PhantomData,
})
}
pub fn elements_with_weights<G>(
self,
gen: G,
) -> Result<BezierDirector<Unknown, Weights<G>, Unknown, WithWeight>, Empty>
where
G: DiscreteGenerator,
G::Output: IntoWeight,
<G::Output as IntoWeight>::Element:
Mul<<G::Output as IntoWeight>::Weight, Output = <G::Output as IntoWeight>::Element>,
<G::Output as IntoWeight>::Weight: Zero + Copy,
{
if gen.len() < 1 {
return Err(Empty::new());
}
Ok(BezierDirector {
input: self.input,
space: self.space,
elements: Weights::new(gen),
_phantom: PhantomData,
})
}
}
impl BezierBuilder<Unknown, Unknown, Unknown, Unknown> {
pub fn elements<E>(self, elements: E) -> BezierBuilder<Unknown, E, Unknown, WithoutWeight>
where
E: DiscreteGenerator,
{
BezierBuilder {
inner: self
.inner
.and_then(|director| director.elements(elements).map_err(|err| err.into())),
}
}
pub fn elements_with_weights<G>(
self,
gen: G,
) -> BezierBuilder<Unknown, Weights<G>, Unknown, WithWeight>
where
G: DiscreteGenerator,
G::Output: IntoWeight,
<G::Output as IntoWeight>::Element:
Mul<<G::Output as IntoWeight>::Weight, Output = <G::Output as IntoWeight>::Element>,
<G::Output as IntoWeight>::Weight: Zero + Copy,
{
BezierBuilder {
inner: self.inner.and_then(|director| {
director
.elements_with_weights(gen)
.map_err(|err| err.into())
}),
}
}
}
impl<E, W> BezierDirector<Unknown, E, Unknown, W> {
pub fn normalized<R>(self) -> BezierDirector<NormalizedInput<R>, E, Unknown, W> {
BezierDirector {
input: NormalizedInput::new(),
space: self.space,
elements: self.elements,
_phantom: self._phantom,
}
}
pub fn domain<R>(self, start: R, end: R) -> BezierDirector<InputDomain<R>, E, Unknown, W> {
BezierDirector {
input: InputDomain::new(start, end),
space: self.space,
elements: self.elements,
_phantom: self._phantom,
}
}
}
impl<E, W> BezierBuilder<Unknown, E, Unknown, W> {
pub fn normalized<R>(self) -> BezierBuilder<NormalizedInput<R>, E, Unknown, W> {
BezierBuilder {
inner: self.inner.map(|director| director.normalized()),
}
}
pub fn domain<R>(self, start: R, end: R) -> BezierBuilder<InputDomain<R>, E, Unknown, W> {
BezierBuilder {
inner: self.inner.map(|director| director.domain(start, end)),
}
}
}
impl<I, E, W> BezierDirector<I, E, Unknown, W>
where
E: DiscreteGenerator,
{
#[cfg(feature = "std")]
pub fn dynamic(self) -> BezierDirector<I, E, DynSpace<E::Output>, W> {
BezierDirector {
input: self.input,
space: DynSpace::new(self.elements.len()),
elements: self.elements,
_phantom: self._phantom,
}
}
pub fn constant<const N: usize>(self) -> BezierDirector<I, E, ConstSpace<E::Output, N>, W>
where
E: ConstDiscreteGenerator<N>,
{
BezierDirector {
input: self.input,
space: ConstSpace::new(),
elements: self.elements,
_phantom: self._phantom,
}
}
pub fn workspace<S>(self, space: S) -> Result<BezierDirector<I, E, S, W>, TooSmallWorkspace>
where
S: Space<E::Output>,
{
if space.len() < self.elements.len() {
return Err(TooSmallWorkspace::new(self.elements.len(), space.len()));
}
Ok(BezierDirector {
input: self.input,
space,
elements: self.elements,
_phantom: self._phantom,
})
}
}
impl<I, E, W> BezierBuilder<I, E, Unknown, W>
where
E: DiscreteGenerator,
{
#[cfg(feature = "std")]
pub fn dynamic(self) -> BezierBuilder<I, E, DynSpace<E::Output>, W> {
BezierBuilder {
inner: self.inner.map(|director| director.dynamic()),
}
}
pub fn constant<const N: usize>(self) -> BezierBuilder<I, E, ConstSpace<E::Output, N>, W>
where
E: ConstDiscreteGenerator<N>,
{
BezierBuilder {
inner: self.inner.map(|director| director.constant()),
}
}
pub fn workspace<S>(self, space: S) -> BezierBuilder<I, E, S, W>
where
S: Space<E::Output>,
{
BezierBuilder {
inner: self
.inner
.and_then(|director| director.workspace(space).map_err(|err| err.into())),
}
}
}
impl<R, E, S> BezierDirector<NormalizedInput<R>, E, S, WithoutWeight>
where
E: DiscreteGenerator,
E::Output: Merge<R>,
S: Space<E::Output>,
R: Real,
{
pub fn build(self) -> Bezier<R, E, S> {
Bezier::new_unchecked(self.elements, self.space)
}
}
impl<R, E, S> BezierBuilder<NormalizedInput<R>, E, S, WithoutWeight>
where
E: DiscreteGenerator,
E::Output: Merge<R>,
S: Space<E::Output>,
R: Real,
{
pub fn build(self) -> Result<Bezier<R, E, S>, BezierError> {
self.inner.map(|director| director.build())
}
}
impl<R, E, S> BezierDirector<InputDomain<R>, E, S, WithoutWeight>
where
E: DiscreteGenerator,
E::Output: Merge<R> + Copy,
S: Space<E::Output>,
R: Real,
{
#[allow(clippy::type_complexity)]
pub fn build(self) -> TransformInput<Bezier<R, E, S>, R, R> {
TransformInput::normalized_to_domain(
Bezier::new_unchecked(self.elements, self.space),
self.input.start,
self.input.end,
)
}
}
impl<R, E, S> BezierBuilder<InputDomain<R>, E, S, WithoutWeight>
where
E: DiscreteGenerator,
E::Output: Merge<R> + Copy,
S: Space<E::Output>,
R: Real,
{
#[allow(clippy::type_complexity)]
pub fn build(self) -> Result<TransformInput<Bezier<R, E, S>, R, R>, BezierError> {
self.inner.map(|director| director.build())
}
}
impl<R, G, S> BezierDirector<NormalizedInput<R>, Weights<G>, S, WithWeight>
where
R: Real + Copy,
G: DiscreteGenerator,
G::Output: IntoWeight,
S: Space<Homogeneous<<G::Output as IntoWeight>::Element, <G::Output as IntoWeight>::Weight>>,
<Weights<G> as Generator<usize>>::Output: Merge<R>,
<G::Output as IntoWeight>::Element:
Div<<G::Output as IntoWeight>::Weight, Output = <G::Output as IntoWeight>::Element>,
{
pub fn build(self) -> WeightedBezier<R, G, S> {
Weighted::new(Bezier::new_unchecked(self.elements, self.space))
}
}
impl<R, G, S> BezierBuilder<NormalizedInput<R>, Weights<G>, S, WithWeight>
where
R: Real + Copy,
G: DiscreteGenerator,
G::Output: IntoWeight,
S: Space<Homogeneous<<G::Output as IntoWeight>::Element, <G::Output as IntoWeight>::Weight>>,
<Weights<G> as Generator<usize>>::Output: Merge<R>,
<G::Output as IntoWeight>::Element:
Div<<G::Output as IntoWeight>::Weight, Output = <G::Output as IntoWeight>::Element>,
{
pub fn build(self) -> Result<WeightedBezier<R, G, S>, BezierError> {
self.inner.map(|director| director.build())
}
}
impl<R, G, S> BezierDirector<InputDomain<R>, Weights<G>, S, WithWeight>
where
R: Real + Copy,
G: DiscreteGenerator,
G::Output: IntoWeight,
S: Space<Homogeneous<<G::Output as IntoWeight>::Element, <G::Output as IntoWeight>::Weight>>,
<Weights<G> as Generator<usize>>::Output: Merge<R> + Copy,
<G::Output as IntoWeight>::Element:
Div<<G::Output as IntoWeight>::Weight, Output = <G::Output as IntoWeight>::Element>,
{
#[allow(clippy::type_complexity)]
pub fn build(self) -> TransformInput<WeightedBezier<R, G, S>, R, R> {
TransformInput::normalized_to_domain(
Weighted::new(Bezier::new_unchecked(self.elements, self.space)),
self.input.start,
self.input.end,
)
}
}
impl<R, G, S> BezierBuilder<InputDomain<R>, Weights<G>, S, WithWeight>
where
R: Real + Copy,
G: DiscreteGenerator,
G::Output: IntoWeight,
S: Space<Homogeneous<<G::Output as IntoWeight>::Element, <G::Output as IntoWeight>::Weight>>,
<Weights<G> as Generator<usize>>::Output: Merge<R> + Copy,
<G::Output as IntoWeight>::Element:
Div<<G::Output as IntoWeight>::Weight, Output = <G::Output as IntoWeight>::Element>,
{
#[allow(clippy::type_complexity)]
pub fn build(self) -> Result<TransformInput<WeightedBezier<R, G, S>, R, R>, BezierError> {
self.inner.map(|director| director.build())
}
}
type WeightedBezier<R, G, S> = Weighted<Bezier<R, Weights<G>, S>>;
#[cfg(test)]
mod test {
use super::{BezierBuilder, BezierDirector};
use crate::{weights::Homogeneous, Generator};
#[test]
fn elements_with_weights() {
BezierBuilder::new()
.elements_with_weights([(1.0, 1.0), (2.0, 2.0), (3.0, 0.0)])
.normalized::<f64>()
.constant()
.build()
.unwrap();
BezierBuilder::new()
.elements_with_weights([1.0, 2.0, 3.0].stack([1.0, 2.0, 0.0]))
.normalized::<f64>()
.constant()
.build()
.unwrap();
BezierBuilder::new()
.elements_with_weights([
Homogeneous::new(1.0),
Homogeneous::weighted_unchecked(2.0, 2.0),
Homogeneous::infinity(3.0),
])
.normalized::<f64>()
.constant()
.build()
.unwrap();
BezierBuilder::new()
.elements([0.1, 0.2, 0.3])
.normalized::<f64>()
.constant()
.build()
.unwrap();
assert!(BezierBuilder::new()
.elements::<[f64; 0]>([])
.normalized::<f64>()
.constant()
.build()
.is_err());
}
#[test]
fn bezier_errors() {
assert!(BezierDirector::new().elements::<[f32; 0]>([]).is_err());
assert!(BezierDirector::new().elements([1.0]).is_ok());
}
}