Генератор роста снежинок (ГРСН)

Вот решил пока без ИИ попробовать :slight_smile:

Исходная база-скетч на основе секундомера, облагороженного ИИ от Бабоса.

#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);

// Константы координат смещения центра
const int CENTER_X = 120;
const int CENTER_Y = 120;

void setup() {
  tft.begin();
  tft.fillScreen(GC9A01A_BLUE);
  tft.cp437(true);
  tft.setRotation(1);

  drawLineSnowflake(10, 0, 100, PI / 24); //пример 1 отрезка
}

void loop() {

}
////////////////////////////////////
//функция построения отрезка в полярных координатах с центром в центре экрана
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_WHITE
  );
}
//////////////////////////////////
//функция построения 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);
  }
}
//////////////////////////////////

Внутри сектора 1 просто наращиваем от центра на периферию отрезки и получаем рост снежинки. Но вот вопрос. Будет задействован наверняка рандом на float по углам. Как его реализовать, ведь по справочнику там long в аргументах?

1 лайк

Работает?

Генерация случайных float для углов

Вы правы — стандартный random() в Arduino возвращает long. Вот несколько способов получить случайный float в нужном диапазоне:

Способ 1: Простое масштабирование (рекомендую)

// Случайный float в диапазоне [0, PI/6]
float randomAngle() {
  return random(0, 10001) / 10000.0 * (PI / 6.0);
}

// Или более универсальная функция:
float randomFloat(float minVal, float maxVal) {
  return minVal + random(0, 10001) / 10000.0 * (maxVal - minVal);
}

Способ 2: Через фиксированную точность

// Если нужен угол с точностью до 0.001 радиана
float randomAnglePrecise(float maxAngle) {
  long maxInt = (long)(maxAngle * 1000);  // переводим в "миллирадианы"
  return random(0, maxInt + 1) / 1000.0;
}

Применение для вашей снежинки

void growSnowflake() {
  // Случайный радиус от 10 до 100
  int r1 = random(10, 50);
  int r2 = random(r1, 101);  // r2 всегда больше r1
  
  // Случайные углы в пределах сектора [0, PI/6]
  float f1 = randomFloat(0, PI / 6.0);
  float f2 = randomFloat(0, PI / 6.0);
  
  drawLineSnowflake(r1, f1, r2, f2);
}

float randomFloat(float minVal, float maxVal) {
  return minVal + random(0, 10001) / 10000.0 * (maxVal - minVal);
}

Важно: инициализация генератора

Не забудьте в setup() инициализировать генератор случайных чисел:

void setup() {
  randomSeed(analogRead(A0));  // шум с неподключенного пина
  // ... остальной код
}

Число 10001 в делителе даёт точность ~0.0001, что более чем достаточно для углов. Можно использовать и 1001 для точности ~0.001 — для визуала разницы не будет.

Спасибо, завтра попробую.

ии не может нарисовать снежинку, она как то не выходит))

#include <Adafruit_GFX.h>
#include <Adafruit_GC9A01A.h>
#include <SPI.h>

#define TFT_DC 17
#define TFT_CS 16

Adafruit_GC9A01A tft(TFT_CS, TFT_DC);

// Константы координат смещения центра
const int CENTER_X = 120;
const int CENTER_Y = 120;

// Параметры снежинки
const int MAX_RADIUS = 120;
const float MAX_ANGLE = PI / 6.0;

void setup() {
  Serial.begin(115200);
  tft.begin();
  tft.fillScreen(GC9A01A_BLUE);
  tft.cp437(true);
  tft.setRotation(1);
  
  // Инициализация генератора случайных чисел
  randomSeed(analogRead(0));
  
  // Пример генерации нескольких случайных отрезков
  for (int i = 0; i < 5; i++) {
    float angle1 = randomFloat(0, MAX_ANGLE);
    float angle2 = randomFloat(0, MAX_ANGLE);
    int radius1 = random(0, MAX_RADIUS / 2);
    int radius2 = random(radius1, MAX_RADIUS);
    
    drawLineSnowflake(radius1, angle1, radius2, angle2);
    delay(100);
  }
}

