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

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

TFT_eSPI tft = TFT_eSPI();

// Пины SD-карты
#define SD_CS    5
#define SD_SCLK 18
#define SD_MOSI 23
#define SD_MISO 19

// Цвета
#define DARK_YELLOW 0x7BE0      // Темно-желтый фон
#define CIRCLE_COLOR TFT_RED    // Красный круг
#define OUTLINE_COLOR TFT_BLUE  // Синий контур
#define TRACE_COLOR TFT_GREEN   // Зеленый след

// Размер круга 3x3
#define CIRCLE_RADIUS 1         // Радиус 1 пиксель (круг 3x3)

// Переменные
int ballX = 0, ballY = 0;
int prevBallX = 0, prevBallY = 0;
int targetX = 0, targetY = 0;
float currentX = 0, currentY = 0;
float speed = 1.2;
int pointCount = 0;
File dataFile;

// Смещение (настройте под ваши координаты)
int offsetX = 0;
int offsetY = 0;

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

// Инициализация дисплея
tft.init();
tft.setRotation(0);

// Подсветка
pinMode(TFT_BL, OUTPUT);
ledcSetup(0, 5000, 8);
ledcAttachPin(TFT_BL, 0);
ledcWrite(0, 255);

// Фон
tft.fillScreen(DARK_YELLOW);

// SD карта
SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
if (!SD.begin(SD_CS)) {
Serial.println("SD card FAIL");
while (true);
}

// Открываем файл
dataFile = SD.open("/trace(2).txt", FILE_READ);
if (!dataFile) {
Serial.println("File not found");
while (true);
}

// Пропускаем заголовок
String firstLine = dataFile.readStringUntil('\n');
Serial.print("Skipped: ");
Serial.println(firstLine);

// Первая точка
if (readNextPoint()) {
currentX = targetX;
currentY = targetY;
ballX = (int)currentX;
ballY = (int)currentY;
drawCircle3x3(ballX, ballY);
Serial.println("Ready!");
}
}

bool readNextPoint() {
if (!dataFile.available()) {
Serial.println("End of file");
dataFile.close();
return false;
}

String line = dataFile.readStringUntil('\n');
line.trim();
if (line.length() == 0) return readNextPoint();

int commaIndex = line.indexOf(',');
if (commaIndex == -1) return readNextPoint();

String xStr = line.substring(0, commaIndex);
String yStr = line.substring(commaIndex + 1);
int rawX = xStr.toInt();
int rawY = yStr.toInt();

// Применяем смещение
targetX = rawX + offsetX;
targetY = rawY + offsetY;

// Ограничиваем границы экрана
targetX = constrain(targetX, 2, TFT_WIDTH - 3);
targetY = constrain(targetY, 2, TFT_HEIGHT - 3);

pointCount++;
return true;
}

// Круг 3x3 с синим контуром
void drawCircle3x3(int x, int y) {
// Синий контур (пиксели по краям)
tft.drawPixel(x-1, y-1, OUTLINE_COLOR);  // левый верх
tft.drawPixel(x+1, y-1, OUTLINE_COLOR);  // правый верх
tft.drawPixel(x-1, y+1, OUTLINE_COLOR);  // левый низ
tft.drawPixel(x+1, y+1, OUTLINE_COLOR);  // правый низ
tft.drawPixel(x, y-1, OUTLINE_COLOR);    // верх
tft.drawPixel(x-1, y, OUTLINE_COLOR);    // лево
tft.drawPixel(x+1, y, OUTLINE_COLOR);    // право
tft.drawPixel(x, y+1, OUTLINE_COLOR);    // низ

// Красная внутренность (центр)
tft.drawPixel(x, y, CIRCLE_COLOR);       // центр
}

// Зеленый след
void drawTraceLine(int x1, int y1, int x2, int y2) {
tft.drawLine(x1, y1, x2, y2, TRACE_COLOR);
}

void loop() {
// Сохраняем старую позицию
prevBallX = ballX;
prevBallY = ballY;

// Плавное движение к цели
float dx = targetX - currentX;
float dy = targetY - currentY;
float distance = sqrt(dx*dx + dy*dy);

if (distance < speed) {
// Достигли точки
currentX = targetX;
currentY = targetY;
// Читаем следующую точку
if (!readNextPoint()) {
// Конец файла
Serial.println("Finished!");
// Мигание 3 раза
for (int i = 0; i < 3; i++) {
tft.drawPixel(ballX, ballY, TFT_WHITE);
delay(200);
drawCircle3x3(ballX, ballY);
delay(200);
}
while(true) delay(1000);
}
} else {
// Двигаемся к цели
currentX += (dx / distance) * speed;
currentY += (dy / distance) * speed;
}

// Обновляем координаты шарика
ballX = (int)currentX;
ballY = (int)currentY;

// Рисуем след
if ((ballX != prevBallX || ballY != prevBallY) && pointCount > 1) {
drawTraceLine(prevBallX, prevBallY, ballX, ballY);
}

// Рисуем круг 3x3
drawCircle3x3(ballX, ballY);

delay(15);  // Скорость анимации
}

