// https://www.reddit.com/r/FastLED/comments/1jw7t7s/help_getting_started_with_an_effect_i_want/

// this doesn't work; it needs to be a build define instead
#define MAX_CLED_CONTROLLERS 20
#include <FastLED.h>

const int xSize = 5;
const int ySize = 4;
const float tau = 6.2831853072;

CHSV colours[xSize * ySize];

CRGBArray<xSize * ySize * 40> leds;

// These sets are arranged in Y-major raster order
const CRGBSet rings[] {
  leds(0, 39),    leds(40, 79),   leds(80, 119),  leds(120, 159),
  leds(160, 199), leds(200, 239), leds(240, 279), leds(280, 319),
  leds(320, 359), leds(360, 399), leds(400, 439), leds(440, 479),
  leds(480, 519), leds(520, 559), leds(560, 599), leds(600, 639),
  leds(640, 679), leds(680, 719), leds(720, 759), leds(760, 799),
};

void setup() {
  // feed the PRNG with some entropy from a floating pin, to get different colours at each boot
  pinMode(A0, INPUT);
  for (int i = 1024; --i; )
    random16_add_entropy(analogRead(A0));

  // set a random base colour for each ring
  // unless every ring was made from the same batch of LEDs, there will be colour differences
  // a small random component to the Hue can help to hide these anomalies.
  uint8_t baseHue = random8();
  uint8_t baseSat = 63 + random8(64);
  for (int i = 0; i < xSize * ySize; i++)
    colours[i] = CHSV(baseHue + random8(16), baseSat + random8(128), 150);

  // The rings are connected to pins in Y-major raster order
  FastLED.addLeds<WS2812B, 14, GRB>(leds.leds, leds.len);
}


void loop() {
  static float angle = 30;
  angle = fmodf(angle + 0.125f, 360);
  windwaves(angle);
  FastLED.show();
}


void windwaves(float angle) {
  // find the centre of the matrix. For even sizes, add 0.5
  const float cx = xSize / 2.f + ((xSize & 1) ? 0 : 0.5f),
              cy = ySize / 2.f + ((ySize & 1) ? 0 : 0.5f);

  // convert the angle from clockwise degrees to anti-clockwise radians
  const float theta = -angle * tau / 360.f;

  // to animate the waves, slowly move the phase of the sine towards the origin
  const float animTheta = -fmodf(millis() / 128.f, tau);

  int count = 0;
  for (auto &ring : rings) {
    // calculate the X & Y positions of this ring within the matrix
    int x = count / ySize;  // 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, etc
    int y = count % ySize;  // 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, etc

    // move this ring as if the whole matrix were centred on the origin
    float ox = x - cx;
    float oy = y - cy;

    // rotate this ring around the origin
    // https://en.wikipedia.org/wiki/Rotation_matrix
    float rx = ox * cosf(theta) - oy * sinf(theta);
    float ry = ox * sinf(theta) + oy * cosf(theta);

    // now the rotated Y value can be scaled and used as the phase of a sine
    // which is then mapped to give a result from 0 to 1
    float intensity = sinf(animTheta + ry * 1.75f) * 0.5f + 0.5f;

    // retrieve the HSV colour for this ring, and modulate V up to 255
    CHSV col = colours[count];
    col.value += intensity * (255 - col.value);

    // fill all the pixels on this ring with our chosen solid colour
    ring.fill_solid(col);
    count++;
  }
}