void loop() {
  // Анимация роста снежинки с случайными отрезками
  static unsigned long lastUpdate = 0;
  const unsigned long interval = 1000; // 1 секунда
  
  if (millis() - lastUpdate > interval) {
    lastUpdate = millis();
    
    // Добавляем новый случайный отрезок
    float angle1 = randomFloat(0, MAX_ANGLE);
    float angle2 = randomFloat(0, MAX_ANGLE);
    int radius1 = random(0, MAX_RADIUS / 2);
    int radius2 = random(radius1, MAX_RADIUS);
    
    drawLineSnowflake(radius1, angle1, radius2, angle2);
  }
}

////////////////////////////////////
// Генерация случайного числа float в заданном диапазоне
float randomFloat(float minVal, float maxVal) {
  return minVal + (maxVal - minVal) * (float)random(0, 10001) / 10000.0;
}

////////////////////////////////////
// Функция построения отрезка в полярных координатах с центром в центре экрана
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_WHITE
  );
}

//////////////////////////////////
// Функция построения 12 отсимметриченных отрезков снежинки по базовому отрезку
void drawLineSnowflake(int r1, float f1, int r2, float f2) {
  if (r1 > MAX_RADIUS || r2 > MAX_RADIUS || r1 < 0 || r2 < 0 || 
      f1 < 0.0 || f2 < 0.0 || f1 > MAX_ANGLE || f2 > MAX_ANGLE) {
    return;
  }
  
  for (float i = 0.0; 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);
  }
}

может пригодится….

а вообще мы не тем занимаемся))) вы знаете что звук влияет на ее форму ? может лучше в реале их выращивать ? возможно облучая катушкой мишина)))

Если присмотреться к снежинкам, то в углах-то как раз рандома нет абсолютно…

снежинки

Реальная снежинка - это в принципе не рандом, это фрактал.
Ну, точнее там есть элемент рандома, но гораздо меньший, чем многие думают. и точно не в размере углов.

Что меня до сих пор удивляет, это не то разнообразие снежинок, а то, что снежинка имеет центральную симметрию. Т.е. каждый луч по мере своего роста повторяет ветвления как и на других лучах. Пока не понимаю, что ему мешает расти по своим ветвлениям, а не как все остальные лучи. В то время как соседние снежинки растут совсем иначе.

Да, пока трудновато выделить единые правила алгоритма генерации и перевести в математику.

VID_20260128_121005

#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);

// Константы координат смещения центра
const int CENTER_X = 120;
const int CENTER_Y = 120;

void setup() {
  tft.begin();
  tft.fillScreen(GC9A01A_BLUE);
  // tft.cp437(true);
  tft.setRotation(1);
}

void loop() {
  for (int i = 0; i < 80; i =  i + 10 ) {
    drawLineSnowflake(i, 0,  i + 10 , PI / 12);
    delay(300);
    drawLineSnowflake(0, 0, i + 20 , 0);
    delay(300);
  }
  delay(3000);
  tft.fillScreen(GC9A01A_BLUE);
  delay(300);
}
////////////////////////////////////
//функция построения отрезка в полярных координатах с центром в центре экрана
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_WHITE
  );
}
//////////////////////////////////
//функция построения 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);
  }
}
//////////////////////////////////

В том то и дело, что они не соседние. Соседние - лучи одной снежинки.
Для каждой снежинки можно нарисовать графики зависимости температуры и влажности окружающего воздуха от времени. Для лучей одной снежинки эти графики будут совпадать, а для разных снежинок - различаться.

Отчего же трудно?
В иллюстративном примере содержится ошибка - элемент появляется сразу и сразу конечной длины, а в реальной снежинке рост начинается в центре и каждый элемент растет постепенно, время от времени ветвясь.
И, кстати, ветвление происходит всегда под тем же углом 60 гр., а не под произвольным углом, как на иллюстрации.

Это к текущему положению, что существенно усложняет математику координат.

…не знаю, изначально хотелось смещение на узор-декор, т.е. рост от центра по 6 направлениям витиевато-зрелищно.

… отрезки лишь схематично могут отобразить снежинку.

От того, что нет чётких простых правил их правдивого построения. Учёные только подступили к истине :slight_smile:

https://rg.ru/2025/07/04/reg-urfo/uchenye-vpervye-vyveli-universalnuiu-formulu-rosta-snezhinok.html?utm_referrer=https%3A%2F%2Fyandex.ru%2F

Мне больно это слышать.

Вообще-то это должно звучать так “…что добавляет один вызов функции по пересчету в новой системе координат.“

Да. Подобное делал в роботе-рисовальщике.

2 лайка

В статье, хоть и нет конкретики, содержится ключевая фраза: “внешний контур снежинки всегда описывается выведенным учеными законом”. Вот именно на нее и нужно ориентироваться при любых попытках “сгенерировать” снежинку.
А вообще, наличие вот таких заголовков:
Математическая модель опровергла историю перехода от охоты к земледелию
резко снижает доверие к сайту, на котором подобное могло появиться.

И еще:

Из шести вариантов “снежинок” минимум четыре следует сразу отбросить как явно невозможные.

успехи есть ?))) с помощью математики можно нарисовать круг потом 8 линий от него, потом линии превратить в перевернутые елочки)))(рисовать на них ветки ели, но перевернутые) все просто, но если чуть сложнее будет, то не понятно как сделать, и может ресурсов не хватить самого экрана, для быстрого обновления и вывода…

еще наверное можно пойти другим путем… добавить размытость снежинки…. и быструю смену кадра, тогда она будет красивее, из за того что глаз не успеет рассмотреть все, и будет быстрое обновление, а следующие кадры будут примерно похоже на предыдущие и снежинку…

и конечно же не стоит забывать о том что это калькулятор, и возможно вывод с sd карты или вовсе поместить картинку в память, даст более лучшую картинку…

Пока элементарности получаются, с уклоном в узоры вместо снежинок, генерация вместо картинок готовых :slight_smile:

#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);

// Константы координат смещения центра
const int CENTER_X = 120;
const int CENTER_Y = 120;
int r1 = 0;
float f1 = 0;

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

void loop() {
  for (int i = 0; i < 10; i++) {
    int r2 = random(0, 100); float f2 = (random(0, 101) / 100.0) * (PI / 6.0);
    drawLineSnowflake(r1, f1, r2 , f2);
    r1 = r2; f1 = f2;
    delay(300);
  }
  delay(3000);
  tft.fillScreen(GC9A01A_BLUE);
  delay(300);
}
////////////////////////////////////
//функция построения отрезка в полярных координатах с центром в центре экрана
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_WHITE
  );
}
//////////////////////////////////
//функция построения 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);
  }
}
//////////////////////////////////
1 лайк

Реализуйте модель: все лучи симметричны. Т.е. для генерации достаточно вести 1/12 снежинки множа и зеркаля результат. А двигаясь по лучу “вглубь“ фрактала для каждого узла с одинаковым порядком применяется один и тот же узор в зеркальной симметрии.

Ветвление только под углом 60 градусов.

Вариация только по длине луча. В реальной снежинке еще “пластины” есть, но для генерации они сложноваты и вобщем-то не так красивы.

Короче, ваша снежинка - это набор длин (а, вернее, коэффициентов длины от максимальной). Первый максимум длины (а вернее, длина основного луча) - это радиус снежинки, далее для любого бокового луча - точка выхода луча за сектор (т.е. встреча с самим собой в соседнем секторе). Любая снежинка будет закодирована вектором чисел примерно размером штук в 10-20 примерно, если говорить о масштабах экрана из примера.

Я сейчас говорю об абсолютно симметричных снежинках. Ибо генерация несимметричной снежинки - это следующий уровень сложности.

фигасеее… да мы тут не мой код от ии, а ваш код облагораживаем, и вы его почти сами сделали…
выводить часть изображения и дорисовывать остальные части это вообще нечто)))
как и в маленьком коде заложить столько разных фигур…

#include <TFT_eSPI.h>
#include <SPI.h>

TFT_eSPI tft = TFT_eSPI();

// ==== РАЗМЕРЫ ====
#define CENTER_X 64
#define CENTER_Y 80
#define MAX_RADIUS 60
#define NUM_SEGMENTS 6
#define MAX_POINTS 15

struct SnowPoint {
float r;     // радиус
float angle; // угол в радианах (0-PI/6)
};