вот версия 0.2 шарика который рисует на виртуальном столе)))

программу конвертации и печати G кодом так и не сделал, столкнулся еще с такой проблемой что некоторые фото программа видит с пятнами….по этому пошел к гайверу на сайт и сделал там рисунок, закинул на sd карту(если уж не фото или анимацию из фото выводить) и вроде рисует,(до конца ждать не стал) можно делать любые рисунки в принципе… и с заполнением, но экран только маленький, 128 на 160 что то маловато… а еще наверное стол сможете собрать, и рисовать на песке магнитом, переделав этот проект https://alexgyver.ru/magicgyver/ сделаете 300 рисунков и будите любоваться))) все таки sd карта мощь!))) а как там у гайвера это управляется не вникал, может даже что то сделал удобное…

https://alexgyver.github.io/MagicGyver/

Да, совершенно верно, я почему-то не написал об этом. Подумал и не написал.

Да, само-собой, три-четыре цикла рекурсии. А то некрасивая будет снежинка.

Я делал рисовалку на конвертере Гайвера как вариант рисования.


Песок это попроще картинки.

Пишите, загружу, сфоткаю и выложу…во всей красе и гармонии :slight_smile:

фото знакомое, но не помню что вы делали ?
что за рисовалка ?
сможете еще раз выложить код ?

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

хотя он у вас 240 на 240 похоже…

тут сразу с sd картой https://aliexpress.ru/item/1005004199531074.html
если по ссылке не выдается красный дисплей с sd картой то вот

Спойлер

Нет, не могу, на форум код скетча не влазит.



Гайверский конвертер - итоги рисования :slight_smile:

lilik рисовать непрерывной линией все таки сложнее будет, и на песке особенно))) хотя можно и левитационную платформу сделать, и поднимать шарик….

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

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

еле вспомнил нюансы

//скетч для генерации узоров
 int l=500;int h=500;//размеры окна
 // Константы координат смещения центра
 int CENTER_X = 250;
 int CENTER_Y = 250;
float r1 = 0;
float f1 = 0;
float delta_r = 20;
float delta_F = 25;
float F1 = 0; //
float cvet = 0; //


void setup() {
  size(500, 500,P3D);
  background(255);
 frameRate(20);//число кадров в секунду 
 
}
void draw() {
 
    //
    float r2 = random(r1 - delta_r, r1 + delta_r );
    if (r2 < 0) {
      r2 = 0;
    }
    if (r2 > 250) {
      r2 = 250;
    }
    //
    float 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);
    if (r2 < 250) {
      cvet = 2;
    }
    if (r2 < 180) {
      cvet = 0;
    }
    if (r2 < 130) {
      cvet = 3;
    }
    if (r2 < 70) {
      cvet = 1;
    }

    drawLineSnowflake(r1, f1, r2 , f2);
    r1 = r2; f1 = f2; F1 = F2;
   
  if(keyPressed&&key == ' '){ // клавиша пробел
   save("kadr_0.png"); // фото сделать в файл с названием
  fill(255);
  rect(0, 0, 500, 500);// стереть узор
  }
  
}

//функция построения отрезка в полярных координатах с центром в центре экрана
void drawLinePol(float r1, float f1, float r2, float f2) {
  if (cvet == 0) {
     strokeWeight(1);
    stroke(0,125,123);
    line(
      CENTER_X + cos(f1) * r1,
      CENTER_Y + sin(f1) * r1,
      CENTER_X + cos(f2) * r2,
      CENTER_Y + sin(f2) * r2
     );
  }
  if (cvet == 1) {
     strokeWeight(1);
     stroke(255,0,0);
    line(
      CENTER_X + cos(f1) * r1,
      CENTER_Y + sin(f1) * r1,
      CENTER_X + cos(f2) * r2,
      CENTER_Y + sin(f2) * r2
      );
  }
  if (cvet == 2) {
     strokeWeight(1);
    stroke(0,0,0);
    line(
      CENTER_X + cos(f1) * r1,
      CENTER_Y + sin(f1) * r1,
      CENTER_X + cos(f2) * r2,
      CENTER_Y + sin(f2) * r2
      
    );
  }
  if (cvet == 3) {
    strokeWeight(1);
    stroke(0,255,0);
    line(
      CENTER_X + cos(f1) * r1,
      CENTER_Y + sin(f1) * r1,
      CENTER_X + cos(f2) * r2,
      CENTER_Y + sin(f2) * r2
     );
  }
}
//////////////////////////////////
//функция построения 12 отсимметриченных отрезков снежинки по базовому отрезку
void drawLineSnowflake (float r1, float f1, float r2, float f2) {
  if (r1 > 250 || r2 > 250 || 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);
  }
}
//////////////////////////////////

