-
Notifications
You must be signed in to change notification settings - Fork 1
/
particle.h
219 lines (174 loc) · 5.4 KB
/
particle.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
#pragma once
#define MAX_PARTICLES 20
#undef PARTICLE_PALETTES
class Particle;
typedef void (*ParticleFn)(Particle *particle, CRGB strip[], uint8_t num_leds);
extern void drawPoint(Particle *particle, CRGB strip[], uint8_t num_leds);
extern void drawFlash(Particle *particle, CRGB strip[], uint8_t num_leds);
class Particle {
const static uint16_t DEFAULT_BRIGHTNESS = (192<<8); // or 96
public:
BeatFrame_24_8 born;
BeatFrame_24_8 lifetime;
BeatFrame_24_8 age;
uint16_t position = 0;
int16_t velocity = 0;
int16_t gravity = 0;
void (*die_fn)(Particle *particle) = NULL;
PenMode pen = Draw;
#ifdef PARTICLE_PALETTES
CRGBPalette16 palette; // 48 bytes per particle!?
#endif
CRGB color;
uint16_t brightness;
ParticleFn drawFn;
Particle(uint16_t position, CRGB color=CRGB::White, PenMode pen=Draw, uint32_t lifetime=20000, ParticleFn drawFn=drawPoint)
{
this->age = 0;
this->position = position;
this->color = color;
this->pen = pen;
this->lifetime = lifetime;
this->brightness = DEFAULT_BRIGHTNESS;
this->drawFn = drawFn;
}
void update(BeatFrame_24_8 frame)
{
this->age = frame - this->born;
this->position = this->udelta16(this->position, this->velocity);
this->velocity = this->delta16(this->velocity, this->gravity);
}
uint16_t age_frac16(BeatFrame_24_8 age)
{
if (age >= this->lifetime)
return 65535;
uint32_t a = age * 65536;
return a / this->lifetime;
}
uint16_t udelta16(uint16_t x, int16_t dx)
{
if (dx > 0 && 65535-x < dx)
return 65335;
if (dx < 0 && x < -dx)
return 0;
return x + dx;
}
int16_t delta16(int16_t x, int16_t dx)
{
if (dx > 0 && 32767-x < dx)
return 32767;
if (dx < 0 && x < -32767 - dx)
return -32767;
return x + dx;
}
CRGB color_at(uint16_t age_frac) {
// Particles get dimmer with age
uint8_t a = age_frac >> 8;
uint8_t brightness = scale8((uint8_t)(this->brightness>>8), 255-a);
#ifdef PARTICLE_PALETTES
// a black pattern actually means to use the current palette
if (this->color == CRGB(0,0,0))
return ColorFromPalette(this->palette, a, brightness);
#endif
uint8_t r = scale8(this->color.r, brightness);
uint8_t g = scale8(this->color.g, brightness);
uint8_t b = scale8(this->color.b, brightness);
return CRGB(r,g,b);
}
void draw_with_pen(CRGB strip[], int pos, CRGB color) {
CRGB new_color;
switch (this->pen) {
case Draw:
strip[pos] = color;
break;
case Blend:
strip[pos] |= color;
break;
case Erase:
strip[pos] &= color;
break;
case Invert:
strip[pos] = -strip[pos];
break;
case Brighten: {
uint8_t t = color.getAverageLight();
new_color = CRGB(t,t,t);
strip[pos] += new_color;
break;
}
case Darken: {
uint8_t t = color.getAverageLight();
new_color = CRGB(t,t,t);
strip[pos] -= new_color;
break;
}
case Flicker: {
uint8_t t = color.getAverageLight();
new_color = CRGB(t,t,t);
if (millis() % 2)
strip[pos] -= new_color;
else
strip[pos] += new_color;
break;
}
case White:
strip[pos] = CRGB::White;
break;
case Black:
strip[pos] = CRGB::Black;
break;
}
}
};
ustd::array<Particle*> particles = ustd::array<Particle*>(5);
BeatFrame_24_8 particle_beat_frame;
void addParticle(Particle *particle) {
particle->born = particle_beat_frame;
particles.add(particle);
if (particles.length() > MAX_PARTICLES) {
Particle *old_particle = particles[0];
delete old_particle;
particles.erase(0);
}
}
void drawFlash(Particle *particle, CRGB strip[], uint8_t num_leds) {
uint16_t age_frac = particle->age_frac16(particle->age);
CRGB c = particle->color_at(age_frac);
for (int pos = 0; pos < num_leds; pos++) {
particle->draw_with_pen(strip, pos, c);
}
}
void drawPoint(Particle *particle, CRGB strip[], uint8_t num_leds) {
uint16_t age_frac = particle->age_frac16(particle->age);
CRGB c = particle->color_at(age_frac);
uint16_t pos = scale16(particle->position, num_leds-1);
particle->draw_with_pen(strip, pos, c);
}
void drawRadius(Particle *particle, CRGB strip[], uint8_t num_leds, uint16_t pos, uint8_t radius, CRGB c, bool dim=true) {
for (int i = 0; i < radius; i++) {
uint8_t bright = dim ? ((radius-i) * 255) / radius : 255;
nscale8(&c, 1, bright);
uint8_t y = pos - i;
if (y >= 0 && y < num_leds)
particle->draw_with_pen(strip, y, c);
if (i == 0)
continue;
y = pos + i;
if (y >= 0 && y < num_leds)
particle->draw_with_pen(strip, y, c);
}
}
void drawPop(Particle *particle, CRGB strip[], uint8_t num_leds) {
uint16_t age_frac = particle->age_frac16(particle->age);
CRGB c = particle->color_at(age_frac);
uint16_t pos = scale16(particle->position, num_leds-1);
uint8_t radius = scale16((sin16(age_frac/2) - 32768) * 2, 8);
drawRadius(particle, strip, num_leds, pos, radius, c);
}
void drawBeatbox(Particle *particle, CRGB strip[], uint8_t num_leds) {
uint16_t age_frac = particle->age_frac16(particle->age);
CRGB c = particle->color_at(age_frac);
uint16_t pos = scale16(particle->position, num_leds-1);
uint8_t radius = 5;
drawRadius(particle, strip, num_leds, pos, radius, c, false);
}