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