SnowPoint points[MAX_POINTS];
int pointCount = 0;
float currentRadius = 5;
int growthDirection = 1;
unsigned long lastTime = 0;
const int animationDelay = 120;

void setup() {
Serial.begin(115200);

tft.init();
tft.setRotation(3);
tft.fillScreen(TFT_BLACK);

#ifdef TFT_BL
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
#endif

}

// ЗЕЛЕНЫЕ ЦВЕТА
uint16_t getSnowColor(float radius) {
float ratio = radius / MAX_RADIUS;

if (ratio < 0.3) return TFT_WHITE;
if (ratio < 0.6) return tft.color565(180, 255, 180); // Светло-зеленый
if (ratio < 0.8) return tft.color565(100, 220, 100); // Зеленый
return tft.color565(50, 180, 50); // Темно-зеленый
}

// Функция для линии
void drawLine(int x0, int y0, int x1, int y1, uint16_t color) {
tft.drawLine(x0, y0, x1, y1, color);
}

// Функция для полярных координат
void drawPolarLine(float r1, float a1, float r2, float a2, uint16_t color) {
int x1 = CENTER_X + (int)(r1 * cos(a1));
int y1 = CENTER_Y + (int)(r1 * sin(a1));
int x2 = CENTER_X + (int)(r2 * cos(a2));
int y2 = CENTER_Y + (int)(r2 * sin(a2));

drawLine(x1, y1, x2, y2, color);
}

// Создание сложной точки снежинки
void addSnowPoint() {
if (pointCount >= MAX_POINTS) {
// Начинаем новую снежинку
pointCount = 0;
tft.fillScreen(TFT_BLACK);
currentRadius = 5;
return;
}

float baseAngle;

if (pointCount == 0) {
// Первая точка - старт из центра
baseAngle = random(5, 26) * PI / 180.0; // 5-25 градусов
} else {
// Продолжаем линию с изгибом
float prevAngle = points[pointCount-1].angle;

// Чем дальше от центра, тем больше изгиб
float curvature = 0.5 + (pointCount / (float)MAX_POINTS) * 2.0;
int angleChange = random(-10, 11) * curvature;

baseAngle = prevAngle + angleChange * PI / 180.0;
baseAngle = constrain(baseAngle, 0, PI/6);
}

// Радиус плавно увеличивается
float r;
if (pointCount == 0) {
r = 3; // Начинаем почти из центра
} else {
r = currentRadius * (0.1 + 0.9 * (pointCount / (float)MAX_POINTS));
}

points[pointCount].r = r;
points[pointCount].angle = baseAngle;

pointCount++;
}

// Удаление последней точки
void removeLastPoint() {
if (pointCount > 0) {
pointCount--;
}
}

// Рисование снежинки (без центрального круга и линий!)
void drawPureSnowflake() {
// Полная очистка
tft.fillScreen(TFT_BLACK);

// Если точек меньше 2, рисуем только первую точку в центре
if (pointCount == 1) {
// Маленькая белая точка в центре
tft.drawPixel(CENTER_X, CENTER_Y, TFT_WHITE);
return;
}

if (pointCount < 2) return;

// Рисуем основную структуру снежинки
for (int i = 1; i < pointCount; i++) {
SnowPoint p1 = points[i-1];
SnowPoint p2 = points[i];

// Для первой линии рисуем от центра
if (i == 1) {
p1.r = 0; // Начинаем из центра
}

uint16_t color = getSnowColor((p1.r + p2.r) / 2);

// 6-кратная симметрия
for (int sector = 0; sector < NUM_SEGMENTS; sector++) {
float rotation = sector * (2 * PI / NUM_SEGMENTS);

// Основная ветвь
drawPolarLine(p1.r, p1.angle + rotation,
p2.r, p2.angle + rotation,
color);

// Зеркальная ветвь
drawPolarLine(p1.r, -p1.angle + rotation,
p2.r, -p2.angle + rotation,
color);
}
}

// Добавляем боковые веточки
if (pointCount > 3) {
drawSideBranches();
}

// Добавляем кончики
if (pointCount > 0) {
drawSnowflakeTips();
}
}

