use crate::geom::{CubicBezierSegment, QuadraticBezierSegment};
use crate::math::*;
use crate::path::builder::*;
use crate::path::{EndpointId, PathEvent};
use std::f32;
pub fn walk_along_path<Iter>(path: Iter, start: f32, pattern: &mut dyn Pattern)
where
Iter: Iterator<Item = PathEvent>,
{
let mut walker = PathWalker::new(start, pattern);
for evt in path {
walker.path_event(evt);
if walker.done {
return;
}
}
}
pub trait Pattern {
fn next(&mut self, position: Point, tangent: Vector, distance: f32) -> Option<f32>;
fn begin(&mut self, distance: f32) -> Option<f32> {
Some(distance)
}
}
pub struct PathWalker<'l> {
prev: Point,
advancement: f32,
leftover: f32,
next_distance: f32,
first: Point,
need_moveto: bool,
done: bool,
pattern: &'l mut dyn Pattern,
}
impl<'l> PathWalker<'l> {
pub fn new(start: f32, pattern: &'l mut dyn Pattern) -> PathWalker<'l> {
let start = f32::max(start, 0.0);
PathWalker {
prev: point(0.0, 0.0),
first: point(0.0, 0.0),
advancement: 0.0,
leftover: 0.0,
next_distance: start,
need_moveto: true,
done: false,
pattern,
}
}
}
impl<'l> PathBuilder for PathWalker<'l> {
fn begin(&mut self, to: Point) -> EndpointId {
self.need_moveto = false;
self.first = to;
self.prev = to;
if let Some(distance) = self.pattern.begin(self.next_distance) {
self.next_distance = distance;
} else {
self.done = true;
}
EndpointId::INVALID
}
fn line_to(&mut self, to: Point) -> EndpointId {
debug_assert!(!self.need_moveto);
let v = to - self.prev;
let d = v.length();
if d < 1e-5 {
return EndpointId::INVALID;
}
let tangent = v / d;
let mut distance = self.leftover + d;
while distance >= self.next_distance {
let position = self.prev + tangent * (self.next_distance - self.leftover);
self.prev = position;
self.leftover = 0.0;
self.advancement += self.next_distance;
distance -= self.next_distance;
if let Some(distance) = self.pattern.next(position, tangent, self.advancement) {
self.next_distance = distance;
} else {
self.done = true;
return EndpointId::INVALID;
}
}
self.prev = to;
self.leftover = distance;
EndpointId::INVALID
}
fn end(&mut self, close: bool) {
if close {
let first = self.first;
self.line_to(first);
self.need_moveto = true;
}
}
fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) -> EndpointId {
let curve = QuadraticBezierSegment {
from: self.prev,
ctrl,
to,
};
curve.for_each_flattened(0.01, &mut |p| {
self.line_to(p);
});
EndpointId::INVALID
}
fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) -> EndpointId {
let curve = CubicBezierSegment {
from: self.prev,
ctrl1,
ctrl2,
to,
};
curve.for_each_flattened(0.01, &mut |p| {
self.line_to(p);
});
EndpointId::INVALID
}
}
pub struct RegularPattern<Cb> {
pub callback: Cb,
pub interval: f32,
}
impl<Cb> Pattern for RegularPattern<Cb>
where
Cb: FnMut(Point, Vector, f32) -> bool,
{
#[inline]
fn next(&mut self, position: Point, tangent: Vector, distance: f32) -> Option<f32> {
if !(self.callback)(position, tangent, distance) {
return None;
}
Some(self.interval)
}
}
pub struct RepeatedPattern<'l, Cb> {
pub callback: Cb,
pub intervals: &'l [f32],
pub index: usize,
}
impl<'l, Cb> Pattern for RepeatedPattern<'l, Cb>
where
Cb: FnMut(Point, Vector, f32) -> bool,
{
#[inline]
fn next(&mut self, position: Point, tangent: Vector, distance: f32) -> Option<f32> {
if !(self.callback)(position, tangent, distance) {
return None;
}
let idx = self.index % self.intervals.len();
self.index += 1;
Some(self.intervals[idx])
}
}
impl<Cb> Pattern for Cb
where
Cb: FnMut(Point, Vector, f32) -> Option<f32>,
{
#[inline]
fn next(&mut self, position: Point, tangent: Vector, distance: f32) -> Option<f32> {
(self)(position, tangent, distance)
}
}
#[test]
fn walk_square() {
let expected = [
(point(0.0, 0.0), vector(1.0, 0.0), 0.0),
(point(2.0, 0.0), vector(1.0, 0.0), 2.0),
(point(4.0, 0.0), vector(1.0, 0.0), 4.0),
(point(6.0, 0.0), vector(1.0, 0.0), 6.0),
(point(6.0, 2.0), vector(0.0, 1.0), 8.0),
(point(6.0, 4.0), vector(0.0, 1.0), 10.0),
(point(6.0, 6.0), vector(0.0, 1.0), 12.0),
(point(4.0, 6.0), vector(-1.0, 0.0), 14.0),
(point(2.0, 6.0), vector(-1.0, 0.0), 16.0),
(point(0.0, 6.0), vector(-1.0, 0.0), 18.0),
(point(0.0, 4.0), vector(0.0, -1.0), 20.0),
(point(0.0, 2.0), vector(0.0, -1.0), 22.0),
(point(0.0, 0.0), vector(0.0, -1.0), 24.0),
];
let mut i = 0;
let mut pattern = RegularPattern {
interval: 2.0,
callback: |pos, n, d| {
println!("p:{:?} n:{:?} d:{:?}", pos, n, d);
assert_eq!(pos, expected[i].0);
assert_eq!(n, expected[i].1);
assert_eq!(d, expected[i].2);
i += 1;
true
},
};
let mut walker = PathWalker::new(0.0, &mut pattern);
walker.begin(point(0.0, 0.0));
walker.line_to(point(6.0, 0.0));
walker.line_to(point(6.0, 6.0));
walker.line_to(point(0.0, 6.0));
walker.close();
}
#[test]
fn walk_with_leftover() {
let expected = [
(point(1.0, 0.0), vector(1.0, 0.0), 1.0),
(point(4.0, 0.0), vector(1.0, 0.0), 4.0),
(point(5.0, 2.0), vector(0.0, 1.0), 7.0),
(point(5.0, 5.0), vector(0.0, 1.0), 10.0),
(point(2.0, 5.0), vector(-1.0, 0.0), 13.0),
(point(0.0, 4.0), vector(0.0, -1.0), 16.0),
(point(0.0, 1.0), vector(0.0, -1.0), 19.0),
];
let mut i = 0;
let mut pattern = RegularPattern {
interval: 3.0,
callback: |pos, n, d| {
println!("p:{:?} n:{:?} d:{:?}", pos, n, d);
assert_eq!(pos, expected[i].0);
assert_eq!(n, expected[i].1);
assert_eq!(d, expected[i].2);
i += 1;
true
},
};
let mut walker = PathWalker::new(1.0, &mut pattern);
walker.begin(point(0.0, 0.0));
walker.line_to(point(5.0, 0.0));
walker.line_to(point(5.0, 5.0));
walker.line_to(point(0.0, 5.0));
walker.close();
}
#[test]
fn walk_starting_after() {
let cb = &mut |_, _, _| -> Option<f32> { panic!() };
let mut walker = PathWalker::new(10.0, cb);
walker.begin(point(0.0, 0.0));
walker.line_to(point(5.0, 0.0));
walker.end(false);
}
#[test]
fn walk_abort_early() {
let mut callback_counter = 0;
let mut pattern = RegularPattern {
interval: 3.0,
callback: |_pos, _n, _d| {
callback_counter += 1;
false
},
};
let mut walker = PathWalker::new(1.0, &mut pattern);
walker.begin(point(0.0, 0.0));
walker.line_to(point(100.0, 0.0));
assert_eq!(callback_counter, 1);
}