//source: https://editor.soulmatelights.com/gallery/user/348-tara

#include "FastLED.h"
#define DATA_PIN 2
#define BRIGHTNESS 255
#define NUM_LEDS 256
#define LED_COLS 16
#define LED_ROWS 16
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
//#define FRAMES_PER_SECOND 60
const uint8_t kMatrixWidth = 16;
const uint8_t kMatrixHeight = 16;

const bool    kMatrixSerpentineLayout = false;
#define COUNT 4  // default: 4 
float posX[COUNT];
float posY[COUNT];
uint8_t aimX[COUNT];
uint8_t aimY[COUNT];
float Sradius[COUNT];
byte Shue[COUNT];;
CRGB color[COUNT];
CRGB color1[COUNT];
CRGB color2[COUNT];
CRGB color3[COUNT];
CRGB color4[COUNT];
CRGB color5[COUNT];
CRGB color6[COUNT];
CRGB color7[COUNT];
CRGB color8[COUNT];
byte starPoints[COUNT];
bool run[COUNT];
bool load = true;
const byte Smax = max(LED_COLS, LED_ROWS);
const float speedFactor = NUM_LEDS/255; 

extern const TProgmemRGBPalette16 ZAlcoholFireColors_p FL_PROGMEM = {CRGB::Black, 0x000033, 0x000066, 0x000099, 0x0000cc, CRGB::Blue, 0x0026ff, 0x004cff, 0x0073ff, 0x0099ff, CRGB::DeepSkyBlue, 0x1bc2fe, 0x36c5fd, 0x51c8fc, 0x6ccbfb, CRGB::LightSkyBlue};  //* Blue
extern const TProgmemRGBPalette16 ZRubidiumFireColors_p FL_PROGMEM = {CRGB::Red, 0x0f001a, 0x1e0034, 0x2d004e, 0x3c0068, CRGB::Red, CRGB::Indigo, CRGB::Indigo, CRGB::Indigo, CRGB::Indigo, CRGB::Indigo, 0x3c0084, 0x2d0086, 0x1e0087, 0x0f0089, CRGB::Red};        //* Indigo

// const float SSspeed = (float) NUM_LEDS / 255; // 255 Speed normalization for different matrix sizes
const float addRadius = (float) NUM_LEDS / 400; //  400 Normalization of increase in radius


void SdrawPixelXYF(float x, float y, CRGB color) {
  if (x < 0 || y < 0 || x > ((float) LED_COLS - 1) || y > ((float) LED_ROWS - 1)) return;
  uint8_t xx = (x - (int) x) * 255, yy = (y - (int) y) * 255, ix = 255 - xx, iy = 255 - yy;
  // calculate the intensities for each affected pixel
  #define WU_WEIGHT(a, b)((uint8_t)(((a) * (b) + (a) + (b)) >> 8))
  uint8_t wu[4] = {
    WU_WEIGHT(ix, iy),
    WU_WEIGHT(xx, iy),
    WU_WEIGHT(ix, yy),
    WU_WEIGHT(xx, yy)
  };
  // multiply the intensities by the colour, and saturating-add them to the pixels
  for (uint8_t i = 0; i < 4; i++) {
    int16_t xn = x + (i & 1), yn = y + ((i >> 1) & 1);
    CRGB clr = leds[XY(xn, yn)];
    clr.r = qadd8(clr.r, (color.r * wu[i]) >> 8);
    clr.g = qadd8(clr.g, (color.g * wu[i]) >> 8);
    clr.b = qadd8(clr.b, (color.b * wu[i]) >> 8);
    leds[XY(xn, yn)] = clr;
  }
}

void SdrawCircleF(float x0, float y0, float radius,
  const CRGB & color, float step = 0.25) {
  float a = radius, b = 0.;
  float radiusError = step - a;
  
  if (radius <= step * 2) {
    SdrawPixelXYF(x0, y0, color);
    return;
  }
  
  while (a >= b) {
    SdrawPixelXYF(a + x0, b + y0, color);
    SdrawPixelXYF(b + x0, a + y0, color);
    SdrawPixelXYF(-a + x0, b + y0, color);
    SdrawPixelXYF(-b + x0, a + y0, color);
    SdrawPixelXYF(-a + x0, -b + y0, color);
    SdrawPixelXYF(-b + x0, -a + y0, color);
    SdrawPixelXYF(a + x0, -b + y0, color);
    SdrawPixelXYF(b + x0, -a + y0, color);
//+++++++++++++++++++++

    b += step;
    if (radiusError < 0.)
      radiusError += 2. * b + step;
    else {
      a -= step;
      radiusError += 2 * (b - a + step);
    }
  }
}

void drawLineF(float x1, float y1, float x2, float y2,
  const CRGB & color) {
  float deltaX = fabs(x2 - x1);
  float deltaY = fabs(y2 - y1);
  float error = deltaX - deltaY;
  
  float signX = x1 < x2 ? 0.5 : -0.5;
  float signY = y1 < y2 ? 0.5 : -0.5;
  
  while (x1 != x2 || y1 != y2) {
    if ((signX > 0. && x1 > x2 + signX) || (signX < 0. && x1 < x2 + signX))
      break;
    if ((signY > 0. && y1 > y2 + signY) || (signY < 0. && y1 < y2 + signY))
      break;
    SdrawPixelXYF(x1, y1, color);
    float error2 = error;
    if (error2 > -deltaY) {
      error -= deltaY;
      x1 += signX;
    }
    if (error2 < deltaX) {
      error += deltaX;
      y1 += signY;
    }
  }
}


void drawStarF(float x, float y, float biggy, float little, int16_t points, float dangle, CRGB color) {
  float radius2 = 255.0 / points;
  for (int i = 0; i < points; i++) {
   // drawLineF(x + ((little * (sin8(i * radius2 + radius2 / 8 - dangle) - 128.0)) / 128), y + ((little * (cos8(i * radius2 + radius2 / 8 - dangle) - 128.0)) / 128), x + ((biggy * (sin8(i * radius2 - dangle) - 128.0)) / 128), y + ((biggy * (cos8(i * radius2 - dangle) - 128.0)) / 128), color);
    //drawLineF(x + ((little * (sin8(i * radius2 - radius2 / 8 - dangle) - 128.0)) / 128), y + ((little * (cos8(i * radius2 - radius2 / 8 - dangle) - 128.0)) / 128), x + ((biggy * (sin8(i * radius2 - dangle) - 128.0)) / 128), y + ((biggy * (cos8(i * radius2 - dangle) - 128.0)) / 128), color);
    drawLineF(x + ((little * (sin8(i * radius2 + radius2 / 8 ) - 128.0)) / 128), y + ((little * (cos8(i * radius2 + radius2 / 8  ) - 128.0)) / 128), x + ((biggy * (sin8(i * radius2 ) - 128.0)) / 128), y + ((biggy * (cos8(i * radius2 ) - 128.0)) / 128), color);
    drawLineF(x + ((little * (sin8(i * radius2 - radius2 / 8 ) - 128.0)) / 128), y + ((little * (cos8(i * radius2 - radius2 / 8 ) - 128.0)) / 128), x + ((biggy * (sin8(i * radius2 ) - 128.0)) / 128), y + ((biggy * (cos8(i * radius2 ) - 128.0)) / 128), color);
  }

}

