1use std::time::Instant;
7
8#[derive(Debug)]
12pub struct Time {
13 elapsed: f64,
15 #[allow(dead_code)]
17 start: Instant,
18 last_frame: Instant,
20 delta: f64,
22 smoothed_delta: f64,
24 fixed_timestep: f64,
26 fixed_accumulator: f64,
28 use_fixed_timestep: bool,
30 frame_count: u64,
32 time_scale: f64,
34 fps_tracker: FpsTracker,
36}
37
38#[derive(Debug)]
39struct FpsTracker {
40 samples: [f64; 60],
41 index: usize,
42 filled: bool,
43}
44
45impl Default for FpsTracker {
46 fn default() -> Self {
47 Self {
48 samples: [0.0; 60],
49 index: 0,
50 filled: false,
51 }
52 }
53}
54
55impl FpsTracker {
56 fn record(&mut self, delta: f64) {
57 self.samples[self.index] = delta;
58 self.index = (self.index + 1) % self.samples.len();
59 if self.index == 0 {
60 self.filled = true;
61 }
62 }
63
64 fn average_fps(&self) -> f64 {
65 let count = if self.filled {
66 self.samples.len()
67 } else {
68 self.index
69 };
70 if count == 0 {
71 return 0.0;
72 }
73 let sum: f64 = self.samples[..count].iter().sum();
74 if sum < 1e-9 {
75 return f64::INFINITY;
76 }
77 count as f64 / sum
78 }
79}
80
81impl Time {
82 pub(crate) fn new() -> Self {
84 let now = Instant::now();
85 Self {
86 elapsed: 0.0,
87 start: now,
88 last_frame: now,
89 delta: 0.0,
90 smoothed_delta: 1.0 / 60.0,
91 fixed_timestep: 1.0 / 60.0,
92 fixed_accumulator: 0.0,
93 use_fixed_timestep: false,
94 frame_count: 0,
95 time_scale: 1.0,
96 fps_tracker: FpsTracker::default(),
97 }
98 }
99
100 pub(crate) fn tick(&mut self) {
102 let now = Instant::now();
103 let raw_delta = now.duration_since(self.last_frame).as_secs_f64();
104 self.last_frame = now;
105
106 let clamped = raw_delta.min(0.25);
108 self.delta = clamped * self.time_scale;
109 self.smoothed_delta = self.smoothed_delta * 0.9 + self.delta * 0.1;
110 self.elapsed += self.delta;
111 self.frame_count += 1;
112 self.fps_tracker.record(self.delta);
113 }
114
115 pub(crate) fn fixed_step_count(&mut self) -> u32 {
118 if !self.use_fixed_timestep {
119 return 0;
120 }
121 self.fixed_accumulator += self.delta;
122 let max_steps = 5; let mut steps = 0;
124 while self.fixed_accumulator >= self.fixed_timestep && steps < max_steps {
125 self.fixed_accumulator -= self.fixed_timestep;
126 steps += 1;
127 }
128 if steps >= max_steps {
129 self.fixed_accumulator = 0.0;
130 }
131 steps
132 }
133
134 #[inline]
138 pub fn elapsed(&self) -> f64 {
139 self.elapsed
140 }
141
142 #[inline]
144 pub fn delta(&self) -> f64 {
145 self.delta
146 }
147
148 #[inline]
150 pub fn smoothed_delta(&self) -> f64 {
151 self.smoothed_delta
152 }
153
154 #[inline]
156 pub fn fps(&self) -> f64 {
157 self.fps_tracker.average_fps()
158 }
159
160 #[inline]
162 pub fn frame_count(&self) -> u64 {
163 self.frame_count
164 }
165
166 #[inline]
168 pub fn time_scale(&self) -> f64 {
169 self.time_scale
170 }
171
172 pub fn set_time_scale(&mut self, scale: f64) {
174 self.time_scale = scale.clamp(0.0, 10.0);
175 }
176
177 pub fn set_fixed_timestep(&mut self, fps: u32) {
179 self.use_fixed_timestep = true;
180 self.fixed_timestep = 1.0 / fps as f64;
181 self.fixed_accumulator = 0.0;
182 }
183
184 pub fn disable_fixed_timestep(&mut self) {
186 self.use_fixed_timestep = false;
187 self.fixed_accumulator = 0.0;
188 }
189
190 #[inline]
192 pub fn is_fixed_timestep_enabled(&self) -> bool {
193 self.use_fixed_timestep
194 }
195
196 #[inline]
198 pub fn fixed_delta(&self) -> f64 {
199 self.fixed_timestep
200 }
201}
202
203#[derive(Debug, Clone)]
218pub struct Timer {
219 duration: f64,
220 elapsed: f64,
221 repeating: bool,
222 finished: bool,
223}
224
225impl Timer {
226 pub fn from_seconds(seconds: f64, repeating: bool) -> Self {
228 Self {
229 duration: seconds,
230 elapsed: 0.0,
231 repeating,
232 finished: false,
233 }
234 }
235
236 pub fn tick(&mut self, delta: f64) -> bool {
238 if self.finished && !self.repeating {
239 return false;
240 }
241 self.elapsed += delta;
242 if self.elapsed >= self.duration {
243 if self.repeating {
244 self.elapsed -= self.duration;
245 } else {
246 self.finished = true;
247 self.elapsed = self.duration;
248 }
249 true
250 } else {
251 false
252 }
253 }
254
255 pub fn reset(&mut self) {
257 self.elapsed = 0.0;
258 self.finished = false;
259 }
260
261 pub fn set_duration(&mut self, seconds: f64) {
263 self.duration = seconds;
264 self.reset();
265 }
266
267 pub fn fraction(&self) -> f32 {
269 (self.elapsed / self.duration).clamp(0.0, 1.0) as f32
270 }
271
272 #[inline]
274 pub fn is_finished(&self) -> bool {
275 self.finished
276 }
277
278 #[inline]
280 pub fn is_running(&self) -> bool {
281 !self.finished
282 }
283
284 pub fn remaining(&self) -> f64 {
286 (self.duration - self.elapsed).max(0.0)
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn test_timer_basic() {
296 let mut t = Timer::from_seconds(1.0, false);
297 assert!(!t.tick(0.5));
298 assert!(!t.tick(0.4));
299 assert!(t.tick(0.2)); assert!(t.is_finished());
301 assert!(!t.tick(0.5)); }
303
304 #[test]
305 fn test_timer_repeating() {
306 let mut t = Timer::from_seconds(1.0, true);
307 assert!(!t.tick(0.5));
308 assert!(t.tick(0.5));
309 assert!(!t.tick(0.3));
310 assert!(t.tick(0.7));
311 }
312}