//Yaroslaw Turbin 15.06.2023
//https://vk.com/ldirko
//https://www.reddit.com/user/ldirko/
//https://twitter.com/ldir_ko
//https://www.youtube.com/@ldirldir 

//license Creative Commons Attribution 4.0 International (CC-BY-4.0)


#include "FastLED.h"
 
#define NUM_LEDS 16*7
#define NUM_LEDS_BUFF 600

// LEDs pin
#define DATA_PIN 3   
// #define LED_TYPE    WS2812B
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
 
// LED brightness
#define BRIGHTNESS 255
#define MAX_POWER_MILLIAMPS 800 
 
// Define the array of leds
CRGB leds[NUM_LEDS+1];
CRGB leds_buff[NUM_LEDS_BUFF+1];

#include "tables.h"


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


byte pixelMapX_norm[NUM_LEDS+1];
byte pixelMapY_norm[NUM_LEDS+1]; 
int lookup_table_from_buff_to_leds [NUM_LEDS];

void GammaCorrection(){   //gamma correction function 
 byte r,g,b;
  for (uint16_t i=0; i<NUM_LEDS; i++){
    r=leds[i].r;
    g=leds[i].g;
    b=leds[i].b;
    leds[i].r = pgm_read_byte(exp_gamma + r);
    leds[i].g = pgm_read_byte(exp_gamma + g);
    leds[i].b = pgm_read_byte(exp_gamma + b);
  }
}

CRGB GammaCorrection_color(CRGB color){   //gamma correction function 
  byte r,g,b;
  r=color.r;
  g=color.g;
  b=color.b;
  color.r = pgm_read_byte(exp_gamma + r);
  color.g = pgm_read_byte(exp_gamma + g);
  color.b = pgm_read_byte(exp_gamma + b);
  return (color);
}

int XY_buff (int x, int y, int _buffX ){
  int index = y*_buffX + x;
  return( index); 
}

int getMinVal (int map[]){
  int minVal = map[0];
  for (int i = 1; i < NUM_LEDS; i++) {
    if (map[i]<minVal) minVal = map[i];
  }
  return minVal;
}

int getMaxVal (int map[]){
  int maxVal = map[0];
  for (int i = 1; i < NUM_LEDS; i++) {
    if (map[i]>maxVal) maxVal = map[i];
  }
  return maxVal;
}

void normMapsGenerator_From_Coords (int Xres, int Yres, int *pixelMapX, int *pixelMapY) {
  static byte init=1;
  byte _buffX, _buffY;

  if (!init) return; 

  double timestamp = millis();

  if ((Xres*Yres) > NUM_LEDS_BUFF) 
    {Serial.println ("too big resolution! _buffX and _buffY set to 24x24"); _buffX = 24; _buffY = 24;} 
      else {_buffX = Xres; _buffY = Yres;}

  int minValueX = getMinVal (pixelMapX);
  int maxValueX = getMaxVal (pixelMapX);
  int minValueY = getMinVal (pixelMapY);
  int maxValueY = getMaxVal (pixelMapY);
  int Xrange = maxValueX-minValueX;
  int Yrange = maxValueY-minValueY;
  
  for (int i = 0; i < NUM_LEDS; i++) {
    int X_norm = (float)(pixelMapX[i]-minValueX)/Xrange*(_buffX-1)+0.5;  // normalize pixel coord to buffer resolution
    int Y_norm = (float)(pixelMapY[i]-minValueY)/Yrange*(_buffY-1)+0.5;  // normalize pixel coord to buffer resolution
    pixelMapX_norm [i] = X_norm;
    pixelMapY_norm [i] = Y_norm;
    int buffIndex = XY_buff(X_norm, Y_norm, _buffX);
    lookup_table_from_buff_to_leds[i]=buffIndex;
  }

  pixelMapX_norm[NUM_LEDS]=_buffX;  //save resolution X
  pixelMapY_norm[NUM_LEDS]=_buffY;  //save resolution Y

  Serial.println("normalization map generated");
  Serial.print("total leds "); Serial.println(NUM_LEDS);
  Serial.print("buffer resolution X: ");
  Serial.println(_buffX);
  Serial.print("buffer resolution Y: ");
  Serial.println(_buffY);

  Serial.print("norm mapX: ");
   for (int i = 0; i < NUM_LEDS; i++) {
    int Xnorm4 = pixelMapX_norm[i];
    Serial.print(Xnorm4); Serial.print(",");
    
  }
  Serial.println();

  Serial.print("norm mapY: ");
   for (int i = 0; i < NUM_LEDS; i++) {
    int Ynorm4 = pixelMapY_norm[i];
    Serial.print(Ynorm4); Serial.print(",");
  }
  Serial.println();

  Serial.println("lookup_table_from_buff_to_leds:");
   for (int i = 0; i < NUM_LEDS; i++) {
    int index = lookup_table_from_buff_to_leds[i];
    Serial.print(index); Serial.print(",");
  }

  Serial.println(); 
  Serial.print("tables generated in sec: ");
  Serial.print((millis()-timestamp)/1000);
  init = 0;
}