void aimChange(byte idx) {
  randomSeed(millis());
  aimX[idx] = random(LED_COLS / 2 , LED_COLS / 2 );
  aimY[idx] = random(LED_ROWS / 2 , LED_ROWS / 2 );
  Sradius[idx] = 1;
  Shue[idx] = random8();
  color[idx] = ColorFromPalette(RainbowStripeColors_p, Shue[idx],255); //CHSV(hue, 255, 255);
  color1[idx] = ColorFromPalette(PartyColors_p, Shue[idx], 255); //CHSV(hue, 255, 255);
  color2[idx] = ColorFromPalette(LavaColors_p, Shue[idx], 255); //CHSV(hue, 255, 255);
  color3[idx] = ColorFromPalette(ZRubidiumFireColors_p, Shue[idx], 255); //CHSV(hue, 255, 255);
  color4[idx] = ColorFromPalette(CloudColors_p, Shue[idx],255); //CHSV(hue, 255, 255);
  color5[idx] = ColorFromPalette(ForestColors_p, Shue[idx], 255); //CHSV(hue, 255, 255);
  color6[idx] = ColorFromPalette(RainbowColors_p, Shue[idx], 255); //CHSV(hue, 255, 255);
  color7[idx] = ColorFromPalette(ZAlcoholFireColors_p, Shue[idx],255); //CHSV(hue, 255, 255);
  color8[idx] = ColorFromPalette(OceanColors_p, Shue[idx],255); //CHSV(hue, 255, 255);
  
  starPoints[idx] = random(4, 7);
  run[idx] = false;
}

void setup() {
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS); //setCorrection(TypicalLEDStrip);
  //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
 //Serial.begin(115200);
}

void loop() 
{
  fadeToBlackBy(leds, NUM_LEDS, 50);
  
  if (load) {
    for (byte i = 0; i < COUNT; i++) {
      aimChange(i);
    }
    run[0] = true;
    load = false;
  }
  for (byte i = 0; i < COUNT; i++) {
    if (Sradius[i] > Smax / COUNT) {
      run[i + 1 < COUNT ? i + 1 : 0] = true;
    }
    
    static float angle[COUNT];
    
    if (run[i]) {
      Sradius[i] += addRadius * speedFactor;
      angle[i] += Sradius[i];
      switch (Shue[i] % 10) {
        case 0:
          SdrawCircleF(aimX[i], aimY[i], Sradius[i], color[i]); // рисуем круг
          break;
        case 1:
          drawStarF(aimX[i], aimY[i], 1.3 * Sradius[i], Sradius[i], 4, angle[i], color[i]); // рисуем квадрат
          break;
        case 2:
          drawStarF(aimX[i], aimY[i], 2 * Sradius[i], Sradius[i], starPoints[i], angle[i], color[i]); // рисуем звезду
          break;
          case 3:
          SdrawCircleF(aimX[i], aimY[i], Sradius[i], color1[i]); // рисуем круг
          break;
        case 4:
          drawStarF(aimX[i], aimY[i], 1.3 * Sradius[i], Sradius[i], 4, angle[i], color2[i]); // рисуем квадрат
          break;
        case 5:
          drawStarF(aimX[i], aimY[i], 2 * Sradius[i], Sradius[i], starPoints[i], angle[i], color3[i]); // рисуем звезду
          break;
           case 6:
          SdrawCircleF(aimX[i], aimY[i], Sradius[i], color4[i]); // рисуем круг
          break;
        case 7:
          drawStarF(aimX[i], aimY[i], 1.3 * Sradius[i], Sradius[i], 4, angle[i], color5[i]); // рисуем квадрат
          break;
        case 8:
          drawStarF(aimX[i], aimY[i], 2 * Sradius[i], Sradius[i], starPoints[i], angle[i], color6[i]); // рисуем звезду
          break;
          case 9:
         // draw7();
          break;
      }
    }
    if (Sradius[i] > Smax) aimChange(i);
  }
  delay(16); //fps ~= 60
  FastLED.show();

}
 
uint16_t XY( uint8_t x, uint8_t y)
{
  uint16_t i;
  if( kMatrixSerpentineLayout == false) {
    i = (y * kMatrixWidth) + x;
  }
  if( kMatrixSerpentineLayout == true) {
    if( y & 0x01) {
      // Odd rows run backwards
      uint8_t reverseX = (kMatrixWidth - 1) - x;
      i = (y * kMatrixWidth) + reverseX;
    } else {
      // Even rows run forwards
      i = (y * kMatrixWidth) + x;
    }
  }
  return i;
}