Skip to content

Instantly share code, notes, and snippets.

@levidehaan
Created July 8, 2024 14:18
Show Gist options
  • Save levidehaan/d309263f0b4a67424d05d618a6c3b60e to your computer and use it in GitHub Desktop.
Save levidehaan/d309263f0b4a67424d05d618a6c3b60e to your computer and use it in GitHub Desktop.
#ifndef _DIGITAL_RAIN_ANIMATION_H
#define _DIGITAL_RAIN_ANIMATION_H
#define FONT_SIZE 2 //set font size 2
#define LINE_WIDTH 12 //width for font size 2
#define LETTER_HEIGHT 14 //height for font size 2
#define KEY_RESET_TIME 60 * 1000 //60 seconds reset time
#include <vector>
#include <string>
#include <HardwareSerial.h> // Include the HardwareSerial library
enum AnimMode { SHOWCASE, MATRIX, TEXT };
template<class T>
class DigitalRainAnimation {
private:
T* _gfx = NULL;
AnimMode _animMode;
int line_len_min; //minimum length of characters
int line_len_max; //maximum length of characters
int line_speed_min; //minimum vertical move speed
int line_speed_max; //maximum vertical move speed
int width, height;
int numOfline;
int timeFrame;
int matrixTimeFrame;
uint8_t fontSize;
uint8_t lineWidth;
uint8_t letterHeight;
bool isPlaying;
bool isAlphabetOnly;
uint32_t lastDrawTime;
uint32_t lastUpdatedKeyTime;
uint16_t headCharColor;
uint16_t textColor;
uint16_t bgColor;
std::vector<int> line_length;
std::vector<int> line_pos;
std::vector<int> line_speed;
std::string keyString;
uint8_t textStartX;
uint8_t textStartY;
int textTimeFrame;
std::string textMessage;
std::string workingTextMessage;
void prepareAnim() {
if (_gfx == NULL) return;
setHeadCharColor(255, 255, 255);
setTextColor(0, 255, 0);
setBGColor(0, 0, 0);
lastDrawTime = millis() - timeFrame;
width = _gfx->width();
height = _gfx->height();
_gfx->fillRect(0, 0, width, height, bgColor);
_gfx->setTextColor(textColor, bgColor);
numOfline = width / lineWidth + 1;
for (int i = 0; i < numOfline; i++) {
line_length.push_back(getRandomNum(line_len_min, line_len_max));
line_pos.push_back(setYPos(line_length[i]) - letterHeight);
line_speed.push_back(getRandomNum(line_speed_min, line_speed_max));
}
isPlaying = true;
lastUpdatedKeyTime = millis() - timeFrame;
}
//updating each line with a new length, Y position, and speed.
void lineUpdate(int lineNum) {
line_length[lineNum] = getRandomNum(line_len_min, line_len_max);
line_pos[lineNum] = setYPos(line_length[lineNum]);
line_speed[lineNum] = getRandomNum(line_speed_min, line_speed_max);
}
//while moving vertically, the color value changes and the character changes as well.
//if a random key is generated, switch to red.
void lineAnimation(int lineNum) {
int startX = lineNum * lineWidth;
int currentY = -letterHeight;
_gfx->fillRect(startX, 0, lineWidth, height, bgColor);
bool isKeyMode = keyString.length() > 0;
for (int i = 0; i < line_length[lineNum]; i++) {
int colorVal = map(i, 0, line_length[lineNum], 10, 255);
uint16_t lumColor = luminance(textColor, colorVal);
_gfx->setTextColor(isKeyMode ? _gfx->color565(colorVal, 0, 0) : lumColor, bgColor);
_gfx->setCursor(startX, line_pos[lineNum] + currentY);
_gfx->setTextSize(fontSize);
_gfx->print(isAlphabetOnly ? getAbcASCIIChar() : getASCIIChar());
currentY = (i * letterHeight);
}
_gfx->setTextColor(headCharColor, bgColor);
if (keyString.length() > lineNum) {
char _char = keyString.at(lineNum);
const char* keyChar = &_char;
_gfx->setCursor(startX, line_pos[lineNum] + currentY);
_gfx->setTextSize(fontSize);
_gfx->print(keyChar);
} else {
_gfx->setCursor(startX, line_pos[lineNum] + currentY);
_gfx->setTextSize(fontSize);
_gfx->print(isAlphabetOnly ? getAbcASCIIChar() : getASCIIChar());
}
line_pos[lineNum] += line_speed[lineNum];
if (line_pos[lineNum] >= height) {
lineUpdate(lineNum);
}
}
//a function that gets randomly from ASCII codes 33 to 65 and 91 to 126.
String getASCIIChar() {
return String((char)(random(0, 2) == 0 ? random(33, 65) : random(91, 126)));
}
//a function that gets only alphabets from ASCII code.
String getAbcASCIIChar() {
return String((char)(random(0, 2) == 0 ? random(65, 91) : random(97, 123)));
}
//move the position to start from out of the screen.
int setYPos(int lineLen) {
return lineLen * -20;
}
//the function is to get the random number (including max)
int getRandomNum(int min, int max) {
return random(min, max + 1);
}
//the function is to generate a random key with length
std::string getKey(int key_length) {
resetKey();
int maxKeyLength = (key_length > 0 ? (key_length > numOfline ? numOfline : key_length) : numOfline);
for (int i = 0; i < maxKeyLength; i++) {
keyString.append((getAbcASCIIChar()).c_str());
}
return keyString;
}
//the function is to remove the generated key
void resetKey() {
keyString = "";
lastUpdatedKeyTime = millis();
}
//set Text Bigger
void setBigText(bool isOn) {
fontSize = isOn ? FONT_SIZE * 2 : FONT_SIZE;
lineWidth = isOn ? LINE_WIDTH * 2 : LINE_WIDTH;
letterHeight = isOn ? LETTER_HEIGHT * 1.6 : LETTER_HEIGHT;
}
//From TFT_eFEX
//https://github.com/Bodmer/TFT_eFEX
uint16_t luminance(uint16_t color, uint8_t luminance) {
// Extract rgb colours and stretch range to 0 - 255
uint16_t r = (color & 0xF800) >> 8;
r |= (r >> 5);
uint16_t g = (color & 0x07E0) >> 3;
g |= (g >> 6);
uint16_t b = (color & 0x001F) << 3;
b |= (b >> 5);
b = ((b * (uint16_t)luminance + 255) >> 8) & 0x00F8;
g = ((g * (uint16_t)luminance + 255) >> 8) & 0x00FC;
r = ((r * (uint16_t)luminance + 255) >> 8) & 0x00F8;
return (r << 8) | (g << 3) | (b >> 3);
}
//the function is to run text animation
void textAnimation() {
int textMsgLength = workingTextMessage.length();
if (textMsgLength <= 0) {
_gfx->fillRect(0, 0, _gfx->width(), _gfx->height(), bgColor);
if (_animMode == TEXT) {
_gfx->setTextSize(fontSize);
_gfx->setCursor(textStartX, textStartY);
workingTextMessage = textMessage;
} else {
_animMode = MATRIX;
timeFrame = matrixTimeFrame;
}
return;
}
char letter = workingTextMessage.at(0);
if (letter == '\n') {
_gfx->fillRect(0, 0, _gfx->width(), _gfx->height(), bgColor);
_gfx->setTextSize(fontSize);
_gfx->setCursor(textStartX, textStartY);
} else {
_gfx->print(letter);
}
workingTextMessage.erase(0, 1);
}
public:
DigitalRainAnimation() {} // Constructor
//initialization
void init(T* gfx, bool biggerText = false, bool alphabetOnly = false) {
_gfx = gfx;
_animMode = MATRIX;
line_len_min = 3;
line_len_max = 20;
line_speed_min = 3;
line_speed_max = 15;
timeFrame = 100;
isAlphabetOnly = alphabetOnly;
setBigText(biggerText);
prepareAnim();
Serial.begin(115200);
}
//setup for Matrix
void setup(int new_line_len_min, int new_line_len_max, int new_line_speed_min, int new_line_speed_max, int matrix_timeFrame) {
line_len_min = new_line_len_min;
line_len_max = new_line_len_max;
line_speed_min = new_line_speed_min;
line_speed_max = new_line_speed_max;
matrixTimeFrame = matrix_timeFrame;
prepareAnim();
}
//setup for Text Animation
void setTextAnimMode(AnimMode animMode, std::string msg, int startX = 60, int startY = 60, int text_timeFrame = 400) {
_animMode = animMode;
setAnimText(msg);
textStartX = startX;
textStartY = startY;
textTimeFrame = text_timeFrame;
timeFrame = text_timeFrame;
}
//set Animation Text
void setAnimText(std::string msg) {
textMessage = msg;
workingTextMessage = textMessage;
timeFrame = 400;
}
//set Head Char Color
void setHeadCharColor(uint8_t red, uint8_t green, uint8_t blue) {
headCharColor = _gfx->color565(red, green, blue);
}
//set Text Color
void setTextColor(uint8_t red, uint8_t green, uint8_t blue) {
textColor = _gfx->color565(red, green, blue);
Serial.print("Text color set to: "); // Debug statement
Serial.print(red);
Serial.print(", ");
Serial.print(green);
Serial.print(", ");
Serial.println(blue);
}
//set BG Color
void setBGColor(uint8_t red, uint8_t green, uint8_t blue) {
bgColor = _gfx->color565(red, green, blue);
}
//updating screen
void loop() {
if (_gfx == NULL) return;
uint32_t currentTime = millis();
if (((currentTime - lastUpdatedKeyTime) > KEY_RESET_TIME)) {
resetKey();
}
if (((currentTime - lastDrawTime) < timeFrame)) {
return;
}
if (isPlaying) {
switch (_animMode) {
case MATRIX:
for (int i = 0; i < numOfline; i++) lineAnimation(i);
break;
case SHOWCASE:
case TEXT:
textAnimation();
break;
}
}
lastDrawTime = currentTime;
if(Serial.available() > 0){
Serial.print("input detected\n");
processSerialInput();
}
}
//a function to stop animation.
void pause() {
isPlaying = false;
}
//a function to resume animation.
void resume() {
isPlaying = true;
}
void processSerialInput() {
int newData = Serial.parseInt();
if (newData) {
Serial.print("Input: ");
Serial.print(newData);
switch (newData) {
case 1: setTextColor(0, 0, 255); Serial.println("\nSet color to Blue"); break; // Blue
case 2: setTextColor(0, 255, 0); Serial.println("\nSet color to Green"); break; // Green
case 3: setTextColor(255, 0, 0); Serial.println("\nSet color to Red"); break; // Red
case 4: setTextColor(75, 125, 215); Serial.println("\nSet color to White"); break; // cheese
default: setTextColor(255, 95, 255); Serial.println("\nSet color to White"); break; // White as default
}
newData = false; // Reset the flag
}
}
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment