hzeller / rpi-rgb-led-matrix

Controlling up to three chains of 64x64, 32x32, 16x32 or similar RGB LED displays using Raspberry Pi GPIO
GNU General Public License v2.0
3.64k stars 1.16k forks source link

Plasma effect for demo-main.cc #177

Open jpasqua opened 8 years ago

jpasqua commented 8 years ago

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.

rhildinger commented 8 years ago

I wouldn't mind trying out your demo, but you did not provide the source for the HSV to RGB utilities you are using...

jpasqua commented 8 years ago

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;
}
Heliosphan commented 2 years ago

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?