void Sin_plasma (byte *pixelMapX_norm, byte *pixelMapY_norm) {
  double t=millis()/200.; 
  int t1 = t*6;
  int t2 = t*7;
  
//   byte _buffX = pixelMapX_norm[NUM_LEDS];  
//   byte _buffY = pixelMapY_norm[NUM_LEDS];  
//   int index =0;
//   for (int j = 0; j < _buffY; j++) { 
//     for (int i = 0; i < _buffX; i++) {
//       byte ind = sin8(i*8+sin8(i*2+t1))/2+sin8(j*8+sin8(j*2+t2)/2);
//       leds_buff [index++].setHue(ind);
//     }
//   }

//  for (int i = 0; i < NUM_LEDS; i++) {
//     leds[i] = leds_buff[lookup_table_from_buff_to_leds[i]];
//   }
 
  for (int z = 0; z < NUM_LEDS; z++) {   //fastest method
    byte i = pixelMapX_norm[z]; 
    byte j = pixelMapY_norm[z];
    byte ind = sin8(i*8+sin8(i*2+t1))/2+sin8(j*8+sin8(j*2+t2)/2);
    leds[z].setHue(ind);
  }

}


void fire2021 (byte *pixelMapX_norm, byte *pixelMapY_norm){
  int  a = millis();
  int  a1 = a/3; 
  byte _buffY = pixelMapY_norm[NUM_LEDS];
  for (int z = 0; z < NUM_LEDS; z++) {   //fastest method
    byte i = pixelMapX_norm[z]; 
    byte j = pixelMapY_norm[z];
    CRGB color = HeatColor(qsub8 (inoise8 (i * 60, j * 60+a, a1), abs8(j - (_buffY-1)) * 255 / (_buffY+4)));
    leds[z] = color;
  }
}


void RGB_hiphotic(byte *pixelMapX_norm, byte *pixelMapY_norm) {

  int a = millis()/8.;
  
  CRGB color;

  for (int z = 0; z < NUM_LEDS; z++) {   //fastest method
    byte x = pixelMapX_norm[z]; 
    byte y = pixelMapY_norm[z];
    color.b=sin8((x-8)*cos8((y+20)*4)/4+a);
    color.g=(sin8(x*16+a/3)+cos8(y*8+a/2))/2;
    color.r=sin8(cos8(x*8+a/3)+sin8(y*8+a/4)+a);  
    color = GammaCorrection_color(color);
    nblend (leds[z], color, 64); 
  }
}


void Distortion_Waves(byte *pixelMapX_norm, byte *pixelMapY_norm) {
  byte speed = 5; 
  uint8_t w = 2;
  uint8_t scale = 4;
  CRGB color;

  byte _buffX = pixelMapX_norm[NUM_LEDS];  
  byte _buffY = pixelMapY_norm[NUM_LEDS];  

  int a=millis()/24;
  uint16_t a2=a/2;
  uint16_t a3=a/3;

  uint16_t cx =  beatsin8 (10-speed,0,_buffX)*scale;
  uint16_t cy =  beatsin8 (12-speed,0,_buffY)*scale;
  uint16_t cx1 = beatsin8 (13-speed,0,_buffX)*scale;
  uint16_t cy1 = beatsin8 (15-speed,0,_buffY)*scale;
  uint16_t cx2 = beatsin8 (17-speed,0,_buffX)*scale;
  uint16_t cy2 = beatsin8 (14-speed,0,_buffY)*scale;
  
    for (int z = 0; z < NUM_LEDS; z++) {
      byte x = pixelMapX_norm[z]; 
      byte y = pixelMapY_norm[z];
      int x_scale=x*scale; 
      int y_scale=y*scale; 
      byte rdistort = cos_wave [(cos_wave[((x<<3)+a )&255]+cos_wave[((y<<3)-a2)&255]+a3   )&255]>>1; 
      byte gdistort = cos_wave [(cos_wave[((x<<3)-a2)&255]+cos_wave[((y<<3)+a3)&255]+a+32 )&255]>>1; 
      byte bdistort = cos_wave [(cos_wave[((x<<3)+a3)&255]+cos_wave[((y<<3)-a) &255]+a2+64)&255]>>1; 

      byte valueR = rdistort+ w*  (a- ( ((x_scale - cx) *  (x_scale - cx) +  (y_scale - cy) *   (y_scale - cy))>>7  ));
      byte valueG = gdistort+ w*  (a2-( ((x_scale - cx1) * (x_scale - cx1) + (y_scale - cy1) *  (y_scale - cy1))>>7 ));
      byte valueB = bdistort+ w*  (a3-( ((x_scale - cx2) * (x_scale - cx2) + (y_scale - cy2) *  (y_scale - cy2))>>7 ));
      
      color.r = cos_wave [valueR];
      color.g = cos_wave [valueG];
      color.b = cos_wave [valueB];
      color = GammaCorrection_color(color);
      nblend (leds[z], color, 16); 
    }
}

void testRadial (byte *pixelMapX_norm, byte *pixelMapY_norm){
  double time= millis()/8.0; 
  int a = time;
  int a4= a/4.;
  for (int z = 0; z < NUM_LEDS; z++) {
      byte i = pixelMapX_norm[z]; 
      byte j = pixelMapY_norm[z];
      leds[z].setHue (((i<<3)+(sin8((j<<3)+a))>>1)+a4); 
  }
  
}

void loop() {
 
  normMapsGenerator_From_Coords (16,16, pixelMapX, pixelMapY);
  // normMapsGenerator_From_Coords (20,8, pixelMapX_Cilindr, pixelMapY_Cilindr);


  // testRadial (pixelMapX_norm, pixelMapY_norm);
  // Sin_plasma (pixelMapX_norm, pixelMapY_norm);
  // fire2021 (pixelMapX_norm, pixelMapY_norm);
  Distortion_Waves(pixelMapX_norm, pixelMapY_norm);
  // RGB_hiphotic(pixelMapX_norm, pixelMapY_norm);
  
  FastLED.show();
}
FPS: 0
Power: 0.00W