Skip to content

Commit 91d6f7d

Browse files
Merge pull request #83 from FrameworkComputer/ledmatrix-startup-animations
LED Matrix startup animations
2 parents c93254f + 1fac070 commit 91d6f7d

File tree

14 files changed

+730
-176
lines changed

14 files changed

+730
-176
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
use crate::control::*;
2+
use crate::games::game_of_life::*;
3+
use crate::games::pong_animation::*;
4+
use crate::games::snake_animation::*;
5+
use crate::matrix::Grid;
6+
use crate::matrix::*;
7+
use crate::patterns::*;
8+
9+
// TODO
10+
// - [ ] Is there a cancellable Iterator? I think Java/Kotlin has one
11+
// - [ ] Each one has a number of frames
12+
// - [ ] Each one might have a different frame-rate
13+
14+
#[allow(clippy::large_enum_variant)]
15+
pub enum Animation {
16+
ZigZag(ZigZagIterator),
17+
Gof(GameOfLifeIterator),
18+
Percentage(StartupPercentageIterator),
19+
Breathing(BreathingIterator),
20+
Snake(SnakeIterator),
21+
Pong(PongIterator),
22+
}
23+
impl Iterator for Animation {
24+
type Item = Grid;
25+
26+
fn next(&mut self) -> Option<Self::Item> {
27+
match self {
28+
Animation::ZigZag(x) => x.next(),
29+
Animation::Gof(x) => x.next(),
30+
Animation::Percentage(x) => x.next(),
31+
Animation::Breathing(x) => x.next(),
32+
Animation::Snake(x) => x.next(),
33+
Animation::Pong(x) => x.next(),
34+
}
35+
}
36+
}
37+
38+
pub struct ZigZagIterator {
39+
frames: usize,
40+
current_frame: usize,
41+
}
42+
43+
impl ZigZagIterator {
44+
pub fn new(frames: usize) -> Self {
45+
Self {
46+
frames,
47+
current_frame: 0,
48+
}
49+
}
50+
}
51+
52+
impl Default for ZigZagIterator {
53+
fn default() -> Self {
54+
Self::new(34)
55+
}
56+
}
57+
58+
impl Iterator for ZigZagIterator {
59+
type Item = Grid;
60+
61+
fn next(&mut self) -> Option<Self::Item> {
62+
if self.current_frame < self.frames {
63+
let mut next = zigzag();
64+
next.rotate(self.current_frame);
65+
self.current_frame += 1;
66+
Some(next)
67+
} else {
68+
None
69+
}
70+
}
71+
}
72+
73+
pub struct StartupPercentageIterator {
74+
frames: usize,
75+
current_frame: usize,
76+
}
77+
78+
impl Default for StartupPercentageIterator {
79+
fn default() -> Self {
80+
Self {
81+
frames: 34,
82+
current_frame: 0,
83+
}
84+
}
85+
}
86+
87+
impl Iterator for StartupPercentageIterator {
88+
type Item = Grid;
89+
90+
fn next(&mut self) -> Option<Self::Item> {
91+
if self.current_frame < self.frames {
92+
self.current_frame += 1;
93+
Some(rows(self.current_frame))
94+
} else {
95+
None
96+
}
97+
}
98+
}
99+
100+
pub struct GameOfLifeIterator {
101+
state: GameOfLifeState,
102+
frames_remaining: usize,
103+
}
104+
105+
impl GameOfLifeIterator {
106+
pub fn new(start_param: GameOfLifeStartParam, frames: usize) -> Self {
107+
Self {
108+
// Could start with a custom grid
109+
state: GameOfLifeState::new(start_param, &Grid::default()),
110+
frames_remaining: frames,
111+
}
112+
}
113+
}
114+
115+
impl Iterator for GameOfLifeIterator {
116+
type Item = Grid;
117+
118+
fn next(&mut self) -> Option<Self::Item> {
119+
if self.frames_remaining > 0 {
120+
self.frames_remaining -= 1;
121+
// Only update every 8th frame, otherwise the animation is too fast
122+
if self.frames_remaining % 8 == 0 {
123+
self.state.tick();
124+
}
125+
Some(self.state.draw_matrix())
126+
} else {
127+
None
128+
}
129+
}
130+
}
131+
132+
pub struct BreathingIterator {
133+
frames_remaining: usize,
134+
current_brightness: u8,
135+
}
136+
137+
impl BreathingIterator {
138+
pub fn new(frames: usize) -> Self {
139+
Self {
140+
frames_remaining: frames,
141+
current_brightness: 0,
142+
}
143+
}
144+
}
145+
impl Default for BreathingIterator {
146+
fn default() -> Self {
147+
Self::new(64)
148+
}
149+
}
150+
151+
impl Iterator for BreathingIterator {
152+
type Item = Grid;
153+
154+
fn next(&mut self) -> Option<Self::Item> {
155+
if self.frames_remaining > 0 {
156+
let mut grid = Grid::default();
157+
let breath_step = 4;
158+
// TODO: Make it cycle up and down
159+
self.current_brightness = (self.current_brightness + breath_step) % 255;
160+
for y in 0..HEIGHT {
161+
for x in 0..WIDTH {
162+
grid.0[x][y] = self.current_brightness;
163+
}
164+
}
165+
self.frames_remaining -= 1;
166+
Some(grid)
167+
} else {
168+
None
169+
}
170+
}
171+
}

fl16-inputmodules/src/control.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub enum GameOfLifeStartParam {
119119
Toad = 0x03,
120120
Beacon = 0x04,
121121
Glider = 0x05,
122+
BeaconToadBlinker = 0x06,
122123
}
123124

124125
#[derive(Copy, Clone, num_derive::FromPrimitive)]

