forked from Zidit/partyhat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathanimation_tasks.h
371 lines (316 loc) · 9.41 KB
/
animation_tasks.h
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#ifndef ANIMATION_TASKS_H
#define ANIMATION_TASKS_H
#include "arduinowrapper.h"
#include "vectors.h"
#include "config.h"
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#define FADE_MIN_TIME 10 //ms
typedef struct {
const void *Next; // Pointer to next animation
uint8_t leds; // bits for led 0-7
uint8_t length; // Frames
uint8_t modes; // Bit flags, will be later to used to indicate fade mode etc
const uint8_t *first_frame;
} Animation;
Animation animation_buffer;
extern Animation animation_buffer;
void load_animation_to_buffer(const Animation* src)
{
memcpy_P(&animation_buffer, src, sizeof(Animation));
}
void load_animation_to_buffer(const Animation* src, Animation* tgt)
{
memcpy_P(tgt, src, sizeof(Animation));
}
enum AnimationState {
STOPPED,
RUNNING,
FADING_START,
FADING,
FADING_END
};
// Pack this matrix to a stuct to make pointer passing saner
typedef struct {
uint8_t leds[7][3];
uint16_t wait_ms;
} frame_data;
class AnimationRunner
{
public:
// Base methods
AnimationRunner();
virtual void run(uint32_t now);
//virtual bool canRun(uint32_t now);
// My own methods
virtual void set_animation(Animation* anim);
virtual void set_animation(const Animation* anim);
virtual void start_animation();
virtual void stop_animation();
uint32_t getNextRunTime(){return runTime;}
protected:
virtual void unpack_frame(const uint8_t *start_of_frame, frame_data& tgt);
void interpolate_fade();
void set_leds(frame_data& src);
void leds_off();
uint32_t runTime;
void incRunTime(uint32_t time) {runTime += time;}
void setRunTime(uint32_t time) {runTime = time;}
const Animation* current_animation;
uint8_t frame_size;
uint8_t current_step;
uint8_t num_leds;
frame_data current_frame;
frame_data next_frame;
frame_data fade_frame;
uint8_t fade_step;
uint8_t num_fade_steps;
AnimationState state;
};
AnimationRunner::AnimationRunner()
{
state = STOPPED;
runTime = millis();
}
/**
* Frame loader loading from PROGMEM
*/
void AnimationRunner::unpack_frame(const uint8_t *start_of_frame, frame_data& tgt)
{
uint8_t frame_position = 0;
for (uint8_t i=0; i < 8; i++)
{
if (current_animation->leds & _BV(i))
{
tgt.leds[i][0] = pgm_read_byte(start_of_frame+frame_position+0);
tgt.leds[i][1] = pgm_read_byte(start_of_frame+frame_position+1);
tgt.leds[i][2] = pgm_read_byte(start_of_frame+frame_position+2);
frame_position += 3;
}
}
//wait_ms = pgm_read_word(start_of_frame+frame_position);
tgt.wait_ms = (pgm_read_byte(start_of_frame+frame_position) << 8) + pgm_read_byte(start_of_frame+frame_position+1);
}
/**
* Load animation struct from SRAM
*/
void AnimationRunner::set_animation(Animation* anim)
{
leds_off();
current_step = 0;
current_animation = anim;
num_leds = 0;
for (uint8_t i=0; i < 8; i++)
{
if (current_animation->leds & _BV(i))
{
num_leds++;
}
}
frame_size = (num_leds*3)+2;
this->unpack_frame(current_animation->first_frame, current_frame);
this->setRunTime(millis());
state = STOPPED;
}
void AnimationRunner::start_animation()
{
state = RUNNING;
}
void AnimationRunner::stop_animation()
{
// Reset the animation
/**
* Seems this fscks some pointer up
set_animation(current_animation);
*/
state = STOPPED;
leds_off();
}
void AnimationRunner::leds_off()
{
// Clear all leds
for (uint8_t i=0; i < 8; i++)
{
vectors[i].sector = 0;
}
}
/**
* Load animation struct from PROGMEM
*/
void AnimationRunner::set_animation(const Animation* anim)
{
load_animation_to_buffer(anim);
set_animation(&animation_buffer);
}
void AnimationRunner::interpolate_fade()
{
for (uint8_t i=0; i < 8; i++)
{
if (current_animation->leds & _BV(i))
{
for (uint8_t ii=0; ii < 3; ii++)
{
uint8_t tmpval1 = current_frame.leds[i][ii];
uint8_t tmpval2 = next_frame.leds[i][ii];
if (tmpval1 == tmpval2)
{
fade_frame.leds[i][ii] = tmpval2;
continue;
}
if (tmpval1 > tmpval2)
{
fade_frame.leds[i][ii] = tmpval1 - (((tmpval1 - tmpval2) / num_fade_steps) * fade_step);
}
else
{
fade_frame.leds[i][ii] = (((tmpval2 - tmpval1) / num_fade_steps) * fade_step);
}
}
}
}
}
void AnimationRunner::set_leds(frame_data& src)
{
//front
vectors[0].direction = 0;
vectors[0].sector = 6;
vectors[0].r = src.leds[2][0];
vectors[0].g = src.leds[2][1];
vectors[0].b = src.leds[2][2];
//back
vectors[1].direction = -128;
vectors[1].sector = 6;
vectors[1].r = src.leds[3][0];
vectors[1].g = src.leds[3][1];
vectors[1].b = src.leds[3][2];
//left
vectors[2].direction = -64;
vectors[2].sector = 6;
vectors[2].r = src.leds[0][0];
vectors[2].g = src.leds[0][1];
vectors[2].b = src.leds[0][2];
//right
vectors[3].direction = 64;
vectors[3].sector = 6;
vectors[3].r = src.leds[1][0];
vectors[3].g = src.leds[1][1];
vectors[3].b = src.leds[1][2];
}
void AnimationRunner::run(uint32_t now)
{
switch (state)
{
case STOPPED:
return;
case RUNNING:
{
set_leds(current_frame);
if (current_animation->modes == 0x1) // Fade mode animation
{
state = FADING_START;
// Returning without incruntime means we will immediately (well, on next scheduler iteration but that's gonne be real-soon-now) come back to the next state
return;
}
// The default hard-switch handler, load next frame and wait.
incRunTime(current_frame.wait_ms);
current_step++;
if (current_step >= current_animation->length)
{
current_step = 0;
}
unpack_frame(current_animation->first_frame+(current_step*frame_size), current_frame);
break;
}
// This is porbably unneeded
case FADING_START:
{
// Load next frame (looping as neccessary
uint8_t next_step = current_step+1;
if (next_step >= current_animation->length)
{
next_step = 0;
}
unpack_frame(current_animation->first_frame+(next_step*frame_size), next_frame);
// Figure out step time and max number of steps we can take within the constraints
fade_frame.wait_ms = FADE_MIN_TIME;
uint16_t fade_max_steps = current_frame.wait_ms/FADE_MIN_TIME;
if (fade_max_steps > 255)
{
fade_max_steps = 255;
fade_frame.wait_ms = current_frame.wait_ms/255;
}
// Set next runtime before all the heavy calculcations
incRunTime(fade_frame.wait_ms);
// Calculate largest difference between values to interpolate
uint8_t largest_diff = 0;
for (uint8_t i=0; i < 8; i++)
{
if (current_animation->leds & _BV(i))
{
for (uint8_t ii=0; ii < 3; ii++)
{
uint8_t tmpval1 = current_frame.leds[i][ii];
uint8_t tmpval2 = next_frame.leds[i][ii];
uint8_t tmpdiff;
if (tmpval1 > tmpval2)
{
tmpdiff = tmpval1-tmpval2;
}
else
{
tmpdiff = tmpval2-tmpval1;
}
if (tmpdiff > largest_diff)
{
largest_diff = tmpdiff;
}
}
}
}
// Set the number of steps we are going to fade for
if (largest_diff <= fade_max_steps)
{
num_fade_steps = largest_diff;
}
else
{
num_fade_steps = fade_max_steps;
}
fade_step = 1;
interpolate_fade();
state = FADING;
break;
}
case FADING:
{
set_leds(fade_frame);
// Set next runtime before all the heavy calculcations
incRunTime(fade_frame.wait_ms);
if (fade_step >= num_fade_steps)
{
state = FADING_END;
}
fade_step++;
interpolate_fade();
break;
}
case FADING_END:
{
set_leds(fade_frame);
incRunTime(fade_frame.wait_ms);
state = RUNNING;
current_step++;
if (current_step >= current_animation->length)
{
current_step = 0;
}
//unpack_frame(current_animation->first_frame+(current_step*frame_size), leds, &wait_ms);
// We already unpacked the next frame to calculate the interpolations, so just switch them over.
current_frame = next_frame;
break;
}
}
}
AnimationRunner anim_runner;
extern AnimationRunner anim_runner;
#endif