use num_traits::{Zero, real::Real};
use crate::ops::*;
use std::ops::*;
use std::ops::Add;
use crate::vec::repr_c::{
Vec3 as CVec3,
Vec4 as CVec4,
};
macro_rules! bezier_impl_any {
(3 $Bezier:ident $Point:ident) => {
bezier_impl_any!{$Bezier $Point}
impl<T: Real> $Bezier<T> {
pub fn aabb(self) -> Aabb<T> {
let (min_x, max_x) = self.x_bounds();
let (min_y, max_y) = self.y_bounds();
let (min_z, max_z) = self.z_bounds();
Aabb {
min: Vec3::new(min_x, min_y, min_z),
max: Vec3::new(max_x, max_y, max_z),
}
}
pub fn flipped_z(self) -> Self {
self.into_vector().map(|mut p| {p.z = -p.z; p}).into()
}
pub fn flip_z(&mut self) {
*self = self.flipped_z();
}
}
impl<T> Mul<$Bezier<T>> for Rows3<T> where T: Real + MulAdd<T,T,Output=T> {
type Output = $Bezier<T>;
fn mul(self, rhs: $Bezier<T>) -> $Bezier<T> {
rhs.into_vector().map(|p| self * p).into()
}
}
impl<T> Mul<$Bezier<T>> for Cols3<T> where T: Real + MulAdd<T,T,Output=T> {
type Output = $Bezier<T>;
fn mul(self, rhs: $Bezier<T>) -> $Bezier<T> {
rhs.into_vector().map(|p| self * p).into()
}
}
impl<T> Mul<$Bezier<T>> for Rows4<T> where T: Real + MulAdd<T,T,Output=T> {
type Output = $Bezier<T>;
fn mul(self, rhs: $Bezier<T>) -> $Bezier<T> {
rhs.into_vector().map(|p| self.mul_point(p).into()).into()
}
}
impl<T> Mul<$Bezier<T>> for Cols4<T> where T: Real + MulAdd<T,T,Output=T> {
type Output = $Bezier<T>;
fn mul(self, rhs: $Bezier<T>) -> $Bezier<T> {
rhs.into_vector().map(|p| self.mul_point(p).into()).into()
}
}
};
(2 $Bezier:ident $Point:ident) => {
bezier_impl_any!{$Bezier $Point}
impl<T> Mul<$Bezier<T>> for Rows2<T> where T: Real + MulAdd<T,T,Output=T> {
type Output = $Bezier<T>;
fn mul(self, rhs: $Bezier<T>) -> $Bezier<T> {
rhs.into_vector().map(|p| self * p).into()
}
}
impl<T> Mul<$Bezier<T>> for Cols2<T> where T: Real + MulAdd<T,T,Output=T> {
type Output = $Bezier<T>;
fn mul(self, rhs: $Bezier<T>) -> $Bezier<T> {
rhs.into_vector().map(|p| self * p).into()
}
}
impl<T> Mul<$Bezier<T>> for Rows3<T> where T: Real + MulAdd<T,T,Output=T> {
type Output = $Bezier<T>;
fn mul(self, rhs: $Bezier<T>) -> $Bezier<T> {
rhs.into_vector().map(|p| self.mul_point_2d(p).into()).into()
}
}
impl<T> Mul<$Bezier<T>> for Cols3<T> where T: Real + MulAdd<T,T,Output=T> {
type Output = $Bezier<T>;
fn mul(self, rhs: $Bezier<T>) -> $Bezier<T> {
rhs.into_vector().map(|p| self.mul_point_2d(p).into()).into()
}
}
};
($Bezier:ident $Point:ident) => {
impl<T: Real> $Bezier<T> {
pub fn normalized_tangent(self, t: T) -> Point<T> where T: Add<T, Output=T> {
self.evaluate_derivative(t).normalized()
}
pub fn length_by_discretization(self, step_count: u16) -> T
where T: Add<T, Output=T> + From<u16>
{
let mut length = T::zero();
let mut prev_point = self.evaluate(T::zero());
for i in 1..(step_count+2) {
let t = <T as From<u16>>::from(i)/(<T as From<u16>>::from(step_count)+T::one());
let next_point = self.evaluate(t);
length = length + (next_point - prev_point).magnitude();
prev_point = next_point;
}
length
}
pub fn aabr(self) -> Aabr<T> {
let (min_x, max_x) = self.x_bounds();
let (min_y, max_y) = self.y_bounds();
Aabr {
min: Vec2::new(min_x, min_y),
max: Vec2::new(max_x, max_y),
}
}
pub fn flipped_x(self) -> Self {
self.into_vector().map(|mut p| {p.x = -p.x; p}).into()
}
pub fn flipped_y(self) -> Self {
self.into_vector().map(|mut p| {p.y = -p.y; p}).into()
}
pub fn flip_x(&mut self) {
*self = self.flipped_x();
}
pub fn flip_y(&mut self) {
*self = self.flipped_y();
}
pub fn binary_search_point_by_steps(self, p: Point<T>, steps: u16, epsilon: T) -> (T, Point<T>)
where T: Add<T, Output=T> + From<u16>
{
let steps_f = <T as From<u16>>::from(steps);
let it = (0..steps).map(|i| {
let i = <T as From<u16>>::from(i);
let t = i / steps_f;
(t, self.evaluate(t))
});
let h = (steps_f + steps_f).recip();
self.binary_search_point(p, it, h, epsilon)
}
pub fn binary_search_point<I>(self, p: Point<T>, coarse: I, half_interval: T, epsilon: T) -> (T, Point<T>)
where T: Add<T, Output=T>, I: IntoIterator<Item=(T, Point<T>)>
{
debug_assert!(epsilon > T::epsilon());
let mut t = T::one();
let mut pt = self.end;
let mut d = pt.distance_squared(p);
for (t_, pt_) in coarse {
let d_ = pt_.distance_squared(p);
if d_ < d {
d = d_; pt = pt_; t = t_;
}
}
let mut h = half_interval;
while h >= epsilon {
let (p1, p2) = (self.evaluate(t-h), self.evaluate(t+h));
let (d1, d2) = (p.distance_squared(p1), p.distance_squared(p2));
if d1 < d || d2 < d {
if d1 < d2 {
d = d1; pt = p1; t = t - h;
} else {
d = d2; pt = p2; t = t + h;
}
continue;
}
h = h / (T::one() + T::one());
}
(t, pt)
}
}
};
}
macro_rules! bezier_impl_quadratic_axis {
($QuadraticBezier:ident $Point:ident ($x_s:expr) $x:ident $x_inflection:ident $x_min:ident $x_max:ident $x_bounds:ident) => {
impl<T: Real> $QuadraticBezier<T> {
#[doc=$x_s]
pub fn $x_inflection(self) -> Option<T> {
let div = self.start.$x - (self.ctrl.$x + self.ctrl.$x) + self.end.$x;
if div.abs() <= T::epsilon() {
return None;
}
let t = (self.start.$x - self.ctrl.$x) / div;
if T::zero() <= t && t <= T::one() {
return Some(t);
}
return None;
}
#[doc=$x_s]
pub fn $x_min(self) -> T {
if let Some(t) = self.$x_inflection() {
let p = self.evaluate(t);
if p.$x < self.start.$x && p.$x < self.end.$x {
return t;
}
}
if self.start.$x < self.end.$x { T::zero() } else { T::one() }
}
#[doc=$x_s]
pub fn $x_max(self) -> T {
if let Some(t) = self.$x_inflection() {
let p = self.evaluate(t);
if p.$x > self.start.$x && p.$x > self.end.$x {
return t;
}
}
if self.start.$x > self.end.$x { T::zero() } else { T::one() }
}
#[doc=$x_s]
pub fn $x_bounds(self) -> (T, T) {
(self.$x_min(), self.$x_max())
}
}
};
}
macro_rules! bezier_impl_cubic_axis {
($CubicBezier:ident $Point:ident ($x_s:expr) $x:ident $x_inflections:ident $x_min:ident $x_max:ident $x_bounds:ident) => {
impl<T: Real> $CubicBezier<T> {
#[doc=$x_s]
pub fn $x_inflections(self) -> Option<(T, Option<T>)> {
let two = T::one() + T::one();
let three = two + T::one();
let four = three + T::one();
let six = three + three;
let a = three * (self.end.$x - three * self.ctrl1.$x + three * self.ctrl0.$x - self.start.$x);
let b = six * (self.ctrl1.$x - two * self.ctrl0.$x + self.start.$x);
let c = three * (self.ctrl0.$x - self.start.$x);
if a.abs() <= T::epsilon() {
return if b.abs() <= T::epsilon() {
if c.abs() <= T::epsilon() {
Some((T::zero(), None))
} else {
None
}
} else {
Some((-c / b, None))
};
}
let is_between01 = |t| { T::zero() < t && t < T::one() };
let discriminant = b * b - four * a * c;
if discriminant < T::zero() {
return None;
}
if discriminant.abs() <= T::epsilon() {
let t = -b / (a + a);
return if is_between01(t) {
Some((t, None))
} else {
None
};
}
let discriminant_sqrt = discriminant.sqrt();
let first_extremum = (-b - discriminant_sqrt) / (a + a);
let second_extremum = (-b + discriminant_sqrt) / (a + a);
if is_between01(first_extremum) {
if is_between01(second_extremum) {
Some((first_extremum, Some(second_extremum)))
} else {
Some((first_extremum, None))
}
} else {
if is_between01(second_extremum) {
Some((second_extremum, None))
} else {
None
}
}
}
#[doc=$x_s]
pub fn $x_min(self) -> T {
if let Some((t1, t2)) = self.$x_inflections() {
let p1 = self.evaluate(t1);
if let Some(t2) = t2 {
let p2 = self.evaluate(t2);
if (p1.$x < self.start.$x && p1.$x < self.end.$x)
|| (p2.$x < self.start.$x && p2.$x < self.end.$x)
{
return if p1.$x < p2.$x { t1 } else { t2 };
}
} else {
if p1.$x < self.start.$x && p1.$x < self.end.$x {
return t1;
}
}
}
if self.start.$x < self.end.$x { T::zero() } else { T::one() }
}
#[doc=$x_s]
pub fn $x_max(self) -> T {
if let Some((t1, t2)) = self.$x_inflections() {
let p1 = self.evaluate(t1);
if let Some(t2) = t2 {
let p2 = self.evaluate(t2);
if (p1.$x > self.start.$x && p1.$x > self.end.$x)
|| (p2.$x > self.start.$x && p2.$x > self.end.$x)
{
return if p1.$x > p2.$x { t1 } else { t2 };
}
} else {
if p1.$x > self.start.$x && p1.$x > self.end.$x {
return t1;
}
}
}
if self.start.$x > self.end.$x { T::zero() } else { T::one() }
}
#[doc=$x_s]
pub fn $x_bounds(self) -> (T, T) {
(self.$x_min(), self.$x_max())
}
}
};
}
macro_rules! bezier_impl_quadratic {
($(#[$attrs:meta])* 3 $QuadraticBezier:ident $CubicBezier:ident $Point:ident $LineSegment:ident) => {
bezier_impl_quadratic!{$(#[$attrs])* $QuadraticBezier $CubicBezier $Point $LineSegment}
bezier_impl_quadratic_axis!{$QuadraticBezier $Point ("Z") z z_inflection min_z max_z z_bounds}
bezier_impl_any!(3 $QuadraticBezier $Point);
};
($(#[$attrs:meta])* 2 $QuadraticBezier:ident $CubicBezier:ident $Point:ident $LineSegment:ident) => {
bezier_impl_quadratic!{$(#[$attrs])* $QuadraticBezier $CubicBezier $Point $LineSegment}
bezier_impl_any!(2 $QuadraticBezier $Point);
};
($(#[$attrs:meta])* $QuadraticBezier:ident $CubicBezier:ident $Point:ident $LineSegment:ident) => {
type Point<T> = $Point<T>;
$(#[$attrs])*
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, )]
#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
pub struct $QuadraticBezier<T> {
pub start: Point<T>,
pub ctrl: Point<T>,
pub end: Point<T>,
}
impl<T: Real> $QuadraticBezier<T> {
pub fn evaluate(self, t: T) -> Point<T> {
let l = T::one();
let two = l+l;
self.start*(l-t)*(l-t) + self.ctrl*two*(l-t)*t + self.end*t*t
}
pub fn evaluate_derivative(self, t: T) -> Point<T> {
let l = T::one();
let n = l+l;
(self.ctrl-self.start)*(l-t)*n + (self.end-self.ctrl)*t*n
}
pub fn matrix() -> Mat3<T> {
let zero = T::zero();
let one = T::one();
let two = one+one;
Mat3 {
rows: CVec3::new(
Vec3::new( one, zero, zero),
Vec3::new(-two, two, zero),
Vec3::new( one, -two, one),
)
}
}
pub fn split(self, t: T) -> [Self; 2] {
let l = T::one();
let two = l+l;
let first = $QuadraticBezier {
start: self.start,
ctrl: self.ctrl*t - self.start*(t-l),
end: self.end*t*t - self.ctrl*two*t*(t-l) + self.start*(t-l)*(t-l),
};
let second = $QuadraticBezier {
start: self.end*t*t - self.ctrl*two*t*(t-l) + self.start*(t-l)*(t-l),
ctrl: self.end*t - self.ctrl*(t-l),
end: self.end,
};
[first, second]
}
pub fn into_cubic(self) -> $CubicBezier<T> {
self.into()
}
}
impl<T> $QuadraticBezier<T> {
pub fn reversed(mut self) -> Self {
self.reverse();
self
}
pub fn reverse(&mut self) {
std::mem::swap(&mut self.start, &mut self.end);
}
pub(crate) fn into_vector(self) -> Vec3<Point<T>> {
self.into_vec3()
}
pub fn into_vec3(self) -> Vec3<Point<T>> {
self.into()
}
pub fn into_tuple(self) -> (Point<T>, Point<T>, Point<T>) {
self.into_vec3().into_tuple()
}
pub fn into_array(self) -> [Point<T>; 3] {
self.into_vec3().into_array()
}
}
impl<T> From<Vec3<Point<T>>> for $QuadraticBezier<T> {
fn from(v: Vec3<Point<T>>) -> Self {
$QuadraticBezier {
start: v.x,
ctrl: v.y,
end: v.z
}
}
}
impl<T> From<$QuadraticBezier<T>> for Vec3<Point<T>> {
fn from(v: $QuadraticBezier<T>) -> Self {
Vec3::new(v.start, v.ctrl, v.end)
}
}
impl<T: Real> From<$LineSegment<T>> for $QuadraticBezier<T> {
fn from(line_segment: $LineSegment<T>) -> Self {
let ctrl = (line_segment.start + line_segment.end) / (T::one() + T::one());
Self {
start: line_segment.start,
ctrl,
end: line_segment.end
}
}
}
impl<T: Real> From<Range<Point<T>>> for $QuadraticBezier<T> {
fn from(range: Range<Point<T>>) -> Self {
Self::from($LineSegment::from(range))
}
}
bezier_impl_quadratic_axis!{$QuadraticBezier $Point ("X") x x_inflection min_x max_x x_bounds}
bezier_impl_quadratic_axis!{$QuadraticBezier $Point ("Y") y y_inflection min_y max_y y_bounds}
}
}
macro_rules! bezier_impl_2d_into_3d {
($Bezier2:ident $Bezier3:ident) => {
impl<T: Zero> $Bezier2<T> {
pub fn into_3d(self) -> $Bezier3<T> {
self.into()
}
}
impl<T: Zero> From<$Bezier2<T>> for $Bezier3<T> {
fn from(c: $Bezier2<T>) -> Self {
c.into_vector().map(Into::into).into()
}
}
};
}
macro_rules! bezier_impl_3d_into_2d {
($Bezier3:ident $Bezier2:ident) => {
impl<T> $Bezier3<T> {
pub fn into_2d(self) -> $Bezier2<T> {
self.into()
}
}
impl<T> From<$Bezier3<T>> for $Bezier2<T> {
fn from(c: $Bezier3<T>) -> Self {
c.into_vector().map(Into::into).into()
}
}
};
}
macro_rules! bezier_impl_cubic {
($(#[$attrs:meta])* 3 $QuadraticBezier:ident $CubicBezier:ident $Point:ident $LineSegment:ident) => {
bezier_impl_cubic!{$(#[$attrs])* $QuadraticBezier $CubicBezier $Point $LineSegment}
bezier_impl_cubic_axis!{$CubicBezier $Point ("Z") z z_inflections min_z max_z z_bounds}
bezier_impl_any!(3 $CubicBezier $Point);
};
($(#[$attrs:meta])* 2 $QuadraticBezier:ident $CubicBezier:ident $Point:ident $LineSegment:ident) => {
bezier_impl_cubic!{$(#[$attrs])* $QuadraticBezier $CubicBezier $Point $LineSegment}
bezier_impl_any!(2 $CubicBezier $Point);
};
($(#[$attrs:meta])* $QuadraticBezier:ident $CubicBezier:ident $Point:ident $LineSegment:ident) => {
type Point<T> = $Point<T>;
$(#[$attrs])*
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, )]
#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
pub struct $CubicBezier<T> {
pub start: Point<T>,
pub ctrl0: Point<T>,
pub ctrl1: Point<T>,
pub end: Point<T>,
}
impl<T: Real> $CubicBezier<T> {
pub fn evaluate(self, t: T) -> Point<T> {
let l = T::one();
let three = l+l+l;
self.start*(l-t)*(l-t)*(l-t) + self.ctrl0*three*(l-t)*(l-t)*t + self.ctrl1*three*(l-t)*t*t + self.end*t*t*t
}
pub fn evaluate_derivative(self, t: T) -> Point<T> {
let l = T::one();
let n = l+l+l;
let two = l+l;
(self.ctrl0-self.start)*(l-t)*(l-t)*n + (self.ctrl1-self.ctrl0)*two*(l-t)*t*n + (self.end-self.ctrl1)*t*t*n
}
pub fn matrix() -> Mat4<T> {
let zero = T::zero();
let one = T::one();
let three = one+one+one;
let six = three + three;
Mat4 {
rows: CVec4::new(
Vec4::new( one, zero, zero, zero),
Vec4::new(-three, three, zero, zero),
Vec4::new( three, -six, three, zero),
Vec4::new(-one, three, -three, one),
)
}
}
pub fn split(self, t: T) -> [Self; 2] {
let l = T::one();
let two = l+l;
let three = l+l+l;
let first = $CubicBezier {
start: self.start,
ctrl0: self.ctrl0*t - self.start*(t-l),
ctrl1: self.ctrl1*t*t - self.ctrl0*two*t*(t-l) + self.start*(t-l)*(t-l),
end: self.end*t*t*t - self.ctrl1*three*t*t*(t-l) + self.ctrl0*three*t*(t-l)*(t-l) - self.start*(t-l)*(t-l)*(t-l),
};
let second = $CubicBezier {
start: self.end*t*t*t - self.ctrl1*three*t*t*(t-l) + self.ctrl0*three*t*(t-l)*(t-l) - self.start*(t-l)*(t-l)*(t-l),
ctrl0: self.end*t*t - self.ctrl1*two*t*(t-l) + self.ctrl0*(t-l)*(t-l),
ctrl1: self.end*t - self.ctrl1*(t-l),
end: self.end,
};
[first, second]
}
pub fn unit_quarter_circle() -> Self {
let (two, three) = (T::one()+T::one(), T::one()+T::one()+T::one());
let coeff = (two+two)*(two.sqrt()-T::one())/three;
Self {
start: Vec2::unit_x().into(),
ctrl0: (Vec2::unit_x() + Vec2::unit_y()*coeff).into(),
ctrl1: (Vec2::unit_x()*coeff + Vec2::unit_y()).into(),
end: Vec2::unit_y().into(),
}
}
pub fn unit_circle() -> [Self; 4] {
let a = Self::unit_quarter_circle();
let b = a.flipped_x();
let c = b.flipped_y();
let d = a.flipped_y();
[a, b, c, d]
}
}
impl<T> $CubicBezier<T> {
pub fn reversed(mut self) -> Self {
self.reverse();
self
}
pub fn reverse(&mut self) {
std::mem::swap(&mut self.start, &mut self.end);
std::mem::swap(&mut self.ctrl0, &mut self.ctrl1);
}
pub(crate) fn into_vector(self) -> Vec4<Point<T>> {
self.into_vec4()
}
pub fn into_vec4(self) -> Vec4<Point<T>> {
self.into()
}
pub fn into_tuple(self) -> (Point<T>, Point<T>, Point<T>, Point<T>) {
self.into_vec4().into_tuple()
}
pub fn into_array(self) -> [Point<T>; 4] {
self.into_vec4().into_array()
}
}
impl<T> From<Vec4<Point<T>>> for $CubicBezier<T> {
fn from(v: Vec4<Point<T>>) -> Self {
$CubicBezier {
start: v.x,
ctrl0: v.y,
ctrl1: v.z,
end: v.w
}
}
}
impl<T> From<$CubicBezier<T>> for Vec4<Point<T>> {
fn from(v: $CubicBezier<T>) -> Self {
Vec4::new(v.start, v.ctrl0, v.ctrl1, v.end)
}
}
impl<T: Real + Lerp<T,Output=T>> From<$LineSegment<T>> for $CubicBezier<T>
{
fn from(line_segment: $LineSegment<T>) -> Self {
let three = T::one() + T::one() + T::one();
let t = three.recip();
let ctrl0 = Lerp::lerp_unclamped(line_segment.start, line_segment.end, t);
let ctrl1 = Lerp::lerp_unclamped(line_segment.start, line_segment.end, t+t);
Self {
start: line_segment.start,
ctrl0,
ctrl1,
end: line_segment.end
}
}
}
impl<T: Real + Lerp<T, Output=T>> From<Range<Point<T>>> for $CubicBezier<T> {
fn from(range: Range<Point<T>>) -> Self {
Self::from($LineSegment::from(range))
}
}
impl<T: Real> From<$QuadraticBezier<T>> for $CubicBezier<T> {
fn from(b: $QuadraticBezier<T>) -> Self {
let three = T::one() + T::one() + T::one();
$CubicBezier {
start: b.start,
ctrl0: (b.start + b.ctrl + b.ctrl) / three,
ctrl1: (b.end + b.ctrl + b.ctrl) / three,
end: b.end,
}
}
}
bezier_impl_cubic_axis!{$CubicBezier $Point ("X") x x_inflections min_x max_x x_bounds}
bezier_impl_cubic_axis!{$CubicBezier $Point ("Y") y y_inflections min_y max_y y_bounds}
}
}
macro_rules! impl_all_beziers {
($mod:ident) => {
use crate::vec::$mod::{Vec3, Vec4, Vec2};
use crate::mat::$mod::row_major::{Mat2 as Rows2, Mat3 as Rows3, Mat4 as Rows4};
use crate::mat::$mod::column_major::{Mat2 as Cols2, Mat3 as Cols3, Mat4 as Cols4};
use crate::geom::$mod::{LineSegment2, LineSegment3, Aabr, Aabb};
use self::Rows4 as Mat4;
use self::Rows3 as Mat3;
mod quadratic_bezier2 {
use super::*;
bezier_impl_quadratic!{
2 QuadraticBezier2 CubicBezier2 Vec2 LineSegment2
}
bezier_impl_2d_into_3d!{QuadraticBezier2 QuadraticBezier3}
}
mod quadratic_bezier3 {
use super::*;
bezier_impl_quadratic!{
3 QuadraticBezier3 CubicBezier3 Vec3 LineSegment3
}
bezier_impl_3d_into_2d!{QuadraticBezier3 QuadraticBezier2}
}
mod cubic_bezier2 {
use super::*;
bezier_impl_cubic!{
2 QuadraticBezier2 CubicBezier2 Vec2 LineSegment2
}
bezier_impl_2d_into_3d!{CubicBezier2 CubicBezier3}
}
mod cubic_bezier3 {
use super::*;
bezier_impl_cubic!{
3 QuadraticBezier3 CubicBezier3 Vec3 LineSegment3
}
bezier_impl_3d_into_2d!{CubicBezier3 CubicBezier2}
}
pub use quadratic_bezier2::*;
pub use quadratic_bezier3::*;
pub use cubic_bezier2::*;
pub use cubic_bezier3::*;
};
}
#[cfg(all(nightly, feature="repr_simd"))]
pub mod repr_simd {
use super::*;
impl_all_beziers!{repr_simd}
}
pub mod repr_c {
use super::*;
impl_all_beziers!{repr_c}
}
pub use self::repr_c::*;
#[cfg(test)]
mod tests {
use super::*;
use crate::vec::{Vec2, Vec3};
macro_rules! test {
($Bezier:ident $bezier:ident $Vec:ident) => {
mod $bezier {
use super::*;
#[test] fn lerp_from_line_segment() {
let count = 32;
let t_iter = (0..(count+1)).into_iter().map(|i| i as f32 / (count as f32));
let l = || $Vec::<f32>::unit_x() .. $Vec::<f32>::unit_y();
let c = $Bezier::from(l());
for t in t_iter {
assert_relative_eq!(c.evaluate(t), Lerp::lerp_unclamped_precise(l().start, l().end, t))
}
}
}
};
}
test!{QuadraticBezier2 quadratic2 Vec2}
test!{QuadraticBezier3 quadratic3 Vec3}
test!{CubicBezier2 cubic2 Vec2}
test!{CubicBezier3 cubic3 Vec3}
}