#[cfg(not(any(feature = "test", test)))]
use interpolation::lerp;
use crate::radians::{self, Radians};
use crate::state::{Path, TurtleState};
use crate::timer::Timer;
pub trait Animation {
fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus;
}
#[cfg_attr(any(feature = "test", test), allow(dead_code))]
pub enum AnimationStatus {
Running(Option<Path>),
Complete(Option<Path>),
}
fn rotate(angle: Radians, rotation: Radians, clockwise: bool) -> Radians {
let angle = if clockwise { angle - rotation } else { angle + rotation };
angle - radians::TWO_PI * (angle / radians::TWO_PI).floor()
}
pub struct MoveAnimation {
pub path: Path,
pub timer: Timer,
pub total_millis: f64,
}
impl Animation for MoveAnimation {
#[cfg(not(any(feature = "test", test)))]
fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
use AnimationStatus::*;
let MoveAnimation {
ref path,
ref timer,
total_millis,
} = *self;
let elapsed = timer.elapsed_millis() as f64;
if elapsed >= total_millis {
turtle.position = path.end;
Complete(Some(path.clone()))
} else {
let t = elapsed / total_millis;
turtle.position = lerp(&path.start, &path.end, &t);
Running(Some(Path {
start: path.start,
end: turtle.position,
pen: path.pen.clone(),
}))
}
}
#[cfg(any(feature = "test", test))]
fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
use AnimationStatus::*;
let MoveAnimation { ref path, .. } = *self;
turtle.position = path.end;
Complete(Some(path.clone()))
}
}
pub struct RotateAnimation {
pub start: Radians,
pub delta_angle: Radians,
pub clockwise: bool,
pub timer: Timer,
pub total_millis: f64,
}
impl Animation for RotateAnimation {
#[cfg(not(any(feature = "test", test)))]
fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
use AnimationStatus::*;
let RotateAnimation {
start,
delta_angle,
clockwise,
ref timer,
total_millis,
} = *self;
let elapsed = timer.elapsed_millis() as f64;
if elapsed >= total_millis {
turtle.heading = rotate(start, delta_angle, clockwise);
Complete(None)
} else {
let t = elapsed / total_millis;
let angle = lerp(&radians::ZERO, &delta_angle, &t);
turtle.heading = rotate(start, angle, clockwise);
assert!(!turtle.heading.is_nan(), "bug: heading became NaN");
Running(None)
}
}
#[cfg(any(feature = "test", test))]
fn advance(&self, turtle: &mut TurtleState) -> AnimationStatus {
use AnimationStatus::*;
let RotateAnimation {
start,
delta_angle,
clockwise,
..
} = *self;
turtle.heading = rotate(start, delta_angle, clockwise);
Complete(None)
}
}