sprite/
animation.rs

1use graphics::ImageSize;
2use graphics::math::Scalar;
3
4use ai_behavior::{
5    Status,
6    Success,
7    Running,
8};
9
10use interpolation::EaseFunction;
11use sprite::Sprite;
12
13/// Animations supported by Sprite
14#[derive(Clone, PartialEq)]
15pub enum Animation {
16    /// duration, x, y
17    ///
18    /// Move sprite to specified position
19    MoveTo(f64, Scalar, Scalar),
20    /// duration, x, y
21    ///
22    /// Move sprite to specified position, relatively
23    MoveBy(f64, Scalar, Scalar),
24    /// duration, deg
25    ///
26    /// Rotate sprite to specified degree
27    RotateTo(f64, Scalar),
28    /// duration, deg
29    ///
30    /// Rotate sprite to specified degree, relatively
31    RotateBy(f64, Scalar),
32    /// duration, sx, sy
33    ///
34    /// Scale sprite to specified scale
35    ScaleTo(f64, Scalar, Scalar),
36    /// duration, sx, sy
37    ///
38    /// Scale sprite to specified scale, relatively
39    ScaleBy(f64, Scalar, Scalar),
40    /// Flip sprite in x direction
41    FlipX(bool),
42    /// Flip sprite in y direction
43    FlipY(bool),
44    /// Set the sprite's visibility to true
45    Show,
46    /// Set the sprite's visibility to false
47    Hide,
48    /// Toggle the sprite's visibility
49    ToggleVisibility,
50    /// duration, times
51    Blink(f64, usize),
52    /// duration
53    ///
54    /// Fade in the sprite, set its opacity from 0 to 1 in `dt` seconds
55    FadeIn(f64),
56    /// duration
57    ///
58    /// Fade out the sprite, set its opacity from 1 to 0 in `dt` seconds
59    FadeOut(f64),
60    /// duration, opacity
61    ///
62    /// Set the sprite's opacity to specified value in `dt` seconds
63    FadeTo(f64, f64),
64    /// ease_function, animation
65    ///
66    /// Tweening the animation with ease function
67    Ease(EaseFunction, Box<Animation>),
68}
69
70impl Animation {
71    /// Generate a new state from Animation with specified Sprite
72    pub fn to_state<I: ImageSize>(&self, sprite: &Sprite<I>) -> AnimationState {
73        use Animation::*;
74        use AnimationState as S;
75
76        match *self {
77            MoveTo(dur, dx, dy) => {
78                let (bx, by) = sprite.get_position();
79                S::Move(0.0, bx, by, dx - bx, dy - by, dur)
80            },
81            MoveBy(dur, cx, cy) => {
82                let (bx, by) = sprite.get_position();
83                S::Move(0.0, bx, by, cx, cy, dur)
84            },
85            RotateTo(dur, d) => {
86                let b = sprite.get_rotation();
87                S::Rotate(0.0, b, d - b, dur)
88            },
89            RotateBy(dur, c) => {
90                let b = sprite.get_rotation();
91                S::Rotate(0.0, b, c, dur)
92            },
93            ScaleTo(dur, dx, dy) => {
94                let (bx, by) = sprite.get_scale();
95                S::Scale(0.0, bx, by, dx - bx, dy - by, dur)
96            },
97            ScaleBy(dur, cx, cy) => {
98                let (bx, by) = sprite.get_scale();
99                S::Scale(0.0, bx, by, cx, cy, dur)
100            },
101            FlipX(flip_x) => {
102                let flip_y = sprite.get_flip_y();
103                S::Flip(flip_x, flip_y)
104            },
105            FlipY(flip_y) => {
106                let flip_x = sprite.get_flip_x();
107                S::Flip(flip_x, flip_y)
108            },
109            Show => {
110                S::Visibility(true)
111            },
112            Hide => {
113                S::Visibility(false)
114            },
115            ToggleVisibility => {
116                let visible = sprite.get_visible();
117                S::Visibility(!visible)
118            },
119            Blink(dur, times) => {
120                S::Blink(0.0, dur, 0, 2 * times)
121            },
122            FadeIn(dur) => {
123                let b = sprite.get_opacity() as f64;
124                S::Fade(0.0, b, 1.0 - b, dur)
125            },
126            FadeOut(dur) => {
127                let b = sprite.get_opacity() as f64;
128                S::Fade(0.0, b, 0.0 - b, dur)
129            },
130            FadeTo(dur, d) => {
131                let b = sprite.get_opacity() as f64;
132                S::Fade(0.0, b, d - b, dur)
133            },
134            Ease(f, ref animation) => {
135                S::Ease(f, Box::new(animation.to_state(sprite)))
136            },
137        }
138    }
139}
140
141/// The state of animation
142#[derive(Clone)]
143pub enum AnimationState {
144    /// time, begin_x, begin_y, change_x, change_y, duration
145    Move(f64, Scalar, Scalar, Scalar, Scalar, f64),
146    /// time, begin, change, duration
147    Rotate(f64, Scalar, Scalar, f64),
148    /// time, begin_x, begin_y, change_x, change_y, duration
149    Scale(f64, Scalar, Scalar, Scalar, Scalar, f64),
150    /// flip_x, flip_y
151    Flip(bool, bool),
152    /// visible
153    Visibility(bool),
154    /// past_time, duration, blinked_times, total_times
155    Blink(f64, f64, usize, usize),
156    /// time, begin, change, duration
157    Fade(f64, f64, f64, f64),
158    /// ease_function, animation
159    Ease(EaseFunction, Box<AnimationState>),
160}
161
162impl AnimationState {
163    /// Update the state and change the sprite's properties
164    pub fn update<I: ImageSize>(
165        &self,
166        sprite: &mut Sprite<I>,
167        dt: f64
168    ) -> (Option<AnimationState>, Status, f64) {
169        use AnimationState::*;
170
171        match *self {
172            Move(t, bx, by, cx, cy, d) => {
173                let factor = (t + dt) / d;
174                update_position(sprite, factor, t + dt, bx, by, cx, cy, d)
175            },
176            Rotate(t, b, c, d) => {
177                let factor = (t + dt) / d;
178                update_rotation(sprite, factor, t + dt, b, c, d)
179            },
180            Scale(t, bx, by, cx, cy, d) => {
181                let factor = (t + dt) / d;
182                update_scale(sprite, factor, t + dt, bx, by, cx, cy, d)
183            },
184            Flip(flip_x, flip_y) => {
185                sprite.set_flip_x(flip_x);
186                sprite.set_flip_y(flip_y);
187                (None, Success, dt)
188            },
189            Visibility(visible) => {
190                sprite.set_visible(visible);
191                (None, Success, dt)
192            },
193            Blink(past, dur, cur, total) => {
194                let period = dur / total as f64;
195                if past + dt >= (cur + 1) as f64 * period {
196                    let visible = sprite.get_visible();
197                    sprite.set_visible(!visible);
198                    if past + dt >= dur {
199                        (None, Success, past + dt - dur)
200                    } else {
201                        (Some(Blink(past + dt, dur, cur + 1, total)),
202                         Running, 0.0)
203                    }
204                } else {
205                    (Some(Blink(past + dt, dur, cur, total)),
206                     Running, 0.0)
207                }
208            },
209            Fade(t, b, c, d) => {
210                let factor = (t + dt) / d;
211                update_opacity(sprite, factor, t + dt, b, c, d)
212            },
213            Ease(f, ref state) => {
214                let mut support_ease = true;
215                let (state, status, remain) = match **state {
216                    Move(t, bx, by, cx, cy, d) => {
217                        let factor = ::interpolation::Ease::calc((t + dt) / d, f);
218                        update_position(sprite, factor, t + dt,
219                                        bx, by, cx, cy, d)
220                    },
221                    Rotate(t, b, c, d) => {
222                        let factor = ::interpolation::Ease::calc((t + dt) / d, f);
223                        update_rotation(sprite, factor, t + dt, b, c, d)
224                    },
225                    Scale(t, bx, by, cx, cy, d) => {
226                        let factor = ::interpolation::Ease::calc((t + dt) / d, f);
227                        update_scale(sprite, factor, t + dt, bx, by, cx, cy, d)
228                    },
229                    Fade(t, b, c, d) => {
230                        let factor = ::interpolation::Ease::calc((t + dt) / d, f);
231                        update_opacity(sprite, factor, t + dt, b, c, d)
232                    },
233                    _ => {
234                        support_ease = false;
235                        state.update(sprite, dt)
236                    }
237                };
238
239                if !support_ease {
240                    return (state, status, remain);
241                }
242
243                if let Some(state) = state {
244                    (Some(AnimationState::Ease(f, Box::new(state))),
245                     status, remain)
246                } else {
247                    (None, status, remain)
248                }
249            },
250        }
251    }
252}
253
254fn update_position<I: ImageSize>(
255    sprite: &mut Sprite<I>,
256    factor: f64,
257    t: f64,
258    bx: f64,
259    by: f64,
260    cx: f64,
261    cy: f64,
262    d: f64
263) -> (Option<AnimationState>, Status, f64) {
264    if t >= d {
265        sprite.set_position(bx + cx, by + cy);
266        (None, Success, t - d)
267    } else {
268        sprite.set_position(bx + cx * factor, by + cy * factor);
269        (Some(AnimationState::Move(t, bx, by, cx, cy, d)),
270         Running, 0.0)
271    }
272}
273
274fn update_rotation<I: ImageSize>(
275    sprite: &mut Sprite<I>,
276    factor: f64,
277    t: f64,
278    b: f64,
279    c: f64,
280    d: f64
281) -> (Option<AnimationState>, Status, f64) {
282    if t >= d {
283        sprite.set_rotation(b + c);
284        (None, Success, t - d)
285    } else {
286        sprite.set_rotation(b + c * factor);
287        (Some(AnimationState::Rotate(t, b, c, d)),
288         Running, 0.0)
289    }
290}
291
292fn update_scale<I: ImageSize>(
293    sprite: &mut Sprite<I>,
294    factor: f64,
295    t: f64,
296    bx: f64,
297    by: f64,
298    cx: f64,
299    cy: f64,
300    d: f64
301) -> (Option<AnimationState>, Status, f64) {
302    if t >= d {
303        sprite.set_scale(bx + cx, by + cy);
304        (None, Success, t - d)
305    } else {
306        sprite.set_scale(bx + cx * factor, by + cy * factor);
307        (Some(AnimationState::Scale(t, bx, by, cx, cy, d)),
308         Running, 0.0)
309    }
310}
311
312fn update_opacity<I: ImageSize>(
313    sprite: &mut Sprite<I>,
314    factor: f64,
315    t: f64,
316    b: f64,
317    c: f64,
318    d: f64
319) -> (Option<AnimationState>, Status, f64) {
320    if t >= d {
321        sprite.set_opacity((b + c) as f32);
322        (None, Success, t - d)
323    } else {
324        sprite.set_opacity((b + c * factor) as f32);
325        (Some(AnimationState::Fade(t, b, c, d)),
326         Running, 0.0)
327    }
328}