//скетч для генерации узоров
 int l=500;int h=500;//размеры окна
 // Константы координат смещения центра
 int CENTER_X = 250;
 int CENTER_Y = 250;
 float cvet = 0; //


void setup() {
  size(500, 500,P3D);
  background(0);
 frameRate(10);//число кадров в секунду 
 
}
void draw() {
 //  
  if(keyPressed&&key == ' '){ // клавиша пробел
   save("kadr_0.png"); // фото сделать в файл с названием
  fill(0);
  rect(0, 0, 500, 500);// стереть узор
  }
//  
    
    float r1 = random(0, 250); float f1 = random(0.0,2*PI / 5.0);
    float r2 = random(0, 250); float f2 = random(0.0,2*PI / 5.0);
    float r3 = random(0, 250); float f3 = random(0.0,2*PI / 5.0);
    fillTriangleSnowflake(r1, f1, r2 , f2, r3, f3);
//   
  
 
}
/////////////////////////////////////
//функция построения закрашенного треугольника в полярных координатах с центром в центре экрана
void fillTrianglePol(float r1, float f1, float r2, float f2, float r3, float f3) {

  if (cvet<1) {
    fill(255);
    triangle(
      CENTER_X + cos(f1) * r1,
      CENTER_Y + sin(f1) * r1,
      CENTER_X + cos(f2) * r2,
      CENTER_Y + sin(f2) * r2,
      CENTER_X + cos(f3) * r3,
      CENTER_Y + sin(f3) * r3
     
    );
  }
  else {
    fill(255,0,0);
  triangle(
      CENTER_X + cos(f1) * r1,
      CENTER_Y + sin(f1) * r1,
      CENTER_X + cos(f2) * r2,
      CENTER_Y + sin(f2) * r2,
      CENTER_X + cos(f3) * r3,
      CENTER_Y + sin(f3) * r3
     
    );
  }

}
//////////////////////////////////
//функция построения 10 отсимметриченных треугольников снежинки по базовому отрезку
void fillTriangleSnowflake (float r1, float f1, float r2, float f2, float r3, float f3) {
  if (r1 > 250 || r2 > 250 || r3 > 250 || r1 < 0 || r2 < 0 || r3 < 0 || f1 < 0.0 || f2 < 0.0 || f3 < 0.0 || f1 > PI / 5.0 || f2 > PI / 5 || f3 > PI / 5) {
    return; //условие рисования базового треугольника в допустимых значениях аргументов (0-120 пикселей радиус, 0-PI/6 угол(сектор))
  }
  cvet = random(0, 2);
  for (float i = 0.01+PI/10; i < 2 * PI+PI/10; i=i+2* PI / 5.0) {
    fillTrianglePol(r1, f1 + i, r2, f2 + i, r3, f3 + i);
    fillTrianglePol(r1, 2 * PI - f1 + i, r2, 2 * PI - f2 + i, r3, 2 * PI - f3 + i);
  }
}
//////////////////////////////////

Попробовал ИИ для преобразования узора в орден. Только на одном ресурсе бесплатно удалось толково :slight_smile: За второй вариант уже деньги просит. Есть бесплатные успешные ИИ редакторы картинок?, на примете.

ну если только нервная система у вас крепкая то вот https://aimpress.ru/gen лично я так и не смог объяснить что тени на фото нельзя делать, когда он шары рисовал))) а вообще почему не grok ии ? из видео извлечете фото… не знаю работает ли, но сейчас поищу ссылку на ютуб видео

Нет, надо ИИ генерация по заданному узору-картинке, а не по голому тексту :slight_smile:

https://www.youtube.com/watch?v=iZUkyRRAbqs подробный обзорчик, (GROK создание видео или картинок из видео)может понравится…

если ии просить превратить фото узора в настоящую медаль, вам это наверное не понравится)))

хотя есть еще такая ссылка https://myneuralnetworks.ru/generating_video_from_text/ несколько попыток в сутки у вас будет

