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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//! Support for scrolling sequences of 5×5 images horizontally.
//!
//! Each kind of scrolling sequence is represented by a type implementing
//! [`Animate`] (for controlling the sequence) and [`Render`] (for displaying
//! it).
//!
//! To create a new kind of scrolling sequence, make a new implementation of
//! [`Scrollable`].
//!
//! The [`ScrollingImages`] implementation can be used for static slices of
//! any type implementing [`Render`].
//!
//! See [`scrolling_text`] for scrolling text strings.
//!
//! # Example
//!
//! ```ignore
//! use rmicrobit::prelude::*;
//! use rmicrobit::display::{MicrobitDisplay, MicrobitFrame};
//! use rmicrobit::graphics::scrolling::ScrollingImages;
//! const BLANK: BitImage = BitImage::blank();
//! const HEART: BitImage = BitImage::new(&[
//!     [0, 1, 0, 1, 0],
//!     [1, 0, 1, 0, 1],
//!     [1, 0, 0, 0, 1],
//!     [0, 1, 0, 1, 0],
//!     [0, 0, 1, 0, 0],
//! ]);
//! let mut display = MicrobitDisplay::new(...);
//! let mut scroller = ScrollingImages::default();
//! let frame = MicrobitFrame::default();
//! scroller.set_images(&[&HEART, &BLANK, &HEART]);
//! while !scroller.is_finished() {
//!     // every 50ms or so
//!     scroller.tick();
//!     frame.set(scroller);
//!     display.set_frame(frame);
//! }
//! ```
//!
//! See examples/scroll_images.rs for a complete example.
//!
//! [`Render`]: crate::display::Render
//! [`scrolling_text`]: crate::graphics::scrolling_text

use tiny_led_matrix::Render;


/// The state of an animation.
pub trait Animate {

    /// Say whether the animation has completed.
    fn is_finished(&self) -> bool;

    /// Reset the  animation to the beginning.
    fn reset(&mut self);

    /// Advance to the next step of the animation.
    ///
    /// If the animation has completed, does nothing.
    fn tick(&mut self);

}


/// Data needed to record the state of a scrolling animation.
///
/// Implementations of [`Scrollable`] should contain one of these and make it
/// available via `state()` and `state_mut()`.
#[derive(Default)]
#[derive(Copy, Clone, Debug)]
pub struct ScrollingState {
    // index of the character being scrolled on, or about to be scrolled on
    index: usize,
    // 0..5
    pixel: usize,
}

impl ScrollingState {

    /// Reset the state to the beginning.
    pub fn reset(&mut self) {
        self.index = 0;
        self.pixel = 0;
    }

    /// Advance the state by one tick.
    pub fn tick(&mut self) {
        self.pixel += 1;
        if self.pixel == 5 {
            self.pixel = 0;
            self.index += 1;
        }
    }

}


/// A horizontally scrolling sequence of 5×5 images.
///
/// `Scrollable`s automatically implement [`Animate`].
///
/// When a `Scrollable` also implements `Render`, the rendered image is the
/// current state of the animation.
pub trait Scrollable {

    /// The type of the underlying 5×5 images.
    type Subimage: Render;

    /// The number of underlying images.
    fn length(&self) -> usize;

    /// A [`ScrollingState`] indicating the current point in the animation.
    fn state(&self) -> &ScrollingState;

    /// A [`ScrollingState`] indicating the current point in the animation, as
    /// a mutable reference.
    fn state_mut(&mut self) -> &mut ScrollingState;

    /// A reference to the underlying image at the specified index.
    fn subimage(&self, index: usize) -> &Self::Subimage;

    /// Returns the brightness value for a single LED in the current state.
    ///
    /// Use this to implement `Render`.
    fn current_brightness_at(&self, x: usize, y: usize) -> u8 {
        if self.state().index > self.length() {return 0}
        let state = self.state();
        let (index, x) = if x + state.pixel < 5 {
            if state.index == 0 {return 0}
            (state.index - 1, x + state.pixel)
        } else {
            if state.index == self.length() {return 0}
            (state.index, x + state.pixel - 5)
        };
        self.subimage(index).brightness_at(x, y)
    }
}


impl<T : Scrollable> Animate for T {

    fn is_finished(&self) -> bool {
        self.state().index > self.length()
    }

    fn reset(&mut self) {
        self.state_mut().reset();
    }

    fn tick(&mut self) {
        if !self.is_finished() {
            self.state_mut().tick();
        }
    }
}


/// A [`Scrollable`] displaying a static slice of arbitrary images.
///
/// The underlying images can be any sized type that implements [`Render`].
#[derive(Copy, Clone)]
pub struct ScrollingImages<T: Render + 'static> {
    images: &'static [T],
    state: ScrollingState,
}

impl<T: Render + 'static> ScrollingImages<T> {

    /// Specifies the images to be displayed.
    ///
    /// This also resets the animation to the beginning.
    pub fn set_images(&mut self, images: &'static [T]) {
        self.images = images;
        self.reset();
    }


}

impl<T: Render + 'static> Default for ScrollingImages<T> {

    fn default() -> ScrollingImages<T> {
        ScrollingImages {
            images: &[],
            state: Default::default(),
        }
    }

}

impl<T: Render + 'static> Scrollable for ScrollingImages<T> {

    type Subimage = T;

    fn length(&self) -> usize {
        self.images.len()
    }

    fn state(&self) -> &ScrollingState {
        &self.state
    }

    fn state_mut(&mut self) -> &mut ScrollingState {
        &mut self.state
    }

    fn subimage(&self, index: usize) -> &T {
        &self.images[index]
    }

}

impl<T: Render + 'static> Render for ScrollingImages<T> {

    fn brightness_at(&self, x: usize, y: usize) -> u8 {
        self.current_brightness_at(x, y)
    }

}