#include <FastLED.h>

const uint8_t kMatrixWidth = 32;
const uint8_t kMatrixHeight = 32;

#define NUM_LEDS    (kMatrixWidth * kMatrixHeight)
#define LED_PIN     2
#define COLOR_ORDER GRB
#define CHIPSET     WS2811
#define BRIGHTNESS  200

CRGB leds[NUM_LEDS + 1];

struct Circle {
  int x, y, r;
};

void setup() {
  Serial.begin(115200);
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(UncorrectedColor);
  FastLED.setBrightness(BRIGHTNESS);
}

void loop() {
  FastLED.clear();
  uint32_t ms = millis();
  float r = kMatrixHeight / 2 - 1;
  float x = kMatrixWidth / 2;
  float y = kMatrixHeight / 2;
  for (uint8_t count = 5; count--; ) {
    if (r >= 1) plotCircleAA({x, y, r}, CRGB(255, 255, 100));
    r /= 1.5;
    x -= r * sinf(ms / (100.f * count)) / 2.f;
    y -= r * cosf(ms / (100.f * count)) / 2.f;
  }
  FastLED.show();
}

uint16_t xy(uint8_t x, uint8_t y) {
  if (x >= kMatrixWidth || y >= kMatrixHeight)
    return NUM_LEDS;
  uint16_t i = x + (y * kMatrixWidth);
  return i;
}

// http://members.chello.at/%7Eeasyfilter/Bresenham.pdf § 7.2
void plotCircleAA(const Circle &circle, const CRGB &color) {
  int x = circle.r, y = 0; /* II. quadrant from bottom left to top right */
  int i, x2, e2, err = 2 - 2 * circle.r; /* error of 1.step */
  int r = 1 - err;
  for ( ; ; ) {
    i = 255 * abs(err + 2 * (x + y) - 2) / r; /* get blend value of pixel */
    CRGB c = color;
    fadeToBlackBy(&c, 1, i);
    leds[xy(circle.x + x, circle.y - y)] |= c; /* I. Quadrant */
    leds[xy(circle.x + y, circle.y + x)] |= c; /* II. Quadrant */
    leds[xy(circle.x - x, circle.y + y)] |= c; /* III. Quadrant */
    leds[xy(circle.x - y, circle.y - x)] |= c; /* IV. Quadrant */
    if (x == 0) break;
    e2 = err; x2 = x; /* remember values */
    if (err > y) { /* x step */
      i = 255 * (err + 2 * x - 1) / r; /* outward pixel */
      if (i < 255) {
        CRGB c2 = color;
        fadeToBlackBy(&c2, 1, i);
        leds[xy(circle.x + x, circle.y - y + 1)] |= c2;
        leds[xy(circle.x + y - 1, circle.y + x)] |= c2;
        leds[xy(circle.x - x, circle.y + y - 1)] |= c2;
        leds[xy(circle.x - y + 1, circle.y - x)] |= c2;
      }
      err -= --x * 2 - 1;
    }
    if (e2 <= x2--) { /* y step */
      i = 255 * (1 - 2 * y - e2) / r; /* inward pixel */
      if (i < 255) {
        CRGB c2 = color;
        fadeToBlackBy(&c2, 1, i);
        leds[xy(circle.x + x2, circle.y - y)] |= c2;
        leds[xy(circle.x + y, circle.y + x2)] |= c2;
        leds[xy(circle.x - x2, circle.y + y)] |= c2;
        leds[xy(circle.x - y, circle.y - x2)] |= c2;
      }
      err -= --y * 2 - 1;
    }
  }
}