#include <FastLED.h>
#define WIDTH 16 //Ширина матрицы
#define HEIGHT 16 //Высота матрицы
#define LED_PIN 13 //Пин подключения матрицы на ардуино
#define BUTTON_PIN 2 //Пин подключения кнопки на ардуино
#define NUM_LEDS (WIDTH * HEIGHT) //Общее количество светодиодов
#define BRIGHTNESS 255 //Яркость (0-255)
#define MAX_COUNT_BALLOONS 5 //Максимальное количество шариков (1-5)
#define INTENSITY 45 //Интенсивность (0-50)
#define MAX_CIRCLES 3 //Максимальное количество окружностей
char ALGORITHM = 1; //1 - Шарики, 2 - Капли, 3 - Матрица
char MODE = 1; //Режим
char lenPush = 0; //Длина нажатия
CRGB leds[NUM_LEDS]; //Массив для светодиодов
int balloons[MAX_COUNT_BALLOONS][3] = {-1}; //Инициализация шариков {x, y, angle}
char balloons_way[MAX_COUNT_BALLOONS][100][2] = {-1};//{x, y}
float HYPOTENUSE = sqrt(WIDTH * WIDTH + HEIGHT * HEIGHT) + 2;
char COUNT_BALLOONS = 3; //Настоящее количество шариков
char circles[MAX_CIRCLES][3] = {0}; //Инициализация окружностей {x, y, r}
float MAX_R = sqrt(WIDTH * WIDTH + HEIGHT * HEIGHT) + 1;
char LEN_LINE = 6; //Длина линии
void setup() {
FastLED.addLeds<WS2811, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
pinMode(BUTTON_PIN, INPUT_PULLUP);
randomSeed(analogRead(0));
CreatingBalloons();
}
void CreatingBalloons() {
for (char i = 0; i < MAX_COUNT_BALLOONS; i++) {
for (char q = 0; q < 3; q++) {
balloons[i][q] = -1;
}
}
for (char i = 0; i < MAX_COUNT_BALLOONS; i++) {
for (char q = 0; q < 100; q++) {
for (char w = 0; w < 2; w++){
balloons_way[i][q][w] = -1;
}
}
}
for (char i = 0; i < COUNT_BALLOONS; i++) {//Создание шариков
balloons[i][0] = random(1, WIDTH - 1);
balloons[i][1] = random(1, HEIGHT - 1);
balloons[i][2] = random(-180, 181);
}
}
void DrawCircle(char x0, char y0, char radius) {
char x = 0;
char y = radius;
char delta = 1 - 2 * radius;
char error = 0;
char res;
if (MODE == 1)
res = radius * 10;
while(y >= 0) { //Алгоритм Брезенхема для окружностей
if (MODE == 2)
res = ((x + WIDTH) * (y + HEIGHT));
if (x0 + x >= 0 && x0 + x < WIDTH && y0 + y >= 0 && y0 + y < HEIGHT)
leds[XY(x0 + x, y0 + y)].setHue(res);
if (x0 + x >= 0 && x0 + x < WIDTH && y0 - y >= 0 && y0 - y < HEIGHT)
leds[XY(x0 + x, y0 - y)].setHue(res);
if (x0 - x >= 0 && x0 - x < WIDTH && y0 + y >= 0 && y0 + y < HEIGHT)
leds[XY(x0 - x, y0 + y)].setHue(res);
if (x0 - x >= 0 && x0 - x < WIDTH && y0 - y >= 0 && y0 - y < HEIGHT)
leds[XY(x0 - x, y0 - y)].setHue(res);
error = 2 * (delta + y) - 1;
if(delta < 0 && error <= 0) {
++x;
delta += 2 * x + 1;
continue;
}
if(delta >= 0 && error > 0) {
--y;
delta += 1 - 2 * y;
continue;
}
++x;
delta += 2 * (x - y);
--y;
}
}
void NextStep() { //Функция обновления состояния доски
if (ALGORITHM == 1) {
for (char i = 0; i < HEIGHT; i++) { //Очистка светодиодов
for (char j = 0; j < WIDTH; j++) {
leds[XY(i, j)] = CRGB::Black;
}
}
for (char i = 0; i < COUNT_BALLOONS; i++) {
bool line_drawn = false; //Начерчена ли линия?
for (char q = 0; q < 100; q++) {
if (balloons_way[i][q][0] != -1 && balloons_way[i][q][1] != -1) {
line_drawn = true;
break;
}
}
if (line_drawn == false) { //Если линия не начерчена, вычисляем её до границы
bool flag_qwe = false;
if (balloons[i][0] == 0 && balloons[i][1] == 0)
balloons[i][2] = random(10, 81);
else if (balloons[i][0] == 0 && balloons[i][1] == HEIGHT - 1)
balloons[i][2] = random(-80, -9);
else if (balloons[i][0] == WIDTH - 1 && balloons[i][1] == HEIGHT - 1)
balloons[i][2] = random(-170, -101);
else if (balloons[i][0] == WIDTH - 1 && balloons[i][1] == 0)
balloons[i][2] = random(100, 171);
else if (balloons[i][0] == 0 || balloons[i][0] == HEIGHT - 1)
balloons[i][2] = 180 - balloons[i][2];
else if (balloons[i][1] == 0 || balloons[i][1] == WIDTH - 1)
balloons[i][2] = -balloons[i][2];
char x1 = balloons[i][0];
char y1 = balloons[i][1];
char x2 = x1 + HYPOTENUSE * cos(balloons[i][2] * PI / 180);
char y2 = y1 + HYPOTENUSE * sin(balloons[i][2] * PI / 180);
const int deltaX = abs(x2 - x1);
const int deltaY = -abs(y2 - y1);
const int signX = x1 < x2 ? 1 : -1;
const int signY = y1 < y2 ? 1 : -1;
int error = deltaX + deltaY;
for (char q = 0; q < 100; q++) {
if (x1 <= -1 || y1 <= -1 || x1 >= WIDTH || y1 >= HEIGHT)//Если достигли границы, заканчивам вычисление
break;
balloons_way[i][q][0] = x1;
balloons_way[i][q][1] = y1;
int error2 = error * 2;
if(error2 >= deltaY) {
error += deltaY;
x1 += signX;
}
if(error2 <= deltaX) {
error += deltaX;
y1 += signY;
}
}
}
for (char q = 0; q < 100; q++) { //Передвигаем шарик на следующую позицию
if (balloons_way[i][q][0] != -1 && balloons_way[i][q][1] != -1) {
balloons[i][0] = balloons_way[i][q][0];
balloons[i][1] = balloons_way[i][q][1];
balloons_way[i][q][0] = -1;
balloons_way[i][q][1] = -1;
break;
}
}
if (i == 0)
leds[XY(balloons[i][0], balloons[i][1])] = CRGB(255, 0, 0);
else if (i == 1)
leds[XY(balloons[i][0], balloons[i][1])] = CRGB(0, 255, 0);
else if (i == 2)
leds[XY(balloons[i][0], balloons[i][1])] = CRGB(0, 0, 255);
else if (i == 3)
leds[XY(balloons[i][0], balloons[i][1])] = CRGB(139, 0, 255);
else if (i == 4)
leds[XY(balloons[i][0], balloons[i][1])] = CRGB(255, 255, 0);
}
}
else if (ALGORITHM == 2) {
for (char i = 0; i < HEIGHT; i++) { //Очистка светодиодов
for (char j = 0; j < WIDTH; j++) {
leds[XY(i, j)] = CRGB::Black;
}
}
if (random(50 - INTENSITY) == 0) { //Случайная точка центра окружности
for (char i = 0; i < MAX_CIRCLES; i++) {
if (circles[i][2] == 0) {
circles[i][0] = random(WIDTH);
circles[i][1] = random(HEIGHT);
circles[i][2] = MAX_R;
break;
}
}
}
float x1, y1, step;
char x, y, r;
for (char i = 0; i < MAX_CIRCLES; i++) {
if (circles[i][2] != 0) {
DrawCircle(circles[i][0], circles[i][1], MAX_R - circles[i][2]);
circles[i][2] -= 1;
}
}
}
else if (ALGORITHM == 3) {
for (char i = 0; i < HEIGHT; i++) {
for (char j = 0; j < WIDTH; j++) {
if (leds[XY(i, j)] != CRGB::Black) {
if (i != 0)
leds[XY(i - 1, j)] = leds[XY(i, j)];
if (leds[XY(i, j)].g < 70 / LEN_LINE)
leds[XY(i, j)].g = 0;
else
leds[XY(i, j)].g -= 70 / LEN_LINE;
}
}
}
for (char i = 0; i < random(1, 4); i++) {
if (random(50 - INTENSITY) == 0) { //Случайная точка начала линии
char rand_wigth = random(WIDTH);
if (leds[XY(HEIGHT - 1, rand_wigth)] == CRGB::Black && leds[XY(HEIGHT - 1, rand_wigth + 1)] == CRGB::Black && leds[XY(HEIGHT - 1, rand_wigth - 1)] == CRGB::Black )
leds[XY(HEIGHT - 1, rand_wigth)] = CRGB(0, 100, 0);
}
}
}
}
int XY(char x, char y) { //Функция преобразования координат
//Для wokwi
return ((WIDTH - x) * WIDTH - (HEIGHT - y));
//Для реальной матрицы
/*if (x % 2 == 0)
return (x * WIDTH + y);
return (x * WIDTH + (WIDTH - y - 1));*/
}
void SetNextAlgorithm() { //Смена алгоритма
for (char i = 0; i < WIDTH; i++) {
for (char j = 0; j < HEIGHT; j++) {
leds[XY(i, j)] = CRGB::Black;
}
}
if (ALGORITHM == 1)
ALGORITHM = 2;
else if (ALGORITHM == 2)
ALGORITHM = 3;
else if (ALGORITHM == 3) {
ALGORITHM = 1;
CreatingBalloons();
}
}
void SetNextMode() { //Смена режима
for (char i = 0; i < WIDTH; i++) {
for (char j = 0; j < HEIGHT; j++) {
leds[XY(i, j)] = CRGB::Black;
}
}
if (MODE == 1) {
MODE = 2;
COUNT_BALLOONS = 5;
}
else if (MODE == 2) {
MODE = 1;
COUNT_BALLOONS = 3;
}
if (ALGORITHM == 1)
CreatingBalloons();
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW)
lenPush += 1;
else if (digitalRead(BUTTON_PIN) != LOW && lenPush != 0) {
if (lenPush < 20 && ALGORITHM == 1 || lenPush < 10 && ALGORITHM == 2 || lenPush < 13 && ALGORITHM == 3)
SetNextMode();
else
SetNextAlgorithm();
lenPush = 0;
}
FastLED.show();
if (ALGORITHM == 1)
delay(50);
else if (ALGORITHM == 2)
delay(100);
else if (ALGORITHM == 3)
delay(80);
NextStep(); //Переход к следующему состоянию
}