extern crate euclid;
mod flatten_cubic;
mod cubic_to_quadratic;
use std::mem::swap;
use flatten_cubic::flatten_cubic_bezier;
pub use flatten_cubic::CubicFlatteningIter;
pub use cubic_to_quadratic::cubic_to_quadratic;
pub type Point = euclid::Point2D<f32>;
pub type Vec2 = euclid::Point2D<f32>;
#[derive(Copy, Clone, Debug)]
pub struct QuadraticBezierSegment {
pub from: Point,
pub ctrl: Point,
pub to: Point,
}
impl QuadraticBezierSegment {
pub fn sample(&self, t: f32) -> Point {
let t2 = t*t;
let one_t = 1.0 - t;
let one_t2 = one_t * one_t;
return self.from * one_t2
+ self.ctrl * 2.0 * one_t * t
+ self.to * t2;
}
pub fn sample_x(&self, t: f32) -> f32 {
let t2 = t*t;
let one_t = 1.0 - t;
let one_t2 = one_t * one_t;
return self.from.x * one_t2
+ self.ctrl.x * 2.0*one_t*t
+ self.to.x * t2;
}
pub fn sample_y(&self, t: f32) -> f32 {
let t2 = t*t;
let one_t = 1.0 - t;
let one_t2 = one_t * one_t;
return self.from.y * one_t2
+ self.ctrl.y * 2.0*one_t*t
+ self.to.y * t2;
}
pub fn flip(&mut self) { swap(&mut self.from, &mut self.to); }
pub fn find_y_maximum(&self) -> f32 {
if let Some(t) = self.find_y_inflection() {
let p = self.sample(t);
if p.y > self.from.y && p.y > self.to.y {
return t;
}
}
return if self.from.y > self.to.y { 0.0 } else { 1.0 };
}
pub fn find_y_inflection(&self) -> Option<f32> {
let div = self.from.y - 2.0 * self.ctrl.y + self.to.y;
if div == 0.0 {
return None;
}
let t = (self.from.y - self.ctrl.y) / div;
if t > 0.0 && t < 1.0 {
return Some(t);
}
return None;
}
pub fn split(&self, t: f32) -> (QuadraticBezierSegment, QuadraticBezierSegment) {
let t_one = t - 1.0;
let split_point = self.sample(t);
return (
QuadraticBezierSegment {
from: self.from,
ctrl: self.ctrl * t - self.from * t_one,
to: split_point,
},
QuadraticBezierSegment {
from: split_point,
ctrl: self.to * t - self.ctrl * t_one,
to: self.to,
}
);
}
pub fn before_split(&self, t: f32) -> QuadraticBezierSegment {
let t_one = t - 1.0;
return QuadraticBezierSegment {
from: self.from,
ctrl: self.ctrl * t - self.from * t_one,
to: self.sample(t),
};
}
pub fn after_split(&self, t: f32) -> QuadraticBezierSegment {
let t_one = t - 1.0;
return QuadraticBezierSegment {
from: self.sample(t),
ctrl: self.to * t - self.ctrl * t_one,
to: self.to
};
}
pub fn to_cubic(&self) -> CubicBezierSegment {
CubicBezierSegment {
from: self.from,
ctrl1: (self.from + self.ctrl * 2.0) / 3.0,
ctrl2: (self.to + self.ctrl * 2.0) / 3.0,
to: self.to,
}
}
pub fn flattening_step(&self, tolerance: f32) -> f32 {
let v1 = self.ctrl - self.from;
let v2 = self.to - self.from;
let v1_cross_v2 = v2.x * v1.y - v2.y * v1.x;
let h = v1.x.hypot(v1.y);
if (v1_cross_v2 * h).abs() <= 0.000001 {
return 1.0;
}
let s2inv = h / v1_cross_v2;
let t = 2.0 * (tolerance * s2inv.abs() / 3.0).sqrt();
if t > 1.0 {
return 1.0;
}
return t;
}
pub fn flattened_for_each<F: FnMut(Point)>(&self, tolerance: f32, call_back: &mut F) {
let mut iter = *self;
loop {
let t = iter.flattening_step(tolerance);
if t == 1.0 {
call_back(iter.to);
break
}
iter = iter.after_split(t);
call_back(iter.from);
}
}
pub fn flattening_iter(&self, tolerance: f32) -> QuadraticFlatteningIter {
QuadraticFlatteningIter::new(*self, tolerance)
}
}
pub struct QuadraticFlatteningIter {
curve: QuadraticBezierSegment,
tolerance: f32,
done: bool,
}
impl QuadraticFlatteningIter {
pub fn new(curve: QuadraticBezierSegment, tolerance: f32) -> Self {
assert!(tolerance > 0.0);
QuadraticFlatteningIter {
curve: curve,
tolerance: tolerance,
done: false,
}
}
}
impl Iterator for QuadraticFlatteningIter {
type Item = Point;
fn next(&mut self) -> Option<Point> {
if self.done {
return None;
}
let t = self.curve.flattening_step(self.tolerance);
if t == 1.0 {
self.done = true;
return Some(self.curve.to);
}
self.curve = self.curve.after_split(t);
return Some(self.curve.from);
}
}
#[derive(Copy, Clone, Debug)]
pub struct CubicBezierSegment {
pub from: Point,
pub ctrl1: Point,
pub ctrl2: Point,
pub to: Point,
}
impl CubicBezierSegment {
pub fn sample(&self, t: f32) -> Point {
let t2 = t * t;
let t3 = t2 * t;
let one_t = 1.0 - t;
let one_t2 = one_t * one_t;
let one_t3 = one_t2 * one_t;
return self.from * one_t3
+ self.ctrl1 * 3.0 * one_t2 * t
+ self.ctrl2 * 3.0 * one_t * t2
+ self.to * t3;
}
pub fn split(&self, t: f32) -> (CubicBezierSegment, CubicBezierSegment) {
let ctrl1a = self.from + (self.ctrl1 - self.from) * t;
let ctrl2a = self.ctrl1 + (self.ctrl2 - self.ctrl1) * t;
let ctrl1aa = ctrl1a + (ctrl2a - ctrl1a) * t;
let ctrl3a = self.ctrl2 + (self.to - self.ctrl2) * t;
let ctrl2aa = ctrl2a + (ctrl3a - ctrl2a) * t;
let ctrl1aaa = ctrl1aa + (ctrl2aa - ctrl1aa) * t;
let to = self.to;
return (
CubicBezierSegment {
from: self.from,
ctrl1: ctrl1a,
ctrl2: ctrl1aa,
to: ctrl1aaa,
},
CubicBezierSegment {
from: ctrl1aaa,
ctrl1: ctrl2aa,
ctrl2: ctrl3a,
to: to,
},
);
}
pub fn before_split(&self, t: f32) -> CubicBezierSegment {
let ctrl1a = self.from + (self.ctrl1 - self.from) * t;
let ctrl2a = self.ctrl1 + (self.ctrl2 - self.ctrl1) * t;
let ctrl1aa = ctrl1a + (ctrl2a - ctrl1a) * t;
let ctrl3a = self.ctrl2 + (self.to - self.ctrl2) * t;
let ctrl2aa = ctrl2a + (ctrl3a - ctrl2a) * t;
let ctrl1aaa = ctrl1aa + (ctrl2aa - ctrl1aa) * t;
return CubicBezierSegment {
from: self.from,
ctrl1: ctrl1a,
ctrl2: ctrl1aa,
to: ctrl1aaa,
}
}
pub fn after_split(&self, t: f32) -> CubicBezierSegment {
let ctrl1a = self.from + (self.ctrl1 - self.from) * t;
let ctrl2a = self.ctrl1 + (self.ctrl2 - self.ctrl1) * t;
let ctrl1aa = ctrl1a + (ctrl2a - ctrl1a) * t;
let ctrl3a = self.ctrl2 + (self.to - self.ctrl2) * t;
let ctrl2aa = ctrl2a + (ctrl3a - ctrl2a) * t;
return CubicBezierSegment {
from: ctrl1aa + (ctrl2aa - ctrl1aa) * t,
ctrl1: ctrl2a + (ctrl3a - ctrl2a) * t,
ctrl2: ctrl3a,
to: self.to,
}
}
pub fn flattening_iter(&self, tolerance: f32) -> CubicFlatteningIter {
CubicFlatteningIter::new(*self, tolerance)
}
pub fn flattened_for_each<F: FnMut(Point)>(&self, tolerance: f32, call_back: &mut F) {
flatten_cubic_bezier(*self, tolerance, call_back);
}
}