Lamp-Da 0.1
A compact lantern project
Loading...
Searching...
No Matches
nudz_scrollimage.hpp
2
3namespace lampda::modes::custom::nudz {
4
5// struct ImageTy {
6// static constexpr uint16_t width = 26;
7// static constexpr uint16_t height = 22;
8// static constexpr uint32_t colormapSize = 0;
9// static constexpr uint32_t colormap[];
10// static constexpr uint8_t indexData[] = {};
11// static constexpr uint32_t rgbData[] = { 0x800000, 0x008000 };
12// };
13
14template<typename ImageType> struct NudzScrollImageMode : public BasicMode
15{
16 struct StateTy
17 {
18 float minSpeed = -0.5f;
19 float maxSpeed = 0.5f;
20 bool randomScroll = false;
21 uint32_t xdecal;
22 uint32_t ydecal;
23 uint32_t last_tick;
24 int8_t xdirection = 1;
25 int8_t ydirection = 0;
26 };
27
28 static void on_enter_mode(auto& ctx)
29 {
30 // reset stateful events
31 ctx.state.xdecal = 0;
32 ctx.state.ydecal = 0;
33 ctx.state.last_tick = 0;
34
36 ctx.template set_config_bool<ConfigKeys::rampSaturates>(true);
37 }
38
39 static void loop(auto& ctx)
40 {
41 float spdRange = ctx.state.maxSpeed - ctx.state.minSpeed;
42 float speed = ctx.state.minSpeed + (float(ctx.get_active_custom_ramp()) / 255) * spdRange;
43 // ease stop at 0
44 if (speed < -spdRange * 0.1)
45 speed += spdRange * 0.1;
46 else if (speed > spdRange * 0.1)
47 speed -= spdRange * 0.1;
48 else
49 speed = 0.f;
50
51 uint16_t imWidth = ImageType::width;
52 uint16_t imHeight = ImageType::height;
53
54 // decal needs a state to keep continuous while speed is changed
55 int32_t xdecal = ctx.state.xdecal + int32_t((ctx.lamp.tick - ctx.state.last_tick) * speed);
56 int32_t ydecal = 0;
57 if (ctx.state.randomScroll)
58 {
59 xdecal =
60 int32_t(ctx.state.xdecal) + int32_t((ctx.lamp.tick - ctx.state.last_tick) * speed) * ctx.state.xdirection;
61 if (ctx.state.ydirection != 0)
62 ydecal = int32_t(ctx.state.ydecal) +
63 int32_t((ctx.lamp.tick - ctx.state.last_tick) * speed) * ctx.state.ydirection;
64 if (xdecal < 0)
65 {
66 xdecal = 0;
67 ctx.state.xdirection *= -random8(2);
68 ctx.state.ydirection = random8(3) - 1;
69 if (ctx.state.xdirection == 0 && ctx.state.ydirection == 0)
70 ctx.state.ydirection = random8(2) * 2 - 1;
71 }
72 else if (xdecal + ctx.lamp.maxWidth >= imWidth)
73 {
74 xdecal = imWidth - ctx.lamp.maxWidth - 1;
75 ctx.state.xdirection *= -random8(2);
76 ctx.state.ydirection = random8(3) - 1;
77 if (ctx.state.xdirection == 0 && ctx.state.ydirection == 0)
78 ctx.state.ydirection = random8(2) * 2 - 1;
79 }
80 if (ydecal < 0)
81 {
82 ydecal = 0;
83 ctx.state.ydirection *= -random8(2);
84 ctx.state.xdirection = random8(3) - 1;
85 if (ctx.state.xdirection == 0 && ctx.state.ydirection == 0)
86 ctx.state.xdirection = random8(2) * 2 - 1;
87 }
88 else if (ydecal + ctx.lamp.maxHeight >= imHeight)
89 {
90 ydecal = imHeight - ctx.lamp.maxHeight - 1;
91 ctx.state.ydirection *= -random8(2);
92 ctx.state.xdirection = random8(3) - 1;
93 if (ctx.state.xdirection == 0 && ctx.state.ydirection == 0)
94 ctx.state.xdirection = random8(2) * 2 - 1;
95 }
96 }
97 if (xdecal != ctx.state.xdecal || ydecal != ctx.state.ydecal)
98 {
99 ctx.state.xdecal = xdecal;
100 ctx.state.ydecal = ydecal;
101 ctx.state.last_tick = ctx.lamp.tick;
102 }
103
104 uint32_t w = min<uint32_t>(ctx.lamp.maxWidth + 1, imWidth);
105 uint32_t h = min<uint32_t>(ctx.lamp.maxHeight, imHeight);
106 if (ImageType::colormapSize == 0)
107 for (uint32_t y = 0; y < h; ++y)
108 for (uint32_t x = 0; x < w; ++x)
109 ctx.lamp.setPixelColorXY(
110 x, y, ImageType::rgbData[((y + ydecal) % imHeight) * imWidth + (x + xdecal) % imWidth]);
111 else
112 {
113 // indexed colormap
114 constexpr uint8_t bmask = (1 << ImageType::bitsPerPixel) - 1;
115 for (uint32_t y = 0; y < h; ++y)
116 {
117 uint32_t yoffset = ((y + ydecal) % imHeight) * imWidth;
118 for (uint32_t x = 0; x < w; ++x)
119 {
120 uint32_t offset = yoffset + (x + xdecal) % imWidth;
121 uint32_t byteOffset = offset * ImageType::bitsPerPixel / 8;
122 uint32_t bitOffset = (offset * ImageType::bitsPerPixel) % 8;
123 uint8_t index = ImageType::indexData[byteOffset];
124 if (bitOffset + ImageType::bitsPerPixel > 8)
125 {
126 uint16_t sindex = (index << 8) | ImageType::indexData[byteOffset + 1];
127 index = (sindex >> (16 - bitOffset - ImageType::bitsPerPixel)) & bmask;
128 }
129 else
130 index = (index >> (8 - bitOffset - ImageType::bitsPerPixel)) & bmask;
131
132 ctx.lamp.setPixelColorXY(x, y, ImageType::colormap[index]);
133 }
134 }
135 }
136 }
137
139 static constexpr bool hasCustomRamp = true;
140};
141
142#include "src/generated/heineken.hpp"
143
145
146#include "src/generated/huit_six.hpp"
147
149
150#include "src/generated/violonsaouls.hpp"
151
156struct NudzViolonsaoulsMode : public NudzScrollImageMode<ViolonsaoulsImageTy>
157{
158 struct StateTy
159 {
160 float minSpeed = 0.f;
161 float maxSpeed = 0.5f;
162 bool randomScroll = false;
163 uint32_t xdecal;
164 uint32_t ydecal;
165 uint32_t last_tick;
166 int8_t xdirection = 1;
167 int8_t ydirection = 0;
168 };
169};
170
176{
177 struct BubbleTy
178 {
179 BubbleTy() : x(-1.f), y(0.f), speed(0.f), color(0x706050), color_fade(0.9) {}
180 int x;
181 float y;
182 float speed;
183 uint32_t color;
185 };
186
187 struct StateTy
188 {
189 float level;
190 float ampl;
191 float accmax;
192 float fall_ampl;
193 float wave_ampl;
194 float decay;
197 uint32_t beer_color;
198 uint32_t foam_color;
200 std::vector<float> levels;
201 std::vector<float> speeds;
202 uint32_t nbubbles;
203 std::vector<BubbleTy> bubbles;
204
206 };
207
208 static void on_enter_mode(auto& ctx)
209 {
210 ctx.state.imuEvent.reset(ctx);
211
212 // reset stateful events
213 ctx.state.level = 10.f;
214 ctx.state.ampl = 0.02f;
215 ctx.state.accmax = 0.2f;
216 ctx.state.fall_ampl = 10.f;
217 ctx.state.wave_ampl = 0.3f;
218 ctx.state.decay = 0.7f;
219 ctx.state.bounce_ratio = 0.7f;
220 ctx.state.beer_color = 0x503000;
221 ctx.state.foam_color = 0x706050;
222 ctx.state.background_color = 0x000000;
223 ctx.state.levels = std::vector<float>(ctx.lamp.maxWidth, ctx.state.level);
224 ctx.state.speeds = std::vector<float>(ctx.lamp.maxWidth, 0.f);
225 ctx.state.nbubbles = 20;
226 ctx.state.bubbles = std::vector<BubbleTy>(ctx.state.nbubbles, BubbleTy());
227
229 ctx.template set_config_bool<ConfigKeys::rampSaturates>(true);
230 }
231
232 static void loop(auto& ctx)
233 {
234 ctx.state.imuEvent.update(ctx);
235
236 updateLevels(ctx);
237
238 // display
239 displayLevels(ctx);
240
241 // bubbles
242 makeBubbles(ctx);
243
244 // draw accel
245 if (ctx.get_active_custom_ramp() >= 240)
246 drawAccel(ctx);
247 }
248
250 static void updateLevels(auto& ctx)
251 {
252 uint32_t nx = ctx.lamp.maxWidth;
253 uint32_t ny = ctx.lamp.maxHeight;
254
255 const auto& reading = ctx.state.imuEvent.lastReading;
256 const auto accel = reading.accel;
257
258 auto& levels = ctx.state.levels;
259 auto& speeds = ctx.state.speeds;
260
261 // change global level, if needed, according to custom ramp
262 float newLevel = float(ctx.get_active_custom_ramp()) / 255 * (ny - 1);
263 float diffLevel = newLevel - ctx.state.level;
264 if (diffLevel < 0)
265 {
266 // avoid leak in beer quantity
267 float missing = 0.f;
268 for (uint32_t x = 0; x < nx; ++x)
269 if (levels[x] < -diffLevel)
270 missing += -diffLevel - levels[x];
271 diffLevel += missing / nx;
272 }
273 ctx.state.level = newLevel;
274 for (uint32_t x = 0; x < nx; ++x)
275 levels[x] += diffLevel;
276
277 utils::vec2d acc(accel.x, accel.y);
278 // threshold because there is a drift in the accel
279 if (acc.x * acc.x + acc.y * acc.y < 4.f)
280 acc = utils::vec2d(0.f, 0.f);
281 acc.x *= ctx.state.ampl;
282 acc.y *= ctx.state.ampl;
283 float accmax = ctx.state.accmax;
284 if (acc.x < -accmax)
285 acc.x = -accmax;
286 else if (acc.x > accmax)
287 acc.x = accmax;
288 if (acc.y < -accmax)
289 acc.y = -accmax;
290 else if (acc.y > accmax)
291 acc.y = accmax;
292
293 std::vector<float> ospeeds = speeds;
294
295 // accelerate column by column
296 // the "column" model is very simple and easy,
297 // but cannot account for real orientation changes
298 // since the Z (column) axis is always considered to be vertical
299 for (uint32_t x = 0; x < nx; ++x)
300 {
301 // angular coords of the pixel
302 float angle = float(x) / nx * M_PI * 2;
303 // normal and tangent to the lamp
304 utils::vec2d norm(cos_t(angle), sin_t(angle));
305 utils::vec2d tan(-norm.y, norm.x);
306 // we consider the accel will push pixels away from the wall
307 // in the direction opposite to the force (if we consider the lamp
308 // as the reference coords
309 float acc0 = norm.dot(acc);
310 if (acc0 < 0.f) // force and normal in the same direction:
311 acc0 = 0.f; // we cannot move because the lamp wall blocks it
312
313 float sign = 1.f;
314 if (acc.dot(tan) > 0) // direction of the propagation
315 sign = -1.f;
316 speeds[x] -= levels[x] * acc0 * sign;
317 ospeeds[x] -= levels[x] * acc0 * sign;
318 float diff = speeds[x]; // quantity of "beer pixels" which should
319 // transfer to neighboring column
320 uint32_t x1, x2;
321 if (diff < 0)
322 {
323 if (x == 0)
324 x1 = nx - 1;
325 else
326 x1 = x - 1;
327 if (levels[x] < -diff) // we cannot remove more material than
328 // the quantity available in this col.
329 diff = -levels[x];
330 levels[x1] -= diff; // transfer from col x to x1
331 levels[x] += diff;
332 ospeeds[x1] += diff * ctx.state.wave_ampl; // impulse a speed to x1
333 }
334 else
335 {
336 diff *= 0.7; // compensate loop propagation direction
337 // because the iteration is left-to-right
338 // and moves to the right are thus way faster
339 if (x == nx - 1)
340 x1 = 0;
341 else
342 x1 = x + 1;
343 if (levels[x] < diff)
344 diff = levels[x];
345 levels[x1] += diff;
346 levels[x] -= diff;
347 ospeeds[x1] += diff * ctx.state.wave_ampl;
348 }
349 }
350
351 // we have worked in a copy of speeds to avoid too fast propagation
352 // in the iteration direction (left to right)
353 ctx.state.speeds = ospeeds;
354
355 // drop and move waves
356 for (uint32_t x = 0; x < nx; ++x)
357 {
358 uint32_t x1 = x + 1;
359 if (x == nx - 1)
360 x1 = 0;
361 float ld = levels[x1] - levels[x];
362 float diff = 0;
363 if (ld > 0)
364 diff = min<float>(ld, ctx.state.fall_ampl);
365 else if (ld < 0)
366 diff = max<float>(ld, -ctx.state.fall_ampl);
367 if (diff > 0 && diff > ld * ctx.state.bounce_ratio)
368 diff = ld * ctx.state.bounce_ratio;
369 else if (diff < 0 && diff < ld * ctx.state.bounce_ratio)
370 diff = ld * ctx.state.bounce_ratio;
371 levels[x] += diff;
372 levels[x1] -= diff;
373 speeds[x] -= diff * ctx.state.wave_ampl;
374 speeds[x] *= ctx.state.decay;
375 }
376 }
377
379 static void displayLevels(auto& ctx)
380 {
381 auto& levels = ctx.state.levels;
382 uint32_t nx = ctx.lamp.maxWidth;
383 uint32_t ny = ctx.lamp.maxHeight;
384
385 for (uint32_t x = 0; x <= nx; ++x)
386 {
387 int32_t x0 = x;
388 if (x == nx)
389 x0 = nx - 1;
390 int32_t y, y1 = int(levels[x0]);
391 if (y1 >= ny)
392 y1 = ny - 1;
393 y1 = ny - 1 - y1;
394 for (y = 0; y < y1 - 3; ++y)
395 ctx.lamp.setPixelColorXY(x, y, ctx.state.background_color);
396 for (; y < y1; ++y)
397 ctx.lamp.setPixelColorXY(x, y, ctx.state.foam_color);
398 for (; y < ny; ++y)
399 ctx.lamp.setPixelColorXY(x, y, ctx.state.beer_color);
400 }
401 }
402
404 static void makeBubbles(auto& ctx)
405 {
406 auto& bubbles = ctx.state.bubbles;
407 auto& levels = ctx.state.levels;
408 uint32_t nx = ctx.lamp.maxWidth;
409 uint32_t ny = ctx.lamp.maxHeight;
410
411 for (uint32_t i = 0; i < ctx.state.nbubbles; ++i)
412 {
413 auto& bubble = bubbles[i];
414 if (bubble.x < 0) // free slot
415 {
416 // new bubble
417 bubble.x = random8(nx);
418 bubble.y = float(random8(uint8_t(levels[bubble.x])));
419 bubble.speed = float(random8()) / 256 * 0.05 + 0.05;
420 bubble.color = ctx.state.foam_color;
421 bubble.color_fade = float(random8()) / 256 * 0.029 + 0.975;
422 }
423 if (int(bubble.y) >= levels[bubble.x])
424 {
425 bubble.x = -1; // die
426 continue;
427 }
428 ctx.lamp.setPixelColorXY(bubble.x, ny - 1 - int(bubble.y), bubble.color);
429
430 // move for next time
431 bubble.y += bubble.speed;
432 // fade color to beer color
433 uint8_t r = uint8_t(float((bubble.color & 0xff0000) >> 16) * bubble.color_fade +
434 float((ctx.state.beer_color & 0xff0000) >> 16) * (1. - bubble.color_fade));
435 uint8_t g = uint8_t(float((bubble.color & 0xff00) >> 8) * bubble.color_fade +
436 float((ctx.state.beer_color & 0xff00) >> 8) * (1. - bubble.color_fade));
437 uint8_t b = uint8_t(float(bubble.color & 0xff) * bubble.color_fade +
438 float(ctx.state.beer_color & 0xff) * (1. - bubble.color_fade));
439 bubble.color = (r << 16) + (g << 8) + b;
440 // if we get cose to beer color, the bubble gets invisible and dies
441 // theshold is 5 in R, G, B
442 if ((float(r) - float((ctx.state.beer_color & 0xff0000) >> 16)) *
443 (float(r) - float((ctx.state.beer_color & 0xff0000) >> 16)) +
444 (float(g) - float((ctx.state.beer_color & 0xff00) >> 8)) *
445 (float(g) - float((ctx.state.beer_color & 0xff00) >> 8)) +
446 (float(b) - float(ctx.state.beer_color & 0xff)) * (float(b) - float(ctx.state.beer_color & 0xff)) <
447 125)
448 bubble.x = -1;
449 }
450 }
451
453 static void drawAccel(auto& ctx)
454 {
455 const auto& reading = ctx.state.imuEvent.lastReading;
456 const auto accel = reading.accel;
457
458 int32_t ax = int32_t(accel.x);
459 int32_t ay = int32_t(accel.y);
460 int32_t az = int32_t(accel.z);
461
462 // clamp to -10 - 10
463 if (ax < -10)
464 ax = -10;
465 else if (ax > 10)
466 ax = 10;
467 if (ay < -10)
468 ay = -10;
469 else if (ay > 10)
470 ay = 10;
471 if (az < -10)
472 az = -10;
473 else if (az > 10)
474 az = 10;
475
476 uint32_t x;
477 for (x = 10 + ax; x < 10; ++x)
478 ctx.lamp.setPixelColorXY(x, 0, 0x800000);
479 for (x = 10; x < 10 + ax; ++x)
480 ctx.lamp.setPixelColorXY(x, 0, 0x008000);
481 for (x = 10 + ay; x < 10; ++x)
482 ctx.lamp.setPixelColorXY(x, 1, 0x800000);
483 for (x = 10; x < 10 + ay; ++x)
484 ctx.lamp.setPixelColorXY(x, 1, 0x008000);
485 for (x = 10 + az; x < 10; ++x)
486 ctx.lamp.setPixelColorXY(x, 2, 0x800000);
487 for (x = 10; x < 10 + az; ++x)
488 ctx.lamp.setPixelColorXY(x, 2, 0x008000);
489 }
490
492 static constexpr bool hasCustomRamp = true;
493};
494
495} // namespace lampda::modes::custom::nudz
User mode IMU utilities.
Parent object for all custom user modes.
Definition: mode_type.hpp:53
int x
x coordinate
Definition: nudz_scrollimage.hpp:180
uint32_t color
color of this buble
Definition: nudz_scrollimage.hpp:183
float y
y coordinate
Definition: nudz_scrollimage.hpp:181
float speed
speed in px/second
Definition: nudz_scrollimage.hpp:182
float color_fade
fade rate
Definition: nudz_scrollimage.hpp:184
float bounce_ratio
if ratio > 0.5 a wave dropping can increase. Its neighbours height over its new height,...
Definition: nudz_scrollimage.hpp:195
std::vector< float > levels
level of every matrix columns
Definition: nudz_scrollimage.hpp:200
uint32_t nbubbles
number of bubbles
Definition: nudz_scrollimage.hpp:202
float level
average level of beer
Definition: nudz_scrollimage.hpp:189
float accmax
clamp accel to avoid too large changes
Definition: nudz_scrollimage.hpp:191
imu::ImuEventTy imuEvent
Handle imu events.
Definition: nudz_scrollimage.hpp:205
float ampl
accel amplitude factor
Definition: nudz_scrollimage.hpp:190
std::vector< BubbleTy > bubbles
store the bubbles
Definition: nudz_scrollimage.hpp:203
uint32_t beer_color
color of the beer :)
Definition: nudz_scrollimage.hpp:197
float wave_ampl
wave speed
Definition: nudz_scrollimage.hpp:193
std::vector< float > speeds
spped of every matric columns
Definition: nudz_scrollimage.hpp:201
float decay
speed decay factor
Definition: nudz_scrollimage.hpp:194
uint32_t foam_color
color of the foam of the beer
Definition: nudz_scrollimage.hpp:198
uint32_t background_color
color background
Definition: nudz_scrollimage.hpp:199
float fall_ampl
height a wave will go down at each step
Definition: nudz_scrollimage.hpp:192
Display a glass of beer, reactive to gravity. The user ramp changes the beer level.
Definition: nudz_scrollimage.hpp:176
static constexpr bool hasCustomRamp
hint manager that we have a custom ramp
Definition: nudz_scrollimage.hpp:492
static void updateLevels(auto &ctx)
update the level of the liquid using IMU events
Definition: nudz_scrollimage.hpp:250
static void displayLevels(auto &ctx)
Display the liquid level on the strip.
Definition: nudz_scrollimage.hpp:379
static void drawAccel(auto &ctx)
Draw the acceleration vector from the IMU.
Definition: nudz_scrollimage.hpp:453
static void on_enter_mode(auto &ctx)
Definition: nudz_scrollimage.hpp:208
static void makeBubbles(auto &ctx)
Invoque new bubbles !
Definition: nudz_scrollimage.hpp:404
float maxSpeed
maximum allowed speed
Definition: nudz_scrollimage.hpp:19
bool randomScroll
random variation of the scroll
Definition: nudz_scrollimage.hpp:20
float minSpeed
minimum allowed speed
Definition: nudz_scrollimage.hpp:18
int8_t ydirection
y scroll direction
Definition: nudz_scrollimage.hpp:25
uint32_t ydecal
start y coordinates
Definition: nudz_scrollimage.hpp:22
uint32_t last_tick
last called time in microseconds
Definition: nudz_scrollimage.hpp:23
int8_t xdirection
x scroll direction
Definition: nudz_scrollimage.hpp:24
uint32_t xdecal
start x coordinates
Definition: nudz_scrollimage.hpp:21
Definition: nudz_scrollimage.hpp:15
static constexpr bool hasCustomRamp
Hint manager to save our custom ramp.
Definition: nudz_scrollimage.hpp:139
static void on_enter_mode(auto &ctx)
Definition: nudz_scrollimage.hpp:28
float maxSpeed
maximum allowed speed
Definition: nudz_scrollimage.hpp:161
bool randomScroll
random variation of the scroll
Definition: nudz_scrollimage.hpp:162
float minSpeed
minimum allowed speed
Definition: nudz_scrollimage.hpp:160
uint32_t last_tick
last called time in microseconds
Definition: nudz_scrollimage.hpp:165
int8_t xdirection
x scroll direction
Definition: nudz_scrollimage.hpp:166
uint32_t ydecal
start y coordinates
Definition: nudz_scrollimage.hpp:164
int8_t ydirection
y scroll direction
Definition: nudz_scrollimage.hpp:167
uint32_t xdecal
start x coordinates
Definition: nudz_scrollimage.hpp:163
Display an image "Violon saoul" scrolling around. User ramp changes scroll speed and direction.
Definition: nudz_scrollimage.hpp:157
Definition: utils.hpp:19
2d vector in any space
Definition: vector_math.h:15
float dot(const vec2d &other) const
Dot product in 2D, representing the arcos of the angle between this vector and another.
Definition: vector_math.h:27
float x
x coordinate in 2D
Definition: vector_math.h:16
float y
y coordinate in 2D
Definition: vector_math.h:17