microbit_text/
scrolling.rs

1//! Support for scrolling sequences of 5×5 images horizontally.
2//!
3//! Each kind of scrolling sequence is represented by a type implementing
4//! [`Animate`] (for controlling the sequence) and [`Render`] (for displaying
5//! it).
6//!
7//! To create a new kind of scrolling sequence, make a new implementation of
8//! [`Scrollable`]. It can implement `Render::brightness_at` by delegating to
9//! `self.current_brightness_at()`. [`Animate`] will be implemented
10//! automatically.
11//!
12//! See [`scrolling_text`] for scrolling text strings.
13//!
14//! See [`scrolling_statics`] for scrolling arbitrary images.
15//!
16//! [`scrolling_text`]: crate::scrolling_text
17//! [`scrolling_statics`]: crate::scrolling_statics
18
19use tiny_led_matrix::Render;
20
21
22/// The state of an animation.
23pub trait Animate {
24
25    /// Say whether the animation has completed.
26    fn is_finished(&self) -> bool;
27
28    /// Reset the  animation to the beginning.
29    fn reset(&mut self);
30
31    /// Advance to the next step of the animation.
32    ///
33    /// If the animation has completed, does nothing.
34    fn tick(&mut self);
35
36}
37
38
39/// Data needed to record the state of a scrolling animation.
40///
41/// Implementations of [`Scrollable`] should contain one of these and make it
42/// available via `state()` and `state_mut()`.
43#[derive(Default)]
44#[derive(Copy, Clone, Debug)]
45pub struct ScrollingState {
46    // index of the character being scrolled on, or about to be scrolled on
47    index: usize,
48    // 0..5
49    pixel: usize,
50}
51
52impl ScrollingState {
53
54    /// Reset the state to the beginning.
55    pub fn reset(&mut self) {
56        self.index = 0;
57        self.pixel = 0;
58    }
59
60    /// Advance the state by one tick.
61    pub fn tick(&mut self) {
62        self.pixel += 1;
63        if self.pixel == 5 {
64            self.pixel = 0;
65            self.index += 1;
66        }
67    }
68
69}
70
71
72/// A horizontally scrolling sequence of 5×5 images.
73///
74/// `Scrollable`s automatically implement [`Animate`].
75///
76/// When a `Scrollable` also implements `Render`, the rendered image is the
77/// current state of the animation.
78pub trait Scrollable {
79
80    /// The type of the underlying 5×5 images.
81    type Subimage: Render;
82
83    /// The number of underlying images.
84    fn length(&self) -> usize;
85
86    /// A [`ScrollingState`] indicating the current point in the animation.
87    fn state(&self) -> &ScrollingState;
88
89    /// A [`ScrollingState`] indicating the current point in the animation, as
90    /// a mutable reference.
91    fn state_mut(&mut self) -> &mut ScrollingState;
92
93    /// A reference to the underlying image at the specified index.
94    fn subimage(&self, index: usize) -> &Self::Subimage;
95
96    /// Returns the brightness value for a single LED in the current state.
97    ///
98    /// Use this to implement `Render`.
99    fn current_brightness_at(&self, x: usize, y: usize) -> u8 {
100        if self.state().index > self.length() {return 0}
101        let state = self.state();
102        let (index, x) = if x + state.pixel < 5 {
103            if state.index == 0 {return 0}
104            (state.index - 1, x + state.pixel)
105        } else {
106            if state.index == self.length() {return 0}
107            (state.index, x + state.pixel - 5)
108        };
109        self.subimage(index).brightness_at(x, y)
110    }
111}
112
113
114impl<T : Scrollable> Animate for T {
115
116    fn is_finished(&self) -> bool {
117        self.state().index > self.length()
118    }
119
120    fn reset(&mut self) {
121        self.state_mut().reset();
122    }
123
124    fn tick(&mut self) {
125        if !self.is_finished() {
126            self.state_mut().tick();
127        }
128    }
129}
130