// Боковые веточки на основных ветвях
void drawSideBranches() {
// Добавляем веточки на каждую 3-ю точку
for (int i = 2; i < pointCount; i += 3) {
SnowPoint p = points[i];

if (p.r > MAX_RADIUS * 0.3) {
uint16_t branchColor = getSnowColor(p.r * 0.8);

for (int sector = 0; sector < NUM_SEGMENTS; sector++) {
float rotation = sector * (2 * PI / NUM_SEGMENTS);

// Координаты точки на основной ветви
int x0 = CENTER_X + (int)(p.r * cos(p.angle + rotation));
int y0 = CENTER_Y + (int)(p.r * sin(p.angle + rotation));

// Две боковые веточки
for (int side = -1; side <= 1; side += 2) {
float branchAngle = p.angle + rotation + side * 45 * PI / 180.0;
float branchLength = p.r * 0.3;

int x1 = x0 + (int)(branchLength * cos(branchAngle));
int y1 = y0 + (int)(branchLength * sin(branchAngle));

drawLine(x0, y0, x1, y1, branchColor);

// Маленькие ответвления на боковых веточках
if (p.r > MAX_RADIUS * 0.5) {
float subAngle = branchAngle + side * 30 * PI / 180.0;
float subLength = branchLength * 0.5;

int x2 = x1 + (int)(subLength * cos(subAngle));
int y2 = y1 + (int)(subLength * sin(subAngle));

drawLine(x1, y1, x2, y2, branchColor);
}
}
}

// Также для зеркальных ветвей
for (int sector = 0; sector < NUM_SEGMENTS; sector++) {
float rotation = sector * (2 * PI / NUM_SEGMENTS);

int x0 = CENTER_X + (int)(p.r * cos(-p.angle + rotation));
int y0 = CENTER_Y + (int)(p.r * sin(-p.angle + rotation));

for (int side = -1; side <= 1; side += 2) {
float branchAngle = -p.angle + rotation + side * 45 * PI / 180.0;
float branchLength = p.r * 0.3;

int x1 = x0 + (int)(branchLength * cos(branchAngle));
int y1 = y0 + (int)(branchLength * sin(branchAngle));

drawLine(x0, y0, x1, y1, branchColor);
}
}
}
}
}

// Кончики снежинки
void drawSnowflakeTips() {
if (pointCount == 0) return;

SnowPoint lastPoint = points[pointCount-1];

if (lastPoint.r > MAX_RADIUS * 0.4) {
uint16_t tipColor = TFT_WHITE;

for (int sector = 0; sector < NUM_SEGMENTS; sector++) {
float rotation = sector * (2 * PI / NUM_SEGMENTS);

// Кончики для основных ветвей
float angles[2] = {lastPoint.angle + rotation, -lastPoint.angle + rotation};

for (int a = 0; a < 2; a++) {
float angle = angles[a];
int x0 = CENTER_X + (int)(lastPoint.r * cos(angle));
int y0 = CENTER_Y + (int)(lastPoint.r * sin(angle));

// Три луча на кончике
for (int i = 0; i < 3; i++) {
float tipAngle = angle + (i * 120 - 60) * PI / 180.0;
float tipLength = lastPoint.r * 0.15;

int x1 = x0 + (int)(tipLength * cos(tipAngle));
int y1 = y0 + (int)(tipLength * sin(tipAngle));

drawLine(x0, y0, x1, y1, tipColor);
}
}
}
}
}

void loop() {
if (millis() - lastTime > animationDelay) {
lastTime = millis();

// Изменение радиуса
currentRadius += growthDirection * 0.7;

// Реверс направления
if (currentRadius >= MAX_RADIUS) {
currentRadius = MAX_RADIUS;
growthDirection = -1;
} else if (currentRadius <= 5) {
currentRadius = 5;
growthDirection = 1;
pointCount = 0;
tft.fillScreen(TFT_BLACK);
return;
}

// Управление точками
if (growthDirection > 0) {
addSnowPoint();
} else {
if (random(100) < 25) {
removeLastPoint();
}
}

// Рисуем чистую снежинку
drawPureSnowflake();
}
}

если все быстро не так и заметно))) если еще озу есть, можно сделать более плавный вывод… все равно это лучше чем то что у меня получалось)))

