swamp_render/
anim.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
 * Licensed under the MIT License. See LICENSE in the project root for license information.
 */
use monotonic_time_rs::{Millis, MillisDuration};
use std::ops::{Div, Sub};

pub trait AnimationLookup {
    fn frame(&self) -> u16;
}

#[derive(Debug, Copy, Clone)]
pub struct Tick(u64);

impl Tick {
    #[inline]
    pub const fn inner(&self) -> u64 {
        self.0
    }
}

impl Sub for Tick {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self::Output {
        Self(self.0 - rhs.0)
    }
}

pub type NumberOfTicks = u64;

impl Div<NumberOfTicks> for Tick {
    type Output = u64;

    fn div(self, rhs: NumberOfTicks) -> Self::Output {
        self.0 / rhs
    }
}

pub type Fps = u16;

#[derive(Debug, Copy, Clone)]
pub struct FrameAnimationConfig {
    start_frame: u16,
    count: u8,
    frame_duration: MillisDuration,
}

impl FrameAnimationConfig {
    pub fn new(start_frame: u16, count: u8, fps: Fps) -> Self {
        Self {
            start_frame,
            count,
            frame_duration: MillisDuration::from_millis(1000) / (fps as u32),
        }
    }
}

#[derive(Debug)]
pub enum PlayMode {
    Once,
    Repeat,
}

#[derive(Debug)]
pub struct FrameAnimation {
    started_at_time: Millis,
    is_playing: bool,
    config: FrameAnimationConfig,
    relative_frame: u8,
    play_mode: PlayMode,
}

impl FrameAnimation {
    pub fn new(config: FrameAnimationConfig) -> Self {
        Self {
            started_at_time: Millis::new(0),
            is_playing: false,
            config,
            relative_frame: 0,
            play_mode: PlayMode::Once,
        }
    }
    pub fn update(&mut self, now: Millis) {
        if !self.is_playing {
            return;
        }

        assert!(now >= self.started_at_time);

        let elapsed_ticks = now - self.started_at_time;
        let frames_since_start = elapsed_ticks.as_millis() / self.config.frame_duration.as_millis();

        match self.play_mode {
            PlayMode::Once => {
                if frames_since_start >= self.config.count as u64 {
                    self.is_playing = false;
                    self.relative_frame = (self.config.count as u16 - 1) as u8;
                } else {
                    self.relative_frame = frames_since_start as u8;
                }
            }
            PlayMode::Repeat => {
                self.relative_frame = (frames_since_start % self.config.count as u64) as u8;
            }
        }
    }

    pub fn is_done(&self) -> bool {
        !self.is_playing
    }

    pub fn is_playing(&self) -> bool {
        self.is_playing
    }

    pub fn absolute_frame(&self) -> u16 {
        self.relative_frame as u16 + self.config.start_frame
    }

    pub fn relative_frame(&self) -> u16 {
        self.relative_frame as u16
    }

    pub fn play(&mut self, now: Millis) {
        self.is_playing = true;
        self.play_mode = PlayMode::Once;
        self.started_at_time = now;
    }

    pub fn play_repeat(&mut self, now: Millis) {
        self.is_playing = true;
        self.play_mode = PlayMode::Repeat;
        self.started_at_time = now;
    }
}

impl AnimationLookup for FrameAnimation {
    fn frame(&self) -> u16 {
        self.absolute_frame()
    }
}