/*
1.Первоначальный отрезок на каждом шаге рандомно либо продолжает расти либо разветвляется на два отрезка под углом 120 град.
2.Каждая ветка на следующем шаге ведет себя независимо и либо продолжается, либо ветвится.
3.Если ветка выходит за границы сектора, она останавливается (и не отрисовывается)
4.Каждый сектор повторяется в первоначальном виде, либо в симметрично отображенном 
 */
#include <Adafruit_GFX.h>
#include <Adafruit_GC9A01A.h>
#include <SPI.h>


#define TFT_DC 17
#define TFT_CS 16
/*
  SCL   --> 18 (SCK)
  SDA   --> 23 (MOSI)
  RST   --> EN
*/

Adafruit_GC9A01A tft(TFT_CS, TFT_DC);


#define BRANCH   1
#define STRAIGHT 2




// Константы координат смещения центра
constexpr int CENTER_X = 120;
constexpr int CENTER_Y = 120;
constexpr float r = 7.0;//размер шага 

struct Line {
  float x1;
  float y1;
  float x2;
  float y2;
};
std::vector<Line> openedLines;
std::vector<Line> nextLines;

constexpr int lineCount = 14;//количество шагов

void setup() {
// Инициализация генератора случайных чисел
  randomSeed(analogRead(32));
  tft.begin();
  tft.fillScreen(GC9A01A_YELLOW);
  // tft.cp437(true);
  tft.setRotation(1);  
}

void loop() {
  openedLines.clear();
 tft.fillScreen(GC9A01A_ORANGE);
  // рисуем первые линии
  Line firstLine;
  firstLine.x1 = 0.0;
  firstLine.y1 = 0.0;
  firstLine.x2 = r;
  firstLine.y2 = 0.0;
  openedLines.push_back(firstLine);
  //drawLineSnowflake(firstLine.x1, firstLine.y1, firstLine.x2, firstLine.y2);

  for (int i = 0; i < lineCount; ++i) {
    for (auto& line : openedLines) {
      int event = getRandomEvent();
      drawNextStep(line, event);
    }
    nextLines.swap(openedLines);
    nextLines.clear();
    delay(50);
  }
  delay(3000);
}

void addLine(float x1, float y1, float x2, float y2)
{
  Line newLine;
  newLine.x1 = x1;
  newLine.y1 = y1;
  newLine.x2 = x2;
  newLine.y2 = y2;

  if (drawLineSnowflake(x1, y1, x2, y2))
    nextLines.push_back(newLine);
}

void drawNextStep(Line &l, int event)
{
  // const calculation
  constexpr float sin60 = sin(PI / 3);
  constexpr float cos60 = cos(PI / 3);
  constexpr float xStraight = 2*r;
  constexpr float yStraight = 0;
  constexpr float xTurn = (1 + sin60)*r;
  constexpr float yTurn = cos60 * r; // *(-1) for right turn

  float tetta = atan((l.y2 - l.y1)/(l.x2 - l.x1));

  if (event == STRAIGHT)
  {
    // turn on tetta:
    float tempX = xStraight * cos(tetta) - yStraight * sin(tetta);
    float tempY = xStraight * sin(tetta) + yStraight * cos(tetta);

     // move to l.x1, l.y1
    float x = tempX + l.x1;
    float y = tempY + l.y1;

    addLine(l.x2, l.y2, x, y);
  }
  else if (event == BRANCH)
  {
    float tempXLeft = xTurn * cos(tetta) - yTurn * sin(tetta);
    float tempYLeft = xTurn * sin(tetta) + yTurn * cos(tetta);
    float tempXRight = xTurn * cos(tetta) + yTurn * sin(tetta);
    float tempYRight = xTurn * sin(tetta) - yTurn * cos(tetta);

    float xLeft = tempXLeft + l.x1;
    float yLeft = tempYLeft + l.y1;
    float xRight = tempXRight + l.x1;
    float yRight = tempYRight + l.y1;

    addLine(l.x2, l.y2, xLeft, yLeft);
    addLine(l.x2, l.y2, xRight, yRight);
  }
}

int getRandomEvent()
{
  long rand = random(60);// вероятность ветвления
  if (rand < 30)    // условие - вероятность ветвления
    return BRANCH;
  return STRAIGHT; 
}


////////////////////////////////////
//функция построения отрезка в полярных координатах с центром в центре экрана
void drawLinePol(int r1, float f1, int r2, float f2) {
  tft.drawLine(
    CENTER_X + cos(f1) * r1,
    CENTER_Y + sin(f1) * r1,
    CENTER_X + cos(f2) * r2,
    CENTER_Y + sin(f2) * r2,
    GC9A01A_BLACK
  );
}
//////////////////////////////////
//функция построения 12 отсимметриченных отрезков снежинки по базовому отрезку
void drawLineSnowflake (int r1, float f1, int r2, float f2) {
  if (r1 > 120 || r2 > 120 || r1 < 0 || r2 < 0 || f1 < 0.0 || f2 < 0.0 || f1 > PI / 6.0 || f2 > PI / 6) {
    return; //условие рисования базового отрезка в допустимых значениях аргументов (0-120 пикселей радиус, 0-PI/6 угол(сектор))
  }
  for (float i = 0.01; i < 2 * PI; i += PI / 3.0) {
    drawLinePol(r1, f1 + i, r2, f2 + i);
    drawLinePol(r1, 2 * PI - f1 + i, r2, 2 * PI - f2 + i);
  }
}
//////////////////////////////////

bool drawLineSnowflake (float x1, float y1, float x2, float y2) {
  float r1 = sqrt(x1*x1 + y1*y1);
  float r2 = sqrt(x2*x2 + y2*y2);
  float f1 = atan(y1/x1);
  float f2 = atan(y2/x2);

  if (r1 > 120 || r2 > 120 || r1 < 0 || r2 < 0 || f1 < 0.0 || f2 < 0.0 || f1 > PI / 6.0 || f2 > PI / 6) {
    return false; //условие рисования базового отрезка в допустимых значениях аргументов (0-120 пикселей радиус, 0-PI/6 угол(сектор))
  }
  for (float i = 0.01; i < 2 * PI; i += PI / 3.0) {
    drawLinePol(r1, f1 + i, r2, f2 + i);
    drawLinePol(r1, 2 * PI - f1 + i, r2, 2 * PI - f2 + i);
  }
  return true;
}