fl16-inputmodules/src/games/game_of_life.rs

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::control::{GameControlArg, GameOfLifeStartParam};
22
use crate::matrix::{GameState, Grid, LedmatrixState, HEIGHT, WIDTH};
33

4-
#[derive(Clone, Copy, num_derive::FromPrimitive)]
4+
#[derive(Clone, Copy, num_derive::FromPrimitive, PartialEq, Eq)]
55
pub enum Cell {
66
Dead = 0,
77
Alive = 1,
@@ -12,6 +12,20 @@ pub struct GameOfLifeState {
1212
cells: [[Cell; WIDTH]; HEIGHT],
1313
}
1414

15+
impl GameOfLifeState {
16+
pub fn combine(&self, other: &Self) -> Self {
17+
let mut state = self.clone();
18+
for x in 0..WIDTH {
19+
for y in 0..HEIGHT {
20+
if other.cells[y][x] == Cell::Alive {
21+
state.cells[y][x] = Cell::Alive;
22+
}
23+
}
24+
}
25+
state
26+
}
27+
}
28+
1529
pub fn start_game(state: &mut LedmatrixState, _random: u8, param: GameOfLifeStartParam) {
1630
let gol = GameOfLifeState::new(param, &state.grid);
1731
state.grid = gol.draw_matrix();
@@ -34,6 +48,7 @@ pub fn game_step(state: &mut LedmatrixState, _random: u8) {
3448
}
3549

3650
impl GameOfLifeState {
51+
// TODO: Integrate Grid into GameOfLifeStartParam because it's only used in one of the enum variants
3752
pub fn new(param: GameOfLifeStartParam, grid: &Grid) -> Self {
3853
match param {
3954
GameOfLifeStartParam::Beacon => Self::beacon(),
@@ -57,6 +72,9 @@ impl GameOfLifeState {
5772
GameOfLifeStartParam::Blinker => Self::blinker(),
5873
GameOfLifeStartParam::Toad => Self::toad(),
5974
GameOfLifeStartParam::Glider => Self::glider(),
75+
GameOfLifeStartParam::BeaconToadBlinker => Self::beacon()
76+
.combine(&Self::toad())
77+
.combine(&Self::blinker()),
6078
}
6179
}
6280
fn pattern1() -> Self {
@@ -81,12 +99,12 @@ impl GameOfLifeState {
8199
// X
82100
// X
83101
let mut cells = [[Cell::Dead; WIDTH]; HEIGHT];
84-
cells[10][5] = Cell::Alive;
85-
cells[10][6] = Cell::Alive;
86-
cells[10][7] = Cell::Alive;
87-
cells[14][5] = Cell::Alive;
88-
cells[14][6] = Cell::Alive;
89-
cells[14][7] = Cell::Alive;
102+
cells[4][5] = Cell::Alive;
103+
cells[4][6] = Cell::Alive;
104+
cells[4][7] = Cell::Alive;
105+
cells[8][5] = Cell::Alive;
106+
cells[8][6] = Cell::Alive;
107+
cells[8][7] = Cell::Alive;
90108
GameOfLifeState { cells }
91109
}
92110
fn toad() -> Self {
@@ -99,12 +117,12 @@ impl GameOfLifeState {
99117
// X X
100118
// X
101119
let mut cells = [[Cell::Dead; WIDTH]; HEIGHT];
102-
cells[10][4] = Cell::Alive;
103-
cells[10][5] = Cell::Alive;
104-
cells[10][6] = Cell::Alive;
105-
cells[11][5] = Cell::Alive;
106-
cells[11][6] = Cell::Alive;
107-
cells[11][7] = Cell::Alive;
120+
cells[17][4] = Cell::Alive;
121+
cells[17][5] = Cell::Alive;
122+
cells[17][6] = Cell::Alive;
123+
cells[18][5] = Cell::Alive;
124+
cells[18][6] = Cell::Alive;
125+
cells[18][7] = Cell::Alive;
108126
GameOfLifeState { cells }
109127
}
110128
fn beacon() -> Self {
@@ -119,15 +137,15 @@ impl GameOfLifeState {
119137
// X
120138
// XX
121139
let mut cells = [[Cell::Dead; WIDTH]; HEIGHT];
122-
cells[10][4] = Cell::Alive;
123-
cells[10][5] = Cell::Alive;
124-
cells[11][4] = Cell::Alive;
125-
cells[11][5] = Cell::Alive;
126-
127-
cells[12][6] = Cell::Alive;
128-
cells[12][7] = Cell::Alive;
129-
cells[13][6] = Cell::Alive;
130-
cells[13][7] = Cell::Alive;
140+
cells[26][4] = Cell::Alive;
141+
cells[26][5] = Cell::Alive;
142+
cells[27][4] = Cell::Alive;
143+
cells[27][5] = Cell::Alive;
144+
145+
cells[28][6] = Cell::Alive;
146+
cells[28][7] = Cell::Alive;
147+
cells[29][6] = Cell::Alive;
148+
cells[29][7] = Cell::Alive;
131149
GameOfLifeState { cells }
132150
}
133151

@@ -197,7 +215,7 @@ impl GameOfLifeState {
197215
self.cells = next_generation;
198216
}
199217

200-
fn draw_matrix(&self) -> Grid {
218+
pub fn draw_matrix(&self) -> Grid {
201219
let mut grid = Grid::default();
202220

203221
for row in 0..HEIGHT {

fl16-inputmodules/src/games/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
pub mod game_of_life;
22
pub mod pong;
3+
pub mod pong_animation;
34
pub mod snake;
5+
pub mod snake_animation;

0 commit comments

Comments
 (0)