другие ссылки на относительно бесплатные ии кажется совсем не то выдавали, и кажется я их уже удалил

p.s. проблема в том что нет конкретного ИИ для создания медалей из узоров, есть универсальные ИИ и они будут выдавать результат хуже чем создание видео с 0 по описанию)))

Для ИИ это вообще не должна быть проблема:) Функция стилизации по условию сохранения форм и цветов исходных.

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

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

Есть генераторы снежинок на питоне.

Если охота разбираться.

Processing надо ставить. Имитировать на нём реальные поделки, а не только узоры :slight_smile:
Светодиодный куб, вертишь, ближе, дальше как в опенскад.
волна-гора

Ещё вариант генерации узоров из картинки


путём вырезания сектора данных из массива

#include <Adafruit_GFX.h>
#include <Adafruit_GC9A01A.h>
#include <SPI.h>
#include "ris_1.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() {
  // Инициализация генератора случайных чисел
  randomSeed(analogRead(32));
  tft.begin();
  tft.fillScreen(GC9A01A_BLACK);
  // tft.cp437(true);
  tft.setRotation(2);
 
}

void loop() {
  for (float f = 0.0; f < 2*PI-PI / 6; f += PI / 6.0) {
  draw_RisSektor(f,marka);
  delay(3000);
  }
}
//////////////////////////////////////
//функция вывода 12 отсимметриченных секторов узора по базовому сектору картинки 
void draw_RisSektor(float S,const uint8_t *bitmap) {
  for (int r1 = 0; r1 <= 120 ; r1 += 1) {
    for (float f1 = S; f1 <= S+PI / 6; f1 += PI / 1000.0) {
      int x = CENTER_X + cos(f1) * r1;
      int y = CENTER_Y + sin(f1) * r1;
      for (float i = 0.01; i < 2 * PI; i += PI / 3.0) {
        int x1 = CENTER_X + cos(f1 + i) * r1;
        int y1 = CENTER_Y + sin(f1 + i) * r1;
        tft.drawPixel(x1, y1, 256 * bitmap[2 * x + 1 + y * 2 * 240] + bitmap[2 * x + y * 2 * 240]);
        int x2 = CENTER_X + cos(2 * PI - f1 + i) * r1;
        int y2 = CENTER_Y + sin(2 * PI - f1 + i) * r1;
        tft.drawPixel(x2, y2, 256 * bitmap[2 * x + 1 + y * 2 * 240] + bitmap[2 * x + y * 2 * 240]);

      }
    }
  }
}
//////////////////////////////////////


Для разнообразия генерации надо подумать о том какая картинка уместней.
… однородный рисунок не очень подходит


узоры похожие

… отрисовка всех пикселей долгая, а выборочная быстрая
VID_20260208_161730
VID_20260208_173623

#include <Adafruit_GFX.h>
#include <Adafruit_GC9A01A.h>
#include <SPI.h>
#include "ris_1.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;
const float Z = 100.0; //от 100.0 до 1000.0 регулировка качества отрисовки пикселей с пропусками и без них

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

}

void loop() {
  float f = (random(0, 1001) / 1000.0) * (2 * PI - PI / 6.0);
  draw_RisSektor(f, ris_1);
}
//////////////////////////////////////
//функция вывода 12 отсимметриченных секторов узора по базовому сектору картинки
void draw_RisSektor(float S, const uint8_t *bitmap) {
  for (int r1 = 0; r1 <= 120 ; r1 += 1) {
    for (float f1 = 0.0; f1 <= PI / 6.0; f1 += PI / Z) {
      int x = CENTER_X + cos(f1 + S) * r1;
      int y = CENTER_Y + sin(f1 + S) * r1;
      for (float i = 0.01; i <= 2 * PI; i += PI / 3.0) {
        int x1 = CENTER_X + cos(f1 + i) * r1;
        int y1 = CENTER_Y + sin(f1 + i) * r1;
        tft.drawPixel(x1, y1, 256 * bitmap[2 * x + 1 + y * 2 * 240] + bitmap[2 * x + y * 2 * 240]);
        int x2 = CENTER_X + cos(2 * PI - f1 + i) * r1;
        int y2 = CENTER_Y + sin(2 * PI - f1 + i) * r1;
        tft.drawPixel(x2, y2, 256 * bitmap[2 * x + 1 + y * 2 * 240] + bitmap[2 * x + y * 2 * 240]);

      }
    }
  }
}
//////////////////////////////////////


2 лайка

VID_20260208_190746
…сказочная снежинка :slight_smile:

2 лайка