use core::mem::MaybeUninit; pub trait Animation { type Frame; fn next_frame(&mut self) -> Option<(Self::Frame, usize)>; } pub struct Animator where A: Animation, { clip: Option>, } impl Animator where A: Animation, { pub const fn new() -> Self { Self { clip: None } } } impl Animator where A: Animation, { pub fn start(&mut self, animation: A) { self.clip.replace(Clip::new(animation)); } pub fn next_frame(&mut self) -> Option { if let Some(current_animation) = self.clip.as_mut() { let frame = current_animation.next(); if frame.is_none() { self.clip.take(); } return frame; } None } } pub struct Clip where A: Animation, { animation: A, buffered: MaybeUninit, frames_left: usize, } impl Clip where A: Animation, { pub fn new(animation: A) -> Self { Self { animation, buffered: MaybeUninit::uninit(), frames_left: 0, } } } impl Iterator for Clip where A: Animation, { type Item = A::Frame; fn next(&mut self) -> Option { while self.frames_left == 0 { let (frame, duration) = self.animation.next_frame()?; self.buffered.write(frame); self.frames_left = duration; } self.frames_left -= 1; // SAFETY: Since `frames_left` is initialized to 0 in `new` (which is the only way // to obtain a `Clip`), we know that the loop above must have been executed at least // once, so `self.buffered` must have been initialized. let frame = unsafe { self.buffered.assume_init_read() }; Some(frame) } }