rpi-rgb-led-matrix icon indicating copy to clipboard operation
rpi-rgb-led-matrix copied to clipboard

Plasma effect for demo-main.cc

Open jpasqua opened this issue 8 years ago • 3 comments

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.

jpasqua avatar Jul 16 '16 19:07 jpasqua

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

rhildinger avatar Aug 01 '16 17:08 rhildinger

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;
}

jpasqua avatar Aug 03 '16 04:08 jpasqua

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?

Heliosphan avatar Jan 17 '22 23:01 Heliosphan