/*  File: Voronoi diagram - animated.
 
    By: Andrew Tuline
 
    Date: June 2021
 
    Slowest implementation in the world of a Voronoi diagram for an Arduino UNO/Nano and the FastLED display library.
    Unfortunately, it's not cool looking like the routines made by Stepko or ldirko.
 
    Reference:
 
    https://en.wikipedia.org/wiki/Voronoi_diagram
 
    Caveat: There are probably still one or more bugs in this, but it was a fun exercise.
 
*/


#include <FastLED.h>

#define matrixWidth 16
#define matrixHeight 16

#define matrixSerpentine true

#define LED_DT 12

#define LED_TYPE WS2812
#define NUM_LEDS matrixWidth*matrixHeight

uint8_t max_bright = 255;

struct CRGB leds[NUM_LEDS];

const uint8_t maxStars = matrixWidth * matrixHeight / 64;

typedef struct star {
  uint8_t xStart;
  uint8_t yStart;
  uint8_t x;
  uint8_t y;
  uint8_t colr;
} Star;

Star stars[maxStars];


void setup() {
  Serial.begin(115200);
  LEDS.addLeds<WS2812, LED_DT, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(max_bright);

  for (int i = 0; i < maxStars; i++) {
    stars[i].xStart = random8(matrixWidth) / 2;
    stars[i].yStart = random8(matrixHeight) / 2;
    stars[i].colr = random8();
  }
} // setup()


void loop() {
  voronoi();
  Serial.print(LEDS.getFPS());
  Serial.println(" FPS.");
  FastLED.show();
} // loop()


void voronoi() {

  random16_set_seed(4832);                 // I use this to make repeating random numbers for the phase shift. Think of it as a 0 byte random() array.

  for (int i = 0; i < maxStars; i++) {
    stars[i].x = stars[i].xStart + beatsin8(7, 0, matrixWidth, 0, random8()) / 2;
    stars[i].y = stars[i].yStart + beatsin8(8, 0, matrixHeight, 0, random8()) / 2;
  }

  for (int x = 0; x < matrixWidth; x++) {
    for (int y = 0; y < matrixHeight; y++) {
      float dist = 254;
      uint8_t scolr = 0;
      for (int star = 0; star < maxStars; star++) {

        // Get length to each star to the current led.
        float thisDist = sqrt(abs(stars[star].x * stars[star].x - x * x) + abs(stars[star].y * stars[star].y - y * y));

        // For the closest star, get current x, y and colour of that star's area.
        if (thisDist < dist) {
          dist = thisDist;
          scolr = stars[star].colr;
        } // if
      } // for star
      leds[XY(x, y)] = CHSV(scolr, 255, 128 / (dist / 4));
    } // for y
  } // for x

  for (int star = 0; star < maxStars; star++) leds[XY(stars[star].x, stars[star].y)] = CHSV(0, 0, 255);
  
} // voronoi()


uint16_t XY( uint8_t x, uint8_t y) {            // Returns with the wiring layout.

  uint16_t i;

  if ( matrixSerpentine == false) {
    i = (y * matrixWidth) + x;
  }

  if ( matrixSerpentine == true) {
    if ( y & 0x01) {
      uint8_t reverseX = (matrixWidth - 1) - x;
      i = (y * matrixWidth) + reverseX;
    } else {
      i = (y * matrixWidth) + x;
    }
  }
  return i;

} // XY()