use super::basis::*;
use super::curve::*;
use super::section::*;
use super::derivative::*;
use crate::geo::*;
use ouroboros::*;
#[inline]
pub fn walk_curve_unevenly<Curve>(curve: &Curve, num_subdivisions: usize) -> impl '_ + Iterator<Item=CurveSection<'_, Curve>>
where
Curve: BezierCurve,
{
if num_subdivisions > 0 {
UnevenWalkIterator {
curve: curve,
step: (1.0)/(num_subdivisions as f64),
num_subdivisions: num_subdivisions,
last_subdivision: 0
}
} else {
UnevenWalkIterator {
curve: curve,
step: 0.0,
num_subdivisions: 0,
last_subdivision: 0
}
}
}
pub fn walk_curve_evenly<Curve>(curve: &Curve, distance: f64, max_error: f64) -> EvenWalkIterator<'_, Curve>
where
Curve: BezierCurve,
{
const INITIAL_INCREMENT: f64 = 0.01;
let max_error = if max_error < 1e-10 { 1e-10 } else { max_error };
let distance = if distance < 1e-10 { 1e-10 } else { distance };
let (cp1, cp2) = curve.control_points();
let (wn1, wn2, wn3) = derivative4(curve.start_point(), cp1, cp2, curve.end_point());
let initial_speed = de_casteljau3(0.001, wn1, wn2, wn3).magnitude();
let initial_speed = if distance/(initial_speed.abs()) > 0.25 { de_casteljau3(0.01, wn1, wn2, wn3).magnitude() } else { initial_speed };
let initial_increment = if initial_speed.abs() < 0.00000001 {
INITIAL_INCREMENT
} else {
distance / initial_speed
};
let initial_increment = if initial_increment > 0.25 { INITIAL_INCREMENT } else { initial_increment };
EvenWalkIterator {
last_point: curve.start_point(),
curve: curve,
derivative: (wn1, wn2, wn3),
last_t: 0.0,
last_increment: initial_increment,
distance: distance,
max_error: max_error
}
}
struct UnevenWalkIterator<'a, Curve: BezierCurve> {
curve: &'a Curve,
step: f64,
num_subdivisions: usize,
last_subdivision: usize
}
impl<'a, Curve: BezierCurve> Iterator for UnevenWalkIterator<'a, Curve> {
type Item = CurveSection<'a, Curve>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.last_subdivision >= self.num_subdivisions {
None
} else {
let t_min = self.step * (self.last_subdivision as f64);
self.last_subdivision += 1;
let t_max = self.step * (self.last_subdivision as f64);
Some(self.curve.section(t_min, t_max))
}
}
}
pub struct EvenWalkIterator<'a, Curve: BezierCurve> {
curve: &'a Curve,
derivative: (Curve::Point, Curve::Point, Curve::Point),
last_t: f64,
last_point: Curve::Point,
last_increment: f64,
distance: f64,
max_error: f64
}
struct VaryingWalkIterator<'a, Curve: BezierCurve, DistanceIter: 'a+Iterator<Item=f64>> {
even_iterator: EvenWalkIterator<'a, Curve>,
distance_iterator: Option<DistanceIter>
}
impl<'a, Curve: BezierCurve> EvenWalkIterator<'a, Curve> {
pub fn vary_by<DistanceIter: 'a+Iterator<Item=f64>>(self, distance: DistanceIter) -> impl 'a+Iterator<Item=CurveSection<'a, Curve>> {
VaryingWalkIterator {
even_iterator: self,
distance_iterator: Some(distance)
}
}
}
impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> {
type Item = CurveSection<'a, Curve>;
fn next(&mut self) -> Option<Self::Item> {
const MAX_ITERATIONS: usize = 32;
let curve = self.curve;
let (wn1, wn2, wn3) = self.derivative;
let distance = self.distance;
let max_error = self.max_error;
let mut t_increment = self.last_increment;
let last_t = self.last_t;
let mut next_t = last_t + t_increment;
let last_point = self.last_point;
let mut next_point;
if last_t >= 1.0 {
return None;
}
if next_t >= 1.0 {
if last_point.distance_to(&curve.point_at_pos(1.0)) < distance {
let last_section = curve.section(last_t, 1.0);
self.last_t = 1.0;
return Some(last_section);
}
}
let mut count = 0;
loop {
debug_assert!(!t_increment.is_nan());
next_point = curve.point_at_pos(next_t);
let next_distance = last_point.distance_to(&next_point);
let error = distance - next_distance;
if error.abs() < max_error {
break;
}
let tangent = de_casteljau3(next_t, wn1, wn2, wn3);
let speed = tangent.magnitude();
if speed.abs() < 0.00000001 {
let error_ratio = distance / next_distance;
t_increment = if error_ratio < 0.5 {
t_increment * 0.5
} else if error_ratio > 1.5 {
t_increment * 1.5
} else {
t_increment * error_ratio
};
} else {
let error = next_distance - distance;
let adjustment = error / speed;
if adjustment >= t_increment {
t_increment *= 0.3333333;
} else {
t_increment -= adjustment;
}
}
next_t = last_t + t_increment;
count += 1;
if count >= MAX_ITERATIONS {
break;
}
}
if next_t > 1.0 {
let last_section = curve.section(last_t, 1.0);
self.last_t = 1.0;
return Some(last_section);
}
self.last_point = next_point;
self.last_increment = t_increment;
self.last_t = next_t;
Some(self.curve.section(last_t, next_t))
}
}
impl<'a, Curve: BezierCurve, DistanceIter: 'a+Iterator<Item=f64>> Iterator for VaryingWalkIterator<'a, Curve, DistanceIter> {
type Item = CurveSection<'a, Curve>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(distance_iterator) = &mut self.distance_iterator {
if let Some(distance) = distance_iterator.next() {
let ratio = distance / self.even_iterator.distance;
self.even_iterator.distance = distance;
self.even_iterator.last_increment *= ratio;
} else {
self.distance_iterator = None;
}
}
self.even_iterator.next()
}
}
#[inline]
pub fn walk_curve_evenly_map<TCurve, TResult>(curve: TCurve, distance: f64, max_error: f64, map_fn: impl for<'a> Fn(CurveSection<'a, TCurve>) -> TResult) -> impl Iterator<Item=TResult>
where
TCurve: 'static + BezierCurve,
{
EvenWalkMapIteratorBuilder {
curve: curve,
map_fn: map_fn,
walk_iterator_builder: move |curve| walk_curve_evenly(curve, distance, max_error)
}.build()
}
#[self_referencing]
struct EvenWalkMapIterator<TCurve, TMapFn, TResult>
where
TCurve: 'static + BezierCurve,
TMapFn: for<'a> Fn(CurveSection<'a, TCurve>) -> TResult,
{
curve: TCurve,
map_fn: TMapFn,
#[borrows(curve)]
#[not_covariant]
walk_iterator: EvenWalkIterator<'this, TCurve>,
}
impl<TCurve, TMapFn, TResult> Iterator for EvenWalkMapIterator<TCurve, TMapFn, TResult>
where
TCurve: 'static + BezierCurve,
TMapFn: for<'a> Fn(CurveSection<'a, TCurve>) -> TResult,
{
type Item = TResult;
#[inline]
fn next(&mut self) -> Option<TResult> {
self.with_mut(|fields| {
let next = fields.walk_iterator.next();
let next = next.map(|next| (fields.map_fn)(next));
next
})
}
}