use super::adaptors::{BorderBuffer, BorderDeletion};
use super::error::{
BSplineError, IncongruousElementsDegree, IncongruousElementsKnots, InvalidDegree, TooFewKnots,
};
use super::{BSpline, TooFewElements, TooSmallWorkspace};
#[cfg(feature = "std")]
use crate::DynSpace;
use crate::builder::{Type, Unknown, WithWeight, WithoutWeight};
use crate::weights::{Homogeneous, IntoWeight, Weighted, Weights};
use crate::{Chain, ConstSpace, Equidistant, Signal, Sorted, SortedChain, Space};
use core::marker::PhantomData;
use core::ops::{Div, Mul};
use num_traits::FromPrimitive;
use num_traits::identities::Zero;
use num_traits::real::Real;
use topology_traits::Merge;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Clamped;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Open;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Legacy;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct UnknownDomain<R> {
_phantom: PhantomData<*const R>,
len: usize,
deg: usize,
}
impl<R> UnknownDomain<R> {
pub fn new(len: usize, deg: usize) -> Self {
UnknownDomain {
_phantom: PhantomData,
len,
deg,
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn deg(&self) -> usize {
self.deg
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct BSplineDirector<K, E, S, W, M> {
elements: E,
knots: K,
space: S,
_phantoms: (PhantomData<*const W>, PhantomData<*const M>),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct BSplineBuilder<K, E, S, W, M> {
inner: Result<BSplineDirector<K, E, S, W, M>, BSplineError>,
}
impl Default for BSplineDirector<Unknown, Unknown, Unknown, Unknown, Open> {
fn default() -> Self {
BSplineDirector::new()
}
}
impl Default for BSplineBuilder<Unknown, Unknown, Unknown, Unknown, Open> {
fn default() -> Self {
BSplineBuilder::new()
}
}
impl BSplineDirector<Unknown, Unknown, Unknown, Unknown, Open> {
pub const fn new() -> Self {
BSplineDirector {
elements: Unknown,
knots: Unknown,
space: Unknown,
_phantoms: (PhantomData, PhantomData),
}
}
}
impl BSplineBuilder<Unknown, Unknown, Unknown, Unknown, Open> {
pub const fn new() -> Self {
BSplineBuilder {
inner: Ok(BSplineDirector::new()),
}
}
}
impl<M> BSplineDirector<Unknown, Unknown, Unknown, Unknown, M> {
pub fn open(self) -> BSplineDirector<Unknown, Unknown, Unknown, Unknown, Open> {
BSplineDirector {
knots: self.knots,
space: self.space,
elements: self.elements,
_phantoms: (self._phantoms.0, PhantomData),
}
}
pub fn clamped(self) -> BSplineDirector<Unknown, Unknown, Unknown, Unknown, Clamped> {
BSplineDirector {
knots: self.knots,
space: self.space,
elements: self.elements,
_phantoms: (self._phantoms.0, PhantomData),
}
}
pub fn legacy(self) -> BSplineDirector<Unknown, Unknown, Unknown, Unknown, Legacy> {
BSplineDirector {
knots: self.knots,
space: self.space,
elements: self.elements,
_phantoms: (self._phantoms.0, PhantomData),
}
}
pub fn elements<E>(
self,
elements: E,
) -> Result<BSplineDirector<Unknown, E, Unknown, WithoutWeight, M>, TooFewElements>
where
E: Chain,
{
if elements.len() < 2 {
return Err(TooFewElements::new(elements.len()));
}
Ok(BSplineDirector {
knots: self.knots,
space: self.space,
elements,
_phantoms: (PhantomData, self._phantoms.1),
})
}
pub fn elements_with_weights<G>(
self,
signal: G,
) -> Result<BSplineDirector<Unknown, Weights<G>, Unknown, WithWeight, M>, TooFewElements>
where
G: Chain,
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 signal.len() < 2 {
return Err(TooFewElements::new(signal.len()));
}
Ok(BSplineDirector {
space: self.space,
knots: self.knots,
elements: Weights::new(signal),
_phantoms: (PhantomData, self._phantoms.1),
})
}
}
impl<M> BSplineBuilder<Unknown, Unknown, Unknown, Unknown, M> {
pub fn open(self) -> BSplineBuilder<Unknown, Unknown, Unknown, Unknown, Open> {
BSplineBuilder {
inner: self.inner.map(|director| director.open()),
}
}
pub fn clamped(self) -> BSplineBuilder<Unknown, Unknown, Unknown, Unknown, Clamped> {
BSplineBuilder {
inner: self.inner.map(|director| director.clamped()),
}
}
pub fn legacy(self) -> BSplineBuilder<Unknown, Unknown, Unknown, Unknown, Legacy> {
BSplineBuilder {
inner: self.inner.map(|director| director.legacy()),
}
}
pub fn elements<E>(self, elements: E) -> BSplineBuilder<Unknown, E, Unknown, WithoutWeight, M>
where
E: Chain,
{
BSplineBuilder {
inner: self
.inner
.and_then(|director| director.elements(elements).map_err(|err| err.into())),
}
}
pub fn elements_with_weights<G>(
self,
signal: G,
) -> BSplineBuilder<Unknown, Weights<G>, Unknown, WithWeight, M>
where
G: Chain,
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,
{
BSplineBuilder {
inner: self.inner.and_then(|director| {
director
.elements_with_weights(signal)
.map_err(|err| err.into())
}),
}
}
}
impl<E, W> BSplineDirector<Unknown, E, Unknown, W, Open> {
pub fn knots<K>(
self,
knots: K,
) -> Result<BSplineDirector<Sorted<K>, E, Unknown, W, Open>, BSplineError>
where
E: Chain,
K: Chain,
K::Output: PartialOrd,
{
if knots.len() < 2 {
return Err(TooFewKnots::new(knots.len()).into());
}
if knots.len() < self.elements.len() {
return Err(IncongruousElementsKnots::open(self.elements.len(), knots.len()).into());
}
if self.elements.len() <= knots.len() - self.elements.len() + 1 {
return Err(IncongruousElementsKnots::open(self.elements.len(), knots.len()).into());
}
Ok(BSplineDirector {
knots: Sorted::new(knots)?,
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
})
}
}
impl<E, W> BSplineBuilder<Unknown, E, Unknown, W, Open> {
pub fn knots<K>(self, knots: K) -> BSplineBuilder<Sorted<K>, E, Unknown, W, Open>
where
E: Chain,
K: Chain,
K::Output: PartialOrd,
{
BSplineBuilder {
inner: self.inner.and_then(|director| director.knots(knots)),
}
}
}
impl<E, W> BSplineDirector<Unknown, E, Unknown, W, Clamped> {
pub fn knots<K>(self, knots: K) -> Result<ClampedBSplineDirector<K, E, W>, BSplineError>
where
E: Chain,
K: Chain,
K::Output: PartialOrd,
{
if knots.len() < 2 {
return Err(TooFewKnots::new(knots.len()).into());
}
if self.elements.len() < knots.len() {
return Err(IncongruousElementsKnots::clamped(self.elements.len(), knots.len()).into());
}
let duplicate = self.elements.len() - knots.len(); Ok(BSplineDirector {
knots: BorderBuffer::new(Sorted::new(knots)?, duplicate),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
})
}
}
impl<E, W> BSplineBuilder<Unknown, E, Unknown, W, Clamped> {
pub fn knots<K>(self, knots: K) -> ClampedBSplineBuilder<K, E, W>
where
E: Chain,
K: Chain,
K::Output: PartialOrd,
{
BSplineBuilder {
inner: self.inner.and_then(|director| director.knots(knots)),
}
}
}
impl<E, W> BSplineDirector<Unknown, E, Unknown, W, Legacy> {
pub fn knots<K>(self, knots: K) -> Result<LegacyBSplineDirector<K, E, W>, BSplineError>
where
E: Chain,
K: Chain,
K::Output: PartialOrd,
{
if knots.len() < 4 {
return Err(TooFewKnots::new(knots.len()).into());
}
if knots.len() <= self.elements.len() + 1 {
return Err(IncongruousElementsKnots::legacy(self.elements.len(), knots.len()).into());
}
if self.elements.len() < knots.len() - self.elements.len() {
return Err(IncongruousElementsKnots::legacy(self.elements.len(), knots.len()).into());
}
Ok(BSplineDirector {
knots: BorderDeletion::new(Sorted::new(knots).unwrap())?,
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
})
}
}
impl<E, W> BSplineBuilder<Unknown, E, Unknown, W, Legacy> {
pub fn knots<K>(self, knots: K) -> LegacyBSplineBuilder<K, E, W>
where
E: Chain,
K: Chain,
K::Output: PartialOrd,
{
BSplineBuilder {
inner: self.inner.and_then(|director| director.knots(knots)),
}
}
}
impl<E, W, M> BSplineDirector<Unknown, E, Unknown, W, M> {
pub fn equidistant<R>(self) -> BSplineDirector<Type<R>, E, Unknown, W, M> {
BSplineDirector {
knots: Type::new(),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
}
}
}
impl<E, W, M> BSplineBuilder<Unknown, E, Unknown, W, M> {
pub fn equidistant<R>(self) -> BSplineBuilder<Type<R>, E, Unknown, W, M> {
BSplineBuilder {
inner: self.inner.map(|director| director.equidistant()),
}
}
}
impl<R, E, W> BSplineDirector<Type<R>, E, Unknown, W, Open>
where
E: Chain,
{
pub fn degree(
self,
degree: usize,
) -> Result<BSplineDirector<UnknownDomain<R>, E, Unknown, W, Open>, BSplineError> {
if degree < 1 {
return Err(InvalidDegree::new(degree).into());
}
if self.elements.len() <= degree {
return Err(IncongruousElementsDegree::open(self.elements.len(), degree).into());
}
Ok(BSplineDirector {
knots: UnknownDomain::new(self.elements.len() - 1 + degree, degree),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
})
}
pub fn quantity(
self,
quantity: usize,
) -> Result<BSplineDirector<UnknownDomain<R>, E, Unknown, W, Open>, BSplineError> {
if quantity < 2 {
return Err(TooFewKnots::new(quantity).into());
}
if quantity < self.elements.len() {
return Err(IncongruousElementsKnots::legacy(self.elements.len(), quantity).into());
}
if self.elements.len() <= quantity - self.elements.len() + 1 {
return Err(IncongruousElementsKnots::open(self.elements.len(), quantity).into());
}
Ok(BSplineDirector {
knots: UnknownDomain::new(quantity, quantity - self.elements.len() + 1),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
})
}
}
impl<R, E, W> BSplineBuilder<Type<R>, E, Unknown, W, Open>
where
E: Chain,
{
pub fn degree(self, degree: usize) -> BSplineBuilder<UnknownDomain<R>, E, Unknown, W, Open> {
BSplineBuilder {
inner: self.inner.and_then(|director| director.degree(degree)),
}
}
pub fn quantity(
self,
quantity: usize,
) -> BSplineBuilder<UnknownDomain<R>, E, Unknown, W, Open> {
BSplineBuilder {
inner: self.inner.and_then(|director| director.quantity(quantity)),
}
}
}
impl<R, E, W> BSplineDirector<Type<R>, E, Unknown, W, Clamped>
where
E: Chain,
{
pub fn degree(
self,
degree: usize,
) -> Result<BSplineDirector<UnknownDomain<R>, E, Unknown, W, Clamped>, BSplineError> {
if degree < 1 {
return Err(InvalidDegree::new(degree).into());
}
if self.elements.len() <= degree {
return Err(IncongruousElementsDegree::clamped(self.elements.len(), degree).into());
}
Ok(BSplineDirector {
knots: UnknownDomain::new(self.elements.len() - degree + 1, degree),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
})
}
pub fn quantity(
self,
quantity: usize,
) -> Result<BSplineDirector<UnknownDomain<R>, E, Unknown, W, Clamped>, BSplineError> {
if quantity < 2 {
return Err(TooFewKnots::new(quantity).into());
}
if self.elements.len() < quantity {
return Err(IncongruousElementsKnots::clamped(self.elements.len(), quantity).into());
}
Ok(BSplineDirector {
knots: UnknownDomain::new(quantity, self.elements.len() - quantity + 1),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
})
}
}
impl<R, E, W> BSplineBuilder<Type<R>, E, Unknown, W, Clamped>
where
E: Chain,
{
pub fn degree(self, degree: usize) -> BSplineBuilder<UnknownDomain<R>, E, Unknown, W, Clamped> {
BSplineBuilder {
inner: self.inner.and_then(|director| director.degree(degree)),
}
}
pub fn quantity(
self,
quantity: usize,
) -> BSplineBuilder<UnknownDomain<R>, E, Unknown, W, Clamped> {
BSplineBuilder {
inner: self.inner.and_then(|director| director.quantity(quantity)),
}
}
}
impl<R, E, W> BSplineDirector<UnknownDomain<R>, E, Unknown, W, Open>
where
E: Chain,
R: Real + FromPrimitive,
{
pub fn domain(self, start: R, end: R) -> BSplineDirector<Equidistant<R>, E, Unknown, W, Open> {
BSplineDirector {
knots: Equidistant::new(self.knots.len(), start, end),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
}
}
pub fn normalized(self) -> BSplineDirector<Equidistant<R>, E, Unknown, W, Open> {
BSplineDirector {
knots: Equidistant::normalized(self.knots.len()),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
}
}
pub fn distance(
self,
start: R,
step: R,
) -> BSplineDirector<Equidistant<R>, E, Unknown, W, Open> {
BSplineDirector {
knots: Equidistant::step(self.knots.len(), start, step),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
}
}
}
impl<R, E, W> BSplineBuilder<UnknownDomain<R>, E, Unknown, W, Open>
where
E: Chain,
R: Real + FromPrimitive,
{
pub fn domain(self, start: R, end: R) -> BSplineBuilder<Equidistant<R>, E, Unknown, W, Open> {
BSplineBuilder {
inner: self.inner.map(|director| director.domain(start, end)),
}
}
pub fn normalized(self) -> BSplineBuilder<Equidistant<R>, E, Unknown, W, Open> {
BSplineBuilder {
inner: self.inner.map(|director| director.normalized()),
}
}
pub fn distance(
self,
start: R,
step: R,
) -> BSplineBuilder<Equidistant<R>, E, Unknown, W, Open> {
BSplineBuilder {
inner: self.inner.map(|director| director.distance(start, step)),
}
}
}
impl<R, E, W> BSplineDirector<UnknownDomain<R>, E, Unknown, W, Clamped>
where
E: Chain,
R: Real + FromPrimitive,
{
pub fn domain(
self,
start: R,
end: R,
) -> BSplineDirector<BorderBuffer<Equidistant<R>>, E, Unknown, W, Clamped> {
BSplineDirector {
knots: BorderBuffer::new(
Equidistant::new(self.knots.len(), start, end),
self.knots.deg() - 1,
),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
}
}
pub fn normalized(
self,
) -> BSplineDirector<BorderBuffer<Equidistant<R>>, E, Unknown, W, Clamped> {
BSplineDirector {
knots: BorderBuffer::new(
Equidistant::normalized(self.knots.len()),
self.knots.deg() - 1,
),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
}
}
pub fn distance(
self,
start: R,
step: R,
) -> BSplineDirector<BorderBuffer<Equidistant<R>>, E, Unknown, W, Clamped> {
BSplineDirector {
knots: BorderBuffer::new(
Equidistant::step(self.knots.len(), start, step),
self.knots.deg() - 1,
),
elements: self.elements,
space: self.space,
_phantoms: self._phantoms,
}
}
}
impl<R, E, W> BSplineBuilder<UnknownDomain<R>, E, Unknown, W, Clamped>
where
E: Chain,
R: Real + FromPrimitive,
{
pub fn domain(
self,
start: R,
end: R,
) -> BSplineBuilder<BorderBuffer<Equidistant<R>>, E, Unknown, W, Clamped> {
BSplineBuilder {
inner: self.inner.map(|director| director.domain(start, end)),
}
}
pub fn normalized(
self,
) -> BSplineBuilder<BorderBuffer<Equidistant<R>>, E, Unknown, W, Clamped> {
BSplineBuilder {
inner: self.inner.map(|director| director.normalized()),
}
}
pub fn distance(
self,
start: R,
step: R,
) -> BSplineBuilder<BorderBuffer<Equidistant<R>>, E, Unknown, W, Clamped> {
BSplineBuilder {
inner: self.inner.map(|director| director.distance(start, step)),
}
}
}
impl<K, E, W, M> BSplineDirector<K, E, Unknown, W, M>
where
E: Chain,
K: Chain,
{
#[cfg(feature = "std")]
pub fn dynamic(self) -> BSplineDirector<K, E, DynSpace<E::Output>, W, M> {
BSplineDirector {
space: DynSpace::new(self.knots.len() - self.elements.len() + 2),
knots: self.knots,
elements: self.elements,
_phantoms: self._phantoms,
}
}
#[allow(clippy::type_complexity)]
pub fn constant<const N: usize>(
self,
) -> Result<BSplineDirector<K, E, ConstSpace<E::Output, N>, W, M>, TooSmallWorkspace> {
if N <= self.knots.len() - self.elements.len() + 1 {
return Err(TooSmallWorkspace::new(
N,
self.knots.len() - self.elements.len() + 1,
));
}
Ok(BSplineDirector {
knots: self.knots,
space: ConstSpace::new(),
elements: self.elements,
_phantoms: self._phantoms,
})
}
pub fn workspace<S>(self, space: S) -> Result<BSplineDirector<K, E, S, W, M>, TooSmallWorkspace>
where
S: Space<E::Output>,
{
if space.len() <= self.knots.len() - self.elements.len() + 1 {
return Err(TooSmallWorkspace::new(
space.len(),
self.knots.len() - self.elements.len() + 1,
));
}
Ok(BSplineDirector {
knots: self.knots,
space,
elements: self.elements,
_phantoms: self._phantoms,
})
}
}
impl<K, E, W, M> BSplineBuilder<K, E, Unknown, W, M>
where
E: Chain,
K: Chain,
{
#[cfg(feature = "std")]
pub fn dynamic(self) -> BSplineBuilder<K, E, DynSpace<E::Output>, W, M> {
BSplineBuilder {
inner: self.inner.map(|director| director.dynamic()),
}
}
pub fn constant<const N: usize>(self) -> BSplineBuilder<K, E, ConstSpace<E::Output, N>, W, M> {
BSplineBuilder {
inner: self
.inner
.and_then(|director| director.constant().map_err(|err| err.into())),
}
}
pub fn workspace<S>(self, space: S) -> BSplineBuilder<K, E, S, W, M>
where
S: Space<E::Output>,
{
BSplineBuilder {
inner: self
.inner
.and_then(|director| director.workspace(space).map_err(|err| err.into())),
}
}
}
impl<K, E, S, M> BSplineDirector<K, E, S, WithoutWeight, M>
where
K: SortedChain,
E: Chain,
E::Output: Merge<K::Output> + Copy,
S: Space<E::Output>,
{
pub fn build(self) -> BSpline<K, E, S> {
BSpline::new_unchecked(self.elements, self.knots, self.space)
}
}
impl<K, E, S, M> BSplineBuilder<K, E, S, WithoutWeight, M>
where
K: SortedChain,
E: Chain,
E::Output: Merge<K::Output> + Copy,
S: Space<E::Output>,
{
pub fn build(self) -> Result<BSpline<K, E, S>, BSplineError> {
match self.inner {
Err(err) => Err(err),
Ok(director) => Ok(director.build()),
}
}
}
impl<K, G, S, M> BSplineDirector<K, Weights<G>, S, WithWeight, M>
where
G: Chain,
G::Output: IntoWeight,
K: SortedChain,
S: Space<Homogeneous<<G::Output as IntoWeight>::Element, <G::Output as IntoWeight>::Weight>>,
<Weights<G> as Signal<usize>>::Output: Merge<K::Output> + Copy,
<G::Output as IntoWeight>::Element:
Div<<G::Output as IntoWeight>::Weight, Output = <G::Output as IntoWeight>::Element>,
{
pub fn build(self) -> WeightedBSpline<K, G, S> {
Weighted::new(BSpline::new_unchecked(
self.elements,
self.knots,
self.space,
))
}
}
impl<K, G, S, M> BSplineBuilder<K, Weights<G>, S, WithWeight, M>
where
G: Chain,
G::Output: IntoWeight,
K: SortedChain,
S: Space<Homogeneous<<G::Output as IntoWeight>::Element, <G::Output as IntoWeight>::Weight>>,
<Weights<G> as Signal<usize>>::Output: Merge<K::Output> + Copy,
<G::Output as IntoWeight>::Element:
Div<<G::Output as IntoWeight>::Weight, Output = <G::Output as IntoWeight>::Element>,
{
pub fn build(self) -> Result<WeightedBSpline<K, G, S>, BSplineError> {
match self.inner {
Err(err) => Err(err),
Ok(director) => Ok(director.build()),
}
}
}
type WeightedBSpline<K, G, S> = Weighted<BSpline<K, Weights<G>, S>>;
type ClampedBSplineBuilder<K, E, W> =
BSplineBuilder<BorderBuffer<Sorted<K>>, E, Unknown, W, Clamped>;
type ClampedBSplineDirector<K, E, W> =
BSplineDirector<BorderBuffer<Sorted<K>>, E, Unknown, W, Clamped>;
type LegacyBSplineBuilder<K, E, W> =
BSplineBuilder<BorderDeletion<Sorted<K>>, E, Unknown, W, Legacy>;
type LegacyBSplineDirector<K, E, W> =
BSplineDirector<BorderDeletion<Sorted<K>>, E, Unknown, W, Legacy>;
#[cfg(test)]
mod test {
use super::BSplineBuilder;
use crate::{Curve, Signal, bspline::BSplineDirector, weights::Homogeneous};
#[test]
fn degenerate_creations() {
let empty: [f64; 0] = [];
assert!(
BSplineBuilder::new()
.elements(empty)
.knots(empty)
.constant::<1>()
.build()
.is_err()
);
assert!(
BSplineBuilder::new()
.elements([1.0])
.knots([1.0])
.constant::<2>()
.build()
.is_err()
);
}
#[test]
fn mode_equality() {
let elements = [1.0, 3.0, 7.0];
let open = BSplineBuilder::new()
.elements(elements)
.knots([0.0, 0.0, 1.0, 1.0])
.constant::<3>()
.build()
.unwrap();
let clamped = BSplineBuilder::new()
.clamped()
.elements(elements)
.knots([0.0, 1.0])
.constant::<3>()
.build()
.unwrap();
let legacy = BSplineBuilder::new()
.legacy()
.elements(elements)
.knots([0.0, 0.0, 0.0, 1.0, 1.0, 1.0])
.constant::<3>()
.build()
.unwrap();
for (a, b, c) in open
.take(10)
.zip(clamped.take(10))
.zip(legacy.take(10))
.map(|((a, b), c)| (a, b, c))
{
assert_f64_near!(a, b);
assert_f64_near!(b, c);
}
}
#[test]
fn elements_with_weights() {
BSplineBuilder::new()
.elements_with_weights([(1.0, 1.0), (2.0, 2.0), (3.0, 0.0)])
.equidistant::<f64>()
.degree(2)
.domain(0.0, 5.0)
.constant::<3>()
.build()
.unwrap();
BSplineBuilder::new()
.elements_with_weights([1.0, 2.0, 3.0].stack([1.0, 2.0, 0.0]))
.equidistant::<f64>()
.degree(1)
.normalized()
.constant::<2>()
.build()
.unwrap();
BSplineBuilder::new()
.elements_with_weights([
Homogeneous::new(1.0),
Homogeneous::weighted_unchecked(2.0, 2.0),
Homogeneous::infinity(3.0),
])
.knots([1.0, 2.0, 3.0])
.constant::<2>()
.build()
.unwrap();
BSplineBuilder::new()
.elements([0.1, 0.2, 0.3])
.equidistant::<f64>()
.degree(1)
.normalized()
.constant::<2>()
.build()
.unwrap();
}
#[test]
fn clamped_errors() {
assert!(BSplineDirector::new().clamped().elements([0.0]).is_err());
assert!(
BSplineDirector::new()
.clamped()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.knots([0.0])
.is_err()
);
assert!(
BSplineDirector::new()
.clamped()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.equidistant::<f32>()
.quantity(1)
.is_err()
);
assert!(
BSplineDirector::new()
.clamped()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.equidistant::<f32>()
.degree(0)
.is_err()
);
assert!(
BSplineDirector::new()
.clamped()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.equidistant::<f32>()
.degree(2)
.unwrap()
.domain(0.0, 1.0)
.constant::<2>()
.is_err()
);
assert!(
BSplineDirector::new()
.clamped()
.elements([0.0, 1.0, 2.0])
.unwrap()
.equidistant::<f32>()
.degree(3)
.is_err()
);
assert!(
BSplineDirector::new()
.clamped()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.equidistant::<f32>()
.degree(3)
.is_ok()
);
assert!(
BSplineDirector::new()
.clamped()
.elements([0.0, 1.0, 2.0])
.unwrap()
.equidistant::<f32>()
.quantity(4)
.is_err()
);
assert!(
BSplineDirector::new()
.clamped()
.elements([0.0, 1.0, 2.0])
.unwrap()
.equidistant::<f32>()
.quantity(3)
.is_ok()
);
}
#[test]
fn open_errors() {
assert!(BSplineDirector::new().open().elements([0.0]).is_err());
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.knots([0.0])
.is_err()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.equidistant::<f32>()
.quantity(1)
.is_err()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.equidistant::<f32>()
.degree(0)
.is_err()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.equidistant::<f32>()
.degree(2)
.unwrap()
.domain(0.0, 1.0)
.constant::<2>()
.is_err()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0, 2.0])
.unwrap()
.equidistant::<f32>()
.quantity(2)
.is_err()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0, 2.0])
.unwrap()
.equidistant::<f32>()
.quantity(3)
.is_ok()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0, 2.0])
.unwrap()
.equidistant::<f32>()
.quantity(4)
.is_ok()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0, 2.0])
.unwrap()
.equidistant::<f32>()
.quantity(5)
.is_err()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0])
.unwrap()
.equidistant::<f32>()
.degree(1)
.is_ok()
);
assert!(
BSplineDirector::new()
.open()
.elements([0.0, 1.0])
.unwrap()
.equidistant::<f32>()
.degree(2)
.is_err()
);
}
#[test]
fn legacy_errors() {
assert!(BSplineDirector::new().legacy().elements([0.0]).is_err());
assert!(
BSplineDirector::new()
.legacy()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.knots([0.0, 1.0, 2.0])
.is_err()
);
assert!(
BSplineDirector::new()
.legacy()
.elements([0.0, 1.0, 2.0, 3.0])
.unwrap()
.knots([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0])
.unwrap()
.constant::<2>()
.is_err()
);
assert!(
BSplineDirector::new()
.legacy()
.elements([0.0, 1.0, 3.0])
.unwrap()
.knots([0.0, 1.0, 2.0, 3.0])
.is_err()
);
assert!(
BSplineDirector::new()
.legacy()
.elements([0.0, 1.0, 3.0])
.unwrap()
.knots([0.0, 1.0, 2.0, 3.0, 4.0])
.is_ok()
);
assert!(
BSplineDirector::new()
.legacy()
.elements([0.0, 1.0, 3.0])
.unwrap()
.knots([0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
.is_ok()
);
assert!(
BSplineDirector::new()
.legacy()
.elements([0.0, 1.0, 3.0])
.unwrap()
.knots([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.is_err()
);
}
}