rpi-rgb-led-matrix
rpi-rgb-led-matrix copied to clipboard
Plasma effect for demo-main.cc
I'm not in a great position to submit a pull request at the moment (I've made a lot of poorly managed changes during experimentation), but I thought I'd pass this along in case someone wants to integrate it properly. It is a new Plasma demo which looks a bit like a lava lamp.
Add the new Plasma class anywhere convenient in demo-main.cc:
// Uncomment to use Double Buffering in the Plasma Visualization
//#define PLASMA_DOUBLE_BUFFER 1
// Based on sample code from RosettaCode.org (Java version)
class Plasma : public ThreadedCanvasManipulator {
public:
#ifdef PLASMA_DOUBLE_BUFFER
Plasma(RGBMatrix *m) : ThreadedCanvasManipulator(m), matrix_(m) {
off_screen_canvas_ = m->CreateFrameCanvas();
#else
Plasma(Canvas *m) : ThreadedCanvasManipulator(m) {
#endif
hueShift = 0.0;
}
void Run() {
const int width = canvas()->width();
const int height = canvas()->height();
float d1 = sqrt(max(width, height))*1.5;
float d2 = d1/2.0;
int dx = rand()%1024;
int dy = rand()%1024;
while (running()) {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
unsigned int x1 = x + dx;
unsigned int y1 = y + dy;
float value = sin(x1 / d1);
value += sin(y1 / d2);
value += sin((x1 + y1) / d1);
value += sin(sqrt(x1 * x1 + y1 * y1) / d2);
value += 4; // shift range from -4 .. 4 to 0 .. 8
value /= 8; // bring range down to 0 .. 1
// value >= 0 && value =< 1.0
float hue = hueShift + value;
if (hue >= 1.0) hue -= 1.0;
Color c = HsvToRGB(HSVColor(hue*255, 255, 128));
#ifdef PLASMA_DOUBLE_BUFFER
matrix_->transformer()->Transform(off_screen_canvas_)->SetPixel(x, y, c.r, c.g, c.b);
#else
canvas()->SetPixel(x, y, c.r, c.g, c.b);
#endif
}
}
#ifdef PLASMA_DOUBLE_BUFFER
off_screen_canvas_ = matrix_->SwapOnVSync(off_screen_canvas_);
#endif
dx = (dx+1)%1024;
dy = (dy+1)%1024;
hueShift += 0.02; if (hueShift >= 1.0) hueShift -= 1.0;
usleep(100 * 1000);
}
}
private:
float hueShift;
#ifdef PLASMA_DOUBLE_BUFFER
RGBMatrix *const matrix_;
FrameCanvas *off_screen_canvas_;
#endif
};
Add the following case to the main switch statement which selects which demo to run:
case 14:
#ifdef PLASMA_DOUBLE_BUFFER
image_gen = new Plasma(matrix);
#else
image_gen = new Plasma(canvas);
#endif
I don't see a big difference with and without double buffering on an RPi2. This could be removed without sacrificing much.
I wouldn't mind trying out your demo, but you did not provide the source for the HSV to RGB utilities you are using...
Sorry about that. I added the following to graphics.h right below the definition of the Color struct:
struct HSVColor {
HSVColor(uint8_t h_, uint8_t s_, uint8_t v_) : h(h_), s(s_), v(v_) {}
uint8_t h;
uint8_t s;
uint8_t v;
};
Color HsvToRGB(HSVColor hsv);
and the following function to graphics.cc
Color HsvToRGB(HSVColor hsv)
{
Color rgb(hsv.v, hsv.v, hsv.v); // Default to case where hsv.s == 0
unsigned char region, remainder, p, q, t;
if (hsv.s == 0) return rgb;
region = hsv.h / 43;
remainder = (hsv.h - (region * 43)) * 6;
p = (hsv.v * (255 - hsv.s)) >> 8;
q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
rgb.r = hsv.v; rgb.g = t; rgb.b = p;
break;
case 1:
rgb.r = q; rgb.g = hsv.v; rgb.b = p;
break;
case 2:
rgb.r = p; rgb.g = hsv.v; rgb.b = t;
break;
case 3:
rgb.r = p; rgb.g = q; rgb.b = hsv.v;
break;
case 4:
rgb.r = t; rgb.g = p; rgb.b = hsv.v;
break;
default:
rgb.r = hsv.v; rgb.g = p; rgb.b = q;
break;
}
return rgb;
}
Great example! I adapted it for the new DemoRunner class and double buffering code in the code as of Jan '22 (probably earlier) - if anybody's interested in getting this code, pm me. Top tip - set the usleep to 30ms instead of 100, as that was way too stuttery, however I'm running this on a new fangled Pi Zero 2 W on a 64x64 matrix, so ymmv. Getting a slightly jarring interrupt in the animation where the plasma changes shape abruptly - any ideas why?