еще можно попытаться просто увеличивать изображение кстате…. и будет и рост и снежинка вроде как…
вам вот такая простая снежинка не подойдет ?)))

#include <TFT_eSPI.h>
#include <SPI.h>
#include <math.h>

TFT_eSPI tft = TFT_eSPI();

#define CENTER_X 80
#define CENTER_Y 80
#define MAX_RADIUS 45
#define PI 3.14159265359
#define BRANCHES 6

float growth = 0.0;
float growthSpeed = 0.004;
unsigned long lastTime = 0;
const int FRAME_DELAY = 30;

float lastDrawnGrowth = -1.0;
int lastClearRadius = 0;

void setup() {
tft.init();
tft.setRotation(3);
tft.fillScreen(TFT_BLACK);

#ifdef TFT_BL
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
#endif
}

uint16_t getSnowColor(float distance) {
float ratio = distance / MAX_RADIUS;

if (ratio < 0.3) return TFT_WHITE;
if (ratio < 0.5) return tft.color565(220, 240, 255);
if (ratio < 0.7) return tft.color565(180, 220, 250);
return tft.color565(140, 200, 240);
}

void clearSnowflakeArea(float currentGrowth) {
int clearRadius = (int)(MAX_RADIUS * currentGrowth) + 10;

if (clearRadius > lastClearRadius || clearRadius < lastClearRadius - 5) {
int clearSize = clearRadius * 2 + 5;
int startX = CENTER_X - clearSize/2;
int startY = CENTER_Y - clearSize/2;

startX = max(0, startX);
startY = max(0, startY);
clearSize = min(clearSize, min(128 - startX, 160 - startY));

tft.fillRect(startX, startY, clearSize, clearSize, TFT_BLACK);
lastClearRadius = clearRadius;
}
}

void drawBranch(float angle, float growthFactor) {
if (growthFactor < 0.05) return;

float length = MAX_RADIUS * growthFactor;
if (length < 2) return;

int x1 = CENTER_X + length * cos(angle);
int y1 = CENTER_Y + length * sin(angle);
uint16_t color = getSnowColor(length);

tft.drawLine(CENTER_X, CENTER_Y, x1, y1, color);

if (growthFactor > 0.35 && length > 10) {
float mid1 = length * 0.33;
int mx1 = CENTER_X + mid1 * cos(angle);
int my1 = CENTER_Y + mid1 * sin(angle);

for (int side = -1; side <= 1; side += 2) {
float sideAngle = angle + side * PI / 4.0;
float sideLen = length * 0.25;
int sx1 = mx1 + sideLen * cos(sideAngle);
int sy1 = my1 + sideLen * sin(sideAngle);

tft.drawLine(mx1, my1, sx1, sy1, color);
}

if (growthFactor > 0.65) {
float mid2 = length * 0.66;
int mx2 = CENTER_X + mid2 * cos(angle);
int my2 = CENTER_Y + mid2 * sin(angle);

for (int side = -1; side <= 1; side += 2) {
float sideAngle = angle + side * PI / 3.0;
float sideLen = length * 0.2;
int sx2 = mx2 + sideLen * cos(sideAngle);
int sy2 = my2 + sideLen * sin(sideAngle);

tft.drawLine(mx2, my2, sx2, sy2, color);
}
}
}

if (growthFactor > 0.85 && length > 20) {
drawTip(x1, y1, angle, length * 0.12);
}
}

void drawTip(int x, int y, float angle, float size) {
if (size < 2) return;

uint16_t tipColor = tft.color565(255, 255, 240);

for (int i = 0; i < 3; i++) {
float tipAngle = angle + (i * 120 - 60) * PI / 180.0;
int x1 = x + size * cos(tipAngle);
int y1 = y + size * sin(tipAngle);

tft.drawLine(x, y, x1, y1, tipColor);
}
}

