extern crate num_traits;
use self::num_traits::Float;
use core::ops::*;
use vec::{Vec3, Vec4, Xy, Xyz};
use mat::{Mat3, Mat4};
use geom::{Line2, Line3};
macro_rules! bezier_impl_any {
($Bezier:ident $Point:ident) => {
impl<T> $Bezier<T> {
pub fn normalized_tangent(self, t: T) -> $Point<T> where T: Float {
self.evaluate_derivative(t).normalized()
}
pub fn approx_length(self, step_count: u32) -> T
where T: Float + AddAssign
{
let mut length = T::zero();
let mut prev_point = self.evaluate(T::zero());
for i in 1..step_count+2 {
let t = T::from(i).unwrap()/(T::from(step_count).unwrap()+T::one());
let next_point = self.evaluate(t);
length += (next_point - prev_point).length();
prev_point = next_point;
}
length
}
}
}
}
macro_rules! bezier_impl_quadratic {
($QuadraticBezier:ident $Point:ident $Line:ident) => {
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct $QuadraticBezier<T>(pub $Point<T>, pub $Point<T>, pub $Point<T>);
impl<T: Float> $QuadraticBezier<T> {
pub fn evaluate(self, t: T) -> $Point<T> {
let l = T::one();
let two = l+l;
self.0*(l-t)*(l-t) + self.1*two*(l-t)*t + self.2*t*t
}
pub fn evaluate_derivative(self, t: T) -> $Point<T> {
let l = T::one();
let n = l+l;
(self.1-self.0)*(l-t)*n + (self.2-self.1)*t*n
}
pub fn from_line(line: $Line<T>) -> Self {
$QuadraticBezier(line.a, line.a, line.b)
}
pub fn matrix() -> Mat3<T> {
let zero = T::zero();
let one = T::one();
let two = one+one;
Mat3 {
rows: Vec3(
Vec3( one, zero, zero),
Vec3(-two, two, zero),
Vec3( one, -two, one),
)
}
}
pub fn split(self, t: T) -> (Self, Self) {
let l = T::one();
let two = l+l;
let first = $QuadraticBezier(
self.0,
self.1*t - self.0*(t-l),
self.2*t*t - self.1*two*t*(t-l) + self.0*(t-l)*(t-l),
);
let second = $QuadraticBezier(
self.2*t*t - self.1*two*t*(t-l) + self.0*(t-l)*(t-l),
self.2*t - self.1*(t-l),
self.2,
);
(first, second)
}
}
impl<T> From<Vec3<$Point<T>>> for $QuadraticBezier<T> {
fn from(v: Vec3<$Point<T>>) -> Self {
$QuadraticBezier(v.0, v.1, v.2)
}
}
impl<T> From<$QuadraticBezier<T>> for Vec3<$Point<T>> {
fn from(v: $QuadraticBezier<T>) -> Self {
Vec3(v.0, v.1, v.2)
}
}
bezier_impl_any!($QuadraticBezier $Point);
}
}
macro_rules! bezier_impl_cubic {
($CubicBezier:ident $Point:ident $Line:ident) => {
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct $CubicBezier<T>(pub $Point<T>, pub $Point<T>, pub $Point<T>, pub $Point<T>);
impl<T: Float> $CubicBezier<T> {
pub fn evaluate(self, t: T) -> $Point<T> {
let l = T::one();
let three = l+l+l;
self.0*(l-t)*(l-t)*(l-t) + self.1*three*(l-t)*(l-t)*t + self.2*three*(l-t)*t*t + self.3*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.1-self.0)*(l-t)*(l-t)*n + (self.2-self.1)*two*(l-t)*t*n + (self.3-self.2)*t*t*n
}
pub fn from_line(line: $Line<T>) -> Self {
$CubicBezier(line.a, line.a, line.b, line.b)
}
pub fn matrix() -> Mat4<T> {
let zero = T::zero();
let one = T::one();
let three = one+one+one;
let six = three + three;
Mat4 {
rows: Vec4(
Vec4( one, zero, zero, zero),
Vec4(-three, three, zero, zero),
Vec4( three, -six, three, zero),
Vec4(-one, three, -three, one),
)
}
}
pub fn split(self, t: T) -> (Self, Self) {
let l = T::one();
let two = l+l;
let three = l+l+l;
let first = $CubicBezier(
self.0,
self.1*t - self.0*(t-l),
self.2*t*t - self.1*two*t*(t-l) + self.0*(t-l)*(t-l),
self.3*t*t*t - self.2*three*t*t*(t-l) + self.1*three*t*(t-l)*(t-l) - self.0*(t-l)*(t-l)*(t-l),
);
let second = $CubicBezier(
self.3*t*t*t - self.2*three*t*t*(t-l) + self.1*three*t*(t-l)*(t-l) - self.0*(t-l)*(t-l)*(t-l),
self.3*t*t - self.2*two*t*(t-l) + self.1*(t-l)*(t-l),
self.3*t - self.2*(t-l),
self.3,
);
(first, second)
}
}
impl<T> From<Vec4<$Point<T>>> for $CubicBezier<T> {
fn from(v: Vec4<$Point<T>>) -> Self {
$CubicBezier(v.0, v.1, v.2, v.3)
}
}
impl<T> From<$CubicBezier<T>> for Vec4<$Point<T>> {
fn from(v: $CubicBezier<T>) -> Self {
Vec4(v.0, v.1, v.2, v.3)
}
}
bezier_impl_any!($CubicBezier $Point);
}
}
bezier_impl_quadratic!(QuadraticBezier2 Xy Line2);
bezier_impl_quadratic!(QuadraticBezier3 Xyz Line3);
bezier_impl_cubic!(CubicBezier2 Xy Line2);
bezier_impl_cubic!(CubicBezier3 Xyz Line3);