use graphics::ImageSize;
use graphics::math::Scalar;
use ai_behavior::{
Status,
Success,
Running,
};
use interpolation::EaseFunction;
use sprite::Sprite;
#[derive(Clone, PartialEq)]
pub enum Animation {
MoveTo(f64, Scalar, Scalar),
MoveBy(f64, Scalar, Scalar),
RotateTo(f64, Scalar),
RotateBy(f64, Scalar),
ScaleTo(f64, Scalar, Scalar),
ScaleBy(f64, Scalar, Scalar),
FlipX(bool),
FlipY(bool),
Show,
Hide,
ToggleVisibility,
Blink(f64, usize),
FadeIn(f64),
FadeOut(f64),
FadeTo(f64, f64),
Ease(EaseFunction, Box<Animation>),
}
impl Animation {
pub fn to_state<I: ImageSize>(&self, sprite: &Sprite<I>) -> AnimationState {
use Animation::*;
use AnimationState as S;
match *self {
MoveTo(dur, dx, dy) => {
let (bx, by) = sprite.get_position();
S::Move(0.0, bx, by, dx - bx, dy - by, dur)
},
MoveBy(dur, cx, cy) => {
let (bx, by) = sprite.get_position();
S::Move(0.0, bx, by, cx, cy, dur)
},
RotateTo(dur, d) => {
let b = sprite.get_rotation();
S::Rotate(0.0, b, d - b, dur)
},
RotateBy(dur, c) => {
let b = sprite.get_rotation();
S::Rotate(0.0, b, c, dur)
},
ScaleTo(dur, dx, dy) => {
let (bx, by) = sprite.get_scale();
S::Scale(0.0, bx, by, dx - bx, dy - by, dur)
},
ScaleBy(dur, cx, cy) => {
let (bx, by) = sprite.get_scale();
S::Scale(0.0, bx, by, cx, cy, dur)
},
FlipX(flip_x) => {
let flip_y = sprite.get_flip_y();
S::Flip(flip_x, flip_y)
},
FlipY(flip_y) => {
let flip_x = sprite.get_flip_x();
S::Flip(flip_x, flip_y)
},
Show => {
S::Visibility(true)
},
Hide => {
S::Visibility(false)
},
ToggleVisibility => {
let visible = sprite.get_visible();
S::Visibility(!visible)
},
Blink(dur, times) => {
S::Blink(0.0, dur, 0, 2 * times)
},
FadeIn(dur) => {
let b = sprite.get_opacity() as f64;
S::Fade(0.0, b, 1.0 - b, dur)
},
FadeOut(dur) => {
let b = sprite.get_opacity() as f64;
S::Fade(0.0, b, 0.0 - b, dur)
},
FadeTo(dur, d) => {
let b = sprite.get_opacity() as f64;
S::Fade(0.0, b, d - b, dur)
},
Ease(f, ref animation) => {
S::Ease(f, Box::new(animation.to_state(sprite)))
},
}
}
}
#[derive(Clone)]
pub enum AnimationState {
Move(f64, Scalar, Scalar, Scalar, Scalar, f64),
Rotate(f64, Scalar, Scalar, f64),
Scale(f64, Scalar, Scalar, Scalar, Scalar, f64),
Flip(bool, bool),
Visibility(bool),
Blink(f64, f64, usize, usize),
Fade(f64, f64, f64, f64),
Ease(EaseFunction, Box<AnimationState>),
}
impl AnimationState {
pub fn update<I: ImageSize>(
&self,
sprite: &mut Sprite<I>,
dt: f64
) -> (Option<AnimationState>, Status, f64) {
use AnimationState::*;
match *self {
Move(t, bx, by, cx, cy, d) => {
let factor = (t + dt) / d;
update_position(sprite, factor, t + dt, bx, by, cx, cy, d)
},
Rotate(t, b, c, d) => {
let factor = (t + dt) / d;
update_rotation(sprite, factor, t + dt, b, c, d)
},
Scale(t, bx, by, cx, cy, d) => {
let factor = (t + dt) / d;
update_scale(sprite, factor, t + dt, bx, by, cx, cy, d)
},
Flip(flip_x, flip_y) => {
sprite.set_flip_x(flip_x);
sprite.set_flip_y(flip_y);
(None, Success, dt)
},
Visibility(visible) => {
sprite.set_visible(visible);
(None, Success, dt)
},
Blink(past, dur, cur, total) => {
let period = dur / total as f64;
if past + dt >= (cur + 1) as f64 * period {
let visible = sprite.get_visible();
sprite.set_visible(!visible);
if past + dt >= dur {
(None, Success, past + dt - dur)
} else {
(Some(Blink(past + dt, dur, cur + 1, total)),
Running, 0.0)
}
} else {
(Some(Blink(past + dt, dur, cur, total)),
Running, 0.0)
}
},
Fade(t, b, c, d) => {
let factor = (t + dt) / d;
update_opacity(sprite, factor, t + dt, b, c, d)
},
Ease(f, ref state) => {
let mut support_ease = true;
let (state, status, remain) = match **state {
Move(t, bx, by, cx, cy, d) => {
let factor = ::interpolation::Ease::calc((t + dt) / d, f);
update_position(sprite, factor, t + dt,
bx, by, cx, cy, d)
},
Rotate(t, b, c, d) => {
let factor = ::interpolation::Ease::calc((t + dt) / d, f);
update_rotation(sprite, factor, t + dt, b, c, d)
},
Scale(t, bx, by, cx, cy, d) => {
let factor = ::interpolation::Ease::calc((t + dt) / d, f);
update_scale(sprite, factor, t + dt, bx, by, cx, cy, d)
},
Fade(t, b, c, d) => {
let factor = ::interpolation::Ease::calc((t + dt) / d, f);
update_opacity(sprite, factor, t + dt, b, c, d)
},
_ => {
support_ease = false;
state.update(sprite, dt)
}
};
if !support_ease {
return (state, status, remain);
}
if let Some(state) = state {
(Some(AnimationState::Ease(f, Box::new(state))),
status, remain)
} else {
(None, status, remain)
}
},
}
}
}
fn update_position<I: ImageSize>(
sprite: &mut Sprite<I>,
factor: f64,
t: f64,
bx: f64,
by: f64,
cx: f64,
cy: f64,
d: f64
) -> (Option<AnimationState>, Status, f64) {
if t >= d {
sprite.set_position(bx + cx, by + cy);
(None, Success, t - d)
} else {
sprite.set_position(bx + cx * factor, by + cy * factor);
(Some(AnimationState::Move(t, bx, by, cx, cy, d)),
Running, 0.0)
}
}
fn update_rotation<I: ImageSize>(
sprite: &mut Sprite<I>,
factor: f64,
t: f64,
b: f64,
c: f64,
d: f64
) -> (Option<AnimationState>, Status, f64) {
if t >= d {
sprite.set_rotation(b + c);
(None, Success, t - d)
} else {
sprite.set_rotation(b + c * factor);
(Some(AnimationState::Rotate(t, b, c, d)),
Running, 0.0)
}
}
fn update_scale<I: ImageSize>(
sprite: &mut Sprite<I>,
factor: f64,
t: f64,
bx: f64,
by: f64,
cx: f64,
cy: f64,
d: f64
) -> (Option<AnimationState>, Status, f64) {
if t >= d {
sprite.set_scale(bx + cx, by + cy);
(None, Success, t - d)
} else {
sprite.set_scale(bx + cx * factor, by + cy * factor);
(Some(AnimationState::Scale(t, bx, by, cx, cy, d)),
Running, 0.0)
}
}
fn update_opacity<I: ImageSize>(
sprite: &mut Sprite<I>,
factor: f64,
t: f64,
b: f64,
c: f64,
d: f64
) -> (Option<AnimationState>, Status, f64) {
if t >= d {
sprite.set_opacity((b + c) as f32);
(None, Success, t - d)
} else {
sprite.set_opacity((b + c * factor) as f32);
(Some(AnimationState::Fade(t, b, c, d)),
Running, 0.0)
}
}