void drawCenter(float growthFactor) {
if (growthFactor < 0.05) return;

tft.fillCircle(CENTER_X, CENTER_Y, 15, TFT_BLACK);

int centerSize = 1 + (int)(growthFactor * 3);
tft.fillCircle(CENTER_X, CENTER_Y, centerSize, TFT_WHITE);

if (growthFactor > 0.15) {
int rayLength = 4 + (int)(growthFactor * 6);
for (int i = 0; i < BRANCHES; i++) {
float angle = i * 2 * PI / BRANCHES;
int x = CENTER_X + rayLength * cos(angle);
int y = CENTER_Y + rayLength * sin(angle);

tft.drawLine(CENTER_X, CENTER_Y, x, y, tft.color565(200, 230, 255));
}
}

if (growthFactor > 0.45) {
tft.drawCircle(CENTER_X, CENTER_Y, 8, tft.color565(180, 210, 240));
}
}

void drawConnections(float growthFactor) {
if (growthFactor < 0.9) return;

uint16_t color = tft.color565(160, 200, 235);
float radius = MAX_RADIUS * 0.6;

for (int i = 0; i < BRANCHES; i += 2) {
int next = (i + 2) % BRANCHES;

float angle1 = i * 2 * PI / BRANCHES;
float angle2 = next * 2 * PI / BRANCHES;

int x1 = CENTER_X + radius * cos(angle1);
int y1 = CENTER_Y + radius * sin(angle1);
int x2 = CENTER_X + radius * cos(angle2);
int y2 = CENTER_Y + radius * sin(angle2);

tft.drawLine(x1, y1, x2, y2, color);
}
}

void drawSnowflake(float currentGrowth) {
clearSnowflakeArea(currentGrowth);
drawCenter(currentGrowth);

for (int i = 0; i < BRANCHES; i++) {
float angle = i * 2 * PI / BRANCHES;
drawBranch(angle, currentGrowth);
}

drawConnections(currentGrowth);
lastDrawnGrowth = currentGrowth;
}

void loop() {
unsigned long currentTime = millis();
if (currentTime - lastTime >= FRAME_DELAY) {
lastTime = currentTime;

growth += growthSpeed;

if (growth >= 1.0) {
growth = 1.0;
static unsigned long pauseStart = 0;
if (pauseStart == 0) pauseStart = currentTime;

if (currentTime - pauseStart > 2000) {
growthSpeed = -0.004;
pauseStart = 0;
}
} else if (growth <= 0.0) {
growth = 0.0;
growthSpeed = 0.004;
lastClearRadius = 0;
lastDrawnGrowth = -1.0;
}

if (abs(growth - lastDrawnGrowth) > 0.001) {
drawSnowflake(growth);
}
}
}

https://ru.files.me/u/p29ne2nvf4

если не везде обновлять изображение, еще может получится шлейф от нее…

а вообще в математики и снежинки лишняя снежинка))) может впихнуть туда золотой сечение фибоначи ? и будет совершенная математическая снежинка))) только не похожая на настоящую, в любом случае может это натолкнет вас на какие то мысли, и вы сделаете какую то свою)))

Можно попробовать.

…пока просто отрезки

VID_20260130_161449 (1)

#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);

// Константы координат смещения центра
const int CENTER_X = 120;
const int CENTER_Y = 120;
int r1 = 0;
float f1 = 0;
int delta_r = 15;
int delta_F = 15;
int F1 = 0; //

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

void loop() {
  for (int i = 0; i < 100; i++) {
    //
    int r2 = random(r1 - delta_r, r1 + delta_r );
    if (r2 < 0) {
      r2 = 0;
    }
    if (r2 > 100) {
      r2 = 100;
    }
    //
    int F2 = random(F1 - delta_F, F1 + delta_F);
    if (F2 < 0) {
      F2 = 0;
    }
    if (F2 > 100) {
      F2 = 100;
    }

    float f2 = (F2 / 100.0) * (PI / 6.0);
    drawLineSnowflake(r1, f1, r2 , f2);
    r1 = r2; f1 = f2; F1 = F2;
    delay(50);
  }
  delay(3000);
  tft.fillScreen(GC9A01A_YELLOW);
  delay(300);
}
////////////////////////////////////
//функция построения отрезка в полярных координатах с центром в центре экрана
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_RED
  );
}
//////////////////////////////////
//функция построения 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);
  }
}
//////////////////////////////////
1 лайк

золотое))) умные проги конвертации звука меня подставляют)))

как у вас у меня так не выйдет, особенно так коротко)))

1 лайк